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:
Trevor Elliott
2023-01-24 14:37:16 -08:00
committed by GitHub
parent ec6922ff24
commit b58a197d33
35 changed files with 943 additions and 159 deletions

View File

@@ -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.