Professional Documents
Culture Documents
es in a basic block Blocks are small (6-7 instructions) Instructions are dependent Goal: Exploit ILP across multiple basic blocks Iterations of a loop for (i = 1000; i > 0; i=i-1) x[i] = x[i] + s; Basic Pipeline Scheduling and Loop Unrolling Idea find sequences of unrelated instructions (no hazard) that can be overlapped in the pipeline to exploit ILP A dependent instruction must be separated from the source instruction by a distance in clock cycles equal to latency of the source instruction to avoid stall
Basic Scheduling
Scheduled pipelined execution: Loop: LD F0, 0(R1) 1 SUBI R1, R1, #8 2 ADDD F4, F0, F2 3 stall 4 BNEZR1, Loop 5 SD 8(R1), F4 6
Loop Unrolling Pros: Larger basic block More scope for scheduling and eliminating dependencies Cons: Increases code size Comment: Often a precursor step for other optimizations
Loop: LD ADDD SD SUBI BEQZ LD ADDD SD SUBI BEQZ LD ADDD SD SUBI BEQZ LD ADDD SD SUBI BNEZ Exit: F0, 0(R1) F4, F0, F2 0(R1), F4 R1, R1, #8 R1, Exit F6, 0(R1) F8, F6, F2 0(R1), F8 R1, R1, #8 R1, Exit F10, 0(R1) F12, F10, F2 0(R1), F12 R1, R1, #8 R1, Exit F14, 0(R1) F16, F14, F2 0(R1), F16 R1, R1, #8 R1, Loop
Loop Transformations Instruction independency is the key requirement for the transformations Example Determine that is legal to move SD after SUBI and BNEZ
Determine that unrolling is useful (iterations are independent) Use different registers to avoid unnecessary constrains Eliminate extra tests and branches Determine that LD and SD can be interchanged Schedule the code, preserving the semantics of the code Loop: LD Name 0(R1) Eliminating F0, Dependences
ADDD F4, F0, F2 SD LD 0(R1), F4 F0, -8(R1)
Loop: LD F0, 0(R1) ADDD F4, F0, F2 SD LD 0(R1), F4 F6, -8(R1)
ADDD F12, F10, F2 SD F12 LD -24(R1) ADDD F16, F14, F2 SD F16 SUBI R1, R1, #32 -24(R1), F14, -16(R1),
SUBI R1, R1, #32 BNEZ R1, Loop Eliminating Control Dependences
Loop: LD F0, 0(R1) ADDD F4, F0, F2 SD 0(R1), F4 SUBI R1, R1, #8 BEQZ R1, Exit LD F6, 0(R1) ADDD F8, F6, F2 SD 0(R1), F8 SUBI R1, R1, #8 BEQZ R1, Exit LD F10, 0(R1) ADDD F12, F10, F2 SD 0(R1), F12 SUBI R1, R1, #8 BEQZ R1, Exit LD F14, 0(R1) ADDD F16, F14, F2 SD 0(R1), F16 SUBI R1, R1, #8 BNEZ R1, Loop Exit:
Eliminating Data Dependences
Loop:
F0, 0(R1) 0(R1), F4 R1, R1, #8 F6, 0(R1) 0(R1), F8 R1, R1, #8 F10, 0(R1) 0(R1), F12 R1, R1, #8 F14, 0(R1) 0(R1), F16 R1, R1, #8
Data dependencies SUBI, LD, SD Force sequential execution of iterations Compiler removes this dependency by: Computing intermediate R1 values Eliminating intermediate SUBI Changing final SUBI Data flow analysis Can do on Registers Cannot do easily on memory locations 100(R1) = 20(R2)
Unrolled loop:
Loop: LD F0, 0(R1) ADDD F4, F0, F2 SD 0(R1), F4 LD F6, -8(R1) ADDD F8, F6, F2 SD -8(R1), F8 LD F10, -16(R1) ADDD F12, F10, F2 SD -16(R1), F12 LD F14, -24(R1) ADDD F16, F14, F2 SD -24(R1), F16 SUBI R1, R1, #32 BNEZ R1, Loop
Some General Comments Dependences are a property of programs Actual hazards are a property of the pipeline Techniques to avoid dependence limitations Maintain dependences but avoid hazards Code scheduling hardware software Eliminate dependences by code transformations Complex Compiler-based Static Branch Prediction Static Branch Prediction: Using Compiler Technology How to statically predict branches? Examination of program behavior Always predict taken (on average, 67% are taken) Mis-prediction rate varies large (9%59%) Predict backward branches taken, forward branches un-taken (misprediction rate > 60% -- 70%) Profile-based predictor: use profile information collected from earlier runs Simplest is the Basket bit idea Easily extends to use more bits Definite win for some regular applications Static Branch Prediction: Using Compiler Technology Useful for Scheduling instructions when the branch delays are exposed by the architecture (either delayed or canceling branches) Assisting dynamic predictors (IA-64 architecture in Section 4.7) Determining which code paths are more frequent, a key step in code scheduling
Static Multiple Issue: VLIW Overview VLIW (very long instruction word) Issue a fixed number of instructions formatted as One large instruction comprising independent MIPS instructions or A fixed instruction packet with the parallelism among instructions explicitly indicated by instruction Also known as EPIC explicitly parallel instruction computers Rely on compiler to Minimize the potential hazard stall
Actually format the instructions in a potential issue packet so that HW need not check explicitly for dependencies. Compiler ensures Dependences within the issue packet cannot be present or Indicate when a dependence may occur
A VLIW uses multiple, independent functional units A VLIW packages multiple independent operations into one very long instruction The burden for choosing and packaging independent operations falls on compiler HW in a superscalar makes these issue decisions is unneeded This advantage increases as the maximum issue rate grows Here we consider a VLIW processor might have instructions that contain 5 operations, including 1 integer (or branch), 2 FP, and 2 memory references Depend on the available FUs and frequency of operation VLIW depends on enough parallelism for keeping FUs busy Loop unrolling and then code scheduling Compiler may need to do local scheduling and global scheduling Techniques to enhance LS and GS will be mentioned later For now, assume we have a technique to generate long, straight-line code sequences
Unrolled 7 times to avoid delays 7 results in 9 clocks, or 1.29 clocks per iteration 23 ops in 9 clock, average 2.5 ops per clock, 60% FUs are used Note: Need more registers in VLIW VLIW Problems Technical Increase in code size Ambitious loop unrolling
Whenever instruction are not full, the unused FUs translate to waste bits in the instruction encoding An instruction may need to be left completely empty if no operation can be scheduled Clever encoding or compress/decompress VLIW Problems Logistical Synchronous VS. Independent FUs Early VLIW all FUs must be kept synchronized A stall in any FU pipeline may cause the entire processor to stall Recent VLIW FUs operate more independently Compiler is used to avoid hazards at issue time Hardware checks allow for unsynchronized execution once instructions are issued. Binary code compatibility Code sequence makes use of both the instruction set definition and the detailed pipeline structure (FUs and latencies) Need migration between successive implementations, or between implementations recompliation Solution Object-code translation or emulation Temper the strictness of the approach so that binary compatibility is still feasible (IA-64 in Section 4.7) Advantages of Superscalar over VLIW Old codes still run Like those tools you have that came as binaries HW detects whether the instruction pair is a legal dual issue pair If not they are run sequentially Little impact on code density Dont need to fill all of the cant issue here slots with NOPs Compiler issues are very similar Still need to do instruction scheduling anyway Dynamic issue hardware is there so the compiler does not have to be too conservative
Loop-level parallelism: analyzed at the source or near level Most ILP analysis: analyzed once instructions have been generated Loop-level analysis Determine what dependences exist among the operands in a loop across the iterations of that loop Determine whether data accesses in later iterations are dependent on data values produced in earlier iterations Loop-carried dependence (LCD) VS. loop-level parallel LCD forces successive loop iterations to execute in series Finding loop-level parallelism involves recognizing structures such as loops, array references, and induction variable computations The compiler can do this analysis more easily at or near the source level
Example 1
for (i=1; i <= 100; i=i+1) { A[i+1] = A [i] + C [i]; B[i+1] = B [i] + A[i+1]; } Assume A, B, and C are distinct, non-overlapping arrays /* S1 */ /* S2 */
Example 2 for (i=1; i <= 100; i=i+1) { A [i] = A [i] + B [i]; B[i+1] = C [i] + D [i]; }
/* S1 */ /* S2 */
The existence of loop-carried dependence does not prevent parallelism S1 uses a value computed by S2 in an earlier iteration (B[i+1]) Loop-carried dependence Dependence is not circular Neither statement depends on itself, and although S1 depends on S2, S2 does not depend on S1 A loop is parallel if it can be written without a cycle in the dependences Absence of a cycle give a partial ordering on the statements
Example 2 (Cont.) A[1] = A[1] + B[1] for (i=1; i <= 99; i=i+1) { B[i+1] = C [i] + D [i]; A[i+1] = A[i+1] + B[i+1]; } B[101] = C[100] + D[100] Transform the code in the previous slide to conform to the partial ordering and expose the parallelism No longer loop-carried. Iterations of the loop may be overlapped, provided the statements in each iteration are kept in order
Detect and Eliminate Dependences Trick is to find and remove dependencies Simple for single variable accesses More complex for pointers, array references, etc. Things get easier if Non-cyclic dependencies No loop-carried dependencies Recurrent dependency distance is larger (more ILP can be explored) Recurrence: when a variable is defined based on the value of that variable in an earlier iteration ex. Y[i] = Y[i-5] + Y[i] (dependence distance = 5) Array index calculation is consistent Affine array indices ai+b where a and b are constants GCD test (pp. 324) to determine two affine functions can have the same value for different indices between the loop bound
Detect and Eliminate Dependences -- Difficulties Barriers to analysis Reference via pointers rather than predictable array indices Array indexing is Indirect through another array: x[y[i]] (non-affine) Common sparse array accesses False dependency: for some input values a dependence may exist Run time checks must be used to determine the dependent case General: NP hard problem Specific cases can be done precisely Current problem: a lot of special cases that dont apply often The good general heuristic is the holy grail Points-to analysis: analyzing programs with pointers (pp. 326327 Eliminating Dependent Computations Techniques
Eliminate or reduce a dependent computation by back substitution within a basic block and within loop Algebraic simplification + Copy propagation (eliminate operations that copy values within a basic block) Reduce multiple increment of array indices during loop unrolling and move increments across memory addresses in Section 4.1
R1, R2, #4 R1, R1, #4
DADDUI DADDUI
DADDUI
R1, R2, #8
DADDUI DADDUI
DADDUI DADDUI
Eliminating Dependent Computations Techniques Most compilers require that optimizations that rely on associativity (e.g. treeheight reduction) be explicitly enabled Integer/FP arithmetic (range and precision) may lead to round-error Optimization related to recurrence Recurrence: expressions whose value on one iteration is given by a function that depends on previous iteration When a loop with a recurrence is unrolled, we may be able to algebraically optimized the unrolled loop, so that the recurrence need only be evaluated once per unrolled iteration sum = sum + x sum = sum + x1 + x2 + x3 + x4 + x5 (5 dependent operations) sum = ((sum + x1) + (x2 + x3)) + (x4 + x5) (3 dependent operations) An Example to Eliminate False Dependences
for (i=1; i <= 100; i=i+1) { Y[ i] = X [i] / C; /* S1 */ X [i] = X [i] + C; /* S2 */ Z [i] = Y [i] + C; /* S3 */ Y [i] = C Y [i]; /* S4 */ }
for (i=1; i <= 100; i=i+1) { T [i] = X [i] / C; /* S1 */ X1 [i] = X [i] + C; /* S2 */ Z [i] = T [i] + C; /* S3 */ Y [i] = C T [i]; /* S4 */ }
Software Pipelining
Observation: if iterations from loops are independent, then can get more ILP by taking instructions from different iterations Software pipelining (Symbolic loop unrolling): reorganizes loops so that each iteration is made from instructions chosen from different iterations of the original loop Idea is to separate dependencies in original loop body Register management can be tricky but idea is to turn the code into a single loop body In practice both unrolling and software pipelining will be necessary due to the register limitations
Start-up
Finish-up
Iteration 0
Iteration 1
1
Iteration 2
Iteration 3
Example
Iteration i+2
0(R1), F4
ADDD F2 SD
0(R1), F4 Loop: SD
ADDD F2
ADDD LD
0(R1), F4
F0, 0(R1)
Software Pipelining Example Before: Unrolled 3 times 1 L.D F0,0(R1) 2 ADD.D F4,F0,F2 3 S.D F4,0(R1) 4 L.D F6,-8(R1) 5 ADD.D F8,F6,F2 6 S.D F8,-8(R1) 7 L.D F10,-16(R1) 8 ADD.D F12,F10,F2 9 S.D F12,-16(R1) 10 DADDUI R1,R1,#-24 11 BNE R1,R2,LOOP After: Software Pipelined 1 S.D F4,16(R1);Stores M[i] 2 ADD.D F4,F0,F2 ; Adds to M[i-1] 3 L.D F0,0(R1) ;Loads M[i-2] 4 DAADUI R1,R1,#-8 5 BNE R1,R2,LOOP Startup and finish up codes are ignored
5 cycles per result, assuming that DADDUI is scheduled before ADD.D, and L.D (with an adjusted offset) is placed in the branch delay slot Software Pipelining Symbolic Loop Unrolling Maximize result-use distance Less code space than unrolling Fill & drain pipe only once per loop vs. once per each unrolled iteration in loop unrolling SW Pipeline overlapped LOops
Time
LD LD DADDU SD
BNEZ R4, elsepart ; Test A SD J elsepart: X join: ; after if SW , Global Try to Scheduling Example of B and C before the0(R3) A ; store C[i] Code move the assignment test of Move B If an instruction in X depends on the original value of B, moving B before BNEZ will influence the data dependence Make a shadow copy of B before IF and use that shadow copy in X? Complex to implement Slow down the program if the trace selected is not optimal Require additional instructions to execute Move C To the THEN part A copy of C must be put in the ELSE part Before the BNEZ If can be moved the copy of C in the ELSE part can be eliminated join ; ; code for X ; j over else , 0(R2) ; Store B
Move Assignment of B BeforeBNEZ LD R4, 0(R1) ; load A LD R5, 0(R2) ; load B DADDU R4, R4, R5 ; Add to A SD R4, 0(R1) ; store A SD , 0(R2) ; Store B BNEZ R4, elsepart ; Test A J join ; j over else elsepart: ; X ; code for X LD R5, 0(R2) DADDU R7, R5, R6 join: ; after if
SW
, 0(R3)
; store C[i]
Factors in Moving B The compiler will consider the following factors Relative execution frequencies of THEN and ELSE Cost of executing the computing and assignment to B above branch Any empty instruction issue slots and stalls above branch? How will the movement of B change the execution time for THEN Is B the best code fragment that can be moved? How about C or others? The cost of the compensation code that may be necessary for ELSE
Trace Scheduling
Useful for processors with a large number of issues per clock, where conditional or predicted execution (Section 4.5) is inappropriate or unsupported, and where loop unrolling may not be sufficient by itself to uncover enough ILP A way to organize the global code motion process, so as to simplify the code scheduling by incurring the costs of possible code motion on the less frequent paths Best used where profile information indicates significant differences in frequency between different paths and where the profile information is highly indicative of program behavior independent of the input Parallelism across conditional branches other than loop branches Looking for the critical path across conditional branches Two steps: Trace Selection: Find likely sequence of multiple basic blocks (trace) of long sequence of straight-line code Trace Compaction Squeeze trace into few VLIW instructions Move trace before the branch decision Need bookkeeping code in case prediction is wrong Compiler undoes bad guess (discards values in registers) Subtle compiler bugs mean wrong answer vs. poor performance; no hardware interlocks
Trace Scheduling Simplify the decisions concerning global code motion Branches are viewed as jumps into or out of the selected trace (the most probable path) Additional book-keeping code will often be needed on the entry of exit point If the trace is so much more probable than the alternatives that the cost of the bookkeeping code need not be a deciding factor Trace scheduling is good for scientific code Intensive loops and accurate profile data But unclear if this approach is suitable for programs that are less simply characterized and less loop-intensive
Superblocks Drawback of trace scheduling: the entries and exits into the middle of the trace cause significant complications Compensation code, and hard to assess their cost Superblocks similar to trace, but Single entry point but allow multiple exits In a loop that has a single loop exit based on a count, the resulting superblocks have only one exit Use tail duplication to create a separate block corresponding to the portion of the trace after the entry
Some Things to Notice SW pipelining, loop unrolling, trace scheduling, superblocks Useful when the branch behavior is fairly predictable at compiler time Not totally independent techniques All try to avoid dependence induced stalls Primary focus Unrolling: reduce loop overhead of index modification and branch SW pipelining: reduce single body dependence stalls Trace scheduling/superblocks: reduce impact of branch walls
Most advanced compilers attempt all Result is a hybrid which blurs the differences Lots of special case analysis changes the hybrid mix All tend to fail if branch prediction is unreliable
Hardware Support for Exposing More Parallelism at Compiler Time Hardware Support Conditional or predicated instructions Can be used to eliminate branches Control dependence is converted into data dependence Useful in hardware as well as software intensive approaches for ILP Compiler speculation with hardware support Support for preserving the exception behavior Support for reordering loads and stores Predicated Instructions
C if (C) {S} S T
Branch is eliminated
C:S
Full predication Simplest case: Only conditional move Useful for short sequences only For large code blocks, many conditional moves may be required inefficient Full predication: All instructions can be conditional Large code blocks may be converted Entire loop body may become free of branches Multiple branches per clock Very likely with high issue rate processor Complex to handle control dependence among branches difficult to predict, update tables etc. Reducing branches per clock (if not eliminating) is useful Remove a branch that is harder to predict increases potential gain Example: A 2 issue machine
Here is a code sequence for a two issue superscalar that can issue a combination of one memory reference and one ALU operation, or a branch by itself, This sequence waste a memory operation slot in the second cycle and will incur a data dependence stall is the branch is not taken, since the second LW after the branch depends on the prior load. Show how the code can be improved using a predicated form of LW.
LW LWC BEQZ LW
ADD ADD
Example: A 2 issue machine One issue slot eliminated One stall cycle eliminated (dep. between loads) No improvement if branch condition is false Entire code (if short) after branch may be moved up Exceptions and predicated instructions Predicated instruction must not generate an exception if the predicate is false LW R8, 0(R10) may generate protection exception if R10 contains 0 When predicate is true, the exception behavior should be as usual LW R8, 0(R10) may still cause a legal and resumable exception (e.g. a page fault) if R10 is not 0 When to annul a pred. instr.? Early - during issue may lead to pipeline stall due to data dependence Late - just before writing results FU resources are consumed - negative impact on performance Limitations with predicated instructions Resources wasted when instructions are annulled except when the slots taken by these instructions would have been idle anyway Useful if predicates can be evaluated early otherwise stalls for data hazards may result Usefulness limited when control flow is more complex than simple if-then-else e.g. moving an instruction across 2 branches requires 2 predicates - large overheads if this is not supported Speed penalty - higher cycle count or slower clock
Purpose:
Improve scheduling or issue rate Compared with predicated instructions: Latter may not always remove control dependence Here the instruction may be moved even before the condition evaluation What is required to speculate ambitiously? Find instruction which can be moved without effecting data flow use register renaming if that helps Ignore exceptions in speculated instruction until you know for sure Interchange load-store or store-store speculate that there are no address conflicts Hardware support needed for 2nd and 3rd
can be handled for speculative instructions in the same way as normal instructions harmless, but resources are consumed Program errors an instruction should not cause program termination until it is found to be no longer speculative Ignore exceptions Resumable exceptions - handle normally, as and when exception occurs Terminating exception - dont terminate, return undefined value speculation correct: wrong program allowed to continue and produce wrong results speculation correct: the result will get ignored anyway Instructions may be marked as speculative or normal helpful, but not necessary errors in normal instructions can terminate program Example if (A == 0) A = B; else A = A + 4; A is at 0(R3) and B is at 0(R2) LW R1,0(R3) LW R14,0(R2) BEQZ R1,L3 ADDI R14,R1,#4 L3: SW R14,0(R3) Use check instructions
LW LW R1,0(R3) R14,0(R2)
LW R1,0(R3) sLW R14,0(R2) BNEZ R1,L1 SPECCK 0(R2) J L2 L1: ADDI R14,R1,#4 L3: SW R14,0(R3)
poison bits for registers, speculative bits for instructions poison bit of destination set if a speculative instruction encounters terminating exception when an instruction reads a register with poison bit on speculative instruction: poison bit of its destination is set normal instruction: a fault occurs stores are never speculative saving and restoring poison bits on context switch special instruction required Code with poison bit LW R1,0(R3) sLW R14,0(R2) BEQZ R1,L3 ADDI R14,R1,#4 L3: SW R14,0(R3) sLW instruction sets poison bit of R14 if R2 contains 0 Use reorder buffer Reorder buffer as in superscalar processor instructions marked as speculative remember how many branches (usually not more than 1) it moved across and what branch action compiler assumed alternative: original location marked by a sentinel - indicates that the results can be committed
Memory reference speculation Move load up across a store no problem if absence of address clash can be checked statically otherwise, mark the instruction as speculative - it saves the address address examined on subsequent stores - a conflict means speculation failed a special instruction is kept at the original location of load - can take care of reload when speculation fails - may require a fix-up sequence as well
HW speculation does not require compensation or bookkeeping code, needed by ambitious SW speculation HW speculation with dynamic scheduling does not require different code sequences to achieve good performance for different implementation of an architecture HW speculation require complex and additional HW resources Some designers have tried to combine the dynamic and compiler-based approaches to achieve the best of each
IA-64 : Background Explicitly Parallel Instruction Computing (EPIC) - Jointly developed by Intel & Hewlett-Packard (HP) New 64 bit architecture
Not extension of x86 series Not adaptation of HP 64bit RISC architecture To exploit increasing chip transistors and increasing speeds
Utilizes systematic parallelism Departure from superscalar trend Note: Became the architecture of the Intel Itanium Basic Concepts for IA-64 Instruction level parallelism EXPLICIT in machine instruction, rather than determined at run time by processor Long or very long instruction words (LIW/VLIW) Fetch bigger chunks already preprocessed * Predicated Execution Marking groups of instructions for a late decision on execution. * Control Speculation Go ahead and fetch & decode instructions, but keep track of them so the decision to issue them, or not, can be practically made later * Data Speculation (or Speculative Loading) Go ahead and load data early so it is ready when needed, and have a practical way to recover if speculation proved wrong Software Pipelining - Multiple iterations of a loop can be executed in parallel
General Organization
Predicate Registers Used as a flag for instructions that may or may not be executed. A set of instructions is assigned a predicate register when it is uncertain whether the instruction sequence will actually be executed (think branch). Only instructions with a predicate value of true are executed. When it is known that the instruction is going to be executed, its predicate is set. All instructions with that predicate true can now be completed. Those instructions with predicate false are now candidates for cleanup.
Predication
Speculative Loading
IA-64 Key Hardware Features Large number of registers IA-64 instruction format assumes 256 Registers 128 * 64 bit integer, logical & general purpose 128 * 82 bit floating point and graphic 64 predicated execution registers (To support high degree of parallelism) Multiple execution units Probably pipelined 8 or more ? IA-64 Register Set
IA-64 Execution Units I-Unit Integer arithmetic Shift and add Logical Compare Integer multimedia ops M-Unit Load and store Between register and memory Some integer ALU operations B-Unit Branch instructions F-Unit Floating point instructions
Instruction Format 128 bit bundles Can fetch one or more bundles at a time Bundle holds three instructions plus template Instructions are usually 41 bit long Have associated predicated execution registers Template contains info on which instructions can be executed in parallel Not confined to single bundle e.g. a stream of 8 instructions may be executed in parallel Compiler will have re-ordered instructions to form contiguous bundles Can mix dependent and independent instructions in same bundle
Assembly Language Format [qp] mnemonic [.comp] dest = srcs ;; // qp - predicate register 1 at execution execute and commit result to hardware 0 result is discarded mnemonic - name of instruction comp one or more instruction completers used to qualify mnemonic dest one or more destination operands srcs one or more source operands ;; - instruction groups stops (when appropriate) Sequence without read after write or write after write Do not need hardware register dependency checks // - comment follows
add r3 = r1, r4
//second group
Second instruction depends on value in r1 Changed by first instruction Can not be in same group for parallel execution Note ;; ends the group of instructions that can be executed in parallel
Assembly Example Multiple Register Dependencies: ld8 r1 = [r5] //first group sub r6 = r8, r9 ;; //first group add r3 = r1, r4 //second group st8 [r6] = r12 //second group Last instruction stores in the memory location whose address is in r6, which is established in the second instruction
Assembly Example Predicated Code Consider the Following program with branches: if (a&&b) j = j + 1; else if(c) k = k + 1; else k = k 1; i = i + 1; Assembly Example Predicated Code
Source Code
if (a&&b)
Pentium Code
cmp a, 0 je L1
cmp b, 0 j = j + 1; je L1
(p3) add
j = 1, j
(p4) add k = 1, k
Example of Prediction
Data Speculation Load data from memory before needed What might go wrong? Load moved before store that might alter memory location Need subsequent check in value
Assembly Example Data Speculation Consider the Following program: (p1) br some_label // cycle 0 ld8 r1 = [r5] ;; // cycle 1 add r1 = r1, r3 // cycle 3 Assembly Example Data Speculation Consider the Following program:
ld8.s r1 = [r5] ;; //cycle -2
(p1) br some_label add r1 = r1, r3 //cycle 0 //cycle 3