10824abbaSChris Fallin //! Alias analysis, consisting of a "last store" pass and a "memory
20824abbaSChris Fallin //! values" pass. These two passes operate as one fused pass, and so
30824abbaSChris Fallin //! are implemented together here.
40824abbaSChris Fallin //!
50824abbaSChris Fallin //! We partition memory state into several *disjoint pieces* of
60824abbaSChris Fallin //! "abstract state". There are a finite number of such pieces:
70824abbaSChris Fallin //! currently, we call them "heap", "table", "vmctx", and "other".Any
80824abbaSChris Fallin //! given address in memory belongs to exactly one disjoint piece.
90824abbaSChris Fallin //!
100824abbaSChris Fallin //! One never tracks which piece a concrete address belongs to at
110824abbaSChris Fallin //! runtime; this is a purely static concept. Instead, all
120824abbaSChris Fallin //! memory-accessing instructions (loads and stores) are labeled with
130824abbaSChris Fallin //! one of these four categories in the `MemFlags`. It is forbidden
140824abbaSChris Fallin //! for a load or store to access memory under one category and a
150824abbaSChris Fallin //! later load or store to access the same memory under a different
160824abbaSChris Fallin //! category. This is ensured to be true by construction during
170824abbaSChris Fallin //! frontend translation into CLIF and during legalization.
180824abbaSChris Fallin //!
190824abbaSChris Fallin //! Given that this non-aliasing property is ensured by the producer
200824abbaSChris Fallin //! of CLIF, we can compute a *may-alias* property: one load or store
210824abbaSChris Fallin //! may-alias another load or store if both access the same category
220824abbaSChris Fallin //! of abstract state.
230824abbaSChris Fallin //!
240824abbaSChris Fallin //! The "last store" pass helps to compute this aliasing: it scans the
250824abbaSChris Fallin //! code, finding at each program point the last instruction that
260824abbaSChris Fallin //! *might have* written to a given part of abstract state.
270824abbaSChris Fallin //!
280824abbaSChris Fallin //! We can't say for sure that the "last store" *did* actually write
290824abbaSChris Fallin //! that state, but we know for sure that no instruction *later* than
300824abbaSChris Fallin //! it (up to the current instruction) did. However, we can get a
310824abbaSChris Fallin //! must-alias property from this: if at a given load or store, we
320824abbaSChris Fallin //! look backward to the "last store", *AND* we find that it has
330824abbaSChris Fallin //! exactly the same address expression and type, then we know that
340824abbaSChris Fallin //! the current instruction's access *must* be to the same memory
350824abbaSChris Fallin //! location.
360824abbaSChris Fallin //!
370824abbaSChris Fallin //! To get this must-alias property, we compute a sparse table of
380824abbaSChris Fallin //! "memory values": these are known equivalences between SSA `Value`s
390824abbaSChris Fallin //! and particular locations in memory. The memory-values table is a
400824abbaSChris Fallin //! mapping from (last store, address expression, type) to SSA
410824abbaSChris Fallin //! value. At a store, we can insert into this table directly. At a
420824abbaSChris Fallin //! load, we can also insert, if we don't already have a value (from
430824abbaSChris Fallin //! the store that produced the load's value).
440824abbaSChris Fallin //!
450824abbaSChris Fallin //! Then we can do two optimizations at once given this table. If a
460824abbaSChris Fallin //! load accesses a location identified by a (last store, address,
470824abbaSChris Fallin //! type) key already in the table, we replace it with the SSA value
480824abbaSChris Fallin //! for that memory location. This is usually known as "redundant load
490824abbaSChris Fallin //! elimination" if the value came from an earlier load of the same
500824abbaSChris Fallin //! location, or "store-to-load forwarding" if the value came from an
510824abbaSChris Fallin //! earlier store to the same location.
520824abbaSChris Fallin //!
530824abbaSChris Fallin //! In theory we could also do *dead-store elimination*, where if a
540824abbaSChris Fallin //! store overwrites a key in the table, *and* if no other load/store
550824abbaSChris Fallin //! to the abstract state category occurred, *and* no other trapping
560824abbaSChris Fallin //! instruction occurred (at which point we need an up-to-date memory
570824abbaSChris Fallin //! state because post-trap-termination memory state can be observed),
580824abbaSChris Fallin //! *and* we can prove the original store could not have trapped, then
590824abbaSChris Fallin //! we can eliminate the original store. Because this is so complex,
600824abbaSChris Fallin //! and the conditions for doing it correctly when post-trap state
610824abbaSChris Fallin //! must be correct likely reduce the potential benefit, we don't yet
620824abbaSChris Fallin //! do this.
630824abbaSChris Fallin
64*76911c29SSSD use crate::{FxHashMap, FxHashSet};
650824abbaSChris Fallin use crate::{
660824abbaSChris Fallin cursor::{Cursor, FuncCursor},
673a14fa39SPaul Nodet dominator_tree::DominatorTree,
680824abbaSChris Fallin inst_predicates::{
690824abbaSChris Fallin has_memory_fence_semantics, inst_addr_offset_type, inst_store_data, visit_block_succs,
700824abbaSChris Fallin },
7190ac295eSAlex Crichton ir::{AliasRegion, Block, Function, Inst, Opcode, Type, Value, immediates::Offset32},
728d022434SBenjamin Bouvier trace,
730824abbaSChris Fallin };
7490ac295eSAlex Crichton use cranelift_entity::{EntityRef, packed_option::PackedOption};
750824abbaSChris Fallin
760824abbaSChris Fallin /// For a given program point, the vector of last-store instruction
770824abbaSChris Fallin /// indices for each disjoint category of abstract state.
780824abbaSChris Fallin #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
79feaa7ca7SChris Fallin pub struct LastStores {
800824abbaSChris Fallin heap: PackedOption<Inst>,
810824abbaSChris Fallin table: PackedOption<Inst>,
820824abbaSChris Fallin vmctx: PackedOption<Inst>,
830824abbaSChris Fallin other: PackedOption<Inst>,
840824abbaSChris Fallin }
850824abbaSChris Fallin
860824abbaSChris Fallin impl LastStores {
update(&mut self, func: &Function, inst: Inst)870824abbaSChris Fallin fn update(&mut self, func: &Function, inst: Inst) {
8825bf8e0eSTrevor Elliott let opcode = func.dfg.insts[inst].opcode();
890824abbaSChris Fallin if has_memory_fence_semantics(opcode) {
900824abbaSChris Fallin self.heap = inst.into();
910824abbaSChris Fallin self.table = inst.into();
920824abbaSChris Fallin self.vmctx = inst.into();
930824abbaSChris Fallin self.other = inst.into();
940824abbaSChris Fallin } else if opcode.can_store() {
9525bf8e0eSTrevor Elliott if let Some(memflags) = func.dfg.insts[inst].memflags() {
96b651c441SAlex Crichton match memflags.alias_region() {
97b651c441SAlex Crichton None => self.other = inst.into(),
98b651c441SAlex Crichton Some(AliasRegion::Heap) => self.heap = inst.into(),
99b651c441SAlex Crichton Some(AliasRegion::Table) => self.table = inst.into(),
100b651c441SAlex Crichton Some(AliasRegion::Vmctx) => self.vmctx = inst.into(),
1010824abbaSChris Fallin }
1020824abbaSChris Fallin } else {
1030824abbaSChris Fallin self.heap = inst.into();
1040824abbaSChris Fallin self.table = inst.into();
1050824abbaSChris Fallin self.vmctx = inst.into();
1060824abbaSChris Fallin self.other = inst.into();
1070824abbaSChris Fallin }
1080824abbaSChris Fallin }
1090824abbaSChris Fallin }
1100824abbaSChris Fallin
get_last_store(&self, func: &Function, inst: Inst) -> PackedOption<Inst>1110824abbaSChris Fallin fn get_last_store(&self, func: &Function, inst: Inst) -> PackedOption<Inst> {
11225bf8e0eSTrevor Elliott if let Some(memflags) = func.dfg.insts[inst].memflags() {
113b651c441SAlex Crichton match memflags.alias_region() {
114b651c441SAlex Crichton None => self.other,
115b651c441SAlex Crichton Some(AliasRegion::Heap) => self.heap,
116b651c441SAlex Crichton Some(AliasRegion::Table) => self.table,
117b651c441SAlex Crichton Some(AliasRegion::Vmctx) => self.vmctx,
1180824abbaSChris Fallin }
11925bf8e0eSTrevor Elliott } else if func.dfg.insts[inst].opcode().can_load()
12025bf8e0eSTrevor Elliott || func.dfg.insts[inst].opcode().can_store()
12125bf8e0eSTrevor Elliott {
1220824abbaSChris Fallin inst.into()
1230824abbaSChris Fallin } else {
1240824abbaSChris Fallin PackedOption::default()
1250824abbaSChris Fallin }
1260824abbaSChris Fallin }
1270824abbaSChris Fallin
meet_from(&mut self, other: &LastStores, loc: Inst)1280824abbaSChris Fallin fn meet_from(&mut self, other: &LastStores, loc: Inst) {
1290824abbaSChris Fallin let meet = |a: PackedOption<Inst>, b: PackedOption<Inst>| -> PackedOption<Inst> {
1300824abbaSChris Fallin match (a.into(), b.into()) {
1310824abbaSChris Fallin (None, None) => None.into(),
1320824abbaSChris Fallin (Some(a), None) => a,
1330824abbaSChris Fallin (None, Some(b)) => b,
1340824abbaSChris Fallin (Some(a), Some(b)) if a == b => a,
1350824abbaSChris Fallin _ => loc.into(),
1360824abbaSChris Fallin }
1370824abbaSChris Fallin };
1380824abbaSChris Fallin
1390824abbaSChris Fallin self.heap = meet(self.heap, other.heap);
1400824abbaSChris Fallin self.table = meet(self.table, other.table);
1410824abbaSChris Fallin self.vmctx = meet(self.vmctx, other.vmctx);
1420824abbaSChris Fallin self.other = meet(self.other, other.other);
1430824abbaSChris Fallin }
1440824abbaSChris Fallin }
1450824abbaSChris Fallin
1460824abbaSChris Fallin /// A key identifying a unique memory location.
1470824abbaSChris Fallin ///
1480824abbaSChris Fallin /// For the result of a load to be equivalent to the result of another
1490824abbaSChris Fallin /// load, or the store data from a store, we need for (i) the
1500824abbaSChris Fallin /// "version" of memory (here ensured by having the same last store
1510824abbaSChris Fallin /// instruction to touch the disjoint category of abstract state we're
1520824abbaSChris Fallin /// accessing); (ii) the address must be the same (here ensured by
1530824abbaSChris Fallin /// having the same SSA value, which doesn't change after computed);
1540824abbaSChris Fallin /// (iii) the offset must be the same; and (iv) the accessed type and
1550824abbaSChris Fallin /// extension mode (e.g., 8-to-32, signed) must be the same.
1560824abbaSChris Fallin #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1570824abbaSChris Fallin struct MemoryLoc {
1580824abbaSChris Fallin last_store: PackedOption<Inst>,
1590824abbaSChris Fallin address: Value,
1600824abbaSChris Fallin offset: Offset32,
1610824abbaSChris Fallin ty: Type,
1620824abbaSChris Fallin /// We keep the *opcode* of the instruction that produced the
1630824abbaSChris Fallin /// value we record at this key if the opcode is anything other
1640824abbaSChris Fallin /// than an ordinary load or store. This is needed when we
1650824abbaSChris Fallin /// consider loads that extend the value: e.g., an 8-to-32
1660824abbaSChris Fallin /// sign-extending load will produce a 32-bit value from an 8-bit
1670824abbaSChris Fallin /// value in memory, so we can only reuse that (as part of RLE)
1680824abbaSChris Fallin /// for another load with the same extending opcode.
1690824abbaSChris Fallin ///
1700824abbaSChris Fallin /// We could improve the transform to insert explicit extend ops
1710824abbaSChris Fallin /// in place of extending loads when we know the memory value, but
1720824abbaSChris Fallin /// we haven't yet done this.
1730824abbaSChris Fallin extending_opcode: Option<Opcode>,
1740824abbaSChris Fallin }
1750824abbaSChris Fallin
1760824abbaSChris Fallin /// An alias-analysis pass.
1770824abbaSChris Fallin pub struct AliasAnalysis<'a> {
1780824abbaSChris Fallin /// The domtree for the function.
1793a14fa39SPaul Nodet domtree: &'a DominatorTree,
1800824abbaSChris Fallin
1810824abbaSChris Fallin /// Input state to a basic block.
1820824abbaSChris Fallin block_input: FxHashMap<Block, LastStores>,
1830824abbaSChris Fallin
1840824abbaSChris Fallin /// Known memory-value equivalences. This is the result of the
1850824abbaSChris Fallin /// analysis. This is a mapping from (last store, address
1860824abbaSChris Fallin /// expression, offset, type) to SSA `Value`.
1870824abbaSChris Fallin ///
1880824abbaSChris Fallin /// We keep the defining inst around for quick dominance checks.
1890824abbaSChris Fallin mem_values: FxHashMap<MemoryLoc, (Inst, Value)>,
1900824abbaSChris Fallin }
1910824abbaSChris Fallin
1920824abbaSChris Fallin impl<'a> AliasAnalysis<'a> {
1930824abbaSChris Fallin /// Perform an alias analysis pass.
new(func: &Function, domtree: &'a DominatorTree) -> AliasAnalysis<'a>1943a14fa39SPaul Nodet pub fn new(func: &Function, domtree: &'a DominatorTree) -> AliasAnalysis<'a> {
1958d022434SBenjamin Bouvier trace!("alias analysis: input is:\n{:?}", func);
1960824abbaSChris Fallin let mut analysis = AliasAnalysis {
1970824abbaSChris Fallin domtree,
1980824abbaSChris Fallin block_input: FxHashMap::default(),
1990824abbaSChris Fallin mem_values: FxHashMap::default(),
2000824abbaSChris Fallin };
2010824abbaSChris Fallin
202feaa7ca7SChris Fallin analysis.compute_block_input_states(func);
2030824abbaSChris Fallin analysis
2040824abbaSChris Fallin }
2050824abbaSChris Fallin
compute_block_input_states(&mut self, func: &Function)206feaa7ca7SChris Fallin fn compute_block_input_states(&mut self, func: &Function) {
2070824abbaSChris Fallin let mut queue = vec![];
2080824abbaSChris Fallin let mut queue_set = FxHashSet::default();
209feaa7ca7SChris Fallin let entry = func.layout.entry_block().unwrap();
2100824abbaSChris Fallin queue.push(entry);
2110824abbaSChris Fallin queue_set.insert(entry);
2120824abbaSChris Fallin
2130824abbaSChris Fallin while let Some(block) = queue.pop() {
2140824abbaSChris Fallin queue_set.remove(&block);
2150c0153c1SNick Fitzgerald let mut state = *self
2160824abbaSChris Fallin .block_input
2170824abbaSChris Fallin .entry(block)
2180c0153c1SNick Fitzgerald .or_insert_with(|| LastStores::default());
2190824abbaSChris Fallin
2208d022434SBenjamin Bouvier trace!(
2210824abbaSChris Fallin "alias analysis: input to block{} is {:?}",
2220824abbaSChris Fallin block.index(),
2230824abbaSChris Fallin state
2240824abbaSChris Fallin );
2250824abbaSChris Fallin
226feaa7ca7SChris Fallin for inst in func.layout.block_insts(block) {
227feaa7ca7SChris Fallin state.update(func, inst);
2288d022434SBenjamin Bouvier trace!("after inst{}: state is {:?}", inst.index(), state);
2290824abbaSChris Fallin }
2300824abbaSChris Fallin
231feaa7ca7SChris Fallin visit_block_succs(func, block, |_inst, succ, _from_table| {
232703871a2SAlex Crichton let succ_first_inst = func.layout.block_insts(succ).next().unwrap();
2330824abbaSChris Fallin let updated = match self.block_input.get_mut(&succ) {
2340824abbaSChris Fallin Some(succ_state) => {
2350c0153c1SNick Fitzgerald let old = *succ_state;
2360824abbaSChris Fallin succ_state.meet_from(&state, succ_first_inst);
2370824abbaSChris Fallin *succ_state != old
2380824abbaSChris Fallin }
2390824abbaSChris Fallin None => {
2400c0153c1SNick Fitzgerald self.block_input.insert(succ, state);
2410824abbaSChris Fallin true
2420824abbaSChris Fallin }
2430824abbaSChris Fallin };
2440824abbaSChris Fallin
2450824abbaSChris Fallin if updated && queue_set.insert(succ) {
2460824abbaSChris Fallin queue.push(succ);
2470824abbaSChris Fallin }
2480824abbaSChris Fallin });
2490824abbaSChris Fallin }
2500824abbaSChris Fallin }
2510824abbaSChris Fallin
252feaa7ca7SChris Fallin /// Get the starting state for a block.
block_starting_state(&self, block: Block) -> LastStores253feaa7ca7SChris Fallin pub fn block_starting_state(&self, block: Block) -> LastStores {
254feaa7ca7SChris Fallin self.block_input
2550824abbaSChris Fallin .get(&block)
2560824abbaSChris Fallin .cloned()
257feaa7ca7SChris Fallin .unwrap_or_else(|| LastStores::default())
258feaa7ca7SChris Fallin }
2590824abbaSChris Fallin
260feaa7ca7SChris Fallin /// Process one instruction. Meant to be invoked in program order
261feaa7ca7SChris Fallin /// within a block, and ideally in RPO or at least some domtree
262feaa7ca7SChris Fallin /// preorder for maximal reuse.
263feaa7ca7SChris Fallin ///
264feaa7ca7SChris Fallin /// Returns `true` if instruction was removed.
process_inst( &mut self, func: &mut Function, state: &mut LastStores, inst: Inst, ) -> Option<Value>265feaa7ca7SChris Fallin pub fn process_inst(
266feaa7ca7SChris Fallin &mut self,
267feaa7ca7SChris Fallin func: &mut Function,
268feaa7ca7SChris Fallin state: &mut LastStores,
269feaa7ca7SChris Fallin inst: Inst,
270feaa7ca7SChris Fallin ) -> Option<Value> {
2718d022434SBenjamin Bouvier trace!(
2720824abbaSChris Fallin "alias analysis: scanning at inst{} with state {:?} ({:?})",
2730824abbaSChris Fallin inst.index(),
2740824abbaSChris Fallin state,
27525bf8e0eSTrevor Elliott func.dfg.insts[inst],
2760824abbaSChris Fallin );
2770824abbaSChris Fallin
278feaa7ca7SChris Fallin let replacing_value = if let Some((address, offset, ty)) = inst_addr_offset_type(func, inst)
279feaa7ca7SChris Fallin {
280feaa7ca7SChris Fallin let address = func.dfg.resolve_aliases(address);
28125bf8e0eSTrevor Elliott let opcode = func.dfg.insts[inst].opcode();
2820824abbaSChris Fallin
2830824abbaSChris Fallin if opcode.can_store() {
284feaa7ca7SChris Fallin let store_data = inst_store_data(func, inst).unwrap();
285feaa7ca7SChris Fallin let store_data = func.dfg.resolve_aliases(store_data);
2860824abbaSChris Fallin let mem_loc = MemoryLoc {
2870824abbaSChris Fallin last_store: inst.into(),
2880824abbaSChris Fallin address,
2890824abbaSChris Fallin offset,
2900824abbaSChris Fallin ty,
2910824abbaSChris Fallin extending_opcode: get_ext_opcode(opcode),
2920824abbaSChris Fallin };
2938d022434SBenjamin Bouvier trace!(
2940824abbaSChris Fallin "alias analysis: at inst{}: store with data v{} at loc {:?}",
2950824abbaSChris Fallin inst.index(),
2960824abbaSChris Fallin store_data.index(),
2970824abbaSChris Fallin mem_loc
2980824abbaSChris Fallin );
2990824abbaSChris Fallin self.mem_values.insert(mem_loc, (inst, store_data));
300feaa7ca7SChris Fallin
301feaa7ca7SChris Fallin None
3020824abbaSChris Fallin } else if opcode.can_load() {
303feaa7ca7SChris Fallin let last_store = state.get_last_store(func, inst);
304feaa7ca7SChris Fallin let load_result = func.dfg.inst_results(inst)[0];
3050824abbaSChris Fallin let mem_loc = MemoryLoc {
3060824abbaSChris Fallin last_store,
3070824abbaSChris Fallin address,
3080824abbaSChris Fallin offset,
3090824abbaSChris Fallin ty,
3100824abbaSChris Fallin extending_opcode: get_ext_opcode(opcode),
3110824abbaSChris Fallin };
3128d022434SBenjamin Bouvier trace!(
3130824abbaSChris Fallin "alias analysis: at inst{}: load with last_store inst{} at loc {:?}",
3140824abbaSChris Fallin inst.index(),
3150824abbaSChris Fallin last_store.map(|inst| inst.index()).unwrap_or(usize::MAX),
3160824abbaSChris Fallin mem_loc
3170824abbaSChris Fallin );
3180824abbaSChris Fallin
3190824abbaSChris Fallin // Is there a Value already known to be stored
3200824abbaSChris Fallin // at this specific memory location? If so,
3210824abbaSChris Fallin // we can alias the load result to this
3220824abbaSChris Fallin // already-known Value.
3230824abbaSChris Fallin //
3240824abbaSChris Fallin // Check if the definition dominates this
3250824abbaSChris Fallin // location; it might not, if it comes from a
3260824abbaSChris Fallin // load (stores will always dominate though if
3270824abbaSChris Fallin // their `last_store` survives through
3280824abbaSChris Fallin // meet-points to this use-site).
329feaa7ca7SChris Fallin let aliased =
330feaa7ca7SChris Fallin if let Some((def_inst, value)) = self.mem_values.get(&mem_loc).cloned() {
3318d022434SBenjamin Bouvier trace!(
3320824abbaSChris Fallin " -> sees known value v{} from inst{}",
3330824abbaSChris Fallin value.index(),
3340824abbaSChris Fallin def_inst.index()
3350824abbaSChris Fallin );
3363a14fa39SPaul Nodet if self.domtree.dominates(def_inst, inst, &func.layout) {
3378d022434SBenjamin Bouvier trace!(
3380824abbaSChris Fallin " -> dominates; value equiv from v{} to v{} inserted",
3390824abbaSChris Fallin load_result.index(),
3400824abbaSChris Fallin value.index()
3410824abbaSChris Fallin );
342feaa7ca7SChris Fallin Some(value)
3430824abbaSChris Fallin } else {
344feaa7ca7SChris Fallin None
3450824abbaSChris Fallin }
3460824abbaSChris Fallin } else {
347feaa7ca7SChris Fallin None
3480824abbaSChris Fallin };
3490824abbaSChris Fallin
3500824abbaSChris Fallin // Otherwise, we can keep *this* load around
3510824abbaSChris Fallin // as a new equivalent value.
352feaa7ca7SChris Fallin if aliased.is_none() {
3538d022434SBenjamin Bouvier trace!(
3540824abbaSChris Fallin " -> inserting load result v{} at loc {:?}",
3550824abbaSChris Fallin load_result.index(),
3560824abbaSChris Fallin mem_loc
3570824abbaSChris Fallin );
3580824abbaSChris Fallin self.mem_values.insert(mem_loc, (inst, load_result));
3590824abbaSChris Fallin }
360feaa7ca7SChris Fallin
361feaa7ca7SChris Fallin aliased
362feaa7ca7SChris Fallin } else {
363feaa7ca7SChris Fallin None
3640824abbaSChris Fallin }
365feaa7ca7SChris Fallin } else {
366feaa7ca7SChris Fallin None
367feaa7ca7SChris Fallin };
368feaa7ca7SChris Fallin
369feaa7ca7SChris Fallin state.update(func, inst);
370feaa7ca7SChris Fallin
371feaa7ca7SChris Fallin replacing_value
3720824abbaSChris Fallin }
3730824abbaSChris Fallin
374feaa7ca7SChris Fallin /// Make a pass and update known-redundant loads to aliased
375feaa7ca7SChris Fallin /// values. We interleave the updates with the memory-location
376feaa7ca7SChris Fallin /// tracking because resolving some aliases may expose others
377feaa7ca7SChris Fallin /// (e.g. in cases of double-indirection with two separate chains
378feaa7ca7SChris Fallin /// of loads).
compute_and_update_aliases(&mut self, func: &mut Function)379feaa7ca7SChris Fallin pub fn compute_and_update_aliases(&mut self, func: &mut Function) {
380feaa7ca7SChris Fallin let mut pos = FuncCursor::new(func);
381feaa7ca7SChris Fallin
382feaa7ca7SChris Fallin while let Some(block) = pos.next_block() {
383feaa7ca7SChris Fallin let mut state = self.block_starting_state(block);
384feaa7ca7SChris Fallin while let Some(inst) = pos.next_inst() {
385feaa7ca7SChris Fallin if let Some(replaced_result) = self.process_inst(pos.func, &mut state, inst) {
386feaa7ca7SChris Fallin let result = pos.func.dfg.inst_results(inst)[0];
387d02f895fSJamey Sharp pos.func.dfg.clear_results(inst);
388feaa7ca7SChris Fallin pos.func.dfg.change_to_alias(result, replaced_result);
389feaa7ca7SChris Fallin pos.remove_inst_and_step_back();
390feaa7ca7SChris Fallin }
3910824abbaSChris Fallin }
3920824abbaSChris Fallin }
3930824abbaSChris Fallin }
3940824abbaSChris Fallin }
3950824abbaSChris Fallin
get_ext_opcode(op: Opcode) -> Option<Opcode>3960824abbaSChris Fallin fn get_ext_opcode(op: Opcode) -> Option<Opcode> {
3970824abbaSChris Fallin debug_assert!(op.can_load() || op.can_store());
3980824abbaSChris Fallin match op {
3990824abbaSChris Fallin Opcode::Load | Opcode::Store => None,
4000824abbaSChris Fallin _ => Some(op),
4010824abbaSChris Fallin }
4020824abbaSChris Fallin }
403