1//
2// unop vA, vB
3// Format 12x: B|A|op
4// (see floating_point.S for float/double ops)
5//
6
7// neg-int vA, vB
8// Format 12x: B|A|7b
9%def op_neg_int():
10%  generic_unop(instr="negw t1, t1")
11
12// not-int vA, vB
13// Format 12x: B|A|7c
14%def op_not_int():
15%  generic_unop(instr="not t1, t1")
16
17// neg-long vA, vB
18// Format 12x: B|A|7d
19%def op_neg_long():
20%  generic_unop(instr="neg t1, t1", is_wide=True)
21
22// not-long vA, vB
23// Format 12x: B|A|7e
24%def op_not_long():
25%  generic_unop(instr="not t1, t1", is_wide=True)
26
27// int-to-long vA, vB
28// Format 12x: B|A|81
29// Note: Sign extension of int32 into int64.
30// Read from 32-bit vreg and write to 64-bit vreg, hence a custom impl.
31%def op_int_to_long():
32   srliw t1, xINST, 12   // t1 := B
33   srliw t2, xINST, 8    // t2 := B|A
34%  get_vreg("t1", "t1")  #  t1 := fp[B] with sign extension to 64 bits
35   FETCH_ADVANCE_INST 1  // advance xPC, load xINST
36   and t2, t2, 0xF       // t2 := A
37   GET_INST_OPCODE t3    // t3 holds next opcode
38   SET_VREG_WIDE t1, t2, z0=t0
39                         // fp[A:A+1] := t1
40   GOTO_OPCODE t3        // continue to next
41
42// long-to-int vA, vB
43// Format 12x: B|A|84
44// Note: Truncation of int64 into int32.
45// Implemented as a read from the low bits from vB, write them to vA.
46// Note: instr is intentionally empty.
47%def op_long_to_int():
48%  generic_unop(instr="")
49
50// int-to-byte vA, vB
51// Format 12x: B|A|8d
52// Note: Truncation of int32 to int8, sign extending the result.
53%def op_int_to_byte():
54%  generic_unop(instr="sext.b t1, t1")
55
56// int-to-byte vA, vB
57// Format 12x: B|A|8e
58// Note: Truncation of int32 to uint16, without sign extension.
59%def op_int_to_char():
60%  generic_unop(instr="zext.h t1, t1")
61
62// int-to-byte vA, vB
63// Format 12x: B|A|8f
64// Note: Truncation of int32 to int16, sign extending the result.
65%def op_int_to_short():
66%  generic_unop(instr="sext.h t1, t1")
67
68// unop boilerplate
69// instr: operand held in t1, result written to t1.
70// instr must not clobber t2.
71// Clobbers: t0, t1, t2
72%def generic_unop(instr, is_wide=False):
73   srliw t1, xINST, 12   // t1 := B
74   srliw t2, xINST, 8    // t2 := B|A
75%  get_vreg("t1", "t1", is_wide=is_wide)
76                         // t1 := fp[B]
77   and t2, t2, 0xF       // t2 := A
78   FETCH_ADVANCE_INST 1  // advance xPC, load xINST
79   $instr                // read t1, write result to t1.
80                         // do not clobber t2!
81%  set_vreg("t1", "t2", z0="t0", is_wide=is_wide)
82                         // fp[A] := t1
83   GET_INST_OPCODE t0    // t0 holds next opcode
84   GOTO_OPCODE t0        // continue to next
85
86//
87// binop vAA, vBB, vCC
88// Format 23x: AA|op CC|BB
89// (see floating_point.S for float/double ops)
90//
91
92// add-int vAA, vBB, vCC
93// Format 23x: AA|90 CC|BB
94%def op_add_int():
95%  generic_binop(instr="addw t1, t1, t2")
96
97// sub-int vAA, vBB, vCC
98// Format 23x: AA|91 CC|BB
99%def op_sub_int():
100%  generic_binop(instr="subw t1, t1, t2")
101
102// mul-int vAA, vBB, vCC
103// Format 23x: AA|92 CC|BB
104%def op_mul_int():
105%  generic_binop(instr="mulw t1, t1, t2")
106
107// div-int vAA, vBB, vCC
108// Format 23x: AA|93 CC|BB
109// Note: Twos-complement division, rounded towards zero (that is, truncated to integer). This throws
110// ArithmeticException if b == 0.
111%def op_div_int():
112%  generic_binop(instr="divw t1, t1, t2", divz_throw=True)
113
114// rem-int vAA, vBB, vCC
115// Format 23x: AA|94 CC|BB
116// Note: Twos-complement remainder after division. The sign of the result is the same as that of a,
117// and it is more precisely defined as result == a - (a / b) * b. This throws ArithmeticException if
118// b == 0.
119%def op_rem_int():
120%  generic_binop(instr="remw t1, t1, t2", divz_throw=True)
121
122// and-int vAA, vBB, vCC
123// Format 23x: AA|95 CC|BB
124%def op_and_int():
125%  generic_binop(instr="and t1, t1, t2")
126
127// or-int vAA, vBB, vCC
128// Format 23x: AA|96 CC|BB
129%def op_or_int():
130%  generic_binop(instr="or t1, t1, t2")
131
132// xor-int vAA, vBB, vCC
133// Format 23x: AA|97 CC|BB
134%def op_xor_int():
135%  generic_binop(instr="xor t1, t1, t2")
136
137// shl-int vAA, vBB, vCC
138// Format 23x: AA|98 CC|BB
139// Note: SLLW uses t2[4:0] for the shift amount.
140%def op_shl_int():
141%  generic_binop(instr="sllw t1, t1, t2")
142
143// shr-int vAA, vBB, vCC
144// Format 23x: AA|99 CC|BB
145// Note: SRAW uses t2[4:0] for the shift amount.
146%def op_shr_int():
147%  generic_binop(instr="sraw t1, t1, t2")
148
149// ushr-int vAA, vBB, vCC
150// Format 23x: AA|9a CC|BB
151// Note: SRLW uses t2[4:0] for the shift amount.
152%def op_ushr_int():
153%  generic_binop(instr="srlw t1, t1, t2")
154
155// add-long vAA, vBB, vCC
156// Format 23x: AA|9b CC|BB
157%def op_add_long():
158%  generic_binop(instr="add t1, t1, t2", is_wide=True)
159
160// sub-long vAA, vBB, vCC
161// Format 23x: AA|9c CC|BB
162%def op_sub_long():
163%  generic_binop(instr="sub t1, t1, t2", is_wide=True)
164
165// mul-long vAA, vBB, vCC
166// Format 23x: AA|9d CC|BB
167%def op_mul_long():
168%  generic_binop(instr="mul t1, t1, t2", is_wide=True)
169
170// div-long vAA, vBB, vCC
171// Format 23x: AA|9e CC|BB
172// Note: Twos-complement division, rounded towards zero (that is, truncated to integer). This throws
173// ArithmeticException if b == 0.
174%def op_div_long():
175%  generic_binop(instr="div t1, t1, t2", divz_throw=True, is_wide=True)
176
177// rem-long vAA, vBB, vCC
178// Format 23x: AA|9f CC|BB
179// Note: Twos-complement remainder after division. The sign of the result is the same as that of a,
180// and it is more precisely defined as result == a - (a / b) * b. This throws ArithmeticException if
181// b == 0.
182%def op_rem_long():
183%  generic_binop(instr="rem t1, t1, t2", divz_throw=True, is_wide=True)
184
185// and-long vAA, vBB, vCC
186// Format 23x: AA|a0 CC|BB
187%def op_and_long():
188%  generic_binop(instr="and t1, t1, t2", is_wide=True)
189
190// or-long vAA, vBB, vCC
191// Format 23x: AA|a1 CC|BB
192%def op_or_long():
193%  generic_binop(instr="or t1, t1, t2", is_wide=True)
194
195// xor-long vAA, vBB, vCC
196// Format 23x: AA|a2 CC|BB
197%def op_xor_long():
198%  generic_binop(instr="xor t1, t1, t2", is_wide=True)
199
200// shl-long vAA, vBB, vCC
201// Format 23x: AA|a3 CC|BB
202// Note: SLL uses t2[5:0] for the shift amount.
203%def op_shl_long():
204%  generic_shift_wide(instr="sll t1, t1, t2")
205
206// shr-long vAA, vBB, vCC
207// Format 23x: AA|a4 CC|BB
208// Note: SRA uses t2[5:0] for the shift amount.
209%def op_shr_long():
210%  generic_shift_wide(instr="sra t1, t1, t2")
211
212// ushr-long vAA, vBB, vCC
213// Format 23x: AA|a5 CC|BB
214// Note: SRL uses t2[5:0] for the shift amount.
215%def op_ushr_long():
216%  generic_shift_wide(instr="srl t1, t1, t2")
217
218// binop boilerplate
219// instr: operands held in t1 and t2, result written to t1.
220// instr must not throw. Exceptions to be thrown prior to instr.
221// instr must not clobber t3.
222//
223// The divz_throw flag generates check-and-throw code for div/0.
224// The is_wide flag ensures vregs are read and written in 64-bit widths.
225// Clobbers: t0, t1, t2, t3
226%def generic_binop(instr, divz_throw=False, is_wide=False):
227   FETCH t1, count=1     // t1 := CC|BB
228   srliw t3, xINST, 8    // t3 := AA
229   srliw t2, t1, 8       // t2 := CC
230   and t1, t1, 0xFF      // t1 := BB
231%  get_vreg("t2", "t2", is_wide=is_wide)  # t2 := fp[CC]
232%  get_vreg("t1", "t1", is_wide=is_wide)  # t1 := fp[BB]
233%  if divz_throw:
234     beqz t2, 1f         // Must throw before FETCH_ADVANCE_INST.
235%#:
236   FETCH_ADVANCE_INST 2  // advance xPC, load xINST
237   $instr                // read t1 and t2, write result to t1.
238                         // do not clobber t3!
239   GET_INST_OPCODE t2    // t2 holds next opcode
240%  set_vreg("t1", "t3", z0="t0", is_wide=is_wide)
241                         // fp[AA] := t1
242   GOTO_OPCODE t2        // continue to next
2431:
244%  if divz_throw:
245     tail common_errDivideByZero
246%#:
247
248// binop wide shift boilerplate
249// instr: operands held in t1 (64-bit) and t2 (32-bit), result written to t1.
250// instr must not clobber t3.
251// Clobbers: t0, t1, t2, t3
252//
253// Note: Contrary to other -long mathematical operations (which take register pairs for both their
254// first and their second source), shl-long, shr-long, and ushr-long take a register pair for their
255// first source (the value to be shifted), but a single register for their second source (the
256// shifting distance).
257%def generic_shift_wide(instr):
258   FETCH t1, count=1     // t1 := CC|BB
259   srliw t3, xINST, 8    // t3 := AA
260   srliw t2, t1, 8       // t2 := CC
261   and t1, t1, 0xFF      // t1 := BB
262%  get_vreg("t2", "t2")  #  t2 := fp[CC]
263   GET_VREG_WIDE t1, t1  // t1 := fp[BB]
264   FETCH_ADVANCE_INST 2  // advance xPC, load xINST
265   $instr                // read t1 and t2, write result to t1.
266                         // do not clobber t3!
267   GET_INST_OPCODE t2    // t2 holds next opcode
268   SET_VREG_WIDE t1, t3, z0=t0
269                         // fp[AA] := t1
270   GOTO_OPCODE t2        // continue to next
271
272//
273// binop/2addr vA, vB
274// Format 12x: B|A|op
275// (see floating_point.S for float/double ops)
276//
277
278// add-int/2addr vA, vB
279// Format 12x: B|A|b0
280%def op_add_int_2addr():
281%  generic_binop_2addr(instr="addw t1, t1, t2")
282
283// sub-int/2addr vA, vB
284// Format 12x: B|A|b1
285%def op_sub_int_2addr():
286%  generic_binop_2addr(instr="subw t1, t1, t2")
287
288// mul-int/2addr vA, vB
289// Format 12x: B|A|b2
290%def op_mul_int_2addr():
291%  generic_binop_2addr(instr="mulw t1, t1, t2")
292
293// div-int/2addr vA, vB
294// Format 12x: B|A|b3
295// Note: Twos-complement division, rounded towards zero (that is, truncated to integer). This throws
296// ArithmeticException if b == 0.
297%def op_div_int_2addr():
298%  generic_binop_2addr(instr="divw t1, t1, t2", divz_throw=True)
299
300// rem-int/2addr vA, vB
301// Format 12x: B|A|b4
302// Note: Twos-complement remainder after division. The sign of the result is the same as that of a,
303// and it is more precisely defined as result == a - (a / b) * b. This throws ArithmeticException if
304// b == 0.
305%def op_rem_int_2addr():
306%  generic_binop_2addr(instr="remw t1, t1, t2", divz_throw=True)
307
308// and-int/2addr vA, vB
309// Format 12x: B|A|b5
310%def op_and_int_2addr():
311%  generic_binop_2addr(instr="and t1, t1, t2")
312
313// or-int/2addr vA, vB
314// Format 12x: B|A|b6
315%def op_or_int_2addr():
316%  generic_binop_2addr(instr="or t1, t1, t2")
317
318// xor-int/2addr vA, vB
319// Format 12x: B|A|b7
320%def op_xor_int_2addr():
321%  generic_binop_2addr(instr="xor t1, t1, t2")
322
323// shl-int/2addr vA, vB
324// Format 12x: B|A|b8
325%def op_shl_int_2addr():
326%  generic_binop_2addr(instr="sllw t1, t1, t2")
327
328// shr-int/2addr vA, vB
329// Format 12x: B|A|b9
330%def op_shr_int_2addr():
331%  generic_binop_2addr(instr="sraw t1, t1, t2")
332
333// ushr-int/2addr vA, vB
334// Format 12x: B|A|ba
335%def op_ushr_int_2addr():
336%  generic_binop_2addr(instr="srlw t1, t1, t2")
337
338// add-long/2addr vA, vB
339// Format 12x: B|A|bb
340%def op_add_long_2addr():
341%  generic_binop_2addr(instr="add t1, t1, t2", is_wide=True)
342
343// sub-long/2addr vA, vB
344// Format 12x: B|A|bc
345%def op_sub_long_2addr():
346%  generic_binop_2addr(instr="sub t1, t1, t2", is_wide=True)
347
348// mul-long/2addr vA, vB
349// Format 12x: B|A|bd
350%def op_mul_long_2addr():
351%  generic_binop_2addr(instr="mul t1, t1, t2", is_wide=True)
352
353// div-long/2addr vA, vB
354// Format 12x: B|A|be
355%def op_div_long_2addr():
356%  generic_binop_2addr(instr="div t1, t1, t2", divz_throw=True, is_wide=True)
357
358// rem-long/2addr vA, vB
359// Format 12x: B|A|bf
360%def op_rem_long_2addr():
361%  generic_binop_2addr(instr="rem t1, t1, t2", divz_throw=True, is_wide=True)
362
363// and-long/2addr vA, vB
364// Format 12x: B|A|c0
365%def op_and_long_2addr():
366%  generic_binop_2addr(instr="and t1, t1, t2", is_wide=True)
367
368// or-long/2addr vA, vB
369// Format 12x: B|A|c1
370%def op_or_long_2addr():
371%  generic_binop_2addr(instr="or t1, t1, t2", is_wide=True)
372
373// xor-long/2addr vA, vB
374// Format 12x: B|A|c2
375%def op_xor_long_2addr():
376%  generic_binop_2addr(instr="xor t1, t1, t2", is_wide=True)
377
378// shl-long/2addr vA, vB
379// Format 12x: B|A|c3
380// Note: SLL uses t2[5:0] for the shift amount.
381%def op_shl_long_2addr():
382%  generic_shift_wide_2addr(instr="sll t1, t1, t2")
383
384// shr-long/2addr vA, vB
385// Format 12x: B|A|c4
386// Note: SRA uses t2[5:0] for the shift amount.
387%def op_shr_long_2addr():
388%  generic_shift_wide_2addr(instr="sra t1, t1, t2")
389
390// ushr-long/2addr vA, vB
391// Format 12x: B|A|c5
392// Note: SRL uses t2[5:0] for the shift amount.
393%def op_ushr_long_2addr():
394%  generic_shift_wide_2addr(instr="srl t1, t1, t2")
395
396// binop 2addr boilerplate
397// instr: operands held in t1 and t2, result written to t1.
398// instr must not throw. Exceptions to be thrown prior to instr.
399// instr must not clobber t3.
400//
401// The divz_throw flag generates check-and-throw code for div/0.
402// The is_wide flag ensures vregs are read and written in 64-bit widths.
403// Clobbers: t0, t1, t2, t3, t4
404%def generic_binop_2addr(instr, divz_throw=False, is_wide=False):
405   srliw t2, xINST, 12   // t2 := B
406   srliw t3, xINST, 8    // t3 := B|A
407%  get_vreg("t2", "t2", is_wide=is_wide)
408                         // t2 := fp[B]
409   and t3, t3, 0xF       // t3 := A (cached for SET_VREG)
410   mv t4, t3             // t4 := A
411%  get_vreg("t1", "t4", is_wide=is_wide)
412                         // t1 := fp[A]
413%  if divz_throw:
414     beqz t2, 1f         // Must throw before FETCH_ADVANCE_INST.
415%#:
416   FETCH_ADVANCE_INST 1  // advance xPC, load xINST
417   $instr                // read t1 and t2, write result to t1.
418                         // do not clobber t3!
419   GET_INST_OPCODE t2    // t2 holds next opcode
420%  set_vreg("t1", "t3", z0="t0", is_wide=is_wide)
421                         // fp[A] := t1
422   GOTO_OPCODE t2        // continue to next
4231:
424%  if divz_throw:
425     tail common_errDivideByZero
426%#:
427
428// binop wide shift 2addr boilerplate
429// instr: operands held in t1 (64-bit) and t2 (32-bit), result written to t1.
430// instr must not clobber t3.
431// Clobbers: t0, t1, t2, t3, t4
432//
433// Note: Contrary to other -long/2addr mathematical operations (which take register pairs for both
434// their destination/first source and their second source), shl-long/2addr, shr-long/2addr, and
435// ushr-long/2addr take a register pair for their destination/first source (the value to be
436// shifted), but a single register for their second source (the shifting distance).
437%def generic_shift_wide_2addr(instr):
438   srliw t2, xINST, 12   // t2 := B
439   srliw t3, xINST, 8    // t3 := B|A
440%  get_vreg("t2", "t2")  #  t2 := fp[B]
441   and t3, t3, 0xF       // t3 := A (cached for SET_VREG_WIDE)
442   mv t4, t3             // t4 := A
443   FETCH_ADVANCE_INST 1  // advance xPC, load xINST
444   GET_VREG_WIDE t1, t4  // t1 := fp[A]
445   $instr                // read t1 and t2, write result to t1.
446                         // do not clobber t3!
447   GET_INST_OPCODE t2    // t2 holds next opcode
448   SET_VREG_WIDE t1, t3, z0=t0
449                         // fp[A] := t1
450   GOTO_OPCODE t2        // continue to next
451
452//
453// binop/lit16 vA, vB, #+CCCC
454// Format 22s: B|A|op CCCC
455//
456
457// add-int/lit16 vA, vB, #+CCCC
458// Format 22s: B|A|d0 CCCC
459%def op_add_int_lit16():
460%  generic_binop_lit16(instr="addw t1, t1, t2")
461
462// rsub-int vA, vB, #+CCCC
463// Format 22s: B|A|d1 CCCC
464// Note: rsub-int does not have a suffix since this version is the main opcode of its family.
465// Note: Twos-complement reverse subtraction.
466%def op_rsub_int():
467%  generic_binop_lit16(instr="subw t1, t2, t1")
468
469// mul-int/lit16 vA, vB, #+CCCC
470// Format 22s: B|A|d2 CCCC
471%def op_mul_int_lit16():
472%  generic_binop_lit16(instr="mulw t1, t1, t2")
473
474// div-int/lit16 vA, vB, #+CCCC
475// Format 22s: B|A|d3 CCCC
476// Note: Twos-complement division, rounded towards zero (that is, truncated to integer). This throws
477// ArithmeticException if b == 0.
478%def op_div_int_lit16():
479%  generic_binop_lit16(instr="divw t1, t1, t2", divz_throw=True)
480
481// rem-int/lit16 vA, vB, #+CCCC
482// Format 22s: B|A|d4 CCCC
483// Note: Twos-complement remainder after division. The sign of the result is the same as that of a,
484// and it is more precisely defined as result == a - (a / b) * b. This throws ArithmeticException if
485// b == 0.
486%def op_rem_int_lit16():
487%  generic_binop_lit16(instr="remw t1, t1, t2", divz_throw=True)
488
489// and-int/lit16 vA, vB, #+CCCC
490// Format 22s: B|A|d5 CCCC
491%def op_and_int_lit16():
492%  generic_binop_lit16(instr="and t1, t1, t2")
493
494// or-int/lit16 vA, vB, #+CCCC
495// Format 22s: B|A|d6 CCCC
496%def op_or_int_lit16():
497%  generic_binop_lit16(instr="or t1, t1, t2")
498
499// xor-int/lit16 vA, vB, #+CCCC
500// Format 22s: B|A|d7 CCCC
501%def op_xor_int_lit16():
502%  generic_binop_lit16(instr="xor t1, t1, t2")
503
504// binop lit16 boilerplate
505// instr: operands held in t1 and t2, result written to t1.
506// instr must not throw. Exceptions to be thrown prior to instr.
507// instr must not clobber t3.
508//
509// The divz_throw flag generates check-and-throw code for div/0.
510// Clobbers: t0, t1, t2, t3
511%def generic_binop_lit16(instr, divz_throw=False):
512   FETCH t2, count=1, signed=1  // t2 := ssssCCCC
513   srliw t1, xINST, 12          // t1 := B
514   srliw t3, xINST, 8           // t3 := B|A
515%  if divz_throw:
516     beqz t2, 1f                // Must throw before FETCH_ADVANCE_INST.
517%#:
518%  get_vreg("t1", "t1")         #  t1 := fp[B]
519   and t3, t3, 0xF              // t3 := A
520   FETCH_ADVANCE_INST 2         // advance xPC, load xINST
521   $instr                       // read t1 and t2, write result to t1.
522                                // do not clobber t3!
523   GET_INST_OPCODE t2           // t2 holds next opcode
524%  set_vreg("t1", "t3", z0="t0")  # fp[A] := t1
525   GOTO_OPCODE t2               // continue to next
5261:
527%  if divz_throw:
528     tail common_errDivideByZero
529%#:
530
531//
532// binop/lit8 vAA, vBB, #+CC
533// Format 22b: AA|op CC|BB
534//
535
536// add-int/lit8, vAA, vBB, #+CC
537// Format 22b: AA|d8, CC|BB
538%def op_add_int_lit8():
539%  generic_binop_lit8(instr="addw t1, t1, t2")
540
541// rsub-int/lit8, vAA, vBB, #+CC
542// Format 22b: AA|d9, CC|BB
543// Note: Twos-complement reverse subtraction.
544%def op_rsub_int_lit8():
545%  generic_binop_lit8(instr="subw t1, t2, t1")
546
547// mul-int/lit8, vAA, vBB, #+CC
548// Format 22b: AA|da, CC|BB
549%def op_mul_int_lit8():
550%  generic_binop_lit8(instr="mulw t1, t1, t2")
551
552// div-int/lit8, vAA, vBB, #+CC
553// Format 22b: AA|db, CC|BB
554// Note: Twos-complement division, rounded towards zero (that is, truncated to integer). This throws
555// ArithmeticException if b == 0.
556%def op_div_int_lit8():
557%  generic_binop_lit8(instr="divw t1, t1, t2", divz_throw=True)
558
559// rem-int/lit8, vAA, vBB, #+CC
560// Format 22b: AA|dc, CC|BB
561// Note: Twos-complement remainder after division. The sign of the result is the same as that of a,
562// and it is more precisely defined as result == a - (a / b) * b. This throws ArithmeticException if
563// b == 0.
564%def op_rem_int_lit8():
565%  generic_binop_lit8(instr="remw t1, t1, t2", divz_throw=True)
566
567// and-int/lit8, vAA, vBB, #+CC
568// Format 22b: AA|dd, CC|BB
569%def op_and_int_lit8():
570%  generic_binop_lit8(instr="and t1, t1, t2")
571
572// or-int/lit8, vAA, vBB, #+CC
573// Format 22b: AA|de, CC|BB
574%def op_or_int_lit8():
575%  generic_binop_lit8(instr="or t1, t1, t2")
576
577// xor-int/lit8, vAA, vBB, #+CC
578// Format 22b: AA|df, CC|BB
579%def op_xor_int_lit8():
580%  generic_binop_lit8(instr="xor t1, t1, t2")
581
582// shl-int/lit8, vAA, vBB, #+CC
583// Format 22b: AA|e0, CC|BB
584// Note: SLLW uses t2[4:0] for the shift amount.
585%def op_shl_int_lit8():
586%  generic_binop_lit8(instr="sllw t1, t1, t2")
587
588// shr-int/lit8, vAA, vBB, #+CC
589// Format 22b: AA|e1, CC|BB
590// Note: SRAW uses t2[4:0] for the shift amount.
591%def op_shr_int_lit8():
592%  generic_binop_lit8(instr="sraw t1, t1, t2")
593
594// ushr-int/lit8, vAA, vBB, #+CC
595// Format 22b: AA|e2, CC|BB
596// Note: SRLW uses t2[4:0] for the shift amount.
597%def op_ushr_int_lit8():
598%  generic_binop_lit8(instr="srlw t1, t1, t2")
599
600// binop lit8 boilerplate
601// instr: operands held in t1 and t2, result written to t1.
602// instr must not throw. Exceptions to be thrown prior to instr.
603// instr must not clobber t3.
604//
605// The divz_throw flag generates check-and-throw code for div/0.
606// Clobbers: t0, t1, t2, t3
607%def generic_binop_lit8(instr, divz_throw=False):
608   FETCH t1, count=1, signed=1  // t1 := ssssCC|BB
609   srliw t3, xINST, 8           // t3 := AA
610   sraiw t2, t1, 8              // t2 := ssssssCC
611   andi t1, t1, 0xFF            // t1 := BB
612%  if divz_throw:
613     beqz t2, 1f                // Must throw before FETCH_ADVANCE_INST.
614%#:
615%  get_vreg("t1", "t1")         #  t1 := fp[BB]
616   FETCH_ADVANCE_INST 2         // advance xPC, load xINST
617   $instr                       // read t1 and t2, write result to t1.
618                                // do not clobber t3!
619   GET_INST_OPCODE t2           // t2 holds next opcode
620%  set_vreg("t1", "t3", z0="t0")  # fp[AA] := t1
621   GOTO_OPCODE t2               // continue to next
6221:
623%  if divz_throw:
624     tail common_errDivideByZero
625%#:
626