2
$\begingroup$

In Intel's x86 architecture, imul (integer multiply) usually has latency of a few clock cycles. Those CPUs are very smart in filling the time (e.g., pipelining or out-of-order execution), but do any commercial CPUs simply let the program access the intermediate unfinished result?

To be realistic, one clock cycle after imul, the output register probably has not changed at all. So, in this case, my question becomes: Is there any commercial CPU which would allow you to read that register in the following clock cycle though it still gives the old contents?

If the compiler plans for these latencies, it seems that this architecture might be useful. It basically passes the burden of pipelining from the CPU to the compiler. I'm sure this approach has a name, but I am just a curious beginner.

$\endgroup$
  • 1
    $\begingroup$ This sounds like a bad idea, since every edition of the CPU will need a different compiler and different compiled code. Compatibility is important. $\endgroup$ – Yuval Filmus Mar 12 '16 at 22:31
  • 2
    $\begingroup$ This is a classic VLIW ability. (A modern example would be Texas Instruments' TMS320C66x DSPs.) The compiler is expected to schedule operations based on compile-time known latencies. The MIPS R2000 had a load delay slot where as long as one avoided a cache miss, the register destination of the load instruction could be read in the cycle after the load started execution. Obviously depending on a cache hit is problematic. (The obvious problem with such is that binary compatibility would place even more constraints than usual on implementation.) [I may come back to work this into an answer.] $\endgroup$ – Paul A. Clayton Mar 13 '16 at 1:02
  • 2
    $\begingroup$ @PaulA.Clayton Please do! $\endgroup$ – Yuval Filmus Mar 13 '16 at 9:33
  • $\begingroup$ By the way, I am really thinking of cases different from the simple one in the question. For example, an addition could reduce two numbers to XOR bits and CARRY bits immediately and just let these temporary bits be available to the program while the carry propagates through. 4GHz clocks could be replaced by 20GHz clocks in the same technology if things like this were planned for. $\endgroup$ – mickey Mar 14 '16 at 20:25
  • $\begingroup$ Looking at your VLIW references, I can't (easily) find an example machine code allowing early access. I'd basically like to see machine code which has a different output if NOPs get inserted. By the way, I'd also like that code's execution to be faster than an optimized x86 run in the same CMOS technology, but that's probably too much to ask. $\endgroup$ – mickey Mar 14 '16 at 20:39
2
$\begingroup$

You are describing pipeline forwarding for data hazards. Here is a wikipedia article about it: https://en.wikipedia.org/wiki/Operand_forwarding

This is a common feature of in-order processors (such as MIPS32). Since they are not operating out-of-order, the data hazard detection logic detects that the result will not be available during the clock cycle immediately after the instruction executes. The pipeline scheduling logic could either stall the pipeline or forward the result from the arithmetic unit (e.g., the multiplier) directly into where the next instruction would expect the result (e.g., one of the inputs to the ALU for example).

The compiler isn't required to be aware of this feature, however, in some scenarios it can optimize for these types of architecture features.

I won't say it is impossible or even say it isn't performed, but doing this in an out-of-order processor is much harder because the instruction window is tracking dependencies of each instruction via register indices. The control logic for the instruction window is significantly more complex if it is allowed to detect all possible data hazards opportunistically.

$\endgroup$
1
$\begingroup$

Classic VLIW architectures provide this feature. Operations are statically scheduled with fixed latency and the old value would be associated with a register name until an overwriting operation completed. Texas Instruments' TMS320C66x DSPs are modern examples.

The MIPS R2000 had a limited form of this in its delayed load slot. Since loads had a latency of two cycles, an instruction immediately following a load could read the old value from the loads destination register if the load was a cache hit (a cache miss would return the newly loaded value).

(Delayed branches are similar except that instead of the old value of the program counter being preserved, the expected value if there was not a branch is provided. ARM, which mapped the program counter to a general purpose register, also had an interesting feature of exposing the pipeline delay (of the original implementation) between instruction fetch and register read for execution such that a read of the program counter would not give the address of the executing instruction.)

If an architecture does not define a non-zero latency of operations, hardware cannot determine when a new value would become visible. With architectural zero-latency operations, an instruction's result may be sourced by an immediately following instruction, requiring hardware checks for availability in a superscalar design (or a scalar design with greater than one cycle execution latency) but making limited instruction fusion more practical. With non-zero latency operations, software must ensure that data dependencies are expressed, that a dependent operation is executed in a later cycle.

Static scheduling introduces significant constraints with respect to binary compatibility if hardware is to remain simple; the latency of an operation cannot change. With explicit parallelism but one cycle latencies, all operations within a cycle-chunk can execute in parallel without dependency checks and a straightforward operand availability check can stall the next chunk of internally independent operations; this represents a compromise between the extremely simple hardware of classic VLIW and the more complex hardware of in-order superscalar designs.

(The handling of write-after-write hazards for an in-order design with out-of-order completion seems loosely related. A later but lower latency instruction must be prevented from hiding the result of an earlier but higher latency instruction to the same destination. One way of doing this is by having register writeback in the same cycle for all instructions and using register name versioning in the result forwarding network.)

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.