Optimization of a single instruction. This does a few things: - Looks up the instruction in the GVN deduplication map. If we already have the same instruction somewhere else, with the same args, then we can alias the original instruction's results and omit this instruction entirely. - If the instruction is "new" (not deduplicated), then apply optimization rules: - All of the mid-end rules written
(&mut self, inst: NewOrExistingInst)
| 196 | /// - Update the value-to-opt-value map, and update the eclass |
| 197 | /// union-find, if we rewrote the value to different form(s). |
| 198 | pub(crate) fn insert_pure_enode(&mut self, inst: NewOrExistingInst) -> Value { |
| 199 | // Create the external context for looking up and updating the |
| 200 | // GVN map. This is necessary so that instructions themselves |
| 201 | // do not have to carry all the references or data for a full |
| 202 | // `Eq` or `Hash` impl. |
| 203 | let gvn_context = GVNContext { |
| 204 | value_lists: &self.func.dfg.value_lists, |
| 205 | }; |
| 206 | |
| 207 | self.stats.pure_inst += 1; |
| 208 | if let NewOrExistingInst::New(..) = inst { |
| 209 | self.stats.new_inst += 1; |
| 210 | } |
| 211 | |
| 212 | // Does this instruction already exist? If so, add entries to |
| 213 | // the value-map to rewrite uses of its results to the results |
| 214 | // of the original (existing) instruction. If not, optimize |
| 215 | // the new instruction. |
| 216 | if let Some(&Some(orig_result)) = self |
| 217 | .gvn_map |
| 218 | .get(&gvn_context, &inst.get_inst_key(&self.func.dfg)) |
| 219 | { |
| 220 | self.stats.pure_inst_deduped += 1; |
| 221 | if let NewOrExistingInst::Existing(inst) = inst { |
| 222 | debug_assert_eq!(self.func.dfg.inst_results(inst).len(), 1); |
| 223 | let result = self.func.dfg.first_result(inst); |
| 224 | self.value_to_opt_value[result] = orig_result; |
| 225 | self.available_block[result] = self.available_block[orig_result]; |
| 226 | } |
| 227 | orig_result |
| 228 | } else { |
| 229 | // Now actually insert the InstructionData and attach |
| 230 | // result value (exactly one). |
| 231 | let (inst, result, ty) = match inst { |
| 232 | NewOrExistingInst::New(data, typevar) => { |
| 233 | self.stats.pure_inst_insert_new += 1; |
| 234 | let inst = self.func.dfg.make_inst(data); |
| 235 | // TODO: reuse return value? |
| 236 | self.func.dfg.make_inst_results(inst, typevar); |
| 237 | let result = self.func.dfg.first_result(inst); |
| 238 | // New inst. We need to do the analysis of its result. |
| 239 | (inst, result, typevar) |
| 240 | } |
| 241 | NewOrExistingInst::Existing(inst) => { |
| 242 | self.stats.pure_inst_insert_orig += 1; |
| 243 | let result = self.func.dfg.first_result(inst); |
| 244 | let ty = self.func.dfg.ctrl_typevar(inst); |
| 245 | (inst, result, ty) |
| 246 | } |
| 247 | }; |
| 248 | |
| 249 | self.available_block[result] = self.get_available_block(inst); |
| 250 | let opt_value = self.optimize_pure_enode(inst); |
| 251 | log::trace!("optimizing inst {inst} orig result {result} gave {opt_value}"); |
| 252 | |
| 253 | let gvn_context = GVNContext { |
| 254 | value_lists: &self.func.dfg.value_lists, |
| 255 | }; |
no test coverage detected