SIR Parser
Parsing SIR from input text is a effectively divided into two stages:
- Running a Pest-generated parser to get the input text into a parse tree
- Running a dual type-checker/parser on the parse tree to generate SIR from it
The second part is the part actually implemented in the compiler.
Preprocessing
The parser effectively runs multiple stages of pre-processing instead of walking the parse tree a single time. This pre-processing enables use-before-definition at both the function and the block level.
Each time a function subtree is found, the function is declared and the function's body is put onto a work list, and once every function in the file is declared the parser goes through the list and actually parses the bodies (and properly defines the functions).
The same is done at the basic-block level inside function bodies to make it possible to branch to blocks that are defined after the branch instruction that targets them. This also makes it easier to preserve the block order that was parsed, even though technically this is irrelevant (as long as the entry block is in the right place).
What this actually means for the instruction parsing code is that all valid block/function names are known by the time they execute, so the names not existing can only mean that the input is invalid.
Names -> Values
The parser relies on the SSA property of the program to map names (e.g. %x
) back to
the actual values that it has generated for those names. While this property is checked (when
new values are introduced it's checked to make sure they did not exist before), it is checked
while instructions are being parsed.
When an instruction or block parameter is parsed, its name (referred to by a LocalIdent
) is
put into the resolver
map inside of SIRParser
. That entry is then mapped to the value referred
to by the name, so that later that value can be found.