Intermediate code generation
Lecture 14
Table of Contents
Review our intermediate language
Examples of intermediate code output
Pick some constructs to look at
Patterns of code generation
- Could hand-write some assembly to match
- How can we describe the translation for any source program?
Describe translations in terms of our grammar
- Use a
semantic action
- Code embedded on the right-hand-side of a production
- Conceptually executed during parsing
stmt: 'input' ID ';' # Input ; // emit(String.format("INPUT %s", ctx.ID()));
Add a snippet of code describing how to translate an input statement
emit
is just a fancy term for printing the target language
Easy in this case because we have the information right in the ID token and it corresponds almost exactly to the intermediate code
What about nested constructs?
stmt: 'output' expr ';' # Input ; // emit(String.format("OUTPUT %s", ???))
There is no one-to-one correspondence for the output construct, because expression is a non-terminal that can represent lots of different source code.
Recursively translate the source language
- Like "evaluating" the language
- But returning intermediate code instead of a value
Where have we seen this recursive, grammar-based strategy for language-processing before?
What implementation technique have we seen that we can reuse here?
Semantic actions work like visitors
- Emit code sequentially, i.e., a traversal
stmt: 'output' expr ';' # Input ; // emit(String.format("OUTPUT %s", ???)) visitOutputStatement(ctx) { visit(ctx.expr(); emit(String.format("OUTPUT %s", ???)); }
The trick is ensuring the child construct's code fits the parent's (sometimes called composition).
Make sure the emitted code for children fits correctly in all cases
Ensuring that the construct's translations fit together can be called composition
What actually goes into the OUTPUT command?
Let's try some examples
output 1; output x + 2;
How do we get a parameter for the OUTPUT opcode?
Associate values with symbols
- Retain a
semantic value
accessible by the semantic action- Store resulting value on child node for use by parent
stmt: 'output' expr ';' # Input ; // emit(String.format("OUTPUT %s", expr.value)) visitOutputStatement(ctx) { String tempname = visit(ctx.expr(); emit(String.format("OUTPUT %s", tempname)); }
Note that visit isn't actually called in a semantic action. Semantic actions are a way to specify code that (effectively) executes during parsing, which is in essence like a visitor, since it can assume the child's value is populated when executed. There are some finer differences between semantic actions and visitors, such as processing direction (bottom-up vs. top-down or both). See the dragon book chapter 5 on syntax-directed translation for more details.
Developing the code generator
We need two things
- A specification of how each construct should be translated
- An algorithm for implementing the translation
How do we know our specification is correct?
Will all source programs make the equivalent target program?
What does equivalence mean here?
Let's develop some of the specification together
- Keep the specs on hand