fspl/generator/README.md

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 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.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 perform the conversion.