Download as pdf or txt
Download as pdf or txt
You are on page 1of 19

Compilation 2014

Code Generation Issues

Aslan Askarov

aslan@cs.au.dk



Based on slides by E. Ernst
Administrativia

• November 28 – guest lecture by Filip Sieczkowski (AU)

• Memory Models

• December 12 – guest lecture by Kevin Millikin (G)

• Register allocation in JIT compilers

• The very last hand-in “Putting it all together” is due on


December 10

• No new functional requirements, just fix your bugs


and resubmit
Code generation issues

• Until now we have discussed Tiger on an abstract platform

• In reality, it needs to run on a concrete one

• Need consider

• Concrete assembly language

• Calling conventions

• Transformation from abstract assembly to concrete


assembly

• Note: no textbook reference here, but check out linked


material on x86 assembly
The x86 assembly language

• Specifies machine instructions for a large family of CPUs – we


choose the 32 bit subfamily

• Only very little abstraction

• Symbolic addresses (crucial!)

• Choice among jumps (please ignore)

• Generation of meta/debug information (please ignore)

• Checks several things

• “This is actually a MOVE” (bit-pattern could mean anything)

• “MUL is actually capable of operating on that register”

• Syntax not standardized: we use AT&T syntax that is


compatible with gcc inline code
The x86 assembly language

• Example instruction: move $-42, 8(%ebp)


• General format: <instr><S> <src>, <dst>
• <S> ∈ {b,s,w,l,q,t} specifies size (l: 32 bit)

• $ indicates literal number

• % indicates register

• o(%r1, %r2, n) means o + %r1 + %r2 * n

• Example label: mylabel

• gives the “current address” the name ‘mylabel’

• Example directives:

• .text .data .globl .ascii .asciz



.size .func .type .endfunc
The x86 assembly language

• Assembler produces object file that contains


segments (address ranges with a purpose)

• Segments containing runnable code: .text

• Segments containing initialized data: .data


• Remember to specify segment to fit purpose

• Specifying data:

• .ascii .asciz
• Metadata:

• .func .type .endfunc .size .globl


• For examples: check provided *.s
The x86 assembly language
.text
# PROCEDURE tigermain
.globl tigermain
.func tigermain
.type tigermain, @function
tigermain:
# FRAME tigermain(1 formals, 10 locals)
pushl %ebp
movl %esp, %ebp
subl $44, %esp
# SP, FP, calleesaves, argregs have values
L2_blocks: # x86gen:122
movl %ebp, -8(%ebp) # x86gen:246 x86frame:575
movl -8(%ebp), %ebx # x86gen:251 x86frame:367
addl $-4, %ebx # x86gen:251 x86frame:372
. ..
movl -28(%ebp), %eax # x86gen:117 x86frame:582
jmp L1_block_done # x86gen:172
L1_block_done: # x86gen:122
# FP, SP, RV, calleesaves still live
leave
ret
.size tigermain, .-tigermain .data
L0_string:
.endfunc
.long 13
# END tigermain
.asciz "DefaultString"
The x86 assembly language
• A reasonable selection of instructions

addl $17, %esp jne Label


addl %eax, %ebx leave
call Label movl $17, %eax
cltd movl $Label, %eax
cmpl $17, %ecx movl %eax, %ebx
cmpl %eax, %edx movl %eax, 17(%ebp)
idivl %ebx movl %esp, %ebp
imull %eax movl 17(%ebp), %eax
je Label negl %eax
jg Label pushl %eax
jge Label pushl %ebp
jl Label ret
jle Label subl $17, %esp
jmp Label subl %eax, %ebx
Calling Convention (cdecl)
• For practical reasons, gcc is used to do linking

• includes startup symbols/code

• easy integration with C (e.g., runtime.c, showstack.c)

• convenient calling convention

• Must use gcc -static .. to allow debugging


• Avoid ‘smart’ options (-fomit-frame-pointer)

• Conventions

• all parameters passed on stack (‘push’)

• last parameter pushed first, .., return address last

• return value passed in %eax


• caller cleans up stack
Recall Tiger frame slots
old FP (prev.frame) Memory[saved_FP], Memory[staticLink]?
...
arg_k Memory[FP+4*(2+k)]
...
arg_1 Memory[FP+4*(2+1)]
staticLink Memory[FP+8]
returnAddr Memory[FP+4]

saved_FP Memory[FP] Top of frame:


FP
localVar_1 Memory[FP-4*1] known early
...
localVar_m Memory[FP-4*m]
temp_1 Memory[FP-4*(m+1)]
...
temp_p Bottom: known
Memory[FP-4*(m+p)]
... later
SP (args for next)
SP Memory[FP-who_cares]
SP
SP
Calling Conventions at Work
• Consider an invocation of the function g(_) from the
function f(…)
L33_f:
.. .
• Reverse-push parameters
pushl %ebx
pushl %ebp
• Push static link
call L3_g
• Call (clean up after return)
addl $8, %esp
.. .

• Callee sets up stack frame


L3_g:
pushl %ebp
• ends by destructing it again movl %esp, %ebp
subl $44, %esp
.. .
leave
ret
From Abstract to Concrete Assembly

• Maximal Munch will generate instructions, but


abstract

• Abstract instructions: Like concrete ones, but


using an unlimited supply of ‘temporaries’

• Register allocation: Reusing registers as much as


possible, then spill

• We will just spill!


Generated Code
• Note dependencies: Each instruction uses some
temporaries and defines others (or the same)

.. .
| munchStm (T.MOVE (T.TEMP t, T.CALL (T.NAME l, args))) =
( emit (A.OPER { assem = "\tcall " ^ S.name l
, src = munchArgs args
, dst = F.calldefs
, jump = NONE
, doc = "x86gen:68"})
; emit (freeArgs (length args))
; emit (moveInstr F.EAX t "70"))

• In 2-op instr. (e.g. addl), first src is implicit dependency


emit (A.OPER { assem = "\taddl `s1, `d0"
, src = [r, munchExp e2] (* old-r used *)
, dst = [r]
, jump = NONE
, doc = "x86gen:270"})
From Abstract to Concrete Assembly
• Actual source code examples:
tigermain:
tigermain:
pushl %ebp
pushl %ebp
movl %esp, %ebp
movl %esp, %ebp
subl $44, %esp
subl $44, %esp
L2_blocks:
L2_blocks:
movl %ebp, -8(%ebp)
movl %ebp, t111
movl -8(%ebp), %ebx
addl $-4, t111
addl $-4, %ebx
movl t111, t110
movl %ebx, -8(%ebp)
movl $0, t112
movl -8(%ebp), %ebx
pushl t112
movl %ebx, -20(%ebp)
movl $10, t113
movl -12(%ebp), %ebx
pushl t113
movl $0, %ebx
call initArray
movl %ebx, -12(%ebp)
...
movl -12(%ebp), %ebx
pushl %ebx
movl -16(%ebp), %ebx
movl $10, %ebx
movl %ebx, -16(%ebp)
movl -16(%ebp), %ebx
pushl %ebx
call initArray
...
Spilling: Basic Idea
• Starting point: instruction using input/output

• Use instead: same instruction, in/out via registers

• Add instructions to move data to/from those


registers

src
bloopl
dst

move bloopl move


src R1 R2 dst
Spilling: Implementation

• Starting point: instruction i using [s0], []

• Use instead: A.OPER variant

• Add movl instruction to move data into

fun expand (A.OPER {src=s0::ss, jump = SOME (j::js), ...}) =


raise Bug "Encountered OPER that uses temps and jumps"
| expand (i as (A.OPER {src=[], dst=[], ...})) = [i]
| expand (i as A.OPER {assem, src=[s0], dst=[], jump, doc}) =
if isRegister s0 then [i]
else (* s0 other temp *)
[ A.OPER { assem = "\tmovl " ^ ofs s0 ^ "(%ebp), `d0"
, src = []
, dst = [EBX]
, jump = NONE
, doc = doc ^ " x86frame:265"}
, A.OPER { assem = assem
, src = [EBX]
, dst = []
, jump = jump
, doc = doc ^ " x86frame:270"}]
. ..
Spilling: Implementation
• Invariant maintained: Registers are initialized and
used locally in snippet of code for one abstract
instruction — so there are no conflicts

• Note special casing with registers


. . .
| expand (i as A.OPER {assem, src=[s0], dst=[], jump, doc}) =
if isRegister s0 then [i]
else (* s0 other temp *)
[ A.OPER { assem = "\tmovl " ^ ofs s0 ^ "(%ebp), `d0"
, src = []
, dst = [EBX]
, jump = NONE
, doc = doc ^ " x86frame:265"}
, A.OPER { assem = assem
, src = [EBX]
, dst = []
, jump = jump
, doc = doc ^ " x86frame:270"}]
. . .
Summary

• Abstract platform (Jouette) now dropped

• Choice: x86, 32 bit

• Assembly language: safer than machine code

• Assembly instruction format, labels, directives

• The notion of a segment

• Actual assembly: check out test0?.s

• An example required set of instructions

• Calling conventions: cdecl

• Transformation abstract/concrete assembly code


References

• Assembly directives: see ‘as’ manual

• https://sourceware.org/binutils/docs/as/index.html

• Sign extension (CTLD):

• https://en.wikipedia.org/wiki/Sign_extension

You might also like