-
Dependences:
-
Dependences are a property of
programs
, not of CPUs and pipelines.
-
A dependence between two instructions will always exist unless the program is changed.
-
The presence of the dependence indicates the
potential
for a hazard.
-
However, the
actual hazard
and the
length of any stall
is a property of the pipeline.
-
There are three types of dependencies:
-
Data dependences
-
Instruction
j
is dependent on
i
if
i
produces a result used by
j
.
-
Dependence is transitive: if
j
is dependent upon
k
and
k
is dependent upon
i
, then
j
is dependent on
i
, i.e.,
-
Dependence chains can be arbitrarily long.
-
When a compiler schedules instructions, it cannot move
j
before
i
if
i
depends upon
j
.
-
Data dependences
-
Indicate the
possibility
of a hazard.
-
Determine the
order
in which results must be generated.
-
Place an upper bound on the amount of ILP available.
-
Data dependences can be overcome in two ways:
-
Maintaining the dependence but
avoiding a hazard
.
-
Eliminating
the dependence by transforming the code.
-
Scheduling is the primary way to
avoid hazards
without altering dependences.
-
We did this in the previous example with LD, ADDD and SD.
-
We scheduled the code to avoid the hazard, but the dependence remained in the code.
-
Data dependences
-
We also
eliminated
dependencies:
-
Compiler removed this dependence by computing the intermediate values of R1 and folding the computation into an offset.
-
Data dependences
-
Since
eliminating
data dependencies requires a fair amount of analysis, it is done by the compiler.
-
Avoiding hazards
through scheduling can be done in both hardware and software.
-
One tricky data dependence that many people overlook is that of memory locations.
-
Registers are easy to figure out, but memory dependences are much harder since they may not be known until runtime.
-
For example, 100(R4) and 20(R6) may refer to the same memory location.
-
We will look at hardware and software techniques that detect data dependencies that involve memory locations.
-
Name Dependences
-
Occur when two instructions use the same register or memory location but there is NO flow of data between instructions that use that name.
-
There are two types. Assume instruction
i
precedes instruction
j
:
-
Antidependence (WAR)
-
This occurs when two instructions use the same register in sequence, with the first reading it and the second writing it.
-
Output dependence (WAW)
-
This occurs when two instructions write to the same register.
-
Name Dependences
-
These are
NOT
data dependences because no information is passed between the two instructions.
-
The instructions could be executed
out of order
or
in parallel
if the CPU renamed the register or memory location involved.
-
Register renaming
can either be done by the
compiler
or the
CPU
.
-
In our earlier unrolling example, the compiler renamed registers when it unrolled the loop.
-
Name Dependences
-
Consider the unrolled loop with loop overhead removed but
no
renaming.
-
Note that this forces almost a complete ordering of the code.
-
Only LD following SD can be interchanged.
-
Control Dependences
-
A control dependency determines the
ordering
of the instructions with respect to branch instructions.
-
If an instruction depends on the outcome of an earlier branch then it is only executed on one of the two forks.
-
Move S1 before the
if stmt
or
-
Move other instructions before the
if stmt
into the "then" block.
-
Control Dependences
-
Consider the unrolled loop with the branches still in place (but the conditions complemented.)
-
Previously, since we knew that the # of iterations was a multiple of 4, we changed the control dependencies and eliminated branches that were never taken.
-
Control Dependences
-
Note that preserving control dependence is
NOT
a critical property.
-
Program correctness
is the critical property that needs to be preserved.
-
We may be willing to
violate
control dependence if program correctness is preserved.
-
The two properties critical to program correctness are:
-
Preserving exception behavior
-
Any changes in the ordering of instructions must NOT change how exceptions are raised.
-
An instruction that should not have been executed, should not be allowed to cause an exception.
-
Memory operations and floating point often cause problems like this.
-
Control Dependences
-
The second property critical to program correctness is:
-
Preserving data flow
-
Branches make the flow of data between instructions
dynamic
:
-
Preserving data dependence is not enough here (OR is dependent on both the ADD and SUB) !
-
There may be other ways of preserving control dependence, i.e.,
-
The CPU can cancel instructions that were wrongly executed.
-
Or the compiler can cancel things out.
-
Control Dependences
-
Sometimes violating control dependencies cannot affect exception behavior or data flow:
-
We could move the SUB instruction before the BEQZ if we knew:
-
The SUB instruction could
not
generate an exception.
-
If R4 were not `live', i.e., used after the skipnext label.
-
This type of scheduling is called
speculation
, i.e.,
-
The compiler is betting that the branch will not be taken.
-
Control Dependences
-
Control dependence is preserved by implementing
control hazard detection
.
-
Control hazard detection
causes
control stalls
.
-
Control stalls
can be avoided by:
-
Scheduling instructions in delay slots.
-
Loop unrolling.
-
Conditional execution.
-
Speculation (by both compiler and CPU).
-
We will cover the latter two in the next two sections.