[[!tag standards]] # New instructions for CR/INT predication See: * main bugreport for crweirds * * * Rationale: Condition Registers are conceptually perfect for use as predicate masks, the only problem being that typical Vector ISAs have quite comprehensive mask-based instructions: set-before-first, popcount and much more. In fact many Vector ISAs can use Vectors *as* masks, consequently the entire Vector ISA is available for use in creating masks. This is not practical for SV given the premise to minimise adding of instructions. With the scalar OpenPOWER v3.0B ISA having already popcnt, cntlz and others normally seen in Vector Mask operations it makes sense to allow *both* scalar integers *and* CR-Vectors to be predicate masks. That in turn means that much more comprehensive interaction between CRs and scalar Integers is required. The opportunity is therefore taken to also augment CR logical arithmetic as well, using a mask-based paradigm that takes into consideration multiple bits of each CR (eq/lt/gt/ov). v3.0B Scalar CR instructions (crand, crxor) only allow a single bit calculation. Basic concept: * CR-based instructions that perform simple AND/OR/XOR from all four bits of a CR to create a single bit value (0/1) in an integer register * Inverse of the same, taking a single bit value (0/1) from an integer register to selectively target all four bits of a given CR * CR-to-CR version of the same, allowing multiple bits to be AND/OR/XORed in one hit. * Vectorisation of the same Purpose: * To provide a merged version of what is currently a multi-sequence of CR operations (crand, cror, crxor) with mfcr and mtcrf, reducing instruction count. * To provide a vectorised version of the same, suitable for advanced predication Side-effects: * mtcrweird when RA=0 is a means to set or clear arbitrary CR bits using immediates embedded within the instruction. (Twin) Predication interactions: * INT twin predication with zeroing is a way to copy an integer into CRs without necessarily needing the INT register (RA). if it is, it is effectively ANDed (or negate-and-ANDed) with the INT Predicate * CR twin predication with zeroing is likewise a way to interact with the incoming integer this gets particularly powerful if data-dependent predication is also enabled. # Bit ordering. IBM chose MSB0 for the OpenPOWER v3.0B specification. This makes things slightly hair-raising. Our desire initially is therefore to follow the logical progression from the defined behaviour of `mtcr` and `mfcr` etc. In [[isa/sprset]] we see the pseudocode for `mtcrf` for example: mtcrf FXM,RS do n = 0 to 7 if FXM[n] = 1 then CR[4*n+32:4*n+35] <- (RS)[4*n+32:4*n+35] This places (according to a mask schedule) `CR0` into MSB0-numbered bits 32-35 of the target Integer register `RS`, these bits of `RS` being the 31st down to the 28th. Unfortunately, even when not Vectorised, this inserts CR numbering inversions on each batch of 8 CRs, massively complicating matters. Predication when using CRs would have to be morphed to this (unacceptably complex) behaviour: for i in range(VL): if INTpredmode: predbit = (r3)[63-i] # IBM MSB0 spec sigh else: # completely incomprehensible vertical numbering n = (7-(i%8)) | (i & ~0x7) # total mess CRpredicate = CR{n} # select CR0, CR1, .... predbit = CRpredicate[offs] # select eq..ov bit Which is nowhere close to matching the straightforward obvious case: for i in range(VL): if INTpredmode: predbit = (r3)[63-i] # IBM MSB0 spec sigh else: CRpredicate = CR{i} # start at CR0, work up predbit = CRpredicate[offs] In other words unless we do something about this, when we transfer bits from an Integer Predicate into a Vector of CRs, our numbering of CRs, when enumerating them in a CR Vector, would be **CR7** CR6 CR5.... CR0 **CR15** CR14 CR13... CR8 **CR23** CR22 etc. **not** the more natural and obvious CR0 CR1 ... CR23. Therefore the instructions below need to **redefine** the relationship so that CR numbers (CR0, CR1) sequentially match the arithmetically-ordered bits of Integer registers. By `arithmetic` this is deduced from the fact that the instruction `addi r3, r0, 1` will result in the **LSB** (numbered 63 in IBM MSB0 order) of r3 being set to 1 and all other bits set to zero. We therefore refer, below, to this LSB as "Arithmetic bit 0", and it is this bit which is used - defined - as being the first bit used in Integer predication (on element 0). Below is some pseudocode that, given a CR offset `offs` to represent `CR.eq` thru to `CR.ov` respectively, will copy the INT predicate bits in the correct order into the first 8 CRs: do n = 0 to 7 CR[4*n+32+offs] <- (RS)[63-n] Assuming that `offs` is set to `CR.eq` this results in: * Arithmetic bit 0 (the LSB, numbered 63 in IBM MSB0 terminology) of RS being inserted into CR0.eq * Arithmetic bit 1 of RS being inserted into CR1.eq * ... * Arithmetic bit 7 of RS being inserted into CR7.eq To clarify, then: all instructions below do **NOT** follow the IBM convention, they follow the natural sequence CR0 CR1 instead, using `CR{fieldnum}` to refer to the individual CR Fields. However it is critically important to note that the offsets **in** a CR field (`CR.eq` for example) continue to follow the v3.0B definition and convention. # Instruction form and pseudocode Note that `CR{n}` refers to `CR0` when `n=0` and consequently, for CR0-7, is defined, in v3.0B pseudocode, as: CR{7-n} = CR[32+n*4:35+n*4] Instruction format: | 0-5 | 6-10 | 11 | 12-15 | 16-18 | 19-20 | 21-25 | 26-30 | 31 | | --- | ---- | -- | ----- | ----- | ----- | ----- | ----- | -- | | 19 | RT | | mask | BB | | XO[0:4] | XO[5:9] | / | | 19 | RT | 0 | mask | BB | 0 M | XO[0:4] | 0 mode | Rc | | 19 | RA | 1 | mask | BB | 0 / | XO[0:4] | 0 mode | / | | 19 | BT // | 0 | mask | BB | 1 / | XO[0:4] | 0 mode | / | | 19 | BFT | 1 | mask | BB | 1 M | XO[0:4] | 0 mode | / | mode is encoded in XO and is 4 bits bit 11=0, bit 19=0 crrweird: RT, BB, mask.mode creg = CR{BB} n0 = mask[0] & (mode[0] == creg[0]) n1 = mask[1] & (mode[1] == creg[1]) n2 = mask[2] & (mode[2] == creg[2]) n3 = mask[3] & (mode[3] == creg[3]) result = n0|n1|n2|n3 if M else n0&n1&n2&n3 RT[63] = result # MSB0 numbering, 63 is LSB If Rc: CR1 = analyse(RT) bit 11=1, bit 19=0 mtcrweird: RA, BB, mask.mode reg = (RA|0) lsb = reg[63] # MSB0 numbering n0 = mask[0] & (mode[0] == lsb) n1 = mask[1] & (mode[1] == lsb) n2 = mask[2] & (mode[2] == lsb) n3 = mask[3] & (mode[3] == lsb) CR{BB} = n0 || n1 || n2 || n3 bit 11=0, bit 19=1 crweird: BT, BB, mask.mode creg = CR{BB} n0 = mask[0] & (mode[0] == creg[0]) n1 = mask[1] & (mode[1] == creg[1]) n2 = mask[2] & (mode[2] == creg[2]) n3 = mask[3] & (mode[3] == creg[3]) CR{BT} = n0 || n1 || n2 || n3 bit 11=1, bit 19=1 crweirder: BFT, BB, mask.mode creg = CR{BB} n0 = mask[0] & (mode[0] == creg[0]) n1 = mask[1] & (mode[1] == creg[1]) n2 = mask[2] & (mode[2] == creg[2]) n3 = mask[3] & (mode[3] == creg[3]) BF = BFT[2:4] # select CR bit = BFT[0:1] # select bit of CR result = n0|n1|n2|n3 if M else n0&n1&n2&n3 CR{BF}[bit] = result Pseudo-op: mtcri BB, mode mtcrweird r0, BB, 0b1111.~mode mtcrset BB, mask mtcrweird r0, BB, mask.0b0000 mtcrclr BB, mask mtcrweird r0, BB, mask.0b1111 # Vectorised versions The name "weird" refers to a minor violation of SV rules when it comes to deriving the Vectorised versions of these instructions. Normally the progression of the SV for-loop would move on to the next register. Instead however in the scalar case these instructions **remain in the same register** and insert or transfer between **bits** of the scalar integer source or destination. crrweird: RT, BB, mask.mode for i in range(VL): if BB.isvec: creg = CR{BB+i} else: creg = CR{BB} n0 = mask[0] & (mode[0] == creg[0]) n1 = mask[1] & (mode[1] == creg[1]) n2 = mask[2] & (mode[2] == creg[2]) n3 = mask[3] & (mode[3] == creg[3]) # OR or AND to a single bit result = n0|n1|n2|n3 if M else n0&n1&n2&n3 if RT.isvec: # TODO: RT.elwidth override to be also added here # note, yes, really, the CR's elwidth field determines # the bit-packing into the INT! if BB.elwidth == 0b00: # pack 1 result into 64-bit registers iregs[RT+i][0..62] = 0 iregs[RT+i][63] = result # sets LSB to result if BB.elwidth == 0b01: # pack 2 results sequentially into INT registers iregs[RT+i//2][0..61] = 0 iregs[RT+i//2][63-(i%2)] = result if BB.elwidth == 0b10: # pack 4 results sequentially into INT registers iregs[RT+i//4][0..59] = 0 iregs[RT+i//4][63-(i%4)] = result if BB.elwidth == 0b11: # pack 8 results sequentially into INT registers iregs[RT+i//8][0..55] = 0 iregs[RT+i//8][63-(i%8)] = result else: iregs[RT][63-i] = result # results also in scalar INT Note that: * in the scalar case the CR-Vector assessment is stored bit-wise starting at the LSB of the destination scalar INT * in the INT-vector case the result is stored in the LSB of each element in the result vector Note that element width overrides are respected on the INT src or destination register, however that it is the CR element-width override that is used to indicate how many bits of CR results are packed/extracted into/from each INT register # v3.1 setbc instructions there are additional setb conditional instructions in v3.1 (p129) RT = (CR[BI] == 1) ? 1 : 0 which also negate that, and also return -1 / 0. these are similar yo crweird but not the same purpose. most notable is that crweird acts on CR fields rather than the entire 32 bit CR.