1 / 59

Memory Layout

Memory Layout. Compiler Baojian Hua bjhua@ustc.edu.cn. Middle and Back End. translation. AST. IR1. translation. IR2. more IRs and translation. asm. Sources and IRs. CODE. DATA. Procedures. Global Static Variables. Global Dynamic Data. Control Flow. Local Variables. Temporaries.

tex
Download Presentation

Memory Layout

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Memory Layout Compiler Baojian Hua bjhua@ustc.edu.cn

  2. Middle and Back End translation AST IR1 translation IR2 more IRs and translation asm

  3. Sources and IRs CODE DATA Procedures Global Static Variables Global Dynamic Data Control Flow Local Variables Temporaries Statements Parameter Passing Data Access Read-only Data

  4. A code generator should… • Translate all “CODE” to machine (or assembly) instructions • target-dependent • Allocate space for variables, etc. (“DATA”) • Respect the calling conventions and other constraints • To do all these, must know details of modern processors! • and the impact on code generation

  5. Overview of a modern processor ALU Control Memory Registers Memory Registers ALU Control

  6. Arithmetic and Logic Unit Most arithmetic and logic operation addl %eax, %ebx incl 4(%ecx) Operands: immediate register memory Memory Registers ALU Control

  7. Arithmetic and Logic Unit Operations may have constraints how to perform a division? cltd; idivl ... Operations may raise exceptions idivl 0 Operations on different types addb, addw, addl, addq Memory Registers ALU Control

  8. Control Executing instructions instructions are in memory (pointed by PC) for (;;) instruction = *PC; PC++; execute (instruction); Memory Registers ALU Control

  9. Registers Limited but high-speed 8 on x86, more on RISC Most are general-purpose but some are of special use Memory Registers ALU Control

  10. Memory 0xffffffff OS • Address space is the way how programs use memory • highly architecture and OS dependent • right is the typical layout of 32-bit x86/Linux 0xc00000000 stack heap data text 0x08048000 0x00100000 BIOS, VGA 0x00000000

  11. Read Only Data OS char *s=“hello”; void f () {printf(s);} stack .text f: pushl $s call printf s: .string “hello” heap Global Static Variables Procedures data Global Dynamic Data Control Flow Local Variables text Temporaries Statements Parameter Passing BIOS, VGA Data Access Read-only Data

  12. Global Static Variables OS int d = 1; void f (){ d++; } stack .text f: movl d, %eax incl %eax movl %eax, d .data d: .int 1 heap Global Static Variables Procedures data Global Dynamic Data Control Flow Local Variables text Temporaries Statements Parameter Passing BIOS, VGA Data Access Read-only Data

  13. Global Dynamic Data OS void f (){ malloc(4); } stack .text f: pushl $4 call malloc movl %eax, %ebx heap Global Static Variables Procedures data Global Dynamic Data Control Flow Local Variables text Temporaries Statements Parameter Passing BIOS, VGA Data Access Read-only Data

  14. Global Dynamic Data OS void f (){ malloc(4); } stack .text f: pushl $4 call malloc movl %eax, %ebx heap Global Static Variables Procedures data Global Dynamic Data Control Flow Local Variables text Temporaries Statements Parameter Passing BIOS, VGA Data Access Read-only Data

  15. Function, or Procedure, or method, or … • High-level abstraction of code • logically-grouped • Good for many things: • design and abstraction • develop, testing, maintain and evolve • … • Implementation? • we start with C-style functions, and deal with more advanced forms later

  16. API & ABI • Application Programming Interface • interfaces between source programs • Application Binary Interface • contracts between binary programs • even compiled from different languages by different compilers • conventions on low-level details: • how to pass arguments? • how to return values? • how to make use of registers? • … • we posted the x86 ABI document on course page

  17. Parameter Passing

  18. Parameter passing • Must answer two problems: • what to pass? • call-by-value • call-by-reference • call-by-need • … • how to pass? • calling convention • http://en.wikipedia.org/wiki/X86_calling_conventions

  19. Call-by-reference // C++ style reference: int f (int &x, int y) { x = 3; y = 4; return 0; } // a call f (a, b); • In languages such as C++ • arguments are escaped • so can not be constants? • actual arguments and formal parameters are aliases

  20. Simulating call-by-reference // simulated: int f (int *x, int y) { *x = 3; y = 4; return 0; } // the call becomes: f (&a, b); // original C++ code: int f (int &x, int y) { x = 3; y = 4; return 0; } // a call f (a, b);

  21. Moral • Call-by-reference is widely considered a wrong design of C++ • the code is inherently inefficient! • the code is ambiguous in nature • x = 4; (?) • A variant of this is the so-called call-by-value/result • looks like call-by-value, but with effect

  22. Call-by-value/result // code: int f (int @x, int y) { x = 3; y = 4; return 0; } // a call f (a, b); • Upon call, the actual arguments is copies • But callee only modifies a local version • Upon exit, callee copies the local version to actual arguments • and formal parameters are aliases

  23. Simulating call-by-value/result // simulated: int f (int *x, int y) { int temp = *x; temp = 3; y = 4; *x = temp; return 0; } // the call becomes: f (&a, b); // original code: int f (int @x, int y) { x = 3; y = 4; return 0; } // a call f (a, b);

  24. Moral • What’s the difference between call-by-value and call-by-value-result? • Is call-by-value/result more efficient than call-by-reference? Why or why not? • We’d come back to a more interesting optimization called register promotion • same idea to pull value into registers

  25. Call-by-name // code: int f (int name x, int y) { if (y) return x; else return 0; } // a call f (a, b); • Some languages, such as Algo60 and Haskell, use call-by-name • Arguments are not evaluated, until they are really needed in the callee • For each argument, create a function, called a thunk

  26. Simulating call-by-name // simulated: int f (fX: unit -> int, int y) { if (y) return fX (); else return 0; } // the call becomes: f (fn () => a, b); // original code: int f (int name x, int y) { if (y) return x; else return 0; } // a call f (a, b); this function is not closed!

  27. Moral • A serious problem with call-by-name, is that the arguments may be evaluated many times • A better solution is to memoize the evaluation result • This method is called call-by-need, or sometimes lazy-evaluation

  28. Simulating call-by-need // simulated: int f (fX: unit -> int, int y) { if (y) return fX() + fX(); else return 0; } // the call becomes: val xMemoize = ref NONE f (fn () => case !xMemoize of NONE => a; store | SOME i => i, b); // original code: int f (int need x, int y) { if (y) return x + x; else return 0; } // a call f (a, b);

  29. Where to pass the parameters? • Different calling conventions: • pass them in registers • pass them on stack (typically: the call stack) • a combination of the two • parts in registers, parts on the stack • This involves not only the ISA, but also the languages

  30. Sample Calling Conventions for C on x86 (from Wiki)

  31. Registers

  32. Register usage • Must be careful on register usage • caller-save: Callee is free to destroy these registers • eax, ecx, edx, eflags, fflags • [and also all FP registers] • callee-save: Callee must restore these registers before returning to caller • ebp, esp, ebx, esi, edi • [and also FP register stack top]

  33. Register usage • Should value reside in caller-save or callee-save registers? • not so easy to determine • and no general rules • must be veryyyyyyyyy careful with language features such as longjmp, goto or exceptions • we’d come back to this later • We’d also come back to this issue later in register allocation part

  34. The Call Stack

  35. Stack on x86 • Two dedicated regs • Stack grows down to lower address • Frame also called activation record high address frame 0 frame 1 %ebp frame 2 %esp low address

  36. Stack Frame int f (int arg0, int arg1, …) { int local1; int local2; …; } … arg1 arg0 ret addr Global Static Variables Procedures old ebp Global Dynamic Data local1 %ebp Control Flow Local Variables local2 Temporaries Statements … Parameter Passing %esp Data Access Read-only Data

  37. Put these together // x86 code main: pushl %ebp movl %esp, %ebp pushl $8 call f incl %eax leave ret // C code int main(void) { return f(8)+1; } int f(int x) { return g(x); } int g(int x) { return x+3; }

  38. Put these together // x86 code f: pushl %ebp movl %esp, %ebp pushl 8(%ebp) call g leave ret // C code int main(void) { return f(8)+1; } int f(int x) { return g(x); } int g(int x) { return x+3; }

  39. Put these together // x86 code g: pushl %ebp movl %esp, %ebp movl 8(%ebp), %eax addl $3, %eax leave ret // C code int main(void) { return f(8)+1; } int f(int x) { return g(x); } int g(int x) { return x+3; }

  40. Implementation • Design a frame (activation record) data structure • the frame size • garbage collection info • detailed layout, etc. • Thus, hide the machine-related details • good for retargeting the compiler

  41. Interface signature FRAME = sig type t (* allocate space for a variable in frame *) val allocVar: unit -> unit (* create a new frame *) val new: unit -> t (* current size of the frame *) val size: unit -> int end

  42. Frame on stack • Both function arguments and locals have a FIFO lifetime as with functions • so one can put stack frame on the call stack • But later, we have the chance to see other possibilities • e.g.: higher-order nested functions

  43. Nested Function

  44. Nested Functions int f (int x, int y) { int m; int g (int z) { int h () { return m+z; } return 1; } return 0; } • Functions declared in the body of another function • So the inner one could refer to the variables in the outer ones • such kind of functions are called open

  45. Nested Functions int f (int x, int y) { int m; int g (int z) { int h () { return m+z; } return 1; } return 0; } • How to access those variables in outer functions? • Three classical methods: • lambda lifting • static link • display

  46. Lambda lifting • In lambda lifting, the program is translated into a form such that all procedures are closed • The translation process starts with the inner-most procedures and works its way outwards

  47. Lambda lifting example int f (int x, int y) { int m; int g (int z) { int h () { return m+z; } return 1; } return 0; } int f (int x, int y) { int m; int g (int z) { int h (int &m, &z) { return m+z; } return 1; } return 0; }

  48. Lambda lifting example int f (int x, int y) { int m; int g (int z) { int h () { return m+z; } return 1; } return 0; } int f (int x, int y) { int m; int g (int &m, int z) { int h (int &m, &z) { return m+z; } return 1; } return 0; }

  49. Lambda lifting example int f (int x, int y) { int m; int g (int z) { int h () { return m+z; } return 1; } return 0; } // flatten int f (int x, int y){ int m; return 0; } int g (int &m, int z){ return 1; } int h (int &m, &z){ return m+z; }

  50. Moral • Pros: • easy to implement, source-to-source translations • even before code generation • Cons: • all variables are escaped • extra arguments passing • on some architectures, more arguments are passed in memory, so it’s inefficient

More Related