CS367 Program Control
outline program control condition codes branching looping conditional moves switches (special case of branching)
Condition Codes
Processor State (x86-64, Partial) Info about currently executing program Temporary data ( %rax, ) Location of runtime stack ( %rsp ) Location of next instruction ( %rip, ) Status of recent tests (CF,ZF,SF,OF) Current stack top Registers %rax %rbx %rcx %rdx %rsi %rdi %rsp %rbp %rip %r8 %r9 %r10 %r11 %r12 %r13 %r14 %r15 Instruc7on pointer next instruc7on CF ZF SF OF Condi7on codes
Condition Codes (Implicit Setting) Single bit registers CF Carry Flag (for unsigned) SF Sign Flag (for signed) ZF Zero Flag OF Overflow Flag (for signed) Implicitly set (think of it as side effect) by arithmetic operations Example: addq Src,Dest t = a+b ZF set if t==0 CF set if carry out (unsigned overflow) SF set if t<0 (as signed) OF set if two s-complement (signed) overflow (a>0 && b>0 && t<0) (a<0 && b<0 && t>=0) Not set by leaq instruction
Condition Codes (Explicit Setting: Compare) Explicit Setting by Compare Instruction cmpq Src2, Src1 cmpq b, a (like computing (a-b) without setting destination) CF set if carryout (unsigned) ZF set if a==b SF set if (a-b)<0 (as signed) OF set if (signed) overflow (a>0 && b<0 && (a-b)<0) (a<0 && b>0 && (a-b)>0)
Condition Codes (Explicit Setting: Test) Explicit Setting by Test instruction testq Src2, Src1 testq b, a like computing a&b without setting destination Sets condition codes based on value of Src1 & Src2 Useful to have one of the operands be a mask ZF set when a&b == 0 SF set when a&b < 0
Reading Condition Codes SetX Instructions Set low-order byte of destination to 0 or 1 based on combinations of condition codes Doesn't alter other (7) bytes SetX Condi7on Descrip7on sete ZF Equal / Zero setne ~ZF Not Equal / Not Zero sets SF Nega7ve setns ~SF Nonnega7ve setg ~(SF^OF)&~ZF Greater (Signed) setge ~(SF^OF) Greater or Equal (Signed) setl (SF^OF) Less (Signed) setle (SF^OF) ZF Less or Equal (Signed) seta ~CF&~ZF Above (unsigned) setb CF Below (unsigned)
x86-64 Integer Registers %rax %al %r8 %r8b %rbx %bl %r9 %r9b %rcx %cl %r10 %r10b %rdx %dl %r11 %r11b %rsi %sil %r12 %r12b %rdi %dil %r13 %r13b %rsp %spl %r14 %r14b %rbp %bpl %r15 %r15b Can reference low-order byte: sometimes 'l' for lower, or 'b' for byte
Reading Condition Codes (Cont.) Typically use movzbl to finish job "move, with zero-extending, a byte to a long-word." also zeroes out upper 32-bit others: movzbq (same as movzbl!), movsbl/movsbq (sign-extend) int gt (long x, long y) { return x > y; Register %rdi %rsi %rax Use(s) Argument x Argument y Return value cmpq %rsi, %rdi # Compare x:y. note ordering! setg %al # record whether > relation holds. movzbl %al, %eax # Zero rest of %rax (l isn't a typo!) ret
practice: condition codes (on pytania)
Branching
Jumping j<rel> instructions Jump to different part of code depending on condition codes jx Condition Description jmp 1 Unconditional je ZF Equal / Zero jne ~ZF Not Equal / Not Zero js SF Negative jns ~SF Nonnegative jg ~(SF^OF)&~ZF Greater (Signed) jge ~(SF^OF) Greater or Equal (Signed) jl (SF^OF) Less (Signed) jle (SF^OF) ZF Less or Equal (Signed) ja ~CF&~ZF Above (unsigned) jb CF Below (unsigned)
Conditional Branch Example long biggest (long x, long y) { long result = x; if (y > x) result = y; return result; biggest: movq %rdi, %rax # result = x cmpq %rsi, %rdi # x:y jg.l4 # jump when x>y movq %rsi, %rax # y>x, update result.l4: #!(y>x) ret jg: "jump when last comparison was a greater-than". branch occurs with opposite condition ( ) Register %rdi %rsi %rax Use(s) Argument x Argument y Return value
Converting C branching to assembly if-else test condition, branch or fall through accordingly. may need extra labels/jumps to go to correct place afterwards. goto direct translation; jmp (unconditionally) to given label. usually guarded by if-statement anyways. switch statements more complicated either "decision tree" of condition-checkbranches, or use of a jump table.
if-statements C if (expr) { thenstmt else { elsestmt assembly <test expr> j<rel> nope # when!expr thenstmt jmp done nope: elsestmt done: if (x==y) { z = 100; else { z = 0; cmpq %rsi, %rdi # x:y jne nope movq $100,%rax jmp done nope: movq $0, %rax done:...
goto-statements they don't even have to be inside an if, but that's the most common. if (x==y) { goto outer; C if (expr) { goto place... place:... assembly <test expr> j<rel> nope #when!expr jmp X nope:... place:... cmpq %rsi, %rdi # x:y jne nope jmp outer nope:...
practice: branching (on pytania)
Loops
converting loop statements while loops: similar to if-statement, but jumps to previous label. must guard against initial iteration we first jump to the test. do-while loops: same as while, w/o guarding against 1 st iteration. for loops: logically convert to while loop.
do-while loops C assembly do { y+=10; x++; while(x<5); y*=100; do { body while (expr); nextstmt loop: body test: <test expr> j<rel> loop #when test done: nextstmt loop: addq $10, %rsi # y incq %rdi # x test: cmpq $5, %rdi # x:5 jl loop done: imulq $100, %rsi
while loops C while (expr) { body nextstmt assembly jmp test loop: body test: <test expr> j<rel> loop #when test done: nextstmt while(x<5) { y+=10; x++; y*=100; jmp test loop: addq $10, %rsi # y incq %rdi # x test: cmpq $5, %rdi # x:5 jl loop done: imulq $100, %rsi
for loops convert for-loops to while-loops, and then to assembly. C-for for (init; guard; update) { body C-while init; while (guard){ body update
for-loop example C-for, C-while for (int i=0; i<10; i++) { x *= 2; ========================== int i=0; while (i<10){ x *= 2; i++; assembly movq $0, %rsi jmp test loop: shlq $1, %rdi # x*=2 incq %rsi # i++ test: cmpq $10, %rsi # i : 10 jl loop # if i<10 done:
practice: loops (on pytania)
Conditional Moves
conditional moves branching has a cost: processor wants to pipeline many instructions at once, but can't look beyond the next branch. common operation: x = guard? expr1 : expr2 ; usage: x = expr2 test guard cmov<rel> expr1 (examples: cmovge, cmovl, cmovne, etc.)
example conditional move C (if-expr and if-stmt versions) z = x<y? 100 : 0; ========================== /* maybe written this way */ if (x<y) z = 100; else z = 0; assembly (new vs old) movq $0, %rax cmpq %rsi, %rdi # x:y cmovl $100, %rax ========old approach======== cmpq %rsi, %rdi # x:y jl thenlabel movq $0, %rax jmp donelabel thenlabel: movq $100, %rax donelabel:
Using Conditional Moves Conditional Move Instructions Instruction supports: if (Test) Dest ß Src Supported in post-1995 x86 processors GCC tries to use them But, only when known to be safe Why? Branches are very disruptive to instruction flow through pipelines Conditional moves do not require control transfer
Bad Cases for Conditional Move Expensive Computations val = guard? hard1(x) : hard2(x); lots of extra work! Risky Computations val = p? *p : 0; follows bad pointers! Side-effectful computations val = x>0? x*=7 : x+=3; x modified both ways!
Switches
Sparse Switch Example /* Return x/111 if x is multiple && <= 999. -1 otherwise */ int div111(int x) { switch(x) { case 0: return 0; case 111: return 1; case 222: return 2; case 333: return 3; case 444: return 4; case 555: return 5; case 666: return 6; case 777: return 7; case 888: return 8; case 999: return 9; default: return -1; Sparse cases (case values are always known.) order them into tree of conditions Example Not practical to use jump table Would require 1000 entries most would be empty Obvious translation into if-then-else would have up to 9 tests
Sparse Switch Code Structure < 444 = > 4 111 L8 777 < = > 1 0 222 555 888 = = = = > -1 0 2 5 8 L17 L4 333 666 999 L14 L5-1 = 7 3-1 6-1 Organizes cases as binary tree Logarithmic performance < L16 = = > = 9 movl 8(%ebp),%eax # get x cmpl $444,%eax # x:444 je L8 jg L16 cmpl $111,%eax # x:111 je L5 jg L17 testl %eax,%eax # x:0 je L4 jmp L14... L5: movl $1,%eax jmp L19 L6: movl $2,%eax jmp L19 L7: movl $3,%eax jmp L19 L8: movl $4,%eax jmp L19...
converting switch statements dense case values all case values are basically consecutive. create array of places to jump to, and rework switch value to become index into that array. jmp *table will index into, get value, and jump there in one step.
Dense Switch Statement Example Multiple-case labels Here: 5 & 6 Fall through cases Here: 2 Missing cases Here: 4 long switch_eg (long x, long y, long z) { long w = 1; switch(x) { case 1: w = y*z; break; case 2: w = y/z; /* Fall Through */ case 3: w += z; break; case 5: case 6: w -= z; break; default: w = 2; return w;
Jump Table Structure Switch Form switch(x) { case val_0: Block 0 case val_1: Block 1... case val_n-1: Block n 1 Transla7on (Extended C) goto *JTab[x]; jtab: Jump Table Targ0 Targ1 Targ2... Targn-1 Targ0: Targ1: Targ2: Targn-1: Jump Targets Code Block 0 Code Block 1 Code Block 2... Code Block n 1
Switch Statement Example long switch_eg(long x, long y, long z) { long w = 1; switch(x) {... return w; switch_eg: movq %rdx, %rcx cmpq $6, %rdi # x:6 ja.l8 # use default jmp *.L4(,%rdi,8) Register %rdi %rsi %rdx %rax Use(s) Argument x Argument y Argument z Return value What range of values takes default? Note that w isn't ini7alized here.
Indirect jump Switch Statement Example long switch_eg(long x, long y, long z) { long w = 1; switch(x) {... return w; Jump table switch_eg: movq %rdx, %rcx cmpq $6, %rdi # x:6 ja.l8 # Use default jmp *.L4(,%rdi,8) # goto *JTab[x].section.rodata.align 8.L4:.quad.L8 # x = 0.quad.L3 # x = 1.quad.L5 # x = 2.quad.L9 # x = 3.quad.L8 # x = 4.quad.L7 # x = 5.quad.L7 # x = 6
Assembly Setup Explanation Table Structure Each target requires 8 bytes Base address at.l4 Jumping Direct: jmp.l8 Jump target is denoted by label.l8 Indirect: jmp *.L4(,%rdi,8) Start of jump table:.l4 Must scale by factor of 8 (addresses are 8 bytes) Fetch target from effective Address.L4 + x*8 Only for 0 x 6 Jump table.section.rodata.align 8.L4:.quad.L8 # x = 0.quad.L3 # x = 1.quad.L5 # x = 2.quad.L9 # x = 3.quad.L8 # x = 4.quad.L7 # x = 5.quad.L7 # x = 6
Jump Table switch(x) { case 1: //.L3 w = y*z; Jump table.section.rodata.align 8.L4:.quad.L8 # x = 0.quad.L3 # x = 1.quad.L5 # x = 2.quad.L9 # x = 3.quad.L8 # x = 4.quad.L7 # x = 5.quad.L7 # x = 6 break; case 2: //.L5 w = y/z; /* Fall Through */ case 3: //.L9 w += z; break; case 5: case 6: //.L7 w -= z; break; default: //.L8 w = 2;
Code Blocks (x == 1) switch(x) { case 1: //.L3 w = y*z; break;....l3: movq imulq ret %rsi, %rax # y %rdx, %rax # y*z Register %rdi %rsi %rdx %rax Use(s) Argument x Argument y Argument z Return value
Handling Fall-Through long w = 1;... switch(x) {... case 2: w = y/z; /* Fall Through */ case 3: w += z; break;... case 2: w = y/z; goto merge; case 3: merge: w = 1; w += z;
Code Blocks (x == 2, x == 3) long w = 1;... switch(x) {... case 2: w = y/z; /* Fall Through */ case 3: w += z; break;....l5: # Case 2 movq %rsi, %rax cqto idivq %rcx # y/z jmp.l6 # goto merge.l9: # Case 3 movl $1, %eax # w = 1.L6: # merge: addq %rcx, %rax # w += z ret Register %rdi %rsi %rdx %rax Use(s) Argument x Argument y Argument z Return value
Code Blocks (x == 5, x == 6, default) switch(x) {... case 5: //.L7 case 6: //.L7 w -= z; break; default: //.L8 w = 2;.L7: # Case 5,6 movl $1, %eax # w = 1 subq %rdx, %rax # w -= z ret.l8: movl $2, %eax # 2 ret Register %rdi %rsi %rdx %rax # Default: Use(s) Argument x Argument y Argument z Return value