300 likes | 431 Vues
In this lecture, we explore Project 3, focusing on the IA32 module and C calling conventions. Due June 7, 2007, at 5:00 PM, this project requires translating Intermediate Representation (IR1) to x86 assembly, which involves handling local, instance, and temporary variables interfacing with memory. We will define the roles of the caller and callee in function calls, including saving registers, stack management, and return value handling. Key concepts include memory addressing, frame pointers, and the various types of variables in x86 assembly.
E N D
Lecture #15, May 22, 2007 • Project 3 • C calling convention • The IA32 module • Translating
Project #3 • In class today we describe project #3 • It is due Thursday, June 7, 2007 at 5:00 PM • this is in 17 days. • In order to get the course graded, there will be no extensions • Final exam will be week of June 11 • Monday June 11
Properties of the X86 translation • In project 3 we translate IR1 to x86 assembly. • Because of the sparseness of the X86 register set we assume variables all live in memory. • We have a number of types of variables • local variables (VAR n) • instance variables (MEMBER(address,n)) • parameters (PARAM n) • temporaries (TEMP n) • How do we access these variables? • Where in memory do they live?
C calling convention • Callers Job • Before the call • Save any registers that might be needed later • push the n arguments in reverse order • call the function • after the call • remove the arguments from the stack • restore any saved registers • return value is in register %eax • Callees Job • Set up the framepointer (%ebp) • allocate space for local variables on the stack • reset the old framepointer • put return value in %eax • return
. . . arg0 higher addresses → return address argn arg1 The X86 stack • Our translation is faithful to the calling convention of the GNU compiler. • The stack on entry to a function looks as follows: %esp
return address var1 argn old %ebp var0 . . . temp1 arg0 higher addresses → Allocating room for locals and temps • Temporaries and locals are treated identically • One exception locals may have some initialization code • Everything is reachable via the frame pointer (%ebp) %ebp %esp return address 4(%ebp) arg0 8(%ebp) arg1 12(%ebp) var0 -4(%ebp) var1 -8(%ebp)
prolog Call prolog precall postcall Return epilog epilog Procedure Call • Has 4 parts • Precall • Postreturn • Prolog • Epilog
Caller - Precall • Save registers that need it • write them to memory • save them on the stack • Push arguments on the stack • pushL arg2 • pushL arg1 • pushL arg0 • call the function • call _malloc
Caller - Postcall • Remove n arguments from stack • addl $(4*n), %esp • Pop saved registers if any
. . . arg0 higher addresses → return address argn arg1 Callee -- Prolog • Set up the framepointer (%ebx) • pushL %ebp # save the old framepointer • movl %esp,%ebp # initialize new framepointer • allocate space for local variables on the stack • subl $(n*4),%esp #subtract from spackpointer %esp
return address var2 argn old %ebp var1 . . . temp1 arg0 higher addresses → Callee -- Epilog • put return value in %eax • movL ans,%eax • reset the old framepointer • movl %ebp,%esp • popl %ebp • return • return %esp %ebp
The IA32 module • We build some data structures to represent IA32 code as SML data. • Registers • represent the machine registers • Modes • represent the addressing modes • Instructions • represent instructions • every instruction has room for a label and a comment
Registers datatype Register = eax (* Accumulator *) | ebx (* Base *) | ecx (* Count *) | edx (* Data *) | esi (* Source index *) | edi (* Destination index *) | ebp (* Base pointer *) | esp (* Stack Pointer *) | eip (* Instruction pointer *) | eflag (* Flags *)
Modes type Label = string; datatype Mode = Mem of string (* top *) | % of Register (* %eax *) | $ of int (* $12 *) | & of (int * Mode); (& n(%eax) *)
Instructions datatype IA32 = Movl of (Label * Mode * Mode * string) | Xchgl of (Label * Mode * Mode * string) | Addl of (Label * Mode * Mode * string) | Subl of (Label * Mode * Mode * string) | Imull of (Label * Mode * Mode * string) | Andl of (Label * Mode * Mode * string) | Orl of (Label * Mode * Mode * string) | Xorl of (Label * Mode * Mode * string) | Cmpl of (Label * Mode * Mode * string) | Idivl of (Label * Mode * string) | Negl of (Label * Mode * string) | Notl of (Label * Mode * string) | Incl of (Label * Mode * string) | Decl of (Label * Mode * string) | Pushl of (Label * Mode * string) | Popl of (Label * Mode * string) | Jmp of (Label * Label * string) | Jz of (Label * Label * string) | Jnz of (Label * Label * string) | Jl of (Label * Label * string) | Jnl of (Label * Label * string) | Jg of (Label * Label * string) | Jng of (Label * Label * string)
lower case instuctions fun movl (m1,m2) = Movl("",m1,m2,""); fun xchgl(m1,m2) = Xchgl("",m1,m2,""); fun addl (m1,m2) = Addl("",m1,m2,""); fun subl (m1,m2) = Subl("",m1,m2,""); fun imull(m1,m2) = Imull("",m1,m2,""); fun andl (m1,m2) = Andl("",m1,m2,""); fun orl (m1,m2) = Orl("",m1,m2,""); fun xorl (m1,m2) = Xorl("",m1,m2,""); fun cmpl (m1,m2) = Cmpl("",m1,m2,""); fun idivl(m1) = Idivl("",m1,""); • etc
Adding Labels fun addLabel l x = case x of Movl (_,m1,m2,s) => Movl (l,m1,m2,s) | Xchgl(_,m1,m2,s) => Xchgl(l,m1,m2,s) | Addl (_,m1,m2,s) => Addl (l,m1,m2,s) | Subl (_,m1,m2,s) => Subl (l,m1,m2,s) | Imull(_,m1,m2,s) => Imull(l,m1,m2,s) | Andl (_,m1,m2,s) => Andl (l,m1,m2,s) | Orl (_,m1,m2,s) => Orl (l,m1,m2,s) | Xorl (_,m1,m2,s) => Xorl (l,m1,m2,s) | Cmpl (_,m1,m2,s) => Cmpl (l,m1,m2,s)
Adding Comments fun addComment s x = case x of Movl (l,m1,m2,_) => Movl (l,m1,m2,s) | Xchgl(l,m1,m2,_) => Xchgl(l,m1,m2,s) | Addl (l,m1,m2,_) => Addl (l,m1,m2,s) | Subl (l,m1,m2,_) => Subl (l,m1,m2,s) | Imull(l,m1,m2,_) => Imull(l,m1,m2,s) | Andl (l,m1,m2,_) => Andl (l,m1,m2,s) | Orl (l,m1,m2,_) => Orl (l,m1,m2,s) | Xorl (l,m1,m2,_) => Xorl (l,m1,m2,s) | Cmpl (l,m1,m2,_) => Cmpl (l,m1,m2,s)
Translation scheme • Translate every IR.Exp into a IA32 list • Simple translation scheme • Every translation of an Expression leaves the answer in the %eax register.
Getting started fun compileE exp = case exp of BINOP(ADD,x,y) => let val xCode = compileE x val yCode = compileE y in xCode @ [ pushl(%eax) ] @ yCode @ [ popl(%ebx), addl(%ebx,%eax) ] end | CONST(s,typ) => let val n = valOf(Int.fromString s) in [ movl($n,%eax) ] end | NAME s => [ movl(Mem s,%eax) ]
Translating function calls fun compileE exp = case exp of | CALL(NAME f,args) => let fun pushargs [] = [] | pushargs (x::xs) = (pushargs xs) @ (compileE x) @ [ pushl (%eax) ] val n = length args in (pushargs args) @ [call f] @ [addl($(wdsize*n),%esp)] end
Translating Statements fun compileS x = case x of MOVE(dest,src) => (compileE src)@ [pushl (%eax)]@ (address dest)@ [popl (%ebx),Movl("",%ebx,&(0,%eax), sSTMT x)] | JUMP n => [Jmp("",label32 n,"")]
return address var2 argn old %ebp var1 . . . temp1 arg0 higher addresses → Translating addresses fun address (VAR n) = [movl(%ebp,%eax) , addl ($(~(n + wdsize)),%eax)] | address (PARAM n) = [movl(%ebp,%eax) ,addl ($( 2*wdsize + n),%eax) ] %esp %ebp
Translating Function definitions fun compileFunc (FUNC(nm,_,vs,ss)) = let fun size((typ),x) = ProgramTypes.typeSize typ + x val n = foldr size 0 vs in [ Pushl(nm,%ebp,"Entering "^nm) , movl (%esp,%ebp) , subl($ n, %esp) ] @ (compileSS ss) @ [ Movl("",%ebp,%esp,"Default Epilog") , popl (%ebp) , return() ] end; Prolog Epilog
Example class T { int instance2 = 0; public int f(int j) { int k = 1; return (j+k); } }
IR1 code ================================= The Class Table with inherited instance variables: class Object has vars: class T has vars: 0: int instance2 := 0 ================================= T_f(int P1) int V0; V0 := 1 return MEM(P1) + MEM(V0)
IA32 code T_f: pushl %ebp # Entering T_f movl %esp,%ebp subl $4,%esp movl $1,%eax pushl %eax movl %ebp,%eax addl $-4,%eax popl %ebx movl %ebx,0(%eax) # V0 := 1 movl %ebp,%eax addl $12,%eax movl 0(%eax),%eax # P1 pushl %eax movl %ebp,%eax addl $-4,%eax movl 0(%eax),%eax # V0 popl %ebx addl %ebx,%eax movl %ebp,%esp # Epilog popl %ebp return movl %ebp,%esp # Default Epilog popl %ebp return
Using the assembler • We will write some code in class.
What to turn in • You should hand in the module Phase3.sml • It should include a function • compileFunc :: IR1.FUNC list -> IA32.IA32 list • You should also write a function • toplevel :: string -> string -> unit • toplevel src dest • parses and compiles src to IA32 list • Then prints it out as assembly code to file dest
The template • I will supply a template. • The template will provide drivers and a complete solution to projects 1 and 2. • I will supply a file runtime.c • you will link your code with this file • You may ask for the template by emailing me. • I have posted the IA32 code on the web page.