int main(void) {
printf("Float rounding method is %i and the eval precision is %i\n", FLT_ROUNDS, FLT_EVAL_METHOD);
float as[6] = {3.14, 512, 2*512, 3*512, 4*512, 5*512};
float b = 1e10;
float c = -1e10;
for (int i = 0; i < 6; ++i) {
printf("a, b, and c are %f, %f, and %f\n", as[i], b, c);
printf("a + b + c is %f\n", as[i] + b + c);
printf("c + b + a is %f\n", c + b + as[i]);
}
return 0;
}
```
---
## Outputs
Float rounding method is 1 and the eval precision is 0
a, b, and c are 3.140000, 10000000000.000000, and -10000000000.000000
a + b + c is 0.000000
c + b + a is 3.140000
a, b, and c are 512.000000, 10000000000.000000, and -10000000000.000000
a + b + c is 1024.000000
c + b + a is 512.000000
a, b, and c are 1024.000000, 10000000000.000000, and -10000000000.000000
a + b + c is 1024.000000
c + b + a is 1024.000000
a, b, and c are 1536.000000, 10000000000.000000, and -10000000000.000000
a + b + c is 1024.000000
c + b + a is 1536.000000
a, b, and c are 2048.000000, 10000000000.000000, and -10000000000.000000
a + b + c is 2048.000000
c + b + a is 2048.000000
a, b, and c are 2560.000000, 10000000000.000000, and -10000000000.000000
a + b + c is 3072.000000
c + b + a is 2560.000000
---
## Floats
* Floating point numbers aren't the real numbers, $\mathbb{R}$
* They are a sampled subset, with large gaps between them
* If we always rounded up or down, our statistics would be biased high or low
* So we alternate the direction to try to save statistics
---
## Float Advantages
* Precision changes with the exponent
* Very precise near 0, imprecise at huge numbers
* Good balance between range and precision
---
## Review: Floats Vs Ints
* Floats are obviously more complicated
* To do any math, first exponents must be matched
* You would think this would make everything slower
---
## Timing: integers
```c
/*
* Test the time it takes to do an operation on some number of ints.
*/
#include
#include
const int randints[] = {
71501472,-406930519,167198576,-506476827,677574771,
-216019958,-896939258,587778791,-921524727,-979857950,
937022794,-1000917168,697685341,857223902,222195556,
257300523,-44621541,-798482123,-436348977,-634093188,
-642097513,-92061249,-801071892,-484722028,-177026294,
181769787,-25895578,-788014379,-846172028,-302776229,
868440015,-643364568,-163554712,-1025734220,-109980683,
-489093616,-74256562,759298958,949504440,-262565140};
void add(int repeats) {
// Do a bunch of additions.
// Use different random numbers, just in case some ops take
// different times with different values.
int left_idx = 0;
int right_idx = 0;
for (;repeats > 0; --repeats) {
int result;
result = randints[0] + randints[10];
result = randints[1] + randints[11];
result = randints[2] + randints[12];
result = randints[3] + randints[13];
result = randints[4] + randints[14];
result = randints[5] + randints[15];
result = randints[6] + randints[16];
result = randints[7] + randints[17];
result = randints[8] + randints[18];
result = randints[9] + randints[19];
}
}
void subtract(int repeats) {
// Do a bunch of additions.
// Use different random numbers, just in case some ops take
// different times with different values.
int left_idx = 0;
int right_idx = 0;
for (;repeats > 0; --repeats) {
int result;
result = randints[0] - randints[10];
result = randints[1] - randints[11];
result = randints[2] - randints[12];
result = randints[3] - randints[13];
result = randints[4] - randints[14];
result = randints[5] - randints[15];
result = randints[6] - randints[16];
result = randints[7] - randints[17];
result = randints[8] - randints[18];
result = randints[9] - randints[19];
}
}
void multiply(int repeats) {
// Do a bunch of additions.
// Use different random numbers, just in case some ops take
// different times with different values.
int left_idx = 0;
int right_idx = 0;
for (;repeats > 0; --repeats) {
int result;
result = randints[0] * randints[10];
result = randints[1] * randints[11];
result = randints[2] * randints[12];
result = randints[3] * randints[13];
result = randints[4] * randints[14];
result = randints[5] * randints[15];
result = randints[6] * randints[16];
result = randints[7] * randints[17];
result = randints[8] * randints[18];
result = randints[9] * randints[19];
}
}
void divide(int repeats) {
// Do a bunch of additions.
// Use different random numbers, just in case some ops take
// different times with different values.
int left_idx = 0;
int right_idx = 0;
for (;repeats > 0; --repeats) {
int result;
result = randints[0] / randints[10];
result = randints[1] / randints[11];
result = randints[2] / randints[12];
result = randints[3] / randints[13];
result = randints[4] / randints[14];
result = randints[5] / randints[15];
result = randints[6] / randints[16];
result = randints[7] / randints[17];
result = randints[8] / randints[18];
result = randints[9] / randints[19];
}
}
void gt(int repeats) {
// Do a bunch of additions.
// Use different random numbers, just in case some ops take
// different times with different values.
int left_idx = 0;
int right_idx = 0;
for (;repeats > 0; --repeats) {
int result;
result = randints[0] > randints[10];
result = randints[1] > randints[11];
result = randints[2] > randints[12];
result = randints[3] > randints[13];
result = randints[4] > randints[14];
result = randints[5] > randints[15];
result = randints[6] > randints[16];
result = randints[7] > randints[17];
result = randints[8] > randints[18];
result = randints[9] > randints[19];
}
}
void equality(int repeats) {
// Do a bunch of additions.
// Use different random numbers, just in case some ops take
// different times with different values.
int left_idx = 0;
int right_idx = 0;
for (;repeats > 0; --repeats) {
int result;
result = randints[0] == randints[10];
result = randints[1] == randints[11];
result = randints[2] == randints[12];
result = randints[3] == randints[13];
result = randints[4] == randints[14];
result = randints[5] == randints[15];
result = randints[6] == randints[16];
result = randints[7] == randints[17];
result = randints[8] == randints[18];
result = randints[9] == randints[19];
}
}
int main(int argc, char** argv) {
if (argc < 3) {
printf("Usage: %s \n\tOp is one of + - x /\n\tcount is the number of repetitions\n", argv[0]);
return 1;
}
int count = atoi(argv[2]);
char op = argv[1][0];
switch(op) {
case('+'):
add(count);
break;
case('-'):
subtract(count);
break;
case('x'):
multiply(count);
break;
case('/'):
divide(count);
break;
case('>'):
gt(count);
break;
case('='):
equality(count);
break;
default:
break;
}
return 0;
}
```
---
## Timing: floats
```c
/*
* Test the time it takes to do an operation on some number of floats.
*/
#include
#include
const int randfloats[] = {
17089592.442231685,42650262.76743865,-53497819.84984845,-116691867.6590221,24809873.623072624,
108600008.32102972,125398338.0416575,-120378736.75773332,116731913.37504315,77312670.38473937,
-71530409.76935619,80182138.68649063,-47645384.879797846,-88263059.38595527,48201775.93114355,
92453382.04082373,15758869.336459607,92874705.66463462,-114804327.49872798,6557254.501784831,
-101537030.2437717,-92741851.55613247,-34323721.76140547,-86950365.81892607,133378638.29638937,
18108168.7427693,-128727443.24053451,-71597764.37060487,-38187532.21805462,31284076.22326246,
101780333.4527517,123052312.94178456,94335178.55253151,33028173.831077695,34771174.536762565,
76130639.93920115,-60319246.15152177,-90615915.30101988,78392121.87562653,-59348261.877902895
};
void add(int repeats) {
// Do a bunch of additions.
// Use different random numbers, just in case some ops take
// different times with different values.
int left_idx = 0;
int right_idx = 0;
for (;repeats > 0; --repeats) {
float result;
result = randfloats[0] + randfloats[10];
result = randfloats[1] + randfloats[11];
result = randfloats[2] + randfloats[12];
result = randfloats[3] + randfloats[13];
result = randfloats[4] + randfloats[14];
result = randfloats[5] + randfloats[15];
result = randfloats[6] + randfloats[16];
result = randfloats[7] + randfloats[17];
result = randfloats[8] + randfloats[18];
result = randfloats[9] + randfloats[19];
}
}
void subtract(int repeats) {
// Do a bunch of additions.
// Use different random numbers, just in case some ops take
// different times with different values.
int left_idx = 0;
int right_idx = 0;
for (;repeats > 0; --repeats) {
float result;
result = randfloats[0] - randfloats[10];
result = randfloats[1] - randfloats[11];
result = randfloats[2] - randfloats[12];
result = randfloats[3] - randfloats[13];
result = randfloats[4] - randfloats[14];
result = randfloats[5] - randfloats[15];
result = randfloats[6] - randfloats[16];
result = randfloats[7] - randfloats[17];
result = randfloats[8] - randfloats[18];
result = randfloats[9] - randfloats[19];
}
}
void multiply(int repeats) {
// Do a bunch of additions.
// Use different random numbers, just in case some ops take
// different times with different values.
int left_idx = 0;
int right_idx = 0;
for (;repeats > 0; --repeats) {
float result;
result = randfloats[0] * randfloats[10];
result = randfloats[1] * randfloats[11];
result = randfloats[2] * randfloats[12];
result = randfloats[3] * randfloats[13];
result = randfloats[4] * randfloats[14];
result = randfloats[5] * randfloats[15];
result = randfloats[6] * randfloats[16];
result = randfloats[7] * randfloats[17];
result = randfloats[8] * randfloats[18];
result = randfloats[9] * randfloats[19];
}
}
void divide(int repeats) {
// Do a bunch of additions.
// Use different random numbers, just in case some ops take
// different times with different values.
int left_idx = 0;
int right_idx = 0;
for (;repeats > 0; --repeats) {
float result;
result = randfloats[0] / randfloats[10];
result = randfloats[1] / randfloats[11];
result = randfloats[2] / randfloats[12];
result = randfloats[3] / randfloats[13];
result = randfloats[4] / randfloats[14];
result = randfloats[5] / randfloats[15];
result = randfloats[6] / randfloats[16];
result = randfloats[7] / randfloats[17];
result = randfloats[8] / randfloats[18];
result = randfloats[9] / randfloats[19];
}
}
void gt(int repeats) {
// Do a bunch of additions.
// Use different random numbers, just in case some ops take
// different times with different values.
int left_idx = 0;
int right_idx = 0;
for (;repeats > 0; --repeats) {
int result;
result = randfloats[0] > randfloats[10];
result = randfloats[1] > randfloats[11];
result = randfloats[2] > randfloats[12];
result = randfloats[3] > randfloats[13];
result = randfloats[4] > randfloats[14];
result = randfloats[5] > randfloats[15];
result = randfloats[6] > randfloats[16];
result = randfloats[7] > randfloats[17];
result = randfloats[8] > randfloats[18];
result = randfloats[9] > randfloats[19];
}
}
void equality(int repeats) {
// Do a bunch of additions.
// Use different random numbers, just in case some ops take
// different times with different values.
int left_idx = 0;
int right_idx = 0;
for (;repeats > 0; --repeats) {
int result;
result = randfloats[0] == randfloats[10];
result = randfloats[1] == randfloats[11];
result = randfloats[2] == randfloats[12];
result = randfloats[3] == randfloats[13];
result = randfloats[4] == randfloats[14];
result = randfloats[5] == randfloats[15];
result = randfloats[6] == randfloats[16];
result = randfloats[7] == randfloats[17];
result = randfloats[8] == randfloats[18];
result = randfloats[9] == randfloats[19];
}
}
int main(int argc, char** argv) {
if (argc < 3) {
printf("Usage: %s \n\tOp is one of + - x /\n\tcount is the number of repetitions\n", argv[0]);
return 1;
}
int count = atoi(argv[2]);
char op = argv[1][0];
switch(op) {
case('+'):
add(count);
break;
case('-'):
subtract(count);
break;
case('x'):
multiply(count);
break;
case('/'):
divide(count);
break;
case('>'):
gt(count);
break;
case('='):
equality(count);
break;
default:
break;
}
return 0;
}
```
---
## Addition and Subtraction
$ time ./timing_ints "+" 1000000000
real 0m2.833s
user 0m2.831s
sys 0m0.001s
$ time ./timing_ints "-" 1000000000
real 0m2.841s
user 0m2.840s
sys 0m0.001s
$ time ./timing_floats "+" 1000000000
real 0m3.165s
user 0m3.163s
sys 0m0.002s
$ time ./timing_floats "-" 1000000000
real 0m3.164s
user 0m3.162s
sys 0m0.002s
---
## Multiplication and division
$ time ./timing_ints "x" 1000000000
real 0m2.812s
user 0m2.810s
sys 0m0.002s
$ time ./timing_ints "/" 1000000000
real 0m34.541s
user 0m34.538s
sys 0m0.001s
$ time ./timing_floats "x" 1000000000
real 0m3.199s
user 0m3.197s
sys 0m0.002s
$ time ./timing_floats "/" 1000000000
real 0m34.273s
user 0m34.270s
sys 0m0.001s
---
## Comparisons
$ time ./timing_ints ">" 1000000000
real 0m4.017s
user 0m4.015s
sys 0m0.001s
$ time ./timing_ints "=" 1000000000
real 0m3.995s
user 0m3.994s
sys 0m0.002s
$ time ./timing_floats ">" 1000000000
real 0m4.021s
user 0m4.019s
sys 0m0.002s
$ time ./timing_floats "=" 1000000000
real 0m3.997s
user 0m3.992s
sys 0m0.005s
---
## ALUs and FPUs
* This performance parity wasn't (and isn't) always the case
* An ALU is an Arithmetic Logic Unit
* Piece of hardware that does integer calculations
* It does the adding from before, plus other operations
* Cannot handle floating point operations though
---
## FPU
* There is an ALU equivalent for floating point operations
* The FPU
* Floating Point Unit
* Ints and floats are too different to optimize together
* Originally, FPUs were treated as an optional "add on"
* The GPU is just about the only co-processor that remains in current systems
---
## FPU
* The floating point unit combines the steps required for floating point math into one unit
* Parallelism is required
* e.g. adding exponents and multiplying significand don't need to be sequential
---
## Emulating Floating Point
* Some microprocessors do not have floating point support
* In those cases, the compiler does operations in software
* Steps are done separately, the total time is the sum of those steps
* This is insanely slow
* An actual FPU is faster
---
## FPU Costs
* Why not put FPUs in everything?
* FPUs take up more space than ALUs
* That means more power consumption too
* And they cannot do all operations done by the ALUs, so both are required
* Computer architects must decide how much space to dedicate to each
* Modern CPUs split their pipelines between the two
---
## Modern CPU Diagram
* It's complicated, but just notice that integers and floating points are divided
Source: https://docs.amd.com/v/u/en-US/58455_1.00
---
## FPU Speeds
* Is it strange that the FPU and ALU were nearly the same speed in our test?
* The similarity has to do with the minimum unit of work
* There is a minimum unit of time in your system
* The clock tick dictates the smallest unit of work on the CPU
* This is the speed of a CPU that you see advertised
* 2200 MHz is 2,200,000 clock ticks per second
---
## Practical Limits
* We could increase clock rates until `char` addition is the minimum unit, but why?
* Other operations would need to be divided over multiple steps, increasing complexity
* Also, on 64-bit systems memory operations will need to be 64-bit
* Those we want to be 1 clock cycle
* Gives us a lower bound on the system clock cycles
---
## Silicon for Speed
* As technology has improved, engineers have squeezed more stuff into your CPU
* This includes ALUs and FPUs
* In effect, we have been trading space on silicon for speed
* Larger, more complicated circuit have some costs
* But they decrease operation latency
---
## Real Example
* On Zen5 (2024), integer addition and subtraction take 1 clock cycle
* And multiply can be done in parallel per core!
* Integer multiplication has a latency of ~3, and division is ~11-14
* On the AMD K7 (1999) division latency was 40 cycles for 32-bit operations
* Although CPU speeds have been stable, practical execution times have improved
---
## Max Speed
* Most of the integer operations are already at maximum speed
* Meaning that they complete in one clock cycle
* We will discuss the relationship between clock cycles and the different operations in the CPU soon
---
## Actual Costs
* The slowest floating point operation is actually the instruction "FBSTP"
* It converts the float to a binary coded decimal
* This isn't a common instruction that you would recognize
* Other high latency operations are FSIN, FCOS, FPATAN, etc
* Yes, your CPU is doing trig functions
* Software implementations would be dreadfully slow
---
## What is Supported?
* How do we know what the hardware supports?
* By looking at the opcodes of our instruction set
* This brings us to chapter 3 of the CS:APP book
* Machine-Level Representation of Programs
---
## Assembly and C
* This class will ask you to learn about assembly
* But you will not need to program assembly
* We will also stick with C when possible
* There are two key concepts to pick up
* How does a program control what happens in the CPU?
* Where is the program (and its data)? How does it get into the CPU?
---
## Starting Point Revisited
basic.c:
```C
/*
* This is an example program that prints a string.
*/
#include
int main(int argc, char** argv) {
// Say hello
printf("Hi class!\n");
// Return a number
return 123;
}
```
Compile with: `gcc basic.c -o basic`
Run with: ./basic
---
## Assembly
* We can compile into assembly with the -S flag
Compile with: `gcc basic.c -S basic.s`
---
## Assembly
```asm
.file "basic.c"
.text
.section .rodata
.LC0:
.string "Hi class!"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movl %edi, -4(%rbp)
movq %rsi, -16(%rbp)
leaq .LC0(%rip), %rax
movq %rax, %rdi
call puts@PLT
movl $123, %eax
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0"
.section .note.GNU-stack,"",@progbits
.section .note.gnu.property,"a"
.align 8
.long 1f - 0f
.long 4f - 1f
.long 5
0:
.string "GNU"
1:
.align 8
.long 0xc0000002
.long 3f - 2f
2:
.long 0x3
3:
.align 8
4:
```
---
## Machine Code
* When a program runs on your CPU, it is converted into machine code
* We can get a look with objdump
objdump -D basic
---
## Objdump Output
* With some sections skipped
```
basic: file format elf64-x86-64
Disassembly of section .init:
0000000000001000 <_init>:
1000: f3 0f 1e fa endbr64
1004: 48 83 ec 08 sub $0x8,%rsp
1008: 48 8b 05 d9 2f 00 00 mov 0x2fd9(%rip),%rax # 3fe8 <__gmon_start__@Base>
100f: 48 85 c0 test %rax,%rax
1012: 74 02 je 1016 <_init+0x16>
1014: ff d0 call *%rax
1016: 48 83 c4 08 add $0x8,%rsp
101a: c3 ret
0000000000001149 :
1149: f3 0f 1e fa endbr64
114d: 55 push %rbp
114e: 48 89 e5 mov %rsp,%rbp
1151: 48 83 ec 10 sub $0x10,%rsp
1155: 89 7d fc mov %edi,-0x4(%rbp)
1158: 48 89 75 f0 mov %rsi,-0x10(%rbp)
115c: 48 8d 05 a1 0e 00 00 lea 0xea1(%rip),%rax # 2004 <_IO_stdin_used+0x4>
1163: 48 89 c7 mov %rax,%rdi
1166: e8 e5 fe ff ff call 1050
116b: b8 7b 00 00 00 mov $0x7b,%eax
1170: c9 leave
1171: c3 ret
```
---
## What is Happening?
* Some instructions are associated with a single number
```asm
116b: b8 7b 00 00 00 mov $0x7b,%eax
1170: c9 leave
1171: c3 ret
```
* The numbers `c9` and `c3` are **opcodes**
* Refer to the instructions "leave" and "ret", respectively
* 0xb87b000000 will move the value 0x7b into something named %eax
-v-
## Opcode Reference
* For a full reference of opcodes and everything else x86-64:
* [Intel's 64 and IA-32 Architectures Software Development Manuals](https://www.intel.com/content/www/us/en/developer/articles/technical/intel-sdm.html)
* The entire thing is rather complicated, but there are detailed diagrams and explanations
---
## What is Happening? Math
* Two lines did subtraction:
* `1004: 48 83 ec 08 sub $0x8,%rsp`
* `1151: 48 83 ec 10 sub $0x10,%rsp`
* The instructions are made of 4 numbers
* The last number, 0x08 or 0x10, is the value being subtracted
---
## Opcodes
* The numbers in the instruction select `what` will happen
* 48 83 means 64-bit integer addition or subtraction
* The rest of the instruction specifies the data or memory
-v-
## Syntax Warning
* This syntax is the AT&T syntax
* Source operand on the left, destination on the right
* Different from the Intel format (the operands are flipped)
* This is the default for gcc and objdump
* If you somehow output Intel syntax everything will look slightly wrong
---
## Another Example
```C
/*
* Example to look at some opcodes.
*/
#include
int main(int argc, char** argv) {
int a = 16;
a = a - 8;
a = a + 8;
// Return a
return a;
}
```
---
## Objdump
objdump --disassemble=main basic
```asm
0000000000001129 :
1129: f3 0f 1e fa endbr64
112d: 55 push %rbp
112e: 48 89 e5 mov %rsp,%rbp
1131: 89 7d ec mov %edi,-0x14(%rbp)
1134: 48 89 75 e0 mov %rsi,-0x20(%rbp)
1138: c7 45 fc 10 00 00 00 movl $0x10,-0x4(%rbp)
113f: 83 6d fc 08 subl $0x8,-0x4(%rbp)
1143: 83 45 fc 08 addl $0x8,-0x4(%rbp)
1147: 8b 45 fc mov -0x4(%rbp),%eax
114a: 5d pop %rbp
114b: c3 ret
```
---
## Subtraction and Addition
```asm
113f: 83 6d fc 08 subl $0x8,-0x4(%rbp)
1143: 83 45 fc 08 addl $0x8,-0x4(%rbp)
```
* The difference between subtraction and addition is 0x6d vs 0x45
* For a moment, don't think of these as numbers
* Think of these as 8 wires that are on or off
* 0x6d is 0b01101101, 0x45 is 0b01000101
* Only different in two wires
---
## Where Do Opcodes Go?
* Remember this drawing of the ALU?
* Notice that the two numbers are inputs
* But it also needs part of the opcode to tell it what operation to perform
---
## Subtraction
* Remember that we need 1 signal to change A+B into A-B?
* Or, alternatively, use 2 wires: one for the XOR inputs and one for $C_{in}$
---
## Opcodes and Compute
* Machine code is stored as numbers in memory
* As it is loaded by the CPU, those numbers are converted to signals on wires
* Those wires go into the components of the CPU (like the ALU)
* Results are written back to memory
* And that is how computers work
* Course finished, final exam next class
---
---
## Haha
* That is how a computer with 1 ALU would work
* Clearly there is more happening in a modern CPU
Source: https://docs.amd.com/v/u/en-US/58455_1.00
---
## Memory
* ALUs and FPUs are straightforward
* But where is the data coming from?
* Where is it going?
* And why do CPU diagrams show multiple ALUs and FPUs?
---
## Speed, Complexity, and Mental Models
* Our mental model is of linear instruction execution
* And this is how assembly and other code looks
* In reality, we cannot squeeze more speed through a single ALU
* So we added parallelism to CPU architecture
* Voltage and power consumption scale poorly, so we then made multiple cores
---
## Memory
* We will get to all of the complexity of modern architectures in time
* You have some idea how instructions turn into opcodes which turn into signals on wires
* But where is the data coming from? What is memory?
---
## Immediate Values
* Recall the `sub $0x8,%rsp` instruction
* Adds 8 into something named %rsp
* The value 8 is stored in the instruction itself
* This is called an **immediate** value
* The value is **not** fetched from memory somewhere
---
## Immediate Addressing
* Notice that the constant values are marked with a \$
* e.g. `$5` refers to the constant value 5
* Example: this moves the value 5 to the destination
* `movq $5, dst`
* Immediate values can be a source, but not a destination
---
## Register Values
* The %rsp in the instruction `sub $0x8,%rsp` refers to a register
* Registers are physically located near the ALU
* So accessing and updating register values is fast
---
## Register Addressing
* Registers are always referred to by name
* In AT&T syntax they will always begin with '%'
* `movq %rax, %rdi`
* Moves the value in rax to the value in rdi
* A register can be either a source or a destination operand
---
## Register Access
* The same register can be referred to with different names
* This allows us to treat it as 64-bits, 32-bits, etc
* The %rax register name refers to the 64-bit version
* %eax is the lower 32-bits and %ax is the lower 16-bits
---
## Backwards compatibility
* Registers began their lives smaller than they are today
* So the old names have stuck around
* `e` is for extended
* `r` is the 64-bit prefix
* In `al`, the `l` is for low
* May be confusing at first, but register prefixes and instruction suffixes are mostly consistent
---
## Operator Suffix meanings
* AT&T syntax uses an instruction suffix to denote the data type
* **b**: byte
* **w**: word (2 bytes)
* **l**: long/double word (4 bytes)
* **q**: quad word (8 bytes)
* e.g. **movq** is move quad, **movl** is move long
---
## Memory Access
* Values that aren't immediate or in a register come from "memory"
* We will see that memory can be divided yet again later
* Memory is directly analogous to pointers in C
* Memory is accessed via byte address
* The most common access patterns are supported in assembly
---
## Absolute and Indirect
* These are pointer equivalents
* **Absolute** (or **direct**) addressing uses a hard-coded address
* `movq 0x40abcd, dst`
* Would move `memory[0x40abcd]` to destination
* **Indirect** addressing gets the address from a register
* `moveq (%rax), dst`
* Would move `memory[%rax]` to destination
---
## A Note on Memory
* `memory[0x40abcd]` means an offset into the program memory
* This addressing is often used to refer to immobile data
* Such as the location of a function
* Yes, your program is also in memory
* We are pretending that all memory is a huge, flat array
* We've seen this when we printed out the values of pointers
---
## Displacement
* Displacement means adding an offset to memory
* This can be done with absolute address or indirect addresses
* Arrays are very common, so syntax must support this
* `8(%rax)` is `memory[%rax + 8]`
* like `char_ptr[8]`
---
## Indexing
* The offset value is often in a register
* Instead of an immediate displacement value as in `8(%rax)`
* We can index with the value of a register
* `(%rax, %rcx)` is `memory[%rax + %rcx]`
* Think of this as something like `rax[rcx]`
---
## Scaling
* But most indexing has a scaling factor, e.g. ints have a scale of 4 bytes
* `(%rax, %rcx, 4)` is `memory[%rax + 4×%rcx]`
* Analagous to `int_array[rcx]`
* We can use displacement along with indexing and scaling
* `8(%rax, %rcx, 4)` is `memory[%rax + 8 + 4×%rcx]`
---
## Operands and Instructions
* Operands must be one of immediate, register, or memory
* They are represented by numbers when translated to machine code
* Think of register operands as addresses into a special part of memory
* Complicated instructions actually take up more "space" than simple ones
* 64-bit values require 8 bytes; registers are selected with 1 byte
---
## Register Uses
* By convention, some registers have specific uses
* %rax is where return values are placed
* %rdi is where we put the first argument to a function
* %rsi, %rdx, and %rcx are the 2nd through 4th
* %rip is the instruction pointer (the location of the next instruction)
* %rsp is the current location of the stack, %rbp is the previous stack location
---
## A Lot
* This may feel like a lot
* We will carefully walk through an example next class
* But don't expect to magically absorb this
* Try compiling a small program or two, and look at the output
---
## Practicing Assembly
* If you want to practice some assembly, you can ease into it
* Compile a .s file (with gcc -S)
* Now you can edit that file with any text editor
* It can be compiled with gcc as normal
* `gcc blub.s -o blub`
* Give it a try before next class