CSE 131 B Compiler Construction II Discussion 6
CSE 131 B – Compiler Construction II Discussion 6: Operations, Branches and Functions 2/21/2007
Overview w Phase 1 w Some Phase 2
Refresher w Remember to work in increments!. section. align. global BEGIN END. “. text” 4 main: set save . SAVE. main, %g 1 %sp, %g 1, %sp ret restore. SAVE. main = -(92 + 0) & -8
Refresher w Remember to work in increments!. var. X: VAR x: INTEGER; BEGIN x : = 99; END. . section. align. word “. data” 4 0 . section. align. global “. text” 4 main set save . SAVE. main, %g 1 %sp, %g 1, %sp set st set ld st 99, %l 0, [%fp-4] ! Temporary storage. var. X, %l 0 [%fp-4], %l 1, [%l 0] ! Global label method main: ret restore. SAVE. main = -(92 + 4) & -8 ! 4 bytes of temp storage
General Principles w Start off doing simple load-compute-store. Don’t worry about efficiency or speed, as you won’t get any points for those. w Var. STOs will always be somewhere in memory (offsets from %fp or %g 6). If the Var. STO is a parameter, it is initially in the I-registers until you move it to it’s proper place (%fp+68, %fp+72, etc). w Expr. STOs will always be in some temporary memory location (offsets from %fp). w Const. STOs shouldn’t be stored in the run-time memory. Just do a “set” instruction with the value when you need it.
Things to Know w We won't be passing more than 6 parameters to procedures (hence, allowing you to rely on the %o 0 -%o 5/%i 0 -%i 5 registers). For the purposes of procedures with receiver variables, we'll only pass up to 5 parameters (with the 6 th parameter implicitly being "this"). w We WILL test more than 4 K 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 +/- 4 K. w 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 namemangle procedure, variable, and loop labels with a ". " in front of them, as Oberon won't allow names with a ". " (and thus avoid colliding names). w 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-compute-store scheme.
Phase 1 –Arithmetic Expressions w Given (assume x is in %fp-4): x : = x + 7; ld set add st ld st [%fp-4], %l 0 7, %l 1 %l 0, %l 1, %l 0, [%fp-8] ! Tmp [%fp-8], %l 0, [%fp-4]
Arithmetic Expressions w OK, that’s simple for a simple statement. w What if we had a statement like this: Z : = ((A+B) + (C+D)) + ((E+F) + (X+Y)) w Which registers do we use? ? ?
Method 1 – Simplicity w Doing load-compute-store will only require 3 registers at any given time. w 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. w You will need to store the temporary memory location you used in your Expr. STO so you can reference it later.
Method 2 – Register Allocation w Have some data structure that lets you know what registers are free and which are currently in use. w Every time you need a register, request one from the data structure, which will remove that register from the available list w When you are done with a register, let the data structure know it is available for use again. w This is a good method too!
Method 2 – Register Allocation w Make some class (Reg. Class) with: n n Get. Free. Reg() – returns an available register Free. Reg(String r) – marks that “r” is available w You will need to store the allotted register in your Expr. STO so you can reference it later.
Method 3 – Clumsy/Bad! w We can take advantage of Java Strings and encapsulate chunks of assembly code within each Expr. STO. w 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). w Very confusing to keep track of – avoid this unless you really feel it is necessary!
Method 3 – Clumsy/Bad! w Always put operands into %o 0 and %o 1 n Useful for calling. mul and. div w Compute operation, and place result in %g 1 w Store the resulting assembly code string into the Expr. STO, without outputting to the file w When that Expr. STO is used in another Expression, dump the stored code and make some small register moves.
Method 3 – Example (a + b) ld ld add + a, %o 0 b, %o 1 %o 0, %o 1, %g 1 {place code (a+b) here} mov %g 1, %l 0 {place code (x+y) here} mov %l 0, %o 0 mov %g 1, %o 1 add %o 0, %o 1, %g 1 (x + y) ld ld add x, %o 0 y, %o 1 %o 0, %o 1, %g 1
Methods are your Friend! w Consider adding methods to your STO’s that make generating assembly for certain cases easier: n n n Get. Address() – returns base/offset (ie, %fp – 4) Get. Value() – will combine Get. Address with an appropriate load instruction Etc.
Conditions – Branching w Given this: IF x THEN (* … *) END; ld cmp be nop (* … *). If. L 1: x, %l 0, %g 0. If. L 1 ! Compare with zero ! Opposite logic
Branching – Where to? w You will need to generate labels for your branch statements. n These labels must be unique w A simple solution would be to use some prefix string (i. e. , “. If. L”), and append some counter at the end: n . If. L 1, . If. L 2, . If. L 3, …
Branching – Label Stack w Consider you had: IF X THEN IF Y THEN (*…*) END; w 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 L 1, push L 1 onto stack IF Y THEN – load Y, compare, branch to L 2, push L 2 onto stack (*…*) END; - Pop L 2 from stack and output label END; - Pop L 1 from stack and output label
Procedures w How to call a procedure? n Ex: call foo nop w How to return from a procedure? n Ex: ret restore w How to return a value from a procedure? n Ex: mov %l 0, %i 0 ret restore
Procedures – Example PROCEDURE foo () : INTEGER; VAR x: INTEGER; BEGIN x : = 2; RETURN x; END foo;
Procedures – Example The following can be generated just by parsing “PROCEDURE foo”: . section. align 4. global “. text”. foo: set. foo. SIZE, %g 1 save %sp, %g 1, %sp
Procedures – Example Now, the body of the function: set 2, %l 0 ! Put “ 2” in a reg. st %l 0, [%fp-8] ! Store in tmp mem ld [%fp-8], %l 0 ! Load from tmp st %l 0, [%fp-4] ! Store expr into “x” ld [%fp-4], %i 0 ! Put “x” in return ret restore ! Return statement
Procedures – Example Lastly, now that we got to “END foo; ”: . foo. SIZE = -(92 + 8) & -8 ! Bytes of local and tmp vars w 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 “yesn”; z : = TRUE; END; WRITE z, NL; END.
Large Example. section. align. I. s: . asciz. NL. s: . asciz. T. s: . asciz. F. s: . asciz. section. align. global “. rodata” 4 “%d” “n” “TRUE” “FALSE” ! General stuff we should always have handy ! Good idea to output this when you open the file “. text” 4. foo: set. SAVE. foo, %g 1 save %sp, %g 1, %sp ! x : = y; ld [%g 6 -4], %l 0 st %l 0, [%fp-8] ld [%fp-8], %l 0 st %l 0, [%fp-4] ! RETURN (x+1); ld [%fp-4], %l 0 set 1, %l 1 add %l 0, %l 1, %l 2 st %l 2, [%fp-12] ld [%fp-12], %l 0 mov %l 0, %i 0 ret restore. SAVE. foo = -(92 + 12) & -8 ! Load global y ! Store in temporary mem ! Get expression to assign statement ! Store result into local x ! Use set for a const (Const. STO or literal) ! Do addition ! Store in temporary mem ! Get expression result from temp mem ! Put result in %i 0, which is return value for procedure ! Do return ! Restore register window ! We had 1 local var and 2 temporary locations 12 bytes
Large Example. section. align. skip. globals: “. bss” 4 4 4 . section. align. global “. text” 4 main ! Right before “main”, we go into BSS and set all the “globals”. ! Global z [%g 6 -8] ! Global y [%g 6 -4] ! Note how they are in reverse order. The easiest way to do this is to have a vector of Var. STOs ! that you add to every time you do a global Var. Decl. Then, walk through it backwards right before “main” main: set. SAVEmain, %g 1 save %sp, %g 1, %sp set. globals, %g 6 ! One-time initialization of %g 6 the be the base address of the globals ! y : = 4; set 4, %l 0 ! Use set for integer literal st %l 0, [%fp-4] ! Store in temporary mem ld [%fp-4], %l 0 ! Get expression to assign statement st %l 0, [%g 6 -4] ! Store result into global y ! IF y < foo() ld [%g 6 -4], %l 0 ! Load global y call. foo nop mov %o 0, %l 1 ! Get result of foo into %l 1 mov %g 0, %l 2 ! Initialize boolean result to FALSE cmp %l 0, %l 1 ! Compare the two bge. L 1 ! Opposite logic! nop
Large Example set 1, %l 2 st %l 2, [%fp-8] ! Change boolean result to TRUE if branch failed ! Branch will jump to here, skipping setting to TRUE ! Store y < foo() boolean result into temporary mem ld cmp be nop [%fp-8], %l 0, %g 0. L 2 ! Get y < foo() result from temp mem ! Compare with FALSE ! Skip over code if FALSE . L 1: . L 3: ! WRITE “yesn”; . section “. rodata”. asciz “yesn”. section “. text”. align 4 set. L 3, %o 0 call printf nop ! z : = TRUE; set 1, %l 0 st %l 0, [%fp-12] ld [%fp-12], %l 0 st %l 0, [%g 6 -8] . L 2: ! Switch to read-only data segment (data segment also OK) ! No align necessary for. asciz (since byte-array, no alignment restrictions) ! Switch back to text segment ! Used a unique global label to point to string literal ! Set TRUE constant literal into register ! Store into temp mem ! Get from result expression from temp mem ! Store into global z ! Skip-over label from IF stmt
Large Example ! WRITE z, NL; ld [%g 6 -8], %l 0 st %l 0, [%fp-16] ld [%fp-16], %l 0 set. F. s, %o 0 cmp %l 0, %g 0 be. L 4 nop set. T. s, %o 0 printf ! Change to “TRUE” if z is TRUE ! Branch over here ! Will print TRUE or FALSE depending on what %o 0 was . NL. s, %o 0 printf ! Set NL literal for output ! Output newline . L 4: call nop set call nop ! Load global z ! Store into temp mem ! Load expression result from temp mem ! Default to print “FALSE” ! Since z is boolean, determine if we print TRUE or FALSE ! Skip over if FALSE ret restore. SAVEmain = -(92 + 16) & -8 ! Implicit return at end of every procedure (including main) ! No local vars and 4 temp locations (16 bytes) When you run this, you should get: yes TRUE -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.
Topics/Questions you may have w Anything else you would like me to go over now? w Anything in particular you would like to see next week?
- Slides: 31