|
@@ -30,12 +30,13 @@ void foo() \{
|
|
|
|
|
|
|
|
{3 Array initialisations}
|
|
{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 in bracket
|
|
|
|
|
- notation. A scalar value is rewritten to a nested for-loop over all array
|
|
|
|
|
- dmensions, with an assignment in the most nested loop. An array constant is
|
|
|
|
|
- rewritten to a series of separate assign statements to the corresponding
|
|
|
|
|
- array indices. The following example shows both transformations:
|
|
|
|
|
|
|
+ A more complex class of initialisations is that of array initialisations.
|
|
|
|
|
+ Arrays can be initialised to a scalar value or to an array literal in
|
|
|
|
|
+ bracket notation. A scalar value is rewritten to a nested for-loop over all
|
|
|
|
|
+ array dmensions, with an assignment in the most nested loop. An array
|
|
|
|
|
+ constant is rewritten to a series of separate assign statements to the
|
|
|
|
|
+ corresponding array indices. The following example shows both
|
|
|
|
|
+ transformations:
|
|
|
{v void foo() \{
|
|
{v void foo() \{
|
|
|
int[3] a = 4;
|
|
int[3] a = 4;
|
|
|
int[2, 2] b = [[3, 4], [5, 6]];
|
|
int[2, 2] b = [[3, 4], [5, 6]];
|
|
@@ -62,9 +63,9 @@ void foo() \{
|
|
|
example to maintain readability.
|
|
example to maintain readability.
|
|
|
|
|
|
|
|
Note that array constants in bracket expressions must have a nesting level
|
|
Note that array constants in bracket expressions must have a nesting level
|
|
|
- that is equal to the number of array dimensions, or an error will occur.
|
|
|
|
|
|
|
+ that is equal to the number of array dimensions, else an error will occur.
|
|
|
|
|
|
|
|
- {2 Prevent incorrect double evaluation}
|
|
|
|
|
|
|
+ {2 Move array dimensions and scalars into new variables}
|
|
|
|
|
|
|
|
In the following code:
|
|
In the following code:
|
|
|
{v int twos = 0;
|
|
{v int twos = 0;
|
|
@@ -89,44 +90,44 @@ void foo() \{
|
|
|
and array dimensions, and replacing the original expression with the
|
|
and array dimensions, and replacing the original expression with the
|
|
|
generated variables. Note that these variables are marked so-called
|
|
generated variables. Note that these variables are marked so-called
|
|
|
"constant variables" since they are known to be assigned exactly once, and
|
|
"constant variables" since they are known to be assigned exactly once, and
|
|
|
- thus optimizable by {!Constprop} in some cases. This way, only the
|
|
|
|
|
- non-constant expressions are defined in new variables in the resulting code.
|
|
|
|
|
|
|
+ thus likely optimizable by {!Constprop}. This way, only the non-constant
|
|
|
|
|
+ expressions are defined in new variables in the final code.
|
|
|
|
|
|
|
|
In the example above, [int[2, two()] a = two();] is transformed as follows:
|
|
In the example above, [int[2, two()] a = two();] is transformed as follows:
|
|
|
{v ...
|
|
{v ...
|
|
|
- int _a_1_1_ = 2; // will be propagated back by constant propagation
|
|
|
|
|
- int _a_2_2_ = two();
|
|
|
|
|
- int _scalar_3_ = two();
|
|
|
|
|
- int[_a_1_1_, _a_2_2_] a = _scalar_3_;
|
|
|
|
|
|
|
+ int _a_0_ = 2; // 2 will be propagated back by constant propagation
|
|
|
|
|
+ int _a_1_ = two();
|
|
|
|
|
+ int _scalar_1_ = two();
|
|
|
|
|
+ int[_a_0_, _a_1_] a = _scalar_1_;
|
|
|
... v}
|
|
... v}
|
|
|
resulting in:
|
|
resulting in:
|
|
|
{v ...
|
|
{v ...
|
|
|
- int _a_1_1_;
|
|
|
|
|
- int _a_2_2_;
|
|
|
|
|
- int _scalar_3_;
|
|
|
|
|
- int[_a_1_1_, _a_2_2_] a;
|
|
|
|
|
- _a_1_1_ = 2;
|
|
|
|
|
- _a_2_2_ = two();
|
|
|
|
|
- _scalar_3_ = two();
|
|
|
|
|
- a := <allocate>(_a_1_1_, _a_2_2_);
|
|
|
|
|
- for (int _i_4 = 0, _a_1_1_) \{
|
|
|
|
|
- for (int _i_5 = 0, _a_2_2_) \{
|
|
|
|
|
- a[_i_4, _i_5] = _scalar_3_;
|
|
|
|
|
|
|
+ int _a_0_;
|
|
|
|
|
+ int _a_1_;
|
|
|
|
|
+ int _scalar_1_;
|
|
|
|
|
+ int[_a_0_, _a_1_] a;
|
|
|
|
|
+ _a_0_ = 2;
|
|
|
|
|
+ _a_1_ = two();
|
|
|
|
|
+ _scalar_1_ = two();
|
|
|
|
|
+ a := <allocate>(_a_0_, _a_1_);
|
|
|
|
|
+ for (int _i_2 = 0, _a_0_) \{
|
|
|
|
|
+ for (int _i_3 = 0, _a_1_) \{
|
|
|
|
|
+ a[_i_2, _i_3] = _scalar_1_;
|
|
|
\}
|
|
\}
|
|
|
\}
|
|
\}
|
|
|
... v}
|
|
... v}
|
|
|
|
|
|
|
|
- The [_a_1_1_] here is formed from the array name [a], the number of the
|
|
|
|
|
- dimension [1], and a global counter variable that happened to be [1] at the
|
|
|
|
|
- moment he variable was generated. The counter is necessary to make the
|
|
|
|
|
- variable name unique, even when the program contains illegal name clashes,
|
|
|
|
|
- which would yield weird errors during context analysis. E.g., a second
|
|
|
|
|
- definition [int[2] a;] would generate a new variable [_a_1] which would
|
|
|
|
|
- clash with the earlier [_a_1], yielding an error on compiler-generated code
|
|
|
|
|
- instead of on the definition of [a].
|
|
|
|
|
-
|
|
|
|
|
- Note that the for-loops are actually transformed into while-loops, but not
|
|
|
|
|
- in this example in order to maintain readability.
|
|
|
|
|
|
|
+ The transformation described above is applied to all array definitions,
|
|
|
|
|
+ including extern arrays. Although dimensions of extern arrays are not
|
|
|
|
|
+ expressions (but identifiers), the transformation is necessary in order to
|
|
|
|
|
+ generate consistent names to be imported/exported. E.g. in [int[n] a], [n]
|
|
|
|
|
+ is just a name given locally to the first dimension of [a]. Therefore it is
|
|
|
|
|
+ transformed into:
|
|
|
|
|
+{v extern int _a_0_;
|
|
|
|
|
+ int[_a_0_] a; v}
|
|
|
|
|
+ Also, all occurrences of [n] in the rest of the module are replaced by
|
|
|
|
|
+ [_a_0_]. For exported arrays, the generated dimension variables need to be
|
|
|
|
|
+ exported as well.
|
|
|
|
|
|
|
|
{2 Transforming for-loops to while-loops}
|
|
{2 Transforming for-loops to while-loops}
|
|
|
|
|
|