(no commit message)
[libreriscv.git] / openpower / sv / branches.mdwn
1 # SVP64 Branch Conditional behaviour
2
3 **DRAFT STATUS**
4
5 Please note: SVP64 Branch instructions should be
6 considered completely separate and distinct from
7 standard scalar OpenPOWER-approved v3.0B branches.
8 **v3.0B branches are in no way impacted, altered,
9 changed or modified in any way, shape or form by
10 the SVP64 Vectorised Variants**.
11
12 Links
13
14 * <https://bugs.libre-soc.org/show_bug.cgi?id=664>
15 * <http://lists.libre-soc.org/pipermail/libre-soc-dev/2021-August/003416.html>
16 * [[openpower/isa/branch]]
17
18 Scalar 3.0B Branch Conditional operations, `bc`, `bctar` etc. test a
19 Condition Register. However for parallel processing it is simply impossible
20 to perform multiple independent branches: the Program Counter simply
21 cannot branch to multiple destinations based on multiple conditions.
22 The best that can be done is
23 to test multiple Conditions and make a decision of a *single* branch,
24 based on analysis of a *Vector* of CR Fields
25 which have just been calculated from a *Vector* of results.
26
27 In 3D Shader
28 binaries, which are inherently parallelised and predicated, testing all or
29 some results and branching based on multiple tests is extremely common,
30 and a fundamental part of Shader Compilers. Example:
31 without such multi-condition
32 test-and-branch, if a predicate mask is all zeros a large batch of
33 instructions may be masked out to `nop`, and it would waste
34 CPU cycles not only to run them but also to load the predicate
35 mask repeatedly for each one. 3D GPU ISAs can test for this scenario
36 and jump over the fully-masked-out operations, by spotting that
37 all Conditions are zero.
38 Therefore, in order to be commercially competitive, `sv.bc` and
39 other Vector-aware Branch Conditional instructions are a high priority
40 for 3D GPU workloads.
41
42 The `BI` field of Branch Conditional operations is five bits, in scalar
43 v3.0B this would select one bit of the 32 bit CR. In SVP64 there are
44 16 32 bit CRs, containing 128 4-bit CR Fields. Therefore, the 2 LSBs of
45 `BI` select the bit from the CR Field (EQ LT GT SO), and the top 3 bits
46 are extended to either scalar or vector and to select CR Fields 0..127
47 as specified in SVP64 [[sv/svp64/appendix]].
48
49 When considering an "array" of branch-tests, there are four useful modes:
50 AND, OR, NAND and NOR of all Conditions.
51 NAND and NOR may be synthesised by
52 inverting `BO[2]` which just leaves two modes:
53
54 * Branch takes place on the first CR test to succeed
55 (a Great Big OR of all condition tests)
56 * Branch takes place only if **all** CR tests succeed:
57 a Great Big AND of all condition tests
58 (including those where the predicate is masked out
59 and the corresponding CR Field is considered to be
60 set to `SNZ`)
61
62 When the CR Fields selected by SVP64 Augmented `BI` is marked as scalar,
63 then as usual the loop ends at the first element tested, after taking
64 predication into consideration. Thus, as usual, when `sz` is zero, srcstep
65 skips forward to the first non-zero predicated element, and only that
66 one element is tested.
67
68 In SVP64 Horizontal-First Mode, the first failure in ALL mode (Great Big
69 AND) results in early exit: no more updates to CTR occur (if requested);
70 no branch occurs, and LR is not updated (if requested). Likewise for
71 non-ALL mode (Great Big Or) on first success early exit also occurs,
72 however this time with the Branch proceeding. In both cases the testing
73 of the Vector of CRs should be done in linear sequential order (or in
74 REMAP re-sequenced order): such that tests that are sequentially beyond
75 the exit point are *not* carried out. (*Note: it is standard practice in
76 Programming languages to exit early from conditional tests, however
77 a little unusual to consider in an ISA that is designed for Parallel
78 Vector Processing. The reason is to have strictly-defined guaranteed
79 behaviour*)
80
81 In Vertical-First Mode, setting the `ALL` bit results in `UNDEFINED`
82 behaviour. Given that only one element is being tested at a time
83 in Vertical-First Mode, a test designed to be done on multiple
84 bits is meaningless.
85
86 Predication in both INT and CR modes may be applied to `sv.bc` and other
87 SVP64 Branch Conditional operations, exactly as they may be applied to
88 other SVP64 operations. When `sz` is zero, any masked-out Branch-element
89 operations are not included in condition testing, exactly like all other
90 SVP64 operations. This *includes* side-effects such as decrementing of
91 CTR, which is also skipped on masked-out CR Field elements, when `sz`
92 is zero.
93
94 However when `sz` is non-zero, this normally requests insertion of a zero
95 in place of the input data, when the relevant predicate mask bit is zero.
96 This would mean that a zero is inserted in place of `CR[BI+32]` for
97 testing against `BO`, which may not be desirable in all circumstances.
98 Therefore, an extra field is provided `SNZ`, which, if set, will insert
99 a **one** in place of a masked-out element, instead of a zero.
100
101 (*Note: Both options are provided because it is useful to deliberately
102 cause the Branch-Conditional Vector testing to fail at a specific point,
103 controlled by the Predicate mask. This is particularly useful in `VLSET`
104 mode, which will truncate SVSTATE.VL at the point of the first failed
105 test.*)
106
107 SVP64 RM `MODE` (includes `ELWIDTH` and `ELWIDTH_SRC` bits) for Branch
108 Conditional:
109
110 | 4 | 5 | 6 | 7 | 19 | 20 | 21 | 22 23 | description |
111 | - | - | - | - | -- | -- | --- |---------|----------------- |
112 |ALL|LRu| / | / | 0 | 0 | / | SNZ sz | normal mode |
113 |ALL|LRu| / |VSb| 0 | 1 | VLI | SNZ sz | VLSET mode |
114 |ALL|LRu| / | / | 1 | 0 | / | SNZ sz | CTR mode |
115 |ALL|LRu| / |VSb| 1 | 1 | VLI | SNZ sz | CTR+VLSET mode |
116
117 Fields:
118
119 * **sz** if predication is enabled will put 4 copies of `SNZ` in place of
120 the src CR Field when the predicate bit is zero. otherwise the element
121 is ignored or skipped, depending on context.
122 * **ALL** when set, all branch conditional tests must pass in order for
123 the branch to succeed. When clear, it is the first sequentially
124 encountered successful test that causes the branch to succeed.
125 * **VLI** VLSET is identical to Data-dependent Fail-First mode.
126 In VLSET mode, VL is set equal (truncated) to the first point
127 where, assuming Conditions are tested sequentially, the branch succeeds
128 *or fails* depending if VSb is set.
129 If VLI (Vector Length Inclusive) is clear,
130 VL is truncated to *exclude* the current element, otherwise it is
131 included. SVSTATE.MVL is not changed: only VL.
132 * **LRu**: Link Register Update. When set, Link Register will
133 only be updated if the Branch Condition succeeds. This avoids
134 destruction of LR during loops (particularly Vertical-First
135 ones).
136 * **VSb** is most relevant for Vertical-First VLSET Mode. After testing,
137 if VSb is set, VL is truncated if the branch succeeds. If VSb is clear,
138 VL is truncated if the branch did **not** take place.
139
140 CTR mode will subtract VL from CTR rather than just decrement
141 CTR by one. Just as when v3.0B Branch-Conditional saves at
142 least one instruction on tight inner loops through auto-decrementation
143 of CTR, likewise it is also possible to save instruction count for
144 SVP64 loops in both Vertical-First and Horizontal-First Mode.
145 Setting CTR Mode in Vertical-First results in `UNDEFINED`
146 behaviour. Given that Vertical-First steps through one element
147 at a time, standard single (v3.0B) CTR decrementing should
148 correspondingly be used.
149
150 If CTR+VLSET Modes are requested, the amount that CTR is decremented
151 by is the value of VL *after* truncation (should that occur).
152
153 Note that, interestingly, due to the useful side-effects of `VLSET` mode
154 it is actually useful to use Branch Conditional even
155 to perform no actual branch operation, i.e to point to the instruction
156 after the branch.
157 If VLSET mode was requested with REMAP, VL will have been set to the
158 length of one of the loop endpoints, as specified by the bit from
159 the Branch `BI` field.
160
161 Also, the unconditional bit `BO[0]` is still relevant when Predication
162 is applied to the Branch because in `ALL` mode all nonmasked bits have
163 to be tested. Even when svstep mode or VLSET mode are not used, CTR
164 may still be decremented by the total number of nonmasked elements.
165 In short, Vectorised Branch becomes an extremely powerful tool.
166
167 `VLSET` mode with Vertical-First is particularly unusual. Vertical-First
168 is used for explicit looping, where the looping is to terminate if the end
169 of the Vector, VL, is reached. If however that loop is terminated early
170 because VL is truncated, VLSET with Vertical-First becomes meaningless.
171 Therefore, with `VSb`, the option to decide whether truncation should occur if the
172 branch succeeds *or* if the branch condition fails allows for flexibility
173 required.
174
175 `VLSET` mode with Horizontal-First when `VSb` is clear is still
176 useful, because it can be used to truncate VL to the first predicated
177 (non-masked-out) element.
178
179 Available options to combine:
180
181 * `BO[0]` to make an unconditional branch would seem irrelevant if
182 it were not for predication and for side-effects.
183 * `BO[1]` to select whether the CR bit being tested is zero or nonzero
184 * `R30` and `~R30` and other predicate mask options including CR and
185 inverted CR bit testing
186 * `sz` and `SNZ` to insert either zeros or ones in place of masked-out
187 predicate bits
188 * `ALL` or `ANY` behaviour corresponding to `AND` of all tests and
189 `OR` of all tests, respectively.
190
191 In addition to the above, it is necessary to select whether, in `svstep`
192 mode, the Vector CR Field is to be overwritten or not: in some cases it
193 is useful to know but in others all that is needed is the branch itself.
194
195 Pseudocode for Horizontal-First Mode:
196
197 ```
198 cond_ok = not SVRMmode.ALL
199 for srcstep in range(VL):
200 # select predicate bit or zero/one
201 if predicate[srcstep]:
202 # get SVP64 extended CR field 0..127
203 SVCRf = SVP64EXTRA(BI>>2)
204 CRbits = CR{SVCRf}
205 testbit = CRbits[BI & 0b11]
206 # testbit = CR[BI+32+srcstep*4]
207 else if not SVRMmode.sz:
208 continue
209 else
210 testbit = SVRMmode.SNZ
211 # actual element test here
212 el_cond_ok <- BO[0] | ¬(testbit ^ BO[1])
213 # merge in the test
214 if SVRMmode.ALL:
215 cond_ok &= el_cond_ok
216 else
217 cond_ok |= el_cond_ok
218 # test for VL to be set (and exit)
219 if VLSET and VSb = el_cond_ok then
220 if SVRMmode.VLI
221 SVSTATE.VL = srcstep+1
222 else
223 SVSTATE.VL = srcstep
224 break
225 # early exit?
226 if SVRMmode.ALL:
227 if ~el_cond_ok:
228 break
229 else
230 if el_cond_ok:
231 break
232 if SVCRf.scalar:
233 break
234 ```
235
236 Pseudocode for Vertical-First Mode:
237
238 ```
239 # get SVP64 extended CR field 0..127
240 SVCRf = SVP64EXTRA(BI>>2)
241 CRbits = CR{SVCRf}
242 # select predicate bit or zero/one
243 if predicate[srcstep]:
244 if BRc = 1 then # CR0 vectorised
245 CR{SVCRf+srcstep} = CRbits
246 testbit = CRbits[BI & 0b11]
247 else if not SVRMmode.sz:
248 SVSTATE.srcstep = new_srcstep
249 exit # no branch testing
250 else
251 testbit = SVRMmode.SNZ
252 # actual element test here
253 cond_ok <- BO[0] | ¬(testbit ^ BO[1])
254 # test for VL to be set (and exit)
255 if VLSET and cond_ok = VSb then
256 if SVRMmode.VLI
257 SVSTATE.VL = new_srcstep+1
258 else
259 SVSTATE.VL = new_srcstep
260 ```
261
262 v3.0B branch pseudocode including LRu
263
264 ```
265 if (mode_is_64bit) then M <- 0
266 else M <- 32
267 if ¬BO[2] then CTR <- CTR - 1
268 ctr_ok <- BO[2] | ((CTR[M:63] != 0) ^ BO[3])
269 cond_ok <- BO[0] | ¬(CR[BI+32] ^ BO[1])
270 lr_ok <- SVRMmode.LRu
271 if ctr_ok & cond_ok then
272 if AA then NIA <-iea EXTS(BD || 0b00)
273 else NIA <-iea CIA + EXTS(BD || 0b00)
274 lr_ok <- 0b1
275 if LK & lr_ok then LR <-iea CIA + 4
276 ```
277
278 # Example Shader code
279
280 ```
281 while(a > 2) {
282 if(b < 5)
283 f();
284 else
285 g();
286 h();
287 }
288 ```
289
290 which compiles to something like:
291
292 ```
293 vec<i32> a, b;
294 // ...
295 pred loop_pred = a > 2;
296 while(loop_pred.any()) {
297 pred if_pred = loop_pred & (b < 5);
298 if(if_pred.any()) {
299 f(if_pred);
300 }
301 label1:
302 pred else_pred = loop_pred & ~if_pred;
303 if(else_pred.any()) {
304 g(else_pred);
305 }
306 h(loop_pred);
307 }
308 ```
309
310 which will end up as:
311
312 ```
313 sv.cmpi CR60.v a.v, 2 # vector compare a into CR60 vector
314 sv.crweird r30, CR60.GT # transfer GT vector to r30
315 while_loop:
316 sv.cmpi CR80.v, b.v, 5 # vector compare b into CR64 Vector
317 sv.bc/m=r30/~ALL/sz CR80.v.LT skip_f # skip when none
318 # only calculate loop_pred & pred_b because needed in f()
319 sv.crand CR80.v.SO, CR60.v.GT, CR80.V.LT # if = loop & pred_b
320 f(CR80.v.SO)
321 skip_f:
322 # illustrate inversion of pred_b. invert r30, test ALL
323 # rather than SOME, but masked-out zero test would FAIL,
324 # therefore masked-out instead is tested against 1 not 0
325 sv.bc/m=~r30/ALL/SNZ CR80.v.LT skip_g
326 # else = loop & ~pred_b, need this because used in g()
327 sv.crternari(A&~B) CR80.v.SO, CR60.v.GT, CR80.V.LT
328 g(CR80.v.SO)
329 skip_g:
330 # conditionally call h(r30) if any loop pred set
331 sv.bclr/m=r30/~ALL/sz BO[1]=1 h()
332 sv.bc/m=r30/~ALL/sz BO[1]=1 while_loop
333 ```