(no commit message)
[libreriscv.git] / openpower / sv / svp64 / appendix.mdwn
index 90fa264884db8b95f4cedeb48f03a4abfd9ce48c..bfbae889afeda5af7303df75e02b4eb1f416a95c 100644 (file)
@@ -2,6 +2,7 @@
 
 * <https://bugs.libre-soc.org/show_bug.cgi?id=574>
 * <https://bugs.libre-soc.org/show_bug.cgi?id=558#c47>
+* <https://bugs.libre-soc.org/show_bug.cgi?id=697>
 
 This is the appendix to [[sv/svp64]], providing explanations of modes
 etc. leaving the main svp64 page's primary purpose as outlining the
@@ -36,7 +37,7 @@ 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
+# v3.0B/v3.1 relevant instructions
 
 SV is primarily designed for use as an efficient hybrid 3D GPU / VPU /
 CPU ISA.
@@ -117,6 +118,54 @@ It is important to note that having a different v3.0B Scalar opcode
 that is different from an SVP64 one is highly undesirable: the complexity
 in the decoder is greatly increased.
 
+# EXTRA Field Mapping
+
+In Power ISA v3.1 prefixing there are bits which describe and classify
+the prefix in a fashion that is independent of the suffix. MLSS for
+example.  For SVP64 there is insufficient space to make the SVP64 Prefix
+"self-describing", and consequently every single Scalar instruction 
+had to be individually analysed, by rote, to craft an EXTRA Field Mapping.
+This process was semi-automated and is described in this section.
+The final results, which are part of the SVP64 Specification, are here:
+
+* [[openpower/opcode_regs_deduped]]
+
+Firstly, every instruction's mnemonic (`add RT, RA, RB`) was analysed
+from reading the markdown formatted version of the Scalar pseudocode
+which is machine-readable and found in [[openpower/isatables]].  The
+analysis gives, by instruction, a "Register Profile".  `add RT, RA, RB`
+for example is given a designation `RM-2R-1W` because it requires
+two GPR reads and one GPR write.
+
+Secondly, the total number of registers was added up (2R-1W is 3 registers)
+and if less than or equal to three then that instruction could be given an
+EXTRA3 designation.  Four or more is given an EXTRA2 designation because
+there are only 9 bits available.
+
+Thirdly, a packing format was decided: for 2R-1W an EXTRA3 indexing
+could have been decided
+that RA would be indexed 0 (EXTRA bits 0-2), RB indexed 1 (EXTRA bits 3-5)
+and RT indexed 2 (EXTRA bits 6-8).  In some cases (LD/ST with update)
+RA-as-a-source is given a **different** EXTRA index from RA-as-a-result
+(because it is possible to do, and perceived to be useful).
+
+Fourthly, the instruction was analysed to see if Twin or Single
+Predication was suitable.  As a general rule this was if there
+was only a single operand and a single result (`extw` and LD/ST)
+however some 2 or 3 operand instructions also qualify.
+
+Fifthly, in an automated process the results of the analysis
+were outputted in CSV Format for use in machine-readable form
+by sv_analysis.py <https://git.libre-soc.org/?p=openpower-isa.git;a=blob;f=src/openpower/sv/sv_analysis.py;hb=HEAD>
+
+Quslifying future Power ISA Scalar instructions for SVP64
+is **strongly** advised to utilise this same process and the same
+sv_analysis.py program.  Alterations to that same program which
+change the Designation is **prohibited** once finalised (ratified
+through the Power ISA WG Process). It would
+be similar to deciding that `add` should be changed from X-Form
+to D-Form.
+
 # Single Predication
 
 This is a standard mode normally found in Vector ISAs.  every element in every source Vector and in the destination uses the same bit of one single predicate mask.
@@ -160,78 +209,82 @@ This is equivalent to
 followed by
 `llvm.masked.expandload.*`
 
-# Rounding, clamp and saturate
-
-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).
-
-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
-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.
-
-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
-the hugely detrimental effect it has on parallel processing, XER.SO is
-**ignored** completely and is **not** brought into play here.  The CR
-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.
-
-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.
-
-# 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
+# Reduce modes
+
+Reduction in SVP64 is deterministic and somewhat of a misnomer.  A normal
+Vector ISA would have explicit Reduce opcodes with defined characteristics
+per operation: in SX Aurora there is even an additional scalar argument
+containing the initial reduction value, and the default is either 0
+or 1 depending on the specifics of the explicit opcode.
+SVP64 fundamentally has to
+utilise *existing* Scalar Power ISA v3.0B operations, which presents some
+unique challenges.
+
+The solution turns out to be to simply define reduction as permitting
+deterministic element-based schedules to be issued using the base Scalar
+operations, and to rely on the underlying microarchitecture to resolve
+Register Hazards at the element level.  This goes back to
+the fundamental principle that SV is nothing more than a Sub-Program-Counter
+sitting between Decode and Issue phases.
+
+Microarchitectures *may* take opportunities to parallelise the reduction
+but only if in doing so they preserve Program Order at the Element Level.
+Opportunities where this is possible include an `OR` operation
+or a MIN/MAX operation: it may be possible to parallelise the reduction,
+but for Floating Point it is not permitted due to different results
+being obtained if the reduction is not executed in strict Program-Sequential
+Order.
+
+In essence it becomes the programmer's responsibility to leverage the
+pre-determined schedules to desired effect.
+
+## Scalar result reduction and iteration
+
+Scalar Reduction per se does not exist, instead is implemented in SVP64
+as a simple and natural relaxation of the usual restriction on the Vector
+Looping which would terminate if the destination was marked as a Scalar.
+Scalar Reduction by contrast *keeps issuing Vector Element Operations*
+even though the destination register is marked as scalar.
+Thus it is up to the programmer to be aware of this and observe some
+conventions.
+
+It is also important to appreciate that there is no
+actual imposition or restriction on how this mode is utilised: there
+will therefore be several valuable uses (including Vector Iteration
+and "Reverse-Gear")
+and it is up to the programmer to make best use of the
+(strictly deterministic) capability
+provided.
 
 In this mode, which is suited to operations involving carry or overflow,
-one register must be identified by the programmer as being the "accumulator".
-Scalar reduction is thus categorised by:
+one register must be assigned, by convention by the programmer to be 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
+* optionally but most usefully when one source scalar register is
+  also the scalar destination (which may be informally termed
+  the "accumulator")
 * That the source register type is the same as the destination register
-  type identified as the "accumulator".  scalar reduction on `cmp`,
+  type identified as the "accumulator".  Scalar reduction on `cmp`,
   `setb` or `isel` makes no sense for example because of the mixture
   between CRs and GPRs.
 
+*Note that issuing instructions in Scalar reduce mode such as `setb`
+are neither `UNDEFINED` nor prohibited, despite them not making much
+sense at first glance.
+Scalar reduce is strictly defined behaviour, and the cost in
+hardware terms of prohibition of seemingly non-sensical operations is too great.
+Therefore it is permitted and required to be executed successfully.
+Implementors **MAY** choose to optimise such instructions in instances
+where their use results in "extraneous execution", i.e. where it is clear
+that the sequence of operations, comprising multiple overwrites to
+a scalar destination **without** cumulative, iterative, or reductive
+behaviour (no "accumulator"), may discard all but the last element
+operation.  Identification
+of such is trivial to do for `setb` and `cmp`: the source register type is
+a completely different register file from the destination*
+
 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.
@@ -240,16 +293,20 @@ elements is the vector starting at r10.
      for i in range(VL):
           iregs[RA] += iregs[RB+i] # RT==RA
 
-However, *unless* the operation is marked as "mapreduce", SV ordinarily
+However, *unless* the operation is marked as "mapreduce" (`sv.add/mr`)
+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`.
 
-To.perform the loop in reverse order, the ```RG``` (reverse gear) bit must be set.  This is useful for leaving a cumulative suffix sum in reverse order:
-
-    for i in (VL-1 downto 0):
-        # RT-1 = RA gives a suffix sum
-        iregs[RT+i] = iregs[RA+i] - iregs[RB+i]
+To perform the loop in reverse order, the ```RG``` (reverse gear) bit must be set.  This may be useful in situations where the results may be different
+(floating-point) if executed in a different order.  Given that there is
+no actual prohibition on Reduce Mode being applied when the destination
+is a Vector, the "Reverse Gear" bit turns out to be a way to apply Iterative
+or Cumulative Vector operations in reverse. `sv.add/rg r3.v, r4.v, r4.v`
+for example will start at the opposite end of the Vector and push
+a cumulative series of overlapping add operations into the Execution units of
+the underlying hardware.
 
 Other examples include shift-mask operations where a Vector of inserts
 into a single destination register is required, as a way to construct
@@ -265,16 +322,11 @@ 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.
-
-If the "accumulator" cannot be identified (one of the sources is also
-a destination) the results are **UNDEFINED**.  This permits implementations
-to not have to have complex decoding analysis of register fields: it
-is thus up to the programmer to ensure that one of the source registers
-is also a destination register in order to take advantage of Scalar
-Reduce Mode.
+well permitted: the programmer may still declare one register, used as
+both a Vector source and Scalar destination, to be utilised as 
+the "accumulator".  In the case of `sv.fmadds` and `sv.maddhw` etc
+this naturally fits well with the normal expected usage of these
+operations.
 
 If an interrupt or exception occurs in the middle of the scalar mapreduce,
 the scalar destination register **MUST** be updated with the current
@@ -291,98 +343,57 @@ be precise.
 
 ## Vector result reduce mode
 
-Vector result reduce mode may utilise the destination vector for
-the purposes of storing intermediary results.  Interrupts and exceptions
-can therefore also be precise.  The result will be in the first
-non-predicate-masked-out destination element.  Note that unlike
-Scalar reduce mode, Vector reduce
-mode is *not* suited to operations which involve carry or overflow.
-
-Programs **MUST NOT** rely on the contents of the intermediate results:
-they may change from hardware implementation to hardware implementation.
-Some implementations may perform an incremental update, whilst others
-may choose to use the available Vector space for a binary tree reduction.
-If an incremental Vector is required (```x[i] = x[i-1] + y[i]```) then
-a *straight* SVP64 Vector instruction can be issued, where the source and
-destination registers overlap: ```sv.add 1.v, 9.v, 2.v```. Due to
-respecting ```Program Order``` being mandatory in SVP64, hardware should
-and must detect this case and issue an incremental sequence of scalar
-element instructions.
-
-1. limited to single predicated dual src operations (add RT, RA, RB).
-   triple source operations are prohibited (such as fma).
-2. limited to operations that make sense.  divide is excluded, as is
-   subtract (X - Y - Z produces different answers depending on the order)
-   and asymmetric CRops (crandc, crorc). sane  operations:
-   multiply, min/max, add, logical bitwise OR, most other CR ops.
-   operations that do have the same source and dest register type are
-   also excluded (isel, cmp). operations involving carry or overflow
-   (XER.CA / OV) are also prohibited.
-3. the destination is a vector but the result is stored, ultimately,
-   in the first nonzero predicated element.  all other nonzero predicated
-   elements are undefined. *this includes the CR vector* when Rc=1
-4. implementations may use any ordering and any algorithm to reduce
-   down to a single result.  However it must be equivalent to a straight
-   application of mapreduce.  The destination vector (except masked out
-   elements) may be used for storing any intermediate results. these may
-   be left in the vector (undefined).
-5. CRM applies when Rc=1.  When CRM is zero, the CR associated with
-   the result is regarded as a "some results met standard CR result
-   criteria". When CRM is one, this changes to "all results met standard
-   CR criteria".
-6. implementations MAY use destoffs as well as srcoffs (see [[sv/sprs]])
-   in order to store sufficient state to resume operation should an
-   interrupt occur. this is also why implementations are permitted to use
-   the destination vector to store intermediary computations
-7. *Predication may be applied*.  zeroing mode is not an option.  masked-out
-   inputs are ignored; masked-out elements in the destination vector are
-   unaltered (not used for the purposes of intermediary storage); the
-   scalar result is placed in the first available unmasked element.
-
-Pseudocode for the case where RA==RB:
-
-    result = op(iregs[RA], iregs[RA+1])
-    CR = analyse(result)
-    for i in range(2, VL):
-        result = op(result, iregs[RA+i])
-        CRnew = analyse(result)
-        if Rc=1
-            if CRM:
-                 CR = CR bitwise or CRnew
-            else:
-                 CR = CR bitwise AND CRnew
-
-TODO: case where RA!=RB which involves first a vector of 2-operand
-results followed by a mapreduce on the intermediates.
+Vector Reduce Mode issues a deterministic tree-reduction schedule to the underlying micro-architecture.  Like Scalar reduction, the "Scalar Base"
+(Power ISA v3.0B) operation is leveraged, unmodified, to give the
+*appearance* and *effect* of Reduction.
+
+Given that the tree-reduction schedule is deterministic,
+Interrupts and exceptions
+can therefore also be precise.  The final result will be in the first
+non-predicate-masked-out destination element, but due again to
+the deterministic schedule programmers may find uses for the intermediate
+results.
+
+When Rc=1 a corresponding Vector of co-resultant CRs is also
+created.  No special action is taken: the result and its CR Field
+are stored "as usual" exactly as all other SVP64 Rc=1 operations.
+
+## Sub-Vector Horizontal Reduction
 
 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:
+illustration with a vec2, assuming RA==RT, e.g `sv.add/mr/vec2 r4, r4, r16`
 
-    result.x = op(iregs[RA].x, iregs[RA+1].x)
-    result.y = op(iregs[RA].y, iregs[RA+1].y)
-    for i in range(2, VL):
-        result.x = op(result.x, iregs[RA+i].x)
-        result.y = op(result.y, iregs[RA+i].y)
+    for i in range(0, VL):
+        # RA==RT in the instruction. does not have to be
+        iregs[RT].x = op(iregs[RT].x, iregs[RB+i].x)
+        iregs[RT].y = op(iregs[RT].y, iregs[RB+i].y)
 
-Note here that Rc=1 does not make sense when SVM is clear and SUBVL!=1.
+Thus logically there is nothing special or unanticipated about
+`SVM=0`: it is expected behaviour according to standard SVP64
+Sub-Vector rules.
 
-When SVM is set and SUBVL!=1, another variant is enabled: horizontal
-subvector mode.  Example for a vec3:
+By contrast, when SVM is set and SUBVL!=1, a Horizontal
+Subvector mode is enabled, which behaves very much more
+like a traditional Vector Processor Reduction instruction.
+Example for a vec3:
 
     for i in range(VL):
-        result = op(iregs[RA+i].x, iregs[RA+i].x)
+        result = iregs[RA+i].x
         result = op(result, iregs[RA+i].y)
         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.
+element creates a corresponding CR element (for the final, reduced, result).
 
 # Fail-on-first
 
-Data-dependent fail-on-first has two distinct variants: one for LD/ST,
-the other for arithmetic operations (actually, CR-driven).  Note in each
+Data-dependent fail-on-first has two distinct variants: one for LD/ST
+(see [[sv/ldst]],
+the other for arithmetic operations (actually, CR-driven)
+([[sv/normal]]) and CR operations ([[sv/cr_ops]]).
+Note in each
 case the assumption is that vector elements are required appear to be
 executed in sequential Program Order, element 0 being the first.
 
@@ -470,36 +481,11 @@ More details can be found in [[sv/cr_ops]].
 
 # 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. Note that the paeudocode for [[sv/cr_ops]] is slightly different.
-
-    for i in range(VL):
-        # predication test, skip all masked out elements.
-        if predicate_masked_out(i):
-             continue
-        result = op(iregs[RA+i], iregs[RB+i])
-        CRnew = analyse(result) # calculates eq/lt/gt
-        # Rc=1 always stores the CR
-        if Rc=1 or RC1:
-            crregs[offs+i] = CRnew
-        # now test CR, similar to branch
-        if RC1 or CRnew[BO[0:1]] != BO[2]:
-            continue # test failed: cancel store
-        # 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.
-
-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.
+Predicate-result merges common CR testing with predication, saving on
+instruction count.  In essence, a Condition Register Field test
+is performed, and if it fails it is considered to have been
+*as if* the destination predicate bit was zero.
+Arithmetic and Logical Pred-result is covered in [[sv/normal]]
 
 ## pred-result mode on CR ops
 
@@ -521,7 +507,7 @@ SV is applied.
 
 Numbering relationships for CR fields are already complex due to being
 in BE format (*the relationship is not clearly explained in the v3.0B
-or v3.1B specification*).  However with some care and consideration
+or v3.1 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.  The notation
 `CR{field number}` is used to indicate access to a particular
@@ -529,6 +515,14 @@ Condition Register Field (as opposed to the notation `CR[bit]`
 which accesses one bit of the 32 bit Power ISA v3.0B
 Condition Register)
 
+`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]
+
+For SVP64 the relationship for the sequential
+numbering of elements is to the CR **fields** within
+the CR Register, not to individual bits within the CR register.
+
 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
@@ -609,9 +603,12 @@ 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
@@ -779,7 +776,7 @@ For modes:
 /// `temp_pred` is a user-visible Vector Condition register 
 ///
 /// all input arrays have length `vl`
-def reduce(  vl,  vec, pred, pred,):
+def reduce(vl, vec, pred):
     step = 1;
     while step < vl
         step *= 2;
@@ -791,7 +788,13 @@ def reduce(  vl,  vec, pred, pred,):
             else if other_pred
                 vec[i] = vec[other];
             pred[i] |= other_pred;
+```
 
+we'd want to use something based on the above pseudo-code
+rather than the below pseudo-code -- reasoning here:
+<https://bugs.libre-soc.org/show_bug.cgi?id=697#c11>
+
+```
 def reduce(  vl,  vec, pred, pred,):
     j = 0
     vi = [] # array of lookup indices to skip nonpredicated