Rework br_table to use BlockCall (#5731)

Rework br_table to use BlockCall, allowing us to avoid adding new nodes during ssa construction to hold block arguments. Additionally, many places where we previously matched on InstructionData to extract branch destinations can be replaced with a use of branch_destination or branch_destination_mut.
This commit is contained in:
Trevor Elliott
2023-02-16 09:23:27 -08:00
committed by GitHub
parent c3c16eb207
commit 80c147d9c0
26 changed files with 475 additions and 269 deletions

View File

@@ -18,15 +18,6 @@ use std::collections::HashMap;
use std::ops::RangeInclusive;
use target_lexicon::{Architecture, Triple};
/// Generates a Vec with `len` elements comprised of `options`
fn arbitrary_vec<T: Clone>(
u: &mut Unstructured,
len: usize,
options: &[T],
) -> arbitrary::Result<Vec<T>> {
(0..len).map(|_| u.choose(options).cloned()).collect()
}
type BlockSignature = Vec<Type>;
fn insert_opcode(
@@ -1610,7 +1601,15 @@ where
}
BlockTerminator::BrTable(default, targets) => {
// Create jump tables on demand
let jt = builder.create_jump_table(JumpTableData::new(default, &targets));
let mut jt = Vec::with_capacity(targets.len());
for block in targets {
let args = self.generate_values_for_block(builder, block)?;
jt.push(builder.func.dfg.block_call(block, &args))
}
let args = self.generate_values_for_block(builder, default)?;
let jt_data = JumpTableData::new(builder.func.dfg.block_call(default, &args), &jt);
let jt = builder.create_jump_table(jt_data);
// br_table only supports I32
let val = builder.use_var(self.get_variable_of_type(I32)?);
@@ -1799,21 +1798,21 @@ where
// If we have more than one block we can allow terminators that target blocks.
// TODO: We could add some kind of BrReturn here, to explore edges where we
// exit in the middle of the function
valid_terminators
.extend_from_slice(&[BlockTerminatorKind::Jump, BlockTerminatorKind::Br]);
}
// BrTable and the Switch interface only allow targeting blocks without params
// we also need to ensure that the next block has no params, since that one is
// guaranteed to be picked in either case.
if has_paramless_targets && next_block_is_paramless {
valid_terminators.extend_from_slice(&[
BlockTerminatorKind::Jump,
BlockTerminatorKind::Br,
BlockTerminatorKind::BrTable,
BlockTerminatorKind::Switch,
]);
}
let terminator = self.u.choose(&valid_terminators[..])?;
// As the Switch interface only allows targeting blocks without params we need
// to ensure that the next block has no params, since that one is guaranteed to be
// picked in either case.
if has_paramless_targets && next_block_is_paramless {
valid_terminators.push(BlockTerminatorKind::Switch);
}
let terminator = self.u.choose(&valid_terminators)?;
// Choose block targets for the terminators that we picked above
Ok(match terminator {
@@ -1829,10 +1828,8 @@ where
let default = next_block;
let target_count = self.param(&self.config.jump_table_entries)?;
let targets = arbitrary_vec(
self.u,
target_count,
self.resources.forward_blocks_without_params(block),
let targets = Result::from_iter(
(0..target_count).map(|_| self.generate_target_block(block)),
)?;
BlockTerminator::BrTable(default, targets)