pass_machinize.c (9279B)
1 #include <string.h> 2 3 #include "cg/native_asm.h" 4 #include "cg/type.h" 5 #include "core/pool.h" 6 #include "core/slice.h" 7 #include "opt/opt_internal.h" 8 9 static int native_resolve_reg(NativeTarget* target, Slice name, Reg* out, 10 RegClass* cls_out) { 11 NativeAllocClass cls; 12 if (!target || !target->regs || !target->regs->resolve_name) return 1; 13 if (target->regs->resolve_name(target->regs, name, out, &cls) != 0) return 1; 14 if (cls_out) *cls_out = (RegClass)cls; 15 return 0; 16 } 17 18 static void asm_prepare_constraints(Func* f, NativeTarget* target, 19 IRAsmAux* aux) { 20 if (!aux) return; 21 for (u32 c = 0; c < OPT_REG_CLASSES; ++c) aux->clobber_mask[c] = 0; 22 if (aux->nout && !aux->out_fixed_regs) { 23 aux->out_fixed_regs = arena_array(f->arena, i32, aux->nout); 24 aux->out_fixed_cls = arena_zarray(f->arena, u8, aux->nout); 25 } 26 if (aux->nin && !aux->in_fixed_regs) { 27 aux->in_fixed_regs = arena_array(f->arena, i32, aux->nin); 28 aux->in_fixed_cls = arena_zarray(f->arena, u8, aux->nin); 29 } 30 if (aux->nout && !aux->out_allowed_masks) { 31 aux->out_allowed_masks = arena_zarray(f->arena, u32, aux->nout); 32 aux->out_allowed_cls = arena_zarray(f->arena, u8, aux->nout); 33 } 34 if (aux->nin && !aux->in_allowed_masks) { 35 aux->in_allowed_masks = arena_zarray(f->arena, u32, aux->nin); 36 aux->in_allowed_cls = arena_zarray(f->arena, u8, aux->nin); 37 } 38 for (u32 i = 0; i < aux->nout; ++i) { 39 aux->out_fixed_regs[i] = -1; 40 aux->out_fixed_cls[i] = 0; 41 aux->out_allowed_masks[i] = 0; 42 aux->out_allowed_cls[i] = 0; 43 } 44 for (u32 i = 0; i < aux->nin; ++i) { 45 aux->in_fixed_regs[i] = -1; 46 aux->in_fixed_cls[i] = 0; 47 aux->in_allowed_masks[i] = 0; 48 aux->in_allowed_cls[i] = 0; 49 } 50 for (u32 i = 0; i < aux->nclob; ++i) { 51 Reg r; 52 RegClass cls; 53 Slice nm = pool_slice(f->c->global, aux->clobbers[i]); 54 if (native_resolve_reg(target, nm, &r, &cls) != 0) continue; 55 if ((u32)cls < OPT_REG_CLASSES && r < 32) aux->clobber_mask[cls] |= 1u << r; 56 } 57 for (u32 i = 0; i < aux->nout; ++i) { 58 NativeAsmRegPin pin; 59 NativeAsmRegPinStatus st = native_asm_resolve_pin(target, aux->outs[i].reg, 60 aux->outs[i].str, &pin); 61 if (st == NATIVE_ASM_REG_PIN_OK) { 62 aux->out_fixed_regs[i] = (i32)pin.reg; 63 aux->out_fixed_cls[i] = (u8)pin.cls; 64 continue; 65 } 66 if (st != NATIVE_ASM_REG_PIN_ABSENT) { 67 compiler_panic(f->c, (SrcLoc){0, 0, 0}, "opt asm: %s", 68 native_asm_pin_status_message(st)); 69 } 70 NativeAsmConstraintInfo info; 71 if (native_asm_constraint_reg_info(target, aux->outs[i].str, &info)) { 72 if (info.allowed_mask) { 73 aux->out_allowed_masks[i] = info.allowed_mask; 74 aux->out_allowed_cls[i] = (u8)info.cls; 75 } 76 if (info.fixed_reg != REG_NONE) { 77 aux->out_fixed_regs[i] = (i32)info.fixed_reg; 78 aux->out_fixed_cls[i] = (u8)info.cls; 79 } 80 } 81 } 82 for (u32 i = 0; i < aux->nin; ++i) { 83 NativeAsmRegPin pin; 84 NativeAsmRegPinStatus st = 85 native_asm_resolve_pin(target, aux->ins[i].reg, aux->ins[i].str, &pin); 86 if (st == NATIVE_ASM_REG_PIN_OK) { 87 aux->in_fixed_regs[i] = (i32)pin.reg; 88 aux->in_fixed_cls[i] = (u8)pin.cls; 89 continue; 90 } 91 if (st != NATIVE_ASM_REG_PIN_ABSENT) { 92 compiler_panic(f->c, (SrcLoc){0, 0, 0}, "opt asm: %s", 93 native_asm_pin_status_message(st)); 94 } 95 NativeAsmConstraintInfo info; 96 if (native_asm_constraint_reg_info(target, aux->ins[i].str, &info)) { 97 if (info.allowed_mask) { 98 aux->in_allowed_masks[i] = info.allowed_mask; 99 aux->in_allowed_cls[i] = (u8)info.cls; 100 } 101 if (info.fixed_reg != REG_NONE) { 102 aux->in_fixed_regs[i] = (i32)info.fixed_reg; 103 aux->in_fixed_cls[i] = (u8)info.cls; 104 } 105 } 106 } 107 } 108 109 static void machinize_reset(Func* f, NativeTarget* target) { 110 f->opt_target = target->c->target; 111 f->opt_has_target = 1; 112 for (u32 c = 0; c < OPT_REG_CLASSES; ++c) { 113 f->opt_hard_reg_count[c] = 0; 114 f->opt_phys_reg_count[c] = 0; 115 f->opt_scratch_reg_count[c] = 0; 116 f->opt_caller_saved[c] = 0; 117 f->opt_callee_saved[c] = 0; 118 f->opt_reserved_regs[c] = 0; 119 f->opt_arg_regs[c] = 0; 120 f->opt_ret_regs[c] = 0; 121 } 122 } 123 124 static void machinize_prepare_insts(Func* f, NativeTarget* target) { 125 for (u32 b = 0; b < f->nblocks; ++b) { 126 Block* bl = &f->blocks[b]; 127 for (u32 i = 0; i < bl->ninsts; ++i) { 128 Inst* in = &bl->insts[i]; 129 if ((IROp)in->op == IR_ASM_BLOCK) 130 asm_prepare_constraints(f, target, (IRAsmAux*)in->extra.aux); 131 } 132 } 133 } 134 135 static void collect_class(Func* f, NativeTarget* target, 136 const NativeAllocClassInfo* ci) { 137 u32 cls = ci->cls; 138 if (cls >= OPT_REG_CLASSES) return; 139 f->opt_caller_saved[cls] = 140 native_target_caller_saved_mask(target, (NativeAllocClass)cls); 141 f->opt_callee_saved[cls] = 142 native_target_callee_saved_mask(target, (NativeAllocClass)cls); 143 f->opt_reserved_regs[cls] = ci->reserved_mask; 144 f->opt_arg_regs[cls] = ci->arg_mask; 145 f->opt_ret_regs[cls] = ci->ret_mask; 146 for (u32 i = 0; 147 i < ci->nphys && f->opt_phys_reg_count[cls] < OPT_MAX_HARD_REGS; ++i) { 148 const NativePhysRegInfo* src = &ci->phys[i]; 149 CGPhysRegInfo* dst = &f->opt_phys_regs[cls][f->opt_phys_reg_count[cls]++]; 150 memset(dst, 0, sizeof *dst); 151 dst->reg = src->reg; 152 dst->cls = src->cls; 153 dst->abi_index = src->abi_index; 154 dst->flags = src->flags; 155 if ((src->flags & CG_REG_ALLOCABLE) && !(src->flags & CG_REG_RESERVED) && 156 f->opt_hard_reg_count[cls] < OPT_MAX_HARD_REGS) 157 f->opt_hard_regs[cls][f->opt_hard_reg_count[cls]++] = src->reg; 158 } 159 for (u32 i = 0; i < ci->nscratch && i < OPT_MAX_SCRATCH_REGS; ++i) 160 f->opt_scratch_regs[cls][f->opt_scratch_reg_count[cls]++] = ci->scratch[i]; 161 } 162 163 static void machinize_collect_regs(Func* f, NativeTarget* target) { 164 if (!target || !target->regs) return; 165 for (u32 i = 0; i < target->regs->nclasses; ++i) 166 collect_class(f, target, &target->regs->classes[i]); 167 } 168 169 static void machinize_check_overlap(Func* f) { 170 for (u32 c = 0; c < OPT_REG_CLASSES; ++c) { 171 for (u32 i = 0; i < f->opt_hard_reg_count[c]; ++i) { 172 Reg hr = f->opt_hard_regs[c][i]; 173 for (u32 s = 0; s < f->opt_scratch_reg_count[c]; ++s) { 174 if (f->opt_scratch_regs[c][s] == hr) { 175 compiler_panic(f->c, (SrcLoc){0, 0, 0}, 176 "opt_machinize: hard reg %u overlaps scratch reg " 177 "in class %u", 178 (unsigned)hr, (unsigned)c); 179 } 180 } 181 } 182 } 183 } 184 185 /* Record, per instruction, the physical registers the target's encoding 186 * clobbers as a side effect (x86 idiv → rax/rdx, variable shift → cl, atomics, 187 * va_arg), so the allocator keeps values live across them out of those 188 * registers. The target reports this through machine_op_clobbers; a NULL hook 189 * means no instruction has fixed-register clobbers and the side table stays 190 * empty. */ 191 static void machinize_inst_clobbers(Func* f, NativeTarget* target) { 192 if (!target->machine_op_clobbers || !f->next_inst_id) return; 193 for (u32 b = 0; b < f->nblocks; ++b) { 194 Block* bl = &f->blocks[b]; 195 for (u32 i = 0; i < bl->ninsts; ++i) { 196 Inst* in = &bl->insts[i]; 197 NativeMachineOp mop; 198 u32 mask[NATIVE_CALL_PLAN_CLASSES]; 199 memset(&mop, 0, sizeof mop); 200 switch ((IROp)in->op) { 201 case IR_BINOP: 202 mop.kind = NATIVE_MOP_BINOP; 203 mop.binop = (u8)in->extra.imm; 204 mop.second_is_reg = 205 (u8)(in->nopnds > 2u && in->opnds[2].kind == OPK_REG); 206 break; 207 case IR_VA_START: 208 mop.kind = NATIVE_MOP_VA_START; 209 break; 210 case IR_VA_ARG: 211 mop.kind = NATIVE_MOP_VA_ARG; 212 mop.result_is_fp = (u8)(in->nopnds > 0u && 213 cg_type_is_float(f->c, in->opnds[0].type)); 214 break; 215 case IR_ATOMIC_CAS: 216 mop.kind = NATIVE_MOP_ATOMIC_CAS; 217 break; 218 case IR_ATOMIC_RMW: 219 mop.kind = NATIVE_MOP_ATOMIC_RMW; 220 break; 221 case IR_TLS_ADDR_OF: 222 mop.kind = NATIVE_MOP_TLS_ADDR; 223 break; 224 case IR_INTRINSIC: { 225 const IRIntrinAux* aux = (const IRIntrinAux*)in->extra.aux; 226 if (!aux) continue; 227 mop.kind = NATIVE_MOP_INTRINSIC; 228 mop.intrin = (u8)aux->kind; 229 break; 230 } 231 default: 232 continue; 233 } 234 mask[0] = mask[1] = mask[2] = 0; 235 if (!target->machine_op_clobbers(target, &mop, mask)) continue; 236 if (in->id == INST_ID_NONE) continue; 237 if (!f->inst_clobbers) { 238 f->inst_clobbers_cap = f->next_inst_id; 239 f->inst_clobbers = 240 arena_zarray(f->arena, OptInstClobberMask, f->inst_clobbers_cap); 241 } 242 if (in->id < f->inst_clobbers_cap) 243 for (u32 c = 0; c < OPT_REG_CLASSES; ++c) 244 f->inst_clobbers[in->id][c] = mask[c]; 245 } 246 } 247 } 248 249 void opt_machinize_native(Func* f, NativeTarget* target) { 250 machinize_reset(f, target); 251 machinize_prepare_insts(f, target); 252 machinize_collect_regs(f, target); 253 machinize_check_overlap(f); 254 machinize_inst_clobbers(f, target); 255 }