Taddeus Kroes 12 лет назад
Родитель
Сommit
b79720f90d
2 измененных файлов с 122 добавлено и 2 удалено
  1. 2 2
      phases/desug.ml
  2. 120 0
      phases/desug.mli

+ 2 - 2
phases/desug.ml

@@ -216,9 +216,8 @@ let rec sublist n = function
   | _ :: tl    -> sublist (n - 1) tl
 
 let rec array_init = function
-  (* Transform array constant inisialisation into separate assign statements
+  (* Transform array constant initialisation into separate assign statements
    * for all entries in the constant array *)
-  (* TODO: only allow when array dimensions are constant? *)
   | Assign (name, None, ArrayInit (ArrayConst _ as value, dims), ann) ->
     let ndims = List.length dims in
     let rec make_assigns depth i indices = function
@@ -283,4 +282,5 @@ let phase = function
     (* Transform ArrayConst assignment to assignments in for-loops, and
      * transform all for-loops to while-loops afterwards *)
     Ast (for_to_while (array_init (node)))
+
   | _ -> raise (InvalidInput "desugar")

+ 120 - 0
phases/desug.mli

@@ -1 +1,121 @@
+(** Desugaring: split variable initialisations, prevent incorrect double
+    evaluation, and transform for-loops to while-loops. *)
+
+(** {2 Split variable initialisations}
+
+    Variable initialisations are splint 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):
+
+{v int foo = 0;
+int bar = foo; v}
+    becomes:
+{v 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 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 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 create the same problem, since
+    this is trwnsformed 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 = two();
+  int const$$1 = two();
+  int const$$2 = two();
+  int[2, a$dim$$2] a;
+  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 = <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}
+
+    Where [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