10 13 2016
play

10/13/2016 Problem How do we allocate memory during the execution - PDF document

10/13/2016 Problem How do we allocate memory during the execution of a program written in C? Programs need memory for code and data such as Chapter 10 instructions, global and local variables, etc. Memory Model for Modern programming


  1. 10/13/2016 Problem How do we allocate memory during the execution of a program written in C? Programs need memory for code and data such as Chapter 10 instructions, global and local variables, etc. Memory Model for Modern programming practices encourage many Program Execution (reusable) functions, callable from anywhere. Some memory can be statically allocated, since the Original slides by Chris Wilcox, size and type is known at compile time. Colorado State University Some memory must be allocated dynamically, size and type is unknown at compile time. 1 2 2 Motivation Goals Why is memory allocation important? Why not just What do we care about? use a memory manager?  Fast program execution Allocation affects the performance and memory  Efficient memory usage usage of every C, C++, Java program.  Avoid memory fragmentation Current systems do not have enough registers to  Maintain data locality store everything that is required.  Allow recursive calls Memory management is too slow and cumbersome  Support parallel execution to solve the problem.  Minimize resource allocation Static allocation of memory resources is too  Memory should never be allocated for functions inflexible and inefficient, as we will see. that are not executed. 3 3 4 1

  2. 10/13/2016 Function Call Storage Requirements Consider the following code: Code must be stored in memory so that we can // main program execute the function. int a = 10; The return address must be stored so that control int b = 20 can be returned to the caller. int c = foo(a, b); int foo(int x, int y) Parameters must be sent from the caller to the { callee so that the function receives them. int z; Return values must be sent from the callee to the z = x + y; caller, that’s how results are returned. return z; } Local variables for the function must be stored What needs to be stored? somewhere, is one copy enough?  Code, parameters, locals, globals, return values 5 5 6 6 Possible Solution: Possible Solution: Mixed Code and Data Mixed Code and Data Function implementation: Calling sequence foo BR foo_begin # skip over data ST R1, foo_paramx # R1 has ‘x’ foo_rv .BLKW 1 # return value ST R2, foo_paramy # R2 has ‘y’ foo_ra .BLKW 1 # return address JSR foo # Function call foo_paramx .BLKW 1 # ‘x’ parameter LD R3, foo_rv # R3 = return value foo_paramy .BLKW 1 # ‘y’ parameter Code generation is relatively simple. foo_localz .BLKW 1 # ‘z’ local foo_begin ST R7, foo_ra # save return Few instructions are spent moving data. … LD R7, foo_ra # restore return RET Can construct data section by appending foo_ 7 7 8 8 2

  3. 10/13/2016 Possible Solution: Possible Solution: Mixed Code and Data Separate Code and Data Memory allocation: Advantages: foo_rv .BLKW 1 # foo return value  Code and data are close together foo_ra .BLKW 1 # foo return address  Conceptually easy to understand foo_paramx .BLKW 1 # foo ‘x’ parameter  Minimizes register usage for variables foo_paramy .BLKW 1 # foo ‘y’ parameter  Data persists through life of program foo_localz .BLKW 1 # foo ‘z’ local bar_rv .BLKW 1 # bar return value Disadvantages: bar_ra .BLKW 1 # bar return address  Cannot handle recursion or parallel execution bar_paramw .BLKW 1 # bar ‘w’ parameter  Code is vulnerable to self-modification Code for foo() and bar() are somewhere else  Consumes resource for inactive functions Function code call is similar to mixed solution 9 9 10 10 Possible Solution: Real Solution: Execution Stack Separate Code and Data Instructions are stored in code segment Advantages: Global data is stored in data segment  Code can be marked ‘read only’ Statically allocated memory uses stack  Conceptually easy to understand Dynamically allocated memory uses heap  Early Fortran used this scheme  Data persists through life of program  Code segment is write protected Code Disadvantages: Data  Initialized and uninitialized globals  Cannot handle recursion or parallel execution Heap  Heap can be fragmented  Consumes resource for inactive functions ↓  Stack size is usually limited ↑  Stack can grow either direction Stack (usual convention is down) 11 11 12 12 3

  4. 10/13/2016 Execution Stack Stack Trace What is a stack? Example stack trace from gdb: main() calls A() calls B() calls C() calls D().  First In, Last Out (FILO) data structure Breakpoint is set in function D(), note that main()  PUSH adds data, POP removes data is at the bottom, D() is at the top.  Overflow condition: push when stack full  Underflow condition: pop when stack empty (gdb) info stack  Stack grows and shrinks as data is added and removed #0 D (a=8, b=9) at stacktest.c:23  Stack grows downward from the end of memory space #1 0x00400531 in C (a=7, b=8) at stacktest.c:19 #2 0x0040050c in B (a=6, b=7) at stacktest.c:15  Function calls allocate a stack frame #3 0x004004e7 in A (a=5, b=6) at stacktest.c:11  Return cleans up by freeing the stack frame #4 0x00400566 in main () at stacktest.c:29  Corresponds nicely to nested function calls  Stack Trace shows current execution (Java/Eclipse) 13 13 14 14 Execution Stack Stack Requirements Consider what has to happen in a function call:  Caller must pass parameters to the callee. Picture of stack during  Caller must transfer control to the callee. program execution, same  Caller must allocate space for the return value. call stack as previous slide: D(8,9)  Caller must save the return address.  main() calls A(5,6)  Callee requires space for local variables. C(7,8)  A(5,6) calls B(6,7)  Callee must return control to the caller. B(6,7)  B(6,7) calls C(7,8) Parameters, return value, return address, and  C(7,8) calls D(8,9) A(5,6) locals are stored on the stack. main() The order above determines the responsibility and order of stack operations. 15 15 16 16 4

  5. 10/13/2016 Execution Stack Stack Pointers Definition: A stack frame or activation record is the Clearly we need a variable to store the stack memory required for a function call: pointer (SP), LC3 assembly uses R6. Stack execution is ubiquitous, so hardware has  Stack frame below contains the a stack pointer, sometimes even instructions. ↑ function that called this function. Problem: stack pointer is difficult to use to Locals  Stack frame above contains the access data, since it moves around constantly. functions called from this function. Return Address Solution: allocate another variable called a frame  Caller pushes parameters. Return Value pointer (FP), for stack frame, uses R5.  Callee allocates the return value, Parameters saves the return address, Where should frame pointer point? Our ↓ allocates/frees local variables, and convention sets it to point to the first local stores the return value. variable. 17 17 18 18 Execution Stack Execution Stack Definition: A stack frame or activation record is the In the previous solutions, the compiler allocated memory required for a function call: parameters and locals in fixed memory locations. Using an execution stack means parameters and  Locals are accessed by negative ↑ locals are constantly moving around. offsets from frame pointer. Locals  Parameters and return value are The frame pointer solves this problem by using fixed accessed by positive offsets. offsets instead of addresses. Return Address  Most offsets are small, this explains The compiler can generate code using offsets, Frame Pointer LDR/STR implementation. without knowing where the stack frame will reside. Return Value  Base register stores pointer, signed Frame pointer needs to be saved and restored offset accesses both directions. Parameters around function calls. How about the stack pointer? ↓ 19 19 20 20 5

  6. 10/13/2016 Nested Calls Execution Stack Advantages: Definition: A stack frame or activation record is the  Code can be marked ‘read only’ memory required for a function call:  Conceptually easy to understand  Locals are accessed by negative  Supports recursion and parallel execution offsets from frame pointer.  No resources for inactive functions FP(D)  Parameters and return value are D(8,9)  Good data locality, no fragmenting accessed by positive offsets. FP(C) C(7,8)  Minimizes register usage  Most offsets are small, this explains FP(B) LDR/STR implementation. Disadvantages: B(6,7)  Base register stores pointer, signed  More memory than static allocation FP(A) A(5,6) offset accesses both directions. main() 21 21 22 22 Detailed Example Detailed Example Assume POP and PUSH code as follows: Main program to illustrate stack convention: MACRO PUSH(reg) .ORIG x3000 ADD R6,R6,#-1 ; Decrement SP MAIN LD R6,STACK ; init stack pointer STR reg,R6,#0 ; Store value LD R0,OPERAND0 ; load first operand END PUSH R0 ; PUSH first operand LD R1,OPERAND1 ; load second operand MACRO POP(reg) PUSH R1 ; PUSH second operand LDR reg,R6,#0 ; Load value JSR FUNCTION ; call function ADD R6,R6,#1 ; Increment SP LDR R0,R6,#0 ; POP return value END ADD R6,R6,#3 ; unwind stack ST R0,RESULT ; store result HALT 23 23 24 24 6

Recommend


More recommend