| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140 |
- (** 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_2_1_;
- int _const_4_;
- int _const_6_;
- int[] a;
- _a_2_1_ = two();
- _const_4_ = two();
- _const_6_ = two();
- a := <allocate>((2 * _a_2_1_));
- a[0] = 1;
- a[1] = _const_4_;
- a[_a_2_1_] = 1;
- a[(_a_2_1_ + 1)] = _const_6_;
- printInt(a[(_a_2_1_ + 1)]);
- \} v}
- {2 Transforming for-loops to while-loops}
- {v for (int i = <start>, <stop>, <step>) \{
- <body>
- \} v}
- is transformed into:
- {v _i_1 = <start>;
- _step_2 = <step>;
- _stop_3 = <stop>;
- while ((_step_2 > 0) ? (_i_1 < _stop_3) : (_i_1 > _stop_3)) \{
- <body>
- _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 [<body>] 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
|