Normal SVP64 Modes, for Arithmetic and Logical Operations
- 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=936 write on failfirst
- svp64
Normal SVP64 Mode covers Arithmetic and Logical operations to provide suitable additional behaviour. The Mode field is bits 19-23 of the svp64 RM Field.
Table of contents:
Mode
Mode is an augmentation of SV behaviour, providing additional functionality. Some of these alterations are element-based (saturation), others are Vector-based (mapreduce, fail-on-first).
ldst, cr ops and branches are covered separately: the following Modes apply to Arithmetic and Logical SVP64 operations:
- simple mode is straight vectorization. No augmentations: the vector comprises an array of independently created results.
- ffirst or data-dependent fail-on-first: see separate section. The vector may be truncated depending on certain criteria. VL is altered as a result.
- sat mode or saturation: clamps each element result to a min/max rather than overflows / wraps. Allows signed and unsigned clamping for both INT and FP.
- reduce mode. If used correctly, a mapreduce (or a prefix sum)
is performed. See appendix.
Note that there are comprehensive caveats when using this mode,
and it should not be confused with the Parallel Reduction remap.
Also care is needed with
hphint
.
Note that ffirst and reduce modes are not anticipated to be high-performance in some implementations. ffirst due to interactions with VL, and reduce due to it creating overlapping operations in many of its uses. simple and saturate are however inter-element independent and may easily be parallelised to give high performance, regardless of the value of VL.
The Mode table for Arithmetic and Logical operations,
being bits 19-23 of SVP64 RM
, is laid out as
follows:
0-1 | 2 | 3 4 | description |
---|---|---|---|
0 0 | 0 | dz sz | simple mode |
0 0 | 1 | RG 0 | scalar reduce mode (mapreduce) |
0 0 | 1 | / 1 | reserved |
1 0 | N | dz sz | sat mode: N=0/1 u/s |
VLi 1 | inv | CR-bit | Rc=1: ffirst CR sel |
VLi 1 | inv | zz RC1 | Rc=0: ffirst z/nonz |
Fields:
- sz / dz source-zeroing, destination-zeroing. if predication is enabled will put zeros into the dest (or as src in the case of twin pred) when the predicate bit is zero. Otherwise the element is ignored or skipped, depending on context.
- zz: both sz and dz are set equal to this flag
- inv CR bit just as in branches (BO) these bits allow testing of a CR bit and whether it is set (inv=0) or unset (inv=1)
- RG inverts the Vector Loop order (VL-1 downto 0) rather than the normal 0..VL-1
- N sets signed/unsigned saturation.
- RC1 as if Rc=1, on operations that do not have it (typically Logical)
- VLi VL inclusive: in fail-first mode, the truncation of VL includes the current element at the failure point rather than excludes it from the count.
For LD/ST Modes, see ldst. For Condition Registers see cr ops. For Branch modes, see branches.
Rounding, clamp and saturate
See av opcodes for relevant opcodes and use-cases.
To help ensure for example 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 2elwidth-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. This behaviour (ignoring XER.SO) is actually optional in the SFFS Compliancy Subset: for SVP64 it is made mandatory but only on Vectorized instructions.
Note also that saturate on operations that set OE=1 must raise an Illegal
Instruction due to the conflicting use of the CR.so bit for storing
if saturation occurred. Vectorized Integer Operations that produce a
Carry-Out (CA, CA32): these two bits will be UNDEFINED
if saturation
is also requested.
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.
Programmer's Note: 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 crrweird instruction with Rc=1, which will transfer the required CR bits to a scalar integer and update CR0, which will allow testing the scalar integer for nonzero. See cr int predication. Alternatively, a Data-Dependent Fail-First may be used to truncate the Vector Length to non-saturated elements, greatly increasing the productivity of parallelised inner hot-loops.
Reduce mode
Reduction in SVP64 is similar in essence to other Vector Processing ISAs, but leverages the underlying scalar Base v3.0B operations. Thus it is more a convention that the programmer may utilise to give the appearance and effect of a Horizontal Vector Reduction. Due to the unusual decoupling it is also possible to perform prefix-sum (Fibonacci Series) in certain circumstances. Details are in the appendix
Reduce Mode should not be confused with Parallel Reduction remap. As explained in the appendix Reduce Mode switches off the check which would normally stop looping if the result register is scalar. Thus, the result scalar register, if also used as a source scalar, may be used to perform sequential accumulation. This deliberately sets up a chain of Register Hazard Dependencies (which advanced hardware may optimise out), whereas Parallel Reduce remap deliberately issues a Tree-Schedule of operations that may be parallelised.
Hardware architectural note: implementations may optimise out the Hazard Dependency chain as long as Sequential Program Execution Order is preserved. Easy examples include Reduction on Logical OR or AND operations.
Horizontal Parallelism Hint
SVSTATE.hphint
declares to hardware that groups of elements up to this
size are 100% independent (free of all Hazards inter-element but not inter-group).
With Reduction literally creating Dependency
Hazards on every element-level sub-instruction it is pretty clear that setting
hphint
at all would cause data corruption. However sv.add *r0, *r4, *r0
for example clearly leaves room for four parallel elements. Programmers must
be aware of this and exercise caution.
Data-dependent Fail-on-first
Data-dependent fail-on-first is CR-field-driven and is completely separate and distinct from LD/ST Fail-First (also known as Fault-First). Note in each case the assumption is that vector elements are required to appear to be executed in sequential Program Order. When REMAP is not active, element 0 would be the first.
Arithmetic/Logical Data-driven (CR-field-driven) fail-on-first performs a
test ofvthe result, similar to
Branch-Conditional BO
field testing, and if the test fails, the
Vector Loop operation terminates, and VL is truncated to either the previous
element or the current one, depending on whether VLi (VL "inclusive")
is clear or set, respectively.
Thus the new VL comprises a contiguous vector of results, all of which pass the testing criteria (equal to zero, less than zero etc as defined by the CR-bit test). When Rc=1 the Condition Regster Field for the element just tested is always written out (regardless of VLi).
- VLi=0 Only elements that passed the test are written out. When Rc=1 the co-result CR Field element is written out (even if the current test failed). Vector length is truncated to "elements that passed"
- VLi=1 Elements that were tested are written out. When Rc=1 the co-result CR Field element is written out. Vector length is truncated to "elements tested up to the first fail point"
Note: when VLi is clear, the behaviour at first seems counter-intuitive. A result is calculated but if the test fails it is prohibited from being actually written. This becomes intuitive again when it is remembered that the length that VL is set to is the number of written elements, and only when VLI is set will the current element be included in that count.
The CR-based data-driven fail-on-first is "new" and not found in ARM SVE
or RVV. At the same time it is "old" because it is almost identical to
a generalised form of Z80's CPIR
instruction. 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) allows instructions that would not normally
have an Rc=1 mode to at least be tested for zero or non-zero.
The CR is stored (and the
CR.eq bit tested against the inv
field). If the CR.eq bit is equal to
inv
then the Vector is truncated and the loop ends.
VLi is only available as an option when Rc=0
(or for instructions
which do not have Rc). When set, the current element is always also
included in the count (the new length that VL will be set to). This may
be useful in combination with "inv" to truncate the Vector to exclude
elements that fail a test, or, in the case of implementations of strncpy,
to include the terminating zero.
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 tests this may be insufficient. If that is the case, a vectorized crop such as crand, cror or cr int predication crweirder may be used, and ffirst applied to the crop instead of to the arithmetic vector. Note that crops are covered by the cr ops Mode format.
Important to note is that reduce mode is implied by Data-Dependent Fail-First. In other words where normally if the destination is Scalar, the looping terminates at the first result, Data-Dependent Fail-First continues just as it does in reduce mode. This allows effectively conditional reduction (one register is both a source and destination) where testing of each result gives an option to exit.
Use of Fail-on-first with Vertical-First Mode is not prohibited but is not really recommended. The effect of truncating VL may have unintended and unexpected consequences on subsequent instructions. VLi set will be fine: it is when VLi is clear that problems may be faced.
Programmer's note: VLi
is only accessible in normal operations which in
turn limits the CR field bit-testing to only EQ/NE
. cr ops are
not so limited. Thus it is possible to use for example sv.cror/ff=gt/vli
*0,*0,*0
, which is not a nop
because it allows Fail-First Mode to
perform a test and truncate VL.
Hardware implementor's note: effective Sequential Program Order must
be preserved. Speculative Execution is perfectly permitted as long as
the speculative elements are held back from writing to register files
(kept in Resevation Stations), until such time as the relevant CR Field
bit(s) has been analysed. All Speculative elements sequentially beyond
the test-failure point MUST be cancelled. This is no different from
standard Out-of-Order Execution and the modification effort to efficiently
support Data-Dependent Fail-First within a pre-existing Multi-Issue
Out-of-Order Engine is anticipated to be minimal. In-Order systems on
the other hand are expected, unavoidably, to be low-performance unless they
also make use of SVSTATE.hphint
and exploit it to safely implement rudimentary
Shadow-Commit-Hold normally only found in Out-of-Order systems.
Two extremely important aspects of ffirst are:
- LDST ffirst may never set VL equal to zero. This because on the first element an exception must be raised "as normal".
- CR-based data-dependent ffirst on the other hand can set VL equal
to zero. When VL is set
zero due to the first element failing the CR bit-test, all subsequent
vectorized operations are effectively
nops
which is precisely the desired and intended behaviour.
The second crucial aspect, compared to LDST Ffirst:
- LD/ST Failfirst may (beyond the initial first element conditions) truncate VL for any architecturally suitable reason. Beyond the first element LD/ST Failfirst is arbitrarily speculative and 100% non-deterministic.
- CR-based data-dependent first on the other hand MUST NOT truncate VL arbitrarily to a length decided by the hardware: VL MUST only be truncated based explicitly on whether a test fails. This because it is a precise Deterministic test on which algorithms can and will rely.
Floating-point Exceptions
When Floating-point exceptions are enabled VL must be truncated at
the point where the Exception appears not to have occurred. If VLi
is set then VL must include the faulting element, and thus the faulting
element will always raise its exception. If however VLi
is clear then
VL excludes the faulting element and thus the exception will never
be raised.
Although very strongly discouraged the Exception Mode that permits Floating Point Exception notification to arrive too late to unwind is permitted (under protest, due it violating the otherwise 100% Deterministic nature of Data-dependent Fail-first).
Use of lax FP Exception Notification Mode could result in parallel computations proceeding with invalid results that have to be explicitly detected, whereas with the strict FP Execption Mode enabled, FFirst truncates VL, allows subsequent parallel computation to avoid the exceptions entirely
Data-dependent fail-first on CR operations (crand etc)
Operations that actually produce or alter CR Field as a result have their own SVP64 Mode, described in cr ops.
\newpage{}