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) {
  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

Author: Paul Gazzillo

Created: 2022-03-14 Mon 14:28