[bitc-dev] Polymorphism through polyinstantiation -- take2
Swaroop Sridhar
swaroop.sridhar at gmail.com
Fri Jul 28 00:11:29 EDT 2006
The take-1 algorithm had a few drawbacks:
i) It forms the bigAST from all the definitions known in the entire
program, rather than doing it `on-demand' one-output point at a time. In
this sense, it is one-shot-compiler friendly and may not scale to an
interactive interpreter.
ii) It mutates the big-AST in place, and is thus not "functional" in
style -- something that is much desired keeping the next version of the
compiler in mind.
iii) It needs a post-instantiation dependency analysis and re-ordering
as the definitions are not emitted on a demand-driven basis.
To ameliorate these problems, a new algorithm is proposed:
The idea is to divide the compiler into:
frontend: parser, type-inference
midend: the (poly)instantiator
backend: closure-conversion, SSA transformation, C-emission.
The front end is done one unit of compilation at a time, or, in an
interactive environment, one top level form at a time. The front end
pulls in other dependent interfaces and also processes them. After the
frontend is completely done, the midend comes into play. It receives a
set of FQNs that must be instantiated in this pass. It starts out from
these as roots and constructs the unified-AST (which is a smaller
version of the big-AST) on an on-demand basis. Then, it passes this
unified-AST to the backend, which can again process this as a single
unit of compilataion.
Below, I give the algorithm for specialization used in our new scheme.
In this scheme, there is no todo-list -- the algorithm recursively acts
on itself. However, it maintains a work-list to ensure that recursive
definitions do not cause the instantiator to go into an infinite loop.
As is practice, it takes in a definition and the desired type.
1) See if the desired identifier has been instantiated for the desired
type by searching the target (unified-AST) environment. If found,
just return.
2) Examine the worklist whether we are in the process of instantiating
the current definition, if so emit a declaration, and exit.
3) Copy the incoming AST -- always copy the definition (rather than the
declaration) if one exists.
4) Clamp the type of this AST to the desired type by introducing a
qualifier.
ex, to instantiate id to (fn (bool) bool), we write:
(define id:(fn (bool) bool) (lambda (x) x))
5) Mangle the name of the identifier being defined depending on the
desired type, and also change any recursive usages of the same name
within the current definition.
ex: (define id#fn_4bool_4bool:(fn (bool) bool) (lambda (x) x))
6) R&T the new definition in the *original* environment -- that was
saved as a pointer in its AST -- so that the types in the body of the
definition get clamped aiding further instantiation. After successful
R&T, throw away any changes to the environment.
7) Add the mangled name (id#fn_bool_bool) onto the worklist.
8) Recursively instantiate the body
9) After the current form has been completely instantiated, add it to
the target (big-AST) environment, and R&T there.
10) Remove the current definition from the worklist, exit.
In the next mail, I will post a small problem and then a solution to it
(obviously, take-3).
Swaroop.
More information about the bitc-dev
mailing list