reg_effects_test.c (38511B)
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 5 #include <kit/frontend.h> 6 7 #include "arch/aa64/aa64.h" 8 #include "arch/arm32/arm32.h" 9 #include "arch/riscv/rv64.h" 10 #include "arch/x64/x64.h" 11 #include "core/pool.h" 12 #include "lib/kit_unit.h" 13 #include "obj/obj.h" 14 #include "opt/opt_internal.h" 15 16 static Operand reg_op(u8 cls, Reg reg) { 17 Operand op; 18 memset(&op, 0, sizeof op); 19 op.kind = OPK_REG; 20 op.cls = cls; 21 op.v.reg = reg; 22 return op; 23 } 24 25 static Operand indirect_op(Reg base, Reg index) { 26 Operand op; 27 memset(&op, 0, sizeof op); 28 op.kind = OPK_INDIRECT; 29 op.cls = RC_INT; 30 op.v.ind.base = base; 31 op.v.ind.index = index; 32 return op; 33 } 34 35 static u32 reg_bit(Reg reg) { return 1u << reg; } 36 37 static void expect_mask_effects_match(KitUnit* u, Func* f, Inst* in, 38 const char* what) { 39 OptRegEffects detailed; 40 OptRegEffectMasks masks; 41 OptHardRegSet detailed_kills; 42 OptHardRegSet mask_kills; 43 OptHardRegSet direct_kills; 44 opt_inst_reg_effects(f, in, &detailed); 45 opt_inst_reg_effect_masks(f, in, &masks); 46 opt_reg_effect_kills(&detailed, &detailed_kills); 47 opt_reg_effect_mask_kills(&masks, &mask_kills); 48 opt_inst_reg_kills(f, in, &direct_kills); 49 CU_EXPECT(u, 50 !memcmp(&detailed.uses, &masks.uses, sizeof masks.uses) && 51 !memcmp(&detailed.defs, &masks.defs, sizeof masks.defs) && 52 !memcmp(&detailed.clobbers, &masks.clobbers, 53 sizeof masks.clobbers) && 54 !memcmp(&detailed_kills, &mask_kills, sizeof mask_kills) && 55 !memcmp(&detailed_kills, &direct_kills, 56 sizeof direct_kills), 57 "%s masks-only effects diverged from detailed effects", what); 58 } 59 60 typedef struct MachinizeRunCtx { 61 Func* f; 62 NativeTarget* target; 63 } MachinizeRunCtx; 64 65 static int invalid_machine_effect( 66 NativeTarget* target, const NativeMachineOp* op, 67 u32 mask[NATIVE_REG_CLASS_COUNT]) { 68 (void)target; 69 (void)op; 70 mask[0] = mask[1] = mask[2] = 0; 71 mask[NATIVE_REG_INT] = reg_bit(31u); 72 return 1; 73 } 74 75 static KitStatus run_machinize(KitCompiler* compiler, void* arg) { 76 MachinizeRunCtx* run = (MachinizeRunCtx*)arg; 77 (void)compiler; 78 opt_machinize_native(run->f, run->target); 79 return KIT_OK; 80 } 81 82 static Operand typed_reg_op(u8 cls, Reg reg, KitCgTypeId type) { 83 Operand op = reg_op(cls, reg); 84 op.type = type; 85 return op; 86 } 87 88 static void expect_body_bytes(KitUnit* u, const Section* sec, u32 begin, 89 const u8* expected, u32 expected_size, 90 const char* what) { 91 u32 size = sec ? buf_pos(&sec->bytes) : 0u; 92 u8* bytes = size ? (u8*)malloc(size) : NULL; 93 CU_EXPECT(u, sec != NULL && begin <= size && size - begin == expected_size, 94 "%s size mismatch: begin=%u size=%u expected=%u", what, begin, 95 size, expected_size); 96 if (!sec || begin > size || size - begin != expected_size || 97 (size && !bytes)) { 98 free(bytes); 99 return; 100 } 101 if (size) buf_flatten(&sec->bytes, bytes); 102 CU_EXPECT(u, !memcmp(bytes + begin, expected, expected_size), 103 "%s encoding mismatch", what); 104 free(bytes); 105 } 106 107 static void x64_dying_fixed_sources_are_staged(KitUnit* u) { 108 static const u8 div_expected[] = { 109 0x49, 0x89, 0xc3, /* mov r11, rax (save divisor) */ 110 0x48, 0x89, 0xd0, /* mov rax, rdx (publish dividend) */ 111 0x48, 0x99, /* cqo */ 112 0x49, 0xf7, 0xfb, /* idiv r11 */ 113 0x49, 0x89, 0xc0, /* mov r8, rax */ 114 }; 115 static const u8 shift_expected[] = { 116 0x49, 0x89, 0xf2, /* mov r10, rsi (stage value) */ 117 0x49, 0xd3, 0xe2, /* shl r10, cl */ 118 0x4c, 0x89, 0xd1, /* mov rcx, r10 */ 119 }; 120 static const u8 copy_to_rax_addr_expected[] = { 121 0x44, 0x0f, 0xb6, 0x12, /* movzx r10d, byte [rdx] */ 122 0x66, 0x45, 0x0f, 0x6e, 0xf2, /* movd xmm14, r10d */ 123 0x66, 0x45, 0x0f, 0x7e, 0xf2, /* movd r10d, xmm14 */ 124 0x44, 0x88, 0x10, /* mov byte [rax], r10b */ 125 }; 126 static const u8 copy_from_rax_addr_expected[] = { 127 0x44, 0x0f, 0xb6, 0x10, /* movzx r10d, byte [rax] */ 128 0x66, 0x45, 0x0f, 0x6e, 0xf2, /* movd xmm14, r10d */ 129 0x66, 0x45, 0x0f, 0x7e, 0xf2, /* movd r10d, xmm14 */ 130 0x44, 0x88, 0x12, /* mov byte [rdx], r10b */ 131 }; 132 static const u8 set_rax_addr_expected[] = { 133 0x41, 0x89, 0xca, /* mov r10d, ecx */ 134 0x41, 0x81, 0xe2, 0xff, 0x00, 0x00, 0x00, /* and r10d, 255 */ 135 0x66, 0x45, 0x0f, 0x6e, 0xf2, /* movd xmm14, r10d */ 136 0x66, 0x45, 0x0f, 0x60, 0xf6, /* punpcklbw xmm14 */ 137 0x66, 0x45, 0x0f, 0x61, 0xf6, /* punpcklwd xmm14 */ 138 0x66, 0x45, 0x0f, 0x62, 0xf6, /* punpckldq xmm14 */ 139 0x66, 0x4d, 0x0f, 0x7e, 0xf2, /* movq r10, xmm14 */ 140 0x4c, 0x89, 0x10, /* mov [rax], r10 */ 141 }; 142 KitCompiler* kc = NULL; 143 Compiler* c; 144 ObjBuilder* obj; 145 MCEmitter* mc; 146 NativeTarget* target; 147 const Section* sec; 148 ObjSecId text; 149 KitCgTypeId i64_type; 150 NativeLoc rax, rcx, rdx, rsi, r8; 151 NativeAddr src_addr, dst_addr; 152 AggregateAccess byte_copy; 153 u32 begin; 154 155 CU_EXPECT(u, 156 kit_unit_compiler_new( 157 u, kit_unit_target(KIT_ARCH_X86_64, KIT_OS_LINUX, KIT_OBJ_ELF), 158 &kc) == KIT_OK && 159 kc != NULL, 160 "compiler allocation failed for x64 fixed-source staging test"); 161 if (!kc) return; 162 c = (Compiler*)kc; 163 obj = obj_new(c); 164 mc = obj ? mc_new(c, obj) : NULL; 165 target = mc ? x64_native_target_new(c, obj, mc) : NULL; 166 CU_EXPECT(u, obj && mc && target, 167 "x64 fixed-source staging backend allocation failed"); 168 if (!obj || !mc || !target) { 169 kit_compiler_free(kc); 170 return; 171 } 172 text = obj_section(obj, pool_intern_slice(c->global, SLICE_LIT(".text")), 173 SEC_TEXT, SF_EXEC | SF_ALLOC, 16u); 174 mc_set_section(mc, text); 175 i64_type = kit_cg_type_builtin(kc, KIT_CG_BUILTIN_I64); 176 rax = native_loc_reg(i64_type, NATIVE_REG_INT, 0u); 177 rcx = native_loc_reg(i64_type, NATIVE_REG_INT, 1u); 178 rdx = native_loc_reg(i64_type, NATIVE_REG_INT, 2u); 179 rsi = native_loc_reg(i64_type, NATIVE_REG_INT, 6u); 180 r8 = native_loc_reg(i64_type, NATIVE_REG_INT, 8u); 181 182 begin = mc_pos(mc); 183 /* The divisor dies in RAX while the dividend arrives in RDX. A naive 184 * dividend-first move destroys the divisor before IDIV can consume it. */ 185 target->binop(target, BO_SDIV, r8, rdx, rax); 186 sec = obj_section_get(obj, text); 187 expect_body_bytes(u, sec, begin, div_expected, sizeof div_expected, 188 "x64 crossed division operands"); 189 190 begin = mc_pos(mc); 191 /* RCX is both the dying count and the requested result. CL must remain the 192 * count until the shift finishes, so the result is computed in private R10 193 * and published afterward. */ 194 target->binop(target, BO_SHL, rcx, rsi, rcx); 195 sec = obj_section_get(obj, text); 196 expect_body_bytes(u, sec, begin, shift_expected, sizeof shift_expected, 197 "x64 RCX-result variable shift"); 198 199 memset(&src_addr, 0, sizeof src_addr); 200 memset(&dst_addr, 0, sizeof dst_addr); 201 memset(&byte_copy, 0, sizeof byte_copy); 202 src_addr.base_kind = NATIVE_ADDR_BASE_REG; 203 src_addr.base.reg = 2u; /* RDX */ 204 src_addr.base_type = i64_type; 205 dst_addr.base_kind = NATIVE_ADDR_BASE_REG; 206 dst_addr.base.reg = 0u; /* RAX */ 207 dst_addr.base_type = i64_type; 208 byte_copy.size = 1u; 209 210 begin = mc_pos(mc); 211 target->copy_bytes(target, dst_addr, src_addr, byte_copy); 212 sec = obj_section_get(obj, text); 213 expect_body_bytes(u, sec, begin, copy_to_rax_addr_expected, 214 sizeof copy_to_rax_addr_expected, 215 "x64 copy to RAX-based address"); 216 217 begin = mc_pos(mc); 218 target->copy_bytes(target, src_addr, dst_addr, byte_copy); 219 sec = obj_section_get(obj, text); 220 expect_body_bytes(u, sec, begin, copy_from_rax_addr_expected, 221 sizeof copy_from_rax_addr_expected, 222 "x64 copy from RAX-based address"); 223 224 byte_copy.size = 8u; 225 begin = mc_pos(mc); 226 target->set_bytes(target, dst_addr, rcx, byte_copy); 227 sec = obj_section_get(obj, text); 228 expect_body_bytes(u, sec, begin, set_rax_addr_expected, 229 sizeof set_rax_addr_expected, 230 "x64 set through RAX-based address"); 231 mc_free(mc); 232 kit_compiler_free(kc); 233 } 234 235 static void explicit_and_machine_effects(KitUnit* u) { 236 Func f; 237 Inst in; 238 Operand opnds[3]; 239 OptInstClobberMask machine_clobbers[4]; 240 OptRegEffects effects; 241 242 memset(&f, 0, sizeof f); 243 memset(&in, 0, sizeof in); 244 memset(machine_clobbers, 0, sizeof machine_clobbers); 245 f.opt_rewritten = 1; 246 f.emit_temp_regs[RC_INT][0] = 8; 247 f.emit_temp_regs[RC_INT][1] = 9; 248 f.emit_temp_reg_count[RC_INT] = 2; 249 f.inst_clobbers = machine_clobbers; 250 f.inst_clobbers_cap = 4; 251 252 in.op = IR_BINOP; 253 in.id = 2; 254 in.opnds = opnds; 255 in.nopnds = 3; 256 opnds[0] = reg_op(RC_INT, 1); 257 opnds[1] = reg_op(RC_INT, 2); 258 opnds[2] = indirect_op(8, 8); 259 machine_clobbers[in.id][RC_INT] = reg_bit(5); 260 261 expect_mask_effects_match(u, &f, &in, "explicit/machine"); 262 opt_inst_reg_effects(&f, &in, &effects); 263 CU_EXPECT(u, effects.uses.cls[RC_INT] == 264 (reg_bit(2) | reg_bit(8)), 265 "explicit/indirect uses mismatch: %#x", effects.uses.cls[RC_INT]); 266 CU_EXPECT(u, effects.use_count[RC_INT][8] == 2, 267 "indirect base+index use count should saturate at two, got %u", 268 (unsigned)effects.use_count[RC_INT][8]); 269 CU_EXPECT(u, effects.defs.cls[RC_INT] == reg_bit(1), 270 "explicit defs mismatch: %#x", effects.defs.cls[RC_INT]); 271 CU_EXPECT(u, effects.clobbers.cls[RC_INT] == reg_bit(5), 272 "machine clobbers mismatch: %#x", 273 effects.clobbers.cls[RC_INT]); 274 275 opnds[2].v.ind.base_kind = OPT_INDIRECT_FRAME; 276 opnds[2].v.ind.index_kind = OPT_INDIRECT_FRAME_ADDR; 277 opt_inst_reg_effects(&f, &in, &effects); 278 CU_EXPECT(u, effects.uses.cls[RC_INT] == reg_bit(2), 279 "frame-backed indirect components are not hard uses: %#x", 280 effects.uses.cls[RC_INT]); 281 CU_EXPECT(u, effects.clobbers.cls[RC_INT] == reg_bit(5), 282 "frame-backed components do not alter machine clobbers: %#x", 283 effects.clobbers.cls[RC_INT]); 284 } 285 286 static void emission_temps_are_not_ir_effects(KitUnit* u) { 287 Func f; 288 Inst in; 289 Operand opnds[2]; 290 OptRegEffects effects; 291 292 memset(&f, 0, sizeof f); 293 memset(&in, 0, sizeof in); 294 f.emit_temp_regs[RC_INT][0] = 8; 295 f.emit_temp_regs[RC_INT][1] = 9; 296 f.emit_temp_reg_count[RC_INT] = 2; 297 in.op = IR_COPY; 298 in.opnds = opnds; 299 in.nopnds = 2; 300 opnds[0] = reg_op(RC_INT, 8); 301 opnds[1] = reg_op(RC_INT, 2); 302 303 opt_inst_reg_effects(&f, &in, &effects); 304 CU_EXPECT(u, effects.clobbers.cls[RC_INT] == 0, 305 "emission temps must not clobber virtual HIR: %#x", 306 effects.clobbers.cls[RC_INT]); 307 308 f.opt_rewritten = 1; 309 opt_inst_reg_effects(&f, &in, &effects); 310 CU_EXPECT(u, effects.clobbers.cls[RC_INT] == 0, 311 "emission temps must remain private in location MIR: %#x", 312 effects.clobbers.cls[RC_INT]); 313 } 314 315 static void call_and_asm_effects(KitUnit* u) { 316 Func f; 317 Inst call; 318 IRCallAux call_aux; 319 CGABIValue arg; 320 OptRegEffects effects; 321 322 memset(&f, 0, sizeof f); 323 memset(&call, 0, sizeof call); 324 memset(&call_aux, 0, sizeof call_aux); 325 memset(&arg, 0, sizeof arg); 326 f.opt_caller_saved[RC_INT] = reg_bit(0) | reg_bit(1); 327 call.op = IR_CALL; 328 call.extra.aux = &call_aux; 329 call_aux.desc.callee = reg_op(RC_INT, 10); 330 call_aux.desc.args = &arg; 331 call_aux.desc.nargs = 1; 332 arg.storage = reg_op(RC_INT, 11); 333 call_aux.desc.ret.storage = reg_op(RC_INT, 0); 334 335 expect_mask_effects_match(u, &f, &call, "call"); 336 opt_inst_reg_effects(&f, &call, &effects); 337 CU_EXPECT(u, effects.uses.cls[RC_INT] == 338 (reg_bit(10) | reg_bit(11)), 339 "call uses mismatch: %#x", effects.uses.cls[RC_INT]); 340 CU_EXPECT(u, effects.defs.cls[RC_INT] == reg_bit(0), 341 "call explicit results mismatch: %#x", effects.defs.cls[RC_INT]); 342 CU_EXPECT(u, effects.clobbers.cls[RC_INT] == 343 (reg_bit(0) | reg_bit(1)), 344 "call ABI clobbers mismatch: %#x", effects.clobbers.cls[RC_INT]); 345 346 call_aux.plan_valid = 1; 347 call_aux.plan.clobber_mask[RC_INT] = reg_bit(6); 348 opt_inst_reg_effects(&f, &call, &effects); 349 CU_EXPECT(u, effects.clobbers.cls[RC_INT] == reg_bit(6), 350 "planned call clobbers must override ABI fallback: %#x", 351 effects.clobbers.cls[RC_INT]); 352 353 Inst block; 354 IRAsmAux asm_aux; 355 Operand inputs[1]; 356 Operand outputs[1]; 357 memset(&block, 0, sizeof block); 358 memset(&asm_aux, 0, sizeof asm_aux); 359 block.op = IR_ASM_BLOCK; 360 block.extra.aux = &asm_aux; 361 inputs[0] = reg_op(RC_INT, 2); 362 outputs[0] = reg_op(RC_INT, 3); 363 asm_aux.in_ops = inputs; 364 asm_aux.nin = 1; 365 asm_aux.out_ops = outputs; 366 asm_aux.nout = 1; 367 asm_aux.clobber_mask[RC_INT] = reg_bit(4); 368 369 expect_mask_effects_match(u, &f, &block, "asm"); 370 opt_inst_reg_effects(&f, &block, &effects); 371 CU_EXPECT(u, effects.uses.cls[RC_INT] == reg_bit(2), 372 "asm uses mismatch: %#x", effects.uses.cls[RC_INT]); 373 CU_EXPECT(u, effects.defs.cls[RC_INT] == reg_bit(3), 374 "asm output defs mismatch: %#x", effects.defs.cls[RC_INT]); 375 CU_EXPECT(u, effects.clobbers.cls[RC_INT] == reg_bit(4), 376 "asm clobbers mismatch: %#x", effects.clobbers.cls[RC_INT]); 377 } 378 379 static void machinize_rebuilds_derived_clobbers(KitUnit* u) { 380 KitCompiler* kc = NULL; 381 Compiler* c; 382 Func* f; 383 NativeTarget* target; 384 CGFuncDesc desc; 385 Inst* in; 386 InstId id; 387 u32 entry; 388 389 CU_EXPECT(u, 390 kit_unit_compiler_new( 391 u, kit_unit_target(KIT_ARCH_X86_64, KIT_OS_LINUX, KIT_OBJ_ELF), 392 &kc) == KIT_OK && 393 kc != NULL, 394 "compiler allocation failed for machinize rebuild test"); 395 if (!kc) return; 396 c = (Compiler*)kc; 397 memset(&desc, 0, sizeof desc); 398 desc.fn_type = kit_cg_type_builtin(kc, KIT_CG_BUILTIN_I64); 399 f = ir_func_new(c, &desc); 400 entry = ir_block_new(f); 401 f->entry = entry; 402 ir_note_emit(f, entry); 403 in = ir_emit(f, entry, IR_BINOP); 404 in->extra.imm = BO_SDIV; 405 id = in->id; 406 target = x64_native_target_new(c, NULL, NULL); 407 408 opt_machinize_native(f, target); 409 CU_EXPECT(u, 410 f->inst_clobbers && id < f->inst_clobbers_cap && 411 f->inst_clobbers[id][RC_INT] != 0, 412 "first machinization did not derive the division clobber set"); 413 414 /* Change the semantic instruction and rerun the derivation. The old table 415 * must not survive merely because the new instruction reports no fixed 416 * machine effect. */ 417 f->blocks[entry].insts[0].op = IR_COPY; 418 opt_machinize_native(f, target); 419 CU_EXPECT(u, f->inst_clobbers == NULL && f->inst_clobbers_cap == 0, 420 "machinization retained a stale per-instruction clobber table"); 421 422 f->blocks[entry].insts[0].op = IR_BINOP; 423 f->blocks[entry].insts[0].extra.imm = BO_SDIV; 424 opt_machinize_native(f, target); 425 CU_EXPECT(u, 426 f->inst_clobbers && id < f->inst_clobbers_cap && 427 f->inst_clobbers[id][RC_INT] != 0, 428 "machinization did not rebuild a cleared clobber table"); 429 kit_compiler_free(kc); 430 } 431 432 static void post_machinize_loop_imm_refreshes_shift_clobber(KitUnit* u) { 433 KitCompiler* kc = NULL; 434 Compiler* c; 435 Func* f; 436 NativeTarget* target; 437 CGFuncDesc desc; 438 KitCgTypeId i64_type; 439 PReg dst; 440 PReg lhs; 441 Inst* shift; 442 Inst* lowered_shift = NULL; 443 InstId shift_id; 444 OptRegEffects effects; 445 u32 entry; 446 u32 initial_mask; 447 u32 stale_mask; 448 449 CU_EXPECT(u, 450 kit_unit_compiler_new( 451 u, kit_unit_target(KIT_ARCH_X86_64, KIT_OS_LINUX, KIT_OBJ_ELF), 452 &kc) == KIT_OK && 453 kc != NULL, 454 "compiler allocation failed for post-machinize effect refresh"); 455 if (!kc) return; 456 c = (Compiler*)kc; 457 i64_type = kit_cg_type_builtin(kc, KIT_CG_BUILTIN_I64); 458 memset(&desc, 0, sizeof desc); 459 desc.fn_type = i64_type; 460 desc.result_type = i64_type; 461 f = ir_func_new(c, &desc); 462 entry = ir_block_new(f); 463 f->entry = entry; 464 ir_note_emit(f, entry); 465 f->blocks[entry].loop_depth = 1; 466 467 dst = ir_alloc_preg(f, i64_type, RC_INT); 468 lhs = ir_alloc_preg(f, i64_type, RC_INT); 469 shift = ir_emit(f, entry, IR_BINOP); 470 shift->type = i64_type; 471 shift->def = dst; 472 shift->extra.imm = BO_SHL; 473 shift->nopnds = 3; 474 shift->opnds = arena_array(f->arena, Operand, shift->nopnds); 475 shift->opnds[0] = typed_reg_op(RC_INT, dst, i64_type); 476 shift->opnds[1] = typed_reg_op(RC_INT, lhs, i64_type); 477 memset(&shift->opnds[2], 0, sizeof shift->opnds[2]); 478 shift->opnds[2].kind = OPK_IMM; 479 shift->opnds[2].cls = RC_INT; 480 shift->opnds[2].type = i64_type; 481 shift->opnds[2].v.imm = 64; 482 shift_id = shift->id; 483 484 target = x64_native_target_new(c, NULL, NULL); 485 opt_machinize_native(f, target); 486 initial_mask = f->inst_clobbers && shift_id < f->inst_clobbers_cap 487 ? f->inst_clobbers[shift_id][RC_INT] 488 : 0u; 489 CU_EXPECT(u, initial_mask == 0, 490 "immediate shift unexpectedly acquired an RCX clobber: %#x", 491 initial_mask); 492 493 /* This is the production pipeline order: machinize first, then lift a 494 * non-foldable loop immediate into a register. On x64 that changes the 495 * encoding to the CL form, so the final HIR boundary must rederive RCX. */ 496 opt_lower_loop_imm_operands(f, target); 497 for (u32 i = 0; i < f->blocks[entry].ninsts; ++i) { 498 Inst* in = &f->blocks[entry].insts[i]; 499 if (in->id == shift_id) lowered_shift = in; 500 } 501 CU_EXPECT(u, 502 lowered_shift && lowered_shift->nopnds == 3 && 503 lowered_shift->opnds[2].kind == OPK_REG, 504 "loop immediate was not lowered to a variable shift"); 505 506 stale_mask = f->inst_clobbers && shift_id < f->inst_clobbers_cap 507 ? f->inst_clobbers[shift_id][RC_INT] 508 : 0u; 509 CU_EXPECT(u, stale_mask == 0, 510 "modeled pre-refresh table was not the stale immediate-shift form: " 511 "%#x", 512 stale_mask); 513 514 opt_refresh_machine_clobbers(f, target); 515 CU_EXPECT(u, 516 f->inst_clobbers && shift_id < f->inst_clobbers_cap && 517 f->inst_clobbers[shift_id][RC_INT] == reg_bit(1u), 518 "post-lowering x64 shift clobbers %#x, expected RCX", 519 f->inst_clobbers && shift_id < f->inst_clobbers_cap 520 ? f->inst_clobbers[shift_id][RC_INT] 521 : 0u); 522 if (lowered_shift) { 523 opt_inst_reg_effects(f, lowered_shift, &effects); 524 CU_EXPECT(u, effects.clobbers.cls[RC_INT] == reg_bit(1u), 525 "canonical variable-shift effects omit RCX: %#x", 526 effects.clobbers.cls[RC_INT]); 527 } 528 kit_compiler_free(kc); 529 } 530 531 static void machinize_rejects_unknown_effect_registers(KitUnit* u) { 532 KitCompiler* kc = NULL; 533 Compiler* c; 534 Func* f; 535 NativeTarget* target; 536 CGFuncDesc desc; 537 MachinizeRunCtx run; 538 KitStatus status; 539 Inst* in; 540 u32 entry; 541 542 CU_EXPECT(u, 543 kit_unit_compiler_new( 544 u, kit_unit_target(KIT_ARCH_X86_64, KIT_OS_LINUX, KIT_OBJ_ELF), 545 &kc) == KIT_OK && 546 kc != NULL, 547 "compiler allocation failed for invalid machine-effect test"); 548 if (!kc) return; 549 c = (Compiler*)kc; 550 memset(&desc, 0, sizeof desc); 551 desc.fn_type = kit_cg_type_builtin(kc, KIT_CG_BUILTIN_I64); 552 f = ir_func_new(c, &desc); 553 entry = ir_block_new(f); 554 f->entry = entry; 555 ir_note_emit(f, entry); 556 in = ir_emit(f, entry, IR_BINOP); 557 in->extra.imm = BO_IADD; 558 target = x64_native_target_new(c, NULL, NULL); 559 target->machine_op_clobbers = invalid_machine_effect; 560 run.f = f; 561 run.target = target; 562 563 u->last_diag[0] = '\0'; 564 ++u->suppress_fatal; 565 status = kit_frontend_run(kc, run_machinize, &run); 566 --u->suppress_fatal; 567 CU_EXPECT(u, status == KIT_ERR, 568 "unknown target machine-effect register was accepted"); 569 CU_EXPECT(u, 570 strstr(u->last_diag, 571 "machine clobber mask names unknown physical register") != 572 NULL, 573 "unexpected invalid machine-effect diagnostic: %s", u->last_diag); 574 kit_compiler_free(kc); 575 } 576 577 static void regalloc_rebuilds_derived_constraints_and_hints(KitUnit* u) { 578 KitCompiler* kc = NULL; 579 Compiler* c; 580 Func* f; 581 NativeTarget* target; 582 CGFuncDesc desc; 583 IRRetAux* ret_aux; 584 Inst* keep_def; 585 Inst* lhs_def; 586 Inst* rhs_def; 587 Inst* div; 588 Inst* keep_use; 589 Inst* ret; 590 PReg keep; 591 PReg lhs; 592 PReg rhs; 593 PReg quotient; 594 PReg keep_sink; 595 KitCgTypeId i64_type; 596 u32 entry; 597 u32 first_frequency; 598 const u32 x64_div_clobbers = reg_bit(0u) | reg_bit(2u); /* rax, rdx */ 599 600 CU_EXPECT(u, 601 kit_unit_compiler_new( 602 u, kit_unit_target(KIT_ARCH_X86_64, KIT_OS_LINUX, KIT_OBJ_ELF), 603 &kc) == KIT_OK && 604 kc != NULL, 605 "compiler allocation failed for regalloc rebuild test"); 606 if (!kc) return; 607 c = (Compiler*)kc; 608 i64_type = kit_cg_type_builtin(kc, KIT_CG_BUILTIN_I64); 609 memset(&desc, 0, sizeof desc); 610 desc.fn_type = i64_type; 611 desc.result_type = i64_type; 612 f = ir_func_new(c, &desc); 613 entry = ir_block_new(f); 614 f->entry = entry; 615 ir_note_emit(f, entry); 616 617 keep = ir_alloc_preg(f, i64_type, RC_INT); 618 lhs = ir_alloc_preg(f, i64_type, RC_INT); 619 rhs = ir_alloc_preg(f, i64_type, RC_INT); 620 quotient = ir_alloc_preg(f, i64_type, RC_INT); 621 keep_sink = ir_alloc_preg(f, i64_type, RC_INT); 622 623 keep_def = ir_emit(f, entry, IR_LOAD_IMM); 624 keep_def->type = i64_type; 625 keep_def->def = keep; 626 keep_def->nopnds = 1; 627 keep_def->opnds = arena_array(f->arena, Operand, 1); 628 keep_def->opnds[0] = typed_reg_op(RC_INT, keep, i64_type); 629 keep_def->extra.imm = 7; 630 631 lhs_def = ir_emit(f, entry, IR_LOAD_IMM); 632 lhs_def->type = i64_type; 633 lhs_def->def = lhs; 634 lhs_def->nopnds = 1; 635 lhs_def->opnds = arena_array(f->arena, Operand, 1); 636 lhs_def->opnds[0] = typed_reg_op(RC_INT, lhs, i64_type); 637 lhs_def->extra.imm = 84; 638 639 rhs_def = ir_emit(f, entry, IR_LOAD_IMM); 640 rhs_def->type = i64_type; 641 rhs_def->def = rhs; 642 rhs_def->nopnds = 1; 643 rhs_def->opnds = arena_array(f->arena, Operand, 1); 644 rhs_def->opnds[0] = typed_reg_op(RC_INT, rhs, i64_type); 645 rhs_def->extra.imm = 2; 646 647 div = ir_emit(f, entry, IR_BINOP); 648 div->type = i64_type; 649 div->def = quotient; 650 div->extra.imm = BO_SDIV; 651 div->nopnds = 3; 652 div->opnds = arena_array(f->arena, Operand, 3); 653 div->opnds[0] = typed_reg_op(RC_INT, quotient, i64_type); 654 div->opnds[1] = typed_reg_op(RC_INT, lhs, i64_type); 655 div->opnds[2] = typed_reg_op(RC_INT, rhs, i64_type); 656 657 keep_use = ir_emit(f, entry, IR_COPY); 658 keep_use->type = i64_type; 659 keep_use->def = keep_sink; 660 keep_use->nopnds = 2; 661 keep_use->opnds = arena_array(f->arena, Operand, 2); 662 keep_use->opnds[0] = typed_reg_op(RC_INT, keep_sink, i64_type); 663 keep_use->opnds[1] = typed_reg_op(RC_INT, keep, i64_type); 664 665 ret = ir_emit(f, entry, IR_RET); 666 ret_aux = arena_zarray(f->arena, IRRetAux, 1); 667 ret->extra.aux = ret_aux; 668 ret_aux->present = 1; 669 ret_aux->val.storage = typed_reg_op(RC_INT, quotient, i64_type); 670 671 target = x64_native_target_new(c, NULL, NULL); 672 opt_build_cfg(f); 673 f->blocks[entry].frequency = 100; 674 opt_machinize_native(f, target); 675 opt_regalloc_locations(f, NULL); 676 first_frequency = f->preg_info[keep].frequency; 677 CU_EXPECT(u, 678 (f->preg_info[keep].forbidden_hard_regs & x64_div_clobbers) == 679 x64_div_clobbers, 680 "value live across div did not acquire rax/rdx forbids: %#x", 681 f->preg_info[keep].forbidden_hard_regs); 682 CU_EXPECT(u, f->preg_info[quotient].preferred_hard_reg == 0, 683 "returned quotient did not acquire the rax ABI hint: %d", 684 (int)f->preg_info[quotient].preferred_hard_reg); 685 686 /* Both fields above are derived from this particular instruction stream. 687 * Remove their sources and rerun the supported allocation entry point. A 688 * previous allocation must not become an implicit input to the next one. */ 689 div->extra.imm = BO_IADD; 690 ret_aux->present = 0; 691 f->blocks[entry].frequency = 1; 692 opt_machinize_native(f, target); 693 opt_regalloc_locations(f, NULL); 694 CU_EXPECT(u, f->preg_info[keep].forbidden_hard_regs == 0, 695 "regalloc retained stale fixed-register forbids: %#x", 696 f->preg_info[keep].forbidden_hard_regs); 697 CU_EXPECT(u, f->preg_info[quotient].preferred_hard_reg < 0, 698 "regalloc retained a stale ABI preference: %d", 699 (int)f->preg_info[quotient].preferred_hard_reg); 700 CU_EXPECT(u, f->preg_info[keep].frequency < first_frequency, 701 "regalloc retained stale range frequency %u after recomputing %u", 702 first_frequency, f->preg_info[keep].frequency); 703 704 kit_compiler_free(kc); 705 } 706 707 static void rv64_syscall_fixed_effects(KitUnit* u) { 708 KitCompiler* kc = NULL; 709 Compiler* c; 710 Func* f; 711 NativeTarget* target; 712 CGFuncDesc desc; 713 IRIntrinAux aux; 714 Inst* in; 715 InstId id; 716 u32 entry; 717 const u32 syscall_regs = (1u << 10u) | (1u << 11u) | (1u << 12u) | 718 (1u << 13u) | (1u << 14u) | (1u << 15u) | 719 (1u << 17u); 720 721 CU_EXPECT(u, 722 kit_unit_compiler_new( 723 u, kit_unit_target(KIT_ARCH_RV64, KIT_OS_LINUX, KIT_OBJ_ELF), 724 &kc) == KIT_OK && 725 kc != NULL, 726 "compiler allocation failed for rv64 syscall effect test"); 727 if (!kc) return; 728 c = (Compiler*)kc; 729 memset(&desc, 0, sizeof desc); 730 memset(&aux, 0, sizeof aux); 731 desc.fn_type = kit_cg_type_builtin(kc, KIT_CG_BUILTIN_I64); 732 f = ir_func_new(c, &desc); 733 entry = ir_block_new(f); 734 f->entry = entry; 735 ir_note_emit(f, entry); 736 in = ir_emit(f, entry, IR_INTRINSIC); 737 in->extra.aux = &aux; 738 aux.kind = INTRIN_SYSCALL; 739 id = in->id; 740 target = rv64_native_target_new(c, NULL, NULL); 741 742 opt_machinize_native(f, target); 743 CU_EXPECT(u, 744 f->inst_clobbers && id < f->inst_clobbers_cap && 745 f->inst_clobbers[id][RC_INT] == syscall_regs, 746 "rv64 syscall fixed effects mismatch: got %#x, expected %#x", 747 f->inst_clobbers && id < f->inst_clobbers_cap 748 ? f->inst_clobbers[id][RC_INT] 749 : 0u, 750 syscall_regs); 751 kit_compiler_free(kc); 752 } 753 754 static void aa64_syscall_fixed_effects(KitUnit* u) { 755 KitCompiler* kc = NULL; 756 Compiler* c; 757 Func* f; 758 NativeTarget* target; 759 CGFuncDesc desc; 760 IRIntrinAux aux; 761 Inst* in; 762 InstId id; 763 u32 entry; 764 const u32 syscall_regs = (1u << 0u) | (1u << 1u) | (1u << 2u) | 765 (1u << 3u) | (1u << 4u) | (1u << 5u) | 766 (1u << 8u); 767 768 CU_EXPECT(u, 769 kit_unit_compiler_new( 770 u, kit_unit_target(KIT_ARCH_ARM_64, KIT_OS_LINUX, KIT_OBJ_ELF), 771 &kc) == KIT_OK && 772 kc != NULL, 773 "compiler allocation failed for aa64 syscall effect test"); 774 if (!kc) return; 775 c = (Compiler*)kc; 776 memset(&desc, 0, sizeof desc); 777 memset(&aux, 0, sizeof aux); 778 desc.fn_type = kit_cg_type_builtin(kc, KIT_CG_BUILTIN_I64); 779 f = ir_func_new(c, &desc); 780 entry = ir_block_new(f); 781 f->entry = entry; 782 ir_note_emit(f, entry); 783 in = ir_emit(f, entry, IR_INTRINSIC); 784 in->extra.aux = &aux; 785 aux.kind = INTRIN_SYSCALL; 786 id = in->id; 787 target = aa64_native_target_new(c, NULL, NULL); 788 789 opt_machinize_native(f, target); 790 CU_EXPECT(u, 791 f->inst_clobbers && id < f->inst_clobbers_cap && 792 f->inst_clobbers[id][RC_INT] == syscall_regs, 793 "aa64 syscall fixed effects mismatch: got %#x, expected %#x", 794 f->inst_clobbers && id < f->inst_clobbers_cap 795 ? f->inst_clobbers[id][RC_INT] 796 : 0u, 797 syscall_regs); 798 kit_compiler_free(kc); 799 } 800 801 static void x64_tls_model_fixed_effects(KitUnit* u) { 802 static const struct { 803 KitOSKind os; 804 KitObjFmt obj; 805 u32 expected; 806 const char* name; 807 } cases[] = { 808 {KIT_OS_MACOS, KIT_OBJ_MACHO, (1u << 0u) | (1u << 7u), 809 "Mach-O descriptor"}, 810 {KIT_OS_LINUX, KIT_OBJ_ELF, 0u, "ELF local-exec"}, 811 }; 812 813 for (u32 i = 0; i < sizeof cases / sizeof cases[0]; ++i) { 814 KitCompiler* kc = NULL; 815 Compiler* c; 816 Func* f; 817 NativeTarget* target; 818 CGFuncDesc desc; 819 Inst* in; 820 InstId id; 821 u32 entry; 822 u32 got; 823 824 CU_EXPECT(u, 825 kit_unit_compiler_new( 826 u, 827 kit_unit_target(KIT_ARCH_X86_64, cases[i].os, cases[i].obj), 828 &kc) == KIT_OK && 829 kc != NULL, 830 "compiler allocation failed for x64 %s TLS effect test", 831 cases[i].name); 832 if (!kc) continue; 833 c = (Compiler*)kc; 834 memset(&desc, 0, sizeof desc); 835 desc.fn_type = kit_cg_type_builtin(kc, KIT_CG_BUILTIN_I64); 836 f = ir_func_new(c, &desc); 837 entry = ir_block_new(f); 838 f->entry = entry; 839 ir_note_emit(f, entry); 840 in = ir_emit(f, entry, IR_TLS_ADDR_OF); 841 id = in->id; 842 target = x64_native_target_new(c, NULL, NULL); 843 844 opt_machinize_native(f, target); 845 got = f->inst_clobbers && id < f->inst_clobbers_cap 846 ? f->inst_clobbers[id][RC_INT] 847 : 0u; 848 CU_EXPECT(u, got == cases[i].expected, 849 "x64 %s TLS fixed effects mismatch: got %#x, expected %#x", 850 cases[i].name, got, cases[i].expected); 851 if (!cases[i].expected) 852 CU_EXPECT(u, !f->inst_clobbers && f->inst_clobbers_cap == 0, 853 "x64 %s TLS unexpectedly allocated a clobber table", 854 cases[i].name); 855 kit_compiler_free(kc); 856 } 857 } 858 859 static void aa64_tls_model_fixed_effects(KitUnit* u) { 860 static const struct { 861 KitOSKind os; 862 KitObjFmt obj; 863 u32 expected; 864 const char* name; 865 } cases[] = { 866 {KIT_OS_MACOS, KIT_OBJ_MACHO, 1u << 0u, "Mach-O descriptor"}, 867 {KIT_OS_LINUX, KIT_OBJ_ELF, 0u, "ELF local-exec"}, 868 }; 869 870 for (u32 i = 0; i < sizeof cases / sizeof cases[0]; ++i) { 871 KitCompiler* kc = NULL; 872 Compiler* c; 873 Func* f; 874 NativeTarget* target; 875 CGFuncDesc desc; 876 Inst* in; 877 InstId id; 878 u32 entry; 879 u32 got; 880 881 CU_EXPECT(u, 882 kit_unit_compiler_new( 883 u, 884 kit_unit_target(KIT_ARCH_ARM_64, cases[i].os, cases[i].obj), 885 &kc) == KIT_OK && 886 kc != NULL, 887 "compiler allocation failed for aa64 %s TLS effect test", 888 cases[i].name); 889 if (!kc) continue; 890 c = (Compiler*)kc; 891 memset(&desc, 0, sizeof desc); 892 desc.fn_type = kit_cg_type_builtin(kc, KIT_CG_BUILTIN_I64); 893 f = ir_func_new(c, &desc); 894 entry = ir_block_new(f); 895 f->entry = entry; 896 ir_note_emit(f, entry); 897 in = ir_emit(f, entry, IR_TLS_ADDR_OF); 898 id = in->id; 899 target = aa64_native_target_new(c, NULL, NULL); 900 901 opt_machinize_native(f, target); 902 got = f->inst_clobbers && id < f->inst_clobbers_cap 903 ? f->inst_clobbers[id][RC_INT] 904 : 0u; 905 CU_EXPECT(u, got == cases[i].expected, 906 "aa64 %s TLS fixed effects mismatch: got %#x, expected %#x", 907 cases[i].name, got, cases[i].expected); 908 if (!cases[i].expected) 909 CU_EXPECT(u, !f->inst_clobbers && f->inst_clobbers_cap == 0, 910 "aa64 %s TLS unexpectedly allocated a clobber table", 911 cases[i].name); 912 kit_compiler_free(kc); 913 } 914 } 915 916 static void x64_machine_effect_families(KitUnit* u) { 917 static const struct { 918 NativeMachineOp op; 919 u32 expected; 920 const char* name; 921 } cases[] = { 922 {{.kind = NATIVE_MOP_BINOP, .binop = BO_SDIV}, 923 (1u << 0u) | (1u << 2u), "signed division"}, 924 {{.kind = NATIVE_MOP_BINOP, .binop = BO_SHL, .second_is_reg = 1u}, 925 1u << 1u, "variable shift"}, 926 {{.kind = NATIVE_MOP_BINOP, .binop = BO_SHL}, 0u, "immediate shift"}, 927 {{.kind = NATIVE_MOP_BITFIELD_LOAD}, 0u, "bitfield load"}, 928 {{.kind = NATIVE_MOP_BITFIELD_STORE}, 929 (1u << 0u) | (1u << 1u) | (1u << 2u), "bitfield store"}, 930 {{.kind = NATIVE_MOP_ATOMIC_CAS}, 931 (1u << 0u) | (1u << 1u) | (1u << 2u), "atomic compare-exchange"}, 932 {{.kind = NATIVE_MOP_ATOMIC_RMW}, 933 (1u << 0u) | (1u << 1u) | (1u << 2u), "atomic read-modify-write"}, 934 {{.kind = NATIVE_MOP_VA_START}, 0u, "va_start"}, 935 {{.kind = NATIVE_MOP_VA_ARG}, 0u, "va_arg"}, 936 {{.kind = NATIVE_MOP_INTRINSIC, .intrin = INTRIN_UMUL_OVERFLOW}, 937 (1u << 0u) | (1u << 2u), "unsigned multiply overflow"}, 938 {{.kind = NATIVE_MOP_INTRINSIC, .intrin = INTRIN_READCYCLECOUNTER}, 939 (1u << 0u) | (1u << 2u), "cycle counter"}, 940 {{.kind = NATIVE_MOP_INTRINSIC, .intrin = INTRIN_SYSCALL}, 941 (1u << 0u) | (1u << 1u) | (1u << 2u) | (1u << 6u) | (1u << 7u) | 942 (1u << 8u) | (1u << 9u), 943 "syscall"}, 944 {{.kind = NATIVE_MOP_TLS_ADDR}, 0u, "ELF local-exec TLS"}, 945 }; 946 KitCompiler* kc = NULL; 947 NativeTarget* target; 948 949 CU_EXPECT(u, 950 kit_unit_compiler_new( 951 u, kit_unit_target(KIT_ARCH_X86_64, KIT_OS_LINUX, KIT_OBJ_ELF), 952 &kc) == KIT_OK && 953 kc != NULL, 954 "compiler allocation failed for x64 machine-effect family test"); 955 if (!kc) return; 956 target = x64_native_target_new((Compiler*)kc, NULL, NULL); 957 for (u32 i = 0; i < sizeof cases / sizeof cases[0]; ++i) { 958 NativeRegMaskSet mask = {0}; 959 int any = target->machine_op_clobbers(target, &cases[i].op, mask); 960 CU_EXPECT(u, 961 mask[NATIVE_REG_INT] == cases[i].expected && 962 any == (cases[i].expected != 0u), 963 "x64 %s fixed effects mismatch: got %#x, expected %#x", 964 cases[i].name, mask[NATIVE_REG_INT], cases[i].expected); 965 } 966 kit_compiler_free(kc); 967 } 968 969 typedef NativeTarget* (*RoleTargetNew)(Compiler*, ObjBuilder*, MCEmitter*); 970 971 typedef struct RoleMaskCase { 972 KitArchKind arch; 973 KitOSKind os; 974 KitObjFmt obj; 975 RoleTargetNew target_new; 976 const char* name; 977 NativeRegMaskSet caller; 978 NativeRegMaskSet callee; 979 NativeRegMaskSet arg; 980 NativeRegMaskSet ret; 981 NativeRegMaskSet reserved; 982 } RoleMaskCase; 983 984 static void register_role_flags_are_authoritative(KitUnit* u) { 985 static const RoleMaskCase cases[] = { 986 {.arch = KIT_ARCH_X86_64, 987 .os = KIT_OS_LINUX, 988 .obj = KIT_OBJ_ELF, 989 .target_new = x64_native_target_new, 990 .name = "x64 SysV", 991 .caller = {0x00000fc7u, 0x0000ffffu, 0u}, 992 .callee = {0x0000f008u, 0u, 0u}, 993 .arg = {0x000003c6u, 0x000000ffu, 0u}, 994 .ret = {0x00000005u, 0x00000003u, 0u}, 995 .reserved = {0x00001f39u, 0x0000f030u, 0u}}, 996 {.arch = KIT_ARCH_X86_64, 997 .os = KIT_OS_WINDOWS, 998 .obj = KIT_OBJ_COFF, 999 .target_new = x64_native_target_new, 1000 .name = "x64 Win64", 1001 .caller = {0x00000f07u, 0x0000003fu, 0u}, 1002 .callee = {0x0000f0c8u, 0x0000ffc0u, 0u}, 1003 /* Argument/return flags describe the shared static x64 register table; 1004 * caller/callee preservation is the only live-ABI override. */ 1005 .arg = {0x000003c6u, 0x000000ffu, 0u}, 1006 .ret = {0x00000005u, 0x00000003u, 0u}, 1007 .reserved = {0x00001f39u, 0x0000f030u, 0u}}, 1008 {.arch = KIT_ARCH_ARM_64, 1009 .os = KIT_OS_LINUX, 1010 .obj = KIT_OBJ_ELF, 1011 .target_new = aa64_native_target_new, 1012 .name = "aa64", 1013 .caller = {0x0007ffffu, 0xffff00ffu, 0u}, 1014 .callee = {0x1ff80000u, 0x0000ff00u, 0u}, 1015 .arg = {0x000000ffu, 0x000000ffu, 0u}, 1016 .ret = {0x00000003u, 0x0000000fu, 0u}, 1017 .reserved = {0xe0070e00u, 0x00300000u, 0u}}, 1018 {.arch = KIT_ARCH_RV64, 1019 .os = KIT_OS_LINUX, 1020 .obj = KIT_OBJ_ELF, 1021 .target_new = rv64_native_target_new, 1022 .name = "rv64", 1023 .caller = {0xf003fce0u, 0xf003fcffu, 0u}, 1024 .callee = {0x0ffc0300u, 0x0ffc0300u, 0u}, 1025 .arg = {0x0003fc00u, 0x0003fc00u, 0u}, 1026 .ret = {0x00000c00u, 0x00000c00u, 0u}, 1027 .reserved = {0xf00001ffu, 0x0000000fu, 0u}}, 1028 {.arch = KIT_ARCH_ARM_32, 1029 .os = KIT_OS_FREESTANDING, 1030 .obj = KIT_OBJ_ELF, 1031 .target_new = arm32_native_target_new, 1032 .name = "arm32", 1033 .caller = {0x0000100fu, 0u, 0u}, 1034 .callee = {0x00000ff0u, 0u, 0u}, 1035 .arg = {0x0000000fu, 0u, 0u}, 1036 .ret = {0x00000003u, 0u, 0u}, 1037 .reserved = {0x0000f080u, 0u, 0u}}, 1038 }; 1039 1040 for (u32 i = 0; i < sizeof cases / sizeof cases[0]; ++i) { 1041 const RoleMaskCase* tc = &cases[i]; 1042 KitCompiler* kc = NULL; 1043 NativeTarget* target; 1044 CU_EXPECT(u, 1045 kit_unit_compiler_new( 1046 u, kit_unit_target(tc->arch, tc->os, tc->obj), &kc) == 1047 KIT_OK && 1048 kc != NULL, 1049 "compiler allocation failed for %s register roles", tc->name); 1050 if (!kc) continue; 1051 target = tc->target_new((Compiler*)kc, NULL, NULL); 1052 CU_EXPECT(u, target != NULL, "%s target allocation failed", tc->name); 1053 if (target) { 1054 native_reg_info_validate((Compiler*)kc, target->regs); 1055 for (u32 cls = 0; cls < NATIVE_REG_CLASS_COUNT; ++cls) { 1056 CU_EXPECT(u, 1057 native_target_caller_saved_mask( 1058 target, (NativeAllocClass)cls) == tc->caller[cls] && 1059 native_target_callee_saved_mask( 1060 target, (NativeAllocClass)cls) == tc->callee[cls] && 1061 native_target_arg_mask(target, (NativeAllocClass)cls) == 1062 tc->arg[cls] && 1063 native_target_ret_mask(target, (NativeAllocClass)cls) == 1064 tc->ret[cls] && 1065 native_target_reserved_mask( 1066 target, (NativeAllocClass)cls) == tc->reserved[cls], 1067 "%s class %u derived register roles mismatch", tc->name, 1068 cls); 1069 } 1070 } 1071 kit_compiler_free(kc); 1072 } 1073 } 1074 1075 int main(void) { 1076 KitUnit u; 1077 kit_unit_init(&u); 1078 explicit_and_machine_effects(&u); 1079 emission_temps_are_not_ir_effects(&u); 1080 call_and_asm_effects(&u); 1081 machinize_rebuilds_derived_clobbers(&u); 1082 post_machinize_loop_imm_refreshes_shift_clobber(&u); 1083 machinize_rejects_unknown_effect_registers(&u); 1084 regalloc_rebuilds_derived_constraints_and_hints(&u); 1085 rv64_syscall_fixed_effects(&u); 1086 aa64_syscall_fixed_effects(&u); 1087 x64_tls_model_fixed_effects(&u); 1088 aa64_tls_model_fixed_effects(&u); 1089 x64_machine_effect_families(&u); 1090 register_role_flags_are_authoritative(&u); 1091 x64_dying_fixed_sources_are_staged(&u); 1092 fprintf(stderr, "reg-effects: %d checks, %d failures\n", u.checks, u.fails); 1093 return kit_unit_status(&u); 1094 }