pass_copy.c (9907B)
1 #include <string.h> 2 3 #include "opt/opt_internal.h" 4 5 typedef struct ConvertStep { 6 Val src; 7 KitCgTypeId src_type; 8 KitCgTypeId dst_type; 9 u32 src_width; 10 u32 dst_width; 11 u8 kind; 12 } ConvertStep; 13 14 static int same_val_shape(Func* f, Val a, Val b) { 15 if (a == VAL_NONE || b == VAL_NONE || a >= f->nvals || b >= f->nvals) 16 return 0; 17 return f->val_cls[a] == f->val_cls[b] && f->val_type[a] == f->val_type[b]; 18 } 19 20 static int copy_values(const Inst* in, Val* dst, Val* src) { 21 if (!in || (IROp)in->op != IR_COPY || in->nopnds < 2) return 0; 22 if (in->opnds[0].kind != OPK_REG || in->opnds[1].kind != OPK_REG) return 0; 23 *dst = (Val)in->opnds[0].v.reg; 24 *src = (Val)in->opnds[1].v.reg; 25 if (in->def != VAL_NONE && in->def != *dst) return 0; 26 return 1; 27 } 28 29 static void replace_one_use(Func* f, const OptUse* use, Val src) { 30 Inst* in = &f->blocks[use->block].insts[use->inst]; 31 switch ((OptUseKind)use->kind) { 32 case OPT_USE_OPERAND: 33 use->operand->v.reg = (Reg)src; 34 use->operand->type = f->val_type[src]; 35 use->operand->cls = f->val_cls[src]; 36 break; 37 case OPT_USE_INDIRECT_BASE: 38 use->operand->v.ind.base = (Reg)src; 39 break; 40 case OPT_USE_INDIRECT_INDEX: 41 use->operand->v.ind.index = (Reg)src; 42 break; 43 case OPT_USE_PHI_INPUT: { 44 IRPhiAux* aux = (IRPhiAux*)in->extra.aux; 45 if (aux && use->phi_pred_index < aux->npreds) 46 aux->pred_vals[use->phi_pred_index] = src; 47 break; 48 } 49 default: 50 break; 51 } 52 } 53 54 static void remove_copy_inst(Inst* in) { 55 in->op = IR_NOP; 56 in->def = VAL_NONE; 57 in->ndefs = 0; 58 in->defs = NULL; 59 in->nopnds = 0; 60 in->opnds = NULL; 61 } 62 63 static void compact_copies(Func* f) { 64 for (u32 b = 0; b < f->nblocks; ++b) { 65 Block* bl = &f->blocks[b]; 66 u32 w = 0; 67 for (u32 i = 0; i < bl->ninsts; ++i) { 68 if ((IROp)bl->insts[i].op == IR_NOP) continue; 69 bl->insts[w] = bl->insts[i]; 70 if (bl->insts[w].def != VAL_NONE && bl->insts[w].def < f->nvals) { 71 f->val_def_block[bl->insts[w].def] = b; 72 f->val_def_inst[bl->insts[w].def] = w; 73 } 74 for (u32 d = 0; d < bl->insts[w].ndefs; ++d) { 75 Val v = bl->insts[w].defs[d]; 76 if (v != VAL_NONE && v < f->nvals) { 77 f->val_def_block[v] = b; 78 f->val_def_inst[v] = w; 79 } 80 } 81 ++w; 82 } 83 bl->ninsts = w; 84 } 85 } 86 87 static Inst* val_def_inst(Func* f, Val v) { 88 if (!f || v == VAL_NONE || v >= f->nvals) return NULL; 89 u32 b = f->val_def_block[v]; 90 u32 i = f->val_def_inst[v]; 91 if (b >= f->nblocks || i >= f->blocks[b].ninsts) return NULL; 92 return &f->blocks[b].insts[i]; 93 } 94 95 static int int_width(Func* f, KitCgTypeId ty, u32* out) { 96 u32 width = kit_cg_type_int_width((KitCompiler*)f->c, ty); 97 if (!width || width > 64u) return 0; 98 *out = width; 99 return 1; 100 } 101 102 static int convert_step(Func* f, const Inst* in, ConvertStep* out) { 103 if (!f || !in || !out || (IROp)in->op != IR_CONVERT || in->nopnds < 2) 104 return 0; 105 if (in->def == VAL_NONE || in->def >= f->nvals) return 0; 106 if (in->opnds[0].kind != OPK_REG || in->opnds[1].kind != OPK_REG) return 0; 107 108 memset(out, 0, sizeof *out); 109 out->src = (Val)in->opnds[1].v.reg; 110 if (out->src == VAL_NONE || out->src >= f->nvals) return 0; 111 out->src_type = in->opnds[1].type; 112 out->dst_type = in->opnds[0].type; 113 out->kind = (u8)in->extra.imm; 114 if (!int_width(f, out->src_type, &out->src_width) || 115 !int_width(f, out->dst_type, &out->dst_width)) 116 return 0; 117 return 1; 118 } 119 120 static int conversion_is_noop(const ConvertStep* s) { 121 if (!s || s->src_width != s->dst_width || s->src_type != s->dst_type) 122 return 0; 123 switch ((ConvKind)s->kind) { 124 case CV_ZEXT: 125 case CV_SEXT: 126 case CV_TRUNC: 127 case CV_BITCAST: 128 return 1; 129 default: 130 return 0; 131 } 132 } 133 134 static int extension_kind(u8 kind) { 135 return kind == CV_ZEXT || kind == CV_SEXT; 136 } 137 138 static void set_convert_source(Func* f, Inst* in, Val src) { 139 in->opnds[1].v.reg = (Reg)src; 140 in->opnds[1].type = f->val_type[src]; 141 in->opnds[1].cls = f->val_cls[src]; 142 } 143 144 static void make_copy_from(Func* f, Inst* in, Val src) { 145 Val dst = in->def; 146 Operand* opnds = in->opnds; 147 if (!opnds) opnds = arena_array(f->arena, Operand, 2); 148 memset(&opnds[0], 0, sizeof opnds[0]); 149 memset(&opnds[1], 0, sizeof opnds[1]); 150 opnds[0].kind = OPK_REG; 151 opnds[0].type = f->val_type[dst]; 152 opnds[0].cls = f->val_cls[dst]; 153 opnds[0].v.reg = (Reg)dst; 154 opnds[1].kind = OPK_REG; 155 opnds[1].type = f->val_type[src]; 156 opnds[1].cls = f->val_cls[src]; 157 opnds[1].v.reg = (Reg)src; 158 in->op = IR_COPY; 159 in->type = f->val_type[dst]; 160 in->opnds = opnds; 161 in->nopnds = 2; 162 } 163 164 static int simplify_convert_chain(Func* f, Inst* outer, const ConvertStep* o, 165 const ConvertStep* inner) { 166 if (!f || !outer || !o || !inner) return 0; 167 168 if (conversion_is_noop(o)) { 169 make_copy_from(f, outer, o->src); 170 return 1; 171 } 172 173 if (extension_kind(o->kind) && o->kind == inner->kind && 174 inner->src_width <= inner->dst_width && 175 inner->dst_width <= o->dst_width) { 176 set_convert_source(f, outer, inner->src); 177 return 1; 178 } 179 180 if (o->kind == CV_TRUNC && extension_kind(inner->kind) && 181 o->dst_width <= inner->dst_width) { 182 set_convert_source(f, outer, inner->src); 183 if (o->dst_width == inner->src_width && o->dst_type == inner->src_type) { 184 make_copy_from(f, outer, inner->src); 185 } else if (o->dst_width > inner->src_width) { 186 outer->extra.imm = inner->kind; 187 } 188 return 1; 189 } 190 191 return 0; 192 } 193 194 /* Precompute the number of definitions of every value in a single sweep so the 195 * batch copy pass can test the def_count==1 removability condition in O(1) 196 * instead of rescanning the whole function per copy. */ 197 static void compute_def_counts(Func* f, u32* ndef) { 198 memset(ndef, 0, sizeof(*ndef) * f->nvals); 199 for (u32 b = 0; b < f->nblocks; ++b) { 200 Block* bl = &f->blocks[b]; 201 for (u32 i = 0; i < bl->ninsts; ++i) { 202 Inst* in = &bl->insts[i]; 203 if (in->def != VAL_NONE && in->def < f->nvals) ++ndef[in->def]; 204 for (u32 d = 0; d < in->ndefs; ++d) 205 if (in->defs[d] != VAL_NONE && in->defs[d] < f->nvals) 206 ++ndef[in->defs[d]]; 207 } 208 } 209 } 210 211 /* Follow the removed-copy chain dst -> src to its final representative: a value 212 * that is not itself the dst of a removed copy (or a self-copy). `repl[v]` is 213 * VAL_NONE when v is not a removed-copy dst. Bounded by nvals to stay safe if a 214 * pathological IR forms a copy cycle. */ 215 static Val resolve_copy(Func* f, const Val* repl, Val v) { 216 for (u32 hops = 0; hops < f->nvals; ++hops) { 217 Val next; 218 if (v == VAL_NONE || v >= f->nvals) break; 219 next = repl[v]; 220 if (next == VAL_NONE || next == v) break; 221 v = next; 222 } 223 return v; 224 } 225 226 /* Collect every removable IR_COPY in one def-use pass, resolve copy chains to 227 * their final source, then redirect all uses and drop the copies together — 228 * O(N) rather than the O(N^2) "rebuild + remove one" loop. Removing a copy 229 * never changes another value's def_count (a removable non-self copy is the 230 * sole def of its dst), so a single pass reaches the fixpoint. */ 231 static int cleanup_copies_pass(Func* f) { 232 u32* ndef; 233 Val* repl; 234 int removed_any = 0; 235 opt_rebuild_def_use(f); 236 if (!f->nvals) return 0; 237 ndef = arena_array(f->arena, u32, f->nvals); 238 repl = arena_array(f->arena, Val, f->nvals); 239 for (u32 v = 0; v < f->nvals; ++v) repl[v] = VAL_NONE; 240 compute_def_counts(f, ndef); 241 /* First sweep: tag each removable copy's dst with its immediate src. */ 242 for (u32 b = 0; b < f->nblocks; ++b) { 243 Block* bl = &f->blocks[b]; 244 for (u32 i = 0; i < bl->ninsts; ++i) { 245 Inst* in = &bl->insts[i]; 246 Val dst = VAL_NONE; 247 Val src = VAL_NONE; 248 if (!copy_values(in, &dst, &src)) continue; 249 if (dst == VAL_NONE || dst >= f->nvals) continue; 250 if (dst != src && !same_val_shape(f, dst, src)) continue; 251 if (dst != src && ndef[dst] != 1) continue; 252 repl[dst] = src; 253 removed_any = 1; 254 } 255 } 256 if (!removed_any) return 0; 257 /* Second sweep: redirect uses to the resolved source and erase the copies. */ 258 for (u32 b = 0; b < f->nblocks; ++b) { 259 Block* bl = &f->blocks[b]; 260 for (u32 i = 0; i < bl->ninsts; ++i) { 261 Inst* in = &bl->insts[i]; 262 Val dst = VAL_NONE; 263 Val src = VAL_NONE; 264 Val final_src; 265 if (!copy_values(in, &dst, &src)) continue; 266 if (dst >= f->nvals || repl[dst] == VAL_NONE) continue; 267 final_src = resolve_copy(f, repl, dst); 268 if (final_src != dst) 269 for (u32 u = f->opt_first_use_by_val[dst]; u != OPT_USE_NONE; 270 u = f->opt_uses[u].next_for_val) 271 replace_one_use(f, &f->opt_uses[u], final_src); 272 remove_copy_inst(in); 273 } 274 } 275 opt_analysis_invalidate(f, OPT_ANALYSIS_DEF_USE); 276 compact_copies(f); 277 return 1; 278 } 279 280 static int cleanup_one_extension(Func* f) { 281 for (u32 b = 0; b < f->nblocks; ++b) { 282 Block* bl = &f->blocks[b]; 283 for (u32 i = 0; i < bl->ninsts; ++i) { 284 Inst* outer = &bl->insts[i]; 285 ConvertStep o; 286 if (!convert_step(f, outer, &o)) continue; 287 288 Inst* inner_inst = val_def_inst(f, o.src); 289 ConvertStep inner; 290 if (!convert_step(f, inner_inst, &inner)) { 291 if (!conversion_is_noop(&o)) continue; 292 make_copy_from(f, outer, o.src); 293 } else if (!simplify_convert_chain(f, outer, &o, &inner)) { 294 continue; 295 } 296 297 opt_analysis_invalidate(f, OPT_ANALYSIS_DEF_USE); 298 return 1; 299 } 300 } 301 return 0; 302 } 303 304 void opt_copy_cleanup(Func* f) { 305 if (!f || f->opt_rewritten) return; 306 while (cleanup_copies_pass(f)) { 307 } 308 opt_rebuild_def_use(f); 309 } 310 311 void opt_copy_prop(Func* f) { 312 if (!f || f->opt_rewritten) return; 313 if (!f->opt_reg_ssa && f->npregs > 1) { 314 opt_copy_cleanup(f); 315 return; 316 } 317 318 opt_copy_cleanup(f); 319 while (cleanup_one_extension(f)) { 320 opt_rebuild_def_use(f); 321 opt_copy_cleanup(f); 322 } 323 opt_copy_cleanup(f); 324 }