kit

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

pass_mir.c (23736B)


      1 #include "opt/opt_internal.h"
      2 
      3 int opt_mir_view(Func* f, Func* out) {
      4   if (!f || !f->mir) return 0;
      5   *out = *f;
      6   out->blocks = f->mir->blocks;
      7   out->nblocks = f->mir->nblocks;
      8   out->blocks_cap = f->mir->blocks_cap;
      9   out->entry = f->mir->entry;
     10   out->emit_order = f->mir->emit_order;
     11   out->emit_order_n = f->mir->emit_order_n;
     12   out->emit_order_cap = f->mir->emit_order_cap;
     13   out->opt_rewritten = 1;
     14   /* HIR def-use records contain direct pointers into HIR operands. MIR passes
     15    * must rebuild any analysis they need over this physical graph. */
     16   out->opt_uses = NULL;
     17   out->opt_nuses = 0;
     18   out->opt_uses_cap = 0;
     19   out->opt_first_use_by_val = NULL;
     20   out->opt_first_use_by_val_cap = 0;
     21   out->opt_valid_analyses = 0;
     22   /* Scope lookup is a recording/semantic cache of direct Inst pointers into
     23    * HIR. Structured scopes have already been lowered before MIR exists, and a
     24    * physical pass must never inherit pointers back into the semantic graph. */
     25   out->scope_aux_inst = NULL;
     26   out->nscopes = 0;
     27   out->scopes_cap = 0;
     28   out->mir = NULL;
     29   return 1;
     30 }
     31 
     32 void opt_mir_commit(Func* f, const Func* view) {
     33   if (!f || !f->mir || !view) return;
     34   f->mir->blocks = view->blocks;
     35   f->mir->nblocks = view->nblocks;
     36   f->mir->blocks_cap = view->blocks_cap;
     37   f->mir->entry = view->entry;
     38   f->mir->emit_order = view->emit_order;
     39   f->mir->emit_order_n = view->emit_order_n;
     40   f->mir->emit_order_cap = view->emit_order_cap;
     41   /* Instruction ids are function-wide side-table keys, but a physical pass
     42    * allocates them through its independently mutable Func view. Commit that
     43    * namespace with the graph so later MIR analyses can size their tables for
     44    * every instruction the pass inserted. */
     45   f->next_inst_id = view->next_inst_id;
     46 }
     47 
     48 #ifndef NDEBUG
     49 
     50 typedef struct MirVerifyCtx {
     51   const char* stage;
     52   /* opt_walk_operand presents an indirect owner immediately followed by
     53    * transient scalar views of its base and (when present) index. Track that
     54    * sequence so a stored OPT_OPERAND_WALK_INDIRECT_PART flag cannot masquerade
     55    * as a legitimate walker annotation. */
     56   const Operand* indirect;
     57   u8 indirect_part; /* 1 = base, 2 = index */
     58 } MirVerifyCtx;
     59 
     60 static void mir_fail(Func* f, const Inst* in, const char* stage,
     61                      const char* msg, u32 a, u32 b) {
     62   compiler_panic(f->c, in ? in->loc : (SrcLoc){0, 0, 0},
     63                  "opt MIR verify %s: %s [op %u, inst %u] (%u, %u)",
     64                  stage ? stage : "?", msg, in ? (unsigned)in->op : 0u,
     65                  in ? (unsigned)in->id : 0u, (unsigned)a, (unsigned)b);
     66 }
     67 
     68 static int mir_pow2(u32 n) { return n && !(n & (n - 1u)); }
     69 
     70 static int mir_type_known(Func* f, KitCgTypeId type) {
     71   return type != KIT_CG_TYPE_NONE && cg_type_get(f->c, type) != NULL;
     72 }
     73 
     74 static int mir_type_valid(Func* f, KitCgTypeId type) {
     75   return mir_type_known(f, type) &&
     76          kit_cg_type_is_sized((KitCompiler*)f->c, type) &&
     77          cg_type_size(f->c, type) != 0 && cg_type_align(f->c, type) != 0;
     78 }
     79 
     80 static int mir_type_scalar(Func* f, KitCgTypeId type) {
     81   const CgType* ty;
     82   if (!mir_type_valid(f, type)) return 0;
     83   ty = cg_type_get(f->c, type);
     84   switch (ty->kind) {
     85     case KIT_CG_TYPE_BOOL:
     86     case KIT_CG_TYPE_INT:
     87     case KIT_CG_TYPE_FLOAT:
     88     case KIT_CG_TYPE_PTR:
     89     case KIT_CG_TYPE_ENUM:
     90     case KIT_CG_TYPE_VARARG_STATE:
     91       return 1;
     92     default:
     93       return 0;
     94   }
     95 }
     96 
     97 static int mir_type_address_component(Func* f, KitCgTypeId type) {
     98   return mir_type_scalar(f, type) &&
     99          opt_value_reg_class(f->c, type) == RC_INT &&
    100          cg_type_size(f->c, type) == f->c->target.ptr_size;
    101 }
    102 
    103 static int mir_reg_known(Func* f, Reg r, u8 cls) {
    104   if (cls >= OPT_REG_CLASSES || r == (Reg)REG_NONE ||
    105       r >= OPT_MAX_HARD_REGS)
    106     return 0;
    107   for (u32 i = 0; i < f->opt_phys_reg_count[cls]; ++i) {
    108     const CGPhysRegInfo* phys = &f->opt_phys_regs[cls][i];
    109     if (phys->reg == r && phys->cls == cls) return 1;
    110   }
    111   return 0;
    112 }
    113 
    114 static u32 mir_known_reg_mask(Func* f, u8 cls) {
    115   u32 mask = 0;
    116   if (!f || cls >= OPT_REG_CLASSES) return 0;
    117   for (u32 i = 0; i < f->opt_phys_reg_count[cls]; ++i) {
    118     Reg r = f->opt_phys_regs[cls][i].reg;
    119     if (r < OPT_MAX_HARD_REGS) mask |= 1u << r;
    120   }
    121   return mask;
    122 }
    123 
    124 static int mir_reg_is_emit_temp(Func* f, Reg r, u8 cls) {
    125   if (cls >= OPT_REG_CLASSES) return 0;
    126   for (u32 i = 0; i < f->emit_temp_reg_count[cls]; ++i)
    127     if (f->emit_temp_regs[cls][i] == r) return 1;
    128   return 0;
    129 }
    130 
    131 static int mir_reg_is_asm_temp(Func* f, Reg r, u8 cls) {
    132   return cls < OPT_REG_CLASSES && r < OPT_MAX_HARD_REGS &&
    133          (f->asm_temp_mask[cls] & (1u << r)) != 0;
    134 }
    135 
    136 static void mir_verify_reg(Func* f, const Inst* in, const char* stage, Reg r,
    137                            u8 cls, const char* what) {
    138   if (!mir_reg_known(f, r, cls))
    139     mir_fail(f, in, stage, what, cls, (u32)r);
    140   if (mir_reg_is_emit_temp(f, r, cls))
    141     mir_fail(f, in, stage, "emitter-owned temp escaped into MIR", cls,
    142              (u32)r);
    143   if (mir_reg_is_asm_temp(f, r, cls))
    144     mir_fail(f, in, stage, "asm-internal temp escaped into MIR", cls,
    145              (u32)r);
    146 }
    147 
    148 static IRFrameSlot* mir_frame_slot(Func* f, const Inst* in, const char* stage,
    149                                    FrameSlot id, const char* what) {
    150   if (id == FRAME_SLOT_NONE || id > f->nframe_slots)
    151     mir_fail(f, in, stage, what, id, f->nframe_slots);
    152   return &f->frame_slots[id - 1u];
    153 }
    154 
    155 static void mir_verify_slot_contains(Func* f, const Inst* in,
    156                                      const char* stage,
    157                                      const IRFrameSlot* slot,
    158                                      KitCgTypeId type, const char* what) {
    159   u64 size;
    160   if (!mir_type_valid(f, type))
    161     mir_fail(f, in, stage, what, type, slot ? slot->id : 0u);
    162   size = cg_type_size(f->c, type);
    163   if (!slot || size > slot->size)
    164     mir_fail(f, in, stage, what, slot ? slot->id : 0u, type);
    165 }
    166 
    167 static void mir_verify_slot_fits(Func* f, const Inst* in, const char* stage,
    168                                  const IRFrameSlot* slot, KitCgTypeId type,
    169                                  const char* what) {
    170   mir_verify_slot_contains(f, in, stage, slot, type, what);
    171   if (cg_type_align(f->c, type) > slot->align)
    172     mir_fail(f, in, stage, what, slot->id, type);
    173 }
    174 
    175 static void mir_verify_frame_table(Func* f, const char* stage) {
    176   const u16 allowed_flags =
    177       (u16)(FSF_ADDR_TAKEN | FSF_MEMORY_REQUIRED | FSF_VOLATILE);
    178   for (u32 i = 0; i < f->nframe_slots; ++i) {
    179     IRFrameSlot* slot = &f->frame_slots[i];
    180     if (slot->id != i + 1u)
    181       mir_fail(f, NULL, stage, "non-canonical frame slot id", slot->id,
    182                i + 1u);
    183     if (slot->kind != FS_LOCAL && slot->kind != FS_PARAM &&
    184         slot->kind != FS_SPILL)
    185       mir_fail(f, NULL, stage, "invalid optimizer frame slot kind", slot->id,
    186                slot->kind);
    187     if (!mir_type_valid(f, slot->type))
    188       mir_fail(f, NULL, stage, "invalid frame slot type", slot->id,
    189                slot->type);
    190     if (!slot->size || !mir_pow2(slot->align))
    191       mir_fail(f, NULL, stage, "invalid frame slot size/alignment", slot->size,
    192                slot->align);
    193     if (cg_type_size(f->c, slot->type) > slot->size ||
    194         cg_type_align(f->c, slot->type) > slot->align)
    195       mir_fail(f, NULL, stage, "frame slot does not fit descriptor type",
    196                slot->id, slot->type);
    197     if (slot->flags & ~allowed_flags)
    198       mir_fail(f, NULL, stage, "invalid frame slot flags", slot->id,
    199                slot->flags);
    200     if (slot->kind == FS_SPILL) {
    201       if (slot->flags)
    202         mir_fail(f, NULL, stage, "spill slot carries semantic flags", slot->id,
    203                  slot->flags);
    204     } else if (slot->priority) {
    205       mir_fail(f, NULL, stage, "semantic frame slot carries spill priority",
    206                slot->id, slot->priority);
    207     }
    208   }
    209 }
    210 
    211 static void mir_verify_asm_requirement(Func* f, const Inst* in,
    212                                        const char* stage,
    213                                        const IRAsmRegRequirement* req,
    214                                        const Operand* op) {
    215   u32 known;
    216   if (!req->present) {
    217     if (req->allowed_mask || req->fixed_reg != -1 || req->cls)
    218       mir_fail(f, in, stage, "non-canonical absent asm requirement",
    219                req->allowed_mask, (u32)req->fixed_reg);
    220     return;
    221   }
    222   if (req->cls >= OPT_REG_CLASSES || !op || !mir_type_scalar(f, op->type) ||
    223       opt_value_reg_class(f->c, op->type) != req->cls)
    224     mir_fail(f, in, stage, "invalid asm requirement class/type", req->cls,
    225              op ? op->type : 0u);
    226   known = mir_known_reg_mask(f, req->cls);
    227   if (req->allowed_mask & ~known)
    228     mir_fail(f, in, stage, "asm allowed mask names unknown register",
    229              req->cls, req->allowed_mask & ~known);
    230   if (req->fixed_reg < -1 || req->fixed_reg >= (i32)OPT_MAX_HARD_REGS ||
    231       (req->fixed_reg >= 0 &&
    232        !(known & (1u << (u32)req->fixed_reg))))
    233     mir_fail(f, in, stage, "asm fixed requirement names unknown register",
    234              req->cls, (u32)req->fixed_reg);
    235   if (req->fixed_reg >= 0 && req->allowed_mask &&
    236       !(req->allowed_mask & (1u << (u32)req->fixed_reg)))
    237     mir_fail(f, in, stage, "asm fixed register violates allowed mask",
    238              req->cls, (u32)req->fixed_reg);
    239 }
    240 
    241 static void mir_verify_asm_aux(Func* f, const Inst* in, const char* stage) {
    242   const IRAsmAux* aux = (const IRAsmAux*)in->extra.aux;
    243   if (!aux)
    244     mir_fail(f, in, stage, "asm instruction has no descriptor", 0, 0);
    245   if ((aux->nout != 0u) != (aux->outs && aux->out_ops && aux->out_reg_reqs) ||
    246       (aux->nin != 0u) != (aux->ins && aux->in_ops && aux->in_reg_reqs) ||
    247       (aux->nclob != 0u) != (aux->clobbers != NULL) ||
    248       aux->has_memory_constraint > 1u)
    249     mir_fail(f, in, stage, "inconsistent asm descriptor arrays", aux->nout,
    250              aux->nin);
    251   for (u32 cls = 0; cls < OPT_REG_CLASSES; ++cls)
    252     if (aux->clobber_mask[cls] & ~mir_known_reg_mask(f, (u8)cls))
    253       mir_fail(f, in, stage, "asm clobber mask names unknown register", cls,
    254                aux->clobber_mask[cls]);
    255   for (u32 i = 0; i < aux->nout; ++i)
    256     mir_verify_asm_requirement(f, in, stage, &aux->out_reg_reqs[i],
    257                                &aux->out_ops[i]);
    258   for (u32 i = 0; i < aux->nin; ++i)
    259     mir_verify_asm_requirement(f, in, stage, &aux->in_reg_reqs[i],
    260                                &aux->in_ops[i]);
    261 }
    262 
    263 static void mir_verify_scalar_location(Func* f, Inst* in, Operand* op,
    264                                        int is_def, MirVerifyCtx* ctx) {
    265   IRFrameSlot* slot;
    266   if (op->flags)
    267     mir_fail(f, in, ctx->stage, "stored operand has transient flags", op->kind,
    268              op->flags);
    269   if (op->shift) {
    270     if (op->kind != OPK_REG || is_def || op->cls != RC_INT ||
    271         op->shift > 4u || (IROp)in->op != IR_BINOP || in->nopnds != 3u ||
    272         op != &in->opnds[2])
    273       mir_fail(f, in, ctx->stage, "invalid shifted-register rider", op->kind,
    274                op->shift);
    275   }
    276   switch ((OptOperandKind)op->kind) {
    277     case OPK_IMM:
    278       /* A zero operand is the canonical absent ABI value (notably a void call
    279        * result). Every present immediate must still carry a real scalar type. */
    280       if (op->type == KIT_CG_TYPE_NONE) {
    281         if (op->cls || op->v.imm)
    282           mir_fail(f, in, ctx->stage, "malformed absent immediate", op->cls,
    283                    (u32)op->v.imm);
    284       } else if (!mir_type_scalar(f, op->type) || op->cls != RC_INT) {
    285         mir_fail(f, in, ctx->stage, "invalid immediate type/class", op->type,
    286                  op->cls);
    287       }
    288       return;
    289     case OPK_GLOBAL:
    290       /* A symbol-address recipe may retain an incomplete array or function
    291        * referent type.  Native emission needs the symbol identity and address
    292        * class, not sized storage; size/alignment checks belong to operations
    293        * that actually access the referent. */
    294       if (!mir_type_known(f, op->type) || op->cls != RC_INT)
    295         mir_fail(f, in, ctx->stage, "invalid global type/class", op->type,
    296                  op->cls);
    297       return;
    298     case OPK_LOCAL:
    299       if (op->cls != RC_INT)
    300         mir_fail(f, in, ctx->stage, "frame local has non-address class",
    301                  op->cls, op->v.frame_slot);
    302       slot = mir_frame_slot(f, in, ctx->stage, op->v.frame_slot,
    303                             "invalid local frame slot");
    304       if (slot->kind != FS_LOCAL && slot->kind != FS_PARAM)
    305         mir_fail(f, in, ctx->stage, "local operand names allocator spill",
    306                  op->v.frame_slot, slot->kind);
    307       /* A semantic local may be a typed subobject view into packed storage.
    308        * Its access alignment lives in MemAccess and may be lower than the
    309        * viewed type's natural alignment; only containment is required here.
    310        * Allocator spills below retain the stronger alignment invariant. */
    311       mir_verify_slot_contains(f, in, ctx->stage, slot, op->type,
    312                                "local operand type does not fit frame slot");
    313       return;
    314     case OPK_STACK:
    315       if (!mir_type_scalar(f, op->type) || op->cls >= OPT_REG_CLASSES ||
    316           opt_value_reg_class(f->c, op->type) != op->cls)
    317         mir_fail(f, in, ctx->stage, "invalid spill value type/class", op->type,
    318                  op->cls);
    319       slot = mir_frame_slot(f, in, ctx->stage, op->v.frame_slot,
    320                             "invalid spill frame slot");
    321       if (slot->kind != FS_SPILL)
    322         mir_fail(f, in, ctx->stage, "spill value names semantic frame slot",
    323                  op->v.frame_slot, slot->kind);
    324       mir_verify_slot_fits(f, in, ctx->stage, slot, op->type,
    325                            "spill value type does not fit frame slot");
    326       return;
    327     case OPK_FRAME_ADDR:
    328       if (is_def || op->cls != RC_INT || !cg_type_is_ptr(f->c, op->type) ||
    329           !mir_type_address_component(f, op->type))
    330         mir_fail(f, in, ctx->stage, "invalid frame-address recipe", op->type,
    331                  op->cls);
    332       slot = mir_frame_slot(f, in, ctx->stage, op->v.frame_slot,
    333                             "invalid frame-address slot");
    334       if (slot->kind != FS_LOCAL && slot->kind != FS_PARAM)
    335         mir_fail(f, in, ctx->stage, "frame-address recipe names spill slot",
    336                  op->v.frame_slot, slot->kind);
    337       return;
    338     case OPK_REG:
    339       if (!mir_type_scalar(f, op->type) || op->cls >= OPT_REG_CLASSES ||
    340           opt_value_reg_class(f->c, op->type) != op->cls)
    341         mir_fail(f, in, ctx->stage, "invalid register value type/class",
    342                  op->type, op->cls);
    343       mir_verify_reg(f, in, ctx->stage, op->v.reg, op->cls,
    344                      "unknown physical register operand");
    345       return;
    346     case OPK_INDIRECT:
    347       mir_fail(f, in, ctx->stage, "nested indirect operand", op->kind, 0);
    348       return;
    349     default:
    350       mir_fail(f, in, ctx->stage, "unknown operand kind", op->kind, 0);
    351       return;
    352   }
    353 }
    354 
    355 static void mir_verify_indirect_component(Func* f, Inst* in, Operand* part,
    356                                           int is_def, MirVerifyCtx* ctx) {
    357   const Operand* owner = ctx->indirect;
    358   u32 value;
    359   u8 kind;
    360   KitCgTypeId type;
    361   u8 expect_kind;
    362   if (!owner || (ctx->indirect_part != 1u && ctx->indirect_part != 2u))
    363     mir_fail(f, in, ctx->stage, "unexpected indirect component", part->kind,
    364              part->flags);
    365   if (part->flags != OPT_OPERAND_WALK_INDIRECT_PART || is_def || part->shift ||
    366       part->cls != RC_INT)
    367     mir_fail(f, in, ctx->stage, "malformed indirect component view",
    368              part->flags, part->kind);
    369   if (ctx->indirect_part == 1u) {
    370     value = owner->v.ind.base;
    371     kind = owner->v.ind.base_kind;
    372     type = owner->v.ind.base_type;
    373   } else {
    374     value = owner->v.ind.index;
    375     kind = owner->v.ind.index_kind;
    376     type = owner->v.ind.index_type;
    377   }
    378   expect_kind = kind == OPT_INDIRECT_REG
    379                     ? OPK_REG
    380                     : (kind == OPT_INDIRECT_FRAME ? OPK_STACK
    381                                                   : OPK_FRAME_ADDR);
    382   if (part->kind != expect_kind || part->type != type ||
    383       (part->kind == OPK_REG ? (u32)part->v.reg
    384                             : (u32)part->v.frame_slot) != value)
    385     mir_fail(f, in, ctx->stage, "indirect component view disagrees with owner",
    386              ctx->indirect_part, part->kind);
    387 
    388   if (ctx->indirect_part == 1u &&
    389       !(owner->v.ind.index_kind == OPT_INDIRECT_REG &&
    390         owner->v.ind.index == (Reg)REG_NONE)) {
    391     ctx->indirect_part = 2u;
    392   } else {
    393     ctx->indirect = NULL;
    394     ctx->indirect_part = 0;
    395   }
    396 }
    397 
    398 static void mir_verify_indirect(Func* f, Inst* in, Operand* op,
    399                                 MirVerifyCtx* ctx) {
    400   IRFrameSlot* slot;
    401   int has_index;
    402   int address_only = (IROp)in->op == IR_ADDR_OF && in->nopnds >= 2u &&
    403                      op == &in->opnds[1];
    404   if (op->flags || op->shift || op->cls != RC_INT)
    405     mir_fail(f, in, ctx->stage, "invalid indirect owner shape", op->flags,
    406              op->shift);
    407   /* IR_ADDR_OF computes the identity of its lvalue and may therefore retain an
    408    * incomplete flexible-array referent.  Instructions that dereference the
    409    * recipe still require sized storage. */
    410   if (!(address_only ? mir_type_known(f, op->type)
    411                      : mir_type_valid(f, op->type)))
    412     mir_fail(f, in, ctx->stage, "invalid indirect owner type", op->type,
    413              (u32)address_only);
    414   if (op->v.ind.base_kind > OPT_INDIRECT_FRAME_ADDR)
    415     mir_fail(f, in, ctx->stage, "invalid indirect base location kind",
    416              op->v.ind.base_kind, op->v.ind.base);
    417   if (!mir_type_address_component(f, op->v.ind.base_type))
    418     mir_fail(f, in, ctx->stage, "invalid indirect base type",
    419              op->v.ind.base_type, op->v.ind.base_kind);
    420   switch ((OptIndirectLocKind)op->v.ind.base_kind) {
    421     case OPT_INDIRECT_REG:
    422       mir_verify_reg(f, in, ctx->stage, op->v.ind.base, RC_INT,
    423                      "unknown indirect base register");
    424       break;
    425     case OPT_INDIRECT_FRAME:
    426       slot = mir_frame_slot(f, in, ctx->stage, (FrameSlot)op->v.ind.base,
    427                             "invalid indirect base spill slot");
    428       if (slot->kind != FS_SPILL)
    429         mir_fail(f, in, ctx->stage, "indirect base value names semantic slot",
    430                  op->v.ind.base, slot->kind);
    431       mir_verify_slot_fits(f, in, ctx->stage, slot, op->v.ind.base_type,
    432                            "indirect base type does not fit spill slot");
    433       break;
    434     case OPT_INDIRECT_FRAME_ADDR:
    435       slot = mir_frame_slot(f, in, ctx->stage, (FrameSlot)op->v.ind.base,
    436                             "invalid indirect frame-address base slot");
    437       if (slot->kind != FS_LOCAL && slot->kind != FS_PARAM)
    438         mir_fail(f, in, ctx->stage,
    439                  "indirect frame-address base names spill slot",
    440                  op->v.ind.base, slot->kind);
    441       break;
    442   }
    443 
    444   if (op->v.ind.index_kind > OPT_INDIRECT_FRAME_ADDR)
    445     mir_fail(f, in, ctx->stage, "invalid indirect index location kind",
    446              op->v.ind.index_kind, op->v.ind.index);
    447   has_index = !(op->v.ind.index_kind == OPT_INDIRECT_REG &&
    448                 op->v.ind.index == (Reg)REG_NONE);
    449   if (!has_index) {
    450     if (op->v.ind.index_type != KIT_CG_TYPE_NONE || op->v.ind.log2_scale ||
    451         op->v.ind.index_ext != OPT_IDX_EXT_NONE)
    452       mir_fail(f, in, ctx->stage, "non-canonical absent indirect index",
    453                op->v.ind.index_type, op->v.ind.log2_scale);
    454   } else {
    455     if (!opt_indirect_index_type_valid(f, op->v.ind.index_type))
    456       mir_fail(f, in, ctx->stage, "invalid indirect index type",
    457                op->v.ind.index_type, op->v.ind.index_kind);
    458     switch ((OptIndirectLocKind)op->v.ind.index_kind) {
    459       case OPT_INDIRECT_REG:
    460         mir_verify_reg(f, in, ctx->stage, op->v.ind.index, RC_INT,
    461                        "unknown indirect index register");
    462         break;
    463       case OPT_INDIRECT_FRAME:
    464         slot = mir_frame_slot(f, in, ctx->stage, (FrameSlot)op->v.ind.index,
    465                               "invalid indirect index spill slot");
    466         if (slot->kind != FS_SPILL)
    467           mir_fail(f, in, ctx->stage,
    468                    "indirect index value names semantic slot",
    469                    op->v.ind.index, slot->kind);
    470         mir_verify_slot_fits(f, in, ctx->stage, slot, op->v.ind.index_type,
    471                              "indirect index type does not fit spill slot");
    472         break;
    473       case OPT_INDIRECT_FRAME_ADDR:
    474         mir_fail(f, in, ctx->stage,
    475                  "frame address cannot be an indirect index",
    476                  op->v.ind.index, op->v.ind.index_type);
    477         break;
    478     }
    479   }
    480   if (op->v.ind.log2_scale > 3u)
    481     mir_fail(f, in, ctx->stage, "invalid indirect scale",
    482              op->v.ind.log2_scale, op->v.ind.index);
    483   if (op->v.ind.index_ext > OPT_IDX_EXT_UXTW ||
    484       (op->v.ind.index_ext != OPT_IDX_EXT_NONE &&
    485        (!has_index || op->v.ind.index_kind == OPT_INDIRECT_FRAME_ADDR ||
    486         cg_type_size(f->c, op->v.ind.index_type) != 4u)))
    487     mir_fail(f, in, ctx->stage, "invalid indirect index extension",
    488              op->v.ind.index_ext, op->v.ind.index_type);
    489 
    490   ctx->indirect = op;
    491   ctx->indirect_part = 1u;
    492 }
    493 
    494 static void mir_verify_operand(Func* f, Inst* in, Operand* op, int is_def,
    495                                void* arg) {
    496   MirVerifyCtx* ctx = (MirVerifyCtx*)arg;
    497   if (!op) return;
    498   if (ctx->indirect) {
    499     mir_verify_indirect_component(f, in, op, is_def, ctx);
    500     return;
    501   }
    502   if (op->flags)
    503     mir_fail(f, in, ctx->stage, "stored operand has transient flags", op->kind,
    504              op->flags);
    505   if (op->kind == OPK_INDIRECT) {
    506     mir_verify_indirect(f, in, op, ctx);
    507     return;
    508   }
    509   mir_verify_scalar_location(f, in, op, is_def, ctx);
    510 }
    511 
    512 #endif /* !NDEBUG */
    513 
    514 void opt_mir_verify(Func* f, const char* stage) {
    515 #ifdef NDEBUG
    516   (void)f;
    517   (void)stage;
    518   return;
    519 #else
    520   Func v;
    521   if (!opt_mir_view(f, &v)) return;
    522   mir_verify_frame_table(&v, stage);
    523   for (u32 b = 0; b < v.nblocks; ++b) {
    524     Block* bl = &v.blocks[b];
    525     if (bl->id != b)
    526       mir_fail(&v, NULL, stage, "non-canonical MIR block id", bl->id, b);
    527     for (u32 i = 0; i < bl->ninsts; ++i) {
    528       Inst* in = &bl->insts[i];
    529       MirVerifyCtx ctx;
    530       memset(&ctx, 0, sizeof ctx);
    531       ctx.stage = stage;
    532       if ((IROp)in->op == IR_PHI) {
    533         mir_fail(&v, in, stage, "phi survived lowering", b, i);
    534       }
    535       if ((IROp)in->op > IR_INTRINSIC)
    536         mir_fail(&v, in, stage, "invalid MIR opcode", in->op, in->id);
    537       if ((in->nopnds != 0u) != (in->opnds != NULL))
    538         mir_fail(&v, in, stage, "inconsistent MIR operand array", in->nopnds,
    539                  in->opnds != NULL);
    540       if (in->def != VAL_NONE || in->ndefs != 0u || in->defs != NULL)
    541         mir_fail(&v, in, stage, "stale semantic definition metadata", in->def,
    542                  in->ndefs);
    543       if ((IROp)in->op == IR_PARAM_DECL) {
    544         if (in->nopnds)
    545           mir_fail(&v, in, stage, "MIR parameter marker carries operands", b,
    546                    i);
    547         continue;
    548       }
    549       if ((IROp)in->op == IR_ASM_BLOCK) mir_verify_asm_aux(&v, in, stage);
    550       opt_walk_inst_operands(&v, in, mir_verify_operand, &ctx);
    551       if (ctx.indirect)
    552         mir_fail(&v, in, stage, "incomplete indirect component walk",
    553                  ctx.indirect_part, in->id);
    554     }
    555   }
    556 #endif
    557 }
    558 
    559 void opt_mir_combine(Func* f, NativeTarget* target) {
    560   Func v;
    561   if (!opt_mir_view(f, &v)) return;
    562   opt_combine(&v, target);
    563   opt_mir_commit(f, &v);
    564 }
    565 
    566 void opt_mir_dce(Func* f) {
    567   Func v;
    568   if (!opt_mir_view(f, &v)) return;
    569   opt_dce(&v);
    570   opt_mir_commit(f, &v);
    571 }
    572 
    573 void opt_mir_build_cfg(Func* f) {
    574   Func v;
    575   if (!opt_mir_view(f, &v)) return;
    576   opt_build_cfg(&v);
    577   opt_mir_commit(f, &v);
    578 }
    579 
    580 void opt_mir_jump_cleanup(Func* f, OptJumpCleanupStage stage) {
    581   Func v;
    582   if (!opt_mir_view(f, &v)) return;
    583   opt_jump_cleanup(&v, stage);
    584   opt_mir_commit(f, &v);
    585 }