(** Desugaring: split variable initialisations, prevent incorrect double evaluation, and transform for-loops to while-loops. *) (** {2 Split variable initialisations} Variable initialisations are split into declarations and assignments, to generalize the AST format. This makes context analysis easier, since initialisations need not be considered. The assignments are placed in the function body, after local fuction declarations (which are not in the example below). Initialisations fo global variables are moved to a new function called "__init", which is a reserved function that is called by the VM before the main function is called. {v int glob = 1; void foo() \{ int foo = 0; int bar = foo; \} v} becomes: {v export void __init() \{ glob = 1; \} int glob; void foo() \{ int foo; int bar; foo = 0; bar = foo; \} v} {3 Array initialisations} A more complex class of initialisations are array initialisations. Arrays can be initialised to a scalar value or to an array constant. For array constants that cover all array dimensions, separate assign statements are generated for each array index. For scalar values and aray constants that have a lower nesting level than the number of array dimensions, for-loops are generated. The following example shows all situation: {v void foo() \{ int[3] a = [1, 2, 3]; int[3] b = 4; int[2, 2] c = [[3, 4], 5]; \} v} The snippet above is transformed to code equivalent to the following: {v void foo() \{ int[] a; int[] b; int[] c; a = __allocate(3); a[0] = 1; a[1] = 2; a[2] = 3; b = __allocate(3); for (int i = 0, 3) \{ b[i] = 4; \} c = __allocate(4); c[0] = 3; c[1] = 4; for (int i = 0, 2) \{ c[1, i] = 5; \} \} v} {2 Prevent incorrect double evaluation} In the following code: {v int twos = 0; int two() \{ twos = twos + 1; return 2; \} void foo() \{ int[2, two()] a = [[1, two()], [1, two()]]; printInt(a[1, 1]); \} v} [two()] must be evaluated exactly three times in order to preserve correct behaviour. However, [a[1, 1]] will at a later stage be transformed into [a[(1 * two()) + 1]], thus incorrectly evaluating [two()] an additional time. Assigning a scalar value to an array creates the same problem, since this is transformed into a for-loop, where the scalar expression is evaluated during each iteration. The problem is solved by creating new so-called "constant variables" for all of these values, and initializing hem to the original expressions. The expressions are then replaced with usages of these variables. A constant variable is a variable that is known to only be assigned once in total. This is only true for some variables generated by the compiler. In the later {!Constprop} phase, these assignments are a found and those which assign constant values (e.g., integers), are propagated to uses of the variable. This way, only the non-constant expressions are defined in new variables in the resulting code. For the example above, the result will be (after array dimension reduction and constant propagation): {v ... void foo() \{ int a$dim$$2; int const$$1; int const$$2; int[2, a$dim$$2] a; a$dim$$2 = two(); const$$1 = two(); const$$2 = two(); a = __allocate(2 * a$dim$$2); a[0] = 1; a[1] = const$$1; a[a$dim$$2)] = 1; a[a$dim$$2) + 1] = const$$2; printInt(a[(1 * a$dim$$2) + 1]); \} v} {2 Transforming for-loops to while-loops} {v for (int i = , , ) \{ \} v} is transformed into: {v i$1 = ; step$2 = ; stop$3 = ; while ((step$2 > 0) ? (i$1 < stop$3) : (i$1 > stop$3)) \{ i$1 = i$1 + step$2; \} v} Here, [i$1], [step$2] and [stop$3] are fresh variables. Definitions of these new variables are added to the scope of the current function. Every occurrence of [i] in [] is replaced with the fresh variable [i$1], this prevents problems with nested for-loops that use the same induction variable. *) (** Main phase function, called by {!Main}. *) val phase : Main.phase_func