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