arith.c (84814B)
1 #include "cg/internal.h" 2 3 static int api_try_fold_int_convert(KitCg* g, ConvKind ck, KitCgTypeId sty, 4 KitCgTypeId dty, i64 in, i64* out) { 5 u32 sw; 6 u32 dw; 7 u64 r; 8 if (!g || !out || !api_foldable_int_like_type(g->c, sty, &sw) || 9 !api_foldable_int_like_type(g->c, dty, &dw)) { 10 return 0; 11 } 12 switch (ck) { 13 case CV_SEXT: 14 r = (u64)api_sign_extend_width((u64)in, sw); 15 break; 16 case CV_ZEXT: 17 r = api_mask_width((u64)in, sw); 18 break; 19 case CV_TRUNC: 20 r = api_mask_width((u64)in, dw); 21 break; 22 default: 23 return 0; 24 } 25 *out = api_fold_result(g->c, dty, r, dw); 26 return 1; 27 } 28 29 void api_cg_binop(KitCg* g, BinOp iop, u32 flags) { 30 ApiSValue b, a; 31 ApiConstValue cb, ca, result_const; 32 CgTarget* T; 33 KitCgTypeId ty; 34 Operand ra, rb; 35 CGLocal rr; 36 Operand dst; 37 ApiSValue folded_sv; 38 i64 folded; 39 int can_delay; 40 if (!g) return; 41 cb = api_const_at(g, 0); 42 ca = api_const_at(g, 1); 43 T = g->target; 44 b = api_pop(g); 45 a = api_pop(g); 46 ty = a.type ? a.type : b.type; 47 api_const_fold_binop(g, iop, ty, ca, cb, flags, &result_const); 48 if (api_unevaluated(g)) { 49 api_release(g, &a); 50 api_release(g, &b); 51 api_push(g, api_uneval_value(g, ty)); 52 api_const_set_top(g, result_const); 53 return; 54 } 55 /* Delayability is a pure function of (ty, flags), neither of which changes 56 * below (strength-reduce rewrites the op and operands, not the type), so 57 * classify the foldable int once instead of re-deriving it at each of the 58 * three delay gates. */ 59 can_delay = api_can_delay_int_arith(g, ty, flags); 60 61 if (!flags && api_sv_op_is(&a, OPK_IMM) && api_sv_op_is(&b, OPK_IMM) && 62 api_try_fold_int_binop(g, iop, ty, a.op.v.imm, b.op.v.imm, &folded)) { 63 api_release(g, &a); 64 api_release(g, &b); 65 api_push(g, api_make_sv(api_op_imm(folded, ty), ty)); 66 api_const_set_top(g, result_const); 67 return; 68 } 69 70 /* Strength-reduce mul/udiv/urem by a power of two into shift/and. Rewrites 71 * iop and the operands in place; the result flows through the same delay / 72 * identity / fallback machinery as any other shift or and. */ 73 if (!flags) api_try_strength_reduce(g, &iop, ty, &a, &b); 74 75 if (can_delay && api_try_fold_arith_chain(g, iop, ty, &a, &b, &folded_sv)) { 76 api_release(g, &a); 77 api_release(g, &b); 78 api_push(g, folded_sv); 79 api_const_set_top(g, result_const); 80 return; 81 } 82 83 if (api_type_is_float(g->c, ty)) { 84 ra = api_force_local(g, &a, ty); 85 rb = api_force_local(g, &b, ty); 86 } else { 87 ra = api_force_local_unless_imm(g, &a, ty); 88 rb = api_force_local_unless_imm(g, &b, ty); 89 } 90 91 if (can_delay && 92 api_try_collapse_binop_identity(g, iop, ty, &a, &b, &folded_sv)) { 93 api_release(g, &a); 94 api_release(g, &b); 95 api_push(g, folded_sv); 96 api_const_set_top(g, result_const); 97 return; 98 } 99 100 if (can_delay && (ra.kind == OPK_LOCAL || rb.kind == OPK_LOCAL) && 101 (ra.kind == OPK_LOCAL || ra.kind == OPK_IMM) && 102 (rb.kind == OPK_LOCAL || rb.kind == OPK_IMM)) { 103 int a_owned = api_sv_owns_operand_local(&a, &ra); 104 int b_owned = api_sv_owns_operand_local(&b, &rb); 105 api_push(g, api_make_arith_binop(g, iop, ra, rb, ty, a_owned, b_owned)); 106 if (a_owned) api_sv_set_res(&a, RES_INHERENT); 107 if (b_owned) api_sv_set_res(&b, RES_INHERENT); 108 api_release(g, &a); 109 api_release(g, &b); 110 api_const_set_top(g, result_const); 111 return; 112 } 113 114 rr = api_alloc_temp_local(g, ty); 115 dst = api_op_local(rr, ty); 116 /* Flag dead-transient operands so the -O0 backend drops them after the op 117 * instead of spilling them at the next barrier (eager dead-operand drop). */ 118 ra = api_op_kill_if_dead(g, ra, dst); 119 rb = api_op_kill_if_dead(g, rb, dst); 120 T->binop(T, iop, dst, ra, rb); 121 api_release(g, &a); 122 api_release(g, &b); 123 api_push(g, api_make_sv(dst, ty)); 124 api_const_set_top(g, result_const); 125 } 126 127 void api_cg_unop(KitCg* g, UnOp iop, u32 flags) { 128 ApiSValue a; 129 ApiConstValue ca, result_const; 130 CgTarget* T; 131 KitCgTypeId ty; 132 Operand ra; 133 CGLocal rr; 134 Operand dst; 135 ApiSValue folded_sv; 136 i64 folded; 137 int can_delay; 138 if (!g) return; 139 ca = api_const_at(g, 0); 140 T = g->target; 141 a = api_pop(g); 142 ty = a.type ? a.type : a.op.type; 143 api_const_fold_unop(g, iop, ty, ca, flags, &result_const); 144 if (api_unevaluated(g)) { 145 api_release(g, &a); 146 api_push(g, api_uneval_value(g, ty)); 147 api_const_set_top(g, result_const); 148 return; 149 } 150 /* Pure function of (ty, flags); classify the foldable int once for both delay 151 * gates below. */ 152 can_delay = api_can_delay_int_arith(g, ty, flags); 153 154 if (iop == UO_FNEG) { 155 CG_REQUIRE(g, api_type_is_float(g->c, ty), 156 "KitCg: FP negation requires floating operand"); 157 ra = api_force_local(g, &a, ty); 158 rr = api_alloc_temp_local(g, ty); 159 dst = api_op_local(rr, ty); 160 ra = api_op_kill_if_dead(g, ra, dst); 161 T->unop(T, iop, dst, ra); 162 api_release(g, &a); 163 api_push(g, api_make_sv(dst, ty)); 164 api_const_set_top(g, result_const); 165 return; 166 } 167 168 /* Logical NOT of a delayed compare stays delayed: invert the predicate in 169 * place. For FP this flips ordered<->unordered as well as the relation (via 170 * api_invert_cmp), so `!(a<b)` becomes UGE (NaN -> true), matching IEEE 171 * negation. The inverted compare keeps the same i32 result type. */ 172 if (iop == UO_NOT && api_sv_kind(&a) == SV_CMP) { 173 a.delayed->cmp.op = api_invert_cmp(a.delayed->cmp.op); 174 api_push(g, a); 175 api_const_set_top(g, result_const); 176 return; 177 } 178 179 if (!flags && api_sv_op_is(&a, OPK_IMM) && 180 api_try_fold_int_unop(g, iop, ty, a.op.v.imm, &folded)) { 181 api_release(g, &a); 182 api_push(g, api_make_sv(api_op_imm(folded, ty), ty)); 183 api_const_set_top(g, result_const); 184 return; 185 } 186 187 if (can_delay && api_try_fold_unary_chain(&a, iop, ty, &folded_sv)) { 188 api_release(g, &a); 189 api_push(g, folded_sv); 190 api_const_set_top(g, result_const); 191 return; 192 } 193 194 ra = api_force_local_unless_imm(g, &a, ty); 195 if (can_delay && ra.kind == OPK_LOCAL) { 196 int a_owned = api_sv_owns_operand_local(&a, &ra); 197 api_push(g, api_make_arith_unop(g, iop, ra, ty, a_owned)); 198 if (a_owned) api_sv_set_res(&a, RES_INHERENT); 199 api_release(g, &a); 200 api_const_set_top(g, result_const); 201 return; 202 } 203 rr = api_alloc_temp_local(g, ty); 204 dst = api_op_local(rr, ty); 205 ra = api_op_kill_if_dead(g, ra, dst); 206 T->unop(T, iop, dst, ra); 207 api_release(g, &a); 208 api_push(g, api_make_sv(dst, ty)); 209 api_const_set_top(g, result_const); 210 } 211 212 void api_cg_cmp(KitCg* g, CmpOp cop) { 213 ApiSValue b, a; 214 ApiConstValue cb, ca, result_const; 215 KitCgTypeId opty; 216 KitCgTypeId i32; 217 Operand ra, rb; 218 i64 folded; 219 if (!g) return; 220 cb = api_const_at(g, 0); 221 ca = api_const_at(g, 1); 222 b = api_pop(g); 223 a = api_pop(g); 224 opty = a.type ? a.type : b.type; 225 i32 = builtin_id(KIT_CG_BUILTIN_I32); 226 api_const_fold_cmp(g, cop, ca, cb, &result_const); 227 if (api_unevaluated(g)) { 228 api_release(g, &a); 229 api_release(g, &b); 230 api_push(g, api_uneval_value(g, i32)); 231 api_const_set_top(g, result_const); 232 return; 233 } 234 235 if (api_sv_op_is(&a, OPK_IMM) && api_sv_op_is(&b, OPK_IMM) && 236 api_try_fold_int_cmp(g, cop, opty, a.op.v.imm, b.op.v.imm, &folded)) { 237 api_release(g, &a); 238 api_release(g, &b); 239 api_push(g, api_make_sv(api_op_imm(folded, i32), i32)); 240 api_const_set_top(g, result_const); 241 return; 242 } 243 244 ra = api_force_local_unless_imm(g, &a, opty); 245 rb = api_force_local_unless_imm(g, &b, opty); 246 /* Both integer and FP compares are produced as delayed SV_CMP values. 247 * Delaying is what lets api_branch_if (and api_cg_unop's UO_NOT) invert 248 * the compare via api_invert_cmp, reaching the unordered FP duals 249 * (UGE/UGT/ULE/ULT/UEQ/UNE) from `!(a<b)` etc. with NaN-correct semantics. 250 * If the compare instead escapes into value context it is materialized 251 * unchanged via api_materialize_cmp_to, which calls T->cmp with the same 252 * opcode the eager path used to. */ 253 api_push(g, 254 api_make_cmp(g, cop, ra, rb, i32, api_sv_owns_operand_local(&a, &ra), 255 api_sv_owns_operand_local(&b, &rb))); 256 api_const_set_top(g, result_const); 257 } 258 259 int api_try_i128_convert(KitCg* g, ConvKind ck, KitCgTypeId sty, 260 KitCgTypeId dty, ApiSValue* v); 261 int api_try_wide8_convert(KitCg* g, ConvKind ck, KitCgTypeId sty, 262 KitCgTypeId dty, ApiSValue* v); 263 264 void api_cg_convert_kind(KitCg* g, KitCgTypeId dst_type, ConvKind ck) { 265 ApiSValue v; 266 ApiConstValue in_const, result_const; 267 CgTarget* T; 268 KitCgTypeId sty; 269 KitCgTypeId dty; 270 Operand src; 271 CGLocal rr; 272 Operand dst; 273 if (!g) return; 274 T = g->target; 275 /* Keep the return-before-pop order on an invalid dst_type. */ 276 dty = resolve_type(g->c, dst_type); 277 if (!dty) return; 278 in_const = api_const_at(g, 0); 279 v = api_pop(g); 280 sty = resolve_type(g->c, v.type ? v.type : v.op.type); 281 if (!sty) { 282 api_release(g, &v); 283 return; 284 } 285 api_const_fold_convert(g, ck, sty, dty, in_const, &result_const); 286 if (api_unevaluated(g)) { 287 api_release(g, &v); 288 api_push(g, api_uneval_value(g, dty)); 289 api_const_set_top(g, result_const); 290 return; 291 } 292 if (sty == dty) { 293 v.type = dty; 294 v.op.type = dty; 295 api_push(g, v); 296 api_const_set_top(g, result_const); 297 return; 298 } 299 if (api_sv_op_is(&v, OPK_IMM)) { 300 i64 folded; 301 if (api_try_fold_int_convert(g, ck, sty, dty, v.op.v.imm, &folded)) { 302 api_release(g, &v); 303 /* A folded split-lane 8-byte result must be memory-resident, not a bare 304 * i64 immediate the backend would truncate. */ 305 if (api_is_wide8_scalar_type(g->c, dty)) 306 api_push(g, api_make_wide8_int_const(g, folded, dty)); 307 else 308 api_push(g, api_make_sv(api_op_imm(folded, dty), dty)); 309 api_const_set_top(g, result_const); 310 return; 311 } 312 } 313 if (api_try_i128_convert(g, ck, sty, dty, &v)) { 314 api_const_set_top(g, result_const); 315 return; 316 } 317 if (api_try_wide8_convert(g, ck, sty, dty, &v)) { 318 api_const_set_top(g, result_const); 319 return; 320 } 321 /* A bitcast between two narrow types of equal width and register class — 322 * pointer<->pointer-width integer (the frontend's ptr_to_int/int_to_ptr that 323 * brackets every field-offset add) or pointer<->pointer — reinterprets the 324 * bits without moving them, so retype the value in place instead of forcing 325 * it to a fresh temp and emitting a register copy. int<->float of equal width 326 * is excluded: it crosses the GPR/FPR banks and needs a real fmov. The wide 327 * (i128 / split-lane) cases are already handled above. */ 328 /* On a machine-word target, a same-width / same-register-class bitcast moves 329 * no bits, so make the result value *be* the source, retyped — no temp, no 330 * register copy. This is a value-identity rewrite (the source is dead: it is 331 * released just below), which only the cg layer can do; the backend sees 332 * convert(dst, src) as two independent locals and cannot know src is 333 * substitutable. untyped_values reports the one backend-specific fact it 334 * needs — typed backends (the C emitter, the optimizer recorder) leave it 0 335 * and keep the explicit convert. OPK_IMM is excluded: a bitcast immediate 336 * that becomes an address base must reach a register (an INDIRECT base cannot 337 * be an immediate), an invariant the aggregate-arg lowering relies on. */ 338 if (ck == CV_BITCAST && T->untyped_values && v.op.kind != OPK_IMM) { 339 /* sty/dty are already unaliased; decode each id's predicate bitset once and 340 * read the aggregate/float bits locally instead of three fresh decodes. */ 341 u8 sty_bits = api_type_pred_bits(g->c, sty); 342 u8 dty_bits = api_type_pred_bits(g->c, dty); 343 if (!(sty_bits & API_PRED_AGGREGATE) && !(dty_bits & API_PRED_AGGREGATE) && 344 abi_cg_sizeof(g->c->abi, sty) == abi_cg_sizeof(g->c->abi, dty) && 345 !(sty_bits & API_PRED_FLOAT) == !(dty_bits & API_PRED_FLOAT)) { 346 v.type = dty; 347 v.op.type = dty; 348 api_push(g, v); 349 api_const_set_top(g, result_const); 350 return; 351 } 352 } 353 if (ck == CV_BITCAST && abi_cg_sizeof(g->c->abi, sty) == 16 && 354 abi_cg_sizeof(g->c->abi, dty) == 16 && 355 (api_is_f128_type(g->c, sty) || api_is_f128_type(g->c, dty))) { 356 CGLocal local = api_f128_temp_local(g, dty); 357 Operand dst_lv = api_op_local(local, dty); 358 if (api_is_lvalue_sv(&v) || v.op.kind == OPK_LOCAL || 359 v.op.kind == OPK_INDIRECT || v.op.kind == OPK_GLOBAL) { 360 KitCgTypeId ptr_ty = cg_type_ptr_to(g->c, dty); 361 ApiSValue src_lv = v; 362 Operand dst_addr; 363 Operand src_addr; 364 AggregateAccess agg; 365 ApiSValue dst_place = api_make_lv(dst_lv, dty); 366 api_sv_set_lvalue(&src_lv, 1); 367 dst_addr = api_lvalue_addr(g, &dst_place, ptr_ty); 368 src_addr = api_lvalue_addr(g, &src_lv, cg_type_ptr_to(g->c, sty)); 369 memset(&agg, 0, sizeof agg); 370 agg.size = 16; 371 agg.align = 16; 372 g->target->copy_bytes(g->target, dst_addr, src_addr, agg); 373 } else if (v.op.kind == OPK_LOCAL) { 374 g->target->store(g->target, dst_lv, v.op, 375 api_mem_for_lvalue(g, &dst_lv, sty)); 376 } else if (v.op.kind == OPK_IMM) { 377 u8 bytes[16]; 378 u64 lo = (u64)v.op.v.imm; 379 memset(bytes, 0, sizeof bytes); 380 for (u32 i = 0; i < 8; ++i) { 381 u32 idx = g->c->target.big_endian ? 15u - i : i; 382 bytes[idx] = (u8)(lo >> (i * 8u)); 383 } 384 api_store_f128_bytes(g, local, dty, bytes); 385 } else { 386 CG_BUG(g, "KitCg: unsupported 16-byte bitcast source"); 387 } 388 api_release(g, &v); 389 api_push(g, api_make_lv(dst_lv, dty)); 390 api_const_set_top(g, result_const); 391 return; 392 } 393 394 src = api_force_local(g, &v, sty); 395 rr = api_alloc_temp_local(g, dty); 396 dst = api_op_local(rr, dty); 397 /* If the source operand is a transient with no other live value-stack 398 * reference (v, its last reference, was already popped), flag it dead so the 399 * -O0 backend can convert in place into the source's register instead of 400 * routing through a fresh one — and drop a redundant extend entirely. */ 401 if (api_coalesce_on(g) && src.kind == OPK_LOCAL && 402 api_temp_dead(g, src.v.local)) 403 src.flags |= OPK_FLAG_KILL; 404 T->convert(T, ck, dst, src); 405 api_release(g, &v); 406 api_push(g, api_make_sv(dst, dty)); 407 api_const_set_top(g, result_const); 408 } 409 410 /* ============================================================ 411 * 128-bit integer lowering 412 * 413 * i128/u128 are 16-byte memory-resident scalars (see api_is_wide16 414 * and src/cg/wide.c). The native backends only model <=64-bit 415 * register ops, so every i128 arithmetic/compare/convert is lowered 416 * here to a compiler-rt-style runtime call (rt/lib/int64). This 417 * mirrors the f128 dispatch in kit_cg_fp_*. 418 * ============================================================ */ 419 420 /* The wide / soft-float dispatch class cached on the value-stack node at 421 * `depth` below TOS (api_push stamps it from api_type_class). The five 422 * stack-top predicates below are now a bounds check plus a tag compare; the 423 * type-get + alias-chase gauntlet that used to run per operand happens once at 424 * push instead. WK_NARROW for an out-of-range depth keeps every predicate false 425 * exactly as the old bounds guards did. */ 426 static u8 api_stack_wide_kind(KitCg* g, u32 depth) { 427 if (!g || g->sp <= depth) return WK_NARROW; 428 return g->stack[g->sp - 1u - depth].bitfield.wide_kind; 429 } 430 431 int api_i128_stack_top(KitCg* g, u32 depth) { 432 return api_stack_wide_kind(g, depth) == WK_I128; 433 } 434 435 /* 64-bit integer split into two 32-bit lanes by the selected ABI. The native 436 * backend handles add/sub/and/or/xor on such values as register pairs, but 437 * mul/div/shift must be lowered to a __*di3 runtime call (see 438 * api_wideint64_binop). i128 routes through its own ti3 path (api_i128_*), so 439 * the WK_WIDE8 class excludes it. */ 440 static int api_wide64_stack_top(KitCg* g, u32 depth) { 441 return api_stack_wide_kind(g, depth) == WK_WIDE8; 442 } 443 444 static int api_binop_is_shift(BinOp iop) { 445 return iop == BO_SHL || iop == BO_SHR_U || iop == BO_SHR_S; 446 } 447 448 static int api_is_bool_type(Compiler* c, KitCgTypeId ty) { 449 const CgType* cg = cg_type_get(c, ty); 450 return cg && cg->kind == KIT_CG_TYPE_BOOL; 451 } 452 453 /* Materialize an i128 value as an lvalue and return a pointer local to it. */ 454 static Operand api_i128_addr(KitCg* g, ApiSValue* v) { 455 KitCgTypeId i128 = builtin_id(KIT_CG_BUILTIN_I128); 456 ApiSValue lv = api_wide16_materialize_lvalue(g, v, i128); 457 return api_lvalue_addr(g, &lv, cg_type_ptr_to(g->c, i128)); 458 } 459 460 /* Load a 64-bit lane of an i128 (addressed by `addr`) into a fresh i64. */ 461 static Operand api_i128_load_lane(KitCg* g, Operand addr, i32 off) { 462 KitCgTypeId i64 = builtin_id(KIT_CG_BUILTIN_I64); 463 CGLocal rr = api_alloc_temp_local(g, i64); 464 Operand dst = api_op_local(rr, i64); 465 MemAccess ma; 466 memset(&ma, 0, sizeof ma); 467 ma.type = i64; 468 ma.size = 8; 469 ma.align = 8; 470 g->target->load(g->target, dst, api_op_indirect(addr.v.local, off, i64), ma); 471 return dst; 472 } 473 474 static void api_i128_binop(KitCg* g, BinOp iop) { 475 KitCgTypeId i128 = builtin_id(KIT_CG_BUILTIN_I128); 476 KitCgTypeId i32 = builtin_id(KIT_CG_BUILTIN_I32); 477 const char* name = api_i128_binop_helper(iop); 478 KitCgTypeId ps[2]; 479 ApiSValue args[2]; 480 if (!name) { 481 compiler_panic(g->c, g->cur_loc, "KitCg: unsupported i128 binop"); 482 return; 483 } 484 args[1] = api_pop(g); 485 args[0] = api_pop(g); 486 ps[0] = i128; 487 ps[1] = api_binop_is_shift(iop) ? i32 : i128; 488 api_runtime_call_values(g, name, i128, ps, 2, args); 489 } 490 491 /* Runtime helper name for a 64-bit-integer mul/div/rem/shift on a 32-bit 492 * target. Mirrors api_i128_binop_helper but with the compiler-rt *di3 names. 493 * Returns NULL for ops the inline backend handles (add/sub/and/or/xor). */ 494 static const char* api_wideint64_binop_helper(BinOp op) { 495 switch (op) { 496 case BO_IMUL: 497 return "__muldi3"; 498 case BO_SDIV: 499 return "__divdi3"; 500 case BO_UDIV: 501 return "__udivdi3"; 502 case BO_SREM: 503 return "__moddi3"; 504 case BO_UREM: 505 return "__umoddi3"; 506 case BO_SHL: 507 return "__ashldi3"; 508 case BO_SHR_U: 509 return "__lshrdi3"; 510 case BO_SHR_S: 511 return "__ashrdi3"; 512 default: 513 return NULL; 514 } 515 } 516 517 /* Lower a 64-bit mul/div/rem/shift to a runtime call. Mirrors api_i128_binop 518 * but ret/params are builtin i64; the shift-count param is i32 (the __ashldi3 519 * family takes (i64 value, i32 count) per compiler-rt). */ 520 static void api_wideint64_binop(KitCg* g, BinOp iop) { 521 KitCgTypeId i64 = builtin_id(KIT_CG_BUILTIN_I64); 522 KitCgTypeId i32 = builtin_id(KIT_CG_BUILTIN_I32); 523 const char* name = api_wideint64_binop_helper(iop); 524 KitCgTypeId ps[2]; 525 ApiSValue args[2]; 526 if (!name) { 527 compiler_panic(g->c, g->cur_loc, "KitCg: unsupported wide i64 binop"); 528 return; 529 } 530 args[1] = api_pop(g); 531 args[0] = api_pop(g); 532 ps[0] = i64; 533 ps[1] = api_binop_is_shift(iop) ? i32 : i64; 534 api_runtime_call_values(g, name, i64, ps, 2, args); 535 } 536 537 /* ============================================================ 538 * wide8 inline 2-word lane arithmetic 539 * 540 * Some 32-bit ABIs represent a 64-bit integer as a memory-resident 8-byte 541 * scalar split into two 32-bit lanes. add/sub/and/or/xor/neg/not and compares 542 * have no compiler-rt helper (they would recurse), so they are emitted INLINE 543 * here as lane ops. mul/div/rem/shift route to __*di3 (api_wideint64_*). 544 * ============================================================ */ 545 546 static i32 wide8_lo_off(KitCg* g) { return g->c->target.big_endian ? 4 : 0; } 547 static i32 wide8_hi_off(KitCg* g) { return g->c->target.big_endian ? 0 : 4; } 548 549 /* Emit one i32 binop into a fresh temp and return it. */ 550 static Operand wide8_i32_binop(KitCg* g, BinOp op, Operand a, Operand b) { 551 KitCgTypeId i32 = builtin_id(KIT_CG_BUILTIN_I32); 552 CGLocal r = api_alloc_temp_local(g, i32); 553 Operand d = api_op_local(r, i32); 554 g->target->binop(g->target, op, d, a, b); 555 return d; 556 } 557 558 /* Emit one i32 compare (0/1 result) into a fresh temp and return it. */ 559 static Operand wide8_i32_cmp(KitCg* g, CmpOp op, Operand a, Operand b) { 560 KitCgTypeId i32 = builtin_id(KIT_CG_BUILTIN_I32); 561 CGLocal r = api_alloc_temp_local(g, i32); 562 Operand d = api_op_local(r, i32); 563 g->target->cmp(g->target, op, d, a, b); 564 return d; 565 } 566 567 /* (lo | hi) of the 8-byte value `v` as an i32, for a truthiness test. Consumes 568 * nothing on the value stack (caller owns *v). */ 569 Operand api_wide8_or_lanes(KitCg* g, ApiSValue* v, KitCgTypeId ty) { 570 Operand addr = api_wide8_addr(g, v, ty); 571 Operand lo = api_wide8_load_lane(g, addr, wide8_lo_off(g)); 572 Operand hi = api_wide8_load_lane(g, addr, wide8_hi_off(g)); 573 return wide8_i32_binop(g, BO_OR, lo, hi); 574 } 575 576 /* add/sub/and/or/xor on two 8-byte ints, result pushed as a fresh 8-byte value. 577 * add/sub carry/borrow through the high lane via an sltu (CMP_LT_U). */ 578 static void api_wide64_binop_inline(KitCg* g, BinOp iop) { 579 ApiSValue b = api_pop(g); 580 ApiSValue a = api_pop(g); 581 KitCgTypeId ty = a.type ? a.type : b.type; 582 int lo = wide8_lo_off(g), hi = wide8_hi_off(g); 583 Operand aa = api_wide8_addr(g, &a, ty); 584 Operand ab = api_wide8_addr(g, &b, ty); 585 Operand alo = api_wide8_load_lane(g, aa, lo); 586 Operand ahi = api_wide8_load_lane(g, aa, hi); 587 Operand blo = api_wide8_load_lane(g, ab, lo); 588 Operand bhi = api_wide8_load_lane(g, ab, hi); 589 CGLocal res = api_wide8_temp_local(g, ty); 590 ApiSValue res_lv = api_make_lv(api_op_local(res, ty), ty); 591 Operand ar = api_lvalue_addr(g, &res_lv, cg_type_ptr_to(g->c, ty)); 592 Operand rlo; 593 Operand rhi; 594 switch (iop) { 595 case BO_AND: 596 case BO_OR: 597 case BO_XOR: 598 rlo = wide8_i32_binop(g, iop, alo, blo); 599 rhi = wide8_i32_binop(g, iop, ahi, bhi); 600 break; 601 case BO_IADD: { 602 Operand carry; 603 rlo = wide8_i32_binop(g, BO_IADD, alo, blo); 604 carry = wide8_i32_cmp(g, CMP_LT_U, rlo, alo); /* unsigned wrap -> carry */ 605 rhi = wide8_i32_binop(g, BO_IADD, ahi, bhi); 606 rhi = wide8_i32_binop(g, BO_IADD, rhi, carry); 607 break; 608 } 609 case BO_ISUB: { 610 Operand borrow = wide8_i32_cmp(g, CMP_LT_U, alo, blo); 611 rlo = wide8_i32_binop(g, BO_ISUB, alo, blo); 612 rhi = wide8_i32_binop(g, BO_ISUB, ahi, bhi); 613 rhi = wide8_i32_binop(g, BO_ISUB, rhi, borrow); 614 break; 615 } 616 default: 617 compiler_panic(g->c, g->cur_loc, 618 "KitCg: unsupported wide i64 inline binop"); 619 return; 620 } 621 api_wide8_store_lane(g, ar, lo, rlo); 622 api_wide8_store_lane(g, ar, hi, rhi); 623 api_release(g, &a); 624 api_release(g, &b); 625 api_push(g, api_make_sv(api_op_local(res, ty), ty)); 626 } 627 628 /* neg / bnot on an 8-byte int. NEG is two's complement: lo = 0-lo with borrow 629 * into hi = 0-hi-borrow. BNOT is lane-wise xor -1. */ 630 static void api_wide64_unop_inline(KitCg* g, UnOp iop) { 631 KitCgTypeId i32 = builtin_id(KIT_CG_BUILTIN_I32); 632 ApiSValue a = api_pop(g); 633 KitCgTypeId ty = a.type ? a.type : a.op.type; 634 int lo = wide8_lo_off(g), hi = wide8_hi_off(g); 635 Operand aa = api_wide8_addr(g, &a, ty); 636 Operand alo = api_wide8_load_lane(g, aa, lo); 637 Operand ahi = api_wide8_load_lane(g, aa, hi); 638 CGLocal res = api_wide8_temp_local(g, ty); 639 ApiSValue res_lv = api_make_lv(api_op_local(res, ty), ty); 640 Operand ar = api_lvalue_addr(g, &res_lv, cg_type_ptr_to(g->c, ty)); 641 Operand rlo; 642 Operand rhi; 643 if (iop == UO_BNOT) { 644 rlo = wide8_i32_binop(g, BO_XOR, alo, api_op_imm(-1, i32)); 645 rhi = wide8_i32_binop(g, BO_XOR, ahi, api_op_imm(-1, i32)); 646 } else { /* UO_NEG: 0 - value */ 647 Operand zero = api_op_imm(0, i32); 648 Operand borrow = wide8_i32_cmp(g, CMP_LT_U, zero, alo); /* 0<lo -> borrow */ 649 rlo = wide8_i32_binop(g, BO_ISUB, zero, alo); 650 rhi = wide8_i32_binop(g, BO_ISUB, zero, ahi); 651 rhi = wide8_i32_binop(g, BO_ISUB, rhi, borrow); 652 } 653 api_wide8_store_lane(g, ar, lo, rlo); 654 api_wide8_store_lane(g, ar, hi, rhi); 655 api_release(g, &a); 656 api_push(g, api_make_sv(api_op_local(res, ty), ty)); 657 } 658 659 /* a < b over 8-byte lanes: (a_hi <{s,u} b_hi) | (a_hi==b_hi & a_lo <u b_lo). 660 * The high lane uses the signed/unsigned relation; the low lane is always 661 * unsigned. Returns an i32 0/1. */ 662 static Operand wide8_lt(KitCg* g, int is_signed, Operand alo, Operand ahi, 663 Operand blo, Operand bhi) { 664 Operand hi_lt = wide8_i32_cmp(g, is_signed ? CMP_LT_S : CMP_LT_U, ahi, bhi); 665 Operand hi_eq = wide8_i32_cmp(g, CMP_EQ, ahi, bhi); 666 Operand lo_lt = wide8_i32_cmp(g, CMP_LT_U, alo, blo); 667 Operand t = wide8_i32_binop(g, BO_AND, hi_eq, lo_lt); 668 return wide8_i32_binop(g, BO_OR, hi_lt, t); 669 } 670 671 static Operand wide8_eq(KitCg* g, Operand alo, Operand ahi, Operand blo, 672 Operand bhi) { 673 KitCgTypeId i32 = builtin_id(KIT_CG_BUILTIN_I32); 674 Operand dlo = wide8_i32_binop(g, BO_XOR, alo, blo); 675 Operand dhi = wide8_i32_binop(g, BO_XOR, ahi, bhi); 676 Operand diff = wide8_i32_binop(g, BO_OR, dlo, dhi); 677 return wide8_i32_cmp(g, CMP_EQ, diff, api_op_imm(0, i32)); 678 } 679 680 static int cmp_is_signed_rel(CmpOp op) { 681 return op == CMP_LT_S || op == CMP_LE_S || op == CMP_GT_S || op == CMP_GE_S; 682 } 683 684 /* 8-byte int compare -> eager i32 0/1 value (not a delayed SV_CMP). */ 685 static void api_wide64_cmp_inline(KitCg* g, CmpOp cop) { 686 KitCgTypeId i32 = builtin_id(KIT_CG_BUILTIN_I32); 687 int sg = cmp_is_signed_rel(cop); 688 ApiSValue b = api_pop(g); 689 ApiSValue a = api_pop(g); 690 KitCgTypeId ty = a.type ? a.type : b.type; 691 int lo = wide8_lo_off(g), hi = wide8_hi_off(g); 692 Operand aa = api_wide8_addr(g, &a, ty); 693 Operand ab = api_wide8_addr(g, &b, ty); 694 Operand alo = api_wide8_load_lane(g, aa, lo); 695 Operand ahi = api_wide8_load_lane(g, aa, hi); 696 Operand blo = api_wide8_load_lane(g, ab, lo); 697 Operand bhi = api_wide8_load_lane(g, ab, hi); 698 Operand one = api_op_imm(1, i32); 699 Operand res; 700 switch (cop) { 701 case CMP_EQ: 702 res = wide8_eq(g, alo, ahi, blo, bhi); 703 break; 704 case CMP_NE: 705 res = wide8_i32_binop(g, BO_XOR, wide8_eq(g, alo, ahi, blo, bhi), one); 706 break; 707 case CMP_LT_S: 708 case CMP_LT_U: 709 res = wide8_lt(g, sg, alo, ahi, blo, bhi); 710 break; 711 case CMP_GT_S: 712 case CMP_GT_U: /* a>b == b<a */ 713 res = wide8_lt(g, sg, blo, bhi, alo, ahi); 714 break; 715 case CMP_LE_S: 716 case CMP_LE_U: /* a<=b == !(b<a) */ 717 res = 718 wide8_i32_binop(g, BO_XOR, wide8_lt(g, sg, blo, bhi, alo, ahi), one); 719 break; 720 case CMP_GE_S: 721 case CMP_GE_U: /* a>=b == !(a<b) */ 722 res = 723 wide8_i32_binop(g, BO_XOR, wide8_lt(g, sg, alo, ahi, blo, bhi), one); 724 break; 725 default: 726 compiler_panic(g->c, g->cur_loc, "KitCg: unsupported wide i64 compare"); 727 return; 728 } 729 api_release(g, &a); 730 api_release(g, &b); 731 api_push(g, api_make_sv(res, i32)); 732 } 733 734 /* ============================================================ 735 * wide64 __builtin_*_overflow on split-lane 64-bit values 736 * 737 * The native backends only model single-register overflow, so a 64-bit 738 * operand traps there. Here we legalize the 6 overflow intrinsics for a 739 * 64-bit operand pair into 32-bit lane ops, computing both the 740 * 64-bit wrapped value (stored to a fresh 8-byte temp) and the boolean 741 * overflow flag, then pushing [value, ok] exactly as the native path does. 742 * add/sub reuse the carry/borrow lane logic; mul builds the full 128-bit 743 * product from 32x32->64 partials (no MULHU opcode exists, so each partial 744 * is itself synthesized from 16-bit halves). 745 * ============================================================ */ 746 747 /* Unsigned 32x32 -> 64 product of i32 lanes a,b, returned as (*plo,*phi) i32 748 * via the 16-bit-halves schoolbook method (the target has no high-multiply 749 * opcode, and a plain BO_IMUL only yields the low 32 bits). 750 * 751 * a = ah*2^16 + al, b = bh*2^16 + bl 752 * a*b = ah*bh*2^32 + (ah*bl + al*bh)*2^16 + al*bl 753 */ 754 static void wide8_umul32(KitCg* g, Operand a, Operand b, Operand* plo, 755 Operand* phi) { 756 KitCgTypeId i32 = builtin_id(KIT_CG_BUILTIN_I32); 757 Operand mask = api_op_imm(0xffff, i32); 758 Operand sh16 = api_op_imm(16, i32); 759 Operand al = wide8_i32_binop(g, BO_AND, a, mask); 760 Operand ah = wide8_i32_binop(g, BO_SHR_U, a, sh16); 761 Operand bl = wide8_i32_binop(g, BO_AND, b, mask); 762 Operand bh = wide8_i32_binop(g, BO_SHR_U, b, sh16); 763 Operand ll = 764 wide8_i32_binop(g, BO_IMUL, al, bl); /* bits 0..31 (<=32 bits) */ 765 Operand lh = wide8_i32_binop(g, BO_IMUL, al, bh); /* bits 16..47 */ 766 Operand hl = wide8_i32_binop(g, BO_IMUL, ah, bl); /* bits 16..47 */ 767 Operand hh = wide8_i32_binop(g, BO_IMUL, ah, bh); /* bits 32..63 */ 768 /* mid = lh + hl + (ll >> 16); a 33-bit sum -> track carry into bit 32. */ 769 Operand ll_hi = wide8_i32_binop(g, BO_SHR_U, ll, sh16); 770 Operand mid = wide8_i32_binop(g, BO_IADD, lh, hl); 771 /* carry out of (lh+hl) into bit 48 (i.e. +2^32 in the high word). */ 772 Operand c0 = wide8_i32_cmp(g, CMP_LT_U, mid, lh); 773 Operand mid2 = wide8_i32_binop(g, BO_IADD, mid, ll_hi); 774 Operand c1 = wide8_i32_cmp(g, CMP_LT_U, mid2, mid); 775 Operand carry32 = wide8_i32_binop(g, BO_IADD, c0, c1); /* into high word */ 776 /* lo = (mid2 << 16) | (ll & 0xffff) */ 777 Operand mid2_lo = wide8_i32_binop(g, BO_AND, mid2, mask); 778 Operand mid2_loshift = wide8_i32_binop(g, BO_SHL, mid2_lo, sh16); 779 Operand ll_lo = wide8_i32_binop(g, BO_AND, ll, mask); 780 *plo = wide8_i32_binop(g, BO_OR, mid2_loshift, ll_lo); 781 /* hi = hh + (mid2 >> 16) + carry32*2^16 */ 782 Operand mid2_hi = wide8_i32_binop(g, BO_SHR_U, mid2, sh16); 783 Operand carry_word = wide8_i32_binop(g, BO_SHL, carry32, sh16); 784 Operand hi = wide8_i32_binop(g, BO_IADD, hh, mid2_hi); 785 *phi = wide8_i32_binop(g, BO_IADD, hi, carry_word); 786 } 787 788 /* Add three i32 columns acc += addend, threading carry: returns the new sum and 789 * adds the unsigned-wrap carry (0/1) into *carry. */ 790 static Operand wide8_addc(KitCg* g, Operand acc, Operand addend, 791 Operand* carry) { 792 Operand sum = wide8_i32_binop(g, BO_IADD, acc, addend); 793 Operand c = wide8_i32_cmp(g, CMP_LT_U, sum, acc); 794 *carry = wide8_i32_binop(g, BO_IADD, *carry, c); 795 return sum; 796 } 797 798 /* The 6 __builtin_*_overflow intrinsics for a split-lane wide64 operand pair. 799 * Pops the two 8-byte args, computes the wrapped 64-bit value into a fresh 800 * 8-byte temp and the bool overflow flag into an i32, then pushes [value, ok] 801 * matching the contract of the native overflow path. */ 802 static void api_wide64_overflow_inline(KitCg* g, KitCgIntrinsic intrin) { 803 KitCgTypeId i32 = builtin_id(KIT_CG_BUILTIN_I32); 804 KitCgTypeId bool_ty = builtin_id(KIT_CG_BUILTIN_BOOL); 805 Operand sh31 = api_op_imm(31, i32); 806 ApiSValue b = api_pop(g); 807 ApiSValue a = api_pop(g); 808 KitCgTypeId ty = a.type ? a.type : b.type; 809 int lo = wide8_lo_off(g), hi = wide8_hi_off(g); 810 Operand aa = api_wide8_addr(g, &a, ty); 811 Operand ab = api_wide8_addr(g, &b, ty); 812 Operand alo = api_wide8_load_lane(g, aa, lo); 813 Operand ahi = api_wide8_load_lane(g, aa, hi); 814 Operand blo = api_wide8_load_lane(g, ab, lo); 815 Operand bhi = api_wide8_load_lane(g, ab, hi); 816 CGLocal res = api_wide8_temp_local(g, ty); 817 ApiSValue res_lv = api_make_lv(api_op_local(res, ty), ty); 818 Operand ar = api_lvalue_addr(g, &res_lv, cg_type_ptr_to(g->c, ty)); 819 Operand rlo; 820 Operand rhi; 821 Operand ok; 822 switch (intrin) { 823 case KIT_CG_INTRIN_UADD_OVERFLOW: 824 case KIT_CG_INTRIN_SADD_OVERFLOW: { 825 Operand carry; 826 rlo = wide8_i32_binop(g, BO_IADD, alo, blo); 827 carry = wide8_i32_cmp(g, CMP_LT_U, rlo, alo); 828 rhi = wide8_i32_binop(g, BO_IADD, ahi, bhi); 829 /* carry-out of the high lane = (rhi<ahi) before +carry, OR wrap on 830 * +carry. Compute rhi step by step so we can detect the final carry-out. 831 */ 832 Operand c_hi0 = wide8_i32_cmp(g, CMP_LT_U, rhi, ahi); 833 rhi = wide8_i32_binop(g, BO_IADD, rhi, carry); 834 Operand c_hi1 = wide8_i32_cmp(g, CMP_LT_U, rhi, carry); 835 if (intrin == KIT_CG_INTRIN_UADD_OVERFLOW) { 836 /* unsigned: ok = carry-out of the high lane */ 837 ok = wide8_i32_binop(g, BO_OR, c_hi0, c_hi1); 838 } else { 839 /* signed: ok = ((a_hi ^ r_hi) & (b_hi ^ r_hi)) sign bit (bit 31) */ 840 Operand ar_x = wide8_i32_binop(g, BO_XOR, ahi, rhi); 841 Operand br_x = wide8_i32_binop(g, BO_XOR, bhi, rhi); 842 Operand both = wide8_i32_binop(g, BO_AND, ar_x, br_x); 843 ok = wide8_i32_binop(g, BO_SHR_U, both, sh31); 844 } 845 break; 846 } 847 case KIT_CG_INTRIN_USUB_OVERFLOW: 848 case KIT_CG_INTRIN_SSUB_OVERFLOW: { 849 Operand borrow = wide8_i32_cmp(g, CMP_LT_U, alo, blo); 850 rlo = wide8_i32_binop(g, BO_ISUB, alo, blo); 851 Operand t = wide8_i32_binop(g, BO_ISUB, ahi, bhi); 852 /* high-lane borrow-out: (ahi < bhi) OR (t < borrow after subtracting). */ 853 Operand b_hi0 = wide8_i32_cmp(g, CMP_LT_U, ahi, bhi); 854 Operand b_hi1 = wide8_i32_cmp(g, CMP_LT_U, t, borrow); 855 rhi = wide8_i32_binop(g, BO_ISUB, t, borrow); 856 if (intrin == KIT_CG_INTRIN_USUB_OVERFLOW) { 857 ok = wide8_i32_binop(g, BO_OR, b_hi0, b_hi1); 858 } else { 859 /* signed: ok = ((a_hi ^ b_hi) & (a_hi ^ r_hi)) sign bit (bit 31) */ 860 Operand ab_x = wide8_i32_binop(g, BO_XOR, ahi, bhi); 861 Operand ar_x = wide8_i32_binop(g, BO_XOR, ahi, rhi); 862 Operand both = wide8_i32_binop(g, BO_AND, ab_x, ar_x); 863 ok = wide8_i32_binop(g, BO_SHR_U, both, sh31); 864 } 865 break; 866 } 867 case KIT_CG_INTRIN_UMUL_OVERFLOW: 868 case KIT_CG_INTRIN_SMUL_OVERFLOW: { 869 int is_signed = (intrin == KIT_CG_INTRIN_SMUL_OVERFLOW); 870 /* For signed, compute |a|,|b| as unsigned 64-bit, do the unsigned 128-bit 871 * product, then apply the result sign. Overflow tests below use the 872 * unsigned magnitude product plus the expected sign. */ 873 Operand ua_lo = alo, ua_hi = ahi, ub_lo = blo, ub_hi = bhi; 874 Operand sgn = (Operand){0}; 875 if (is_signed) { 876 /* a_sign = ahi >> 31 (0 or 1 in i32, but as a mask we want -1/0). */ 877 Operand am = wide8_i32_binop(g, BO_SHR_S, ahi, sh31); /* 0 or -1 */ 878 Operand bm = wide8_i32_binop(g, BO_SHR_S, bhi, sh31); 879 /* |a| = (a ^ am) - am (two's-complement abs), lane-wise w/ borrow. */ 880 Operand axl = wide8_i32_binop(g, BO_XOR, alo, am); 881 Operand axh = wide8_i32_binop(g, BO_XOR, ahi, am); 882 Operand brwa = wide8_i32_cmp(g, CMP_LT_U, axl, am); 883 ua_lo = wide8_i32_binop(g, BO_ISUB, axl, am); 884 Operand tah = wide8_i32_binop(g, BO_ISUB, axh, am); 885 ua_hi = wide8_i32_binop(g, BO_ISUB, tah, brwa); 886 Operand bxl = wide8_i32_binop(g, BO_XOR, blo, bm); 887 Operand bxh = wide8_i32_binop(g, BO_XOR, bhi, bm); 888 Operand brwb = wide8_i32_cmp(g, CMP_LT_U, bxl, bm); 889 ub_lo = wide8_i32_binop(g, BO_ISUB, bxl, bm); 890 Operand tbh = wide8_i32_binop(g, BO_ISUB, bxh, bm); 891 ub_hi = wide8_i32_binop(g, BO_ISUB, tbh, brwb); 892 sgn = wide8_i32_binop(g, BO_XOR, am, bm); /* result sign mask -1/0 */ 893 } 894 /* Unsigned 128-bit product of (ua_hi:ua_lo) * (ub_hi:ub_lo). 895 * P00 = ua_lo*ub_lo -> columns 0,1 896 * P01 = ua_lo*ub_hi -> columns 1,2 897 * P10 = ua_hi*ub_lo -> columns 1,2 898 * P11 = ua_hi*ub_hi -> columns 2,3 */ 899 Operand p00l, p00h, p01l, p01h, p10l, p10h, p11l, p11h; 900 wide8_umul32(g, ua_lo, ub_lo, &p00l, &p00h); 901 wide8_umul32(g, ua_lo, ub_hi, &p01l, &p01h); 902 wide8_umul32(g, ua_hi, ub_lo, &p10l, &p10h); 903 wide8_umul32(g, ua_hi, ub_hi, &p11l, &p11h); 904 Operand zero = api_op_imm(0, i32); 905 /* column 0 */ 906 Operand r0 = p00l; 907 /* column 1 = p00h + p01l + p10l */ 908 Operand c1 = zero; 909 Operand r1 = p00h; 910 r1 = wide8_addc(g, r1, p01l, &c1); 911 r1 = wide8_addc(g, r1, p10l, &c1); 912 /* column 2 = p01h + p10h + p11l + c1 */ 913 Operand c2 = zero; 914 Operand r2 = p01h; 915 r2 = wide8_addc(g, r2, p10h, &c2); 916 r2 = wide8_addc(g, r2, p11l, &c2); 917 r2 = wide8_addc(g, r2, c1, &c2); 918 /* column 3 = p11h + c2 */ 919 Operand r3 = wide8_i32_binop(g, BO_IADD, p11h, c2); 920 /* low 64 bits = (r1:r0); high 64 bits = (r3:r2). */ 921 Operand mlo = r0, mhi = r1; 922 Operand hi_lo = r2, hi_hi = r3; 923 if (is_signed) { 924 /* Apply result sign: negate the 128-bit magnitude if sgn==-1. 925 * negated = (x ^ sgn) - sgn across all 4 words with borrow. */ 926 Operand w0 = wide8_i32_binop(g, BO_XOR, mlo, sgn); 927 Operand w1 = wide8_i32_binop(g, BO_XOR, mhi, sgn); 928 Operand w2 = wide8_i32_binop(g, BO_XOR, hi_lo, sgn); 929 Operand w3 = wide8_i32_binop(g, BO_XOR, hi_hi, sgn); 930 Operand bor0 = wide8_i32_cmp(g, CMP_LT_U, w0, sgn); 931 mlo = wide8_i32_binop(g, BO_ISUB, w0, sgn); 932 Operand t1 = wide8_i32_binop(g, BO_ISUB, w1, sgn); 933 Operand bor1a = wide8_i32_cmp(g, CMP_LT_U, w1, sgn); 934 Operand bor1b = wide8_i32_cmp(g, CMP_LT_U, t1, bor0); 935 mhi = wide8_i32_binop(g, BO_ISUB, t1, bor0); 936 Operand bor1 = wide8_i32_binop(g, BO_OR, bor1a, bor1b); 937 Operand t2 = wide8_i32_binop(g, BO_ISUB, w2, sgn); 938 Operand bor2a = wide8_i32_cmp(g, CMP_LT_U, w2, sgn); 939 Operand bor2b = wide8_i32_cmp(g, CMP_LT_U, t2, bor1); 940 hi_lo = wide8_i32_binop(g, BO_ISUB, t2, bor1); 941 Operand bor2 = wide8_i32_binop(g, BO_OR, bor2a, bor2b); 942 Operand t3 = wide8_i32_binop(g, BO_ISUB, w3, sgn); 943 hi_hi = wide8_i32_binop(g, BO_ISUB, t3, bor2); 944 } 945 rlo = mlo; 946 rhi = mhi; 947 if (!is_signed) { 948 /* unsigned overflow: high 64 bits nonzero. */ 949 Operand t = wide8_i32_binop(g, BO_OR, hi_lo, hi_hi); 950 ok = wide8_i32_cmp(g, CMP_NE, t, zero); 951 } else { 952 /* signed overflow: the 128-bit result is not the sign-extension of its 953 * low 64 bits. sext = (rhi >> 31) replicated; overflow if 954 * (hi_lo != sext) | (hi_hi != sext) where sext = arithmetic sign of 955 * the signed low-64 result (bit 63 = rhi sign). */ 956 Operand sext = wide8_i32_binop(g, BO_SHR_S, rhi, sh31); /* 0 or -1 */ 957 Operand d2 = wide8_i32_binop(g, BO_XOR, hi_lo, sext); 958 Operand d3 = wide8_i32_binop(g, BO_XOR, hi_hi, sext); 959 Operand d = wide8_i32_binop(g, BO_OR, d2, d3); 960 ok = wide8_i32_cmp(g, CMP_NE, d, zero); 961 } 962 break; 963 } 964 default: 965 compiler_panic(g->c, g->cur_loc, 966 "KitCg: unsupported wide i64 overflow intrinsic"); 967 api_release(g, &a); 968 api_release(g, &b); 969 return; 970 } 971 api_wide8_store_lane(g, ar, lo, rlo); 972 api_wide8_store_lane(g, ar, hi, rhi); 973 api_release(g, &a); 974 api_release(g, &b); 975 /* Materialize ok as a fresh bool temp so it has a stable home. */ 976 { 977 CGLocal okl = api_alloc_temp_local(g, bool_ty); 978 Operand okd = api_op_local(okl, bool_ty); 979 g->target->binop(g->target, BO_AND, okd, ok, api_op_imm(1, i32)); 980 api_push(g, api_make_sv(api_op_local(res, ty), ty)); 981 api_push(g, api_make_sv(okd, bool_ty)); 982 } 983 } 984 985 /* int<->split-i64 conversions (sext/zext/trunc/bitcast across the 4<->8 986 * boundary, and i64->bool). Returns 1 if it handled (and consumed) *v. The 987 * i64<->float conversions are routed to libcalls in kit_cg_*_to_float / 988 * kit_cg_float_to_* and never reach here. */ 989 int api_try_wide8_convert(KitCg* g, ConvKind ck, KitCgTypeId sty, 990 KitCgTypeId dty, ApiSValue* v) { 991 KitCgTypeId i32 = builtin_id(KIT_CG_BUILTIN_I32); 992 int s_wide = api_is_wide8_scalar_type(g->c, sty); 993 int d_wide = api_is_wide8_scalar_type(g->c, dty); 994 int lo = wide8_lo_off(g), hi = wide8_hi_off(g); 995 if (!s_wide && !d_wide) return 0; 996 if (s_wide && d_wide) { 997 /* i64<->soft-double reinterpret (same 8-byte layout) or i64<->u64. */ 998 v->type = dty; 999 v->op.type = dty; 1000 api_push(g, *v); 1001 return 1; 1002 } 1003 if (d_wide) { 1004 /* narrower int -> i64: low lane is the (converted-to-i32) source; high lane 1005 * is the sign-extension (CV_SEXT) or zero (CV_ZEXT/CV_BITCAST of a ptr). */ 1006 int sext = (ck == CV_SEXT); 1007 Operand src32; 1008 CGLocal res; 1009 ApiSValue res_lv; 1010 Operand ar; 1011 Operand hival; 1012 if (sty != i32) { 1013 api_push(g, *v); 1014 api_cg_convert_kind(g, i32, ck == CV_SEXT ? CV_SEXT : CV_ZEXT); 1015 *v = api_pop(g); 1016 } 1017 src32 = api_force_local(g, v, i32); 1018 res = api_wide8_temp_local(g, dty); 1019 res_lv = api_make_lv(api_op_local(res, dty), dty); 1020 ar = api_lvalue_addr(g, &res_lv, cg_type_ptr_to(g->c, dty)); 1021 api_wide8_store_lane(g, ar, lo, src32); 1022 if (sext) 1023 hival = wide8_i32_binop(g, BO_SHR_S, src32, api_op_imm(31, i32)); 1024 else 1025 hival = api_op_imm(0, i32); 1026 api_wide8_store_lane(g, ar, hi, hival); 1027 api_release(g, v); 1028 api_push(g, api_make_sv(api_op_local(res, dty), dty)); 1029 return 1; 1030 } 1031 /* s_wide: i64 -> narrower. _Bool is "any bit set"; else take the low lane and 1032 * truncate/convert further. */ 1033 if (api_is_bool_type(g->c, dty)) { 1034 Operand orl = api_wide8_or_lanes(g, v, sty); 1035 api_release(g, v); 1036 api_push(g, api_make_sv(orl, i32)); 1037 kit_cg_push_int(g, 0, i32); 1038 api_cg_cmp(g, CMP_NE); 1039 api_cg_convert_kind(g, dty, CV_TRUNC); 1040 return 1; 1041 } 1042 { 1043 Operand addr = api_wide8_addr(g, v, sty); 1044 Operand lolane = api_wide8_load_lane(g, addr, lo); 1045 api_release(g, v); 1046 api_push(g, api_make_sv(lolane, i32)); 1047 if (dty != i32) api_cg_convert_kind(g, dty, CV_TRUNC); 1048 return 1; 1049 } 1050 } 1051 1052 static void api_i128_unop(KitCg* g, UnOp iop) { 1053 KitCgTypeId i128 = builtin_id(KIT_CG_BUILTIN_I128); 1054 const char* name = NULL; 1055 ApiSValue args[1]; 1056 KitCgTypeId ps[1]; 1057 if (iop == UO_NEG) 1058 name = "__negti2"; 1059 else if (iop == UO_BNOT) 1060 name = "__kit_notti3"; 1061 else { 1062 compiler_panic(g->c, g->cur_loc, "KitCg: unsupported i128 unop"); 1063 return; 1064 } 1065 args[0] = api_pop(g); 1066 ps[0] = i128; 1067 api_runtime_call_values(g, name, i128, ps, 1, args); 1068 } 1069 1070 /* Map a relational op to the form used to compare a __kit_*cmpti2 1071 * result (-1/0/1, a signed i32) against zero. */ 1072 static CmpOp api_i128_cmp_vs_zero(CmpOp cop) { 1073 switch (cop) { 1074 case CMP_EQ: 1075 return CMP_EQ; 1076 case CMP_NE: 1077 return CMP_NE; 1078 case CMP_LT_S: 1079 case CMP_LT_U: 1080 return CMP_LT_S; 1081 case CMP_LE_S: 1082 case CMP_LE_U: 1083 return CMP_LE_S; 1084 case CMP_GT_S: 1085 case CMP_GT_U: 1086 return CMP_GT_S; 1087 case CMP_GE_S: 1088 case CMP_GE_U: 1089 return CMP_GE_S; 1090 default: 1091 return CMP_NE; 1092 } 1093 } 1094 1095 static void api_i128_cmp(KitCg* g, CmpOp cop) { 1096 KitCgTypeId i128 = builtin_id(KIT_CG_BUILTIN_I128); 1097 KitCgTypeId i32 = builtin_id(KIT_CG_BUILTIN_I32); 1098 const char* name = 1099 api_i128_cmp_is_unsigned(cop) ? "__kit_ucmpti2" : "__kit_cmpti2"; 1100 KitCgTypeId ps[2] = {i128, i128}; 1101 ApiSValue args[2]; 1102 args[1] = api_pop(g); 1103 args[0] = api_pop(g); 1104 api_runtime_call_values(g, name, i32, ps, 2, args); 1105 kit_cg_push_int(g, 0, i32); 1106 api_cg_cmp(g, api_i128_cmp_vs_zero(cop)); 1107 } 1108 1109 /* int<->i128 conversions. Returns 1 if it handled the conversion and 1110 * consumed *v, 0 to fall through to the generic path. */ 1111 int api_try_i128_convert(KitCg* g, ConvKind ck, KitCgTypeId sty, 1112 KitCgTypeId dty, ApiSValue* v) { 1113 KitCgTypeId i128 = builtin_id(KIT_CG_BUILTIN_I128); 1114 KitCgTypeId i64 = builtin_id(KIT_CG_BUILTIN_I64); 1115 int s_is_128 = api_is_i128_type(g->c, sty); 1116 int d_is_128 = api_is_i128_type(g->c, dty); 1117 if (!s_is_128 && !d_is_128) return 0; 1118 if (s_is_128 && d_is_128) { 1119 /* signed<->unsigned i128 reinterpret: identical layout. */ 1120 v->type = dty; 1121 v->op.type = dty; 1122 api_push(g, *v); 1123 return 1; 1124 } 1125 if (d_is_128) { 1126 u32 sw = kit_cg_type_int_width((KitCompiler*)g->c, sty); 1127 const char* name = (ck == CV_SEXT) ? "__kit_sext64ti" : "__kit_zext64ti"; 1128 ApiSValue arg; 1129 KitCgTypeId ps[1]; 1130 if (sw == 0) return 0; /* float->i128 unsupported here */ 1131 if (sw >= 64) { 1132 arg = *v; 1133 arg.type = i64; 1134 arg.op.type = i64; 1135 } else { 1136 api_push(g, *v); 1137 api_cg_convert_kind(g, i64, ck); 1138 arg = api_pop(g); 1139 } 1140 ps[0] = i64; 1141 api_runtime_call_values(g, name, i128, ps, 1, &arg); 1142 return 1; 1143 } 1144 /* s_is_128, dty is _Bool: "value != 0" over the full 128 bits, not a 1145 * low-lane truncation (a value whose only set bits are above bit 63 must 1146 * still become 1). Reuse the runtime i128 compare. */ 1147 if (api_is_bool_type(g->c, dty)) { 1148 api_push(g, *v); 1149 kit_cg_push_int(g, 0, i128); 1150 api_i128_cmp(g, CMP_NE); /* leaves i32 0/1 */ 1151 api_cg_convert_kind(g, dty, CV_TRUNC); 1152 return 1; 1153 } 1154 /* s_is_128, dty is a narrower integer: take the low 64 bits, then 1155 * truncate further if needed. */ 1156 { 1157 u32 dw = kit_cg_type_int_width((KitCompiler*)g->c, dty); 1158 i32 lo_off = g->c->target.big_endian ? 8 : 0; 1159 Operand addr; 1160 Operand lo; 1161 if (dw == 0) return 0; /* i128->float unsupported here */ 1162 addr = api_i128_addr(g, v); 1163 lo = api_i128_load_lane(g, addr, lo_off); 1164 api_release(g, v); 1165 if (dw >= 64) { 1166 api_push(g, api_make_sv(lo, dty)); 1167 } else { 1168 api_push(g, api_make_sv(lo, i64)); 1169 api_cg_convert_kind(g, dty, CV_TRUNC); 1170 } 1171 return 1; 1172 } 1173 } 1174 1175 void kit_cg_int_binop(KitCg* g, KitCgIntBinOp op, uint32_t flags) { 1176 BinOp iop = api_map_int_binop(op); 1177 ApiConstValue result_const; 1178 KitCgTypeId ty = KIT_CG_TYPE_NONE; 1179 if (g && g->sp >= 2u) { 1180 ApiConstValue cb = api_const_at(g, 0); 1181 ApiConstValue ca = api_const_at(g, 1); 1182 ty = api_sv_type(&g->stack[g->sp - 2u]); 1183 api_const_fold_binop(g, iop, ty, ca, cb, flags, &result_const); 1184 } else { 1185 result_const = api_const_unknown(KIT_CG_TYPE_NONE); 1186 } 1187 if (api_unevaluated(g)) { 1188 api_cg_binop(g, iop, flags); 1189 return; 1190 } 1191 if (g && (api_i128_stack_top(g, 0) || api_i128_stack_top(g, 1))) { 1192 api_i128_binop(g, iop); 1193 api_const_set_top(g, result_const); 1194 return; 1195 } 1196 /* 64-bit int split into 32-bit lanes: mul/div/rem/shift become __*di3 1197 * runtime calls; add/sub/and/or/xor are emitted inline as 2-word lane ops 1198 * (no compiler-rt helper exists for them). Both keep the value 1199 * memory-resident so the allocator never tries to put 8 bytes in one 4-byte 1200 * value slot. */ 1201 if (g && (api_wide64_stack_top(g, 0) || api_wide64_stack_top(g, 1))) { 1202 if (api_wideint64_binop_helper(iop)) 1203 api_wideint64_binop(g, iop); 1204 else 1205 api_wide64_binop_inline(g, iop); 1206 api_const_set_top(g, result_const); 1207 return; 1208 } 1209 api_cg_binop(g, iop, flags); 1210 } 1211 1212 void kit_cg_int_unop(KitCg* g, KitCgIntUnOp op, uint32_t flags) { 1213 UnOp iop = api_map_int_unop(op); 1214 ApiConstValue result_const; 1215 KitCgTypeId ty = KIT_CG_TYPE_NONE; 1216 if (g && g->sp >= 1u) { 1217 ApiConstValue ca = api_const_at(g, 0); 1218 ty = api_sv_type(&g->stack[g->sp - 1u]); 1219 api_const_fold_unop(g, iop, ty, ca, flags, &result_const); 1220 } else { 1221 result_const = api_const_unknown(KIT_CG_TYPE_NONE); 1222 } 1223 if (api_unevaluated(g)) { 1224 api_cg_unop(g, iop, flags); 1225 return; 1226 } 1227 if (g && api_i128_stack_top(g, 0) && (iop == UO_NEG || iop == UO_BNOT)) { 1228 api_i128_unop(g, iop); 1229 api_const_set_top(g, result_const); 1230 return; 1231 } 1232 /* Split 64-bit int: neg/bnot are inline 2-word lane ops; logical-not (!x) is 1233 * the full-value truthiness test (lo|hi)==0. */ 1234 if (g && api_wide64_stack_top(g, 0)) { 1235 if (iop == UO_NEG || iop == UO_BNOT) { 1236 api_wide64_unop_inline(g, iop); 1237 api_const_set_top(g, result_const); 1238 return; 1239 } 1240 if (iop == UO_NOT) { 1241 KitCgTypeId i32 = builtin_id(KIT_CG_BUILTIN_I32); 1242 ApiSValue v = api_pop(g); 1243 KitCgTypeId ty = v.type ? v.type : v.op.type; 1244 Operand orl = api_wide8_or_lanes(g, &v, ty); 1245 api_release(g, &v); 1246 api_push(g, api_make_sv(orl, i32)); 1247 kit_cg_push_int(g, 0, i32); 1248 api_cg_cmp(g, CMP_EQ); 1249 api_cg_convert_kind(g, ty, CV_ZEXT); 1250 api_const_set_top(g, result_const); 1251 return; 1252 } 1253 } 1254 api_cg_unop(g, iop, flags); 1255 } 1256 1257 void kit_cg_int_cmp(KitCg* g, KitCgIntCmpOp op) { 1258 CmpOp cop = api_map_int_cmp(op); 1259 ApiConstValue result_const; 1260 if (g && g->sp >= 2u) { 1261 ApiConstValue cb = api_const_at(g, 0); 1262 ApiConstValue ca = api_const_at(g, 1); 1263 api_const_fold_cmp(g, cop, ca, cb, &result_const); 1264 } else { 1265 result_const = api_const_unknown(builtin_id(KIT_CG_BUILTIN_I32)); 1266 } 1267 if (api_unevaluated(g)) { 1268 api_cg_cmp(g, cop); 1269 return; 1270 } 1271 if (g && (api_i128_stack_top(g, 0) || api_i128_stack_top(g, 1))) { 1272 api_i128_cmp(g, cop); 1273 api_const_set_top(g, result_const); 1274 return; 1275 } 1276 if (g && (api_wide64_stack_top(g, 0) || api_wide64_stack_top(g, 1))) { 1277 api_wide64_cmp_inline(g, cop); 1278 api_const_set_top(g, result_const); 1279 return; 1280 } 1281 api_cg_cmp(g, cop); 1282 } 1283 1284 const char* api_i128_binop_helper(BinOp op) { 1285 switch (op) { 1286 case BO_IADD: 1287 return "__kit_addti3"; 1288 case BO_ISUB: 1289 return "__kit_subti3"; 1290 case BO_IMUL: 1291 return "__multi3"; 1292 case BO_SDIV: 1293 return "__divti3"; 1294 case BO_UDIV: 1295 return "__udivti3"; 1296 case BO_SREM: 1297 return "__modti3"; 1298 case BO_UREM: 1299 return "__umodti3"; 1300 case BO_AND: 1301 return "__kit_andti3"; 1302 case BO_OR: 1303 return "__kit_orti3"; 1304 case BO_XOR: 1305 return "__kit_xorti3"; 1306 case BO_SHL: 1307 return "__ashlti3"; 1308 case BO_SHR_U: 1309 return "__lshrti3"; 1310 case BO_SHR_S: 1311 return "__ashrti3"; 1312 case BO_FADD: 1313 case BO_FSUB: 1314 case BO_FMUL: 1315 case BO_FDIV: 1316 default: 1317 return NULL; 1318 } 1319 } 1320 1321 int api_i128_cmp_is_unsigned(CmpOp op) { 1322 return op == CMP_LT_U || op == CMP_LE_U || op == CMP_GT_U || op == CMP_GE_U; 1323 } 1324 1325 const char* api_f128_binop_helper(KitCgFpBinOp op) { 1326 switch (op) { 1327 case KIT_CG_FP_ADD: 1328 return "__addtf3"; 1329 case KIT_CG_FP_SUB: 1330 return "__subtf3"; 1331 case KIT_CG_FP_MUL: 1332 return "__multf3"; 1333 case KIT_CG_FP_DIV: 1334 return "__divtf3"; 1335 } 1336 return NULL; 1337 } 1338 1339 /* Runtime helper name for double (f64) arithmetic on a target that lacks a 1340 * hardware double unit. Mirrors api_f128_binop_helper with the __*df3 names. */ 1341 static const char* api_softdf_binop_helper(KitCgFpBinOp op) { 1342 switch (op) { 1343 case KIT_CG_FP_ADD: 1344 return "__adddf3"; 1345 case KIT_CG_FP_SUB: 1346 return "__subdf3"; 1347 case KIT_CG_FP_MUL: 1348 return "__muldf3"; 1349 case KIT_CG_FP_DIV: 1350 return "__divdf3"; 1351 } 1352 return NULL; 1353 } 1354 1355 int api_f128_stack_top(KitCg* g, u32 depth) { 1356 return api_stack_wide_kind(g, depth) == WK_F128; 1357 } 1358 1359 /* True when the target has no hardware double: float_abi is SOFT (ilp32/lp64, 1360 * no FP regs) or SINGLE (ilp32f/lp64f, only float in FP regs — double is always 1361 * soft). DOUBLE (rv64 lp64d) and DEFAULT (x64/aa64 hardware-double targets that 1362 * never set float_abi) keep the inline hardware path, so existing rv64/x64/aa64 1363 * codegen is unchanged. */ 1364 static int api_target_double_is_soft(KitCg* g) { 1365 if (!g) return 0; 1366 return g->c->target.float_abi == KIT_FLOAT_ABI_SOFT || 1367 g->c->target.float_abi == KIT_FLOAT_ABI_SINGLE; 1368 } 1369 1370 /* True when ty is a 64-bit float (double) AND the target lacks hardware double. 1371 * f128 is handled by the separate api_f128_* path, so width must be exactly 64. 1372 */ 1373 static int api_type_is_soft_double(KitCg* g, KitCgTypeId ty) { 1374 if (!api_target_double_is_soft(g)) return 0; 1375 return kit_cg_type_float_width((KitCompiler*)g->c, ty) == 64; 1376 } 1377 1378 static int api_soft_double_stack_top(KitCg* g, u32 depth) { 1379 return api_stack_wide_kind(g, depth) == WK_SOFT_DOUBLE; 1380 } 1381 1382 /* f32 under pure-soft ilp32/lp64 (float_abi SOFT, no FP unit): single-precision 1383 * arithmetic/compare/convert is also a libcall. Under SINGLE (ilp32f) float is 1384 * hardware (fadd.s etc.) so this is false; DOUBLE/DEFAULT keep hardware too. */ 1385 static int api_type_is_soft_single(KitCg* g, KitCgTypeId ty) { 1386 if (!g || g->c->target.float_abi != KIT_FLOAT_ABI_SOFT) return 0; 1387 return kit_cg_type_float_width((KitCompiler*)g->c, ty) == 32; 1388 } 1389 1390 static int api_soft_single_stack_top(KitCg* g, u32 depth) { 1391 return api_stack_wide_kind(g, depth) == WK_SOFT_SINGLE; 1392 } 1393 1394 /* Runtime helper for f32 arithmetic on a soft-float target (mirrors 1395 * api_softdf_binop_helper with the __*sf3 names). */ 1396 static const char* api_softsf_binop_helper(KitCgFpBinOp op) { 1397 switch (op) { 1398 case KIT_CG_FP_ADD: 1399 return "__addsf3"; 1400 case KIT_CG_FP_SUB: 1401 return "__subsf3"; 1402 case KIT_CG_FP_MUL: 1403 return "__mulsf3"; 1404 case KIT_CG_FP_DIV: 1405 return "__divsf3"; 1406 } 1407 return NULL; 1408 } 1409 1410 /* Soft-float binary arithmetic via a single libcall `name(a,b)`, both operands 1411 * (and the result) of type `opty`. Consumes the two operands on the stack and 1412 * pushes the result. Shared by the f128 (tf), soft-double (df) and soft-single 1413 * (sf) paths — only the helper name and operand type differ; the runtime ABI 1414 * (two same-type args, same-type result) is width-neutral. `what` names the 1415 * width in the panic raised when `op` has no helper. Mirrors api_softfp_cmp. */ 1416 static void api_softfp_binop(KitCg* g, const char* name, KitCgTypeId opty, 1417 const char* what) { 1418 KitCgTypeId ps[2]; 1419 ApiSValue args[2]; 1420 if (!name) 1421 compiler_panic(g->c, g->cur_loc, "KitCg: unsupported %s binop", what); 1422 args[1] = api_pop(g); 1423 args[0] = api_pop(g); 1424 ps[0] = opty; 1425 ps[1] = opty; 1426 api_runtime_call_values(g, name, opty, ps, 2, args); 1427 } 1428 1429 void api_f128_call_unary(KitCg* g, const char* name, KitCgTypeId ret, 1430 KitCgTypeId param) { 1431 ApiSValue args[1]; 1432 KitCgTypeId ps[1]; 1433 args[0] = api_pop(g); 1434 ps[0] = param; 1435 api_runtime_call_values(g, name, ret, ps, 1, args); 1436 } 1437 1438 void kit_cg_fp_binop(KitCg* g, KitCgFpBinOp op, uint32_t flags) { 1439 (void)flags; 1440 if (api_unevaluated(g)) { 1441 api_cg_binop(g, api_map_fp_binop(op), 0); 1442 return; 1443 } 1444 if (api_f128_stack_top(g, 0) || api_f128_stack_top(g, 1)) { 1445 api_softfp_binop(g, api_f128_binop_helper(op), 1446 builtin_id(KIT_CG_BUILTIN_F128), "f128"); 1447 return; 1448 } 1449 if (api_soft_double_stack_top(g, 0) || api_soft_double_stack_top(g, 1)) { 1450 api_softfp_binop(g, api_softdf_binop_helper(op), 1451 builtin_id(KIT_CG_BUILTIN_F64), "soft double"); 1452 return; 1453 } 1454 if (api_soft_single_stack_top(g, 0) || api_soft_single_stack_top(g, 1)) { 1455 api_softfp_binop(g, api_softsf_binop_helper(op), 1456 builtin_id(KIT_CG_BUILTIN_F32), "soft single"); 1457 return; 1458 } 1459 api_cg_binop(g, api_map_fp_binop(op), 0); 1460 } 1461 1462 void kit_cg_fp_unop(KitCg* g, KitCgFpUnOp op, uint32_t flags) { 1463 (void)flags; 1464 if (!g) return; 1465 CG_REQUIRE(g, op == KIT_CG_FP_NEG, "KitCg: FP unary op unsupported"); 1466 if (api_unevaluated(g)) { 1467 api_cg_unop(g, UO_FNEG, 0); 1468 return; 1469 } 1470 if (api_f128_stack_top(g, 0)) { 1471 KitCgTypeId f128 = builtin_id(KIT_CG_BUILTIN_F128); 1472 api_f128_call_unary(g, "__negtf2", f128, f128); 1473 return; 1474 } 1475 /* Soft float has no FP unit, so negation is a libcall too (the inline FNEG 1476 * path emits fsgnj on an FP register, which does not exist here). */ 1477 if (api_soft_double_stack_top(g, 0)) { 1478 KitCgTypeId f64 = builtin_id(KIT_CG_BUILTIN_F64); 1479 api_f128_call_unary(g, "__negdf2", f64, f64); 1480 return; 1481 } 1482 if (api_soft_single_stack_top(g, 0)) { 1483 KitCgTypeId f32 = builtin_id(KIT_CG_BUILTIN_F32); 1484 api_f128_call_unary(g, "__negsf2", f32, f32); 1485 return; 1486 } 1487 api_cg_unop(g, UO_FNEG, 0); 1488 } 1489 1490 /* Soft-float single-libcall comparison: call `name(a,b)` (both operands of type 1491 * `opty`) and test its i32 three-way result against 0 with `icmp`. Consumes the 1492 * two operands on the stack and pushes the i32 boolean. Shared by the f128 (tf) 1493 * and soft-double (df) paths — only the helper name and operand type differ; 1494 * the compiler-rt NaN-sign convention is identical for both. */ 1495 static void api_softfp_cmp_call(KitCg* g, const char* name, KitCgTypeId opty, 1496 CmpOp icmp) { 1497 KitCgTypeId i32 = builtin_id(KIT_CG_BUILTIN_I32); 1498 KitCgTypeId ps[2]; 1499 ApiSValue args[2]; 1500 ps[0] = opty; 1501 ps[1] = opty; 1502 args[1] = api_pop(g); 1503 args[0] = api_pop(g); 1504 api_runtime_call_values(g, name, i32, ps, 2, args); 1505 kit_cg_push_int(g, 0, i32); 1506 api_cg_cmp(g, icmp); 1507 } 1508 1509 /* UEQ and ONE are the only soft-float predicates that cannot be a single 1510 * libcall: "equal" and "unordered" both yield a nonzero magnitude from 1511 * __eq*2/__ne*2, so they need a separate __unord*2 to split them. 1512 * UEQ = (__eq*2(a,b) == 0) || (__unord*2(a,b) != 0) 1513 * ONE = (__ne*2(a,b) != 0) && (__unord*2(a,b) == 0) 1514 * `suffix` is "tf" (f128) or "df" (double); `opty` the matching operand type. 1515 * The operands are dup'd (kit_cg_dup copies into a fresh owned local) so each 1516 * libcall consumes its own copy. */ 1517 static void api_softfp_cmp_with_unord(KitCg* g, KitCgFpCmpOp op, 1518 const char* suffix, KitCgTypeId opty) { 1519 char relname[16]; 1520 char unordname[16]; 1521 CmpOp relcmp = (op == KIT_CG_FP_UEQ) ? CMP_EQ : CMP_NE; 1522 const char* rel = (op == KIT_CG_FP_UEQ) ? "eq" : "ne"; 1523 snprintf(relname, sizeof relname, "__%s%s2", rel, suffix); 1524 snprintf(unordname, sizeof unordname, "__unord%s2", suffix); 1525 /* [a, b] -> [a, b, a, b] */ 1526 kit_cg_dup2(g); 1527 /* relation on the top (dup'd) copy: [a, b, R] */ 1528 api_softfp_cmp_call(g, relname, opty, relcmp); 1529 /* bring the original a, b back to TOS with R underneath: [R, a, b] */ 1530 kit_cg_rot3(g); 1531 kit_cg_rot3(g); 1532 if (op == KIT_CG_FP_UEQ) { 1533 api_softfp_cmp_call(g, unordname, opty, CMP_NE); /* [R, unordered?] */ 1534 api_cg_binop(g, BO_OR, 0); /* R || unordered */ 1535 } else { 1536 api_softfp_cmp_call(g, unordname, opty, CMP_EQ); /* [R, ordered?] */ 1537 api_cg_binop(g, BO_AND, 0); /* R && ordered */ 1538 } 1539 } 1540 1541 /* Emit a soft-float comparison for either f128 (suffix "tf", opty f128) or 1542 * soft double (suffix "df", opty f64). The predicate->helper mapping and the 1543 * compiler-rt NaN-sign convention are XLEN/width-neutral, so a single body 1544 * serves both — only the suffix and operand type vary. */ 1545 static void api_softfp_cmp(KitCg* g, KitCgFpCmpOp op, const char* suffix, 1546 KitCgTypeId opty) { 1547 char name[16]; 1548 switch (op) { 1549 case KIT_CG_FP_OEQ: 1550 snprintf(name, sizeof name, "__eq%s2", suffix); 1551 api_softfp_cmp_call(g, name, opty, CMP_EQ); 1552 return; 1553 case KIT_CG_FP_UNE: 1554 snprintf(name, sizeof name, "__ne%s2", suffix); 1555 api_softfp_cmp_call(g, name, opty, CMP_NE); 1556 return; 1557 case KIT_CG_FP_OLT: 1558 snprintf(name, sizeof name, "__lt%s2", suffix); 1559 api_softfp_cmp_call(g, name, opty, CMP_LT_S); 1560 return; 1561 case KIT_CG_FP_OLE: 1562 snprintf(name, sizeof name, "__le%s2", suffix); 1563 api_softfp_cmp_call(g, name, opty, CMP_LE_S); 1564 return; 1565 case KIT_CG_FP_OGT: 1566 snprintf(name, sizeof name, "__gt%s2", suffix); 1567 api_softfp_cmp_call(g, name, opty, CMP_GT_S); 1568 return; 1569 case KIT_CG_FP_OGE: 1570 snprintf(name, sizeof name, "__ge%s2", suffix); 1571 api_softfp_cmp_call(g, name, opty, CMP_GE_S); 1572 return; 1573 /* unordered duals via the opposite-sign helper (NaN flips the test): */ 1574 case KIT_CG_FP_UGE: 1575 snprintf(name, sizeof name, "__lt%s2", suffix); 1576 api_softfp_cmp_call(g, name, opty, CMP_GE_S); 1577 return; 1578 case KIT_CG_FP_UGT: 1579 snprintf(name, sizeof name, "__le%s2", suffix); 1580 api_softfp_cmp_call(g, name, opty, CMP_GT_S); 1581 return; 1582 case KIT_CG_FP_ULT: 1583 snprintf(name, sizeof name, "__ge%s2", suffix); 1584 api_softfp_cmp_call(g, name, opty, CMP_LT_S); 1585 return; 1586 case KIT_CG_FP_ULE: 1587 snprintf(name, sizeof name, "__gt%s2", suffix); 1588 api_softfp_cmp_call(g, name, opty, CMP_LE_S); 1589 return; 1590 case KIT_CG_FP_UEQ: 1591 case KIT_CG_FP_ONE: 1592 api_softfp_cmp_with_unord(g, op, suffix, opty); 1593 return; 1594 } 1595 } 1596 1597 void kit_cg_fp_cmp(KitCg* g, KitCgFpCmpOp op) { 1598 /* f128/long double and soft double are both soft-float: the comparison is a 1599 * libcall returning a three-way i32 we test against 0. kit's runtime uses the 1600 * standard compiler-rt sign convention (rt/lib/impl/fp_compare_impl.inc): 1601 * __le-family (__eq*2/__ne*2/__lt*2/__le*2): NaN -> +1 1602 * __ge-family (__ge*2/__gt*2): NaN -> -1 1603 * so each ordered predicate AND its unordered dual maps to one libcall, 1604 * choosing the helper whose NaN sign makes the integer test fall the right 1605 * way (ordered: NaN must fail; unordered: NaN must pass). Only UEQ/ONE, which 1606 * must split "equal" from "unordered", need a second (__unord*2) call. The 1607 * convention is width-neutral, so the same logic drives the tf and df 1608 * suffixes via api_softfp_cmp. */ 1609 if (api_unevaluated(g)) { 1610 api_cg_cmp(g, api_map_fp_cmp(op)); 1611 return; 1612 } 1613 if (api_f128_stack_top(g, 0) || api_f128_stack_top(g, 1)) { 1614 api_softfp_cmp(g, op, "tf", builtin_id(KIT_CG_BUILTIN_F128)); 1615 return; 1616 } 1617 if (api_soft_double_stack_top(g, 0) || api_soft_double_stack_top(g, 1)) { 1618 api_softfp_cmp(g, op, "df", builtin_id(KIT_CG_BUILTIN_F64)); 1619 return; 1620 } 1621 if (api_soft_single_stack_top(g, 0) || api_soft_single_stack_top(g, 1)) { 1622 api_softfp_cmp(g, op, "sf", builtin_id(KIT_CG_BUILTIN_F32)); 1623 return; 1624 } 1625 api_cg_cmp(g, api_map_fp_cmp(op)); 1626 } 1627 1628 void kit_cg_sext(KitCg* g, KitCgTypeId dst) { 1629 api_cg_convert_kind(g, dst, CV_SEXT); 1630 } 1631 1632 void kit_cg_zext(KitCg* g, KitCgTypeId dst) { 1633 api_cg_convert_kind(g, dst, CV_ZEXT); 1634 } 1635 1636 void kit_cg_trunc(KitCg* g, KitCgTypeId dst) { 1637 api_cg_convert_kind(g, dst, CV_TRUNC); 1638 } 1639 1640 void kit_cg_ptr_to_int(KitCg* g, KitCgTypeId dst) { 1641 api_cg_convert_kind(g, dst, CV_BITCAST); 1642 } 1643 1644 void kit_cg_int_to_ptr(KitCg* g, KitCgTypeId dst) { 1645 api_cg_convert_kind(g, dst, CV_BITCAST); 1646 } 1647 1648 void kit_cg_bitcast(KitCg* g, KitCgTypeId dst) { 1649 api_cg_convert_kind(g, dst, CV_BITCAST); 1650 } 1651 1652 void kit_cg_fpext(KitCg* g, KitCgTypeId dst) { 1653 /* The f128 / soft-double predicates below validate `dst` internally (and the 1654 * fall-through reuses `dst` directly), so the standalone resolve_type is 1655 * redundant: for a valid id resolve_type(dst)==dst, for an invalid id every 1656 * predicate is false and the fall-through validates. */ 1657 KitCgTypeId dty = dst; 1658 if (!g) return; 1659 if (api_unevaluated(g)) { 1660 api_cg_convert_kind(g, dst, CV_FEXT); 1661 return; 1662 } 1663 if (api_is_f128_type(g->c, dty)) { 1664 ApiSValue v = api_pop(g); 1665 KitCgTypeId sty = api_sv_type(&v); 1666 const char* name = sty == builtin_id(KIT_CG_BUILTIN_F32) ? "__extendsftf2" 1667 : "__extenddftf2"; 1668 api_push(g, v); 1669 api_f128_call_unary(g, name, dty, sty); 1670 return; 1671 } 1672 /* float -> soft double: runtime widen via __extendsfdf2. */ 1673 if (api_type_is_soft_double(g, dty)) { 1674 ApiSValue v = api_pop(g); 1675 KitCgTypeId sty = api_sv_type(&v); 1676 api_push(g, v); 1677 api_f128_call_unary(g, "__extendsfdf2", dty, sty); 1678 return; 1679 } 1680 api_cg_convert_kind(g, dst, CV_FEXT); 1681 } 1682 1683 void kit_cg_fptrunc(KitCg* g, KitCgTypeId dst) { 1684 /* resolve_type(dst)==dst for any valid id; the predicates / fall-through 1685 * validate, so drop the redundant standalone validation (see kit_cg_fpext). 1686 */ 1687 KitCgTypeId dty = dst; 1688 if (!g) return; 1689 if (api_unevaluated(g)) { 1690 api_cg_convert_kind(g, dst, CV_FTRUNC); 1691 return; 1692 } 1693 if (api_f128_stack_top(g, 0)) { 1694 ApiSValue v = api_pop(g); 1695 KitCgTypeId f128 = builtin_id(KIT_CG_BUILTIN_F128); 1696 const char* name = 1697 dty == builtin_id(KIT_CG_BUILTIN_F32) ? "__trunctfsf2" : "__trunctfdf2"; 1698 api_push(g, v); 1699 api_f128_call_unary(g, name, dty, f128); 1700 return; 1701 } 1702 /* soft double -> float: runtime narrow via __truncdfsf2. */ 1703 if (api_soft_double_stack_top(g, 0)) { 1704 ApiSValue v = api_pop(g); 1705 KitCgTypeId sty = api_sv_type(&v); 1706 api_push(g, v); 1707 api_f128_call_unary(g, "__truncdfsf2", dty, sty); 1708 return; 1709 } 1710 api_cg_convert_kind(g, dst, CV_FTRUNC); 1711 } 1712 1713 /* The integer builtin a soft-float int<->float conversion uses for an operand 1714 * whose ABI size is `sz` bytes: i128 (>8), i64 (>4), else i32. Shared by all 1715 * eight conversion blocks (the same width ladder appeared in each). */ 1716 static KitCgTypeId api_int_builtin_for_size(u32 sz) { 1717 return sz > 8 ? builtin_id(KIT_CG_BUILTIN_I128) 1718 : (sz > 4 ? builtin_id(KIT_CG_BUILTIN_I64) 1719 : builtin_id(KIT_CG_BUILTIN_I32)); 1720 } 1721 1722 /* The compiler-rt integer-width suffix matching api_int_builtin_for_size: 1723 * "ti" (i128), "di" (i64), "si" (i32). */ 1724 static const char* api_int_suffix_for_size(u32 sz) { 1725 return sz > 8 ? "ti" : (sz > 4 ? "di" : "si"); 1726 } 1727 1728 /* The four width-laddered soft-float int<->float conversion families. The 1729 * compiler-rt name is a fixed prefix, the width-derived integer suffix 1730 * (api_int_suffix_for_size) and a fixed float suffix ("tf" for f128, "df" for 1731 * soft double); int->float places the integer suffix before the float suffix, 1732 * float->int after it. */ 1733 typedef enum ApiFpConvOp { 1734 API_FPCONV_SINT_TO_FLOAT, /* __float<int><flt> */ 1735 API_FPCONV_UINT_TO_FLOAT, /* __floatun<int><flt> */ 1736 API_FPCONV_FLOAT_TO_SINT, /* __fix<flt><int> */ 1737 API_FPCONV_FLOAT_TO_UINT, /* __fixuns<flt><int> */ 1738 } ApiFpConvOp; 1739 1740 typedef struct FpConvDesc { 1741 const char* prefix; 1742 int int_first; /* 1: prefix+int+flt (int->float); 0: prefix+flt+int */ 1743 } FpConvDesc; 1744 1745 static const FpConvDesc kFpConvTable[] = { 1746 [API_FPCONV_SINT_TO_FLOAT] = {"__float", 1}, 1747 [API_FPCONV_UINT_TO_FLOAT] = {"__floatun", 1}, 1748 [API_FPCONV_FLOAT_TO_SINT] = {"__fix", 0}, 1749 [API_FPCONV_FLOAT_TO_UINT] = {"__fixuns", 0}, 1750 }; 1751 1752 /* Build the compiler-rt conversion libcall name for `op` with the given integer 1753 * width (`sz` bytes) and float suffix `flt` ("tf"/"df") into `buf`. The result 1754 * is byte-identical to the names the eight blocks previously open-coded. */ 1755 static void api_fp_conv_name(char* buf, size_t cap, ApiFpConvOp op, 1756 const char* flt, u32 sz) { 1757 const FpConvDesc* d = &kFpConvTable[op]; 1758 const char* is = api_int_suffix_for_size(sz); 1759 if (d->int_first) 1760 snprintf(buf, cap, "%s%s%s", d->prefix, is, flt); 1761 else 1762 snprintf(buf, cap, "%s%s%s", d->prefix, flt, is); 1763 } 1764 1765 void kit_cg_sint_to_float(KitCg* g, KitCgTypeId dst, KitCgRounding rounding) { 1766 (void)rounding; 1767 if (api_unevaluated(g)) { 1768 api_cg_convert_kind(g, dst, CV_ITOF_S); 1769 return; 1770 } 1771 if (api_is_f128_type(g->c, dst)) { 1772 ApiSValue v = api_pop(g); 1773 KitCgTypeId sty = api_sv_type(&v); 1774 u32 sz = (u32)abi_cg_sizeof(g->c->abi, sty); 1775 char name[16]; 1776 api_fp_conv_name(name, sizeof name, API_FPCONV_SINT_TO_FLOAT, "tf", sz); 1777 api_push(g, v); 1778 api_f128_call_unary(g, name, dst, api_int_builtin_for_size(sz)); 1779 return; 1780 } 1781 /* signed int -> soft double: __floatsidf (i32) / __floatdidf (i64). */ 1782 if (api_type_is_soft_double(g, dst)) { 1783 ApiSValue v = api_pop(g); 1784 KitCgTypeId sty = api_sv_type(&v); 1785 u32 sz = (u32)abi_cg_sizeof(g->c->abi, sty); 1786 char name[16]; 1787 api_fp_conv_name(name, sizeof name, API_FPCONV_SINT_TO_FLOAT, "df", sz); 1788 api_push(g, v); 1789 api_f128_call_unary(g, name, dst, api_int_builtin_for_size(sz)); 1790 return; 1791 } 1792 /* signed split-i64 -> hardware single float: use __floatdisf. */ 1793 if (api_wide64_stack_top(g, 0)) { 1794 api_f128_call_unary(g, "__floatdisf", dst, builtin_id(KIT_CG_BUILTIN_I64)); 1795 return; 1796 } 1797 /* i32 -> soft single float (ilp32, no FPU): __floatsisf. */ 1798 if (api_type_is_soft_single(g, dst)) { 1799 api_f128_call_unary(g, "__floatsisf", dst, builtin_id(KIT_CG_BUILTIN_I32)); 1800 return; 1801 } 1802 api_cg_convert_kind(g, dst, CV_ITOF_S); 1803 } 1804 1805 void kit_cg_uint_to_float(KitCg* g, KitCgTypeId dst, KitCgRounding rounding) { 1806 (void)rounding; 1807 if (api_unevaluated(g)) { 1808 api_cg_convert_kind(g, dst, CV_ITOF_U); 1809 return; 1810 } 1811 if (api_is_f128_type(g->c, dst)) { 1812 ApiSValue v = api_pop(g); 1813 KitCgTypeId sty = api_sv_type(&v); 1814 u32 sz = (u32)abi_cg_sizeof(g->c->abi, sty); 1815 char name[16]; 1816 api_fp_conv_name(name, sizeof name, API_FPCONV_UINT_TO_FLOAT, "tf", sz); 1817 api_push(g, v); 1818 api_f128_call_unary(g, name, dst, api_int_builtin_for_size(sz)); 1819 return; 1820 } 1821 /* unsigned int -> soft double: __floatunsidf (i32) / __floatundidf (i64). */ 1822 if (api_type_is_soft_double(g, dst)) { 1823 ApiSValue v = api_pop(g); 1824 KitCgTypeId sty = api_sv_type(&v); 1825 u32 sz = (u32)abi_cg_sizeof(g->c->abi, sty); 1826 char name[16]; 1827 api_fp_conv_name(name, sizeof name, API_FPCONV_UINT_TO_FLOAT, "df", sz); 1828 api_push(g, v); 1829 api_f128_call_unary(g, name, dst, api_int_builtin_for_size(sz)); 1830 return; 1831 } 1832 /* unsigned i64 -> hardware single float: __floatundisf. */ 1833 if (api_wide64_stack_top(g, 0)) { 1834 api_f128_call_unary(g, "__floatundisf", dst, 1835 builtin_id(KIT_CG_BUILTIN_I64)); 1836 return; 1837 } 1838 /* u32 -> soft single float (ilp32, no FPU): __floatunsisf. */ 1839 if (api_type_is_soft_single(g, dst)) { 1840 api_f128_call_unary(g, "__floatunsisf", dst, 1841 builtin_id(KIT_CG_BUILTIN_I32)); 1842 return; 1843 } 1844 api_cg_convert_kind(g, dst, CV_ITOF_U); 1845 } 1846 1847 void kit_cg_float_to_sint(KitCg* g, KitCgTypeId dst, KitCgRounding rounding) { 1848 (void)rounding; 1849 if (api_unevaluated(g)) { 1850 api_cg_convert_kind(g, dst, CV_FTOI_S); 1851 return; 1852 } 1853 if (api_f128_stack_top(g, 0)) { 1854 KitCgTypeId dty = dst; 1855 u32 sz = (u32)abi_cg_sizeof(g->c->abi, dty); 1856 KitCgTypeId rty = api_int_builtin_for_size(sz); 1857 char name[16]; 1858 api_fp_conv_name(name, sizeof name, API_FPCONV_FLOAT_TO_SINT, "tf", sz); 1859 api_f128_call_unary(g, name, rty, builtin_id(KIT_CG_BUILTIN_F128)); 1860 if (rty != dty) api_cg_convert_kind(g, dty, CV_TRUNC); 1861 return; 1862 } 1863 /* soft double -> signed int: __fixdfsi (i32) / __fixdfdi (i64). */ 1864 if (api_soft_double_stack_top(g, 0)) { 1865 KitCgTypeId dty = dst; 1866 KitCgTypeId f64 = builtin_id(KIT_CG_BUILTIN_F64); 1867 u32 sz = (u32)abi_cg_sizeof(g->c->abi, dty); 1868 KitCgTypeId rty = api_int_builtin_for_size(sz); 1869 char name[16]; 1870 api_fp_conv_name(name, sizeof name, API_FPCONV_FLOAT_TO_SINT, "df", sz); 1871 api_f128_call_unary(g, name, rty, f64); 1872 if (rty != dty) api_cg_convert_kind(g, dty, CV_TRUNC); 1873 return; 1874 } 1875 /* hardware single float -> split-i64: use __fixsfdi. */ 1876 if (api_is_wide8_scalar_type(g->c, dst)) { 1877 api_f128_call_unary(g, "__fixsfdi", dst, builtin_id(KIT_CG_BUILTIN_F32)); 1878 return; 1879 } 1880 /* soft single float -> signed int <=32 (ilp32, no FPU): __fixsfsi. */ 1881 if (api_soft_single_stack_top(g, 0)) { 1882 KitCgTypeId dty = dst; 1883 KitCgTypeId i32 = builtin_id(KIT_CG_BUILTIN_I32); 1884 api_f128_call_unary(g, "__fixsfsi", i32, builtin_id(KIT_CG_BUILTIN_F32)); 1885 if (i32 != dty) api_cg_convert_kind(g, dty, CV_TRUNC); 1886 return; 1887 } 1888 api_cg_convert_kind(g, dst, CV_FTOI_S); 1889 } 1890 1891 void kit_cg_float_to_uint(KitCg* g, KitCgTypeId dst, KitCgRounding rounding) { 1892 (void)rounding; 1893 if (api_unevaluated(g)) { 1894 api_cg_convert_kind(g, dst, CV_FTOI_U); 1895 return; 1896 } 1897 if (api_f128_stack_top(g, 0)) { 1898 KitCgTypeId dty = dst; 1899 u32 sz = (u32)abi_cg_sizeof(g->c->abi, dty); 1900 KitCgTypeId rty = api_int_builtin_for_size(sz); 1901 char name[16]; 1902 api_fp_conv_name(name, sizeof name, API_FPCONV_FLOAT_TO_UINT, "tf", sz); 1903 api_f128_call_unary(g, name, rty, builtin_id(KIT_CG_BUILTIN_F128)); 1904 if (rty != dty) api_cg_convert_kind(g, dty, CV_TRUNC); 1905 return; 1906 } 1907 /* soft double -> unsigned int: __fixunsdfsi (i32) / __fixunsdfdi (i64). */ 1908 if (api_soft_double_stack_top(g, 0)) { 1909 KitCgTypeId dty = dst; 1910 KitCgTypeId f64 = builtin_id(KIT_CG_BUILTIN_F64); 1911 u32 sz = (u32)abi_cg_sizeof(g->c->abi, dty); 1912 KitCgTypeId rty = api_int_builtin_for_size(sz); 1913 char name[16]; 1914 api_fp_conv_name(name, sizeof name, API_FPCONV_FLOAT_TO_UINT, "df", sz); 1915 api_f128_call_unary(g, name, rty, f64); 1916 if (rty != dty) api_cg_convert_kind(g, dty, CV_TRUNC); 1917 return; 1918 } 1919 /* hardware single float -> split-u64: use __fixunssfdi. */ 1920 if (api_is_wide8_scalar_type(g->c, dst)) { 1921 api_f128_call_unary(g, "__fixunssfdi", dst, builtin_id(KIT_CG_BUILTIN_F32)); 1922 return; 1923 } 1924 /* soft single float -> unsigned int <=32 (ilp32, no FPU): __fixunssfsi. */ 1925 if (api_soft_single_stack_top(g, 0)) { 1926 KitCgTypeId dty = dst; 1927 KitCgTypeId i32 = builtin_id(KIT_CG_BUILTIN_I32); 1928 api_f128_call_unary(g, "__fixunssfsi", i32, builtin_id(KIT_CG_BUILTIN_F32)); 1929 if (i32 != dty) api_cg_convert_kind(g, dty, CV_TRUNC); 1930 return; 1931 } 1932 api_cg_convert_kind(g, dst, CV_FTOI_U); 1933 } 1934 1935 /* ============================================================ 1936 * Intrinsics (stub) 1937 * ============================================================ */ 1938 1939 /* One descriptor per KitCgIntrinsic, indexed by the enum value. The four 1940 * accessors below are field reads off this single source of truth; unmapped 1941 * intrinsics (FMA/cache/coro) use an INTRIN_NONE row. The table is laid 1942 * out in enum order; the _Static_assert guards its length so a new enumerator 1943 * is a compile error rather than a silently truncated index. */ 1944 typedef struct IntrinDesc { 1945 IntrinKind kind; 1946 const char* name; 1947 bool is_void; 1948 bool is_overflow; 1949 } IntrinDesc; 1950 1951 static const IntrinDesc kIntrinTable[] = { 1952 [KIT_CG_INTRIN_TRAP] = {INTRIN_TRAP, "trap", true, false}, 1953 [KIT_CG_INTRIN_CLZ] = {INTRIN_CLZ, "clz", false, false}, 1954 [KIT_CG_INTRIN_CTZ] = {INTRIN_CTZ, "ctz", false, false}, 1955 [KIT_CG_INTRIN_POPCOUNT] = {INTRIN_POPCOUNT, "popcount", false, false}, 1956 [KIT_CG_INTRIN_BSWAP] = {INTRIN_BSWAP, "bswap", false, false}, 1957 [KIT_CG_INTRIN_SETJMP] = {INTRIN_SETJMP, "setjmp", false, false}, 1958 [KIT_CG_INTRIN_LONGJMP] = {INTRIN_LONGJMP, "longjmp", true, false}, 1959 [KIT_CG_INTRIN_SADD_OVERFLOW] = {INTRIN_SADD_OVERFLOW, "sadd_overflow", 1960 false, true}, 1961 [KIT_CG_INTRIN_UADD_OVERFLOW] = {INTRIN_UADD_OVERFLOW, "uadd_overflow", 1962 false, true}, 1963 [KIT_CG_INTRIN_SSUB_OVERFLOW] = {INTRIN_SSUB_OVERFLOW, "ssub_overflow", 1964 false, true}, 1965 [KIT_CG_INTRIN_USUB_OVERFLOW] = {INTRIN_USUB_OVERFLOW, "usub_overflow", 1966 false, true}, 1967 [KIT_CG_INTRIN_SMUL_OVERFLOW] = {INTRIN_SMUL_OVERFLOW, "smul_overflow", 1968 false, true}, 1969 [KIT_CG_INTRIN_UMUL_OVERFLOW] = {INTRIN_UMUL_OVERFLOW, "umul_overflow", 1970 false, true}, 1971 [KIT_CG_INTRIN_FMA] = {INTRIN_NONE, "fma", false, false}, 1972 [KIT_CG_INTRIN_PREFETCH] = {INTRIN_PREFETCH, "prefetch", true, false}, 1973 [KIT_CG_INTRIN_EXPECT] = {INTRIN_EXPECT, "expect", false, false}, 1974 [KIT_CG_INTRIN_ASSUME_ALIGNED] = {INTRIN_ASSUME_ALIGNED, "assume_aligned", 1975 false, false}, 1976 [KIT_CG_INTRIN_SYSCALL] = {INTRIN_SYSCALL, "syscall", false, false}, 1977 [KIT_CG_INTRIN_IRQ_SAVE] = {INTRIN_IRQ_SAVE, "irq_save", false, false}, 1978 [KIT_CG_INTRIN_IRQ_RESTORE] = {INTRIN_IRQ_RESTORE, "irq_restore", true, 1979 false}, 1980 [KIT_CG_INTRIN_IRQ_DISABLE] = {INTRIN_IRQ_DISABLE, "irq_disable", true, 1981 false}, 1982 [KIT_CG_INTRIN_IRQ_ENABLE] = {INTRIN_IRQ_ENABLE, "irq_enable", true, false}, 1983 [KIT_CG_INTRIN_DMB] = {INTRIN_DMB, "dmb", true, false}, 1984 [KIT_CG_INTRIN_DSB] = {INTRIN_DSB, "dsb", true, false}, 1985 [KIT_CG_INTRIN_ISB] = {INTRIN_ISB, "isb", true, false}, 1986 [KIT_CG_INTRIN_DCACHE_CLEAN] = {INTRIN_NONE, "dcache_clean", false, false}, 1987 [KIT_CG_INTRIN_DCACHE_INVALIDATE] = {INTRIN_NONE, "dcache_invalidate", 1988 false, false}, 1989 [KIT_CG_INTRIN_DCACHE_CLEAN_INVALIDATE] = {INTRIN_NONE, 1990 "dcache_clean_invalidate", false, 1991 false}, 1992 [KIT_CG_INTRIN_ICACHE_INVALIDATE] = {INTRIN_NONE, "icache_invalidate", 1993 false, false}, 1994 [KIT_CG_INTRIN_CPU_NOP] = {INTRIN_CPU_NOP, "cpu_nop", true, false}, 1995 [KIT_CG_INTRIN_CPU_YIELD] = {INTRIN_CPU_YIELD, "cpu_yield", true, false}, 1996 [KIT_CG_INTRIN_WFI] = {INTRIN_WFI, "wfi", true, false}, 1997 [KIT_CG_INTRIN_WFE] = {INTRIN_WFE, "wfe", true, false}, 1998 [KIT_CG_INTRIN_SEV] = {INTRIN_SEV, "sev", true, false}, 1999 [KIT_CG_INTRIN_CORO_SWITCH] = {INTRIN_NONE, "coro_switch", false, false}, 2000 [KIT_CG_INTRIN_FRAME_ADDRESS] = {INTRIN_FRAME_ADDRESS, "frame_address", 2001 false, false}, 2002 [KIT_CG_INTRIN_RETURN_ADDRESS] = {INTRIN_RETURN_ADDRESS, "return_address", 2003 false, false}, 2004 [KIT_CG_INTRIN_READCYCLECOUNTER] = {INTRIN_READCYCLECOUNTER, 2005 "readcyclecounter", false, false}, 2006 [KIT_CG_INTRIN_SMUL_HIGH] = {INTRIN_SMUL_HIGH, "smul_high", false, false}, 2007 [KIT_CG_INTRIN_UMUL_HIGH] = {INTRIN_UMUL_HIGH, "umul_high", false, false}, 2008 }; 2009 2010 _Static_assert(sizeof(kIntrinTable) / sizeof(kIntrinTable[0]) == 2011 KIT_CG_INTRIN_UMUL_HIGH + 1, 2012 "kIntrinTable must have exactly one row per KitCgIntrinsic"); 2013 2014 /* Bounds-guarded row lookup: an out-of-range intrinsic falls back to the NONE 2015 * row, preserving the defensive `default:` behavior the four switches carried 2016 * before they collapsed into kIntrinTable. */ 2017 static const IntrinDesc* intrin_desc(KitCgIntrinsic intrin) { 2018 static const IntrinDesc none = {INTRIN_NONE, NULL, false, false}; 2019 unsigned i = (unsigned)intrin; 2020 return i < sizeof(kIntrinTable) / sizeof(kIntrinTable[0]) ? &kIntrinTable[i] 2021 : &none; 2022 } 2023 2024 IntrinKind api_map_intrinsic(KitCg* g, KitCgIntrinsic intrin, 2025 KitCgTypeId result_type) { 2026 /* Width-by-type: backends derive operand width from the result type, so the 2027 * mapping no longer needs the size here. */ 2028 (void)g; 2029 (void)result_type; 2030 return intrin_desc(intrin)->kind; 2031 } 2032 2033 int api_intrinsic_is_void(KitCgIntrinsic intrin) { 2034 return intrin_desc(intrin)->is_void; 2035 } 2036 2037 int api_intrinsic_is_overflow(KitCgIntrinsic intrin) { 2038 return intrin_desc(intrin)->is_overflow; 2039 } 2040 2041 const char* api_intrinsic_name(KitCgIntrinsic intrin) { 2042 const char* name = intrin_desc(intrin)->name; 2043 return name ? name : "intrinsic"; 2044 } 2045 2046 void kit_cg_intrinsic(KitCg* g, KitCgIntrinsic intrin, uint32_t nargs, 2047 KitCgTypeId result_type) { 2048 CgTarget* T; 2049 KitCgTypeId rty; 2050 KitCgTypeId int_ty; 2051 IntrinKind kind; 2052 ApiSValue* svs; 2053 Operand* args; 2054 Operand dsts[2]; 2055 u32 ndst = 0; 2056 Heap* h; 2057 if (!g) return; 2058 if (api_unevaluated(g)) { 2059 KitCgTypeId rty_u = resolve_type(g->c, result_type); 2060 for (u32 i = 0; i < nargs; ++i) { 2061 ApiSValue sv = api_pop(g); 2062 api_release(g, &sv); 2063 } 2064 if (api_intrinsic_is_overflow(intrin)) { 2065 KitCgTypeId bool_ty = builtin_id(KIT_CG_BUILTIN_BOOL); 2066 if (!rty_u) rty_u = builtin_id(KIT_CG_BUILTIN_I32); 2067 api_push(g, api_uneval_value(g, rty_u)); 2068 api_const_set_top(g, api_const_unknown(rty_u)); 2069 api_push(g, api_uneval_value(g, bool_ty)); 2070 api_const_set_top(g, api_const_unknown(bool_ty)); 2071 } else if (!api_intrinsic_is_void(intrin) && 2072 (rty_u && !cg_type_is_void(g->c, rty_u))) { 2073 api_push(g, api_uneval_value(g, rty_u)); 2074 api_const_set_top(g, api_const_unknown(rty_u)); 2075 } 2076 return; 2077 } 2078 /* readcyclecounter returns a 64-bit value. On rv32 that is a register pair 2079 * the single-register native intrinsic path can't carry, and the read spans 2080 * the cycle/cycleh CSRs with a re-read loop, so route to the libkit_rt helper 2081 * (mirrors the wide-64 clz/ctz routing below). rv64 reads it inline. */ 2082 if (intrin == KIT_CG_INTRIN_READCYCLECOUNTER && 2083 g->c->target.arch == KIT_ARCH_RV32) { 2084 api_runtime_call_values(g, "__kit_readcyclecounter", 2085 builtin_id(KIT_CG_BUILTIN_I64), NULL, 0, NULL); 2086 return; 2087 } 2088 /* A 64-bit value is a two-register pair on the 32-bit native targets. The 2089 * ordinary intrinsic ABI carries one register per operand/result, so use the 2090 * rollover-free compiler runtime's limb implementation for high multiply. */ 2091 if (nargs == 2 && 2092 (intrin == KIT_CG_INTRIN_SMUL_HIGH || 2093 intrin == KIT_CG_INTRIN_UMUL_HIGH) && 2094 api_wide64_stack_top(g, 0) && api_wide64_stack_top(g, 1)) { 2095 ApiSValue args_h[2]; 2096 KitCgTypeId i64 = builtin_id(KIT_CG_BUILTIN_I64); 2097 KitCgTypeId ps[2] = {i64, i64}; 2098 args_h[1] = api_pop(g); 2099 args_h[0] = api_pop(g); 2100 api_runtime_call_values(g, 2101 intrin == KIT_CG_INTRIN_SMUL_HIGH 2102 ? "__kit_mulhdi3" 2103 : "__kit_umulhdi3", 2104 i64, ps, 2, args_h); 2105 return; 2106 } 2107 /* clz/ctz/popcount/bswap on a split 64-bit value cannot use the backend's 2108 * single-register software sequence. Route them to the compiler-rt __*di2 2109 * helpers, which decompose into 32-bit operations. (32-bit forms still lower 2110 * inline.) */ 2111 if (nargs == 1 && api_wide64_stack_top(g, 0)) { 2112 const char* name = NULL; 2113 KitCgTypeId i32 = builtin_id(KIT_CG_BUILTIN_I32); 2114 KitCgTypeId i64 = builtin_id(KIT_CG_BUILTIN_I64); 2115 KitCgTypeId ret = i32; 2116 switch (intrin) { 2117 case KIT_CG_INTRIN_CLZ: 2118 name = "__clzdi2"; 2119 break; 2120 case KIT_CG_INTRIN_CTZ: 2121 name = "__ctzdi2"; 2122 break; 2123 case KIT_CG_INTRIN_POPCOUNT: 2124 name = "__popcountdi2"; 2125 break; 2126 case KIT_CG_INTRIN_BSWAP: 2127 name = "__bswapdi2"; 2128 ret = i64; 2129 break; 2130 default: 2131 break; 2132 } 2133 if (name) { 2134 ApiSValue arg = api_pop(g); 2135 KitCgTypeId ps[1] = {i64}; 2136 api_runtime_call_values(g, name, ret, ps, 1, &arg); 2137 if (ret == i32 && result_type != i32) 2138 api_cg_convert_kind(g, result_type, CV_ZEXT); 2139 return; 2140 } 2141 } 2142 /* __builtin_*_overflow on a split 64-bit operand pair traps in the native 2143 * backend (it only models single-register overflow). Legalize all 6 forms 2144 * inline as 2-lane / 4-lane ops, pushing [value, ok] like the native path. 2145 * Gated on both operands being wide64 so other targets are unchanged. */ 2146 if (nargs == 2 && api_intrinsic_is_overflow(intrin) && 2147 api_wide64_stack_top(g, 0) && api_wide64_stack_top(g, 1)) { 2148 api_wide64_overflow_inline(g, intrin); 2149 return; 2150 } 2151 if (nargs == 2 && intrin == KIT_CG_INTRIN_EXPECT && 2152 api_wide64_stack_top(g, 1)) { 2153 ApiSValue expected = api_pop(g); 2154 ApiSValue val = api_pop(g); 2155 api_release(g, &expected); 2156 api_push(g, val); 2157 return; 2158 } 2159 T = g->target; 2160 h = g->c->ctx->heap; 2161 rty = resolve_type(g->c, result_type); 2162 int_ty = builtin_id(KIT_CG_BUILTIN_I32); 2163 kind = api_map_intrinsic(g, intrin, result_type); 2164 if (!kit_cg_target_supports_intrinsic(g->c, intrin) || kind == INTRIN_NONE) { 2165 compiler_panic( 2166 g->c, g->cur_loc, "KitCg: target '%s' does not support intrinsic '%s'", 2167 arch_kind_name(g->c->target.arch), api_intrinsic_name(intrin)); 2168 return; 2169 } 2170 2171 svs = NULL; 2172 args = NULL; 2173 if (nargs) { 2174 svs = (ApiSValue*)h->alloc(h, sizeof(*svs) * nargs, _Alignof(ApiSValue)); 2175 args = (Operand*)h->alloc(h, sizeof(*args) * nargs, _Alignof(Operand)); 2176 memset(args, 0, sizeof(*args) * nargs); 2177 for (u32 i = 0; i < nargs; ++i) { 2178 u32 idx = nargs - 1u - i; 2179 KitCgTypeId aty; 2180 svs[idx] = api_pop(g); 2181 aty = api_sv_type(&svs[idx]); 2182 if (api_sv_op_is(&svs[idx], OPK_IMM) && 2183 (intrin == KIT_CG_INTRIN_EXPECT || 2184 intrin == KIT_CG_INTRIN_ASSUME_ALIGNED || 2185 intrin == KIT_CG_INTRIN_PREFETCH || intrin == KIT_CG_INTRIN_DMB || 2186 intrin == KIT_CG_INTRIN_DSB || 2187 intrin == KIT_CG_INTRIN_FRAME_ADDRESS || 2188 intrin == KIT_CG_INTRIN_RETURN_ADDRESS)) { 2189 args[idx] = svs[idx].op; 2190 } else { 2191 args[idx] = api_force_local(g, &svs[idx], aty); 2192 } 2193 } 2194 } 2195 2196 if (api_intrinsic_is_overflow(intrin)) { 2197 KitCgTypeId vty = rty ? rty : (nargs ? api_sv_type(&svs[0]) : int_ty); 2198 KitCgTypeId bool_ty = builtin_id(KIT_CG_BUILTIN_BOOL); 2199 CGLocal rr = api_alloc_temp_local(g, vty); 2200 CGLocal ok = api_alloc_temp_local(g, bool_ty); 2201 dsts[0] = api_op_local(rr, vty); 2202 dsts[1] = api_op_local(ok, bool_ty); 2203 ndst = 2; 2204 } else if (!api_intrinsic_is_void(intrin) && !cg_type_is_void(g->c, rty)) { 2205 CGLocal rr = api_alloc_temp_local(g, rty); 2206 dsts[0] = api_op_local(rr, rty); 2207 ndst = 1; 2208 } 2209 2210 T->intrinsic(T, kind, ndst ? dsts : NULL, ndst, args, nargs); 2211 2212 for (u32 i = 0; i < nargs; ++i) api_release(g, &svs[i]); 2213 if (svs) h->free(h, svs, sizeof(*svs) * nargs); 2214 if (args) h->free(h, args, sizeof(*args) * nargs); 2215 2216 if (api_intrinsic_is_overflow(intrin)) { 2217 api_push(g, api_make_sv(dsts[0], dsts[0].type)); 2218 api_push(g, api_make_sv(dsts[1], dsts[1].type)); 2219 } else if (ndst == 1) { 2220 api_push(g, api_make_sv(dsts[0], rty)); 2221 } 2222 } 2223 2224 /* ============================================================ 2225 * Atomics (stub) 2226 * ============================================================ */ 2227 2228 KitCgTypeId api_atomic_pointee(KitCg* g, KitCgTypeId pty, const char* who) { 2229 KitCgTypeId pointee = cg_type_pointee(g->c, pty); 2230 CG_REQUIRE(g, pointee, "%.*s: operand is not a pointer", 2231 SLICE_ARG(slice_from_cstr(who))); 2232 return pointee; 2233 }