cse 131b – compiler construction ii discussion 6: operations, branches and functions 2/21/2007

31
CSE 131B – Compiler Construction II Discussion 6: Operations, Branches and Functions 2/21/2007

Post on 21-Dec-2015

214 views

Category:

Documents


1 download

TRANSCRIPT

CSE 131B – Compiler Construction II

Discussion 6: Operations, Branches and Functions

2/21/2007

Overview

Phase 1

Some Phase 2

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

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 “.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.

Topics/Questions you may have

Anything else you would like me to go over now?

Anything in particular you would like to see next week?