kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

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 }