pass_dce.c (6396B)
1 #include "core/arena.h" 2 #include "opt/opt_internal.h" 3 4 /* A value-producing op whose destination is an OPK_LOCAL operand writes to an 5 * address-taken (frame-homed) local. cg_ir_lower emits those as a value op with 6 * a frame destination rather than a separate IR_STORE, so the write is a memory 7 * side effect even though the op itself (e.g. IR_LOAD_IMM, IR_COPY) is 8 * otherwise pure. Without this, dead-def elimination drops stores to escaped 9 * locals. */ 10 static int opt_inst_writes_frame_local(const Inst* in) { 11 switch ((IROp)in->op) { 12 case IR_LOAD_IMM: 13 case IR_LOAD_CONST: 14 case IR_LOAD_LABEL_ADDR: 15 case IR_COPY: 16 case IR_LOAD: 17 case IR_ADDR_OF: 18 case IR_TLS_ADDR_OF: 19 case IR_BINOP: 20 case IR_UNOP: 21 case IR_CMP: 22 case IR_CONVERT: 23 return in->nopnds > 0 && 24 (in->opnds[0].kind == OPK_LOCAL || 25 in->opnds[0].kind == OPK_STACK); 26 default: 27 return 0; 28 } 29 } 30 31 int opt_inst_has_side_effect(Func* f, const Inst* in) { 32 (void)f; 33 if (opt_inst_writes_frame_local(in)) return 1; 34 switch ((IROp)in->op) { 35 case IR_LOAD: 36 return opt_mem_observable(&in->extra.mem); 37 case IR_BITFIELD_LOAD: { 38 IRBitFieldAux* aux = (IRBitFieldAux*)in->extra.aux; 39 return aux && opt_mem_observable(&aux->access.storage); 40 } 41 case IR_ALLOCA: 42 case IR_PARAM_DECL: 43 case IR_STORE: 44 case IR_AGG_COPY: 45 case IR_AGG_SET: 46 case IR_BITFIELD_STORE: 47 case IR_CALL: 48 case IR_BR: 49 case IR_CONDBR: 50 case IR_CMP_BRANCH: 51 case IR_SWITCH: 52 case IR_INDIRECT_BRANCH: 53 case IR_LOCAL_STATIC_DATA_BEGIN: 54 case IR_LOCAL_STATIC_DATA_WRITE: 55 case IR_LOCAL_STATIC_DATA_LABEL_ADDR: 56 case IR_LOCAL_STATIC_DATA_END: 57 case IR_RET: 58 case IR_UNREACHABLE: 59 case IR_SCOPE_BEGIN: 60 case IR_SCOPE_END: 61 case IR_BREAK_TO: 62 case IR_CONTINUE_TO: 63 case IR_VA_START: 64 case IR_VA_ARG: 65 case IR_VA_END: 66 case IR_VA_COPY: 67 case IR_ATOMIC_LOAD: 68 case IR_ATOMIC_STORE: 69 case IR_ATOMIC_RMW: 70 case IR_ATOMIC_CAS: 71 case IR_FENCE: 72 case IR_ASM_BLOCK: 73 case IR_INTRINSIC: 74 return 1; 75 default: 76 return 0; 77 } 78 } 79 80 static int val_has_uses(Func* f, Val v) { 81 return v != VAL_NONE && v < f->nvals && f->opt_first_use_by_val && 82 f->opt_first_use_by_val[v] != OPT_USE_NONE; 83 } 84 85 static int ssa_dce_candidate(const Inst* in) { 86 switch ((IROp)in->op) { 87 case IR_CONST_I: 88 case IR_CONST_BYTES: 89 case IR_LOAD_IMM: 90 case IR_LOAD_CONST: 91 case IR_LOAD_LABEL_ADDR: 92 case IR_COPY: 93 case IR_BINOP: 94 case IR_UNOP: 95 case IR_CMP: 96 case IR_CONVERT: 97 case IR_PHI: 98 return 1; 99 default: 100 return 0; 101 } 102 } 103 104 static int inst_all_defs_unused(Func* f, const Inst* in) { 105 if (in->def != VAL_NONE && val_has_uses(f, in->def)) return 0; 106 for (u32 i = 0; i < in->ndefs; ++i) 107 if (val_has_uses(f, in->defs[i])) return 0; 108 return in->def != VAL_NONE || in->ndefs != 0; 109 } 110 111 static void refresh_def_locations(Func* f) { 112 for (u32 b = 0; b < f->nblocks; ++b) { 113 Block* bl = &f->blocks[b]; 114 for (u32 i = 0; i < bl->ninsts; ++i) { 115 Inst* in = &bl->insts[i]; 116 if (in->def != VAL_NONE && in->def < f->nvals) { 117 f->val_def_block[in->def] = b; 118 f->val_def_inst[in->def] = i; 119 } 120 for (u32 d = 0; d < in->ndefs; ++d) { 121 Val v = in->defs[d]; 122 if (v == VAL_NONE || v >= f->nvals) continue; 123 f->val_def_block[v] = b; 124 f->val_def_inst[v] = i; 125 } 126 } 127 } 128 } 129 130 static void compact_nops(Func* f) { 131 for (u32 b = 0; b < f->nblocks; ++b) { 132 Block* bl = &f->blocks[b]; 133 u32 w = 0; 134 for (u32 i = 0; i < bl->ninsts; ++i) { 135 if ((IROp)bl->insts[i].op == IR_NOP) continue; 136 bl->insts[w++] = bl->insts[i]; 137 } 138 bl->ninsts = w; 139 } 140 refresh_def_locations(f); 141 } 142 143 void opt_ssa_dce(Func* f) { 144 if (!f || f->opt_rewritten) return; 145 int changed = 0; 146 int again = 1; 147 opt_rebuild_def_use(f); 148 while (again) { 149 again = 0; 150 for (u32 b = 0; b < f->nblocks; ++b) { 151 Block* bl = &f->blocks[b]; 152 for (u32 i = 0; i < bl->ninsts; ++i) { 153 Inst* in = &bl->insts[i]; 154 if (!ssa_dce_candidate(in)) continue; 155 if (opt_inst_has_side_effect(f, in)) continue; 156 if (!inst_all_defs_unused(f, in)) continue; 157 in->op = IR_NOP; 158 in->def = VAL_NONE; 159 in->ndefs = 0; 160 in->defs = NULL; 161 in->nopnds = 0; 162 in->opnds = NULL; 163 changed = 1; 164 again = 1; 165 } 166 } 167 if (again) opt_rebuild_def_use(f); 168 } 169 if (changed) { 170 opt_analysis_invalidate(f, OPT_ANALYSIS_DEF_USE); 171 compact_nops(f); 172 } 173 opt_rebuild_def_use(f); 174 } 175 176 void opt_dce(Func* f) { 177 opt_analysis_invalidate(f, OPT_ANALYSIS_DEF_USE); 178 OptHardBlockLive* hard_live = opt_maybe_build_hard_live(f); 179 for (u32 b = 0; b < f->nblocks; ++b) { 180 Block* bl = &f->blocks[b]; 181 if (f->opt_rewritten) { 182 OptHardRegSet live = 183 opt_hard_live_out_for_block(hard_live ? &hard_live[b] : NULL); 184 Inst* new_insts = arena_array(f->arena, Inst, bl->ninsts); 185 u32 w = 0; 186 for (u32 ri = bl->ninsts; ri > 0; --ri) { 187 u32 i = ri - 1u; 188 Inst* in = &bl->insts[i]; 189 OptRegEffects effects; 190 OptHardRegSet kills; 191 if ((IROp)in->op == IR_NOP) continue; 192 opt_inst_reg_effects(f, in, &effects); 193 opt_reg_effect_kills(&effects, &kills); 194 if (!opt_inst_has_side_effect(f, in) && 195 !opt_hard_empty(&effects.defs) && 196 !opt_hard_intersects(&effects.defs, &live)) { 197 continue; 198 } 199 if (!opt_inst_has_side_effect(f, in) && 200 opt_hard_empty(&effects.defs) && in->nopnds == 0) { 201 continue; 202 } 203 new_insts[w++] = *in; 204 opt_hard_live_step(&live, &effects.uses, &kills); 205 } 206 for (u32 i = 0; i < w / 2; ++i) { 207 Inst tmp = new_insts[i]; 208 new_insts[i] = new_insts[w - 1u - i]; 209 new_insts[w - 1u - i] = tmp; 210 } 211 bl->insts = new_insts; 212 bl->ninsts = w; 213 bl->cap = w; 214 continue; 215 } 216 217 u32 w = 0; 218 for (u32 i = 0; i < bl->ninsts; ++i) { 219 Inst* in = &bl->insts[i]; 220 if ((IROp)in->op == IR_NOP) continue; 221 if (!opt_inst_has_side_effect(f, in) && in->def == VAL_NONE && 222 in->ndefs == 0 && in->nopnds == 0) 223 continue; 224 bl->insts[w++] = *in; 225 } 226 bl->ninsts = w; 227 } 228 refresh_def_locations(f); 229 }