Aggregation pass
Problem we are trying to solve
Two dependant transformation are currently battling in the compiler: morphisation (module transformed to records) and monomorphisation (specialisation of polymorphic functions).
Those transformation are defined as "flying processes" meaning that they are not defined as self-passes but rather as routines that the caller (here, the build system) need to call in a specific order - keep in mind that monomorphization of functions requires actual application that might hide in a different module than the definition ... -
Solution
New stage
This MR defines a new stage: 6-ast_aggregated
. This stage is similar to 5-ast_typed
but without module constructs nor declarations.
Two new passes are added:
- Aggregation:
ast_typed
toast_aggregated
. - Self aggregated:
ast_aggregated
toast_aggregated
.
Aggregation
we do a "flattening" of modules, i.e. we eliminate all accessors, module constructors, etc.
this happens in src/passes/12-aggregation/aggregation.ml
Declaration aggregation
The program representation at this point is a little bit weird.. It consists of a chain of "let-ins" definitions ending with a "hole". This hole represents the result of the last binding of the chain: type 'a program = 'a -> expression
-> Ast_typed.expression -> expression
e.g. file
let a = 1
let b = 2
will be aggregated using Aggregation.compile_program : Ast_typed.module_fully_typed -> Ast_typed.expression Ast_aggregated.program
:
let a = 1 in
let b = 2 in
<hole>
To evaluate an arbitrary expression in this context we call Aggregation.compile_expression_in_context : Ast_typed.expression -> Ast_typed.expression Ast_aggregated.program -> Ast_aggregated.expression
. This would compile the given expression in the context of the program and produce a "flat" expression
Module flattening
Modules's declarations are "lifted at top-level" (?), module accesses are transformed into simple variables.
module A = struct
let a = 1
end
module B = struct
module A = A
let b = 1
end
let x = B.A.a
|->
let #A#a#193 = 1[@inline] in
let #B#b#194 = 1[@inline] in
let x = #A#a#193 in
<hole>
Self aggregated
We apply monomorphisation and provide the tools for checking that an expression is "object" ligo (these were previously in self_ast_typed
). Happens in src/passes/13-self_ast_aggregated/monomorphisation.ml
Side effects
- the REPL's state is simplified (at the
ast_typed
level) - the interpreter is simplified by adapting it to work at the level of
ast_aggregated
(no module support needed). - in self_mini_c, one condition for beta reduction has been modified:
(** This case shows up in the compilation of modules:
(let x = e1 in e2)@e3 ↦ let x = e1 in e2@e3 (only if e2 and e3 are pure??) *)
| E_application ({ content = E_let_in (e1, inline, ((x, a), e2)); _ }, e3) ->
if is_pure e2 || is_pure e3
then
let x' = Location.wrap (Var.fresh_like (Location.unwrap x)) in
let e2 = Subst.replace e2 x x' in
changed := true;
{e with content = E_let_in (e1, inline, ((x', a), {e with content = E_application (e2, e3)}))}
else e
this makes compilation faster since we don't have to inline every module declarations
Perfs
on a big contract using many modules ; most of the time passed in self-mini-c optimizations
ligo 31.0 | after this MR | |
---|---|---|
size | 38029 bytes | 34311 bytes |
time to compile | 4.8 sec | 3.5 sec |
-
has a changelog entry
Related issues
fix #1309 (closed) : after !1431 (9ca6eb00) , we don't need to inline declaration anymore