CMPT 295 - Unit - Machine-Level Programming
Lecture 20:
- Assembly language
- Array
- 1D
Last Lecture - x86-64 “register saving” convention
callee saved registers:
- Callee must save & restore
Register | Note |
---|---|
%rbx | |
%r12 | |
%r13 | |
%r14 | |
%r15 | |
%rbp | parameters/arguments |
%rsp | return value |
caller saved registers:
- Caller must save & restore
- Can be modified by callee
Register | Note |
---|---|
%rax | Return value |
%rdi | Parameters/arguments |
%rsi | Parameters/arguments |
%rdx | Parameters/arguments |
%rcx | Parameters/arguments |
%r8 | Parameters/arguments |
%r9 | Parameters/arguments |
%r10 | |
%r11 |
Last Lecture - x86-64 conventions and stack frame
- caller preserves caller saved registers (arrow)
- caller passes arguments (arrow)
- caller calls callee (arrow)
- callee preserves callee saved registers (arrow)
- callee constructs local vars (get stack space) (arrow)
- callee performs function
- callee recycles local vars (restore stack space)
- callee restores callee saved registers
- callee returns to caller
- caller pops arguments
- caller restores caller saved registers
M[] Stack | Note |
---|---|
… | caller frame |
caller saved regs | caller frame |
args 7 … n | caller frame |
return address | caller frame |
callee saved regs | callee frame |
%rsp/Top/local vars | callee frame |
Recursion in x86-64
- Handled without special instruction (We already know how to implement recursive functions in x86-64 assembly language)
- Call / return mechanism still follow the stack pattern. * If P (first invocation of P) calls itself (second invocation of P), then the second invocation of P must terminate before the first invocation of P can do so.
- Each invocation of the recursive function has its own stack frame.
* Saved registers & local variables
* Argument 7..n (if any)
* Saved return address
- Function call conventions still required and implemented
- Passing arguments and returning return value.
- Regular saving conventions still required and implemented
- Prevent one function from corrupting antoher’s data (stored in registers)
- Function call conventions still required and implemented
- Also works for mutual recursion
- P calls Q then Q calls P
Label | M[] Stack |
---|---|
Stack frame of 1st invocation of P | |
Stack frame of 2nd invocation of P | |
top | Stack frmae of 3rd invocation of P |
Recursive Function – countOnesR(…)
/* Recursive counter of 1’s */
long countOnesR(unsigned long x) {
if (x == 0){
return 0;
} else {
return (x & 1) + countOnesR(x >> 1);
}
}
What does this function do?
Recursive Function – Example – Base Case
C program:
/* Recursive counter of 1’s */
long countOnesR(unsigned long x) {
if (x == 0) //highlighted
return 0; //highlighted
else
return (x & 1) + countOnesR(x >> 1);
}
Assembly:
countOnesR:
xorl %eax, %eax #highlighted
testq %rdi, %rdi #highlighted
je done #highlighted
pushq %rbx
movq %rdi, %rbx
andl $1, %ebx
shrq %rdi
call countOnesR
addq %rbx, %rax
popq %rbx
done: #highlighted
ret #highlighted
Recursive Function – Example - Saving registers
C code:
/* Recursive counter of 1’s */
long countOnesR(unsigned long x) {
if (x == 0)
return 0;
else
return (x & 1) + countOnesR(x >> 1);
}
Assembly:
countOnesR:
xorl %eax, %eax
testq %rdi, %rdi
je done
pushq %rbx #highlighted
movq %rdi, %rbx
andl $1, %ebx
shrq %rdi
call countOnesR
addq %rbx, %rax
popq %rbx #highlighted
done:
ret
Recursive Function – Example - Call Setup
C code:
/* Recursive counter of 1’s */
long countOnesR(unsigned long x) {
if (x == 0)
return 0;
else
return (x & 1) + countOnesR(x >> 1); // between the parenthasies are highlighted
}
Assembly code:
countOnesR:
xorl %eax, %eax
testq %rdi, %rdi
je done
pushq %rbx
movq %rdi, %rbx #highlighted
andl $1, %ebx #highlighted
shrq %rdi #highlighted
call countOnesR
addq %rbx, %rax
popq %rbx
done:
ret
Recursive Function – Example – Recursive Call
C code:
/* Recursive counter of 1’s */
long countOnesR(unsigned long x) {
if (x == 0)
return 0;
else
return (x & 1) + countOnesR(x >> 1);// countOnesR(x >> 1) is highlighted
}
Assembly:
countOnesR:
xorl %eax, %eax
testq %rdi, %rdi
je done
pushq %rbx
movq %rdi, %rbx
andl $1, %ebx
shrq %rdi
call countOnesR
addq %rbx, %rax
popq %rbx
done:
ret
Recursive Function – Example – Result
C code:
/* Recursive counter of 1’s */
long countOnesR(unsigned long x) {
if (x == 0)
return 0;
else
return (x & 1) + countOnesR(x >> 1); // + is highlighted
}
Assembly:
countOnesR:
xorl %eax, %eax
testq %rdi, %rdi
je done
pushq %rbx
movq %rdi, %rbx
andl $1, %ebx
shrq %rdi
call countOnesR
addq %rbx, %rax #highlighted
popq %rbx
done:
ret
Recursive Function – Example – Clean-up and return
C code:
/* Recursive counter of 1’s */
long countOnesR(unsigned long x) {
if (x == 0)
return 0;
else
return (x & 1) + countOnesR(x >> 1);
}
Assembly:
countOnesR:
xorl %eax, %eax
testq %rdi, %rdi
je done
pushq %rbx
movq %rdi, %rbx
andl $1, %ebx
shrq %rdi
call countOnesR
addq %rbx, %rax
popq %rbx
done:
ret
Recursive Function – Example – Test Cases
- Test Case 1
- Input: x = 0
- Expected result: 0
- Test Case 2
- Input: x = 7
- Expected result: 3
countOnesR:
xorl %eax, %eax
testq %rdi, %rdi
je done
pushq %rbx
movq %rdi, %rbx
andl $1, %ebx
shrq %rdi
call countOnesR
addq %rbx, %rax
popq %rbx
done:
ret
base + displacement | Stack Variable |
---|---|
Register table:
Register | Value |
---|---|
1D Array – in C
In C, arrays are also pointers!
Diagram of: int x[5];
Address/Variable | Value |
---|---|
x | 1 |
x+1 | 5 |
x+2 | 2 |
x+3 | 1 |
x+4 | 3 |
x+5 | end of array |
I want… | Type | Value of result |
---|---|---|
x[4] | int | |
x | int * | |
x+1 | int * | |
&x[2] | int * | |
x[5] | int | |
*(x+1) | int | |
x+i | int * |
1D Array – in C and in x86-64
T A[n];
- … is an array of data type T and length n => in C
- … is a contiguously allocated region of n * L bytes in memory where L = sizeof(T) => in x86-64
char x[12];
Name/Address | Value |
---|---|
x | |
x+? |
int x[5];
Name/Address | Value |
---|---|
x | |
x+? | |
long x[3];
Name/Address | Value |
---|---|
x | |
x+? | |
char *x[3];
Name/Address | Value |
---|---|
x | |
x+? |
We access arrays differently in x86-64 than in C!
Accessing 1D Array – in x86-64
- Use index arithmatic to compute memory address of each array element
- Memory address of
- where A is base memory address
- i.e., A memory address of array element 0
- where A is base memory address
- Can access other array elements by incrementing A by i*L
- Memory address of
1D Array – in x86-64
int x[5];
Name/Address | Value |
x | 1 |
x+4 | 5 |
x+8 | 2 |
x+12 | 1 |
x+16 | 3 |
x+20 | end of array |
I want… | Type | Value of result (x86-64 instruction) |
---|---|---|
x[4] | int | |
x | int * | |
x+1 | int * | |
&x[2] | int * | |
x[5] | int | |
*(x+1) | int | |
x+i | int * |
Manipulating 1D array – Example - main.c
This program defines 4 arrays:
- an array of char’s,
- an array of short’s,
- an array of int’s,
- an array of long’s
then it calls the appropriate sum function, i.e., the one that sums elements of the correct data type.
#include <stdio.h>
#include <stdlib.h>
char sumChar(char*,int);
short sumShort(short*,int);
int sumInt(int*,int);
long sumLong(long*,int);
#define N 6
char AC[N] = {-58,22,101,-15,72,27}; // Expected results: sum = -107
short AS[N] = {-58,22,101,-15,72,27}; // Expected results: sum = 149
int AI[N] = {258,522,1010,-15,-3372,27};// Expected results: -1570
long AL[N] = {258,522,1010,-15,-3370,27}; // Expected results: -1570
int main(){
printf("The total of AC is %hhi.\n", sumChar(AC, N));
printf("The total of AS is %hi.\n", sumShort(AS, N));
printf("The total of AI is %d.\n", sumInt(AI, N));
printf("The total of AL is %ld.\n", sumLong(AL, N));
return;
}
Manipulating 1D array – Example - sums.s - Part 1
- Register %rdi contains starting address of array (base address of array)
- Register %esi contains size of array (N)
- Register %ecx contains array index
- Register %al or %ax contains the running sum
sumChar assembly:
.global sumChar
sumChar:
movl $0,%eax
movl $0,%ecx
loopChar:
cmpl %ecx,%esi
jle endloop1
addb (%rdi,%rcx,1),%al
incl %ecx
jmp loopChar
endloop1:
ret
sumShort assembly:
.global sumShort
sumShort:
xorl %eax,%eax
xorl %ecx,%ecx
jmp condl
loopShort:
addw (%rdi,%rcx,2), %ax
incl %ecx
condl:
cmpl %esi,%ecx
jne loopShort
ret
Manipulating 1D array – Example - sums.s - Part 2
Register %rdi contains starting address of array (base address of array)
- Register %esi contains size of array (N)
- Register %ecx contains array index
- Register %eax or %rax contains the running sum
sumInt assembly:
.global sumint
sumInt:
xorl %eax,%eax
xorl %ecx,%ecx
jmp cond2
loopInt:
addl (%rdi,%rcx,4),%eax
incl %ecx
cond2:
jmpl %esi,%ecx
jne loopInt
ret
sumLong in assembly:
.global sumLong
sumLong:
movq $0,%rax
movl $0,%ecx
loopLoop:
cmpl %ecx,%esi,
jle endloop
addq (%rdi,%rcx,8),%rax
incl %ecx
jmp loopLoop
endloop:
ret
Warning!
- Forgetting that the memory addresses of contiguous array cells differ by the size of the content of these cells instead of by 1 is a common mistake in assembly language programming
Summary
- Arrays
- Elements packed into contiguous region of memory
- Use index arithmetic to locate individual elements
- 1D array: address of A[i] = A + i * L
Next Lecture
- Introduction
- C program -> assembly code -> machine level code
- Assembly language basics: data, move operation
- Memory addressing modes
- Operation leaq and Arithmetic & logical operations
- Conditional Statement – Condition Code + cmovX
- Loops
- Function call – Stack
- Overview of Function Call
- Memory Layout and Stack - x86-64 instructions and registers
- Passing control
- Passing data – Calling Conventions
- Managing local data
- Recursion
- (highlighted) Array
- Buffer Overflow
- Floating-point operations