1 / 39

UM Macro Assembler Functions

COMP 40: Machine Structure and Assembly Language Programming (Spring 2014). UM Macro Assembler Functions. Noah Mendelsohn Tufts University Email: noah@cs.tufts.edu Web: http:// www.cs.tufts.edu/~noah. Review x86-64 Standard Function Linkage.

nansen
Download Presentation

UM Macro Assembler Functions

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. COMP 40: Machine Structure and Assembly Language Programming (Spring 2014) UM Macro AssemblerFunctions Noah Mendelsohn Tufts UniversityEmail: noah@cs.tufts.edu Web: http://www.cs.tufts.edu/~noah

  2. Reviewx86-64 Standard Function Linkage

  3. Why have a standard AMD 64 “linkage” for calling functions? (Review) • Functions are compiled separately and linked together • We need to standardize enough that function calls can be freely mixed • We may “cheat” when caller and callee are in the same source file

  4. Function calls on Linux/AMD 64 • Caller “pushes” return address on stack • Where practical, arguments passed in registers • Exceptions: • Structs, etc. • Too many • What can’t be passed in registers is at known offsets from stack pointer! • Return values • In register, typically %rax for integers and pointers • Exception: structures • Each function gets a stack frame • Leaf functions that make no calls may skip setting one up • Caller vs. callee saved registers

  5. AMD 64 The stack – general case (review) Before call After callq If callee needs frame Arguments Arguments Arguments %rsp Return address ???? ???? Return address %rsp Callee vars framesize Args to next call? %rsp sub $0x{framesize},%rsp

  6. AMD 64 arguments/return values in registers Arguments and return values passed in registers when types are suitable and when there aren’t too many Return values usually in %rax, %eax, etc.Callee may change these and some other registers! MMX and FP 87 registers used for floating pointRead the specifications for full details!

  7. AMD 64 Factorial Revisited fact: .LFB2: pushq %rbx .LCFI0: movq %rdi, %rbx movl $1, %eax testq %rdi, %rdi je .L4 leaq -1(%rdi), %rdi call fact imulq %rbx, %rax .L4: popq %rbx ret int fact(int n) { if (n == 0) return 1; else return n * fact(n - 1); }

  8. The UMASMStandard Function Linkage

  9. Why have a standard UMASM function linkage? • Functions are compiled separately and linked together • We need to standardize enough that function calls can be freely mixed • It’s tricky, so doing it the same way every time minimizes bugs and makes code clearer • We may “cheat” when caller and callee are in the same source file

  10. What do we need to know about calling conventions? • Which registers hold parameters? [none — use the stack] • Which registers hold results? [r1] • Which registers are preserved across calls? [r0, r2, r3, r4] • Which register is the stack pointer? [r2] • When we call a procedure, where is the return address? [in r1] • What is the layout of the stack frame? [r2 points to arguments] REMEMBER: all of this assumes that a stack with sufficient spacehas been created in segment 0, and that r2 is set as the sp.

  11. Startup code with stack (REVIEW) .section init .temps r6, r7 .section stk .space 100000 endstack: .section init .zero r0 start: r0 := 0 r2 := endstack gotomain linking r1 halt • Goals of startup code: • Set up useful registers • Set up a stack • Let UMASM know about register assignments • Invoke or drop through to user code init (instructions) stk10000 words Richer version with stack start endstack(r2)

  12. Summarizing calling conventions • r0 is always 0 (points to segment 0, used for goto and for the call stack) • r1 is the return-address register and the result register • r2 is the stack pointer and must have same value after return (nonvolatile) • r3, r4 are nonvolatiles • r5 is a helpful volatile register (good to copy return address) • r6, r7 are volatile and are normally .temps (but up to each procedure) • Arguments are on the stack, with first argument at the “bottom” (where r2 points) EXCEPT FOR VOLATILES AND RESULT REGISTER CALLEE MUST LEAVE REGISTERS AND STACK UNCHANGED!

  13. Calling a function with no arguments r1 := return_addr // Offset in seg 0 r7 := somefunc // Offset in seg 0 goto*r7 in program m[r0] // Call the function return_addr: // Function returns here …back in caller… call somefunc();

  14. Calling a function with no arguments r1:= return_addr // Offset in seg 0 r7 := somefunc// Offset in seg 0 goto*r7 in program m[r0] // Call the function return_addr: // Function returns here …back in caller… Use of r1 for return address is required call somefunc();

  15. Calling a function with no arguments r1 := return_addr // Offset in seg 0 r7 := somefunc// Offset in seg 0 goto*r7 in program m[r0] // Call the function return_addr: // Function returns here …back in caller… call somefunc(); Use of r7 for function address is programmer choice

  16. Calling a function with no arguments r1 := return_addr // Offset in seg 0 r7 := somefunc// Offset in seg 0 goto*r7 in program m[r0] // Call the function return_addr: // Function returns here …back in caller… call somefunc(); gotosomefunclinking r1 // goto main, setting r1 to the return address UMASM provides this built-in macro to do the above for you!

  17. Calling a function with no arguments r1 := return_addr // Offset in seg 0 r7 := somefunc// Offset in seg 0 goto*r7 in program m[r0] // Call the function return_addr: // Function returns here …back in caller… call somefunc(); gotosomefunc linking r1 // goto main, setting r1 to the return address UMASM provides this built-in macro to do the above for you! Also remember: r1 is both the linking and the result register. If somefunc calls another function, then r1 must be saved.

  18. Organizing Startup Code

  19. We can implement a common version of UMASM startup What’s common to all UMASM program startup? Let’s write it just once and reuse it! Set up stack Set r0 := 0

  20. urt0: A reuseable UMASM startup routine .zero r0 .temps r7 .section stk .space 100000 endstack: .section init _ustart: r0 := 0 r2 := endstack goto main linking r1 halt // Finis urt0.ums

  21. urt0: A reuseable UMASM startup routine Establish r0 as always containing 0 .zero r0 .temps r7 .section stk .space 100000 endstack: .section init _ustart: r0 := 0 r2 := endstack goto main linking r1 halt // Finis

  22. Build stack and set up r2 as stack pointer urt0: A reuseable UMASM startup routine .zero r0 .temps r7 .section stk .space 100000 endstack: .section init _ustart: r0 := 0 r2 := endstack goto main linking r1 halt // Finis

  23. Startup code with stack (REVIEW) .section init .temps r6, r7 .section stk .space 100000 endstack: .section init .zero r0 start: r0 := 0 r2 := endstack gotomain linking r1 halt • Goals of startup code: • Set up useful registers • Set up a stack • Let UMASM know about register assignments • Invoke or drop through to user code init (instructions) stk10000 words Richer version with stack start endstack(r2) WE’VE NOW MADE A COMMON VERSION OF THIS IN urt0.ums!!

  24. Pushing and popping (Review) Expands to: • Push macro • Pop macro .zero r0 .temps r6, r7 push r3 on stack r2 r6 := -1 // macro instruction (not shown)r2 := r2 + r6 m[r0][r2] := r3 Expands to: r3 := m[r0][r2] r6 := 1 r2 := r2 + r6 pop r3 off stack r2 These instructions can be use to save and restore registers andto put function arguments on the stack!

  25. Our program can go in the “main” function .zero r0 .temps r6,r7 main: push r1 on stack r2 // Address main returns to # Can skip one or both of the next two if registers not needed push r3 on stack r2 // Callee saves non-volatile reg push r4 on stack r2 // Callee saves non-volatile reg ...DO REAL WORK HERE... output "Hello world\n" pop r4 off stack r2 // restore caller's r4 pop r3 off stack r2 // restore caller's r3 pop r5 off stack r2 // get return address r1 := 0 // EXIT_SUCCESS is the result goto r5

  26. Remember…we’re assuming urt0 has set up stack and r0 Now our program can go in the “main” function .zero r0 .temps r6,r7 main: push r1 on stack r2 // Address main returns to # Can skip one or both of the next two if registers not needed push r3 on stack r2 // Callee saves non-volatile reg push r4 on stack r2 // Callee saves non-volatile reg ...DO REAL WORK HERE... output "Hello world\n" pop r4 off stack r2 // restore caller's r4 pop r3 off stack r2 // restore caller's r3 pop r5 off stack r2 // get return address r1 := 0 // EXIT_SUCCESS is the result goto r5

  27. Now our program can go in the “main” function Here’s where our useful work will go. .zero r0 .temps r6,r7 main: push r1 on stack r2 // Address main returns to # Can skip one or both of the next two if registers not needed push r3 on stack r2 // Callee saves non-volatile reg push r4 on stack r2 // Callee saves non-volatile reg ...DO REAL WORK HERE... output "Hello world\n" pop r4 off stack r2 // restore caller's r4 pop r3 off stack r2 // restore caller's r3 pop r5 off stack r2 // get return address r1 := 0 // EXIT_SUCCESS is the result goto r5

  28. Return address saved from r1 Now our program can go in the “main” function .zero r0 .temps r6,r7 main: push r1 on stack r2 // Address main returns to # Can skip one or both of the next two if registers not needed push r3 on stack r2 // Callee saves non-volatile reg push r4 on stack r2 // Callee saves non-volatile reg ...DO REAL WORK HERE... output "Hello world\n" pop r4 off stack r2 // restore caller's r4 pop r3 off stack r2 // restore caller's r3 pop r5 off stack r2 // get return address r1 := 0 // EXIT_SUCCESS is the result goto r5 ..but popped into R5

  29. Only save non-volatiles if necessary Now our program can go in the “main” function .zero r0 .temps r6,r7 main: push r1 on stack r2 // Address main returns to # Can skip one or both of the next two if registers not needed push r3 on stack r2 // Callee saves non-volatile reg push r4 on stack r2 // Callee saves non-volatile reg ...DO REAL WORK HERE... output "Hello world\n" pop r4 off stack r2 // restore caller's r4 pop r3 off stack r2 // restore caller's r3 pop r5 off stack r2 // get return address r1 := 0 // EXIT_SUCCESS is the result goto r5

  30. Now our program can go in the “main” function Remember to combine all the pieces when you build .zero r0 .temps r6,r7 main: push r1 on stack r2 // Address main returns to # Can skip one or both of the next two if registers not needed push r3 on stack r2 // Callee saves non-volatile reg push r4 on stack r2 // Callee saves non-volatile reg ...DO REAL WORK HERE... output "Hello world\n" pop r4 off stack r2 // restore caller's r4 pop r3 off stack r2 // restore caller's r3 pop r5 off stack r2 // get return address r1 := 0 // EXIT_SUCCESS is the result goto r5 umasm urt0.ums hello.ums ...more ums here if needed... > hello

  31. UMASMFunction Argumentsand theCall Stack

  32. UMASM function arguments (Review) At function entry • At entry to a function, the stack must look like thism[r0][r2 + 0] // first parameter m[r0][r2 + 1] // second parameterm[r0][r2 + 2] // third parameter • The called function may push more data on the stack which will change r2… • …when this happens the parameters will appear to be at larger offsets from the (modified) r2! Third parm Second parm First parm r2 ????

  33. Recursion:Factorial

  34. Can we build UMASM code for this? int fact(int n) { if (n == 0) return 1; else return n * fact(n - 1); } REMEMBER THE CONTRACT BETWEEN CALLER AND CALLEE!

  35. Summarizing calling conventions • r0 is always 0 (points to segment 0, used for goto and for the call stack) • r1 is the return-address register and the result register • r2 is the stack pointer and must have same value after return (nonvolatile) • r3, r4 are nonvolatiles • r5 is a helpful volatile register (good to copy return address) • r6, r7 are volatile and are normally .temps (but up to each procedure) • Arguments are on the stack, with first argument at the “bottom” (where r2 points) EXCEPT FOR VOLATILES AND RESULT REGISTER CALLEE MUST LEAVE REGISTERS AND STACK UNCHANGED!

  36. Factorial .section init .zero r0 .temp r6,r7 fact: push r1 on stack r2 // save return address push r3 on stack r2 // save nonvolatile register r3 := m[r0][r2 + 2] // first parameter 'n' // Handle base case: if n == 0 if (r3 == 0) gotobase_case // recursive call r5 := r3 - 1 // OK to step on volatile register push r5 on stack r2 // now (n - 1) is a paraemter goto fact linking r1 // make the recursive call pop stack r2 // pop parameter r1 := r3 * r1 // n * fact(n-1) gotofinish_fact base_case: r1 := 1 finish_fact: // What are invariants here? pop r3 off stack r2 // restore saved register pop r5 off stack r2 // put return address in r5 goto r5

  37. Factorial .section init .zero r0 .temp r6,r7 fact: push r1 on stack r2 // save return address push r3 on stack r2 // save nonvolatile register r3 := m[r0][r2 + 2] // first parameter 'n' // Handle base case: if n == 0 if (r3 == 0) gotobase_case // recursive call r5 := r3 - 1 // OK to step on volatile register push r5 on stack r2 // now (n - 1) is a paraemter goto fact linking r1 // make the recursive call pop stack r2 // pop parameter r1 := r3 * r1 // n * fact(n-1) gotofinish_fact base_case: r1 := 1 finish_fact: // What are invariants here? pop r3 off stack r2 // restore saved register pop r5 off stack r2 // put return address in r5 goto r5 What are the invariants here?

  38. Main program to call factorial void main(void) { for (inti = 10; i != 0; i--) { print_d(i); puts("! == "); print_d(fact(i)); putchar('\n'); } return EXIT_SUCCESS; }

  39. Main program to call factorial .zero r0 .temps r6,r7 main: push r1 on stack r2 // Address main returns to push r3 on stack r2 // Callee saves non-volatile reg r3 := 10 // Using r3 for i - loop var gotomain_test main_loop: push r3 on stack r2 // Pass i as first arg to function gotoprint_d linking r1 // Call print_d pop stack r2 // Function leaves arg on stack output "! == " // puts() is inlined push r3 on stack r2 // Pass i as first arg to function goto fact linking r1 // Call fact push r1 on stack r2 // Pass fact's return value as arg gotoprint_d linking r1 // Call print_d r2 := r2 + 2 // pop 2 parameters, 1 for each call output '\n' // putc() inlined --- it's a C macro r3 := r3 - 1 // i-- main_test: if (r3 != 0) gotomain_loop // while (i != 0) pop r3 off stack r2 // restore caller's r3 pop r5 off stack r2 // get return address r1 := 0 // EXIT_SUCCESS is the result goto r5 // return

More Related