(no commit message)
[libreriscv.git] / openpower / sv / svp64 / appendix.mdwn
index a0d42385ac1bd06b486bd4452f995f8319cb9f39..3e9955c4787153f3c56962033c550e27cc44664a 100644 (file)
@@ -4,7 +4,8 @@
 * <https://bugs.libre-soc.org/show_bug.cgi?id=558#c47>
 
 This is the appendix to [[sv/svp64]], providing explanations of modes
-etc. leaving the main svp64 page's primary purpose as outlining the instruction format.
+etc. leaving the main svp64 page's primary purpose as outlining the
+instruction format.
 
 Table of contents:
 
@@ -18,28 +19,61 @@ independent.  XER SO and other global "accumulation" flags (CR.OV) cause
 Read-Write Hazards on single-bit global resources, having a significant
 detrimental effect.
 
-Consequently in SV, XER.SO and CR.OV behaviour is disregarded (including in `cmp` instructions).  XER is
-simply neither read nor written.  This includes when `scalar identity behaviour` occurs.  If precise OpenPOWER v3.0/1 scalar behaviour is desired then OpenPOWER v3.0/1 instructions should be used without an SV Prefix.
+Consequently in SV, XER.SO and CR.OV behaviour is disregarded (including
+in `cmp` instructions).  XER is simply neither read nor written.
+This includes when `scalar identity behaviour` occurs.  If precise
+OpenPOWER v3.0/1 scalar behaviour is desired then OpenPOWER v3.0/1
+instructions should be used without an SV Prefix.
 
-An interesting side-effect of this decision is that the OE flag is now free for other uses when SV Prefixing is used.
+An interesting side-effect of this decision is that the OE flag is now
+free for other uses when SV Prefixing is used.
 
-Regarding XER.CA: this does not fit either: it was designed for a scalar ISA. Instead, both carry-in and carry-out go into the CR.so bit of a given Vector element.  This provides a means to perform large parallel batches of Vectorised carry-capable additions.  crweird instructions can be used to transfer the CRs in and out of an integer, where bitmanipulation may be performed to analyse the carry bits (including carry lookahead propagation) before continuing with further parallel additions.
+Regarding XER.CA: this does not fit either: it was designed for a scalar
+ISA. Instead, both carry-in and carry-out go into the CR.so bit of a given
+Vector element.  This provides a means to perform large parallel batches
+of Vectorised carry-capable additions.  crweird instructions can be used
+to transfer the CRs in and out of an integer, where bitmanipulation
+may be performed to analyse the carry bits (including carry lookahead
+propagation) before continuing with further parallel additions.
 
 # v3.0B/v3.1B relevant instructions
 
-SV is primarily designed for use as an efficient hybrid 3D GPU / VPU / CPU ISA.
-
-As mentioned above, OE=1 is not applicable in SV, freeing this bit for alternative uses.  Additionally, Vectorisation of the VSX SIMD system likewise makes no sense whatsoever. SV *replaces* VSX and provides, at the very minimum, predication (which VSX was designed without).  Thus all VSX Major Opcodes - all of them - are "unused" and must raise illegal instruction exceptions in SV Prefix Mode.
-
-Likewise, `lq` (Load Quad), and Load/Store Multiple make no sense to have because they are not only provided by SV, the SV alternatives may be predicated as well, making them far better suited to use in function calls and context-switching.
-
-Additionally, some v3.0/1 instructions simply make no sense at all in a Vector context: `twi` and `tdi` fall into this category, as do branch operations as well as `sc` and `scv`.  Here there is simply no point trying to Vectorise them: the standard OpenPOWER v3.0/1 instructions should be called instead.
-
-Fortuitously this leaves several Major Opcodes free for use by SV to fit alternative future instructions.  In a 3D context this means Vector Product, Vector Normalise, [[sv/mv.swizzle]], Texture LD/ST operations, and others critical to an efficient, effective 3D GPU and VPU ISA. With such instructions being included as standard in other commercially-successful GPU ISAs it is likewise critical that a 3D GPU/VPU based on svp64 also have such instructions.
-
-Note however that svp64 is stand-alone and is in no way critically dependent on the existence or provision of 3D GPU or VPU instructions. These should be considered extensions, and their discussion and specification is out of scope for this document.
-
-Note, again: this is *only* under svp64 prefixing.  Standard v3.0B / v3.1B is *not* altered by svp64 in any way.
+SV is primarily designed for use as an efficient hybrid 3D GPU / VPU /
+CPU ISA.
+
+As mentioned above, OE=1 is not applicable in SV, freeing this bit for
+alternative uses.  Additionally, Vectorisation of the VSX SIMD system
+likewise makes no sense whatsoever. SV *replaces* VSX and provides,
+at the very minimum, predication (which VSX was designed without).
+Thus all VSX Major Opcodes - all of them - are "unused" and must raise
+illegal instruction exceptions in SV Prefix Mode.
+
+Likewise, `lq` (Load Quad), and Load/Store Multiple make no sense to
+have because they are not only provided by SV, the SV alternatives may
+be predicated as well, making them far better suited to use in function
+calls and context-switching.
+
+Additionally, some v3.0/1 instructions simply make no sense at all in a
+Vector context: `twi` and `tdi` fall into this category, as do branch
+operations as well as `sc` and `scv`.  Here there is simply no point
+trying to Vectorise them: the standard OpenPOWER v3.0/1 instructions
+should be called instead.
+
+Fortuitously this leaves several Major Opcodes free for use by SV
+to fit alternative future instructions.  In a 3D context this means
+Vector Product, Vector Normalise, [[sv/mv.swizzle]], Texture LD/ST
+operations, and others critical to an efficient, effective 3D GPU and
+VPU ISA. With such instructions being included as standard in other
+commercially-successful GPU ISAs it is likewise critical that a 3D
+GPU/VPU based on svp64 also have such instructions.
+
+Note however that svp64 is stand-alone and is in no way
+critically dependent on the existence or provision of 3D GPU or VPU
+instructions. These should be considered extensions, and their discussion
+and specification is out of scope for this document.
+
+Note, again: this is *only* under svp64 prefixing.  Standard v3.0B /
+v3.1B is *not* altered by svp64 in any way.
 
 ## Major opcode map (v3.0B)
 
@@ -59,9 +93,14 @@ Table 9: Primary Opcode Map (opcode bits 0:5)
 
 ## Suitable for svp64
 
-This is the same table containing v3.0B Primary Opcodes except those that make no sense in a Vectorisation Context have been removed.  These removed POs can, *in the SV Vector Context only*, be assigned to alternative (Vectorised-only) instructions, including future extensions.
+This is the same table containing v3.0B Primary Opcodes except those that
+make no sense in a Vectorisation Context have been removed.  These removed
+POs can, *in the SV Vector Context only*, be assigned to alternative
+(Vectorised-only) instructions, including future extensions.
 
-Note, again, to emphasise: outside of svp64 these opcodes **do not** change.  When not prefixed with svp64 these opcodes **specifically** retain their v3.0B / v3.1B OpenPOWER Standard compliant meaning.
+Note, again, to emphasise: outside of svp64 these opcodes **do not**
+change.  When not prefixed with svp64 these opcodes **specifically**
+retain their v3.0B / v3.1B OpenPOWER Standard compliant meaning.
 
         |  000   |   001 |  010  | 011   |  100  |    101 |  110  |  111
     000 |        |       |       |       |       |        |       | mulli | 000
@@ -74,6 +113,12 @@ Note, again, to emphasise: outside of svp64 these opcodes **do not** change.  Wh
     111 |        |       | EXT58 | EXT59 |       | EXT61  |       | EXT63 | 111
         |  000   |   001 |   010 |  011  |   100 |   101  | 110   |  111
 
+# Single Predication
+
+This is a standard mode normally found in Vector ISAs.  every element in rvery source Vector and in the destination uses the same bit of one single predicate mask.
+
+Note however that in SVSTATE, implementors MUST increment both srcstep and dststep, and that the two must be equal at all times.
+
 # Twin Predication
 
 This is a novel concept that allows predication to be applied to a single
@@ -102,9 +147,10 @@ This is a huge list that creates extremely powerful combinations,
 particularly given that one of the predicate options is `(1<<r3)`
 
 Additional unusual capabilities of Twin Predication include a back-to-back
-version of VCOMPRESS-VEXPAND which is effectively the ability to do 
-sequentially ordered multiple VINSERTs.  The source predicate selects a 
-sequentially ordered subset of elements to be inserted; the destination predicate specifies the sequentially ordered recipient locations.
+version of VCOMPRESS-VEXPAND which is effectively the ability to do
+sequentially ordered multiple VINSERTs.  The source predicate selects a
+sequentially ordered subset of elements to be inserted; the destination
+predicate specifies the sequentially ordered recipient locations.
 This is equivalent to
 `llvm.masked.compressstore.*`
 followed by
@@ -117,9 +163,9 @@ see  [[av_opcodes]].
 
 To help ensure that audio quality is not compromised by overflow,
 "saturation" is provided, as well as a way to detect when saturation
-occurred if desired (Rc=1). When Rc=1 there will be a *vector* of CRs, one CR per
-element in the result (Note: this is different from VSX which has a
-single CR per block).
+occurred if desired (Rc=1). When Rc=1 there will be a *vector* of CRs,
+one CR per element in the result (Note: this is different from VSX which
+has a single CR per block).
 
 When N=0 the result is saturated to within the maximum range of an
 unsigned value.  For integer ops this will be 0 to 2^elwidth-1. Similar
@@ -127,7 +173,8 @@ logic applies to FP operations, with the result being saturated to
 maximum rather than returning INF, and the minimum to +0.0
 
 When N=1 the same occurs except that the result is saturated to the min
-or max of a signed result, and for FP to the min and max value rather than returning +/- INF.
+or max of a signed result, and for FP to the min and max value rather
+than returning +/- INF.
 
 When Rc=1, the CR "overflow" bit is set on the CR associated with the
 element, to indicate whether saturation occurred.  Note that due to
@@ -136,17 +183,84 @@ the hugely detrimental effect it has on parallel processing, XER.SO is
 overflow bit is therefore simply set to zero if saturation did not occur,
 and to one if it did.
 
-Note also that saturate on operations that produce a carry output are prohibited due to the conflicting use of the CR.so bit for storing if saturation occurred.
+Note also that saturate on operations that produce a carry output are
+prohibited due to the conflicting use of the CR.so bit for storing if
+saturation occurred.
 
 Post-analysis of the Vector of CRs to find out if any given element hit
 saturation may be done using a mapreduced CR op (cror), or by using the
 new crweird instruction, transferring the relevant CR bits to a scalar
 integer and testing it for nonzero.  see [[sv/cr_int_predication]]
 
-Note that the operation takes place at the maximum bitwidth (max of src and dest elwidth) and that truncation occurs to the range of the dest elwidth.
+Note that the operation takes place at the maximum bitwidth (max of
+src and dest elwidth) and that truncation occurs to the range of the
+dest elwidth.
 
 # Reduce mode
 
+There are two variants here.  The first is when the destination is scalar
+and at least one of the sources is Vector.  The second is more complex
+and involves map-reduction on vectors.
+
+The first defining characteristic distinguishing Scalar-dest reduce mode
+from Vector reduce mode is that Scalar-dest reduce issues VL element
+operations, whereas Vector reduce mode performs an actual map-reduce
+(tree reduction): typically `O(VL log VL)` actual computations.
+
+The second defining characteristic of scalar-dest reduce mode is that it
+is, in simplistic and shallow terms *serial and sequential in nature*,
+whereas the Vector reduce mode is definitely inherently paralleliseable.
+
+The reason why scalar-dest reduce mode is "simplistically" serial and
+sequential is that in certain circumstances (such as an `OR` operation
+or a MIN/MAX operation) it may be possible to parallelise the reduction.
+
+## Scalar result reduce mode
+
+In this mode, one register is identified as being the "accumulator".
+Scalar reduction is thus categorised by:
+
+* One of the sources is a Vector
+* the destination is a scalar
+* optionally but most usefully when one source register is also the destination
+* That the source register type is the same as the destination register
+  type identified as the "accumulator".  scalar reduction on `cmp`,
+  `setb` or `isel` is not possible for example because of the mixture
+  between CRs and GPRs.
+
+Typical applications include simple operations such as `ADD r3, r10.v,
+r3` where, clearly, r3 is being used to accumulate the addition of all
+elements is the vector starting at r10.
+
+     # add RT, RA,RB but when RT==RA
+     for i in range(VL):
+          iregs[RA] += iregs[RB+i] # RT==RA
+
+However, *unless* the operation is marked as "mapreduce", SV ordinarily
+**terminates** at the first scalar operation.  Only by marking the
+operation as "mapreduce" will it continue to issue multiple sub-looped
+(element) instructions in `Program Order`.
+
+Other examples include shift-mask operations where a Vector of inserts
+into a single destination register is required, as a way to construct
+a value quickly from multiple arbitrary bit-ranges and bit-offsets.
+Using the same register as both the source and destination, with Vectors
+of different offsets masks and values to be inserted has multiple
+applications including Video, cryptography and JIT compilation.
+
+Subtract and Divide are still permitted to be executed in this mode,
+although from an algorithmic perspective it is strongly discouraged.
+It would be better to use addition followed by one final subtract,
+or in the case of divide, to get better accuracy, to perform a multiply
+cascade followed by a final divide.
+
+Note that single-operand or three-operand scalar-dest reduce is perfectly
+well permitted: both still meet the qualifying characteristics that one
+source operand can also be the destination, which allows the "accumulator"
+to be identified.
+
+## Vector result reduce mode
+
 1. limited to single predicated dual src operations (add RT, RA, RB).
    triple source operations are prohibited (fma).
 2. limited to operations that make sense.  divide is excluded, as is
@@ -193,8 +307,9 @@ Pseudocode for the case where RA==RB:
 TODO: case where RA!=RB which involves first a vector of 2-operand
 results followed by a mapreduce on the intermediates.
 
-Note that when SVM is clear and SUBVL!=1 the sub-elements are *independent*, i.e. they
-are mapreduced per *sub-element* as a result.  illustration with a vec2:
+Note that when SVM is clear and SUBVL!=1 the sub-elements are
+*independent*, i.e. they are mapreduced per *sub-element* as a result.
+illustration with a vec2:
 
     result.x = op(iregs[RA].x, iregs[RA+1].x)
     result.y = op(iregs[RA].y, iregs[RA+1].y)
@@ -204,7 +319,8 @@ are mapreduced per *sub-element* as a result.  illustration with a vec2:
 
 Note here that Rc=1 does not make sense when SVM is clear and SUBVL!=1.
 
-When SVM is set and SUBVL!=1, another variant is enabled: horizontal subvector mode.  Example for a vec3:
+When SVM is set and SUBVL!=1, another variant is enabled: horizontal
+subvector mode.  Example for a vec3:
 
     for i in range(VL):
         result = op(iregs[RA+i].x, iregs[RA+i].x)
@@ -212,7 +328,8 @@ When SVM is set and SUBVL!=1, another variant is enabled: horizontal subvector m
         result = op(result, iregs[RA+i].z)
         iregs[RT+i] = result
 
-In this mode, when Rc=1 the Vector of CRs is as normal: each result element creates a corresponding CR element.
+In this mode, when Rc=1 the Vector of CRs is as normal: each result
+element creates a corresponding CR element.
 
 # Fail-on-first
 
@@ -233,10 +350,14 @@ executed in sequential Program Order, element 0 being the first.
   Thus the new VL comprises a contiguous vector of results, all of which
   pass the testing criteria (equal to zero, less than zero).
 
-The CR-based data-driven fail-on-first is new and not found in ARM SVE
-or RVV. It is extremely useful for reducing instruction count, however
-requires speculative execution involving modifications of VL to get high
-performance implementations.  An additional mode (RC1=1) effectively turns what would otherwise be an arithmetic operation into a type of `cmp`.  The CR is stored (and the CR.eq bit tested).  If the CR.eq bit fails then the Vector is truncated and the loop ends.  Note that when RC1=1 the result elements arw never stored, only the CRs.
+The CR-based data-driven fail-on-first is new and not found in ARM
+SVE or RVV. It is extremely useful for reducing instruction count,
+however requires speculative execution involving modifications of VL
+to get high performance implementations.  An additional mode (RC1=1)
+effectively turns what would otherwise be an arithmetic operation
+into a type of `cmp`.  The CR is stored (and the CR.eq bit tested).
+If the CR.eq bit fails then the Vector is truncated and the loop ends.
+Note that when RC1=1 the result elements arw never stored, only the CRs.
 
 In CR-based data-driven fail-on-first there is only the option to select
 and test one bit of each CR (just as with branch BO).  For more complex
@@ -255,13 +376,23 @@ One extremely important aspect of ffirst is:
   vectorised operations are effectively `nops` which is
   *precisely the desired and intended behaviour*.
 
-Another aspect is that for ffirst LD/STs, VL may be truncated arbitrarily to a nonzero value for any implementation-specific reason.  For example: it is perfectly reasonable for implementations to alter VL when ffirst LD or ST operations are initiated on a nonaligned boundary, such that within a loop the subsequent iteration of that loop begins subsequent ffirst LD/ST operations on an aligned boundary.  Likewise, to reduce workloads or balance resources.
+Another aspect is that for ffirst LD/STs, VL may be truncated arbitrarily
+to a nonzero value for any implementation-specific reason.  For example:
+it is perfectly reasonable for implementations to alter VL when ffirst
+LD or ST operations are initiated on a nonaligned boundary, such that
+within a loop the subsequent iteration of that loop begins subsequent
+ffirst LD/ST operations on an aligned boundary.  Likewise, to reduce
+workloads or balance resources.
 
-CR-based data-dependent first on the other hand MUST not truncate VL arbitrarily.  This because it is a precise test on which algorithms will rely.
+CR-based data-dependent first on the other hand MUST not truncate VL
+arbitrarily.  This because it is a precise test on which algorithms
+will rely.
 
 # pred-result mode
 
-This mode merges common CR testing with predication, saving on instruction count. Below is the pseudocode excluding predicate zeroing and elwidth overrides.
+This mode merges common CR testing with predication, saving on instruction
+count. Below is the pseudocode excluding predicate zeroing and elwidth
+overrides.
 
     for i in range(VL):
         # predication test, skip all masked out elements.
@@ -278,18 +409,28 @@ This mode merges common CR testing with predication, saving on instruction count
         # result optionally stored but CR always is
         iregs[RT+i] = result
 
-The reason for allowing the CR element to be stored is so that post-analysis
-of the CR Vector may be carried out.  For example: Saturation may have occurred (and been prevented from updating, by the test) but it is desirable to know *which* elements fail saturation.
+The reason for allowing the CR element to be stored is so that
+post-analysis of the CR Vector may be carried out.  For example:
+Saturation may have occurred (and been prevented from updating, by the
+test) but it is desirable to know *which* elements fail saturation.
 
-Note that RC1 Mode basically turns all operations into `cmp`.  The calculation is performed but it is only the CR that is written. The element result is *always* discarded, never written (just like `cmp`).
+Note that RC1 Mode basically turns all operations into `cmp`.  The
+calculation is performed but it is only the CR that is written. The
+element result is *always* discarded, never written (just like `cmp`).
 
-Note that predication is still respected: predicate zeroing is slightly different: elements that fail the CR test *or* are masked out are zero'd.
+Note that predication is still respected: predicate zeroing is slightly
+different: elements that fail the CR test *or* are masked out are zero'd.
 
 ## pred-result mode on CR ops
 
-Yes, really: CR operations (mtcr, crand, cror) may be Vectorised, predicated, and also pred-result mode applied to it.  In this case, the Vectorisation applies to the batch of 4 bits, i.e. it is not the CR individual bits that are treated as the Vector, but the CRs themselves (CR0, CR8, CR9...)
+Yes, really: CR operations (mtcr, crand, cror) may be Vectorised,
+predicated, and also pred-result mode applied to it.  In this case,
+the Vectorisation applies to the batch of 4 bits, i.e. it is not the CR
+individual bits that are treated as the Vector, but the CRs themselves
+(CR0, CR8, CR9...)
 
-Thus after each Vectorised operation (crand) a test of the CR result can in fact be performed.
+Thus after each Vectorised operation (crand) a test of the CR result
+can in fact be performed.
 
 # CR Operations
 
@@ -299,20 +440,6 @@ the access pattern needs to be understandable in relation to v3.0B / v3.1B
 numbering, with a clear linear relationship and mapping existing when
 SV is applied.
 
-## Analysis for compilers
-
-gcc and llvm automatically manage CRs: they are not explicitly accessible even at the assembly level (asm statement). This causes something of an issue even just for basic testing.  A solution then is to leverage the following:
-
-* scalar instructions produce scalar CRs.
-* gcc and llvm scalar instructions produce scalar CRs.
-* vector instructions produce vector CRs
-* therefore with the exception of branches there should be a match which does not require heavy modification of gcc
-
-The basic principle being to ensure that Vector attribute tags propagate through to the CRs.  For this to worrk all gcc and llvm code must act as if the numbering of CRs is kept entirely looking scalar despite the fact that it is actually vector.
-
-Thus the compiler when referring to CR0 still generates code that it thinks is scalar and thinks a scalar computation created CR0 but the same code serves double-duty and thus does not need drastic rewrites or modification.
-
-
 ## CR EXTRA mapping table and algorithm
 
 Numbering relationships for CR fields are already complex due to being
@@ -321,8 +448,8 @@ or v3.1B specification*).  However with some care and consideration
 the exact same mapping used for INT and FP regfiles may be applied,
 just to the upper bits, as explained below.
 
-In OpenPOWER v3.0/1, BF/BT/BA/BB are all 5 bits.  The top 3 bits (2:4)
-select one of the 8 CRs; the bottom 2 bits (0:1) select one of 4 bits
+In OpenPOWER v3.0/1, BF/BT/BA/BB are all 5 bits.  The top 3 bits (0:2)
+select one of the 8 CRs; the bottom 2 bits (3:4) select one of 4 bits
 *in* that CR.  The numbering was determined (after 4 months of
 analysis and research) to be as follows:
 
@@ -333,31 +460,31 @@ analysis and research) to be as follows:
     CR_bit = (CR_reg & (1<<bit_index)) != 0
 
 When it comes to applying SV, it is the CR\_reg number to which SV EXTRA2/3
-applies, **not** the CR\_bit portion (bits 0:1):
+applies, **not** the CR\_bit portion (bits 3:4):
 
     if extra3_mode:
         spec = EXTRA3
     else:
         spec = EXTRA2<<1 | 0b0
-    if spec[2]:
-       # vector constructs "BA[2:4] spec[0:1] 0 BA[0:1]"
-       return ((BA >> 2)<<5) | # hi 3 bits shifted up
-              (spec[0:1]<<3) |  # to make room for these
+    if spec[0]:
+       # vector constructs "BA[0:2] spec[1:2] 00 BA[3:4]"
+       return ((BA >> 2)<<6) | # hi 3 bits shifted up
+              (spec[1:2]<<4) | # to make room for these
               (BA & 0b11)      # CR_bit on the end
     else:
-       # scalar constructs "0 spec[0:1] BA[0:4]"
-       return (spec[0:1] << 5) | BA
+       # scalar constructs "00 spec[1:2] BA[0:4]"
+       return (spec[1:2] << 5) | BA
 
 Thus, for example, to access a given bit for a CR in SV mode, the v3.0B
 algorithm to determin CR\_reg is modified to as follows:
 
     CR_index = 7-(BA>>2)      # top 3 bits but BE
-    if spec[2]:
-        # vector mode
-        CR_index = (CR_index<<3) | (spec[0:1] << 1)
+    if spec[0]:
+        # vector mode, 0-124 increments of 4
+        CR_index = (CR_index<<4) | (spec[1:2] << 2)
     else:
-        # scalar mode
-        CR_index = (spec[0:1]<<3) | CR_index
+        # scalar mode, 0-32 increments of 1
+        CR_index = (spec[1:2]<<3) | CR_index
     # same as for v3.0/v3.1 from this point onwards
     bit_index = 3-(BA & 0b11) # low 2 bits but BE
     CR_reg = CR{CR_index}     # get the CR
@@ -401,12 +528,9 @@ result of the operation as one part of that element *and a corresponding
 CR element*.  Greatly simplified pseudocode:
 
     for i in range(VL):
-         # calculate the vector result of an add
-         iregs[RT+i] = iregs[RA+i] + iregs[RB+i]
-         # now calculate CR bits
-         CRs{8+i}.eq = iregs[RT+i] == 0
-         CRs{8+i}.gt = iregs[RT+i] > 0
-         ... etc
+         # calculate the vector result of an add iregs[RT+i] = iregs[RA+i]
+         + iregs[RB+i] # now calculate CR bits CRs{8+i}.eq = iregs[RT+i]
+         == 0 CRs{8+i}.gt = iregs[RT+i] > 0 ... etc
 
 If a "cumulated" CR based analysis of results is desired (a la VSX CR6)
 then a followup instruction must be performed, setting "reduce" mode on
@@ -426,10 +550,15 @@ hindrance, regardless of the length of VL.
 
 ## Rc=1 when SUBVL!=1
 
-sub-vectors are effectively a form of SIMD (length 2 to 4). Only 1 bit of predicate is allocated per subvector; likewise only one CR is allocated
+sub-vectors are effectively a form of SIMD (length 2 to 4). Only 1 bit of
+predicate is allocated per subvector; likewise only one CR is allocated
 per subvector.
 
-This leaves a conundrum as to how to apply CR computation per subvector, when normally Rc=1 is exclusively applied to scalar elements.  A solution is to perform a bitwise OR or AND of the subvector tests.  Given that OE is ignored, rhis field may (when available) be used to select OR or AND behavior.
+This leaves a conundrum as to how to apply CR computation per subvector,
+when normally Rc=1 is exclusively applied to scalar elements.  A solution
+is to perform a bitwise OR or AND of the subvector tests.  Given that
+OE is ignored, rhis field may (when available) be used to select OR or
+AND behavior.
 
 ### Table of CR fields
 
@@ -438,7 +567,9 @@ so FP instructions with Rc=1 write to CR[1] aka SVCR1_000.
 
 CRs are not stored in SPRs: they are registers in their own right.
 Therefore context-switching the full set of CRs involves a Vectorised
-mfcr or mtcr, using VL=64, elwidth=8 to do so.  This is exactly as how scalar OpenPOWER context-switches CRs: it is just that there are now more of them.
+mfcr or mtcr, using VL=64, elwidth=8 to do so.  This is exactly as how
+scalar OpenPOWER context-switches CRs: it is just that there are now
+more of them.
 
 The 64 SV CRs are arranged similarly to the way the 128 integer registers
 are arranged.  TODO a python program that auto-generates a CSV file
@@ -461,37 +592,37 @@ TODO generate table which will be here [[svp64/reg_profiles]]
 
 ## Single-predicated Instruction
 
-illustration of normal mode add operation: zeroing not included, elwidth overrides not included.  if there is no predicate, it is set to all 1s
+illustration of normal mode add operation: zeroing not included, elwidth
+overrides not included.  if there is no predicate, it is set to all 1s
 
     function op_add(rd, rs1, rs2) # add not VADD!
-      int i, id=0, irs1=0, irs2=0;
-      predval = get_pred_val(FALSE, rd);
+      int i, id=0, irs1=0, irs2=0; predval = get_pred_val(FALSE, rd);
       for (i = 0; i < VL; i++)
-        STATE.srcoffs = i # save context
-        if (predval & 1<<i) # predication uses intregs
-           ireg[rd+id] <= ireg[rs1+irs1] + ireg[rs2+irs2];
-           if (!int_vec[rd ].isvec) break;
-        if (rd.isvec)  { id += 1; }
-        if (rs1.isvec)  { irs1 += 1; }
-        if (rs2.isvec)  { irs2 += 1; }
-        if (id == VL or irs1 == VL or irs2 == VL) {
-          # end VL hardware loop
-          STATE.srcoffs = 0; # reset
-          return;
+        STATE.srcoffs = i # save context if (predval & 1<<i) # predication
+        uses intregs
+           ireg[rd+id] <= ireg[rs1+irs1] + ireg[rs2+irs2]; if (!int_vec[rd
+           ].isvec) break;
+        if (rd.isvec)  { id += 1; } if (rs1.isvec)  { irs1 += 1; } if
+        (rs2.isvec)  { irs2 += 1; } if (id == VL or irs1 == VL or irs2 ==
+        VL) {
+          # end VL hardware loop STATE.srcoffs = 0; # reset return;
         }
 
 This has several modes:
 
-* RT.v = RA.v RB.v
-* RT.v = RA.v RB.s (and RA.s RB.v)
-* RT.v = RA.s RB.s
-* RT.s = RA.v RB.v
-* RT.s = RA.v RB.s (and RA.s RB.v)
-* RT.s = RA.s RB.s
+* RT.v = RA.v RB.v * RT.v = RA.v RB.s (and RA.s RB.v) * RT.v = RA.s RB.s *
+RT.s = RA.v RB.v * RT.s = RA.v RB.s (and RA.s RB.v) * RT.s = RA.s RB.s
 
-All of these may be predicated.  Vector-Vector is straightfoward.  When one of source is a Vector and the other a Scalar, it is clear that each element of the Vector source should be added to the Scalar source, each result placed into the Vector (or, if the destination is a scalar, only the first nonpredicated result). 
+All of these may be predicated.  Vector-Vector is straightfoward.
+When one of source is a Vector and the other a Scalar, it is clear that
+each element of the Vector source should be added to the Scalar source,
+each result placed into the Vector (or, if the destination is a scalar,
+only the first nonpredicated result).
 
-The one that is not obvious is RT=vector but both RA/RB=scalar.  Here this acts as a "splat scalar result", copying the same result into all nonpredicated result elements.  If a fixed destination scalar was intended, then an all-Scalar operation should be used.
+The one that is not obvious is RT=vector but both RA/RB=scalar.
+Here this acts as a "splat scalar result", copying the same result into
+all nonpredicated result elements.  If a fixed destination scalar was
+intended, then an all-Scalar operation should be used.
 
 See <https://bugs.libre-soc.org/show_bug.cgi?id=552>
 
@@ -514,3 +645,40 @@ Fields:
 * spred={reg spec}
 
 similar to x86 "rex" prefix.
+
+For actual assembler:
+
+    sv.asmcode/mode.vec{N}.ew=8,sw=16,m={pred},sm={pred} reg.v, src.s
+
+Qualifiers:
+
+* m={pred}: predicate mask mode
+* sm={pred}: source-predicate mask mode (only allowed in Twin-predication)
+* vec{N}: vec2 OR vec3 OR vec4 - sets SUBVL=2/3/4
+* ew={N}: ew=8/16/32 - sets elwidth override
+* sw={N}: sw=8/16/32 - sets source elwidth override
+* ff={xx}: see fail-first mode
+* pr={xx}: see predicate-result mode
+* sat{x}: satu / sats - see saturation mode
+* mr: see map-reduce mode
+* mr.svm see map-reduce with sub-vector mode
+* crm: see map-reduce CR mode
+* crm.svm see map-reduce CR with sub-vector mode
+* sz: predication with source-zeroing
+* dz: predication with dest-zeroing
+
+For modes:
+
+* pred-result:
+  - pm=lt/gt/le/ge/eq/ne/so/ns OR
+  - pm=RC1 OR pm=~RC1
+* fail-first
+  - ff=lt/gt/le/ge/eq/ne/so/ns OR
+  - ff=RC1 OR ff=~RC1
+* saturation:
+  - sats
+  - satu
+* map-reduce:
+  - mr OR crm: "normal" map-reduce mode or CR-mode.
+  - mr.svm OR crm.svm: when vec2/3/4 set, sub-vector mapreduce is enabled
+