Compute and return a lowered block order for `f`.
(
f: &Function,
domtree: &DominatorTree,
ctrl_plane: &mut ControlPlane,
)
| 146 | impl BlockLoweringOrder { |
| 147 | /// Compute and return a lowered block order for `f`. |
| 148 | pub fn new( |
| 149 | f: &Function, |
| 150 | domtree: &DominatorTree, |
| 151 | ctrl_plane: &mut ControlPlane, |
| 152 | ) -> BlockLoweringOrder { |
| 153 | trace!("BlockLoweringOrder: function body {:?}", f); |
| 154 | |
| 155 | // Step 1: compute the in-edge and out-edge count of every block. |
| 156 | let mut block_in_count = SecondaryMap::with_default(0); |
| 157 | let mut block_out_count = SecondaryMap::with_default(0); |
| 158 | |
| 159 | // Block successors are stored as `LoweredBlocks` to simplify the construction of |
| 160 | // `lowered_succs` in the final result. Initially, all entries are `Orig` values, and are |
| 161 | // updated to be `CriticalEdge` when those cases are identified in step 2 below. |
| 162 | let mut block_succs: SmallVec<[LoweredBlock; 128]> = SmallVec::new(); |
| 163 | let mut block_succ_range = SecondaryMap::with_default(0..0); |
| 164 | |
| 165 | let mut indirect_branch_target_clif_blocks = FxHashSet::default(); |
| 166 | |
| 167 | for block in f.layout.blocks() { |
| 168 | let start = block_succs.len(); |
| 169 | visit_block_succs(f, block, |_, succ, from_table| { |
| 170 | block_out_count[block] += 1; |
| 171 | block_in_count[succ] += 1; |
| 172 | block_succs.push(LoweredBlock::Orig { block: succ }); |
| 173 | |
| 174 | if from_table { |
| 175 | indirect_branch_target_clif_blocks.insert(succ); |
| 176 | } |
| 177 | }); |
| 178 | |
| 179 | // Ensure that blocks terminated by br_table instructions |
| 180 | // with an empty jump table are still treated like |
| 181 | // conditional blocks from the point of view of critical |
| 182 | // edge splitting. Also do the same for TryCall and |
| 183 | // TryCallIndirect: we cannot have edge moves before the |
| 184 | // branch, even if they have empty handler tables and thus |
| 185 | // would otherwise have only one successor. |
| 186 | if let Some(inst) = f.layout.last_inst(block) { |
| 187 | match f.dfg.insts[inst].opcode() { |
| 188 | Opcode::BrTable | Opcode::TryCall | Opcode::TryCallIndirect => { |
| 189 | block_out_count[block] = block_out_count[block].max(2); |
| 190 | } |
| 191 | _ => {} |
| 192 | } |
| 193 | } |
| 194 | |
| 195 | let end = block_succs.len(); |
| 196 | block_succ_range[block] = start..end; |
| 197 | } |
| 198 | |
| 199 | // Step 2: walk the postorder from the domtree in reverse to produce our desired node |
| 200 | // lowering order, identifying critical edges to split along the way. |
| 201 | |
| 202 | let mut lowered_order = Vec::new(); |
| 203 | let mut blockindex_by_block = SecondaryMap::with_default(BlockIndex::invalid()); |
| 204 | for &block in domtree.cfg_rpo() { |
| 205 | let idx = BlockIndex::new(lowered_order.len()); |
nothing calls this directly
no test coverage detected