87 lines
3.8 KiB
Markdown
87 lines
3.8 KiB
Markdown
|
# generator
|
||
|
|
||
|
# Responsibilities
|
||
|
|
||
|
Given a compilation target, turn a well-formed FSPL semantic tree into an LLVM
|
||
|
IR module tree.
|
||
|
|
||
|
## Organization
|
||
|
|
||
|
Generator defines the Target type, which contains information about the system
|
||
|
that the program is being compiled for. The [native sub-package](native) uses
|
||
|
Go's conditional compilation directives to provide a default Target that matches
|
||
|
the system the compiler has been natively built for.
|
||
|
|
||
|
The entry point for all logic defined in this package is Target.Generate(). This
|
||
|
method creates a new generator, and uses it to recursively generate and return an
|
||
|
LLVM module. The details of the generator are hidden from other packages, and
|
||
|
instances of it only last for the duration of Target.Generate().
|
||
|
|
||
|
The generator contains a stack of blockManagers, which plays a similar role to
|
||
|
[analyzer](../analyzer).scopeContextManager, except that the stack of
|
||
|
blockManagers is managed directly by the generator, which contains appropriate
|
||
|
methods for pushing/popping them.
|
||
|
|
||
|
Like the analyzer, the generator greedily generates code, and one function may
|
||
|
be generated in the middle of the generation process of another function. Thus,
|
||
|
each blockManager is tied to a specific LLVM function, and is in charge of
|
||
|
variables/stack allocations and to a degree, control flow flattening
|
||
|
(specifically loops). It also embeds the current active block, allowing for
|
||
|
generator routines to call its methods to add new instructions to the current
|
||
|
block, and switch between different blocks when necessary.
|
||
|
|
||
|
## Operation
|
||
|
|
||
|
When Target.Generate() is called, a new generator is created. It is given the
|
||
|
semantic tree to generate, as well as a copy of the Target. All data structure
|
||
|
initialization within the generator happens at this point.
|
||
|
|
||
|
Then, the generate() method on the newly created generator is called. This is
|
||
|
the entry point for the actual generation logic. This routine is comprised of
|
||
|
two phases:
|
||
|
|
||
|
- Function generation
|
||
|
- Method generation
|
||
|
|
||
|
You'll notice that there is no step for type generation. This is because types
|
||
|
are generated on-demand in order to reduce IR clutter.
|
||
|
|
||
|
## Expression Generation
|
||
|
|
||
|
Since expressions make up the bulk of FSPL, expression generation makes up the
|
||
|
bulk of the code generator. The generator is able to produce expressions in one
|
||
|
of three modes:
|
||
|
|
||
|
- Location: The generator will return an IR register that contains a pointer to
|
||
|
the result of the expression.
|
||
|
- Value: The generator will return an IR register that directly contains the
|
||
|
result of the expression.
|
||
|
- Any: The generator will decide which of these two options is best for the
|
||
|
specific expression, and will let the caller know which was chosen, in case it
|
||
|
cares. Some expressions are better suited to returning a pointer, such as
|
||
|
array subscripting or member access. Other expressions are better suited to
|
||
|
returning a value, such as arithmetic operators and function calls.
|
||
|
|
||
|
It is important to note that generating a Value expression may return a pointer,
|
||
|
because *FSPL pointers are first-class values*. The distinction between location
|
||
|
and value generation modes is purely to do with LLVM. It is similar to the
|
||
|
concept of location expressions within the analyzer, but not 100% identical all
|
||
|
of the time.
|
||
|
|
||
|
Whenever an expression needs to be generated, one of the following routines is
|
||
|
called:
|
||
|
|
||
|
- generator.generateExpression()
|
||
|
- generator.generateAny()
|
||
|
- generator.generateVal()
|
||
|
- generator.generateLoc()
|
||
|
|
||
|
The generator.generateExpression() routine takes in a mode value and depending
|
||
|
on it, calls one of the other more specific routines. Each of these routines, in
|
||
|
turn, calls a more specialized generation routine depending on the specific
|
||
|
expression.
|
||
|
|
||
|
If it is specifically requested to generate a value for an expression with only
|
||
|
its location component defined or vice versa, generator.generateVal/Loc() will
|
||
|
automatically do the conversion.
|