Lecture 8: Control, Procedures, and the Stack CS 2011 Fall 2014, Dr. Rozier
MIDTERM I
Midterm I Median: C-
Midterm I Median: B-
GDB: THE GNU DEBUGGER
gdb: The GNU Debugger Standard and portable debugger for Unix and Unix-like systems. – Originally written in 1986 – Very active tool. Three software releases in – Still the gold-standard for debugging Enables users to trace, alter, and execute computer programs in a controlled environment.
gdb: The GNU Debugger Most useful features – Step through program execution, line by line, or instruction by instruction. – Examine the values of variables and registers. – Trap system signals. – Set breakpoints to halt execution at any point. – Watch variables to see when they change.
gdb: The GNU Debugger Some commands – run – executes the program – break - sets a breakpoint at label – break * - sets a breakpoint at the address – print - prints the register’s value – stepi – step through one assembly instruction
gdb: The GNU Debugger Some commands – disas - disassemble the code at label. – continue – continue execution after halting at a breakpoint. – info [break| ] - give information about breakpoints or registers – info r – display the value of all registers – x/ - display the value stored at in the format specified by
gdb: The GNU Debugger We will show an example next Tuesday of the debugger in action.
CONTROL
Implementing Loop Statements C code: while (i < j) i += 1; ARM code Loop: cmp r0, r1 bge Exit add r0, r0, #1 bal Loop Exit: i < j? i=i+1 i<j Exit i>=j
Basic Blocks A basic block is a sequence of instructions with – No embedded branches (except at end) – No branch targets (except at beginning) A compiler identifies basic blocks for optimization An advanced processor can accelerate execution of basic blocks
What about a Case Statement? Say we have a case statement: switch(x) { case 0: foo(); break; case 1: bar(); break; case 2: baz(); break; case 3: qux(); break; }
Jump Tables Set up a portion of memory as such: If our case variable is stored in r0… – r0 << #2 is the index into our jump table of the function address. Memory LocationContents 0x???? + 0address of foo 0x???? + 4address of bar 0x???? + 8address of baz 0x???? + 12address of qux
Cases Statements as Jump Tables (assume r0 holds the switch variable) ldr r1, =jumptable ldr pc, [r1, r0, lsl #2] Wasn’t that easy?
Pseudo-Instructions Notice the use of: – ldr r0, =jumptable What is really going on here?
Hello World string:.asciiz "Hello World!\n"; ldrr1,=string swi0 movr7,#1 swi0
Pseudo-Instructions Code as we wrote it: Disasembled code: ldrr1,=string swi0 movr7,#1 swi0 0x8080ldrr1,[pc,#8] 0x8084svc0x0 0x8088movr7#1 0x808csvc0x0 0x8090muleqr1r4r0
This is weird… Let’s play with gdb… x/x 0x8090 0x8090 :0x x/x 0x x10094 :“Hello World!\nA\025”
So why does it show up as muleq? Representing instructions – Condition Field 0000 – EQ – 0000 | | 0 | 0 |????|????|????| 1001|???? mul r1, r4, r0 mul{ }{S} rd, rm, rs Cond000000ASRdRnRs1001Rm Instruc ???? 1001???? Hex Bin
So why does it show up as muleq? Representing instructions mul r1, r4, r0 mul{ }{S} rd, rm, rs mul 0001, 0100, 0000 Cond000000ASRdRnRs1001Rm Instruc ???? 1001???? Hex Bin
So what is this? Code as we wrote it: Disasembled code: ldrr1,=string swi0 movr7,#1 swi0 0x8080ldrr1,[pc,#8] 0x8084svc0x0 0x8088movr7#1 0x808csvc0x0 0x8090muleqr1r4r0
The problem with immediates The fact that instructions, AND all their arguments, must take up only 32 bits limits the size of immediates to 1 byte. – Range 0 – 255. – Hello world was in 0x10094 – PC was at 0x8088 – Max offset with immediate value? 0x xFF = 0x8187
Enter, the Literal Pool 0x8080ldrr1,[pc,#8] 0x8084svc0x0 0x8088movr7#1 0x808csvc0x0 0x Last instruction in basic block Literal Pool
PROCEDURES
Basic Blocks A basic block is a sequence of instructions with No embedded branches (except at end) No branch targets (except at beginning) A compiler identifies basic blocks for optimization An advanced processor can accelerate execution of basic blocks
A Call Chain Image by David Thomas
Procedure Calling 1.Place parameters for procedure in registers 2.Transfer control to procedure 3.Procedure acquires storage 4.Procedure performs function. 5.Procedure places return value in appropriate register 6.Return control
The Stack Region of memory managed with stack discipline. Accessed with push and pop
The Stack
Stack Frames Stack frames “belong” to a procedure. Store local variables here (they go out of scope automatically) Can communicate with other procedures with a stack frame.
Procedures and Register Use What if we want to use some registers in the procedure? Caller could have data in it!
Stack Discipline and ABI Stack Discipline is important Define an Application Binary Interface – How should procedures communicate? How should the be called? Consistency, following standards, is the key.
Conventions and Discipline Caller Caller saves temporary values in its frame before the call. Caller restores values in its frame after the call. Callee Callee saves temporary values in its frame before using. Callee restores values in its frame after using.
Stack Frames Contents – Local variables – Return information – Temporary space Management – Space is allocated when entering Set-up code – Deallocated when returning Finish code Frame Pointer Stack Pointer
Example foo() { … bar() … } foo() { … bar() … } foo frame ptr stack ptr foo()
Example foo() { … bar() … } foo() { … bar() … } foo frame ptr stack ptr foo() bar() { … baz … baz() … } bar() { … baz … baz() … } bar bar()
Example foo() { … bar() … } foo() { … bar() … } foo frame ptr stack ptr foo() bar() { … baz … baz() … } bar() { … baz … baz() … } bar bar() baz() { … baz() … } baz() { … baz() … } baz baz()
Example foo() { … bar() … } foo() { … bar() … } foo frame ptr stack ptr foo() bar() { … baz … baz() … } bar() { … baz … baz() … } bar bar() baz() { … baz() … } baz() { … baz() … } baz baz() { … baz() … } baz() { … baz() … } baz baz()
Example foo() { … bar() … } foo() { … bar() … } foo frame ptr stack ptr foo() bar() { … baz … baz() … } bar() { … baz … baz() … } bar bar() baz() { … baz() … } baz() { … baz() … } baz baz() { … baz() … } baz() { … baz() … } baz baz() { … baz() … } baz() { … baz() … } baz baz()
Example foo() { … bar() … } foo() { … bar() … } foo frame ptr stack ptr foo() bar() { … baz … baz() … } bar() { … baz … baz() … } bar bar() baz() { … baz() … } baz() { … baz() … } baz baz() { … baz() … } baz() { … baz() … } baz baz()
Example foo() { … bar() … } foo() { … bar() … } foo frame ptr stack ptr foo() bar() { … baz … baz() … } bar() { … baz … baz() … } bar bar() baz() { … baz() … } baz() { … baz() … } baz baz()
Example foo() { … bar() … } foo() { … bar() … } foo frame ptr stack ptr foo() bar() { … baz … baz() … } bar() { … baz … baz() … } bar bar()baz()
Example foo() { … bar() … } foo() { … bar() … } foo frame ptr stack ptr foo() bar() { … baz … baz() … } bar() { … baz … baz() … } bar bar() baz() { … baz() … } baz() { … baz() … } baz baz()
Example foo() { … bar() … } foo() { … bar() … } foo frame ptr stack ptr foo() bar() { … baz … baz() … } bar() { … baz … baz() … } bar bar()baz()
Example foo() { … bar() … } foo() { … bar() … } foo frame ptr stack ptr foo()bar()baz()
What is the frame pointer used for? Given a caller and a callee, the callee will begin with: – fp = sp A callee will use the stack to: – Save the current sp, lr, and fp before calling a function – Store the arguments for, and return value space before calling a function – Save used registers before calling a function – Store variables local to the procedure
Frame pointer foo frame ptr stack ptr
Frame pointer foo frame ptr stack ptr a = 0x00 PUSH
Frame pointer foo frame ptr stack ptr a = 0x00 b = 0xFC PUSH
Frame pointer foo frame ptr stack ptr a = 0x00 b = 0xFC PUSH c = 0x87
Frame pointer foo frame ptr stack ptr a = 0x00 b = 0xFC PREPARE TO CALL c = 0x87
Frame pointer foo frame ptr stack ptr a = 0x00 b = 0xFC PUSH c = 0x87 r0 lr fp a0 ret
Frame pointer foo stack ptr a = 0x00 b = 0xFC RETURN FROM CALL c = 0x87 r0 lr fp a0 ret
Frame pointer foo frame ptr stack ptr a = 0x00 b = 0xFC POP! c = 0x87
Frame pointer foo frame ptr stack ptr a = 0x00 b = 0xFC c = 0x
Frame pointer The frame pointer gives us a base address from which to offset in our frame for local storage that will automatically go out of scope. foo frame ptr stack ptr a = 0x00 b = 0xFC c = 0x
Recursion foo() { … bar() … } foo() { … bar() … } foo frame ptr stack ptr foo() bar() { … baz … baz() … } bar() { … baz … baz() … } bar bar() baz() { … baz() … } baz() { … baz() … } baz baz() { … baz() … } baz() { … baz() … } baz baz() { … baz() … } baz() { … baz() … } baz baz()
Recursion Stack frames give each function private storage – Saved registers – Local variables – Return portions Recursion is handled without special consideration. Following stack discipline and call/return pattern is critical
PUSH and POP PUSH {reglist} POP {reglist} Really pseudo-instructions! PUSH – STMDB Store multiple registers, decrement address before access. POP – LDMIA Load multiple registers, increment address after access.
Multiple Ways to Handle the Stack Stack Pointer can – Point to the last occupied stack (Full stack) (needs pre-decrementing) – Point to the next address to be occupied (Empty stack) (needs post-decrementing) Stack type can be given as a postfix to the instruction: – STMFD/LDMFD – STMFA/LDMFA – STMED/LDMED – STMEA/LDMEA
RISC Mentality There are no special functions for stacks! Let’s look at stack instructions: – STMDA/LDMDA
Single Data Transfer Interpreting – Cond – 4 bits, condition field – I – immediate offset – Pre/post indexing bit (add offset before(0)/after(1) transfer) – Up/down bit (subtract(0)/add(1) offset to base – Byte/word bit (transfer word(0)/byte(1)) – Write-back bit (1 to write address back into base) – Load store bit (0 store to memory, 1 load from memory) – Rn – base register – Rd – source/destination register COND 01 I I P P U U B B W W L L Rn Rd Offset
Single Data Transfer Interpreting – Cond – 4 bits, condition field – I – immediate offset 1 – offset is immediate COND 01 I I P P U U B B W W L L Rn Rd Offset
Single Data Transfer Interpreting – Cond – 4 bits, condition field – I – immediate offset 0 – offset is register COND 01 I I P P U U B B W W L L Rn Rd Offset Rm Shift
Block Data Transfer Interpreting – Cond – 4 bits, condition field – I – immediate offset – Pre/post indexing bit (add offset before(0)/after(1) transfer) – Up/down bit (subtract(0)/add(1) offset to base – PSR & force user bit (0 do not load PSR or force user mode, 1 load PSR and force user mode) – Write-back bit (1 to write address back into base) – Load store bit (0 store to memory, 1 load from memory) – Rn – base register – Register List COND 100 P P U U S S W W L L Rn Register List
LDR/STR LDM/STM Load or Store single, or multiple Write back, W-bit – ldr Rd, [Rn, Offset] – ldr Rd!, [Rn, Offset]
Block Data Transfer Register list, how to implement? – 16 bit field COND 100 P P U U S S W W L L Rn Register List
For next time MP1, more on the stack