Migrate cranelift-wasm to brif (#5638)
Incrementally working towards removing brz and brnz completely.
This commit is contained in:
@@ -680,14 +680,14 @@ impl<'a> FunctionBuilder<'a> {
|
|||||||
///
|
///
|
||||||
/// **Note:** You are responsible for maintaining the coherence with the arguments of
|
/// **Note:** You are responsible for maintaining the coherence with the arguments of
|
||||||
/// other jump instructions.
|
/// other jump instructions.
|
||||||
pub fn change_jump_destination(&mut self, inst: Inst, new_dest: Block) {
|
pub fn change_jump_destination(&mut self, inst: Inst, old_block: Block, new_block: Block) {
|
||||||
let dfg = &mut self.func.dfg;
|
let dfg = &mut self.func.dfg;
|
||||||
for old_dest in dfg.insts[inst].branch_destination_mut() {
|
for block in dfg.insts[inst].branch_destination_mut() {
|
||||||
self.func_ctx
|
if block.block(&dfg.value_lists) == old_block {
|
||||||
.ssa
|
self.func_ctx.ssa.remove_block_predecessor(old_block, inst);
|
||||||
.remove_block_predecessor(old_dest.block(&dfg.value_lists), inst);
|
block.set_block(new_block, &mut dfg.value_lists);
|
||||||
old_dest.set_block(new_dest, &mut dfg.value_lists);
|
self.func_ctx.ssa.declare_block_predecessor(new_block, inst);
|
||||||
self.func_ctx.ssa.declare_block_predecessor(new_dest, inst);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -292,6 +292,7 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
|||||||
Operator::If { blockty } => {
|
Operator::If { blockty } => {
|
||||||
let val = state.pop1();
|
let val = state.pop1();
|
||||||
|
|
||||||
|
let next_block = builder.create_block();
|
||||||
let (params, results) = blocktype_params_results(validator, *blockty)?;
|
let (params, results) = blocktype_params_results(validator, *blockty)?;
|
||||||
let (destination, else_data) = if params.clone().eq(results.clone()) {
|
let (destination, else_data) = if params.clone().eq(results.clone()) {
|
||||||
// It is possible there is no `else` block, so we will only
|
// It is possible there is no `else` block, so we will only
|
||||||
@@ -301,21 +302,38 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
|||||||
// up discovering an `else`, then we will allocate a block for it
|
// up discovering an `else`, then we will allocate a block for it
|
||||||
// and go back and patch the jump.
|
// and go back and patch the jump.
|
||||||
let destination = block_with_params(builder, results.clone(), environ)?;
|
let destination = block_with_params(builder, results.clone(), environ)?;
|
||||||
let branch_inst =
|
let branch_inst = canonicalise_brif(
|
||||||
canonicalise_then_brz(builder, val, destination, state.peekn(params.len()));
|
builder,
|
||||||
(destination, ElseData::NoElse { branch_inst })
|
val,
|
||||||
|
next_block,
|
||||||
|
&[],
|
||||||
|
destination,
|
||||||
|
state.peekn(params.len()),
|
||||||
|
);
|
||||||
|
(
|
||||||
|
destination,
|
||||||
|
ElseData::NoElse {
|
||||||
|
branch_inst,
|
||||||
|
placeholder: destination,
|
||||||
|
},
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
// The `if` type signature is not valid without an `else` block,
|
// The `if` type signature is not valid without an `else` block,
|
||||||
// so we eagerly allocate the `else` block here.
|
// so we eagerly allocate the `else` block here.
|
||||||
let destination = block_with_params(builder, results.clone(), environ)?;
|
let destination = block_with_params(builder, results.clone(), environ)?;
|
||||||
let else_block = block_with_params(builder, params.clone(), environ)?;
|
let else_block = block_with_params(builder, params.clone(), environ)?;
|
||||||
canonicalise_then_brz(builder, val, else_block, state.peekn(params.len()));
|
canonicalise_brif(
|
||||||
|
builder,
|
||||||
|
val,
|
||||||
|
next_block,
|
||||||
|
&[],
|
||||||
|
else_block,
|
||||||
|
state.peekn(params.len()),
|
||||||
|
);
|
||||||
builder.seal_block(else_block);
|
builder.seal_block(else_block);
|
||||||
(destination, ElseData::WithElse { else_block })
|
(destination, ElseData::WithElse { else_block })
|
||||||
};
|
};
|
||||||
|
|
||||||
let next_block = builder.create_block();
|
|
||||||
canonicalise_then_jump(builder, next_block, &[]);
|
|
||||||
builder.seal_block(next_block); // Only predecessor is the current block.
|
builder.seal_block(next_block); // Only predecessor is the current block.
|
||||||
builder.switch_to_block(next_block);
|
builder.switch_to_block(next_block);
|
||||||
|
|
||||||
@@ -357,7 +375,10 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
|||||||
// Ensure we have a block for the `else` block (it may have
|
// Ensure we have a block for the `else` block (it may have
|
||||||
// already been pre-allocated, see `ElseData` for details).
|
// already been pre-allocated, see `ElseData` for details).
|
||||||
let else_block = match *else_data {
|
let else_block = match *else_data {
|
||||||
ElseData::NoElse { branch_inst } => {
|
ElseData::NoElse {
|
||||||
|
branch_inst,
|
||||||
|
placeholder,
|
||||||
|
} => {
|
||||||
let (params, _results) =
|
let (params, _results) =
|
||||||
blocktype_params_results(validator, blocktype)?;
|
blocktype_params_results(validator, blocktype)?;
|
||||||
debug_assert_eq!(params.len(), num_return_values);
|
debug_assert_eq!(params.len(), num_return_values);
|
||||||
@@ -370,7 +391,11 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
|||||||
);
|
);
|
||||||
state.popn(params.len());
|
state.popn(params.len());
|
||||||
|
|
||||||
builder.change_jump_destination(branch_inst, else_block);
|
builder.change_jump_destination(
|
||||||
|
branch_inst,
|
||||||
|
placeholder,
|
||||||
|
else_block,
|
||||||
|
);
|
||||||
builder.seal_block(else_block);
|
builder.seal_block(else_block);
|
||||||
else_block
|
else_block
|
||||||
}
|
}
|
||||||
@@ -2171,6 +2196,7 @@ fn translate_unreachable_operator<FE: FuncEnvironment + ?Sized>(
|
|||||||
ir::Block::reserved_value(),
|
ir::Block::reserved_value(),
|
||||||
ElseData::NoElse {
|
ElseData::NoElse {
|
||||||
branch_inst: ir::Inst::reserved_value(),
|
branch_inst: ir::Inst::reserved_value(),
|
||||||
|
placeholder: ir::Block::reserved_value(),
|
||||||
},
|
},
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
@@ -2198,7 +2224,10 @@ fn translate_unreachable_operator<FE: FuncEnvironment + ?Sized>(
|
|||||||
state.reachable = true;
|
state.reachable = true;
|
||||||
|
|
||||||
let else_block = match *else_data {
|
let else_block = match *else_data {
|
||||||
ElseData::NoElse { branch_inst } => {
|
ElseData::NoElse {
|
||||||
|
branch_inst,
|
||||||
|
placeholder,
|
||||||
|
} => {
|
||||||
let (params, _results) =
|
let (params, _results) =
|
||||||
blocktype_params_results(validator, blocktype)?;
|
blocktype_params_results(validator, blocktype)?;
|
||||||
let else_block = block_with_params(builder, params, environ)?;
|
let else_block = block_with_params(builder, params, environ)?;
|
||||||
@@ -2206,7 +2235,11 @@ fn translate_unreachable_operator<FE: FuncEnvironment + ?Sized>(
|
|||||||
frame.truncate_value_stack_to_else_params(&mut state.stack);
|
frame.truncate_value_stack_to_else_params(&mut state.stack);
|
||||||
|
|
||||||
// We change the target of the branch instruction.
|
// We change the target of the branch instruction.
|
||||||
builder.change_jump_destination(branch_inst, else_block);
|
builder.change_jump_destination(
|
||||||
|
branch_inst,
|
||||||
|
placeholder,
|
||||||
|
else_block,
|
||||||
|
);
|
||||||
builder.seal_block(else_block);
|
builder.seal_block(else_block);
|
||||||
else_block
|
else_block
|
||||||
}
|
}
|
||||||
@@ -2816,10 +2849,9 @@ fn translate_br_if(
|
|||||||
) {
|
) {
|
||||||
let val = state.pop1();
|
let val = state.pop1();
|
||||||
let (br_destination, inputs) = translate_br_if_args(relative_depth, state);
|
let (br_destination, inputs) = translate_br_if_args(relative_depth, state);
|
||||||
canonicalise_then_brnz(builder, val, br_destination, inputs);
|
|
||||||
|
|
||||||
let next_block = builder.create_block();
|
let next_block = builder.create_block();
|
||||||
canonicalise_then_jump(builder, next_block, &[]);
|
canonicalise_brif(builder, val, br_destination, inputs, next_block, &[]);
|
||||||
|
|
||||||
builder.seal_block(next_block); // The only predecessor is the current block.
|
builder.seal_block(next_block); // The only predecessor is the current block.
|
||||||
builder.switch_to_block(next_block);
|
builder.switch_to_block(next_block);
|
||||||
}
|
}
|
||||||
@@ -3120,28 +3152,28 @@ fn canonicalise_then_jump(
|
|||||||
builder.ins().jump(destination, canonicalised)
|
builder.ins().jump(destination, canonicalised)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The same but for a `brz` instruction.
|
/// The same but for a `brif` instruction.
|
||||||
fn canonicalise_then_brz(
|
fn canonicalise_brif(
|
||||||
builder: &mut FunctionBuilder,
|
builder: &mut FunctionBuilder,
|
||||||
cond: ir::Value,
|
cond: ir::Value,
|
||||||
destination: ir::Block,
|
block_then: ir::Block,
|
||||||
params: &[Value],
|
params_then: &[ir::Value],
|
||||||
|
block_else: ir::Block,
|
||||||
|
params_else: &[ir::Value],
|
||||||
) -> ir::Inst {
|
) -> ir::Inst {
|
||||||
let mut tmp_canonicalised = SmallVec::<[ir::Value; 16]>::new();
|
let mut tmp_canonicalised_then = SmallVec::<[ir::Value; 16]>::new();
|
||||||
let canonicalised = canonicalise_v128_values(&mut tmp_canonicalised, builder, params);
|
let canonicalised_then =
|
||||||
builder.ins().brz(cond, destination, canonicalised)
|
canonicalise_v128_values(&mut tmp_canonicalised_then, builder, params_then);
|
||||||
}
|
let mut tmp_canonicalised_else = SmallVec::<[ir::Value; 16]>::new();
|
||||||
|
let canonicalised_else =
|
||||||
/// The same but for a `brnz` instruction.
|
canonicalise_v128_values(&mut tmp_canonicalised_else, builder, params_else);
|
||||||
fn canonicalise_then_brnz(
|
builder.ins().brif(
|
||||||
builder: &mut FunctionBuilder,
|
cond,
|
||||||
cond: ir::Value,
|
block_then,
|
||||||
destination: ir::Block,
|
canonicalised_then,
|
||||||
params: &[Value],
|
block_else,
|
||||||
) -> ir::Inst {
|
canonicalised_else,
|
||||||
let mut tmp_canonicalised = SmallVec::<[ir::Value; 16]>::new();
|
)
|
||||||
let canonicalised = canonicalise_v128_values(&mut tmp_canonicalised, builder, params);
|
|
||||||
builder.ins().brnz(cond, destination, canonicalised)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A helper for popping and bitcasting a single value; since SIMD values can lose their type by
|
/// A helper for popping and bitcasting a single value; since SIMD values can lose their type by
|
||||||
|
|||||||
@@ -22,6 +22,9 @@ pub enum ElseData {
|
|||||||
/// instruction that needs to be fixed up to point to the new `else`
|
/// instruction that needs to be fixed up to point to the new `else`
|
||||||
/// block rather than the destination block after the `if...end`.
|
/// block rather than the destination block after the `if...end`.
|
||||||
branch_inst: Inst,
|
branch_inst: Inst,
|
||||||
|
|
||||||
|
/// The placeholder block we're replacing.
|
||||||
|
placeholder: Block,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// We have already allocated an `else` block.
|
/// We have already allocated an `else` block.
|
||||||
@@ -43,9 +46,8 @@ pub enum ElseData {
|
|||||||
/// - `num_return_values`: number of values returned by the control block;
|
/// - `num_return_values`: number of values returned by the control block;
|
||||||
/// - `original_stack_size`: size of the value stack at the beginning of the control block.
|
/// - `original_stack_size`: size of the value stack at the beginning of the control block.
|
||||||
///
|
///
|
||||||
/// Moreover, the `if` frame has the `branch_inst` field that points to the `brz` instruction
|
/// The `loop` frame has a `header` field that references the `Block` that contains the beginning
|
||||||
/// separating the `true` and `false` branch. The `loop` frame has a `header` field that references
|
/// of the body of the loop.
|
||||||
/// the `Block` that contains the beginning of the body of the loop.
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ControlStackFrame {
|
pub enum ControlStackFrame {
|
||||||
If {
|
If {
|
||||||
|
|||||||
Reference in New Issue
Block a user