11.3. GENERATING EXPRESSIONS 183
Finally, we need a way of mapping from the symbols in a program to
the assembly language code representing those symbols. For this, write a
function to generate the address of a symbol:
const char
*
symbol_codegen( struct symbol
*
s );
This function returns a string which is a fragment of an instruction,
representing the address computation needed for a given symbol. Write
symbol
codegen to first examine the scope of the symbol. Global vari-
ables are easy: the name in assembly language is the same as in the source
language. If you have a symbol structure representing the global variable
count:integer, then symbol
codegen should simply return count.
Symbols that represent local variables and function parameters should
instead return an address computation that yields the position of that lo-
cal variable or parameter on the stack. The groundwork for this was laid
in the typechecking phase, where you assigned each parameter and each
local variable a unique sequence number.
For example, suppose you have this function definition:
f: function void ( x: integer, y: integer ) =
{
z: integer = 10;
return x + y + z;
}
In this case, x has parameter position zero, y has position one, and z is
local variable zero. Now look back at Figure 10.5, which shows the stack
layout on the X86-64 processor. Parameter position zero is at the address
-8(%rbp), parameter one is at -16(%rbp), and then local variable zero
follows after that at -24(%rbp).
Given that, you can now extend symbol
codegen to return a string
describing the precise stack address of local variables and parameters,
knowing only its position in the stack frame.
11.3 Generating Expressions
The basic approach to generating assembly code for an expression is to
perform a post-order traversal of the AST or DAG, and emit one or more
instructions for each node. The main idea is to keep track of the registers
in which each intermediate value is stored. To do this, add a reg field to
the AST or DAG node structure, which will hold the number of a register
returned by scratch
alloc. As you visit each node, emit an instruction
and place into the reg field the number of the register containing that
value. When the node is no longer needed, call scratch free to release
it.
183