desug.mli 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. (** Desugaring: split variable initialisations, generate array dimension
  2. variables, 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. The assignments are placed in the function body,
  6. after local fuction declarations (which are not in the example below).
  7. Initialisations fo global variables are moved to a new function called
  8. "__init", which is a reserved function that is called by the VM before the
  9. main function is called.
  10. {v int glob = 1;
  11. void foo() \{
  12. int foo = 0;
  13. int bar = foo;
  14. \} v}
  15. becomes:
  16. {v export void __init() \{
  17. glob = 1;
  18. \}
  19. int glob;
  20. void foo() \{
  21. int foo;
  22. int bar;
  23. foo = 0;
  24. bar = foo;
  25. \} v}
  26. {3 Array initialisations}
  27. A more complex class of initialisations is that of array initialisations.
  28. Arrays can be initialised to a scalar value or to an array literal in
  29. bracket notation. A scalar value is rewritten to a nested for-loop over all
  30. array dimensions, with an assignment in the most nested loop. An array
  31. constant is rewritten to a series of separate assign statements to the
  32. corresponding array indices. The following example shows both
  33. transformations:
  34. {v void foo() \{
  35. int[3] a = 4;
  36. int[2, 2] b = [[1, 2], [3, 4]];
  37. \} v}
  38. This is transformed into:
  39. {v void foo() \{
  40. int[3] a;
  41. int[2, 2] b;
  42. a = __allocate(3);
  43. for (int i = 0, 3) \{
  44. a[i] = 4;
  45. \}
  46. b = __allocate(2, 2);
  47. b[0, 0] = 1;
  48. b[0, 1] = 2;
  49. b[1, 0] = 3;
  50. b[1, 1] = 4;
  51. \} v}
  52. Actually, the dimensions of [a] and [b] should have been replaced by new
  53. variables earlier for reasons described below, but this is not done in the
  54. example to maintain readability.
  55. Note that array constants in bracket expressions must have a nesting level
  56. that is equal to the number of array dimensions, else an error will occur.
  57. {2 Move array dimensions and scalars into new variables}
  58. In the following code:
  59. {v int twos = 0;
  60. int two() \{
  61. twos = twos + 1;
  62. return 2;
  63. \}
  64. void foo() \{
  65. int[2, two()] a = two();
  66. printInt(twos);
  67. printInt(a[1, 1]);
  68. \} v}
  69. [two()] must be evaluated exactly twice in order to preserve correct
  70. behaviour. However, the scalar inialisation will be transformed into a
  71. for-loop, evaluating [two()] in each loop iteration. Moreover, [a[1, 1]]
  72. would at a later stage (during array dimension reduction) be transformed
  73. into [a[(1 * two()) + 1]], thus incorrectly evaluating [two()] an additional
  74. time.
  75. The problem is solved by generating new variables for scalar initialisations
  76. and array dimensions, and replacing the original expression with the
  77. generated variables. Note that these variables are marked so-called
  78. "constant variables" since they are known to be assigned exactly once, and
  79. thus likely optimizable by {!Constprop}. This way, only the non-constant
  80. expressions are defined in new variables in the final code.
  81. In the example above, [int[2, two()] a = two();] is transformed as follows:
  82. {v ...
  83. int _a_0_ = 2; // 2 will be propagated back by constant propagation
  84. int _a_1_ = two();
  85. int _scalar_1_ = two();
  86. int[_a_0_, _a_1_] a = _scalar_1_;
  87. ... v}
  88. resulting in:
  89. {v ...
  90. int _a_0_;
  91. int _a_1_;
  92. int _scalar_1_;
  93. int[_a_0_, _a_1_] a;
  94. _a_0_ = 2;
  95. _a_1_ = two();
  96. _scalar_1_ = two();
  97. a = __allocate(_a_0_, _a_1_);
  98. for (int _i_2 = 0, _a_0_) \{
  99. for (int _i_3 = 0, _a_1_) \{
  100. a[_i_2, _i_3] = _scalar_1_;
  101. \}
  102. \}
  103. ... v}
  104. The transformation described above is applied to both local and global array
  105. definitions. For exported global arrays, however, dimensions are renamed
  106. slightly differently to be able to consistently the dimensions in another
  107. module. The renaming rules for this case are described in {!Context}.
  108. {2 Transforming for-loops to while-loops}
  109. {v for (int i = <start>, <stop>, <step>) \{
  110. <body>
  111. \} v}
  112. is transformed into:
  113. {v _i_1 = <start>;
  114. _step_2 = <step>;
  115. _stop_3 = <stop>;
  116. while ((_step_2 > 0) ? (_i_1 < _stop_3) : (_i_1 > _stop_3)) \{
  117. <body>
  118. _i_1 = _i_1 + _step_2;
  119. \} v}
  120. Here, [_i_1], [_step_2] and [_stop_3] are fresh variables. Definitions of
  121. these new variables are added to the scope of the current function. Every
  122. occurrence of [i] in [<body>] is replaced with the fresh variable [_i_1],
  123. this prevents problems with nested for-loops that use the same induction
  124. variable.
  125. *)
  126. (** Main phase function, called by {!Main}. *)
  127. val phase : Main.phase_func