Peephole Optimization l Final pass over generated code

  • Slides: 22
Download presentation
Peephole Optimization l Final pass over generated code: l l examine a few consecutive

Peephole Optimization l Final pass over generated code: l l examine a few consecutive instructions: 2 to 4 See if an obvious replacement is possible: store/load pairs l MOV %eax => mema MOV mema => %eax l Can eliminate the second instruction without needing any global knowledge of mema l Use algebraic identities l Special-case individual instructions

Algebraic identities worth recognizing single instructions with a constant operand: l l A*2= A+A

Algebraic identities worth recognizing single instructions with a constant operand: l l A*2= A+A l A*1= A l A*0= 0 l A/1= A More delicate with floating-point

Is this ever helpful? l Why would anyone write X * 1? l Why

Is this ever helpful? l Why would anyone write X * 1? l Why bother to correct such obvious junk code? l In fact one might write l l #define MAX_TASKS 1. . . a = b * MAX_TASKS; Also, seemingly redundant code can be produced by other optimizations. This is an important effect.

Replace Multiply by Shift l l A : = A * 4; l Can

Replace Multiply by Shift l l A : = A * 4; l Can be replaced by 2 -bit left shift (signed/unsigned) l But must worry about overflow if language does A : = A / 4; l If unsigned, can replace with shift right l But shift right arithmetic is a well-known problem l Language may allow it anyway (traditional C)

Addition chains for multiplication l If multiply is very slow (or on a machine

Addition chains for multiplication l If multiply is very slow (or on a machine with no multiply instruction like the original SPARC), decomposing a constant operand into sum of powers of two can be effective: l X * 125 = x * 128 - x*4 + x l two shifts, one subtract and one add, which may be faster than one multiply l Note similarity with efficient exponentiation method

The Right Shift problem l Arithmetic Right shift: l shift right and use sign

The Right Shift problem l Arithmetic Right shift: l shift right and use sign bit to fill most significant bits l -5 111111. . . 1111111011 l SAR 111111. . . 111101 l which is -3, not -2 l l in most languages -5/2 = -2 Prior to C 99, implementations were allowed to truncate towards or away from zero if either operand was negative

Folding Jumps to Jumps l A jump to an unconditional jump can copy the

Folding Jumps to Jumps l A jump to an unconditional jump can copy the target address l JNE lab 1. . . lab 1 JMP lab 2 l Can be replaced by JNE lab 2 l As a result, lab 1 may become dead (unreferenced)

Jump to Return l A jump to a return can be replaced by a

Jump to Return l A jump to a return can be replaced by a return l l JMP lab 1. . . lab 1 RET Can be replaced by RET l lab 1 may become dead code

Tail Recursion Elimination l A subprogram is tail-recursive if the last computation is a

Tail Recursion Elimination l A subprogram is tail-recursive if the last computation is a call to itself: function last (lis : list_type) return lis_type is begin if lis. next = null then return lis; else return last (lis. next); end; l Recursive call can be replaced with lis : = lis. next; goto start; -- added label

Advantages of tail recursion elimination l saves time: an assignment and jump is faster

Advantages of tail recursion elimination l saves time: an assignment and jump is faster than a call with one parameter l saves stack space: converts linear stack usage to constant usage. l In languages with no loops, this may be a required optimization: specified in Scheme standard.

Tail-recursion elimination at the Instruction Level l Consider the sequence on the x 86:

Tail-recursion elimination at the Instruction Level l Consider the sequence on the x 86: l CALL func RET l CALL pushes return point on stack, RET in body of func removes it, RET in caller returns l Can generate instead: JMP func l Now RET in func returns to original caller, because single return address on stack

Peephole optimization in the REALIA COBOL compiler l Full compiler for Standard COBOL, targeted

Peephole optimization in the REALIA COBOL compiler l Full compiler for Standard COBOL, targeted to the IBM PC. l Now distributed by Computer Associates l Runs in 150 K bytes, but must be able to handle very large programs that run on mainframes l No global optimization possible: multiple linear passes over code, no global data structures, no flow graph. l Multiple peephole optimizations, compiler iterates until code is stable. Each pass scan code backwards to minimize address recomputations

Typical COBOL code: control structures and perform blocks. l Process-Balance. if Balance is negative

Typical COBOL code: control structures and perform blocks. l Process-Balance. if Balance is negative then perform Send-Bill else perform Record-Credit end-if. Send-Bill. . Record-Credit. .

Simple Assembly: perform equivalent to call l Pb: cmp jnl call jmp L 1:

Simple Assembly: perform equivalent to call l Pb: cmp jnl call jmp L 1: call L 2: ret Sb: . . . ret Rc: . . . ret balance, 0 L 1 Sb L 2 Rc

Fold jump to return statement l Pb: cmp jnl call jmp l L 1:

Fold jump to return statement l Pb: cmp jnl call jmp l L 1: call L 2: ret Sb: . . . ret Rc: . . . ret balance, 0 L 1 Sb L 2 -- jump to return Rc

Corresponding Assembly l Pb: cmp jnl call ret L 1: jmp L 2: ret

Corresponding Assembly l Pb: cmp jnl call ret L 1: jmp L 2: ret Sb: . . . ret Rc: . . . ret balance, 0 L 1 -- jump to unconditional jump Sb -- folded Rc -- will become useless

code following a jump is unreachable l Pb: cmp jnl jmp ret L 1:

code following a jump is unreachable l Pb: cmp jnl jmp ret L 1: jmp Sb: . . . ret Rc: . . . ret balance, 0 Rc -- folded Sb -- unreachable Rc -- unreachable

Jump to following instruction is a noop l Pb: Sb: Rc: cmp balance, 0

Jump to following instruction is a noop l Pb: Sb: Rc: cmp balance, 0 jnl Rc jmp Sb -- jump to next instruction. . . ret

Final code l Pb: Sb: Rc: cmp balance, 0 jnl Rc. . . ret

Final code l Pb: Sb: Rc: cmp balance, 0 jnl Rc. . . ret l Final code as efficient as inlining. l All transformations are local. Each optimization may yield further optimization opportunities l Iterate till no further change

Arcane tricks l Consider typical maximum computation l if A >= B then C

Arcane tricks l Consider typical maximum computation l if A >= B then C : = A; else C : = B; end if; l For simplicity assume all unsigned, and all in registers

Eliminating max jump on x 86 l Simple-minded asm code l CMP A, B

Eliminating max jump on x 86 l Simple-minded asm code l CMP A, B JNAE L 1 MOV A=>C JMP L 2 L 1: MOV B=>C L 2: l One jump in either case

Computing max without jumps on X 86 l Architecture-specific trick: use subtract with borrow

Computing max without jumps on X 86 l Architecture-specific trick: use subtract with borrow instruction and carry flag l CMP A, B SBB %eax, %eax MOV %eax, C NOT C AND B=>%eax AND A=>C OR %eax=>C ; CF=1 if B > A, CF = 0 if A >= B ; all 1's if B > A, all 0's if A >= B ; all 0's if B > A, all 1's if A >= B ; B if B>A, 0 if A>=B ; 0 if B >A, A if A>=B ; B if B>A, A if A>=B l More instructions, but NO JUMPS l Supercompiler: exhaustive search of instruction patterns to uncover similar tricks