cranelift: Add a conditional branch instruction with two targets (#5446)
Add a conditional branch instruction with two targets: brif. This instruction will eventually replace brz and brnz, as it encompasses the behavior of both. This PR also changes the InstructionData layout for instruction formats that hold BlockCall values, taking the same approach we use for Value arguments. This allows branch_destination to return a slice to the BlockCall values held in the instruction, rather than requiring that we pattern match on InstructionData to fetch the then/else blocks. Function generation for fuzzing has been updated to generate uses of brif, and I've run the cranelift-fuzzgen target locally for hours without triggering any new failures.
This commit is contained in:
@@ -106,46 +106,48 @@ impl<'short, 'long> InstBuilderBase<'short> for FuncInstBuilder<'short, 'long> {
|
||||
self.builder.func.set_srcloc(inst, self.builder.srcloc);
|
||||
}
|
||||
|
||||
if data.opcode().is_branch() {
|
||||
match data.branch_destination() {
|
||||
Some(dest_block) => {
|
||||
// If the user has supplied jump arguments we must adapt the arguments of
|
||||
// the destination block
|
||||
self.builder.declare_successor(
|
||||
dest_block.block(&self.builder.func.dfg.value_lists),
|
||||
inst,
|
||||
);
|
||||
match self.builder.func.dfg.analyze_branch(inst) {
|
||||
ir::instructions::BranchInfo::NotABranch => (),
|
||||
|
||||
ir::instructions::BranchInfo::SingleDest(dest) => {
|
||||
// If the user has supplied jump arguments we must adapt the arguments of
|
||||
// the destination block
|
||||
let block = dest.block(&self.builder.func.dfg.value_lists);
|
||||
self.builder.declare_successor(block, inst);
|
||||
}
|
||||
|
||||
ir::instructions::BranchInfo::Conditional(branch_then, branch_else) => {
|
||||
let block_then = branch_then.block(&self.builder.func.dfg.value_lists);
|
||||
let block_else = branch_else.block(&self.builder.func.dfg.value_lists);
|
||||
|
||||
self.builder.declare_successor(block_then, inst);
|
||||
if block_then != block_else {
|
||||
self.builder.declare_successor(block_else, inst);
|
||||
}
|
||||
None => {
|
||||
// branch_destination() doesn't detect jump_tables
|
||||
// If jump table we declare all entries successor
|
||||
if let InstructionData::BranchTable {
|
||||
table, destination, ..
|
||||
} = data
|
||||
{
|
||||
// Unlike all other jumps/branches, jump tables are
|
||||
// capable of having the same successor appear
|
||||
// multiple times, so we must deduplicate.
|
||||
let mut unique = EntitySet::<Block>::new();
|
||||
for dest_block in self
|
||||
.builder
|
||||
.func
|
||||
.jump_tables
|
||||
.get(table)
|
||||
.expect("you are referencing an undeclared jump table")
|
||||
.iter()
|
||||
.filter(|&dest_block| unique.insert(*dest_block))
|
||||
{
|
||||
// Call `declare_block_predecessor` instead of `declare_successor` for
|
||||
// avoiding the borrow checker.
|
||||
self.builder
|
||||
.func_ctx
|
||||
.ssa
|
||||
.declare_block_predecessor(*dest_block, inst);
|
||||
}
|
||||
self.builder.declare_successor(destination, inst);
|
||||
}
|
||||
}
|
||||
|
||||
ir::instructions::BranchInfo::Table(table, destination) => {
|
||||
// Unlike all other jumps/branches, jump tables are
|
||||
// capable of having the same successor appear
|
||||
// multiple times, so we must deduplicate.
|
||||
let mut unique = EntitySet::<Block>::new();
|
||||
for dest_block in self
|
||||
.builder
|
||||
.func
|
||||
.jump_tables
|
||||
.get(table)
|
||||
.expect("you are referencing an undeclared jump table")
|
||||
.iter()
|
||||
.filter(|&dest_block| unique.insert(*dest_block))
|
||||
{
|
||||
// Call `declare_block_predecessor` instead of `declare_successor` for
|
||||
// avoiding the borrow checker.
|
||||
self.builder
|
||||
.func_ctx
|
||||
.ssa
|
||||
.declare_block_predecessor(*dest_block, inst);
|
||||
}
|
||||
self.builder.declare_successor(destination, inst);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -680,14 +682,13 @@ impl<'a> FunctionBuilder<'a> {
|
||||
/// other jump instructions.
|
||||
pub fn change_jump_destination(&mut self, inst: Inst, new_dest: Block) {
|
||||
let dfg = &mut self.func.dfg;
|
||||
let old_dest = dfg.insts[inst]
|
||||
.branch_destination_mut()
|
||||
.expect("you want to change the jump destination of a non-jump instruction");
|
||||
self.func_ctx
|
||||
.ssa
|
||||
.remove_block_predecessor(old_dest.block(&dfg.value_lists), inst);
|
||||
old_dest.set_block(new_dest, &mut dfg.value_lists);
|
||||
self.func_ctx.ssa.declare_block_predecessor(new_dest, inst);
|
||||
for old_dest in dfg.insts[inst].branch_destination_mut() {
|
||||
self.func_ctx
|
||||
.ssa
|
||||
.remove_block_predecessor(old_dest.block(&dfg.value_lists), inst);
|
||||
old_dest.set_block(new_dest, &mut dfg.value_lists);
|
||||
self.func_ctx.ssa.declare_block_predecessor(new_dest, inst);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if and only if the current `Block` is sealed and has no predecessors declared.
|
||||
|
||||
Reference in New Issue
Block a user