1%def fbinop(instr=""):
2    /*
3     * Generic 32-bit floating-point operation.  Provide an "instr" line that
4     * specifies an instruction that performs "s2 = s0 op s1".  Because we
5     * use the "softfp" ABI, this must be an instruction, not a function call.
6     *
7     * For: add-float, sub-float, mul-float, div-float
8     */
9    /* floatop vAA, vBB, vCC */
10    FETCH r0, 1                         @ r0<- CCBB
11    mov     r9, rINST, lsr #8           @ r9<- AA
12    mov     r3, r0, lsr #8              @ r3<- CC
13    and     r2, r0, #255                @ r2<- BB
14    VREG_INDEX_TO_ADDR r3, r3           @ r3<- &vCC
15    VREG_INDEX_TO_ADDR r2, r2           @ r2<- &vBB
16    GET_VREG_FLOAT_BY_ADDR s1, r3       @ s1<- vCC
17    GET_VREG_FLOAT_BY_ADDR s0, r2       @ s0<- vBB
18
19    FETCH_ADVANCE_INST 2                @ advance rPC, load rINST
20    $instr                              @ s2<- op
21    GET_INST_OPCODE ip                  @ extract opcode from rINST
22    SET_VREG_FLOAT s2, r9, lr           @ vAA<- s2
23    GOTO_OPCODE ip                      @ jump to next instruction
24
25%def fbinop2addr(instr=""):
26    /*
27     * Generic 32-bit floating point "/2addr" binary operation.  Provide
28     * an "instr" line that specifies an instruction that performs
29     * "s2 = s0 op s1".
30     *
31     * For: add-float/2addr, sub-float/2addr, mul-float/2addr, div-float/2addr
32     */
33    /* binop/2addr vA, vB */
34    mov     r3, rINST, lsr #12          @ r3<- B
35    ubfx    r9, rINST, #8, #4           @ r9<- A
36    VREG_INDEX_TO_ADDR r3, r3           @ r3<- &vB
37    VREG_INDEX_TO_ADDR r9, r9           @ r9<- &vA
38    GET_VREG_FLOAT_BY_ADDR s1, r3       @ s1<- vB
39    FETCH_ADVANCE_INST 1                @ advance rPC, load rINST
40    GET_VREG_FLOAT_BY_ADDR s0, r9       @ s0<- vA
41    $instr                              @ s2<- op
42    GET_INST_OPCODE ip                  @ extract opcode from rINST
43    SET_VREG_FLOAT_BY_ADDR s2, r9       @ vAA<- s2 No need to clear as it's 2addr
44    GOTO_OPCODE ip                      @ jump to next instruction
45
46%def fbinopWide(instr=""):
47    /*
48     * Generic 64-bit double-precision floating point binary operation.
49     * Provide an "instr" line that specifies an instruction that performs
50     * "d2 = d0 op d1".
51     *
52     * for: add-double, sub-double, mul-double, div-double
53     */
54    /* doubleop vAA, vBB, vCC */
55    FETCH r0, 1                         @ r0<- CCBB
56    mov     r9, rINST, lsr #8           @ r9<- AA
57    mov     r3, r0, lsr #8              @ r3<- CC
58    and     r2, r0, #255                @ r2<- BB
59    VREG_INDEX_TO_ADDR r3, r3           @ r3<- &vCC
60    VREG_INDEX_TO_ADDR r2, r2           @ r2<- &vBB
61    GET_VREG_DOUBLE_BY_ADDR d1, r3      @ d1<- vCC
62    GET_VREG_DOUBLE_BY_ADDR d0, r2      @ d0<- vBB
63    FETCH_ADVANCE_INST 2                @ advance rPC, load rINST
64    $instr                              @ s2<- op
65    CLEAR_SHADOW_PAIR r9, ip, lr        @ Zero shadow regs
66    GET_INST_OPCODE ip                  @ extract opcode from rINST
67    VREG_INDEX_TO_ADDR r9, r9           @ r9<- &vAA
68    SET_VREG_DOUBLE_BY_ADDR d2, r9      @ vAA<- d2
69    GOTO_OPCODE ip                      @ jump to next instruction
70
71%def fbinopWide2addr(instr=""):
72    /*
73     * Generic 64-bit floating point "/2addr" binary operation.  Provide
74     * an "instr" line that specifies an instruction that performs
75     * "d2 = d0 op d1".
76     *
77     * For: add-double/2addr, sub-double/2addr, mul-double/2addr,
78     *      div-double/2addr
79     */
80    /* binop/2addr vA, vB */
81    mov     r3, rINST, lsr #12          @ r3<- B
82    ubfx    r9, rINST, #8, #4           @ r9<- A
83    VREG_INDEX_TO_ADDR r3, r3           @ r3<- &vB
84    CLEAR_SHADOW_PAIR r9, ip, r0        @ Zero out shadow regs
85    GET_VREG_DOUBLE_BY_ADDR d1, r3      @ d1<- vB
86    VREG_INDEX_TO_ADDR r9, r9           @ r9<- &vA
87    FETCH_ADVANCE_INST 1                @ advance rPC, load rINST
88    GET_VREG_DOUBLE_BY_ADDR d0, r9      @ d0<- vA
89    $instr                              @ d2<- op
90    GET_INST_OPCODE ip                  @ extract opcode from rINST
91    SET_VREG_DOUBLE_BY_ADDR d2, r9      @ vAA<- d2
92    GOTO_OPCODE ip                      @ jump to next instruction
93
94%def funop(instr=""):
95    /*
96     * Generic 32-bit unary floating-point operation.  Provide an "instr"
97     * line that specifies an instruction that performs "s1 = op s0".
98     *
99     * for: int-to-float, float-to-int
100     */
101    /* unop vA, vB */
102    mov     r3, rINST, lsr #12          @ r3<- B
103    VREG_INDEX_TO_ADDR r3, r3           @ r3<- &vB
104    GET_VREG_FLOAT_BY_ADDR s0, r3       @ s0<- vB
105    ubfx    r9, rINST, #8, #4           @ r9<- A
106    FETCH_ADVANCE_INST 1                @ advance rPC, load rINST
107    $instr                              @ s1<- op
108    GET_INST_OPCODE ip                  @ extract opcode from rINST
109    SET_VREG_FLOAT s1, r9, lr           @ vA<- s1
110    GOTO_OPCODE ip                      @ jump to next instruction
111
112%def funopNarrower(instr=""):
113    /*
114     * Generic 64bit-to-32bit unary floating point operation.  Provide an
115     * "instr" line that specifies an instruction that performs "s0 = op d0".
116     *
117     * For: double-to-int, double-to-float
118     */
119    /* unop vA, vB */
120    mov     r3, rINST, lsr #12          @ r3<- B
121    VREG_INDEX_TO_ADDR r3, r3           @ r3<- &vB
122    GET_VREG_DOUBLE_BY_ADDR d0, r3      @ d0<- vB
123    ubfx    r9, rINST, #8, #4           @ r9<- A
124    FETCH_ADVANCE_INST 1                @ advance rPC, load rINST
125    $instr                              @ s0<- op
126    GET_INST_OPCODE ip                  @ extract opcode from rINST
127    SET_VREG_FLOAT s0, r9, lr           @ vA<- s0
128    GOTO_OPCODE ip                      @ jump to next instruction
129
130%def funopWider(instr=""):
131    /*
132     * Generic 32bit-to-64bit floating point unary operation.  Provide an
133     * "instr" line that specifies an instruction that performs "d0 = op s0".
134     *
135     * For: int-to-double, float-to-double
136     */
137    /* unop vA, vB */
138    mov     r3, rINST, lsr #12          @ r3<- B
139    VREG_INDEX_TO_ADDR r3, r3           @ r3<- &vB
140    GET_VREG_FLOAT_BY_ADDR s0, r3       @ s0<- vB
141    ubfx    r9, rINST, #8, #4           @ r9<- A
142    FETCH_ADVANCE_INST 1                @ advance rPC, load rINST
143    $instr                              @ d0<- op
144    CLEAR_SHADOW_PAIR r9, ip, lr        @ Zero shadow regs
145    GET_INST_OPCODE ip                  @ extract opcode from rINST
146    VREG_INDEX_TO_ADDR r9, r9           @ r9<- &vA
147    SET_VREG_DOUBLE_BY_ADDR d0, r9      @ vA<- d0
148    GOTO_OPCODE ip                      @ jump to next instruction
149
150%def op_add_double():
151%  fbinopWide(instr="faddd   d2, d0, d1")
152
153%def op_add_double_2addr():
154%  fbinopWide2addr(instr="faddd   d2, d0, d1")
155
156%def op_add_float():
157%  fbinop(instr="fadds   s2, s0, s1")
158
159%def op_add_float_2addr():
160%  fbinop2addr(instr="fadds   s2, s0, s1")
161
162%def op_cmpg_double():
163    /*
164     * Compare two floating-point values.  Puts 0, 1, or -1 into the
165     * destination register based on the results of the comparison.
166     *
167     * int compare(x, y) {
168     *     if (x == y) {
169     *         return 0;
170     *     } else if (x < y) {
171     *         return -1;
172     *     } else if (x > y) {
173     *         return 1;
174     *     } else {
175     *         return 1;
176     *     }
177     * }
178     */
179    /* op vAA, vBB, vCC */
180    FETCH r0, 1                         @ r0<- CCBB
181    mov     r9, rINST, lsr #8           @ r9<- AA
182    and     r2, r0, #255                @ r2<- BB
183    mov     r3, r0, lsr #8              @ r3<- CC
184    VREG_INDEX_TO_ADDR r2, r2           @ r2<- &vBB
185    VREG_INDEX_TO_ADDR r3, r3           @ r3<- &vCC
186    GET_VREG_DOUBLE_BY_ADDR d0, r2      @ d0<- vBB
187    GET_VREG_DOUBLE_BY_ADDR d1, r3      @ d1<- vCC
188    vcmpe.f64 d0, d1                    @ compare (vBB, vCC)
189    FETCH_ADVANCE_INST 2                @ advance rPC, load rINST
190    mov     r0, #1                      @ r0<- 1 (default)
191    GET_INST_OPCODE ip                  @ extract opcode from rINST
192    fmstat                              @ export status flags
193    mvnmi   r0, #0                      @ (less than) r1<- -1
194    moveq   r0, #0                      @ (equal) r1<- 0
195    SET_VREG r0, r9                     @ vAA<- r0
196    GOTO_OPCODE ip                      @ jump to next instruction
197
198%def op_cmpg_float():
199    /*
200     * Compare two floating-point values.  Puts 0, 1, or -1 into the
201     * destination register based on the results of the comparison.
202     *
203     * int compare(x, y) {
204     *     if (x == y) {
205     *         return 0;
206     *     } else if (x < y) {
207     *         return -1;
208     *     } else if (x > y) {
209     *         return 1;
210     *     } else {
211     *         return 1;
212     *     }
213     * }
214     */
215    /* op vAA, vBB, vCC */
216    FETCH r0, 1                         @ r0<- CCBB
217    mov     r9, rINST, lsr #8           @ r9<- AA
218    and     r2, r0, #255                @ r2<- BB
219    mov     r3, r0, lsr #8              @ r3<- CC
220    VREG_INDEX_TO_ADDR r2, r2           @ r2<- &vBB
221    VREG_INDEX_TO_ADDR r3, r3           @ r3<- &vCC
222    GET_VREG_FLOAT_BY_ADDR s0, r2       @ s0<- vBB
223    GET_VREG_FLOAT_BY_ADDR s1, r3       @ s1<- vCC
224    vcmpe.f32 s0, s1                    @ compare (vBB, vCC)
225    FETCH_ADVANCE_INST 2                @ advance rPC, load rINST
226    mov     r0, #1                      @ r0<- 1 (default)
227    GET_INST_OPCODE ip                  @ extract opcode from rINST
228    fmstat                              @ export status flags
229    mvnmi   r0, #0                      @ (less than) r1<- -1
230    moveq   r0, #0                      @ (equal) r1<- 0
231    SET_VREG r0, r9                     @ vAA<- r0
232    GOTO_OPCODE ip                      @ jump to next instruction
233
234%def op_cmpl_double():
235    /*
236     * Compare two floating-point values.  Puts 0, 1, or -1 into the
237     * destination register based on the results of the comparison.
238     *
239     * int compare(x, y) {
240     *     if (x == y) {
241     *         return 0;
242     *     } else if (x > y) {
243     *         return 1;
244     *     } else if (x < y) {
245     *         return -1;
246     *     } else {
247     *         return -1;
248     *     }
249     * }
250     */
251    /* op vAA, vBB, vCC */
252    FETCH r0, 1                         @ r0<- CCBB
253    mov     r9, rINST, lsr #8           @ r9<- AA
254    and     r2, r0, #255                @ r2<- BB
255    mov     r3, r0, lsr #8              @ r3<- CC
256    VREG_INDEX_TO_ADDR r2, r2           @ r2<- &vBB
257    VREG_INDEX_TO_ADDR r3, r3           @ r3<- &vCC
258    GET_VREG_DOUBLE_BY_ADDR d0, r2      @ d0<- vBB
259    GET_VREG_DOUBLE_BY_ADDR d1, r3      @ d1<- vCC
260    vcmpe.f64 d0, d1                    @ compare (vBB, vCC)
261    FETCH_ADVANCE_INST 2                @ advance rPC, load rINST
262    mvn     r0, #0                      @ r0<- -1 (default)
263    GET_INST_OPCODE ip                  @ extract opcode from rINST
264    fmstat                              @ export status flags
265    movgt   r0, #1                      @ (greater than) r1<- 1
266    moveq   r0, #0                      @ (equal) r1<- 0
267    SET_VREG r0, r9                     @ vAA<- r0
268    GOTO_OPCODE ip                      @ jump to next instruction
269
270%def op_cmpl_float():
271    /*
272     * Compare two floating-point values.  Puts 0, 1, or -1 into the
273     * destination register based on the results of the comparison.
274     *
275     * int compare(x, y) {
276     *     if (x == y) {
277     *         return 0;
278     *     } else if (x > y) {
279     *         return 1;
280     *     } else if (x < y) {
281     *         return -1;
282     *     } else {
283     *         return -1;
284     *     }
285     * }
286     */
287    /* op vAA, vBB, vCC */
288    FETCH r0, 1                         @ r0<- CCBB
289    mov     r9, rINST, lsr #8           @ r9<- AA
290    and     r2, r0, #255                @ r2<- BB
291    mov     r3, r0, lsr #8              @ r3<- CC
292    VREG_INDEX_TO_ADDR r2, r2           @ r2<- &vBB
293    VREG_INDEX_TO_ADDR r3, r3           @ r3<- &vCC
294    GET_VREG_FLOAT_BY_ADDR s0, r2       @ s0<- vBB
295    GET_VREG_FLOAT_BY_ADDR s1, r3       @ s1<- vCC
296    vcmpe.f32  s0, s1                   @ compare (vBB, vCC)
297    FETCH_ADVANCE_INST 2                @ advance rPC, load rINST
298    mvn     r0, #0                      @ r0<- -1 (default)
299    GET_INST_OPCODE ip                  @ extract opcode from rINST
300    fmstat                              @ export status flags
301    movgt   r0, #1                      @ (greater than) r1<- 1
302    moveq   r0, #0                      @ (equal) r1<- 0
303    SET_VREG r0, r9                     @ vAA<- r0
304    GOTO_OPCODE ip                      @ jump to next instruction
305
306%def op_div_double():
307%  fbinopWide(instr="fdivd   d2, d0, d1")
308
309%def op_div_double_2addr():
310%  fbinopWide2addr(instr="fdivd   d2, d0, d1")
311
312%def op_div_float():
313%  fbinop(instr="fdivs   s2, s0, s1")
314
315%def op_div_float_2addr():
316%  fbinop2addr(instr="fdivs   s2, s0, s1")
317
318%def op_double_to_float():
319%  funopNarrower(instr="vcvt.f32.f64  s0, d0")
320
321%def op_double_to_int():
322%  funopNarrower(instr="ftosizd  s0, d0")
323
324%def op_double_to_long():
325%  unopWide(instr="bl      d2l_doconv")
326%  add_helper(op_double_to_long_helper)
327
328%def op_double_to_long_helper():
329/*
330 * Convert the double in r0/r1 to a long in r0/r1.
331 *
332 * We have to clip values to long min/max per the specification.  The
333 * expected common case is a "reasonable" value that converts directly
334 * to modest integer.  The EABI convert function isn't doing this for us.
335 */
336d2l_doconv:
337    ubfx    r2, r1, #20, #11            @ grab the exponent
338    movw    r3, #0x43e
339    cmp     r2, r3                      @ MINLONG < x > MAXLONG?
340    bhs     d2l_special_cases
341    b       __aeabi_d2lz                @ tail call to convert double to long
342d2l_special_cases:
343    movw    r3, #0x7ff
344    cmp     r2, r3
345    beq     d2l_maybeNaN                @ NaN?
346d2l_notNaN:
347    adds    r1, r1, r1                  @ sign bit to carry
348    mov     r0, #0xffffffff             @ assume maxlong for lsw
349    mov     r1, #0x7fffffff             @ assume maxlong for msw
350    adc     r0, r0, #0
351    adc     r1, r1, #0                  @ convert maxlong to minlong if exp negative
352    bx      lr                          @ return
353d2l_maybeNaN:
354    orrs    r3, r0, r1, lsl #12
355    beq     d2l_notNaN                  @ if fraction is non-zero, it's a NaN
356    mov     r0, #0
357    mov     r1, #0
358    bx      lr                          @ return 0 for NaN
359
360%def op_float_to_double():
361%  funopWider(instr="vcvt.f64.f32  d0, s0")
362
363%def op_float_to_int():
364%  funop(instr="ftosizs s1, s0")
365
366%def op_float_to_long():
367%  unopWider(instr="bl      f2l_doconv")
368%  add_helper(op_float_to_long_helper)
369
370%def op_float_to_long_helper():
371/*
372 * Convert the float in r0 to a long in r0/r1.
373 *
374 * We have to clip values to long min/max per the specification.  The
375 * expected common case is a "reasonable" value that converts directly
376 * to modest integer.  The EABI convert function isn't doing this for us.
377 */
378f2l_doconv:
379    ubfx    r2, r0, #23, #8             @ grab the exponent
380    cmp     r2, #0xbe                   @ MININT < x > MAXINT?
381    bhs     f2l_special_cases
382    b       __aeabi_f2lz                @ tail call to convert float to long
383f2l_special_cases:
384    cmp     r2, #0xff                   @ NaN or infinity?
385    beq     f2l_maybeNaN
386f2l_notNaN:
387    adds    r0, r0, r0                  @ sign bit to carry
388    mov     r0, #0xffffffff             @ assume maxlong for lsw
389    mov     r1, #0x7fffffff             @ assume maxlong for msw
390    adc     r0, r0, #0
391    adc     r1, r1, #0                  @ convert maxlong to minlong if exp negative
392    bx      lr                          @ return
393f2l_maybeNaN:
394    lsls    r3, r0, #9
395    beq     f2l_notNaN                  @ if fraction is non-zero, it's a NaN
396    mov     r0, #0
397    mov     r1, #0
398    bx      lr                          @ return 0 for NaN
399
400%def op_int_to_double():
401%  funopWider(instr="fsitod  d0, s0")
402
403%def op_int_to_float():
404%  funop(instr="fsitos  s1, s0")
405
406%def op_long_to_double():
407    /*
408     * Specialised 64-bit floating point operation.
409     *
410     * Note: The result will be returned in d2.
411     *
412     * For: long-to-double
413     */
414    mov     r3, rINST, lsr #12          @ r3<- B
415    ubfx    r9, rINST, #8, #4           @ r9<- A
416    CLEAR_SHADOW_PAIR r9, ip, lr        @ Zero shadow regs
417    VREG_INDEX_TO_ADDR r3, r3           @ r3<- &fp[B]
418    VREG_INDEX_TO_ADDR r9, r9           @ r9<- &fp[A]
419    GET_VREG_DOUBLE_BY_ADDR d0, r3      @ d0<- vBB
420    FETCH_ADVANCE_INST 1                @ advance rPC, load rINST
421
422    vcvt.f64.s32    d1, s1              @ d1<- (double)(vAAh)
423    vcvt.f64.u32    d2, s0              @ d2<- (double)(vAAl)
424    vldr            d3, constval$opcode
425    vmla.f64        d2, d1, d3          @ d2<- vAAh*2^32 + vAAl
426
427    GET_INST_OPCODE ip                  @ extract opcode from rINST
428    SET_VREG_DOUBLE_BY_ADDR d2, r9      @ vAA<- d2
429    GOTO_OPCODE ip                      @ jump to next instruction
430
431    /* literal pool helper */
432constval${opcode}:
433    .8byte          0x41f0000000000000
434
435%def op_long_to_float():
436%  unopNarrower(instr="bl      __aeabi_l2f")
437
438%def op_mul_double():
439%  fbinopWide(instr="fmuld   d2, d0, d1")
440
441%def op_mul_double_2addr():
442%  fbinopWide2addr(instr="fmuld   d2, d0, d1")
443
444%def op_mul_float():
445%  fbinop(instr="fmuls   s2, s0, s1")
446
447%def op_mul_float_2addr():
448%  fbinop2addr(instr="fmuls   s2, s0, s1")
449
450%def op_neg_double():
451%  unopWide(instr="add     r1, r1, #0x80000000")
452
453%def op_neg_float():
454%  unop(instr="add     r0, r0, #0x80000000")
455
456%def op_rem_double():
457/* EABI doesn't define a double remainder function, but libm does */
458%  binopWide(instr="bl      fmod")
459
460%def op_rem_double_2addr():
461/* EABI doesn't define a double remainder function, but libm does */
462%  binopWide2addr(instr="bl      fmod")
463
464%def op_rem_float():
465/* EABI doesn't define a float remainder function, but libm does */
466%  binop(instr="bl      fmodf")
467
468%def op_rem_float_2addr():
469/* EABI doesn't define a float remainder function, but libm does */
470%  binop2addr(instr="bl      fmodf")
471
472%def op_sub_double():
473%  fbinopWide(instr="fsubd   d2, d0, d1")
474
475%def op_sub_double_2addr():
476%  fbinopWide2addr(instr="fsubd   d2, d0, d1")
477
478%def op_sub_float():
479%  fbinop(instr="fsubs   s2, s0, s1")
480
481%def op_sub_float_2addr():
482%  fbinop2addr(instr="fsubs   s2, s0, s1")
483