Optimizer

Using the Interpreter's tool set, the optimizer is able to modify the code structure by using multiple compiler optimization methods.

Constant Folding

By keeping track of defined constant variables, references to them are substituted with their actual value.

let x: i32 = 12;
return x;

Becomes

return 12;

This also works for binary operation that uses literal and/or constants.

let x: i32 = 10 + 5;
return x * 2;

Becomes

return 30;

Conditional Unwrapping

Using constant folding, Conditional branches that have constant value are pre-computed, the resulting body (if/else) is then inserted at its place. The other body is discarded.

let foo: mut f32 = 1.7;
let x: i32 = 1;

if (x) {
    foo = 12.2;
} else {
    foo = 4.6;
}

Becomes

let foo: mut f32 = 1.7;
foo = 12.2;

Returning Branch Unpacking

When entering an If statement that is guaranteed to return, its else branch (if any) is unpacked to the main body since the rest of the body will never be executed if the main branch of the If is entered.

if (foo == bar) {
    foo = 20;
    return 1;
} else {
    foo = 13;
}
bar = 20;
return 0;

Becomes

if (foo == bar) {
    foo = 20;
    return 1;
}
foo = 13;
bar = 20;
return 0;

Dead Code Elimination

Code that succeed a return statement is discarded as it will never be executed. This pairs well with returning branch unpacking as it can lead to mutiple return statements on the same branch.

bar = 10;
return 0;
let foo: mut i32 = 4;
while (foo > 0) {
    bar = bar * 2;
}

Becomes

bar = 10;
return 0;

Function Inlining

Functions that do simple operations (including those that are optimized as such) are inlined when used as expressions. The function definition itself is not discarded.

fn compute(a: i32, b: i32) -> i32
{
    let c: i32 = 12;
    return a * b * c;
}

let x: mut i32 = compute(10, 2);

Becomes

fn compute(a: i32, b: i32) -> i32
{
    return a * b * 12;
}

let x: mut i32 = 240;

Loop Unrolling

Some while loops can have their content unrolled into the main branch when specific conditions are met:

  • The condition of the while loop is constant expect a single mutable variable (named the loop variable)
  • The body of the loop modifies the loop variable in a constant way (i.e. i = i + 1 or i = compute(i, 13) assuming compute is an inlineable function)
  • The while in finite and has at most 16 loops
let i: mut i32 = 0;
let nb: mut i32 = 1;

while (i < 3) {
    nb = nb * nb + i;
    i = i + 1;
}

Becomes

let i: mut i32 = 0;
let nb: mut i32 = 1;

nb = nb * nb + i;
i = i + 1;
nb = nb * nb + i;
i = i + 1;
nb = nb * nb + i;
i = i + 1;