cse 131b – compiler construction ii discussion 6: operations, branches and functions 2/21/2007
Post on 21-Dec-2015
214 views
TRANSCRIPT
Refresher
Remember to work in increments!
BEGIN
END.
.section “.text”
.align 4
.global mainmain:
set .SAVE.main, %g1save %sp, %g1, %sp
retrestore
.SAVE.main = -(92 + 0) & -8
Refresher
Remember to work in increments!
VAR x: INTEGER;
BEGIN
x := 99;
END.
.section “.data”
.align 4.varX: .word 0 ! Global label method
.section “.text”
.align 4
.global mainmain:
set .SAVE.main, %g1save %sp, %g1, %sp
set 99, %l0st %l0, [%fp-4] ! Temporary storageset .varX, %l0ld [%fp-4], %l1st %l1, [%l0]
retrestore
.SAVE.main = -(92 + 4) & -8 ! 4 bytes of temp storage
General Principles
Start off doing simple load-load-compute-store. Don’t worry about efficiency or speed, as you won’t get any points for those.
VarSTOs will always be somewhere in memory (offsets from %fp or %g6). If the VarSTO is a parameter, it is initially in the I-registers until you move it to it’s proper place (%fp+68, %fp+72, etc).
ExprSTOs will always be in some temporary memory location (offsets from %fp).
ConstSTOs shouldn’t be stored in the run-time memory. Just do a “set” instruction with the value when you need it.
Things to Know
We won't be passing more than 6 parameters to procedures (hence, allowing you to rely on the %o0-%o5/%i0-%i5 registers). For the purposes of procedures with receiver variables, we'll only pass up to 5 parameters (with the 6th parameter implicitly being "this").
We WILL test more than 4K local variables/temporary memory (as well as large arrays). Make sure your load/store instructions and save instructions can handle those cases where the constant is more than +/- 4K.
When you make labels for things, make sure you make them unique. Think of cases like a procedure in Oberon named "main" (this is legal). You should name-mangle procedure, variable, and loop labels with a "." in front of them, as Oberon won't allow names with a "." (and thus avoid colliding names).
Don't assume statements will be simple. We may having complex things like: Z := X + (Y + (Z + (W + (B + C)))) DIV (X + Y + A + B + C). Your compiler should easily scale if you properly do the load-load-compute-store scheme.
Phase 1 –Arithmetic Expressions
Given (assume x is in %fp-4):x := x + 7;
ld [%fp-4], %l0set 7, %l1add %l0, %l1, %l0st %l0, [%fp-8] ! Tmpld [%fp-8], %l0st %l0, [%fp-4]
Arithmetic Expressions
OK, that’s simple for a simple statement. What if we had a statement like this:
Z := ((A+B) + (C+D)) + ((E+F) + (X+Y))
Which registers do we use???
Method 1 – Simplicity
Doing load-load-compute-store will only require 3 registers at any given time.
Thus, even with that complicated example, you just load two operands, compute it, and store the result into some temporary memory location. Then, the registers are all free again for use.
You will need to store the temporary memory location you used in your ExprSTO so you can reference it later.
Method 2 – Register Allocation
Have some data structure that lets you know what registers are free and which are currently in use.
Every time you need a register, request one from the data structure, which will remove that register from the available list
When you are done with a register, let the data structure know it is available for use again.
This is a good method too!
Method 2 – Register Allocation
Make some class (RegClass) with: GetFreeReg() – returns an available register FreeReg(String r) – marks that “r” is available
You will need to store the allotted register in your ExprSTO so you can reference it later.
Method 3 – Clumsy/Bad!
We can take advantage of Java Strings and encapsulate chunks of assembly code within each ExprSTO.
This will require a fixed register approach – you will need to have registers that serve a specific purpose (ie, a specific register is the result of an operation, etc).
Very confusing to keep track of – avoid this unless you really feel it is necessary!
Method 3 – Clumsy/Bad!
Always put operands into %o0 and %o1 Useful for calling .mul and .div
Compute operation, and place result in %g1 Store the resulting assembly code string into the
ExprSTO, without outputting to the file When that ExprSTO is used in another Expression,
dump the stored code and make some small register moves.
Method 3 – Example
(a + b) + (x + y)ld a, %o0 ld x, %o0
ld b, %o1 ld y, %o1
add %o0, %o1, %g1 add %o0, %o1, %g1
{place code (a+b) here}
mov %g1, %l0
{place code (x+y) here}
mov %l0, %o0
mov %g1, %o1
add %o0, %o1, %g1
Methods are your Friend!
Consider adding methods to your STO’s that make generating assembly for certain cases easier:
GetAddress() – returns base/offset (ie, %fp – 4) GetValue() – will combine GetAddress with an
appropriate load instruction Etc.
Conditions – Branching
Given this:IF x THEN (* … *) END;
ld x, %l0cmp %l0, %g0 ! Compare with zerobe .IfL1 ! Opposite logicnop(* … *)
.IfL1:
Branching – Where to?
You will need to generate labels for your branch statements. These labels must be unique
A simple solution would be to use some prefix string (i.e., “.IfL”), and append some counter at the end: .IfL1, .IfL2, .IfL3, …
Branching – Label Stack
Consider you had:
IF X THENIF Y THEN (*…*) END;
END;
You will eventually need some sort of label stack to alleviate issues that arise from nested conditions.
Branching – Label Stack
IF X THEN – load X, compare, branch to L1, push L1 onto stack
IF Y THEN – load Y, compare, branch to L2, push L2 onto stack
(*…*)
END; - Pop L2 from stack and output label
END; - Pop L1 from stack and output label
Procedures
How to call a procedure? Ex: call foo
nop How to return from a procedure?
Ex: retrestore
How to return a value from a procedure? Ex: mov %l0, %i0
retrestore
Procedures – Example
The following can be generated just by parsing “PROCEDURE foo”:
.section “.text”
.align 4
.global .foo.foo:
set .foo.SIZE, %g1save %sp, %g1, %sp
Procedures – Example
Now, the body of the function:set 2, %l0 ! Put “2” in a reg.st %l0, [%fp-8] ! Store in tmp memld [%fp-8], %l0 ! Load from tmpst %l0, [%fp-4] ! Store expr into “x”ld [%fp-4], %i0 ! Put “x” in return
ret ! Return statementrestore
Procedures – Example
Lastly, now that we got to “END foo;”:
.foo.SIZE = -(92 + 8) & -8 ! Bytes of local and tmp vars
By leaving this to the end, you can also allocate extra stack space for intermediate expression storage if needed during the body of the function.
Large Example
VAR y : INTEGER;
PROCEDURE foo() : INTEGER;
VAR x : INTEGER;
BEGIN
x := y; (* note how this uses a global that is set at runtime *)
RETURN (x + 1);
END foo;
VAR z : BOOLEAN;
BEGIN
y := 4;
IF y < foo() THEN WRITE “yes\n”; z := TRUE; END;
WRITE z, NL;
END.
Large Example.section “.rodata”
.align 4
.I.s: .asciz “%d” ! General stuff we should always have handy
.NL.s: .asciz “\n” ! Good idea to output this when you open the file
.T.s: .asciz “TRUE”
.F.s: .asciz “FALSE”
.section “.text”
.align 4
.global .foo
.foo:
set .SAVE.foo, %g1
save %sp, %g1, %sp
! x := y;
ld [%g6-4], %l0 ! Load global y
st %l0, [%fp-8] ! Store in temporary mem
ld [%fp-8], %l0 ! Get expression to assign statement
st %l0, [%fp-4] ! Store result into local x
! RETURN (x+1);
ld [%fp-4], %l0
set 1, %l1 ! Use set for a const (ConstSTO or literal)
add %l0, %l1, %l2 ! Do addition
st %l2, [%fp-12] ! Store in temporary mem
ld [%fp-12], %l0 ! Get expression result from temp mem
mov %l0, %i0 ! Put result in %i0, which is return value for procedure
ret ! Do return
restore ! Restore register window
.SAVE.foo = -(92 + 12) & -8 ! We had 1 local var and 2 temporary locations 12 bytes
Large Example.section “.bss” ! Right before “main”, we go into BSS and set all the “globals”.
.align 4
.skip 4 ! Global z [%g6-8]
.skip 4 ! Global y [%g6-4]
.globals: ! Note how they are in reverse order. The easiest way to do this is to have a vector of VarSTOs
! that you add to every time you do a global VarDecl. Then, walk through it backwards right before “main”
.section “.text”
.align 4
.global main
main:
set .SAVEmain, %g1
save %sp, %g1, %sp
set .globals, %g6 ! One-time initialization of %g6 the be the base address of the globals
! y := 4;
set 4, %l0 ! Use set for integer literal
st %l0, [%fp-4] ! Store in temporary mem
ld [%fp-4], %l0 ! Get expression to assign statement
st %l0, [%g6-4] ! Store result into global y
! IF y < foo()
ld [%g6-4], %l0 ! Load global y
call .foo
nop
mov %o0, %l1 ! Get result of foo into %l1
mov %g0, %l2 ! Initialize boolean result to FALSE
cmp %l0, %l1 ! Compare the two
bge .L1 ! Opposite logic!
nop
Large Example
set 1, %l2 ! Change boolean result to TRUE if branch failed
.L1: ! Branch will jump to here, skipping setting to TRUE
st %l2, [%fp-8] ! Store y < foo() boolean result into temporary mem
ld [%fp-8], %l0 ! Get y < foo() result from temp mem
cmp %l0, %g0 ! Compare with FALSE
be .L2 ! Skip over code if FALSEnop
! WRITE “yes\n”;
.section “.rodata” ! Switch to read-only data segment (data segment also OK)
.L3: .asciz “yes\n” ! No align necessary for .asciz (since byte-array, no alignment restrictions)
.section “.text” ! Switch back to text segment
.align 4
set .L3, %o0 ! Used a unique global label to point to string literal
call printf
nop
! z := TRUE;
set 1, %l0 ! Set TRUE constant literal into register
st %l0, [%fp-12] ! Store into temp mem
ld [%fp-12], %l0 ! Get from result expression from temp mem
st %l0, [%g6-8] ! Store into global z
.L2: ! Skip-over label from IF stmt
Large Example! WRITE z, NL;
ld [%g6-8], %l0 ! Load global z
st %l0, [%fp-16] ! Store into temp mem
ld [%fp-16], %l0 ! Load expression result from temp mem
set .F.s, %o0 ! Default to print “FALSE”
cmp %l0, %g0 ! Since z is boolean, determine if we print TRUE or FALSE
be .L4 ! Skip over if FALSE
nop
set .T.s, %o0 ! Change to “TRUE” if z is TRUE
.L4: ! Branch over here
call printf ! Will print TRUE or FALSE depending on what %o0 was
nop
set .NL.s, %o0 ! Set NL literal for output
call printf ! Output newline
nop
ret ! Implicit return at end of every procedure (including main)
restore
.SAVEmain = -(92 + 16) & -8 ! No local vars and 4 temp locations (16 bytes)
When you run this, you should get:yesTRUE-bash-3.00$
What to do Next!
1. Continue planning out how you want to structure your project – good planning leads to an easier design in the long run.
2. Finish Phase 1.
3. Start of Phase 2.
4. Come to lab hours and ask questions.