(no commit message)
[libreriscv.git] / openpower / sv / setvl.mdwn
index 4c1aa1159c5be852a3477655404e74ed7f0734dd..6bd770b5fe113b81ad5a5f0c6522c418aa200fb6 100644 (file)
@@ -18,7 +18,9 @@ Use of setvl results in changes to the MVL, VL and STATE SPRs. see [[sv/sprs]]
 SV's Vector Engine is based on Cray-style Variable-length Vectorisation,
 just like RVV.  However unlike RVV, SV sits on top of the standard Scalar
 regfiles: there is no separate Vector register numbering.  Therefore, also
-unlike RVV, SV does not have hard-coded "Lanes".  The relevant parameter
+unlike RVV, SV does not have hard-coded "Lanes": microarchitects
+may use *ordinary* in-order, out-of-order, or superscalar designs
+as the basis for SV. By contrast, the relevant parameter
 in RVV is "MAXVL" and this is architecturally hard-coded into RVV systems,
 anywhere from 1 to tens of thousands of Lanes in supercomputers.
 
@@ -40,7 +42,8 @@ a specific cpu it may affect hw unit usage.
 
 Other than being able to set MVL, SV's VL (Vector Length) works just like
 RVV's VL, with one minor twist.  RVV permits the `setvl` instruction to
-set VL to an arbitrary value.  Given that RVV only works on Vector Loops,
+set VL to an arbitrary explicit value.  Within the limit of MVL, VL
+**MUST** be set to the requested value. Given that RVV only works on Vector Loops,
 this is fine and part of its value and design.  However, SV sits on top
 of the standard register files.  When MVL=VL=2, a Vector Add on `r3`
 will perform two Scalar Adds: one on `r3` and one on `r4`.
@@ -62,6 +65,10 @@ the stack.  This *only works if VL is set to the requested value* rather
 than, as in RVV, allowing the hardware to set VL to an arbitrary value
 (caveat being, limited to not exceed MVL)
 
+Also available is the option to set VL from CTR (`VL = MIN(CTR, MVL)`.
+In combination with SVP64 [[sv/branches]] this can save one instruction
+inside critical inner loops.
+
 # Format
 
 *(Allocation of opcode TBD pending OPF ISA WG approval)*,
@@ -70,15 +77,16 @@ using EXT22 temporarily and fitting into the
 
 Form: SVL-Form (see [[isatables/fields.text]])
 
-| 0.5|6.10|11.15|16..21|22| 23...25  | 26.30 |31|  name   |
-| -- | -- | --- | ---- |---------- | ----- |--| ------- |
-|OPCD| RT | RA  | SVi  |/ | vm vs ms | 11110 |Rc| setvl   |
+| 0.5|6.10|11.15|16..21| 22...25    | 26.30 |31|  name   |
+| -- | -- | --- | ---- |----------- | ----- |--| ------- |
+|OPCD| RT | RA  | SVi  |cv ms vs vf | 11110 |Rc| setvl   |
 
 Note that the immediate (`SVi`) spans 7 bits (16 to 22)
 
-`ms` - bit 25 - allows for setting of MVL.  `vs` - bit 24 - allows for
-setting of VL.  `vm` - bit 23 - sets "Vertical Mode" which is
-stored in `MSR` bit 6 (**TODO: needs approval**)
+* `cv` - bit 22 - reads CTR instead of RA 
+* `ms` - bit 23 - allows for setting of MVL.
+* `vs` - bit 24 - allows for setting of VL.
+* `vf` - bit 25 - sets "Vertical First Mode".
 
 Note that in immediate setting mode VL and MVL start from **one**
 i.e. that an immediate value of zero will result in VL/MVL being set to 1.
@@ -86,7 +94,7 @@ i.e. that an immediate value of zero will result in VL/MVL being set to 1.
 VL/MVL to 1 results in "scalar identity" behaviour, where setting VL/MVL
 to 0 would result in all Vector operations becoming `nop`.  If this is
 truly desired (nop behaviour) then setting VL and MVL to zero is to be
-done via the [[SV SPRs|sv/sprs]]
+done via the [[SVSTATE SPR|sv/sprs]]
 
 Note that setmvli is a pseudo-op, based on RA/RT=0, and setvli likewise
 
@@ -106,6 +114,49 @@ Note that whilst it is possible to set both MVL and VL from the same
 immediate, it is not possible to set them to different immediates in
 the same instruction.  That would require two instructions.
 
+# setmvlhi
+
+In Vertical-First Mode the minimum expectation is that one scalar
+element may be executed by each instruction. There are however
+circumstances where it may be possible to execute more than one
+element per instruction (srcstep elements 0-3 for example)
+but leaving it up to hardware to
+determine a "safe minimum amount" where memory corruption does
+not occur may not be practical (or is simply very costly).
+
+Therefore, setmvlhi may specify, as determined by the compiler,
+exactly what that quantity is.  Unlike VL, which is an amount
+that, when requested, **must** be executed, VFhint may be set
+by the hardware to an amount that the hardware is capable of.
+In other words: setmvlhi requests a hint size, bur hardware chooses
+the actual hint.
+
+The reason for this cooperative negotiation between hardware and
+software is that whilst the compiler may have information about
+memory hazards that must be avoided which hardware cannot
+know about, the hardware knows the maximum batch size
+it can execute in parallel but the compiler is unaware of
+the variance in that batch size on different implementations.
+Thus, hardware sets VLHint to the minimum of the requested
+amount and the hardware limit. Simple implementations always
+set VLHint to 1.
+
+Critical to note are two things:
+
+1. VFhint must not be set by hardware to an amount that
+exceeds either MVL or the requested amount, and must set
+VFhint to at least 1 element.
+2. svstep will increment srcstep and dststep by VFhint,
+therefore when hardware says it can perform N element
+operations, hardware **MUST** perform N operations
+for every single instruction. 
+
+Form: SVL-Form (see [[isatables/fields.text]])
+
+| 0.5|6.10|11.15|16..21|22 | 23...25  | 26.30 |31|  name    |
+| -- | -- | --- | ---- |---| -------- | ----- |--| -------- |
+|OPCD| RT | MVL | SVi  |MVL| ms vs vf | 10110 |Rc| setmvlhi  |
+
 # Vertical First Mode
 
 Vertical First is effectively like an implicit single bit predicate
@@ -115,9 +166,9 @@ increment, and the Program Counter progresses **immediately* to
 the next instruction just as it would for any standard scalar v3.0B
 instruction.
 
-An explicit instruction is called which can move srcstep and
+An explicit mode of setvl is called which can move srcstep and
 dststep on to the next element, still respecting predicate
-masks.
+masks.  
 
 In other words, where normal SVP64 Vectorisation acts "horizontally"
 by looping first through 0 to VL-1 and only then moving the PC
@@ -128,11 +179,32 @@ advance srcstep/dststep, and an outer loop is expected to be
 used (branch instruction) which completes a series of
 Vector operations.
 
+```svstep``` mode is enabled when vf=1, vs=0 and ms=0. 
+When Rc=1 it is possible to determine when any level of
+loops reach an end condition, or if VL has been reached. The immediate can
+be reinterpreted as indicating which SVSTATE (0-3)
+should be tested and placed into CR0.
+
+* setvl immediate = 1: only VL testing is enabled. CR0.SO is set
+  to 1 when either srcstep or dststep reach VL
+* setvl immediate = 2: also include inner middle and outer
+  loop end conditions from SVSTATE0 into CR.EQ CR.LE CR.GT
+* setvl immediate = 3: test SVSTATE1
+* setvl immediate = 4: test SVSTATE2
+* setvl immediate = 5: test SVSTATE3
+
+Testing any end condition of any loop of any REMAP state allows branches to be used to create loops.
+
+*Programmers should be aware that VL, srcstep and dststep are global in nature.
+Nested looping with different schedules is perfectly possible, as is
+calling of functions, however SVSTATE (and any associated SVSTATE) should be stored on the stack.*
+
 # Pseudocode
 
     // instruction fields:
     rd = get_rt_field();         // bits 6..10
     ra = get_ra_field();         // bits 11..15
+    vc = get_vc_field();         // bit 22
     vf = get_vf_field();         // bit 23
     vs = get_vs_field();         // bit 24
     ms = get_ms_field();         // bit 25
@@ -167,10 +239,12 @@ Vector operations.
         vlimmed = get_immed_field()+1; //  16..22
 
         // set VL (or not).
-        // 3 options: from SPR, from immed, from ra
+        // 4 options: from SPR, from immed, from ra, from CTR
         if vs {
            // VL to be sourced from fields/regs
-           if ra != 0 {
+           if vc {
+               VL = CTR
+           } else if ra != 0 {
                VL = GPR[ra]
            } else {
                VL = vlimmed
@@ -194,8 +268,8 @@ Vector operations.
         VL = min(VL, MVL)
 
         // store VL, MVL
-        SPR[SV_VL] = VL
-        SPR[SV_MVL] = MVL
+        SVSTATE.VL = VL
+        SVSTATE.MVL = MVL
 
         // write rd
         if rt != 0 {
@@ -209,15 +283,16 @@ Vector operations.
             ...
             ...
         }
-        // write Vertical-First mode into MSR
-        MSR[6] = vf
+        // write Vertical-First mode
+        SVSTATE.vf = vf
     }
 
 # Examples
 
 ## Core concept loop
 
-    loop:
+```
+loop:
     setvl a3, a0, MVL=8    #  update a3 with vl
                            # (# of elements this iteration)
                            # set MVL to 8
@@ -225,6 +300,7 @@ Vector operations.
     # ...
     sub a0, a0, a3   # Decrement count by vl
     bnez a0, loop    # Any more?
+```
 
 ## Loop using Rc=1
 
@@ -239,3 +315,56 @@ Vector operations.
       bne cr0, loop
     end:
       blr
+
+## setmvlhi double loop
+
+Two elements per inner loop are executed per instruction. This assumes
+that underlying hardware, when `setmvlhi` requests a parallelism hint of 2
+actually sets a parallelism hint of 2.
+
+This example, in c, would be:
+
+```
+long *r4;
+for (i=0; i < CTR; i++)
+{
+    r4[i+2] += r4[i]
+}
+```
+
+where, clearly, it is not possible to do more
+than 2 elements in parallel at a time: attempting
+to do so would result in data corruption. The compiler
+may be able to determine memory aliases and inform
+hardware at runtime of the maximum safe parallelism
+limit.
+
+Whilst this example could be simplified to simply set VL=2,
+or exploit the fact that overlapping adds have well-defined
+behaviour, this has not been done, here, for illustrative purposes
+in order to demonstrate setmvhli and Vertical-First Mode.
+
+Note, crucially, how r4, r32 and r20 are **NOT** incremented
+inside the inner loop.  The MAXVL reservation is still 8,
+i.e. as srcstep and dststep advance (by 2 elements at a time)
+registers r20-r27 will be used for the first LD, and
+registers r32-39 for the second LD.  `r4+srcstep*8` will be used
+as the elstrided offset for LDs.
+
+```
+   setmvlhi  8, 2 # MVL=8, VFHint=2
+loop:
+    setvl  r1, CTR, vf=1 # VL=r1=MAX(MVL, CTR), VF=1
+    mulli  r1, r1, 8     # multiply by int width
+loopinner:
+    sv.ld r20.v, r4(0) # load VLhint elements (max 2)
+    addi r2, r4, 16    # 2 elements ahead
+    sv.ld r32.v, r2(0) # load VLhint elements (max 2)
+    sv.add r32.v, r20.v, r32.v # x[i+2] += x[i]
+    sv.st r32.v, r2(0) # store VLhint elements
+    svstep.            # srcstep += VLhint
+    bnz loopinner      # repeat until srcstep=VL
+    # now done VL elements, move to next batch
+    add r4, r4, r1     # move r4 pointer forward
+    sv.bnz/ctr loop    # decrement CTR by VL
+```