Title: Compiling to the Assembly Level
1Compiling to the Assembly Level
- Chapter 6
- CIS-2261
- Foundations in Computer Science
2Homework Chapter 6
Exercises 1, 4, 6, 8, 10, 11, 16
3Introduction
- The Assembly level was invented to relieve the
programmer of the tedium of programming in binary - Now its time to look in more detail at the
relationship of the level 6 language to the level
5 language - Its time to look at how a compiler might
translate level 6 programming features into the
equivalent program at level 5
4Introduction
- Need to recognize that there are major
differences between the two levels that the
compiler must bridge - Level 5 has an absence of extensive data types
offered by a level 6 language - To define an array of structures in assembly
language requires a partitioning of bits and
bytes - This partitioning is the job of a compiler for
level 6 programming
5Introduction
- Also need to think about how control of execution
flow is handled at level 5 vice level 6 - Level 6 (C) has IF, WHILE, DO, FOR, SWITCH, and
FUNCTION statements that can alter normal
sequential flow - Assembly language is limited by von Neumann
design to more primitive control statements
6Introduction
- Need to make sure we look at how the compiler
must combine several primitive level 5 control
statements to execute a single, more powerful
level 6 control statement!!
7OVERVIEW
- Branching Instructions and Flow of Control
- Stack-Relative Addressing and Procedure Calls
- Indexed Addressing and Arrays
- Data Types at Level 5
8Pep/6 Branching Instructions
Remember the operation of the Branching
Instructions!
Status Bits
Addr Modes
Meaning
Mnemonic
Opcode
ix
If N1 or Z1, PC Oprnd
BRLE
01111
ix
If N1, PC Oprnd
BRLT
10000
ix
If Z1, PC Oprnd
BREQ
10001
ix
If Z0, PC Oprnd
BRNE
10010
ix
If N0, PC Oprnd
BRGE
10011
ix
If N0 and Z0, PC Oprnd
BRGT
10100
ix
If V1, PC Oprnd
BRV
10101
ix
If C1, PC Oprnd
BRC
10110
9Branching Instructions
- Each of the branching instructions tests one or
two of the four status bits - Use the results of the test to control flow
- If the condition is true, take the branch - ie
place the operand into the PC - If the condition is not true, do not take the
branch - ie continue execution with the next
instruction
10Branching Instructions
- Whether a branch occurs depends on the value of
the status bits - The value of the status bits are affected by the
execution of other instructions
LOADA num, d BRLT Place
LOADA sets the N and Z bits BRLT tests the N bit
only
Translation If num is negative, take the branch
to Place
11Branching Instructions
- Lets take a look at how a compiler would
translate an if statement from C to Pep/6
Assembly - The program computes the absolute value of an
integer
12Program 6.1 - IF Translation
High -Order Language (Source Code) include
ltiostream.hgt int number main() cin gtgt
number if (number lt 0)
number -number cout ltlt number
13Program 6.1 - IF Translation
Cin translation for a number
High level Statements
IF TEST
Computer the negative
14Program 6.1 Execution
- Cin statement translates to DECI
- cout translates to DECO
- Assignment statement translates into sequence
LOADA, NOTA, ADDA, STOREA - NOTA and ADDA compute the 2s complement
- IF translates into LOADA and BRGE
- LOADA sets the N and Z bits
- BRGE tests the N bit
- See TEXT page 201-202 for more detail
15Flow Control for IF Statement
Level 6 Structure
Resulting Level 5 Structure
Statements 1
Statements 1
IF (Comparison 1 - BRGE)
IF (Comparison 1 - lt 0)
Branch if GE
DO Statements 2
DO Statements 2
Statements 3
Statements 3
Need to bypass some of the code if not negative
16Optimizing Compiler
- Previous program appeared to have an extra LOADA
statement that would not be strictly required - Can eliminate the second LOADA this time because
the value of number is already in the accumulator - For an assignment statement, a typical compiler
will - load accumulator
- evaluate expression
- store the result
17Optimizing Compilers
- To eliminate the extra load in program 6.1 would
require the compiler to remember the content of
the accumulator - A compiler that can do this (and other things) is
called an optimizing compiler - Thus Optimizing compilers are harder to design
and generally take longer to compile programs - Optimizing or non-optimizing depends on the use
18More Branching Instructions
- Lets take a look at how a compiler might compile
a more complex flow controls statement such as an
IF/ELSE statement - In this case the IF body requires an
unconditional branch around the else body - The compiler must recognize this and build that
control structure into the assembly language
19Program 6.2 - IF/ELSE Translation
High -Order Language (Source Code) include
ltiostream.hgt const int limit 100 int
num main() cin gtgt num if (number
gt limit) cout ltlt high else
cout ltlt low
How should the compiler handle this output??
20Program 6.2 IF/ELSE
- Program 6.2 does not compare a variables value
with zero - Comparison is to a value not equal to zero
- How can we handle this at the assembly level?
- Are there more ways than one to handle this
comparison?
21Program 6.2 IF/ELSE
- At least two ways to do this
- Use COMPA instruction
- Use SUBA instruction
- Which way is best?
- Think in terms of code efficiency
22Program 6.2 Using COMPA
Does not destroy the result in the Accumulator
only affects the flags
23Program 6.2 Using SUBA
Destroys the result in the Accumulator you
have Lost your initial value
24Flow Control for IF/ELSE Statement
Level 6 Structure
Resulting Level 5 Structure
Statements 1
Statements 1
IF (Comparison 1 - BRLT)
IF (Comparison 1 - lt 0)
Branch if LT
DO Statements 2
Else
DO Output high
DO Statements 3
Branch at END of IF
Statements 4
ELSE DO Output low
END of IF
25More on Flow Control
- Have talked about how the IF statement can
control the execution flow - Simple conditional branch used
- Have talked about how the IF/ELSE statement can
control the execution flow - Still use conditional branch but also have to
include an unconditional branch to exit out of
the IF body - Lets take a look at how the WHILE statement
control structure is implemented in assembly
26Program 6.3 - While Translation
High -Order Language (Source Code) include
ltiostream.hgt Char letter main() cin gtgt
letter while (letter ! )
cout ltlt letter cin gtgt
letter
How should the compiler handle the while??
27Program 6.3 WHILE Control
- WHILE is a looping instruction and requires the
compiler to generate assembly code that
conditionally branches back to the beginning of a
loop - Where does the compiler put the conditional test
and branch - At the beginning of the loop?
- At the end of the loop?
28Program 6.3 WHILE Control
Test at top of while loop
Loop Back to top
29Flow Control for WHILE Statement
Resulting Level 5 Structure
Level 6 Structure
Statements 1
Statements 1
WHILE (Comparison 1 - BREQ)
WHILE (Comparison 1)
Branch if EQ
Statements 2
Output letter
Statements 3
Branch back to top
Statements 3
30More on Flow Control
- Does the compiler always have to put the
conditional test at the top of the loop? - With WHILE yes
- But can have different control structure for
DO-WHILE
31Program 6.4 DO-While Translation
High -Order Language (Source Code) include
ltiostream.hgt Int cop Int driver main() cop
0 driver 40 do cop 25 driver
20 while (cop lt driver)
cout ltlt cop
How should the compiler implement
32Program 6.4 DO-WHILE
Only need one conditional branch
33Flow Control for DO-WHILE
Resulting Level 5 Structure
Level 6 Structure
Statements 1
Statements 1
DO
DO
Statements 2
Statements 2
WHILE (Comparison 1)
WHILE (Comparison 1 - BREQ)
Statements 3
Statements 3
34FOR Statements
- FOR statements are similar to WHILE statements
wrt to conditional testing - Test at the top of the loop
- Compiler has to generate code to initialize and
increment the control variable - The FOR statement at level 5 becomes
- Initialize the control variable
- Test the control variable
- Execute the loop body
- Increment the control variable
- Branch to the test at the top of the loop
- This is pretty similar to the WHILE implementation
35Program 6.5 FOR Translation
High -Order Language (Source Code) include
ltiostream.hgt int i main() for (i 0 i lt 3
i) cout ltlt i ltlt i ltlt endl cout ltlt i
ltlt i ltlt endl
36Program 6.5 FOR Translation
Test at the top
Loop back to top
37Flow Control for FOR Statement
Resulting Level 5 Structure
Level 6 Structure
Statements 1
Statements 1
FOR (Comparison 1 - BRGE)
FOR (Comparison 1)
Branch if GE
Statements 2
Statements 2
Statements 3
Branch back to top
Statements 3
38OVERVIEW
- Branching Instructions and Flow of Control
- Stack-Relative Addressing and Procedure Calls
- Indexed Addressing and Arrays
- Data Types at Level 5
39Stack-Relative Addressing
- So far have talked about direct addressing modes
and immediate addressing modes - What about Stack-Relative addressing modes?
- Why would we want to have that type of addressing
mode?
40Stack-Relative Addressing
- Need to support methods that will allow us to
make routine calls and returns - Need to use the stack to implement that
efficiently - Use the stack like we use the Program Counter
only use it to point to data instead of
instructions - Remember the instructions that use the stack
pointer (chapter 2) - JSR
- RTS
41Stack-Relative Addressing
- Compiler will implement procedure calls with the
JSR instruction - JSR pushes the content of the PC onto the top of
stack and loads the operand into the PC - Stack Pointer used to keep track of the top of
stack - JSR implements as
- SP SP-2
- MemSP PC
- PC Operand
42Stack-Relative Addressing
- Compiler will implement procedure returns with
the RTS instruction - RTS pops the return address from the top of stack
and loads the that address into the PC - Must readjust the Stack Pointer to new top of
stack - RTS implements as
- PC MemSP
- SP SP2
- Also need to think about how to pass parameters!
- So far have only talked about a parameter less
calls - Compiler must also handle this over and above
what it does on JSR and RTS
43Program 6.7 Procedure Call w/o Parameters
High -Order Language (Source Code) include
ltiostream.hgt Void PrintTri () cout ltlt ltlt
endl cout ltlt ltlt endl cout ltlt ltlt
endl cout ltlt ltlt endl main()
PrintTri() PrintTri() PrintTri()
44Program 6.7 Procedure Call w/o Parameters
Before first JSR
Mem
CPU
002E
PC
?
0BE4
0BE6
SP
?
0BE6
After first JSR
Mem
CPU
0003
PC
0031
0BE4
0BE4
SP
?
0BE6
45Stack-Relative Addressing
- When a program calls a procedure, the calling
program must allocate storage on the stack for - Parameters first
- Next the return address
- The Called procedure then allocates storage for
its local variables
46Stack-Relative Addressing
- How does the machine reference the data using the
Stack Pointer? - Stack-Relative Addressing
- Oprnd MemSP OprndSpec
- Use the Stack Pointer as a memory address to
which the operand is added - Think of the operand specifier as the offset from
the top of stack
47Stack-Relative Addressing
- Pep/6 has one instruction for manipulating the
stack pointer - ADDSP
- Add a negative value to the stack pointer to
allocate storage onto the stack - Add a positive value to the stack pointer to
deallocate storage
48Program 6.8 - Stack-Relative Addressing
Mem
?
0BE2
?
0BE3
?
0BE4
CPU
?
0BE5
SP
0BE6
?
0BE6
?
Before execution
l
0BE2
a
0BE3
e
0BE4
CPU
r
0BE5
SP
0BE2
?
0BE6
After ADDSP
?
49Program 6.8 - Stack-Pointer Operations
Mem
0
l
?
-4
1
a
?
-3
e
2
?
-2
CPU
CPU
r
3
?
-1
SP
SP
?
4
0BE6
?
0
0BE2
?
?
Before execution
After ADDSP
To deallocate the stack have to add back because
the allocation process adjusted the stack pointer
to a higher place in memory
50Stack Operations
- Lets look at how the stack is used during a
procedure call
51Program 6.9
High -Order Language (Source Code) include
ltiostream.hgt int numPts int value int I void
PrintBar (int n) int j for (j 1 j lt n
j) cout ltlt cout ltlt endl main()
cin gtgt numPts for (i 1 i lt numPts
i) cin gtgt value PrintBar
(value)
52Stack Operations - Procedures
- The calling routine is responsible for
- pushing the actual parameters
- executing the JSR (pushing the return address)
- The called procedure is responsible for
- allocating storage for local variables
- after execution deallocating storage for local
variables - popping the return address (executing RTS)
- The calling routine must still
- deallocate storage for actual parameters
53Program 6.9 - Procedure Call
Allocate Local
Execute Procedure
Deallocate Local
Execute RTS
54Program 6.9 - Procedure Call
Read in value to pass
Allocate Parameter
Adjust stack pointer
Execute JSR
Deallocate Parameter
55Stack-Relative Addressing - Functions
- Addressing for Function Calls are similar to
Procedure Calls - Calling program must
- Push storage for returned values before actual
parameters - Pop returned values upon return
56Program 6.10 - Function Call
57Program 6.10 - Function Call
58Program 6.10 - Function Call
59OVERVIEW
- Branching Instructions and Flow of Control
- Stack-Relative Addressing and Procedure Calls
- Indexed Addressing and Arrays
- Data Types at Level 5
60Indexed Addressing and Arrays
- Remember that a variable is referred to by name
at level 6 but by memory location at level 5 - How then should we handle manipulation of arrays
- How about using an Index register to point to the
right location in memory for an array variable?
61Program 6.11
- In the past we have used a brute force method for
outputting a sequence of characters - That is fine for short programs, but not very
efficient for long programs
62Program 6.11 - Example
High -Order Language (Source Code) include
ltiostream.hgt main() cout ltlt A long
message.
63Program 6.11 - Bad Solution
This code is self-modifying - dangerous!!
64Program 6.11 - Bad Example
- Why is this a bad example?
- Not normally a good thing for executing code to
modify itself during execution - Called an IMPURE program
- LISP is a notable exception
- Better to use hardware to index through the array
65Indexed Addressing
- Use indexed addressing to solve the previous
problem - Remember (YOU WILL SEE AGAIN!)
- Immediate addressing Oprnd OprndSpec
- Direct addressing Oprnd MemOprndSpec
- Stack-relative add Oprnd MemSPOprndSpec
- Indexed addressing Oprnd MemBX
66Problem 6.12 - Good Example
Uses indexing method to work through array
Initially acc MemmsgX
67OVERVIEW
- Branching Instructions and Flow of Control
- Stack-Relative Addressing and Procedure Calls
- Indexed Addressing and Arrays
- Data Types at Level 5
68Data Types at Level 5
- So far we have looked at implementing level 6
code from the perspective of program instruction
and control - Also need to consider how data is abstracted as
well - At the machine level the only data types are bits
and bytes - The compiler must use its symbol table to achieve
data abstraction - Lets look at how this might be achieved
69Enumerated Data Types
- This type lets the programmer give a name to a
nonnegative integer value - The compiler stores the names in a table
- When the compiler encounters the name later on,
it substitutes the corresponding value
70Enumerated Data Types
71Boolean Data Types
- Several ways of storing/treating boolean values
at the assembly level - C handles booleans as either true or false
- Compiler must translate that representation into
a numerical representation - True becomes 0001
- False becomes 0000
72Boolean Data Types
- This approach works well for most of the
operations - AND and OR operators function correctly
- NOT operator does not
- To overcome these problems with the NOT operation
- Assign True the value of FFFF instead of 0001
- Assign False the value of 0000 as usual
- Everything now works OK
73Structure Types
- Structures are the key to data abstraction at
Level 6 - Allows consolidation of variables with primitive
data types into a single abstract data type - The compiler uses the struct construct at level
6 - This equates to a contiguous group of bytes at
the assembly level
74Pointer Types
- A pointer at level 6 is a memory address at level
5 - Use the pointer concept in C with new
statements and as references in JAVA - Operation is similar to that already described
but uses something called the heap as opposed to
the stack
75END Chapter 6