pass_dce.c (6210B)
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 && in->opnds[0].kind == OPK_LOCAL; 24 default: 25 return 0; 26 } 27 } 28 29 int opt_inst_has_side_effect(Func* f, const Inst* in) { 30 (void)f; 31 if (opt_inst_writes_frame_local(in)) return 1; 32 switch ((IROp)in->op) { 33 case IR_LOAD: 34 return opt_mem_observable(&in->extra.mem); 35 case IR_BITFIELD_LOAD: { 36 IRBitFieldAux* aux = (IRBitFieldAux*)in->extra.aux; 37 return aux && opt_mem_observable(&aux->access.storage); 38 } 39 case IR_ALLOCA: 40 case IR_PARAM_DECL: 41 case IR_STORE: 42 case IR_AGG_COPY: 43 case IR_AGG_SET: 44 case IR_BITFIELD_STORE: 45 case IR_CALL: 46 case IR_BR: 47 case IR_CONDBR: 48 case IR_CMP_BRANCH: 49 case IR_SWITCH: 50 case IR_INDIRECT_BRANCH: 51 case IR_LOCAL_STATIC_DATA_BEGIN: 52 case IR_LOCAL_STATIC_DATA_WRITE: 53 case IR_LOCAL_STATIC_DATA_LABEL_ADDR: 54 case IR_LOCAL_STATIC_DATA_END: 55 case IR_RET: 56 case IR_UNREACHABLE: 57 case IR_SCOPE_BEGIN: 58 case IR_SCOPE_END: 59 case IR_BREAK_TO: 60 case IR_CONTINUE_TO: 61 case IR_VA_START: 62 case IR_VA_ARG: 63 case IR_VA_END: 64 case IR_VA_COPY: 65 case IR_ATOMIC_LOAD: 66 case IR_ATOMIC_STORE: 67 case IR_ATOMIC_RMW: 68 case IR_ATOMIC_CAS: 69 case IR_FENCE: 70 case IR_ASM_BLOCK: 71 case IR_INTRINSIC: 72 return 1; 73 default: 74 return 0; 75 } 76 } 77 78 static int val_has_uses(Func* f, Val v) { 79 return v != VAL_NONE && v < f->nvals && f->opt_first_use_by_val && 80 f->opt_first_use_by_val[v] != OPT_USE_NONE; 81 } 82 83 static int ssa_dce_candidate(const Inst* in) { 84 switch ((IROp)in->op) { 85 case IR_CONST_I: 86 case IR_CONST_BYTES: 87 case IR_LOAD_IMM: 88 case IR_LOAD_CONST: 89 case IR_LOAD_LABEL_ADDR: 90 case IR_COPY: 91 case IR_BINOP: 92 case IR_UNOP: 93 case IR_CMP: 94 case IR_CONVERT: 95 case IR_PHI: 96 return 1; 97 default: 98 return 0; 99 } 100 } 101 102 static int inst_all_defs_unused(Func* f, const Inst* in) { 103 if (in->def != VAL_NONE && val_has_uses(f, in->def)) return 0; 104 for (u32 i = 0; i < in->ndefs; ++i) 105 if (val_has_uses(f, in->defs[i])) return 0; 106 return in->def != VAL_NONE || in->ndefs != 0; 107 } 108 109 static void refresh_def_locations(Func* f) { 110 for (u32 b = 0; b < f->nblocks; ++b) { 111 Block* bl = &f->blocks[b]; 112 for (u32 i = 0; i < bl->ninsts; ++i) { 113 Inst* in = &bl->insts[i]; 114 if (in->def != VAL_NONE && in->def < f->nvals) { 115 f->val_def_block[in->def] = b; 116 f->val_def_inst[in->def] = i; 117 } 118 for (u32 d = 0; d < in->ndefs; ++d) { 119 Val v = in->defs[d]; 120 if (v == VAL_NONE || v >= f->nvals) continue; 121 f->val_def_block[v] = b; 122 f->val_def_inst[v] = i; 123 } 124 } 125 } 126 } 127 128 static void compact_nops(Func* f) { 129 for (u32 b = 0; b < f->nblocks; ++b) { 130 Block* bl = &f->blocks[b]; 131 u32 w = 0; 132 for (u32 i = 0; i < bl->ninsts; ++i) { 133 if ((IROp)bl->insts[i].op == IR_NOP) continue; 134 bl->insts[w++] = bl->insts[i]; 135 } 136 bl->ninsts = w; 137 } 138 refresh_def_locations(f); 139 } 140 141 void opt_ssa_dce(Func* f) { 142 if (!f || f->opt_rewritten) return; 143 int changed = 0; 144 int again = 1; 145 opt_rebuild_def_use(f); 146 while (again) { 147 again = 0; 148 for (u32 b = 0; b < f->nblocks; ++b) { 149 Block* bl = &f->blocks[b]; 150 for (u32 i = 0; i < bl->ninsts; ++i) { 151 Inst* in = &bl->insts[i]; 152 if (!ssa_dce_candidate(in)) continue; 153 if (opt_inst_has_side_effect(f, in)) continue; 154 if (!inst_all_defs_unused(f, in)) continue; 155 in->op = IR_NOP; 156 in->def = VAL_NONE; 157 in->ndefs = 0; 158 in->defs = NULL; 159 in->nopnds = 0; 160 in->opnds = NULL; 161 changed = 1; 162 again = 1; 163 } 164 } 165 if (again) opt_rebuild_def_use(f); 166 } 167 if (changed) { 168 opt_analysis_invalidate(f, OPT_ANALYSIS_DEF_USE); 169 compact_nops(f); 170 } 171 opt_rebuild_def_use(f); 172 } 173 174 void opt_dce(Func* f) { 175 opt_analysis_invalidate(f, OPT_ANALYSIS_DEF_USE); 176 OptHardBlockLive* hard_live = opt_maybe_build_hard_live(f); 177 for (u32 b = 0; b < f->nblocks; ++b) { 178 Block* bl = &f->blocks[b]; 179 if (f->opt_rewritten) { 180 OptHardRegSet live = 181 opt_hard_live_out_for_block(hard_live ? &hard_live[b] : NULL); 182 Inst* new_insts = arena_array(f->arena, Inst, bl->ninsts); 183 u32 w = 0; 184 for (u32 ri = bl->ninsts; ri > 0; --ri) { 185 u32 i = ri - 1u; 186 Inst* in = &bl->insts[i]; 187 OptHardRegSet use, def; 188 if ((IROp)in->op == IR_NOP) continue; 189 opt_hard_inst_use_def(f, in, &use, &def); 190 if (!opt_inst_has_side_effect(f, in) && !opt_hard_empty(&def) && 191 !opt_hard_intersects(&def, &live)) { 192 continue; 193 } 194 if (!opt_inst_has_side_effect(f, in) && opt_hard_empty(&def) && 195 in->nopnds == 0) { 196 continue; 197 } 198 new_insts[w++] = *in; 199 opt_hard_live_step(&live, &use, &def); 200 } 201 for (u32 i = 0; i < w / 2; ++i) { 202 Inst tmp = new_insts[i]; 203 new_insts[i] = new_insts[w - 1u - i]; 204 new_insts[w - 1u - i] = tmp; 205 } 206 bl->insts = new_insts; 207 bl->ninsts = w; 208 bl->cap = w; 209 continue; 210 } 211 212 u32 w = 0; 213 for (u32 i = 0; i < bl->ninsts; ++i) { 214 Inst* in = &bl->insts[i]; 215 if ((IROp)in->op == IR_NOP) continue; 216 if (!opt_inst_has_side_effect(f, in) && in->def == VAL_NONE && 217 in->ndefs == 0 && in->nopnds == 0) 218 continue; 219 bl->insts[w++] = *in; 220 } 221 bl->ninsts = w; 222 } 223 refresh_def_locations(f); 224 }