desug.mli 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. (** Desugaring: split variable initialisations, prevent incorrect double
  2. evaluation, and transform for-loops to while-loops. *)
  3. (** {2 Split variable initialisations}
  4. Variable initialisations are split into declarations and assignments, to
  5. generalize the AST format. This makes context analysis easier, since
  6. initialisations need not be considered. The assignments are placed in the
  7. function body, after local fuction declarations (which are not in the
  8. example below). Initialisations fo global variables are moved to a new
  9. function called "__init", which is a reserved function that is called by the
  10. VM before the main function is called.
  11. {v int glob = 1;
  12. export int main() \{
  13. int foo = 0;
  14. int bar = foo;
  15. \} v}
  16. becomes:
  17. {v export void __init() \{
  18. glob = 1;
  19. \}
  20. int glob;
  21. export int main() \{
  22. int foo;
  23. int bar;
  24. foo = 0;
  25. bar = foo;
  26. \} v}
  27. {3 Array initialisations}
  28. A more complex class of initialisations are array initialisations. Arrays
  29. can be initialised to a scalar value or to an array constant. For array
  30. constants that cover all array dimensions, separate assign statements are
  31. generated for each array index. For scalar values and aray constants that
  32. have a lower nesting level than the number of array dimensions, for-loops
  33. are generated. The following example shows all situation:
  34. {v int[3] a = [1, 2, 3];
  35. int[3] b = 4;
  36. int[2, 2] c = [[3, 4], 5]; v}
  37. The snippet above is transformed to code equivalent to the following:
  38. {v int[] a;
  39. int[] b;
  40. int[] c;
  41. a = __allocate(3);
  42. a[0] = 1;
  43. a[1] = 2;
  44. a[2] = 3;
  45. b = __allocate(3);
  46. for (int i = 0, 3) \{
  47. b[i] = 4;
  48. \}
  49. c = __allocate(4);
  50. c[0] = 3;
  51. c[1] = 4;
  52. for (int i = 0, 2) \{
  53. c[1, i] = 5;
  54. \} v}
  55. {2 Prevent incorrect double evaluation}
  56. In the following code:
  57. {v int twos = 0;
  58. int two() \{
  59. twos = twos + 1;
  60. return 2;
  61. \}
  62. void foo() \{
  63. int[2, two()] a = [[1, two()], [1, two()]];
  64. printInt(a[1, 1]);
  65. \} v}
  66. [two()] must be evaluated exactly three times in order to preserve correct
  67. behaviour. However, [a[1, 1]] will at a later stage be transformed into
  68. [a[(1 * two()) + 1]], thus incorrectly evaluating [two()] an additional
  69. time. Assigning a scalar value to an array create the same problem, since
  70. this is trwnsformed into a for-loop, where the scalar expression is
  71. evaluated during each iteration. The problem is solved by creating new
  72. so-called "constant variables" for all of these values, and initializing hem
  73. to the original expressions. The expressions are then replaced with usages
  74. of these variables. A constant variable is a variable that is known to only
  75. be assigned once in total. This is only true for some variables generated by
  76. the compiler. In the later {!Constprop} phase, these assignments are a found
  77. and those which assign constant values (e.g., integers), are propagated to
  78. uses of the variable. This way, only the non-constant expressions are
  79. defined in new variables in the resulting code.
  80. For the example above, the result will be (after array dimension reduction
  81. and constant propagation):
  82. {v ...
  83. void foo() \{
  84. int a$dim$$2 = two();
  85. int const$$1 = two();
  86. int const$$2 = two();
  87. int[2, a$dim$$2] a;
  88. a = __allocate(2 * a$dim$$2);
  89. a[0] = 1;
  90. a[1] = const$$1;
  91. a[a$dim$$2)] = 1;
  92. a[a$dim$$2) + 1] = const$$2;
  93. printInt(a[(1 * a$dim$$2) + 1]);
  94. \} v}
  95. {2 Transforming for-loops to while-loops}
  96. {v for(int i = <start>,<stop>,<step>) \{
  97. <body>
  98. \} v}
  99. is transformed into:
  100. {v i$1 = <start>;
  101. step$2 = <step>;
  102. stop$3 = <stop>;
  103. while((step$2 > 0) ? (i$1 < stop$3) : (i$1 > stop$3)) \{
  104. <body>
  105. i$1 = i$1 + step$2;
  106. \} v}
  107. Where [i$1], [step$2] and [stop$3] are fresh variables. Definitions of these
  108. new variables are added to the scope of the current function. Every
  109. occurrence of [i] in [<body>] is replaced with the fresh variable [i$1],
  110. this prevents problems with nested for-loops that use the same induction
  111. variable .*)
  112. (** Main phase function, called by {!Main}. *)
  113. val phase : Main.phase_func