xref: /aosp_15_r20/art/runtime/interpreter/mterp/arm64ng/arithmetic.S (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
1%def binop(preinstr="", result="w0", chkzero="0", instr=""):
2    /*
3     * Generic 32-bit binary operation.  Provide an "instr" line that
4     * specifies an instruction that performs "result = w0 op w1".
5     * This could be an ARM instruction or a function call.  (If the result
6     * comes back in a register other than w0, you can override "result".)
7     *
8     * If "chkzero" is set to 1, we perform a divide-by-zero check on
9     * vCC (w1).  Useful for integer division and modulus.  Note that we
10     * *don't* check for (INT_MIN / -1) here, because the ARM math lib
11     * handles it correctly.
12     *
13     * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
14     *      xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
15     *      mul-float, div-float, rem-float
16     */
17    /* binop vAA, vBB, vCC */
18    FETCH w0, 1                         // w0<- CCBB
19    lsr     w9, wINST, #8               // w9<- AA
20    lsr     w3, w0, #8                  // w3<- CC
21    and     w2, w0, #255                // w2<- BB
22    GET_VREG w1, w3                     // w1<- vCC
23    GET_VREG w0, w2                     // w0<- vBB
24    .if $chkzero
25    cbz     w1, common_errDivideByZero  // is second operand zero?
26    .endif
27    FETCH_ADVANCE_INST 2                // advance rPC, load rINST
28    $preinstr                           // optional op; may set condition codes
29    $instr                              // $result<- op, w0-w3 changed
30    GET_INST_OPCODE ip                  // extract opcode from rINST
31    SET_VREG $result, w9                // vAA<- $result
32    GOTO_OPCODE ip                      // jump to next instruction
33    /* 11-14 instructions */
34
35%def binop2addr(preinstr="", result="w0", chkzero="0", instr=""):
36    /*
37     * Generic 32-bit "/2addr" binary operation.  Provide an "instr" line
38     * that specifies an instruction that performs "result = w0 op w1".
39     * This could be an ARM instruction or a function call.  (If the result
40     * comes back in a register other than w0, you can override "result".)
41     *
42     * If "chkzero" is set to 1, we perform a divide-by-zero check on
43     * vCC (w1).  Useful for integer division and modulus.
44     *
45     * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
46     *      rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
47     *      shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
48     *      sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
49     */
50    /* binop/2addr vA, vB */
51    lsr     w3, wINST, #12              // w3<- B
52    ubfx    w9, wINST, #8, #4           // w9<- A
53    GET_VREG w1, w3                     // w1<- vB
54    GET_VREG w0, w9                     // w0<- vA
55    .if $chkzero
56    cbz     w1, common_errDivideByZero
57    .endif
58    FETCH_ADVANCE_INST 1                // advance rPC, load rINST
59    $preinstr                           // optional op; may set condition codes
60    $instr                              // $result<- op, w0-w3 changed
61    GET_INST_OPCODE ip                  // extract opcode from rINST
62    SET_VREG $result, w9                // vAA<- $result
63    GOTO_OPCODE ip                      // jump to next instruction
64    /* 10-13 instructions */
65
66%def binopLit16(preinstr="", result="w0", chkzero="0", instr=""):
67    /*
68     * Generic 32-bit "lit16" binary operation.  Provide an "instr" line
69     * that specifies an instruction that performs "result = w0 op w1".
70     * This could be an ARM instruction or a function call.  (If the result
71     * comes back in a register other than w0, you can override "result".)
72     *
73     * If "chkzero" is set to 1, we perform a divide-by-zero check on
74     * vCC (w1).  Useful for integer division and modulus.
75     *
76     * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
77     *      rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
78     */
79    /* binop/lit16 vA, vB, #+CCCC */
80    FETCH_S w1, 1                       // w1<- ssssCCCC (sign-extended)
81    lsr     w2, wINST, #12              // w2<- B
82    ubfx    w9, wINST, #8, #4           // w9<- A
83    GET_VREG w0, w2                     // w0<- vB
84    .if $chkzero
85    cbz     w1, common_errDivideByZero
86    .endif
87    FETCH_ADVANCE_INST 2                // advance rPC, load rINST
88    $preinstr
89    $instr                              // $result<- op, w0-w3 changed
90    GET_INST_OPCODE ip                  // extract opcode from rINST
91    SET_VREG $result, w9                // vAA<- $result
92    GOTO_OPCODE ip                      // jump to next instruction
93    /* 10-13 instructions */
94
95%def binopLit8(extract="asr     w1, w3, #8", preinstr="", result="w0", chkzero="0", instr=""):
96    /*
97     * Generic 32-bit "lit8" binary operation.  Provide an "instr" line
98     * that specifies an instruction that performs "result = w0 op w1".
99     * This could be an ARM instruction or a function call.  (If the result
100     * comes back in a register other than w0, you can override "result".)
101     *
102     * You can override "extract" if the extraction of the literal value
103     * from w3 to w1 is not the default "asr w1, w3, #8". The extraction
104     * can be omitted completely if the shift is embedded in "instr".
105     *
106     * If "chkzero" is set to 1, we perform a divide-by-zero check on
107     * vCC (w1).  Useful for integer division and modulus.
108     *
109     * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
110     *      rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
111     *      shl-int/lit8, shr-int/lit8, ushr-int/lit8
112     */
113    /* binop/lit8 vAA, vBB, #+CC */
114    FETCH_S w3, 1                       // w3<- ssssCCBB (sign-extended for CC)
115    lsr     w9, wINST, #8               // w9<- AA
116    and     w2, w3, #255                // w2<- BB
117    GET_VREG w0, w2                     // w0<- vBB
118    $extract                            // optional; typically w1<- ssssssCC (sign extended)
119    .if $chkzero
120    cbz     w1, common_errDivideByZero
121    .endif
122    FETCH_ADVANCE_INST 2                // advance rPC, load rINST
123    $preinstr                           // optional op; may set condition codes
124    $instr                              // $result<- op, w0-w3 changed
125    GET_INST_OPCODE ip                  // extract opcode from rINST
126    SET_VREG $result, w9                // vAA<- $result
127    GOTO_OPCODE ip                      // jump to next instruction
128    /* 10-12 instructions */
129
130%def binopWide(preinstr="", instr="add x0, x1, x2", result="x0", r1="x1", r2="x2", chkzero="0"):
131    /*
132     * Generic 64-bit binary operation.  Provide an "instr" line that
133     * specifies an instruction that performs "result = x1 op x2".
134     * This could be an ARM instruction or a function call.  (If the result
135     * comes back in a register other than x0, you can override "result".)
136     *
137     * If "chkzero" is set to 1, we perform a divide-by-zero check on
138     * vCC (w1).  Useful for integer division and modulus.
139     *
140     * For: add-long, sub-long, mul-long, div-long, rem-long, and-long, or-long,
141     *      xor-long, add-double, sub-double, mul-double, div-double, rem-double
142     */
143    /* binop vAA, vBB, vCC */
144    FETCH w0, 1                           // w0<- CCBB
145    LOAD_SCALED_VREG_MASK w5, 0xff        // w5<- ff * sizeof(vreg)
146    EXTRACT_SCALED_VREG w4, w5, wINST, 8  // w4<- AA * sizeof(vreg)
147    EXTRACT_SCALED_VREG w2, w5, w0, 8     // w2<- CC * sizeof(vreg)
148    EXTRACT_SCALED_VREG w1, w5, w0, 0     // w1<- BB * sizeof(vreg)
149    GET_VREG_WIDE_PRESCALED $r2, w2       // w2<- vCC
150    GET_VREG_WIDE_PRESCALED $r1, w1       // w1<- vBB
151    .if $chkzero
152    cbz     $r2, common_errDivideByZero   // is second operand zero?
153    .endif
154    FETCH_ADVANCE_INST 2                  // advance rPC, load rINST
155    $preinstr
156    $instr                                // $result<- op, w0-w4 changed
157    GET_INST_OPCODE ip                    // extract opcode from rINST
158    SET_VREG_WIDE_PRESCALED $result, w4   // vAA<- $result
159    GOTO_OPCODE ip                        // jump to next instruction
160    /* 11-14 instructions */
161
162%def binopWide2addr(preinstr="", instr="add x0, x0, x1", r0="x0", r1="x1", chkzero="0"):
163    /*
164     * Generic 64-bit "/2addr" binary operation.  Provide an "instr" line
165     * that specifies an instruction that performs "x0 = x0 op x1".
166     * This must not be a function call, as we keep w2 live across it.
167     *
168     * If "chkzero" is set to 1, we perform a divide-by-zero check on
169     * vCC (w1).  Useful for integer division and modulus.
170     *
171     * For: add-long/2addr, sub-long/2addr, mul-long/2addr, div-long/2addr,
172     *      and-long/2addr, or-long/2addr, xor-long/2addr,
173     *      shl-long/2addr, shr-long/2addr, ushr-long/2addr, add-double/2addr,
174     *      sub-double/2addr, mul-double/2addr, div-double/2addr, rem-double/2addr
175     */
176    /* binop/2addr vA, vB */
177    lsr     w1, wINST, #12              // w1<- B
178    ubfx    w2, wINST, #8, #4           // w2<- A
179    GET_VREG_WIDE $r1, w1               // x1<- vB
180    GET_VREG_WIDE $r0, w2               // x0<- vA
181    .if $chkzero
182    cbz     $r1, common_errDivideByZero
183    .endif
184    FETCH_ADVANCE_INST 1                // advance rPC, load rINST
185    $preinstr
186    $instr                              // result<- op
187    GET_INST_OPCODE ip                  // extract opcode from rINST
188    SET_VREG_WIDE $r0, w2               // vAA<- result
189    GOTO_OPCODE ip                      // jump to next instruction
190    /* 10-13 instructions */
191
192%def shiftWide(opcode="shl"):
193    /*
194     * 64-bit shift operation.
195     *
196     * For: shl-long, shr-long, ushr-long
197     */
198    /* binop vAA, vBB, vCC */
199    FETCH w0, 1                         // w0<- CCBB
200    lsr      w3, wINST, #8               // w3<- AA
201    lsr      w2, w0, #8                  // w2<- CC
202    GET_VREG w2, w2                     // w2<- vCC (shift count)
203    and      w1, w0, #255                // w1<- BB
204    GET_VREG_WIDE x1, w1                // x1<- vBB
205    FETCH_ADVANCE_INST 2                // advance rPC, load rINST
206    $opcode  x0, x1, x2                 // Do the shift. Only low 6 bits of x2 are used.
207    GET_INST_OPCODE ip                  // extract opcode from rINST
208    SET_VREG_WIDE x0, w3                // vAA<- x0
209    GOTO_OPCODE ip                      // jump to next instruction
210    /* 11-14 instructions */
211
212%def shiftWide2addr(opcode="lsl"):
213    /*
214     * Generic 64-bit shift operation.
215     */
216    /* binop/2addr vA, vB */
217    lsr     w1, wINST, #12              // w1<- B
218    ubfx    w2, wINST, #8, #4           // w2<- A
219    GET_VREG w1, w1                     // x1<- vB
220    GET_VREG_WIDE x0, w2                // x0<- vA
221    FETCH_ADVANCE_INST 1                // advance rPC, load rINST
222    $opcode x0, x0, x1                  // Do the shift. Only low 6 bits of x1 are used.
223    GET_INST_OPCODE ip                  // extract opcode from rINST
224    SET_VREG_WIDE x0, w2               // vAA<- result
225    GOTO_OPCODE ip                      // jump to next instruction
226    /* 10-13 instructions */
227
228%def unop(instr=""):
229    /*
230     * Generic 32-bit unary operation.  Provide an "instr" line that
231     * specifies an instruction that performs "result = op w0".
232     * This could be an ARM instruction or a function call.
233     *
234     * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
235     *      int-to-byte, int-to-char, int-to-short
236     */
237    /* unop vA, vB */
238    lsr     w3, wINST, #12              // w3<- B
239    GET_VREG w0, w3                     // w0<- vB
240    ubfx    w9, wINST, #8, #4           // w9<- A
241    FETCH_ADVANCE_INST 1                // advance rPC, load rINST
242    $instr                              // w0<- op, w0-w3 changed
243    GET_INST_OPCODE ip                  // extract opcode from rINST
244    SET_VREG w0, w9                     // vAA<- w0
245    GOTO_OPCODE ip                      // jump to next instruction
246    /* 8-9 instructions */
247
248%def unopWide(instr="sub x0, xzr, x0"):
249    /*
250     * Generic 64-bit unary operation.  Provide an "instr" line that
251     * specifies an instruction that performs "result = op x0".
252     *
253     * For: neg-long, not-long
254     */
255    /* unop vA, vB */
256    lsr     w3, wINST, #12              // w3<- B
257    ubfx    w4, wINST, #8, #4           // w4<- A
258    GET_VREG_WIDE x0, w3
259    FETCH_ADVANCE_INST 1                // advance rPC, load wINST
260    $instr
261    GET_INST_OPCODE ip                  // extract opcode from wINST
262    SET_VREG_WIDE x0, w4
263    GOTO_OPCODE ip                      // jump to next instruction
264    /* 10-11 instructions */
265
266%def op_add_int():
267%  binop(instr="add     w0, w0, w1")
268
269%def op_add_int_2addr():
270%  binop2addr(instr="add     w0, w0, w1")
271
272%def op_add_int_lit16():
273%  binopLit16(instr="add     w0, w0, w1")
274
275%def op_add_int_lit8():
276%  binopLit8(extract="", instr="add     w0, w0, w3, asr #8")
277
278%def op_add_long():
279%  binopWide(instr="add x0, x1, x2")
280
281%def op_add_long_2addr():
282%  binopWide2addr(instr="add     x0, x0, x1")
283
284%def op_and_int():
285%  binop(instr="and     w0, w0, w1")
286
287%def op_and_int_2addr():
288%  binop2addr(instr="and     w0, w0, w1")
289
290%def op_and_int_lit16():
291%  binopLit16(instr="and     w0, w0, w1")
292
293%def op_and_int_lit8():
294%  binopLit8(extract="", instr="and     w0, w0, w3, asr #8")
295
296%def op_and_long():
297%  binopWide(instr="and x0, x1, x2")
298
299%def op_and_long_2addr():
300%  binopWide2addr(instr="and     x0, x0, x1")
301
302%def op_cmp_long():
303    FETCH w0, 1                         // w0<- CCBB
304    LOAD_SCALED_VREG_MASK w5, 0xff      // w4<- ff * sizeof(vreg)
305    lsr     w4, wINST, #8               // w4<- AA
306    EXTRACT_SCALED_VREG w2, w5, w0, 0   // w2<- BB * sizeof(vreg)
307    EXTRACT_SCALED_VREG w3, w5, w0, 8   // w3<- CC * sizeof(vreg)
308    GET_VREG_WIDE_PRESCALED x1, w2
309    GET_VREG_WIDE_PRESCALED x2, w3
310    cmp     x1, x2
311    cset    w0, ne
312    cneg    w0, w0, lt
313    FETCH_ADVANCE_INST 2                // advance rPC, load wINST
314    SET_VREG w0, w4
315    GET_INST_OPCODE ip                  // extract opcode from wINST
316    GOTO_OPCODE ip                      // jump to next instruction
317
318%def op_div_int():
319%  binop(instr="sdiv     w0, w0, w1", chkzero="1")
320
321%def op_div_int_2addr():
322%  binop2addr(instr="sdiv     w0, w0, w1", chkzero="1")
323
324%def op_div_int_lit16():
325%  binopLit16(instr="sdiv w0, w0, w1", chkzero="1")
326
327%def op_div_int_lit8():
328%  binopLit8(instr="sdiv     w0, w0, w1", chkzero="1")
329
330%def op_div_long():
331%  binopWide(instr="sdiv x0, x1, x2", chkzero="1")
332
333%def op_div_long_2addr():
334%  binopWide2addr(instr="sdiv     x0, x0, x1", chkzero="1")
335
336%def op_int_to_byte():
337%  unop(instr="sxtb    w0, w0")
338
339%def op_int_to_char():
340%  unop(instr="uxth    w0, w0")
341
342%def op_int_to_long():
343    /* int-to-long vA, vB */
344    lsr     w3, wINST, #12              // w3<- B
345    ubfx    w4, wINST, #8, #4           // w4<- A
346    GET_VREG_S x0, w3                   // x0<- sign_extend(fp[B])
347    FETCH_ADVANCE_INST 1                // advance rPC, load wINST
348    GET_INST_OPCODE ip                  // extract opcode from wINST
349    SET_VREG_WIDE x0, w4                // fp[A]<- x0
350    GOTO_OPCODE ip                      // jump to next instruction
351
352%def op_int_to_short():
353%  unop(instr="sxth    w0, w0")
354
355%def op_long_to_int():
356/* we ignore the high word, making this equivalent to a 32-bit reg move */
357%  op_move()
358
359%def op_mul_int():
360/* must be "mul w0, w1, w0" -- "w0, w0, w1" is illegal */
361%  binop(instr="mul     w0, w1, w0")
362
363%def op_mul_int_2addr():
364/* must be "mul w0, w1, w0" -- "w0, w0, w1" is illegal */
365%  binop2addr(instr="mul     w0, w1, w0")
366
367%def op_mul_int_lit16():
368/* must be "mul w0, w1, w0" -- "w0, w0, w1" is illegal */
369%  binopLit16(instr="mul     w0, w1, w0")
370
371%def op_mul_int_lit8():
372/* must be "mul w0, w1, w0" -- "w0, w0, w1" is illegal */
373%  binopLit8(instr="mul     w0, w1, w0")
374
375%def op_mul_long():
376%  binopWide(instr="mul x0, x1, x2")
377
378%def op_mul_long_2addr():
379%  binopWide2addr(instr="mul     x0, x0, x1")
380
381%def op_neg_int():
382%  unop(instr="sub     w0, wzr, w0")
383
384%def op_neg_long():
385%  unopWide(instr="sub x0, xzr, x0")
386
387%def op_not_int():
388%  unop(instr="mvn     w0, w0")
389
390%def op_not_long():
391%  unopWide(instr="mvn     x0, x0")
392
393%def op_or_int():
394%  binop(instr="orr     w0, w0, w1")
395
396%def op_or_int_2addr():
397%  binop2addr(instr="orr     w0, w0, w1")
398
399%def op_or_int_lit16():
400%  binopLit16(instr="orr     w0, w0, w1")
401
402%def op_or_int_lit8():
403%  binopLit8(extract="", instr="orr     w0, w0, w3, asr #8")
404
405%def op_or_long():
406%  binopWide(instr="orr x0, x1, x2")
407
408%def op_or_long_2addr():
409%  binopWide2addr(instr="orr     x0, x0, x1")
410
411%def op_rem_int():
412%  binop(preinstr="sdiv     w2, w0, w1", instr="msub w0, w2, w1, w0", chkzero="1")
413
414%def op_rem_int_2addr():
415%  binop2addr(preinstr="sdiv     w2, w0, w1", instr="msub w0, w2, w1, w0", chkzero="1")
416
417%def op_rem_int_lit16():
418%  binopLit16(preinstr="sdiv w3, w0, w1", instr="msub w0, w3, w1, w0", chkzero="1")
419
420%def op_rem_int_lit8():
421%  binopLit8(preinstr="sdiv w3, w0, w1", instr="msub w0, w3, w1, w0", chkzero="1")
422
423%def op_rem_long():
424%  binopWide(preinstr="sdiv x3, x1, x2", instr="msub x0, x3, x2, x1", chkzero="1")
425
426%def op_rem_long_2addr():
427%  binopWide2addr(preinstr="sdiv x3, x0, x1", instr="msub x0, x3, x1, x0", chkzero="1")
428
429%def op_rsub_int():
430/* this op is "rsub-int", but can be thought of as "rsub-int/lit16" */
431%  binopLit16(instr="sub     w0, w1, w0")
432
433%def op_rsub_int_lit8():
434%  binopLit8(instr="sub     w0, w1, w0")
435
436%def op_shl_int():
437%  binop(instr="lsl     w0, w0, w1")
438
439%def op_shl_int_2addr():
440%  binop2addr(instr="lsl     w0, w0, w1")
441
442%def op_shl_int_lit8():
443%  binopLit8(extract="ubfx    w1, w3, #8, #5", instr="lsl     w0, w0, w1")
444
445%def op_shl_long():
446%  shiftWide(opcode="lsl")
447
448%def op_shl_long_2addr():
449%  shiftWide2addr(opcode="lsl")
450
451%def op_shr_int():
452%  binop(instr="asr     w0, w0, w1")
453
454%def op_shr_int_2addr():
455%  binop2addr(instr="asr     w0, w0, w1")
456
457%def op_shr_int_lit8():
458%  binopLit8(extract="ubfx    w1, w3, #8, #5", instr="asr     w0, w0, w1")
459
460%def op_shr_long():
461%  shiftWide(opcode="asr")
462
463%def op_shr_long_2addr():
464%  shiftWide2addr(opcode="asr")
465
466%def op_sub_int():
467%  binop(instr="sub     w0, w0, w1")
468
469%def op_sub_int_2addr():
470%  binop2addr(instr="sub     w0, w0, w1")
471
472%def op_sub_long():
473%  binopWide(instr="sub x0, x1, x2")
474
475%def op_sub_long_2addr():
476%  binopWide2addr(instr="sub     x0, x0, x1")
477
478%def op_ushr_int():
479%  binop(instr="lsr     w0, w0, w1")
480
481%def op_ushr_int_2addr():
482%  binop2addr(instr="lsr     w0, w0, w1")
483
484%def op_ushr_int_lit8():
485%  binopLit8(extract="ubfx    w1, w3, #8, #5", instr="lsr     w0, w0, w1")
486
487%def op_ushr_long():
488%  shiftWide(opcode="lsr")
489
490%def op_ushr_long_2addr():
491%  shiftWide2addr(opcode="lsr")
492
493%def op_xor_int():
494%  binop(instr="eor     w0, w0, w1")
495
496%def op_xor_int_2addr():
497%  binop2addr(instr="eor     w0, w0, w1")
498
499%def op_xor_int_lit16():
500%  binopLit16(instr="eor     w0, w0, w1")
501
502%def op_xor_int_lit8():
503%  binopLit8(extract="", instr="eor     w0, w0, w3, asr #8")
504
505%def op_xor_long():
506%  binopWide(instr="eor x0, x1, x2")
507
508%def op_xor_long_2addr():
509%  binopWide2addr(instr="eor     x0, x0, x1")
510