1 Machine-Level Programming II: Basics Comp 21000: Introduction to Computer Organization & Systems Spring 2015 Instructor: John Barr * Modified slides from the book “Computer Systems: a Programmer’s Perspective”, Randy Bryant & David O’Hallaron, 2011
2 Machine Programming I: Basics History of Intel processors and architectures C, assembly, machine code Assembly Basics: Registers, operands, move Intro to x86-64
3 CISC Properties Instruction can reference different operand types Immediate, register, memory Arithmetic operations can read/write memory Memory reference can involve complex computation Rb + S*Ri + D Useful for arithmetic expressions, too Instructions can have varying lengths IA32 instructions can range from 1 to 15 bytes
4 Features of IA32 instructions IA32 instructions can be from 1 to 15 bytes. More commonly used instructions are shorter Instructions with fewer operands are shorter Each instruction has an instruction format Each instruction has a unique byte representation i.e., instruction pushl %ebp has encoding 55 IA32 started as a 16 bit language So IA32 calls a 16-bit piece of data a “word” A 32-bit piece of data is a “double word” or a “long word” A 64-bit piece of data is a “quad word”
5 CPU Assembly Programmer’s View (review) Programmer-Visible State PC: Program counter Address of next instruction Called “EIP” (IA32) or “RIP” (x86-64) Register file Heavily used program data 8 named locations, 32 bit values (x86-64) Condition codes Store status information about most recent arithmetic operation Used for conditional branching PC Registers Memory Object Code Program Data OS Data Addresses Data Instructions Stack Condition Codes Memory Byte addressable array Code, user data, (some) OS data Includes stack used to support procedures
6 Instruction format Assembly language instructions have a very rigid format For most instructions the format is movl Source, Dest Instruction name Source of data for the instruction: Registers/memory Destination of instruction results: Registers/memory Instruction suffix
7 Every operation in GAS has a single-character suffix Denotes the size of the operand Example: basic instruction is mov Can move byte ( movb ), word ( movw ) and double word ( movl ) Note that floating point operations have entirely different instructions. C declaration Intel data type GAS suffixSize (bytes) char Byteb1 short Wordw2 int Double wordl4 unsigned Double wordl4 long int Double wordl4 unsigned long Double wordl4 char * Double wordl4 float Single precision s4 double Double precision l8 long double Extended precision t10/12
8 Registers 8 32-bit general purpose registers Programmers/compilers can use these All registers begin with %e Rest of name is historical: from 8086 Registers originally had specific purposes No restrictions on use of registers in commands However, some instructions use fixed registers as source/destination In procedures there are different conventions for saving/restoring the first 3 registers (%eax, %eac, %edx) than the next 3 (%ebx, %edi, %esi). Final two registers have special purposes in procedures –%ebp (frame pointer) –%esp (stack pointer) Will discuss all these later
9 Registers 8 32-bit general purpose registers The low-order bytes of the first 4 registers can be independently read or written by byte operation instructions. Done for backward compatibility with 8008 and 8080 (1970’s!) When a byte of the register is changed, the rest of the register is unaffected. The low-order 2 bytes (16 bits, i.e., a single word) can be independently read/wrote by word operation instructions Comes from bit heritage When a word of the register is changed, the rest of the register is unaffected. See next slide!
10 Integer Registers (IA32) %eax %ecx %edx %ebx %esi %edi %esp %ebp %ax %cx %dx %bx %si %di %sp %bp %ah %ch %dh %bh %al %cl %dl %bl 16-bit virtual registers (%ax, %cx,dx, …) (backwards compatibility) general purpose accumulate counter data base source index destination index stack pointer base pointer Origin (mostly obsolete) 8-bit register (%ah, %al,ch, …) 32-bit register (%eax, %ecx, …)
11 Moving Data: IA32 Moving Data movl Source, Dest: Move 4-byte (“long”) word Lots of these in typical code Operand Types Immediate: Constant integer data Example: $0x400, $-533 Like C constant, but prefixed with ‘$’ Encoded with 1, 2, or 4 bytes Register: One of 8 integer registers Example: %eax, %edx But %esp and %ebp reserved for special use Others have special uses for particular instructions Memory: 4 consecutive bytes of memory at address given by register Simplest example: (%eax) Various other “address modes” %eax %ecx %edx %ebx %esi %edi %esp %ebp
12 Simple Memory Addressing Modes Normal(R)Mem[Reg[R]] Register R specifies memory address movl (%ecx),%eax DisplacementD(R)Mem[Reg[R]+D] Register R specifies start of memory region Constant displacement D specifies offset movl 8(%ebp),%edx
13 Simple Addressing Modes (cont) Immediate$ImmImm The value Imm is the value that is used movl $4096,%eax AbsoluteImmMem[Imm] No dollar sign before the number The number is the memory address to use movl4096,%edx The book has more details on addressing modes!!
14 movl Operand Combinations Cannot do memory-memory transfer with a single instruction movl Imm Reg Mem Reg Mem Reg Mem Reg SourceDestC Analog movl $0x4,%eaxtemp = 0x4; movl $-147,(%eax)*p = -147; movl %eax,%edxtemp2 = temp1; movl %eax,(%edx)*p = temp; movl (%eax),%edxtemp = *p; Src,Dest
15 mov instructions InstructionEffectDescription movl S,D movw S,D D S Move double word Move word movb S,D D SMove byte movsbl S,D D SignExtend (S) Move sign-extended byte movzbl S,D D ZeroExtend Move zero-extended byte Notes:1. byte movements must use one of the 8 single-byte registers 2. word movements must use one of the 8 2-byte registers 3. movsbl takes single byte source, performs sign-extension on high-order 24 bits, copies the resulting double word to dest. 4. movzbl takes single byte source, performs adds 24 0’s to high-order bits, copies the resulting double word to dest
16 mov instruction example Assume that %dh = 8D and %eax = at the beginning of each of these instructions instructionresult 1. movb %dh, %al %eax = 2. movsbl %dh, %eax %eax = 3. movzbl %dh, %eax %eax = D FFFFFF8D D
17 mov instruction example instructionaddressing mode 1. movl $0x4050, %eax 2. movl %ebp, %esp 3. movl (%ecx), %eax 4. movl $-17, (%esp) 5. movl %eax, -12(%ebp) Imm Reg Reg Reg Mem Reg Imm Mem Reg Mem (Displacement)
18 Special memory areas: stack and heap Stack Space in Memory allocated to each running program (called a process) Used to store “temporary” variable values Used to store variables for functions Heap Space in Memory allocated to each running program (process) Used to store dynamically allocated variables Kernel virtual memory Memory mapped region for shared libraries Run-time heap (created at runtime by malloc) User stack (created at runtime) Unused Read/write segment (.data,.bss ) Read-only segment (.init,.text,.rodata )
19 Using Simple Addressing Modes void swap(int *xp, int *yp) { int t0 = *xp; int t1 = *yp; *xp = t1; *yp = t0; } Body Set Up Finish swap: pushl %ebp movl %esp,%ebp pushl %ebx movl 8(%ebp), %edx movl 12(%ebp), %ecx movl (%edx), %ebx movl (%ecx), %eax movl %eax, (%edx) movl %ebx, (%ecx) popl %ebx popl %ebp ret Use %ebp to manipulate the stack; we’ll discuss why later
20 Using Simple Addressing Modes void swap(int *xp, int *yp) { int t0 = *xp; int t1 = *yp; *xp = t1; *yp = t0; } swap: pushl %ebp movl %esp,%ebp pushl %ebx movl8(%ebp), %edx movl12(%ebp), %ecx movl(%edx), %ebx movl(%ecx), %eax movl%eax, (%edx) movl%ebx, (%ecx) popl%ebx popl%ebp ret Body Set Up Finish
21 Understanding Swap void swap(int *xp, int *yp) { int t0 = *xp; int t1 = *yp; *xp = t1; *yp = t0; } Stack (in memory) RegisterValue %edxxp %ecxyp %ebxt0 %eaxt1 yp xp Rtn adr Old % ebp %ebp Offset Old % ebx -4 %esp movl8(%ebp), %edx# edx = xp movl12(%ebp), %ecx# ecx = yp movl(%edx), %ebx# ebx = *xp (t0) movl(%ecx), %eax# eax = *yp (t1) movl%eax, (%edx)# *xp = t1 movl%ebx, (%ecx)# *yp = t0 Pushed on the stack by the calling function NOTE: MEMORY GROWS UP; 0 IS AT BOTTOM!
22 Understanding Swap 0x120 0x124 Rtn adr %ebp Offset Address 0x124 0x120 0x11c 0x118 0x114 0x110 0x10c 0x108 0x104 0x100 yp xp %eax %edx %ecx %ebx %esi %edi %esp %ebp0x104 movl8(%ebp), %edx# edx = xp movl12(%ebp), %ecx# ecx = yp movl(%edx), %ebx# ebx = *xp (t0) movl(%ecx), %eax# eax = *yp (t1) movl%eax, (%edx)# *xp = t1 movl%ebx, (%ecx)# *yp = t0 Assume that the main() uses this memory for variables
23 Understanding Swap 0x120 0x124 Rtn adr %ebp Offset Address 0x124 0x120 0x11c 0x118 0x114 0x110 0x10c 0x108 0x104 0x100 yp xp %eax %edx %ecx %ebx %esi %edi %esp %ebp 0x124 0x104 0x120 movl8(%ebp), %edx# edx = xp movl12(%ebp), %ecx# ecx = yp movl(%edx), %ebx# ebx = *xp (t0) movl(%ecx), %eax# eax = *yp (t1) movl%eax, (%edx)# *xp = t1 movl%ebx, (%ecx)# *yp = t0
24 Understanding Swap 0x120 0x124 Rtn adr %ebp Offset Address 0x124 0x120 0x11c 0x118 0x114 0x110 0x10c 0x108 0x104 0x100 yp xp %eax %edx %ecx %ebx %esi %edi %esp %ebp 0x120 0x104 0x124 movl8(%ebp), %edx# edx = xp movl12(%ebp), %ecx# ecx = yp movl(%edx), %ebx# ebx = *xp (t0) movl(%ecx), %eax# eax = *yp (t1) movl%eax, (%edx)# *xp = t1 movl%ebx, (%ecx)# *yp = t0
Understanding Swap 0x120 0x124 Rtn adr %ebp Offset Address 0x124 0x120 0x11c 0x118 0x114 0x110 0x10c 0x108 0x104 0x100 yp xp %eax %edx %ecx %ebx %esi %edi %esp %ebp 0x124 0x x104 movl8(%ebp), %edx# edx = xp movl12(%ebp), %ecx# ecx = yp movl(%edx), %ebx# ebx = *xp (t0) movl(%ecx), %eax# eax = *yp (t1) movl%eax, (%edx)# *xp = t1 movl%ebx, (%ecx)# *yp = t0
26 Understanding Swap 0x120 0x124 Rtn adr %ebp Offset Address 0x124 0x120 0x11c 0x118 0x114 0x110 0x10c 0x108 0x104 0x100 yp xp %eax %edx %ecx %ebx %esi %edi %esp %ebp 456 0x124 0x120 0x movl8(%ebp), %edx# edx = xp movl12(%ebp), %ecx# ecx = yp movl(%edx), %ebx# ebx = *xp (t0) movl(%ecx), %eax# eax = *yp (t1) movl%eax, (%edx)# *xp = t1 movl%ebx, (%ecx)# *yp = t0
Understanding Swap 0x120 0x124 Rtn adr %ebp Offset -4 Address 0x124 0x120 0x11c 0x118 0x114 0x110 0x10c 0x108 0x104 0x100 yp xp %eax %edx %ecx %ebx %esi %edi %esp %ebp 456 0x124 0x x movl8(%ebp), %edx# edx = xp movl12(%ebp), %ecx# ecx = yp movl(%edx), %ebx# ebx = *xp (t0) movl(%ecx), %eax# eax = *yp (t1) movl%eax, (%edx)# *xp = t1 movl%ebx, (%ecx)# *yp = t0
28 Understanding Swap 0x120 0x124 Rtn adr %ebp Offset Address 0x124 0x120 0x11c 0x118 0x114 0x110 0x10c 0x108 0x104 0x100 yp xp %eax %edx %ecx %ebx %esi %edi %esp %ebp 456 0x124 0x120 0x movl8(%ebp), %edx# edx = xp movl12(%ebp), %ecx# ecx = yp movl(%edx), %ebx# ebx = *xp (t0) movl(%ecx), %eax# eax = *yp (t1) movl%eax, (%edx)# *xp = t1 movl%ebx, (%ecx)# *yp = t0
29 Understanding Assembly void mystery(int a, int b) { int t0 = __________; int t1 = __________; int t2 = __________; return _________; } int main() { int x = mystery(10, 33); return 0; } Complete this exercise on your handout sheet!
30 Understanding Assembly void mystery (int a, int b) { int t0 = __________; int t1 = __________; int t2 = __________; return _________; } Stack (in memory) RegisterValue %edx %ecx %ebx %eax Rtn adr ?? %esp Offset ?? -4 0x :push %ebp 0x :mov %esp,%ebp 0x :sub $0x10,%esp Pushed on the stack by the calling function NOTE: Stack often increased to account for local variables ?? b a Fill out the addresses for the stack on your handout
31 Understanding Assembly void mystery (int a, int b) { int t0 = __________; int t1 = __________; int t2 = __________; return _________; } Stack (in memory) RegisterValue %edx %ecx %ebx %eax Rtn adr old %ebp %esp Offset ?? -4 0x :push %ebp 0x :mov %esp,%ebp 0x :sub $0x10,%esp Pushed on the stack by the calling function NOTE: Stack often increased arbitrary amount for local variables ?? b a
32 Understanding Assembly void mystery (int a, int b) { int t0 = __________; int t1 = __________; int t2 = __________; return _________; } Stack (in memory) RegisterValue %edx %ecx %ebx %eax Rtn adr old %ebp %esp Offset ?? -4 0x :push %ebp 0x :mov %esp,%ebp 0x :sub $0x10,%esp Pushed on the stack by the calling function NOTE: Stack often increased arbitrary amount for local variables ?? b a %ebp
33 Understanding Assembly void mystery (int a, int b) { int t0 = __________; int t1 = __________; int t2 = __________; return _________; } Stack (in memory) RegisterValue %edx %ecx %ebx %eax Rtn adr old %ebp %ebp Offset ?? -4 0x :push %ebp 0x :mov %esp,%ebp 0x :sub $0x10,%esp Pushed on the stack by the calling function NOTE: Stack often increased arbitrary amount for local variables ?? %esp b a
34 Understanding Assembly void mystery (int a, int b) { int t0 = __________; int t1 = __________; int t2 = __________; return _________; } Stack (in memory) RegisterValue %edx %ecx %ebx %eax Rtn adr Old % ebp %ebp Offset ?? -4 %esp 0x :mov 0x8(%ebp),%eax 0x :add $0x5,%eax 0x c :mov %eax,-0xc(%ebp) Pushed on the stack by the calling function ?? b a ?? -8
35 Understanding Assembly void mystery (int a, int b) { int t0 = __________; int t1 = __________; int t2 = __________; return _________; } Stack (in memory) RegisterValue %edx %ecx %ebx %eax Rtn adr Old % ebp %ebp Offset ?? -4 %esp 0x :mov 0x8(%ebp),%eax 0x :add $0x5,%eax 0x c :mov %eax,-0xc(%ebp) Pushed on the stack by the calling function ?? b a ?? -8
36 Understanding Assembly void mystery (int a, int b) { int t0 = a + 5; int t1 = __________; int t2 = __________; return _________; } Stack (in memory) RegisterValue %edx %ecx %ebx %eax Rtn adr Old % ebp %ebp Offset ?? -4 %esp 0x :mov 0x8(%ebp),%eax 0x :add $0x5,%eax 0x c :mov %eax,-0xc(%ebp) Pushed on the stack by the calling function 15 ?? b t0 a ?? -8
37 Understanding Assembly void mystery (int a, int b) { int t0 = a + 5; int t1 = __________; int t2 = __________; return _________; } Stack (in memory) RegisterValue %edx %ecx %ebx %eax Rtn adr Old % ebp %ebp Offset ?? -4 %esp 0x f :mov 0xc(%ebp),%eax 0x :sub $0x3,%eax 0x :mov %eax,-0x8(%ebp) Pushed on the stack by the calling function 15 ?? b t0 a %eax is often used for temporary values ?? -8
38 Understanding Assembly void mystery (int a, int b) { int t0 = a + 5; int t1 = __________; int t2 = __________; return _________; } Stack (in memory) RegisterValue %edx %ecx %ebx %eax Rtn adr Old % ebp %ebp Offset ?? -4 %esp 0x f :mov 0xc(%ebp),%eax 0x :sub $0x3,%eax 0x :mov %eax,-0x8(%ebp) Pushed on the stack by the calling function 15 ?? b t0 a ?? -8
39 Understanding Assembly void mystery (int a, int b) { int t0 = a + 5; int t1 = b - 3; int t2 = __________; return _________; } Stack (in memory) RegisterValue %edx %ecx %ebx %eax Rtn adr Old % ebp %ebp Offset ?? -4 %esp 0x f :mov 0xc(%ebp),%eax 0x :sub $0x3,%eax 0x :mov %eax,-0x8(%ebp) Pushed on the stack by the calling function 15 ?? b t0 a t1
40 Understanding Assembly void mystery (int a, int b) { int t0 = a + 5; int t1 = b - 3; int t2 = __________; return _________; } Stack (in memory) RegisterValue %edx %ecx %ebx %eax Rtn adr Old % ebp %ebp Offset ?? -4 %esp 0x :mov -0x8(%ebp),%eax 0x b :mov -0xc(%ebp),%edx 0x e :add %edx,%eax 0x :mov %eax,-0x4(%ebp) Pushed on the stack by the calling function 15 ?? b t0 a t1 Finish this exercise on your handout sheet
41 Understanding Assembly void mystery (int a, int b) { int t0 = a + 5; int t1 = b - 3; int t2 = t0 + t1; return t2; } Stack (in memory) RegisterValue %edx15 %ecx %ebx %eax Rtn adr Old % ebp %ebp Offset %esp 0x :mov -0x4(%ebp),%eax 0x :leave 0x :ret Pushed on the stack by the calling function 15 ?? b t0 a t1 t2 NOTE: by convention the return value is ALWAYS stored in %eax
42 Complete Memory Addressing Modes Most General Form D(Rb,Ri,S)Mem[Reg[Rb]+S*Reg[Ri]+ D] D: Constant “displacement” 1, 2, or 4 bytes Rb: Base register: Any of 8 integer registers Ri:Index register: Any, except for %esp Unlikely you’d use %ebp, either S: Scale: 1, 2, 4, or 8 (why these numbers?) Special Cases (Rb,Ri)Mem[Reg[Rb]+Reg[Ri]] D(Rb,Ri)Mem[Reg[Rb]+Reg[Ri]+D] (Rb,Ri,S)Mem[Reg[Rb]+S*Reg[Ri]] D(Rb,Ri,S)Mem[Reg[Rb]+S*Reg[Ri]+D]
43 Address Computation Examples %edx %ecx 0xf000 0x100 ExpressionComputationAddress 0x8(%edx) (%edx,%ecx) (%edx,%ecx,4) 0x80(,%edx,2)
44 Address Computation Examples %edx %ecx 0xf000 0x100 ExpressionComputationAddress 0x8(%edx) (%edx,%ecx) (%edx,%ecx,4) 0x80(,%edx,2) 0xf x80xf008 0xf x1000xf100 0xf *0x1000xf400 2 *0xf x800x1e080
45 Address Computation Instruction leal Src,Dest Src is address mode expression Set Dest to address denoted by expression Format Looks like a memory access Does not actually access memory Rather, calculates the memory address, then stores in a register Uses Computing addresses without a memory reference E.g., translation of p = &x[i]; Computing arithmetic expressions of the form x + k*y k = 1, 2, 4, or 8. lea = load effective address
46 Example Converted to ASM by compiler: int mul12(int x) { return x*12; } int mul12(int x) { return x*12; } leal (%eax,%eax,2), %eax ;t <- x+x*2 sall $2, %eax ;return t<<2 ; or t * 4 leal (%eax,%eax,2), %eax ;t <- x+x*2 sall $2, %eax ;return t<<2 ; or t * 4
47 Address Computation Instruction ExpressionResult leal 6(%eax), %edx leal (%eax, %ecx), %edx leal (%eax, %ecx, 4), %edx leal 7(%eax, %eax, 8), %edx leal 0xA(,%eax, 4), %edx leal 9(%eax,%ecx, 2), %edx Assume %eax holds value x and %ecx holds value y * Note the leading comma in the next to last entry
48 Address Computation Instruction ExpressionResult leal 6(%eax), %edx leal (%eax, %ecx), %edx leal (%eax, %ecx, 4), %edx leal 7(%eax, %eax, 8), %edx leal 0xA(,%eax, 4), %edx leal 9(%eax,%ecx, 2), %edx Assume %eax holds value x and %ecx holds value y * Note the leading comma in the next to last entry %edx x + 6 %edx x + (y * 2) + 9 %edx (x * 4) + 10 %edx x + (x * 8) + 7 = (x * 9) + 7 %edx x + (y * 4) %edx x + y
49 pushl and popl instructions InstructionEffectDescription pushl SR[%esp] R[%esp]–4; M[R[%esp]] S Push on runtime stack popl DD M[R[%esp]]; R[%esp] R[%esp]+4 Pop from runtime stack Notes:1. both instructions take a single operand 2. Stack is a place in memory allocated to a process 3. Stack grows from smaller addresses to larger addresses 4. %esp holds the address of the current top of stack 5. When pushl a value, first increment %esp by 4, then write the value at the new top of stack address. Effect of a pushl %ebp instruction: subl $4, %esp movl %ebp, (%esp) Effect of a popl %eax instruction: movl (%esp), %eax subl$4, %esp
50 Increasing address Stack “top” Stack “bottom” 0x108 Stack “top” Stack “bottom” 0x104 Stack “top” Stack “bottom” 0x108 0x x108 %eax %edx %esp Initially 0x x104 %eax %edx %esp pushl %eax 0x123 0x108 %eax %edx %esp popl %edx 0x123 0x By convention we draw the stack with the top towards the bottom 2. IA32 stack grows toward lower addresses Stack Grows Down
51 Machine Programming I: Basics History of Intel processors and architectures C, assembly, machine code Assembly Basics: Registers, operands, move Intro to x86-64
52 Data Representations: IA32 + x86-64 Sizes of C Objects (in Bytes) C Data TypeGeneric 32-bitIntel IA32x86-64 unsigned444 int444 long int448 char111 short222 float444 double888 long double810/1216 char *448 –Or any other pointer
53 %rsp x86-64 Integer Registers Extend existing registers. Add 8 new ones. Make %ebp / %rbp general purpose %eax %ebx %ecx %edx %esi %edi %esp %ebp %r8d %r9d %r10d %r11d %r12d %r13d %r14d %r15d %r8 %r9 %r10 %r11 %r12 %r13 %r14 %r15 %rax %rbx %rcx %rdx %rsi %rdi %rbp
54 Instructions Long word l (4 Bytes) ↔ Quad word q (8 Bytes) New instructions: movl ➙ movq addl ➙ addq sall ➙ salq etc. 32-bit instructions that generate 32-bit results Set higher order bits of destination register to 0 Example: addl
55 32-bit code for swap void swap(int *xp, int *yp) { int t0 = *xp; int t1 = *yp; *xp = t1; *yp = t0; } Body Set Up Finish swap: pushl %ebp movl %esp,%ebp pushl %ebx movl8(%ebp), %edx movl12(%ebp), %ecx movl(%edx), %ebx movl(%ecx), %eax movl%eax, (%edx) movl%ebx, (%ecx) popl%ebx popl%ebp ret
56 64-bit code for swap Operands passed in registers (why useful?) First ( xp ) in %rdi, second ( yp ) in %rsi 64-bit pointers No stack operations required 32-bit data Data held in registers %eax and %edx movl operation void swap(int *xp, int *yp) { int t0 = *xp; int t1 = *yp; *xp = t1; *yp = t0; } Body Set Up Finish swap: movl(%rdi), %edx movl(%rsi), %eax movl%eax, (%rdi) movl%edx, (%rsi) ret
57 64-bit code for long int swap 64-bit data Data held in registers %rax and %rdx movq operation “q” stands for quad-word void swap(long *xp, long *yp) { long t0 = *xp; long t1 = *yp; *xp = t1; *yp = t0; } Body Set Up Finish swap_l: movq (%rdi), %rdx movq (%rsi), %rax movq %rax, (%rdi) movq %rdx, (%rsi) ret
58 Machine Programming I: Summary History of Intel processors and architectures Evolutionary design leads to many quirks and artifacts C, assembly, machine code Compiler must transform statements, expressions, procedures into low-level instruction sequences Assembly Basics: Registers, operands, move The x86 move instructions cover wide range of data movement forms Intro to x86-64 A major departure from the style of code seen in IA32