location_mir_test.c (43748B)
1 #include <stdio.h> 2 #include <string.h> 3 4 #include <kit/frontend.h> 5 6 #include "lib/kit_unit.h" 7 #include "opt/opt_internal.h" 8 9 typedef struct WalkSummary { 10 FrameSlot stack_uses[4]; 11 u32 nstack_uses; 12 u32 reserved_regs_seen; 13 u32 reserved_mask[OPT_REG_CLASSES]; 14 } WalkSummary; 15 16 typedef void (*PanicAction)(void*); 17 18 typedef struct PanicInvocation { 19 PanicAction action; 20 void* arg; 21 } PanicInvocation; 22 23 static KitStatus run_panicking_action(KitCompiler* kc, void* arg) { 24 PanicInvocation* call = (PanicInvocation*)arg; 25 (void)kc; 26 call->action(call->arg); 27 return KIT_OK; 28 } 29 30 static int expect_compiler_panic(KitUnit* u, Compiler* c, PanicAction action, 31 void* arg, const char* expected) { 32 PanicInvocation call; 33 KitStatus status; 34 call.action = action; 35 call.arg = arg; 36 u->last_diag[0] = '\0'; 37 ++u->suppress_fatal; 38 status = kit_frontend_run((KitCompiler*)c, run_panicking_action, &call); 39 --u->suppress_fatal; 40 return status == KIT_ERR && strstr(u->last_diag, expected) != NULL; 41 } 42 43 static Operand reg_op(PReg reg, KitCgTypeId type) { 44 Operand op; 45 memset(&op, 0, sizeof op); 46 op.kind = OPK_REG; 47 op.cls = RC_INT; 48 op.type = type; 49 op.v.reg = reg; 50 return op; 51 } 52 53 static Operand indirect_op(PReg base, PReg index, KitCgTypeId base_type, 54 KitCgTypeId index_type) { 55 Operand op; 56 memset(&op, 0, sizeof op); 57 op.kind = OPK_INDIRECT; 58 op.cls = RC_INT; 59 op.type = base_type; 60 op.v.ind.base = base; 61 op.v.ind.index = index; 62 op.v.ind.log2_scale = 3; 63 op.v.ind.base_type = base_type; 64 op.v.ind.index_type = index_type; 65 return op; 66 } 67 68 static FrameSlot spill_slot(Func* f, KitCgTypeId type) { 69 FrameSlotDesc desc; 70 memset(&desc, 0, sizeof desc); 71 desc.type = type; 72 desc.size = 8; 73 desc.align = 8; 74 desc.kind = FS_SPILL; 75 return ir_frame_slot_new(f, &desc); 76 } 77 78 static FrameSlot local_slot(Func* f, KitCgTypeId type, u32 size, u32 align) { 79 FrameSlotDesc desc; 80 memset(&desc, 0, sizeof desc); 81 desc.type = type; 82 desc.size = size; 83 desc.align = align; 84 desc.kind = FS_LOCAL; 85 return ir_frame_slot_new(f, &desc); 86 } 87 88 static void set_stack_loc(Func* f, PReg reg, FrameSlot slot) { 89 f->preg_locs[reg].kind = OPT_LOC_STACK; 90 f->preg_locs[reg].cls = RC_INT; 91 f->preg_locs[reg].spill_slot = slot; 92 f->preg_info[reg].alloc_kind = OPT_ALLOC_SPILL; 93 f->preg_info[reg].cls = RC_INT; 94 f->preg_info[reg].spill_slot = slot; 95 } 96 97 static void set_hard_loc(Func* f, PReg reg, Reg hard_reg) { 98 f->preg_locs[reg].kind = OPT_LOC_HARD; 99 f->preg_locs[reg].cls = RC_INT; 100 f->preg_locs[reg].hard_reg = hard_reg; 101 f->preg_info[reg].alloc_kind = OPT_ALLOC_HARD; 102 f->preg_info[reg].cls = RC_INT; 103 f->preg_info[reg].hard_reg = hard_reg; 104 } 105 106 typedef struct MachinizeInvocation { 107 Func* f; 108 NativeTarget* target; 109 } MachinizeInvocation; 110 111 static void run_machinize(void* arg) { 112 MachinizeInvocation* call = (MachinizeInvocation*)arg; 113 opt_machinize_native(call->f, call->target); 114 } 115 116 #ifndef NDEBUG 117 static void run_mir_verify(void* arg) { 118 opt_mir_verify((Func*)arg, "negative-temp-escape"); 119 } 120 #endif 121 122 static void machinize_rejects_cross_owner_registers(KitUnit* u) { 123 KitCompiler* kc = NULL; 124 Compiler* c; 125 CGFuncDesc desc; 126 Func* f; 127 NativePhysRegInfo phys[2]; 128 NativeAllocClassInfo cls; 129 NativeRegInfo regs; 130 NativeTarget target; 131 MachinizeInvocation call; 132 Reg emit_temp = 1u; 133 Reg asm_temp = 2u; 134 135 CU_EXPECT(u, 136 kit_unit_compiler_new( 137 u, kit_unit_target(KIT_ARCH_X86_64, KIT_OS_LINUX, KIT_OBJ_ELF), 138 &kc) == KIT_OK && 139 kc != NULL, 140 "compiler allocation failed for machinize ownership test"); 141 if (!kc) return; 142 c = (Compiler*)kc; 143 memset(&desc, 0, sizeof desc); 144 desc.fn_type = kit_cg_type_builtin(kc, KIT_CG_BUILTIN_I64); 145 f = ir_func_new(c, &desc); 146 147 memset(phys, 0, sizeof phys); 148 phys[0].reg = emit_temp; 149 phys[0].cls = NATIVE_REG_INT; 150 phys[0].flags = NATIVE_REG_ALLOCABLE | NATIVE_REG_CALLER_SAVED; 151 phys[1].reg = asm_temp; 152 phys[1].cls = NATIVE_REG_INT; 153 phys[1].flags = NATIVE_REG_CALLER_SAVED | NATIVE_REG_RESERVED; 154 memset(&cls, 0, sizeof cls); 155 cls.cls = NATIVE_REG_INT; 156 cls.emit_temps = &emit_temp; 157 cls.nemit_temps = 1u; 158 cls.phys = phys; 159 cls.nphys = 2u; 160 memset(®s, 0, sizeof regs); 161 regs.classes = &cls; 162 regs.nclasses = 1u; 163 memset(&target, 0, sizeof target); 164 target.c = c; 165 target.regs = ®s; 166 call.f = f; 167 call.target = ⌖ 168 169 CU_EXPECT(u, 170 expect_compiler_panic(u, c, run_machinize, &call, 171 "emit temp 1 is allocable"), 172 "machinize accepted a register shared by allocation and the " 173 "emitter-temp owner"); 174 175 phys[0].flags = NATIVE_REG_CALLER_SAVED; 176 cls.asm_temps = &asm_temp; 177 cls.nasm_temps = 1u; 178 opt_machinize_native(f, &target); 179 CU_EXPECT(u, 180 (f->opt_reserved_regs[NATIVE_REG_INT] & (1u << emit_temp)) != 0, 181 "machinize did not reserve the policy-owned emitter temp"); 182 183 /* The asm bank has a separate lifetime and must be statically reserved. */ 184 phys[1].flags = NATIVE_REG_CALLER_SAVED; 185 CU_EXPECT(u, 186 expect_compiler_panic(u, c, run_machinize, &call, 187 "asm temp bank is not reserved"), 188 "machinize accepted an asm-internal temp outside the reserved " 189 "register owner"); 190 191 kit_compiler_free(kc); 192 } 193 194 static void mir_verify_rejects_private_temp_escape(KitUnit* u) { 195 #ifdef NDEBUG 196 (void)u; 197 #else 198 KitCompiler* kc = NULL; 199 Compiler* c; 200 KitCgTypeId i64_type; 201 CGFuncDesc desc; 202 Func* f; 203 PReg value; 204 Inst* load; 205 u32 entry; 206 Reg temp = 9u; 207 208 CU_EXPECT(u, 209 kit_unit_compiler_new( 210 u, kit_unit_target(KIT_ARCH_X86_64, KIT_OS_LINUX, KIT_OBJ_ELF), 211 &kc) == KIT_OK && 212 kc != NULL, 213 "compiler allocation failed for MIR temp-escape test"); 214 if (!kc) return; 215 c = (Compiler*)kc; 216 i64_type = kit_cg_type_builtin(kc, KIT_CG_BUILTIN_I64); 217 memset(&desc, 0, sizeof desc); 218 desc.fn_type = i64_type; 219 desc.result_type = i64_type; 220 f = ir_func_new(c, &desc); 221 entry = ir_block_new(f); 222 f->entry = entry; 223 ir_note_emit(f, entry); 224 value = ir_alloc_preg(f, i64_type, RC_INT); 225 f->preg_locs = arena_zarray(f->arena, OptLoc, f->npregs); 226 f->preg_info = arena_zarray(f->arena, OptPRegInfo, f->npregs); 227 set_hard_loc(f, value, temp); 228 f->opt_phys_regs[RC_INT][0].reg = temp; 229 f->opt_phys_regs[RC_INT][0].cls = RC_INT; 230 f->opt_phys_reg_count[RC_INT] = 1u; 231 f->emit_temp_regs[RC_INT][0] = temp; 232 f->emit_temp_reg_count[RC_INT] = 1u; 233 f->opt_reserved_regs[RC_INT] = 1u << temp; 234 235 load = ir_emit(f, entry, IR_LOAD_IMM); 236 load->type = i64_type; 237 load->def = value; 238 load->extra.imm = 42; 239 load->nopnds = 1u; 240 load->opnds = arena_array(f->arena, Operand, 1u); 241 load->opnds[0] = reg_op(value, i64_type); 242 opt_lower_to_mir(f, NULL); 243 CU_EXPECT(u, f->mir != NULL, 244 "temp-escape fixture did not lower to location MIR"); 245 if (f->mir) { 246 CU_EXPECT(u, 247 expect_compiler_panic(u, c, run_mir_verify, f, 248 "emitter-owned temp escaped into MIR"), 249 "MIR verifier accepted an emitter-owned temporary as a " 250 "persistent value"); 251 252 f->emit_temp_reg_count[RC_INT] = 0u; 253 f->asm_temp_mask[RC_INT] = 1u << temp; 254 CU_EXPECT(u, 255 expect_compiler_panic(u, c, run_mir_verify, f, 256 "asm-internal temp escaped into MIR"), 257 "MIR verifier accepted an asm-internal temporary as a " 258 "persistent value"); 259 } 260 261 kit_compiler_free(kc); 262 #endif 263 } 264 265 static void mir_commit_propagates_instruction_namespace(KitUnit* u) { 266 KitCompiler* kc = NULL; 267 Compiler* c; 268 CGFuncDesc desc; 269 Func* f; 270 Func view; 271 InstId committed; 272 273 CU_EXPECT(u, 274 kit_unit_compiler_new( 275 u, kit_unit_target(KIT_ARCH_X86_64, KIT_OS_LINUX, KIT_OBJ_ELF), 276 &kc) == KIT_OK && 277 kc != NULL, 278 "compiler allocation failed for MIR commit contract test"); 279 if (!kc) return; 280 c = (Compiler*)kc; 281 memset(&desc, 0, sizeof desc); 282 desc.fn_type = kit_cg_type_builtin(kc, KIT_CG_BUILTIN_I64); 283 f = ir_func_new(c, &desc); 284 f->mir = arena_zarray(f->arena, MFunc, 1u); 285 286 CU_EXPECT(u, opt_mir_view(f, &view), 287 "MIR commit fixture did not produce a physical view"); 288 committed = f->next_inst_id + 7u; 289 view.next_inst_id = committed; 290 opt_mir_commit(f, &view); 291 CU_EXPECT(u, f->next_inst_id == committed, 292 "MIR commit lost the physical graph's instruction-id namespace"); 293 294 kit_compiler_free(kc); 295 } 296 297 static Inst* find_inst(Block* block, InstId id) { 298 for (u32 i = 0; i < block->ninsts; ++i) 299 if (block->insts[i].id == id) 300 return &block->insts[i]; 301 return NULL; 302 } 303 304 static void summarize_location(Func* f, Inst* in, Operand* op, int is_def, 305 void* arg) { 306 WalkSummary* summary = (WalkSummary*)arg; 307 (void)f; 308 (void)in; 309 if (op->kind == OPK_STACK && !is_def && 310 summary->nstack_uses < 311 (u32)(sizeof summary->stack_uses / sizeof summary->stack_uses[0])) { 312 summary->stack_uses[summary->nstack_uses++] = op->v.frame_slot; 313 } 314 if (op->kind == OPK_REG && op->cls < OPT_REG_CLASSES && op->v.reg < 32u && 315 (summary->reserved_mask[op->cls] & (1u << op->v.reg))) { 316 ++summary->reserved_regs_seen; 317 } 318 } 319 320 static void spilled_values_remain_locations(KitUnit* u) { 321 KitCompiler* kc = NULL; 322 Compiler* c; 323 Func* f; 324 KitCgTypeId i64_type, ptr_type; 325 CGFuncDesc desc; 326 PReg bin_dst, bin_lhs, bin_rhs, load_dst, addr_base, addr_index; 327 FrameSlot bin_dst_slot, bin_lhs_slot, bin_rhs_slot; 328 FrameSlot addr_base_slot, addr_index_slot; 329 u32 entry; 330 Inst* bin; 331 Inst* load; 332 InstId bin_id; 333 InstId load_id; 334 Block* hir_blocks; 335 Inst* hir_insts; 336 Operand* hir_bin_opnds; 337 Operand* hir_load_opnds; 338 u32* hir_succ; 339 Block* mir_block; 340 WalkSummary all; 341 WalkSummary address; 342 Func view; 343 344 CU_EXPECT(u, 345 kit_unit_compiler_new( 346 u, kit_unit_target(KIT_ARCH_ARM_64, KIT_OS_LINUX, KIT_OBJ_ELF), 347 &kc) == KIT_OK && 348 kc != NULL, 349 "compiler allocation failed"); 350 if (!kc) 351 return; 352 c = (Compiler*)kc; 353 i64_type = kit_cg_type_builtin(kc, KIT_CG_BUILTIN_I64); 354 ptr_type = kit_cg_type_ptr( 355 kc, kit_cg_type_builtin(kc, KIT_CG_BUILTIN_VOID), 0); 356 357 memset(&desc, 0, sizeof desc); 358 desc.fn_type = i64_type; 359 desc.result_type = i64_type; 360 f = ir_func_new(c, &desc); 361 entry = ir_block_new(f); 362 f->entry = entry; 363 ir_note_emit(f, entry); 364 365 bin_dst = ir_alloc_preg(f, i64_type, RC_INT); 366 bin_lhs = ir_alloc_preg(f, i64_type, RC_INT); 367 bin_rhs = ir_alloc_preg(f, i64_type, RC_INT); 368 load_dst = ir_alloc_preg(f, i64_type, RC_INT); 369 addr_base = ir_alloc_preg(f, ptr_type, RC_INT); 370 addr_index = ir_alloc_preg(f, i64_type, RC_INT); 371 372 bin_dst_slot = spill_slot(f, i64_type); 373 bin_lhs_slot = spill_slot(f, i64_type); 374 bin_rhs_slot = spill_slot(f, i64_type); 375 /* Spill coloring may reuse a physical slot across unrelated type IDs. Keep 376 * intentionally mismatched descriptor types here: location MIR must carry 377 * the base/index value types rather than reconstructing them from the slot. */ 378 addr_base_slot = spill_slot(f, i64_type); 379 addr_index_slot = spill_slot(f, ptr_type); 380 381 f->preg_locs = arena_zarray(f->arena, OptLoc, f->npregs); 382 f->preg_info = arena_zarray(f->arena, OptPRegInfo, f->npregs); 383 set_stack_loc(f, bin_dst, bin_dst_slot); 384 set_stack_loc(f, bin_lhs, bin_lhs_slot); 385 set_stack_loc(f, bin_rhs, bin_rhs_slot); 386 set_hard_loc(f, load_dst, 1); 387 set_stack_loc(f, addr_base, addr_base_slot); 388 set_stack_loc(f, addr_index, addr_index_slot); 389 390 /* These are emitter-private temporaries. They may be selected while native 391 * code is emitted, but must never become values in post-regalloc MIR. */ 392 f->emit_temp_regs[RC_INT][0] = 9; 393 f->emit_temp_regs[RC_INT][1] = 10; 394 f->emit_temp_regs[RC_INT][2] = 11; 395 f->emit_temp_reg_count[RC_INT] = 3; 396 f->opt_reserved_regs[RC_INT] = (1u << 9) | (1u << 10) | (1u << 11); 397 398 bin = ir_emit(f, entry, IR_BINOP); 399 bin->type = i64_type; 400 bin->def = bin_dst; 401 bin->extra.imm = BO_IADD; 402 bin->nopnds = 3; 403 bin->opnds = arena_array(f->arena, Operand, bin->nopnds); 404 bin->opnds[0] = reg_op(bin_dst, i64_type); 405 bin->opnds[1] = reg_op(bin_lhs, i64_type); 406 bin->opnds[2] = reg_op(bin_rhs, i64_type); 407 bin_id = bin->id; 408 409 load = ir_emit(f, entry, IR_LOAD); 410 load->type = i64_type; 411 load->def = load_dst; 412 load->nopnds = 2; 413 load->opnds = arena_array(f->arena, Operand, load->nopnds); 414 load->opnds[0] = reg_op(load_dst, i64_type); 415 load->opnds[1] = indirect_op(addr_base, addr_index, ptr_type, i64_type); 416 load->extra.mem.type = i64_type; 417 load->extra.mem.size = 8; 418 load->extra.mem.align = 8; 419 load_id = load->id; 420 421 /* Capture the semantic HIR allocation after the last ir_emit (which may 422 * grow the block's instruction array). Lowering must build a physically 423 * separate MIR graph and leave each virtual operand in this graph intact. */ 424 hir_blocks = f->blocks; 425 hir_insts = f->blocks[entry].insts; 426 hir_bin_opnds = find_inst(&f->blocks[entry], bin_id)->opnds; 427 hir_load_opnds = find_inst(&f->blocks[entry], load_id)->opnds; 428 hir_succ = f->blocks[entry].succ; 429 430 opt_lower_to_mir(f, NULL); 431 CU_EXPECT(u, f->mir != NULL, "lowering did not produce MIR"); 432 if (!f->mir) { 433 kit_compiler_free(kc); 434 return; 435 } 436 mir_block = &f->mir->blocks[entry]; 437 bin = find_inst(mir_block, bin_id); 438 load = find_inst(mir_block, load_id); 439 440 CU_EXPECT(u, f->blocks == hir_blocks && f->blocks != f->mir->blocks, 441 "lowering did not preserve a distinct HIR block graph"); 442 CU_EXPECT(u, 443 f->mir->blocks_cap == f->mir->nblocks && 444 f->mir->emit_order_cap == f->mir->emit_order_n && 445 f->mir->blocks[entry].cap == 446 f->mir->blocks[entry].ninsts && 447 f->mir->blocks[entry].succ_cap == 448 f->mir->blocks[entry].nsucc && 449 opt_mir_view(f, &view) && view.blocks == f->mir->blocks && 450 view.blocks_cap == f->mir->blocks_cap && view.mir == NULL && 451 view.scope_aux_inst == NULL && view.nscopes == 0, 452 "MIR retained recording slack or stale HIR graph metadata"); 453 CU_EXPECT(u, 454 f->blocks[entry].insts == hir_insts && 455 f->mir->blocks[entry].insts != hir_insts, 456 "lowering aliases HIR and MIR instruction arrays"); 457 CU_EXPECT(u, 458 f->blocks[entry].succ == hir_succ && 459 f->mir->blocks[entry].succ != hir_succ, 460 "lowering aliases HIR and MIR CFG arrays"); 461 { 462 Inst* hir_bin = find_inst(&f->blocks[entry], bin_id); 463 Inst* hir_load = find_inst(&f->blocks[entry], load_id); 464 CU_EXPECT(u, 465 hir_bin && hir_bin->opnds == hir_bin_opnds && 466 hir_bin->opnds[0].kind == OPK_REG && 467 hir_bin->opnds[0].v.reg == bin_dst && 468 hir_bin->opnds[1].v.reg == bin_lhs && 469 hir_bin->opnds[2].v.reg == bin_rhs && bin && 470 bin->opnds != hir_bin_opnds, 471 "location rewrite changed or aliased HIR binop operands"); 472 CU_EXPECT(u, 473 hir_load && hir_load->opnds == hir_load_opnds && 474 hir_load->opnds[0].kind == OPK_REG && 475 hir_load->opnds[0].v.reg == load_dst && 476 hir_load->opnds[1].kind == OPK_INDIRECT && 477 hir_load->opnds[1].v.ind.base == addr_base && 478 hir_load->opnds[1].v.ind.index == addr_index && load && 479 load->opnds != hir_load_opnds, 480 "location rewrite changed or aliased HIR address operands"); 481 } 482 483 CU_EXPECT(u, f->opt_rewrite_inserted_insts == 0, 484 "location lowering inserted %llu reload/store instructions", 485 (unsigned long long)f->opt_rewrite_inserted_insts); 486 CU_EXPECT(u, mir_block->ninsts == 2, 487 "location MIR should preserve two source instructions, got %u", 488 mir_block->ninsts); 489 CU_EXPECT(u, bin != NULL && bin->op == IR_BINOP, 490 "original binop did not survive location lowering"); 491 CU_EXPECT(u, load != NULL && load->op == IR_LOAD, 492 "original indirect load did not survive location lowering"); 493 CU_EXPECT(u, 494 load && load->opnds[1].kind == OPK_INDIRECT && 495 load->opnds[1].v.ind.base_kind == OPT_INDIRECT_FRAME && 496 load->opnds[1].v.ind.index_kind == OPT_INDIRECT_FRAME && 497 load->opnds[1].v.ind.base_type == ptr_type && 498 load->opnds[1].v.ind.index_type == i64_type, 499 "indirect component location/type provenance was not preserved"); 500 501 /* OPK_STACK is the post-allocation scalar-value location. OPK_LOCAL remains 502 * a semantic frame lvalue/address, so accepting it here would collapse the 503 * exact boundary this test protects. */ 504 if (bin && bin->nopnds == 3) { 505 CU_EXPECT(u, 506 bin->opnds[0].kind == OPK_STACK && 507 bin->opnds[0].v.frame_slot == bin_dst_slot, 508 "spilled scalar def is not its stable frame location"); 509 CU_EXPECT(u, 510 bin->opnds[1].kind == OPK_STACK && 511 bin->opnds[1].v.frame_slot == bin_lhs_slot, 512 "first spilled scalar use is not its stable frame location"); 513 CU_EXPECT(u, 514 bin->opnds[2].kind == OPK_STACK && 515 bin->opnds[2].v.frame_slot == bin_rhs_slot, 516 "second spilled scalar use is not its stable frame location"); 517 } else { 518 CU_EXPECT(u, 0, "lowered binop has the wrong operand shape"); 519 } 520 521 memset(&address, 0, sizeof address); 522 address.reserved_mask[RC_INT] = f->opt_reserved_regs[RC_INT]; 523 if (load) 524 opt_walk_inst_operands(f, load, summarize_location, &address); 525 CU_EXPECT(u, 526 address.nstack_uses == 2 && 527 address.stack_uses[0] == addr_base_slot && 528 address.stack_uses[1] == addr_index_slot, 529 "spilled indirect base/index are not stable frame locations"); 530 531 memset(&all, 0, sizeof all); 532 for (u32 cls = 0; cls < OPT_REG_CLASSES; ++cls) 533 all.reserved_mask[cls] = f->opt_reserved_regs[cls]; 534 for (u32 i = 0; i < mir_block->ninsts; ++i) 535 opt_walk_inst_operands(f, &mir_block->insts[i], summarize_location, &all); 536 CU_EXPECT(u, all.reserved_regs_seen == 0, 537 "MIR exposes %u emitter-private register operands", 538 all.reserved_regs_seen); 539 540 kit_compiler_free(kc); 541 } 542 543 static void spilled_frame_address_remains_recipe(KitUnit* u) { 544 KitCompiler* kc = NULL; 545 Compiler* c; 546 Func* f; 547 KitCgTypeId i32_type, ptr_type; 548 CGFuncDesc desc; 549 PReg address, copy; 550 FrameSlot value_slot, address_spill, copy_spill; 551 u32 entry; 552 Inst* addr; 553 Inst* move; 554 InstId addr_id; 555 InstId move_id; 556 557 CU_EXPECT(u, 558 kit_unit_compiler_new( 559 u, kit_unit_target(KIT_ARCH_ARM_64, KIT_OS_LINUX, KIT_OBJ_ELF), 560 &kc) == KIT_OK && 561 kc != NULL, 562 "compiler allocation failed for frame-address recipe test"); 563 if (!kc) return; 564 c = (Compiler*)kc; 565 i32_type = kit_cg_type_builtin(kc, KIT_CG_BUILTIN_I32); 566 ptr_type = kit_cg_type_ptr(kc, i32_type, 0); 567 568 memset(&desc, 0, sizeof desc); 569 desc.fn_type = ptr_type; 570 desc.result_type = ptr_type; 571 f = ir_func_new(c, &desc); 572 entry = ir_block_new(f); 573 f->entry = entry; 574 ir_note_emit(f, entry); 575 576 address = ir_alloc_preg(f, ptr_type, RC_INT); 577 copy = ir_alloc_preg(f, ptr_type, RC_INT); 578 value_slot = local_slot(f, i32_type, 4, 4); 579 address_spill = spill_slot(f, ptr_type); 580 copy_spill = spill_slot(f, ptr_type); 581 f->preg_locs = arena_zarray(f->arena, OptLoc, f->npregs); 582 f->preg_info = arena_zarray(f->arena, OptPRegInfo, f->npregs); 583 set_stack_loc(f, address, address_spill); 584 set_stack_loc(f, copy, copy_spill); 585 586 addr = ir_emit(f, entry, IR_ADDR_OF); 587 addr_id = addr->id; 588 addr->type = ptr_type; 589 addr->def = address; 590 addr->nopnds = 2; 591 addr->opnds = arena_zarray(f->arena, Operand, addr->nopnds); 592 addr->opnds[0] = reg_op(address, ptr_type); 593 addr->opnds[1].kind = OPK_LOCAL; 594 addr->opnds[1].cls = RC_INT; 595 addr->opnds[1].type = i32_type; 596 addr->opnds[1].v.frame_slot = value_slot; 597 598 move = ir_emit(f, entry, IR_COPY); 599 move_id = move->id; 600 move->type = ptr_type; 601 move->def = copy; 602 move->nopnds = 2; 603 move->opnds = arena_zarray(f->arena, Operand, move->nopnds); 604 move->opnds[0] = reg_op(copy, ptr_type); 605 move->opnds[1] = reg_op(address, ptr_type); 606 607 opt_lower_to_mir(f, NULL); 608 opt_mir_combine(f, NULL); 609 opt_mir_dce(f); 610 opt_mir_jump_cleanup(f, OPT_JUMP_CLEANUP_CFG); 611 opt_mir_build_cfg(f); 612 opt_mir_jump_cleanup(f, OPT_JUMP_CLEANUP_LAYOUT); 613 CU_EXPECT(u, f->mir != NULL, "frame-address lowering did not produce MIR"); 614 if (f->mir) { 615 Block* block = &f->mir->blocks[entry]; 616 Inst* mir_addr = find_inst(block, addr_id); 617 Inst* mir_move = find_inst(block, move_id); 618 CU_EXPECT(u, mir_addr == NULL, 619 "rematerialized frame-address producer survived MIR lowering"); 620 CU_EXPECT(u, block->ninsts == 1 && mir_move && mir_move->nopnds == 2, 621 "frame-address recipe lowering has the wrong instruction shape"); 622 CU_EXPECT(u, 623 mir_move && mir_move->opnds[0].kind == OPK_STACK && 624 mir_move->opnds[0].v.frame_slot == copy_spill, 625 "frame-address copy destination is not its spill home"); 626 CU_EXPECT(u, 627 mir_move && mir_move->opnds[1].kind == OPK_FRAME_ADDR && 628 mir_move->opnds[1].v.frame_slot == value_slot, 629 "frame-address recipe was rewritten as an unrelated value"); 630 } 631 addr = find_inst(&f->blocks[entry], addr_id); 632 move = find_inst(&f->blocks[entry], move_id); 633 CU_EXPECT(u, 634 addr && addr->opnds[1].kind == OPK_LOCAL && 635 addr->opnds[1].v.frame_slot == value_slot && move && 636 move->opnds[1].kind == OPK_REG && 637 move->opnds[1].v.reg == address, 638 "frame-address location lowering mutated semantic HIR"); 639 640 kit_compiler_free(kc); 641 } 642 643 static void call_aux_is_independent(KitUnit* u) { 644 KitCompiler* kc = NULL; 645 Compiler* c; 646 Func* f; 647 KitCgTypeId i64_type; 648 CGFuncDesc desc; 649 PReg callee_reg, arg_reg, arg_part_reg, ret_reg, ret_part_reg; 650 IRCallAux* hir_aux; 651 IRCallAux* mir_aux; 652 Inst* call; 653 InstId call_id; 654 u32 entry; 655 656 CU_EXPECT(u, 657 kit_unit_compiler_new( 658 u, kit_unit_target(KIT_ARCH_ARM_64, KIT_OS_LINUX, KIT_OBJ_ELF), 659 &kc) == KIT_OK && 660 kc != NULL, 661 "compiler allocation failed for call-aux clone test"); 662 if (!kc) return; 663 c = (Compiler*)kc; 664 i64_type = kit_cg_type_builtin(kc, KIT_CG_BUILTIN_I64); 665 666 memset(&desc, 0, sizeof desc); 667 desc.fn_type = i64_type; 668 f = ir_func_new(c, &desc); 669 entry = ir_block_new(f); 670 f->entry = entry; 671 ir_note_emit(f, entry); 672 673 callee_reg = ir_alloc_preg(f, i64_type, RC_INT); 674 arg_reg = ir_alloc_preg(f, i64_type, RC_INT); 675 arg_part_reg = ir_alloc_preg(f, i64_type, RC_INT); 676 ret_reg = ir_alloc_preg(f, i64_type, RC_INT); 677 ret_part_reg = ir_alloc_preg(f, i64_type, RC_INT); 678 f->preg_locs = arena_zarray(f->arena, OptLoc, f->npregs); 679 f->preg_info = arena_zarray(f->arena, OptPRegInfo, f->npregs); 680 set_hard_loc(f, callee_reg, 1); 681 set_hard_loc(f, arg_reg, 2); 682 set_hard_loc(f, arg_part_reg, 3); 683 set_hard_loc(f, ret_reg, 4); 684 set_hard_loc(f, ret_part_reg, 5); 685 686 call = ir_emit(f, entry, IR_CALL); 687 call_id = call->id; 688 call->def = ret_reg; 689 hir_aux = arena_znew(f->arena, IRCallAux); 690 hir_aux->desc.callee = reg_op(callee_reg, i64_type); 691 hir_aux->desc.nargs = 1; 692 hir_aux->desc.args = arena_zarray(f->arena, CGABIValue, 1); 693 hir_aux->desc.args[0].type = i64_type; 694 hir_aux->desc.args[0].storage = reg_op(arg_reg, i64_type); 695 hir_aux->desc.args[0].nparts = 1; 696 hir_aux->desc.args[0].parts = arena_zarray(f->arena, CGABIPart, 1); 697 hir_aux->desc.args[0].parts[0].op = reg_op(arg_part_reg, i64_type); 698 hir_aux->desc.ret.type = i64_type; 699 hir_aux->desc.ret.storage = reg_op(ret_reg, i64_type); 700 hir_aux->desc.ret.nparts = 1; 701 hir_aux->desc.ret.parts = arena_zarray(f->arena, CGABIPart, 1); 702 hir_aux->desc.ret.parts[0].op = reg_op(ret_part_reg, i64_type); 703 hir_aux->plan.callee = reg_op(callee_reg, i64_type); 704 hir_aux->plan.nargs = 2; 705 hir_aux->plan.args = 706 arena_zarray(f->arena, CGCallPlanMove, hir_aux->plan.nargs); 707 hir_aux->plan.args[0].src = reg_op(arg_reg, i64_type); 708 hir_aux->plan.args[0].dst = reg_op(arg_part_reg, i64_type); 709 hir_aux->plan.args[0].src_kind = CG_CALL_PLAN_SRC_VALUE; 710 hir_aux->plan.args[0].dst_kind = CG_CALL_PLAN_REG; 711 hir_aux->plan.args[0].dst_reg = 3; 712 hir_aux->plan.args[1].src = reg_op(arg_part_reg, i64_type); 713 hir_aux->plan.args[1].dst_kind = CG_CALL_PLAN_STACK; 714 hir_aux->plan.args[1].stack_offset = 24; 715 hir_aux->plan.nrets = 2; 716 hir_aux->plan.rets = 717 arena_zarray(f->arena, CGCallPlanRet, hir_aux->plan.nrets); 718 hir_aux->plan.rets[0].dst = reg_op(ret_reg, i64_type); 719 hir_aux->plan.rets[0].src_reg = 4; 720 hir_aux->plan.rets[1].dst = reg_op(ret_part_reg, i64_type); 721 hir_aux->plan.rets[1].src_reg = 5; 722 hir_aux->plan_valid = 1; 723 hir_aux->nresults = 1; 724 hir_aux->results = arena_array(f->arena, Val, 1); 725 hir_aux->results[0] = ret_reg; 726 call->extra.aux = hir_aux; 727 728 opt_lower_to_mir(f, NULL); 729 call = f->mir ? find_inst(&f->mir->blocks[entry], call_id) : NULL; 730 mir_aux = call ? (IRCallAux*)call->extra.aux : NULL; 731 CU_EXPECT(u, call && mir_aux && mir_aux != hir_aux, 732 "call aux object aliases HIR after MIR lowering"); 733 CU_EXPECT(u, 734 mir_aux && mir_aux->desc.args != hir_aux->desc.args && 735 mir_aux->desc.args[0].parts != hir_aux->desc.args[0].parts && 736 mir_aux->desc.ret.parts != hir_aux->desc.ret.parts && 737 mir_aux->plan.args != hir_aux->plan.args && 738 mir_aux->plan.rets != hir_aux->plan.rets && 739 mir_aux->results != hir_aux->results, 740 "call aux nested arrays alias HIR after MIR lowering"); 741 CU_EXPECT(u, 742 hir_aux->desc.callee.v.reg == callee_reg && 743 hir_aux->desc.args[0].storage.v.reg == arg_reg && 744 hir_aux->desc.args[0].parts[0].op.v.reg == arg_part_reg && 745 hir_aux->desc.ret.storage.v.reg == ret_reg && 746 hir_aux->desc.ret.parts[0].op.v.reg == ret_part_reg && 747 mir_aux && mir_aux->desc.callee.v.reg == 1 && 748 mir_aux->desc.args[0].storage.v.reg == 2 && 749 mir_aux->desc.args[0].parts[0].op.v.reg == 3 && 750 mir_aux->desc.ret.storage.v.reg == 4 && 751 mir_aux->desc.ret.parts[0].op.v.reg == 5, 752 "call operand rewrite crossed the HIR/MIR ownership boundary"); 753 CU_EXPECT(u, 754 mir_aux && mir_aux->plan.nargs == 2 && mir_aux->plan.nrets == 2 && 755 mir_aux->plan.args[0].src.v.reg == arg_reg && 756 mir_aux->plan.args[1].stack_offset == 24 && 757 mir_aux->plan.rets[0].dst.v.reg == ret_reg && 758 mir_aux->plan.rets[1].src_reg == 5, 759 "call plan arrays did not survive MIR cloning"); 760 761 if (mir_aux) { 762 mir_aux->plan.args[0].src.kind = OPK_IMM; 763 mir_aux->plan.args[0].src.v.imm = 91; 764 mir_aux->plan.args[1].stack_offset = 88; 765 mir_aux->plan.rets[0].dst.v.reg = 27; 766 mir_aux->plan.rets[1].src_reg = 28; 767 } 768 CU_EXPECT(u, 769 hir_aux->plan.args[0].src.kind == OPK_REG && 770 hir_aux->plan.args[0].src.v.reg == arg_reg && 771 hir_aux->plan.args[1].stack_offset == 24 && 772 hir_aux->plan.rets[0].dst.v.reg == ret_reg && 773 hir_aux->plan.rets[1].src_reg == 5, 774 "mutating MIR call-plan arrays changed semantic HIR"); 775 776 hir_aux->plan.args[1].stack_offset = 104; 777 hir_aux->plan.rets[1].src_reg = 30; 778 CU_EXPECT(u, 779 mir_aux && mir_aux->plan.args[1].stack_offset == 88 && 780 mir_aux->plan.rets[1].src_reg == 28, 781 "mutating HIR call-plan arrays changed location MIR"); 782 783 kit_compiler_free(kc); 784 } 785 786 static void structured_aux_arrays_are_independent(KitUnit* u) { 787 KitCompiler* kc = NULL; 788 Compiler* c; 789 Func* f; 790 KitCgTypeId i64_type; 791 CGFuncDesc desc; 792 PReg dst0, dst1, arg0, arg1; 793 Inst* phi; 794 Inst* sw; 795 Inst* indirect; 796 Inst* intrin; 797 InstId phi_id, switch_id, indirect_id, intrin_id; 798 IRPhiAux* hir_phi; 799 IRPhiAux* mir_phi; 800 IRSwitchAux* hir_switch; 801 IRSwitchAux* mir_switch; 802 IRIndirectAux* hir_indirect; 803 IRIndirectAux* mir_indirect; 804 IRIntrinAux* hir_intrin; 805 IRIntrinAux* mir_intrin; 806 u32 entry; 807 808 CU_EXPECT(u, 809 kit_unit_compiler_new( 810 u, kit_unit_target(KIT_ARCH_ARM_64, KIT_OS_LINUX, KIT_OBJ_ELF), 811 &kc) == KIT_OK && 812 kc != NULL, 813 "compiler allocation failed for structured-aux clone test"); 814 if (!kc) return; 815 c = (Compiler*)kc; 816 i64_type = kit_cg_type_builtin(kc, KIT_CG_BUILTIN_I64); 817 818 memset(&desc, 0, sizeof desc); 819 desc.fn_type = i64_type; 820 f = ir_func_new(c, &desc); 821 entry = ir_block_new(f); 822 f->entry = entry; 823 ir_note_emit(f, entry); 824 825 dst0 = ir_alloc_preg(f, i64_type, RC_INT); 826 dst1 = ir_alloc_preg(f, i64_type, RC_INT); 827 arg0 = ir_alloc_preg(f, i64_type, RC_INT); 828 arg1 = ir_alloc_preg(f, i64_type, RC_INT); 829 f->preg_locs = arena_zarray(f->arena, OptLoc, f->npregs); 830 f->preg_info = arena_zarray(f->arena, OptPRegInfo, f->npregs); 831 set_hard_loc(f, dst0, 8); 832 set_hard_loc(f, dst1, 9); 833 set_hard_loc(f, arg0, 10); 834 set_hard_loc(f, arg1, 11); 835 836 phi = ir_emit(f, entry, IR_PHI); 837 phi_id = phi->id; 838 hir_phi = arena_znew(f->arena, IRPhiAux); 839 hir_phi->npreds = 2; 840 hir_phi->pred_blocks = arena_array(f->arena, u32, hir_phi->npreds); 841 hir_phi->pred_vals = arena_array(f->arena, Val, hir_phi->npreds); 842 hir_phi->pred_blocks[0] = 3; 843 hir_phi->pred_blocks[1] = 4; 844 hir_phi->pred_vals[0] = 13; 845 hir_phi->pred_vals[1] = 14; 846 phi->extra.aux = hir_phi; 847 848 sw = ir_emit(f, entry, IR_SWITCH); 849 switch_id = sw->id; 850 hir_switch = arena_znew(f->arena, IRSwitchAux); 851 hir_switch->ncases = 2; 852 hir_switch->cases = 853 arena_zarray(f->arena, IRSwitchAuxCase, hir_switch->ncases); 854 hir_switch->cases[0].value = 21; 855 hir_switch->cases[0].block = 5; 856 hir_switch->cases[1].value = 22; 857 hir_switch->cases[1].block = 6; 858 sw->extra.aux = hir_switch; 859 860 indirect = ir_emit(f, entry, IR_INDIRECT_BRANCH); 861 indirect_id = indirect->id; 862 hir_indirect = arena_znew(f->arena, IRIndirectAux); 863 hir_indirect->ntargets = 2; 864 hir_indirect->targets = arena_array(f->arena, u32, hir_indirect->ntargets); 865 hir_indirect->targets[0] = 7; 866 hir_indirect->targets[1] = 8; 867 indirect->extra.aux = hir_indirect; 868 869 intrin = ir_emit(f, entry, IR_INTRINSIC); 870 intrin_id = intrin->id; 871 hir_intrin = arena_znew(f->arena, IRIntrinAux); 872 hir_intrin->kind = INTRIN_SADD_OVERFLOW; 873 hir_intrin->ndst = 2; 874 hir_intrin->narg = 2; 875 hir_intrin->dsts = arena_zarray(f->arena, Operand, hir_intrin->ndst); 876 hir_intrin->args = arena_zarray(f->arena, Operand, hir_intrin->narg); 877 hir_intrin->result_vals = arena_array(f->arena, Val, hir_intrin->ndst); 878 hir_intrin->dsts[0] = reg_op(dst0, i64_type); 879 hir_intrin->dsts[1] = reg_op(dst1, i64_type); 880 hir_intrin->args[0] = reg_op(arg0, i64_type); 881 hir_intrin->args[1] = reg_op(arg1, i64_type); 882 hir_intrin->result_vals[0] = 31; 883 hir_intrin->result_vals[1] = 32; 884 intrin->extra.aux = hir_intrin; 885 886 opt_lower_to_mir(f, NULL); 887 phi = f->mir ? find_inst(&f->mir->blocks[entry], phi_id) : NULL; 888 sw = f->mir ? find_inst(&f->mir->blocks[entry], switch_id) : NULL; 889 indirect = 890 f->mir ? find_inst(&f->mir->blocks[entry], indirect_id) : NULL; 891 intrin = f->mir ? find_inst(&f->mir->blocks[entry], intrin_id) : NULL; 892 mir_phi = phi ? (IRPhiAux*)phi->extra.aux : NULL; 893 mir_switch = sw ? (IRSwitchAux*)sw->extra.aux : NULL; 894 mir_indirect = indirect ? (IRIndirectAux*)indirect->extra.aux : NULL; 895 mir_intrin = intrin ? (IRIntrinAux*)intrin->extra.aux : NULL; 896 897 CU_EXPECT(u, 898 mir_phi && mir_phi != hir_phi && 899 mir_phi->pred_blocks != hir_phi->pred_blocks && 900 mir_phi->pred_vals != hir_phi->pred_vals, 901 "phi aux arrays alias semantic HIR"); 902 CU_EXPECT(u, 903 mir_switch && mir_switch != hir_switch && 904 mir_switch->cases != hir_switch->cases, 905 "switch case array aliases semantic HIR"); 906 CU_EXPECT(u, 907 mir_indirect && mir_indirect != hir_indirect && 908 mir_indirect->targets != hir_indirect->targets, 909 "indirect-branch target array aliases semantic HIR"); 910 CU_EXPECT(u, 911 mir_intrin && mir_intrin != hir_intrin && 912 mir_intrin->dsts != hir_intrin->dsts && 913 mir_intrin->args != hir_intrin->args && 914 mir_intrin->result_vals != hir_intrin->result_vals, 915 "intrinsic aux arrays alias semantic HIR"); 916 CU_EXPECT(u, 917 mir_phi && mir_phi->pred_blocks[0] == 3 && 918 mir_phi->pred_vals[1] == 14 && mir_switch && 919 mir_switch->cases[1].value == 22 && 920 mir_switch->cases[1].block == 6 && mir_indirect && 921 mir_indirect->targets[1] == 8 && mir_intrin && 922 mir_intrin->result_vals[0] == 31 && 923 mir_intrin->dsts[0].v.reg == 8 && 924 mir_intrin->dsts[1].v.reg == 9 && 925 mir_intrin->args[0].v.reg == 10 && 926 mir_intrin->args[1].v.reg == 11, 927 "structured aux payloads did not survive location-MIR cloning"); 928 929 if (mir_phi) { 930 mir_phi->pred_blocks[0] = 103; 931 mir_phi->pred_vals[0] = 113; 932 } 933 if (mir_switch) { 934 mir_switch->cases[0].value = 121; 935 mir_switch->cases[0].block = 105; 936 } 937 if (mir_indirect) mir_indirect->targets[0] = 107; 938 if (mir_intrin) { 939 mir_intrin->dsts[0].kind = OPK_IMM; 940 mir_intrin->dsts[0].v.imm = 131; 941 mir_intrin->args[0].kind = OPK_IMM; 942 mir_intrin->args[0].v.imm = 132; 943 mir_intrin->result_vals[0] = 133; 944 } 945 CU_EXPECT(u, 946 hir_phi->pred_blocks[0] == 3 && hir_phi->pred_vals[0] == 13 && 947 hir_switch->cases[0].value == 21 && 948 hir_switch->cases[0].block == 5 && 949 hir_indirect->targets[0] == 7 && 950 hir_intrin->dsts[0].kind == OPK_REG && 951 hir_intrin->dsts[0].v.reg == dst0 && 952 hir_intrin->args[0].kind == OPK_REG && 953 hir_intrin->args[0].v.reg == arg0 && 954 hir_intrin->result_vals[0] == 31, 955 "mutating MIR structured aux arrays changed semantic HIR"); 956 957 hir_phi->pred_blocks[1] = 204; 958 hir_phi->pred_vals[1] = 214; 959 hir_switch->cases[1].value = 222; 960 hir_switch->cases[1].block = 206; 961 hir_indirect->targets[1] = 208; 962 hir_intrin->dsts[1].v.reg = dst0; 963 hir_intrin->args[1].v.reg = arg0; 964 hir_intrin->result_vals[1] = 232; 965 CU_EXPECT(u, 966 mir_phi && mir_phi->pred_blocks[1] == 4 && 967 mir_phi->pred_vals[1] == 14 && mir_switch && 968 mir_switch->cases[1].value == 22 && 969 mir_switch->cases[1].block == 6 && mir_indirect && 970 mir_indirect->targets[1] == 8 && mir_intrin && 971 mir_intrin->dsts[1].v.reg == 9 && 972 mir_intrin->args[1].v.reg == 11 && 973 mir_intrin->result_vals[1] == 32, 974 "mutating HIR structured aux arrays changed location MIR"); 975 976 kit_compiler_free(kc); 977 } 978 979 static void asm_aux_resolved_metadata_is_independent(KitUnit* u) { 980 KitCompiler* kc = NULL; 981 Compiler* c; 982 Func* f; 983 KitCgTypeId i64_type; 984 CGFuncDesc desc; 985 PReg out_reg, in_reg; 986 IRAsmAux* hir_aux; 987 IRAsmAux* mir_aux; 988 Inst* asm_in; 989 InstId asm_id; 990 FrameSlot memory_slot; 991 FrameSlotDesc memory_desc; 992 Sym out_name, in_name, clobber_name; 993 u32 entry; 994 995 CU_EXPECT(u, 996 kit_unit_compiler_new( 997 u, kit_unit_target(KIT_ARCH_ARM_64, KIT_OS_LINUX, KIT_OBJ_ELF), 998 &kc) == KIT_OK && 999 kc != NULL, 1000 "compiler allocation failed for asm-aux clone test"); 1001 if (!kc) return; 1002 c = (Compiler*)kc; 1003 i64_type = kit_cg_type_builtin(kc, KIT_CG_BUILTIN_I64); 1004 out_name = kit_sym_intern(kc, KIT_SLICE_LIT("out")); 1005 in_name = kit_sym_intern(kc, KIT_SLICE_LIT("in")); 1006 clobber_name = kit_sym_intern(kc, KIT_SLICE_LIT("x12")); 1007 1008 memset(&desc, 0, sizeof desc); 1009 desc.fn_type = i64_type; 1010 f = ir_func_new(c, &desc); 1011 entry = ir_block_new(f); 1012 f->entry = entry; 1013 ir_note_emit(f, entry); 1014 1015 out_reg = ir_alloc_preg(f, i64_type, RC_INT); 1016 in_reg = ir_alloc_preg(f, i64_type, RC_INT); 1017 f->preg_locs = arena_zarray(f->arena, OptLoc, f->npregs); 1018 f->preg_info = arena_zarray(f->arena, OptPRegInfo, f->npregs); 1019 set_hard_loc(f, out_reg, 6); 1020 set_hard_loc(f, in_reg, 7); 1021 memset(&memory_desc, 0, sizeof memory_desc); 1022 memory_desc.type = i64_type; 1023 memory_desc.size = 8; 1024 memory_desc.align = 8; 1025 memory_desc.kind = FS_LOCAL; 1026 memory_slot = ir_frame_slot_new(f, &memory_desc); 1027 1028 asm_in = ir_emit(f, entry, IR_ASM_BLOCK); 1029 asm_id = asm_in->id; 1030 asm_in->def = out_reg; 1031 hir_aux = arena_znew(f->arena, IRAsmAux); 1032 hir_aux->tmpl = "add %0, %1"; 1033 hir_aux->nout = 1; 1034 hir_aux->nin = 2; 1035 hir_aux->nclob = 1; 1036 hir_aux->outs = arena_zarray(f->arena, AsmConstraint, hir_aux->nout); 1037 hir_aux->ins = arena_zarray(f->arena, AsmConstraint, hir_aux->nin); 1038 hir_aux->clobbers = arena_array(f->arena, Sym, hir_aux->nclob); 1039 hir_aux->out_ops = arena_zarray(f->arena, Operand, hir_aux->nout); 1040 hir_aux->in_ops = arena_zarray(f->arena, Operand, hir_aux->nin); 1041 hir_aux->out_reg_reqs = 1042 arena_zarray(f->arena, IRAsmRegRequirement, hir_aux->nout); 1043 hir_aux->in_reg_reqs = 1044 arena_zarray(f->arena, IRAsmRegRequirement, hir_aux->nin); 1045 hir_aux->outs[0].str = "=r"; 1046 hir_aux->outs[0].name = out_name; 1047 hir_aux->outs[0].type = i64_type; 1048 hir_aux->outs[0].dir = KIT_CG_ASM_OUT; 1049 hir_aux->ins[0].str = "r"; 1050 hir_aux->ins[0].name = in_name; 1051 hir_aux->ins[0].type = i64_type; 1052 hir_aux->ins[0].dir = KIT_CG_ASM_IN; 1053 hir_aux->ins[1].str = "m"; 1054 hir_aux->ins[1].type = i64_type; 1055 hir_aux->ins[1].dir = KIT_CG_ASM_IN; 1056 hir_aux->clobbers[0] = clobber_name; 1057 hir_aux->out_ops[0] = reg_op(out_reg, i64_type); 1058 hir_aux->in_ops[0] = reg_op(in_reg, i64_type); 1059 hir_aux->in_ops[1].kind = OPK_LOCAL; 1060 hir_aux->in_ops[1].cls = RC_INT; 1061 hir_aux->in_ops[1].type = i64_type; 1062 hir_aux->in_ops[1].v.frame_slot = memory_slot; 1063 hir_aux->out_reg_reqs[0].allowed_mask = 0x000000f0u; 1064 hir_aux->out_reg_reqs[0].fixed_reg = 6; 1065 hir_aux->out_reg_reqs[0].cls = RC_INT; 1066 hir_aux->out_reg_reqs[0].present = 1; 1067 hir_aux->in_reg_reqs[0].allowed_mask = 0x0000ff00u; 1068 hir_aux->in_reg_reqs[0].fixed_reg = -1; 1069 hir_aux->in_reg_reqs[0].cls = RC_INT; 1070 hir_aux->in_reg_reqs[0].present = 1; 1071 hir_aux->in_reg_reqs[1].fixed_reg = -1; 1072 hir_aux->clobber_mask[RC_INT] = (1u << 3) | (1u << 12); 1073 hir_aux->clobber_mask[RC_FP] = 1u << 9; 1074 hir_aux->clobber_mask[RC_VEC] = 1u << 2; 1075 hir_aux->clobber_abi_sets = KIT_CG_ASM_CLOBBER_ABI_CALLER_SAVED; 1076 hir_aux->has_memory_constraint = 1; 1077 asm_in->extra.aux = hir_aux; 1078 1079 opt_lower_to_mir(f, NULL); 1080 asm_in = f->mir ? find_inst(&f->mir->blocks[entry], asm_id) : NULL; 1081 mir_aux = asm_in ? (IRAsmAux*)asm_in->extra.aux : NULL; 1082 CU_EXPECT(u, asm_in && mir_aux && mir_aux != hir_aux, 1083 "asm aux object aliases HIR after MIR lowering"); 1084 CU_EXPECT(u, 1085 mir_aux && mir_aux->outs != hir_aux->outs && 1086 mir_aux->ins != hir_aux->ins && 1087 mir_aux->clobbers != hir_aux->clobbers && 1088 mir_aux->out_ops != hir_aux->out_ops && 1089 mir_aux->in_ops != hir_aux->in_ops && 1090 mir_aux->out_reg_reqs != hir_aux->out_reg_reqs && 1091 mir_aux->in_reg_reqs != hir_aux->in_reg_reqs, 1092 "asm aux nested mutable arrays alias HIR"); 1093 CU_EXPECT(u, 1094 mir_aux && 1095 memcmp(mir_aux->clobber_mask, hir_aux->clobber_mask, 1096 sizeof hir_aux->clobber_mask) == 0 && 1097 mir_aux->clobber_abi_sets == hir_aux->clobber_abi_sets && 1098 mir_aux->has_memory_constraint == 1099 hir_aux->has_memory_constraint && 1100 memcmp(mir_aux->out_reg_reqs, hir_aux->out_reg_reqs, 1101 sizeof hir_aux->out_reg_reqs[0]) == 0 && 1102 memcmp(mir_aux->in_reg_reqs, hir_aux->in_reg_reqs, 1103 sizeof hir_aux->in_reg_reqs[0] * hir_aux->nin) == 0, 1104 "resolved asm placement metadata did not survive MIR cloning"); 1105 CU_EXPECT(u, 1106 hir_aux->out_ops[0].kind == OPK_REG && 1107 hir_aux->out_ops[0].v.reg == out_reg && 1108 hir_aux->in_ops[0].kind == OPK_REG && 1109 hir_aux->in_ops[0].v.reg == in_reg && mir_aux && 1110 mir_aux->out_ops[0].kind == OPK_REG && 1111 mir_aux->out_ops[0].v.reg == 6 && 1112 mir_aux->in_ops[0].kind == OPK_REG && 1113 mir_aux->in_ops[0].v.reg == 7 && 1114 hir_aux->in_ops[1].kind == OPK_LOCAL && 1115 hir_aux->in_ops[1].v.frame_slot == memory_slot && 1116 mir_aux->in_ops[1].kind == OPK_LOCAL && 1117 mir_aux->in_ops[1].v.frame_slot == memory_slot, 1118 "asm operand rewrite crossed the HIR/MIR ownership boundary"); 1119 1120 if (mir_aux) { 1121 mir_aux->out_reg_reqs[0].allowed_mask = 1u << 20; 1122 mir_aux->out_reg_reqs[0].fixed_reg = 20; 1123 mir_aux->in_reg_reqs[0].present = 0; 1124 mir_aux->clobber_mask[RC_INT] = 1u << 22; 1125 mir_aux->has_memory_constraint = 0; 1126 mir_aux->outs[0].name = in_name; 1127 mir_aux->ins[0].str = "m"; 1128 mir_aux->clobbers[0] = out_name; 1129 mir_aux->out_ops[0].v.reg = 20; 1130 mir_aux->in_ops[0].kind = OPK_IMM; 1131 mir_aux->in_ops[0].v.imm = 42; 1132 } 1133 CU_EXPECT(u, 1134 hir_aux->out_reg_reqs[0].allowed_mask == 0x000000f0u && 1135 hir_aux->out_reg_reqs[0].fixed_reg == 6 && 1136 hir_aux->in_reg_reqs[0].present == 1 && 1137 hir_aux->clobber_mask[RC_INT] == 1138 ((1u << 3) | (1u << 12)) && 1139 hir_aux->has_memory_constraint == 1 && 1140 hir_aux->outs[0].name == out_name && 1141 strcmp(hir_aux->ins[0].str, "r") == 0 && 1142 hir_aux->clobbers[0] == clobber_name && 1143 hir_aux->out_ops[0].kind == OPK_REG && 1144 hir_aux->out_ops[0].v.reg == out_reg && 1145 hir_aux->in_ops[0].kind == OPK_REG && 1146 hir_aux->in_ops[0].v.reg == in_reg, 1147 "mutating MIR asm metadata changed the semantic HIR graph"); 1148 1149 /* Prove independence in the other direction as well: the already-mutated 1150 * MIR requirements and operands must not observe later HIR changes. */ 1151 hir_aux->out_reg_reqs[0].allowed_mask = 1u; 1152 hir_aux->in_ops[0].v.reg = out_reg; 1153 CU_EXPECT(u, 1154 mir_aux && mir_aux->out_reg_reqs[0].allowed_mask == (1u << 20) && 1155 mir_aux->in_ops[0].kind == OPK_IMM && 1156 mir_aux->in_ops[0].v.imm == 42, 1157 "mutating HIR asm metadata changed the location MIR graph"); 1158 1159 kit_compiler_free(kc); 1160 } 1161 1162 int main(void) { 1163 KitUnit u; 1164 kit_unit_init(&u); 1165 machinize_rejects_cross_owner_registers(&u); 1166 mir_verify_rejects_private_temp_escape(&u); 1167 mir_commit_propagates_instruction_namespace(&u); 1168 spilled_values_remain_locations(&u); 1169 spilled_frame_address_remains_recipe(&u); 1170 call_aux_is_independent(&u); 1171 structured_aux_arrays_are_independent(&u); 1172 asm_aux_resolved_metadata_is_independent(&u); 1173 kit_unit_summary(&u, "location-mir"); 1174 return kit_unit_status(&u); 1175 }