Predicate operations

From Mill Computing Wiki
Jump to: navigation, search

Predicate operations form a part of the Mill operation set. They are niladic, taking no explicit arguments, and retire a single bool value to the belt. They serve the purpose of condition codes and branch-on-condition operations in other architectures.


Background

The Mill uses an explicit representation of Boolean values. They may be of any width, but conditional operations such as branches and the pick operation examine only the least significant bit, where a cleared bit is considered to be false and a set bit to be true. Operations that produce Boolean results (such as the relational operations) thus drop a full-width result to the belt, rather than modifying some global state such as a set of condition codes. This structure permits a single instruction to contain several different operations with Boolean results without conflict over global shared state.

However, a common idiom is to perform some calculation (such as a subtract), test the result against a predicate (such as greater than zero), and branch on the predicate result. Because the greater-than comparison needs the result of the subtract, it cannot appear in the same instruction with the subtract, but must be placed in a following instruction. The latency of this idiom is the sum of the latency of the subtract and the latency of the predicate; the latency of the branch is hidden by phasing. In contrast, an architecture using condition codes can set them at the same time as it executes the subtract, so the overall latency is only that of the subtract rather than the sum.


Predicate gangs

Predicate operations are ganged with a compute operation in the same instruction and are placed in the immediately following slot. They implicitly accept the result of the adjacent compute operation, apply their predicate, and drop a Boolean result reflecting the test of the predicate. Meanwhile, the ganged compute operation completes normally and drops its normal result as well. The predicate part of the gang drops its result at the same time as the compute part drops its result, even if the operation has a multi-cycle latency. The predicate and compute results have the same width, as determined by the usual rules for the compute side of the gang.

Using predicate ops permits the above idiom to use only a single instruction; the compute and predicate execute together, and the branch executes based on the predicate result.

Predicate operations exist for all the integer relational operations using a comparison with zero. In addition, special predicates test for the occurrence of a carry or an overflow condition in the compute.

Assembler access

In conAsm a predicate op is written in the usua; functional notation, without arguments, following the operation that computes the value to test:

  sub(b4, b2), gtr(), brtr("somewhere");

In genAsm, the predicate follows the compute op with a separating period. The ganged pair is considered to have two results, the compute result and the predicate result in that order on the belt:

  x sub.gtr y -:{diff, endFlag}