fuzzgen: Mostly Forward Branching (#4894)
* cranelift: Test Forward branching * fuzzgen: Separate terminators * fuzzgen: Avoid generating jumptables if we have no valid targets * fuzzgen: Forward Jump Tables * fuzzgen: Cleanup some feedback Thanks @jameysharp! * fuzzgen: Cleanup block generation Thanks @jameysharp! * fuzzgen: Style Cleanups These were accidentally reverted in a rebase * fuzzgen: Prevent block0 from being targeted for branches * fuzzgen: Add jump tables sorting TODO
This commit is contained in:
@@ -35,6 +35,10 @@ pub struct Config {
|
|||||||
pub static_stack_slots_per_function: RangeInclusive<usize>,
|
pub static_stack_slots_per_function: RangeInclusive<usize>,
|
||||||
/// Size in bytes
|
/// Size in bytes
|
||||||
pub static_stack_slot_size: RangeInclusive<usize>,
|
pub static_stack_slot_size: RangeInclusive<usize>,
|
||||||
|
|
||||||
|
/// Determines how often we generate a backwards branch
|
||||||
|
/// Backwards branches are prone to infinite loops, and thus cause timeouts.
|
||||||
|
pub backwards_branch_ratio: (usize, usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Config {
|
impl Default for Config {
|
||||||
@@ -55,6 +59,9 @@ impl Default for Config {
|
|||||||
funcrefs_per_function: 0..=8,
|
funcrefs_per_function: 0..=8,
|
||||||
static_stack_slots_per_function: 0..=8,
|
static_stack_slots_per_function: 0..=8,
|
||||||
static_stack_slot_size: 0..=128,
|
static_stack_slot_size: 0..=128,
|
||||||
|
// 0.1% allows us to explore this, while not causing enough timeouts to significantly
|
||||||
|
// impact execs/s
|
||||||
|
backwards_branch_ratio: (1, 1000),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -564,6 +564,157 @@ const OPCODE_SIGNATURES: &'static [(
|
|||||||
(Opcode::Call, &[], &[], insert_call),
|
(Opcode::Call, &[], &[], insert_call),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
type BlockTerminator = fn(
|
||||||
|
fgen: &mut FunctionGenerator,
|
||||||
|
builder: &mut FunctionBuilder,
|
||||||
|
source_block: Block,
|
||||||
|
) -> Result<()>;
|
||||||
|
|
||||||
|
fn insert_return(
|
||||||
|
fgen: &mut FunctionGenerator,
|
||||||
|
builder: &mut FunctionBuilder,
|
||||||
|
_source_block: Block,
|
||||||
|
) -> Result<()> {
|
||||||
|
let types: Vec<Type> = {
|
||||||
|
let rets = &builder.func.signature.returns;
|
||||||
|
rets.iter().map(|p| p.value_type).collect()
|
||||||
|
};
|
||||||
|
let vals = fgen.generate_values_for_signature(builder, types.into_iter())?;
|
||||||
|
|
||||||
|
builder.ins().return_(&vals[..]);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn insert_jump(
|
||||||
|
fgen: &mut FunctionGenerator,
|
||||||
|
builder: &mut FunctionBuilder,
|
||||||
|
source_block: Block,
|
||||||
|
) -> Result<()> {
|
||||||
|
let (block, args) = fgen.generate_target_block(builder, source_block)?;
|
||||||
|
builder.ins().jump(block, &args[..]);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generates a br_table into a random block
|
||||||
|
fn insert_br_table(
|
||||||
|
fgen: &mut FunctionGenerator,
|
||||||
|
builder: &mut FunctionBuilder,
|
||||||
|
source_block: Block,
|
||||||
|
) -> Result<()> {
|
||||||
|
let var = fgen.get_variable_of_type(I32)?; // br_table only supports I32
|
||||||
|
let val = builder.use_var(var);
|
||||||
|
|
||||||
|
let target_blocks = fgen.resources.forward_blocks_without_params(source_block);
|
||||||
|
let default_block = *fgen.u.choose(target_blocks)?;
|
||||||
|
|
||||||
|
// We can still select a backwards branching jump table here!
|
||||||
|
let tables = fgen.resources.forward_jump_tables(builder, source_block);
|
||||||
|
let jt = *fgen.u.choose(&tables[..])?;
|
||||||
|
builder.ins().br_table(val, default_block, jt);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generates a brz/brnz into a random block
|
||||||
|
fn insert_br(
|
||||||
|
fgen: &mut FunctionGenerator,
|
||||||
|
builder: &mut FunctionBuilder,
|
||||||
|
source_block: Block,
|
||||||
|
) -> Result<()> {
|
||||||
|
let (block, args) = fgen.generate_target_block(builder, source_block)?;
|
||||||
|
|
||||||
|
let condbr_types = [I8, I16, I32, I64, I128, B1];
|
||||||
|
let _type = *fgen.u.choose(&condbr_types[..])?;
|
||||||
|
let var = fgen.get_variable_of_type(_type)?;
|
||||||
|
let val = builder.use_var(var);
|
||||||
|
|
||||||
|
if bool::arbitrary(fgen.u)? {
|
||||||
|
builder.ins().brz(val, block, &args[..]);
|
||||||
|
} else {
|
||||||
|
builder.ins().brnz(val, block, &args[..]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// After brz/brnz we must generate a jump
|
||||||
|
insert_jump(fgen, builder, source_block)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn insert_bricmp(
|
||||||
|
fgen: &mut FunctionGenerator,
|
||||||
|
builder: &mut FunctionBuilder,
|
||||||
|
source_block: Block,
|
||||||
|
) -> Result<()> {
|
||||||
|
let (block, args) = fgen.generate_target_block(builder, source_block)?;
|
||||||
|
|
||||||
|
let cc = *fgen.u.choose(IntCC::all())?;
|
||||||
|
let _type = *fgen.u.choose(&[I8, I16, I32, I64, I128])?;
|
||||||
|
|
||||||
|
let lhs_var = fgen.get_variable_of_type(_type)?;
|
||||||
|
let lhs_val = builder.use_var(lhs_var);
|
||||||
|
|
||||||
|
let rhs_var = fgen.get_variable_of_type(_type)?;
|
||||||
|
let rhs_val = builder.use_var(rhs_var);
|
||||||
|
|
||||||
|
builder
|
||||||
|
.ins()
|
||||||
|
.br_icmp(cc, lhs_val, rhs_val, block, &args[..]);
|
||||||
|
|
||||||
|
// After bricmp's we must generate a jump
|
||||||
|
insert_jump(fgen, builder, source_block)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn insert_switch(
|
||||||
|
fgen: &mut FunctionGenerator,
|
||||||
|
builder: &mut FunctionBuilder,
|
||||||
|
source_block: Block,
|
||||||
|
) -> Result<()> {
|
||||||
|
let _type = *fgen.u.choose(&[I8, I16, I32, I64, I128][..])?;
|
||||||
|
let switch_var = fgen.get_variable_of_type(_type)?;
|
||||||
|
let switch_val = builder.use_var(switch_var);
|
||||||
|
|
||||||
|
// TODO: We should also generate backwards branches in switches
|
||||||
|
let default_block = {
|
||||||
|
let target_blocks = fgen.resources.forward_blocks_without_params(source_block);
|
||||||
|
*fgen.u.choose(target_blocks)?
|
||||||
|
};
|
||||||
|
|
||||||
|
// Build this into a HashMap since we cannot have duplicate entries.
|
||||||
|
let mut entries = HashMap::new();
|
||||||
|
for _ in 0..fgen.param(&fgen.config.switch_cases)? {
|
||||||
|
// The Switch API only allows for entries that are addressable by the index type
|
||||||
|
// so we need to limit the range of values that we generate.
|
||||||
|
let (ty_min, ty_max) = _type.bounds(false);
|
||||||
|
let range_start = fgen.u.int_in_range(ty_min..=ty_max)?;
|
||||||
|
|
||||||
|
// We can either insert a contiguous range of blocks or a individual block
|
||||||
|
// This is done because the Switch API specializes contiguous ranges.
|
||||||
|
let range_size = if bool::arbitrary(fgen.u)? {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
fgen.param(&fgen.config.switch_max_range_size)?
|
||||||
|
} as u128;
|
||||||
|
|
||||||
|
// Build the switch entries
|
||||||
|
for i in 0..range_size {
|
||||||
|
let index = range_start.wrapping_add(i) % ty_max;
|
||||||
|
let block = {
|
||||||
|
let target_blocks = fgen.resources.forward_blocks_without_params(source_block);
|
||||||
|
*fgen.u.choose(target_blocks)?
|
||||||
|
};
|
||||||
|
|
||||||
|
entries.insert(index, block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut switch = Switch::new();
|
||||||
|
for (entry, block) in entries.into_iter() {
|
||||||
|
switch.set_entry(entry, block);
|
||||||
|
}
|
||||||
|
switch.emit(builder, switch_val, default_block);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// These libcalls need a interpreter implementation in `cranelift-fuzzgen.rs`
|
/// These libcalls need a interpreter implementation in `cranelift-fuzzgen.rs`
|
||||||
const ALLOWED_LIBCALLS: &'static [LibCall] = &[
|
const ALLOWED_LIBCALLS: &'static [LibCall] = &[
|
||||||
LibCall::CeilF32,
|
LibCall::CeilF32,
|
||||||
@@ -593,6 +744,44 @@ struct Resources {
|
|||||||
stack_slots: Vec<(StackSlot, StackSize)>,
|
stack_slots: Vec<(StackSlot, StackSize)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Resources {
|
||||||
|
/// Returns [JumpTable]'s where all blocks are forward of `block`
|
||||||
|
fn forward_jump_tables(&self, builder: &FunctionBuilder, block: Block) -> Vec<JumpTable> {
|
||||||
|
// TODO: We can avoid allocating a Vec here by sorting self.jump_tables based
|
||||||
|
// on the minimum block and returning a slice based on that.
|
||||||
|
// See https://github.com/bytecodealliance/wasmtime/pull/4894#discussion_r971241430 for more details
|
||||||
|
|
||||||
|
// Unlike with the blocks below jump table targets are not ordered, thus we do need
|
||||||
|
// to allocate a Vec here.
|
||||||
|
let jump_tables = &builder.func.jump_tables;
|
||||||
|
self.jump_tables
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.filter(|jt| jump_tables[*jt].iter().all(|target| *target > block))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Partitions blocks at `block`. Only blocks that can be targeted by branches are considered.
|
||||||
|
///
|
||||||
|
/// The first slice includes all blocks up to and including `block`.
|
||||||
|
/// The second slice includes all remaining blocks.
|
||||||
|
fn partition_target_blocks(
|
||||||
|
&self,
|
||||||
|
block: Block,
|
||||||
|
) -> (&[(Block, BlockSignature)], &[(Block, BlockSignature)]) {
|
||||||
|
// Blocks are stored in-order and have no gaps, this means that we can simply index them by
|
||||||
|
// their number. We also need to exclude the entry block since it isn't a valid target.
|
||||||
|
let target_blocks = &self.blocks[1..];
|
||||||
|
target_blocks.split_at(block.as_u32() as usize)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generates a slice of `blocks_without_params` ahead of `block`
|
||||||
|
fn forward_blocks_without_params(&self, block: Block) -> &[Block] {
|
||||||
|
let partition_point = self.blocks_without_params.partition_point(|b| *b <= block);
|
||||||
|
&self.blocks_without_params[partition_point..]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'r, 'data> FunctionGenerator<'r, 'data>
|
impl<'r, 'data> FunctionGenerator<'r, 'data>
|
||||||
where
|
where
|
||||||
'data: 'r,
|
'data: 'r,
|
||||||
@@ -758,8 +947,20 @@ where
|
|||||||
fn generate_target_block(
|
fn generate_target_block(
|
||||||
&mut self,
|
&mut self,
|
||||||
builder: &mut FunctionBuilder,
|
builder: &mut FunctionBuilder,
|
||||||
|
source_block: Block,
|
||||||
) -> Result<(Block, Vec<Value>)> {
|
) -> Result<(Block, Vec<Value>)> {
|
||||||
let block_targets = &self.resources.blocks[1..];
|
// We try to mostly generate forward branches to avoid generating an excessive amount of
|
||||||
|
// infinite loops. But they are still important, so give them a small chance of existing.
|
||||||
|
let (backwards_blocks, forward_blocks) =
|
||||||
|
self.resources.partition_target_blocks(source_block);
|
||||||
|
let ratio = self.config.backwards_branch_ratio;
|
||||||
|
let block_targets = if !backwards_blocks.is_empty() && self.u.ratio(ratio.0, ratio.1)? {
|
||||||
|
backwards_blocks
|
||||||
|
} else {
|
||||||
|
forward_blocks
|
||||||
|
};
|
||||||
|
assert!(!block_targets.is_empty());
|
||||||
|
|
||||||
let (block, signature) = self.u.choose(block_targets)?.clone();
|
let (block, signature) = self.u.choose(block_targets)?.clone();
|
||||||
let args = self.generate_values_for_signature(builder, signature.into_iter())?;
|
let args = self.generate_values_for_signature(builder, signature.into_iter())?;
|
||||||
Ok((block, args))
|
Ok((block, args))
|
||||||
@@ -779,131 +980,50 @@ where
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_return(&mut self, builder: &mut FunctionBuilder) -> Result<()> {
|
|
||||||
let types: Vec<Type> = {
|
|
||||||
let rets = &builder.func.signature.returns;
|
|
||||||
rets.iter().map(|p| p.value_type).collect()
|
|
||||||
};
|
|
||||||
let vals = self.generate_values_for_signature(builder, types.into_iter())?;
|
|
||||||
|
|
||||||
builder.ins().return_(&vals[..]);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn generate_jump(&mut self, builder: &mut FunctionBuilder) -> Result<()> {
|
|
||||||
let (block, args) = self.generate_target_block(builder)?;
|
|
||||||
builder.ins().jump(block, &args[..]);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generates a br_table into a random block
|
|
||||||
fn generate_br_table(&mut self, builder: &mut FunctionBuilder) -> Result<()> {
|
|
||||||
let var = self.get_variable_of_type(I32)?; // br_table only supports I32
|
|
||||||
let val = builder.use_var(var);
|
|
||||||
|
|
||||||
let default_block = *self.u.choose(&self.resources.blocks_without_params)?;
|
|
||||||
|
|
||||||
let jt = *self.u.choose(&self.resources.jump_tables)?;
|
|
||||||
builder.ins().br_table(val, default_block, jt);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generates a brz/brnz into a random block
|
|
||||||
fn generate_br(&mut self, builder: &mut FunctionBuilder) -> Result<()> {
|
|
||||||
let (block, args) = self.generate_target_block(builder)?;
|
|
||||||
|
|
||||||
let condbr_types = [I8, I16, I32, I64, I128, B1];
|
|
||||||
let _type = *self.u.choose(&condbr_types[..])?;
|
|
||||||
let var = self.get_variable_of_type(_type)?;
|
|
||||||
let val = builder.use_var(var);
|
|
||||||
|
|
||||||
if bool::arbitrary(self.u)? {
|
|
||||||
builder.ins().brz(val, block, &args[..]);
|
|
||||||
} else {
|
|
||||||
builder.ins().brnz(val, block, &args[..]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// After brz/brnz we must generate a jump
|
|
||||||
self.generate_jump(builder)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn generate_bricmp(&mut self, builder: &mut FunctionBuilder) -> Result<()> {
|
|
||||||
let (block, args) = self.generate_target_block(builder)?;
|
|
||||||
|
|
||||||
let cc = *self.u.choose(IntCC::all())?;
|
|
||||||
let _type = *self.u.choose(&[I8, I16, I32, I64, I128])?;
|
|
||||||
|
|
||||||
let lhs_var = self.get_variable_of_type(_type)?;
|
|
||||||
let lhs_val = builder.use_var(lhs_var);
|
|
||||||
|
|
||||||
let rhs_var = self.get_variable_of_type(_type)?;
|
|
||||||
let rhs_val = builder.use_var(rhs_var);
|
|
||||||
|
|
||||||
builder
|
|
||||||
.ins()
|
|
||||||
.br_icmp(cc, lhs_val, rhs_val, block, &args[..]);
|
|
||||||
|
|
||||||
// After bricmp's we must generate a jump
|
|
||||||
self.generate_jump(builder)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn generate_switch(&mut self, builder: &mut FunctionBuilder) -> Result<()> {
|
|
||||||
let _type = *self.u.choose(&[I8, I16, I32, I64, I128][..])?;
|
|
||||||
let switch_var = self.get_variable_of_type(_type)?;
|
|
||||||
let switch_val = builder.use_var(switch_var);
|
|
||||||
|
|
||||||
let default_block = *self.u.choose(&self.resources.blocks_without_params)?;
|
|
||||||
|
|
||||||
// Build this into a HashMap since we cannot have duplicate entries.
|
|
||||||
let mut entries = HashMap::new();
|
|
||||||
for _ in 0..self.param(&self.config.switch_cases)? {
|
|
||||||
// The Switch API only allows for entries that are addressable by the index type
|
|
||||||
// so we need to limit the range of values that we generate.
|
|
||||||
let (ty_min, ty_max) = _type.bounds(false);
|
|
||||||
let range_start = self.u.int_in_range(ty_min..=ty_max)?;
|
|
||||||
|
|
||||||
// We can either insert a contiguous range of blocks or a individual block
|
|
||||||
// This is done because the Switch API specializes contiguous ranges.
|
|
||||||
let range_size = if bool::arbitrary(self.u)? {
|
|
||||||
1
|
|
||||||
} else {
|
|
||||||
self.param(&self.config.switch_max_range_size)?
|
|
||||||
} as u128;
|
|
||||||
|
|
||||||
// Build the switch entries
|
|
||||||
for i in 0..range_size {
|
|
||||||
let index = range_start.wrapping_add(i) % ty_max;
|
|
||||||
let block = *self.u.choose(&self.resources.blocks_without_params)?;
|
|
||||||
entries.insert(index, block);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut switch = Switch::new();
|
|
||||||
for (entry, block) in entries.into_iter() {
|
|
||||||
switch.set_entry(entry, block);
|
|
||||||
}
|
|
||||||
switch.emit(builder, switch_val, default_block);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// We always need to exit safely out of a block.
|
/// We always need to exit safely out of a block.
|
||||||
/// This either means a jump into another block or a return.
|
/// This either means a jump into another block or a return.
|
||||||
fn finalize_block(&mut self, builder: &mut FunctionBuilder) -> Result<()> {
|
fn finalize_block(&mut self, builder: &mut FunctionBuilder, source_block: Block) -> Result<()> {
|
||||||
let gen = self.u.choose(
|
let has_jump_tables = !self
|
||||||
&[
|
.resources
|
||||||
Self::generate_bricmp,
|
.forward_jump_tables(builder, source_block)
|
||||||
Self::generate_br,
|
.is_empty();
|
||||||
Self::generate_br_table,
|
|
||||||
Self::generate_jump,
|
|
||||||
Self::generate_return,
|
|
||||||
Self::generate_switch,
|
|
||||||
][..],
|
|
||||||
)?;
|
|
||||||
|
|
||||||
gen(self, builder)
|
let has_forward_blocks = {
|
||||||
|
let (_, forward_blocks) = self.resources.partition_target_blocks(source_block);
|
||||||
|
!forward_blocks.is_empty()
|
||||||
|
};
|
||||||
|
|
||||||
|
let has_forward_blocks_without_params = !self
|
||||||
|
.resources
|
||||||
|
.forward_blocks_without_params(source_block)
|
||||||
|
.is_empty();
|
||||||
|
|
||||||
|
let terminators: &[(BlockTerminator, bool)] = &[
|
||||||
|
// Return is always a valid option
|
||||||
|
(insert_return, true),
|
||||||
|
// If we have forward blocks, we can allow generating jumps and branches
|
||||||
|
(insert_jump, has_forward_blocks),
|
||||||
|
(insert_br, has_forward_blocks),
|
||||||
|
(insert_bricmp, has_forward_blocks),
|
||||||
|
// Switches can only use blocks without params
|
||||||
|
(insert_switch, has_forward_blocks_without_params),
|
||||||
|
// We need both jump tables and a default block for br_table
|
||||||
|
(
|
||||||
|
insert_br_table,
|
||||||
|
has_jump_tables && has_forward_blocks_without_params,
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
let terminators: Vec<_> = terminators
|
||||||
|
.into_iter()
|
||||||
|
.filter(|(_, valid)| *valid)
|
||||||
|
.map(|(term, _)| term)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let inserter = self.u.choose(&terminators[..])?;
|
||||||
|
inserter(self, builder, source_block)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fills the current block with random instructions
|
/// Fills the current block with random instructions
|
||||||
@@ -917,6 +1037,11 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn generate_jumptables(&mut self, builder: &mut FunctionBuilder) -> Result<()> {
|
fn generate_jumptables(&mut self, builder: &mut FunctionBuilder) -> Result<()> {
|
||||||
|
// We shouldn't try to generate jumptables if we don't have any valid targets!
|
||||||
|
if self.resources.blocks_without_params.is_empty() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
for _ in 0..self.param(&self.config.jump_tables_per_function)? {
|
for _ in 0..self.param(&self.config.jump_tables_per_function)? {
|
||||||
let mut jt_data = JumpTableData::new();
|
let mut jt_data = JumpTableData::new();
|
||||||
|
|
||||||
@@ -1010,18 +1135,15 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a random amount of blocks in this function
|
/// Creates a random amount of blocks in this function
|
||||||
fn generate_blocks(
|
fn generate_blocks(&mut self, builder: &mut FunctionBuilder, sig: &Signature) -> Result<()> {
|
||||||
&mut self,
|
|
||||||
builder: &mut FunctionBuilder,
|
|
||||||
sig: &Signature,
|
|
||||||
) -> Result<Vec<(Block, BlockSignature)>> {
|
|
||||||
let extra_block_count = self.param(&self.config.blocks_per_function)?;
|
let extra_block_count = self.param(&self.config.blocks_per_function)?;
|
||||||
|
|
||||||
// We must always have at least one block, so we generate the "extra" blocks and add 1 for
|
// We must always have at least one block, so we generate the "extra" blocks and add 1 for
|
||||||
// the entry block.
|
// the entry block.
|
||||||
let block_count = 1 + extra_block_count;
|
let block_count = 1 + extra_block_count;
|
||||||
|
|
||||||
(0..block_count)
|
// Blocks need to be sorted in ascending order
|
||||||
|
self.resources.blocks = (0..block_count)
|
||||||
.map(|i| {
|
.map(|i| {
|
||||||
let is_entry = i == 0;
|
let is_entry = i == 0;
|
||||||
let block = builder.create_block();
|
let block = builder.create_block();
|
||||||
@@ -1046,7 +1168,17 @@ where
|
|||||||
Ok((block, sig))
|
Ok((block, sig))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect()
|
.collect::<Result<Vec<_>>>()?;
|
||||||
|
|
||||||
|
// Valid blocks for jump tables have to have no parameters in the signature, and must also
|
||||||
|
// not be the first block.
|
||||||
|
self.resources.blocks_without_params = self.resources.blocks[1..]
|
||||||
|
.iter()
|
||||||
|
.filter(|(_, sig)| sig.len() == 0)
|
||||||
|
.map(|(b, _)| *b)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_block_signature(&mut self) -> Result<BlockSignature> {
|
fn generate_block_signature(&mut self) -> Result<BlockSignature> {
|
||||||
@@ -1110,15 +1242,7 @@ where
|
|||||||
|
|
||||||
let mut builder = FunctionBuilder::new(&mut func, &mut fn_builder_ctx);
|
let mut builder = FunctionBuilder::new(&mut func, &mut fn_builder_ctx);
|
||||||
|
|
||||||
self.resources.blocks = self.generate_blocks(&mut builder, &sig)?;
|
self.generate_blocks(&mut builder, &sig)?;
|
||||||
|
|
||||||
// Valid blocks for jump tables have to have no parameters in the signature, and must also
|
|
||||||
// not be the first block.
|
|
||||||
self.resources.blocks_without_params = self.resources.blocks[1..]
|
|
||||||
.iter()
|
|
||||||
.filter(|(_, sig)| sig.len() == 0)
|
|
||||||
.map(|(b, _)| *b)
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
// Function preamble
|
// Function preamble
|
||||||
self.generate_jumptables(&mut builder)?;
|
self.generate_jumptables(&mut builder)?;
|
||||||
@@ -1126,9 +1250,9 @@ where
|
|||||||
self.generate_stack_slots(&mut builder)?;
|
self.generate_stack_slots(&mut builder)?;
|
||||||
|
|
||||||
// Main instruction generation loop
|
// Main instruction generation loop
|
||||||
for (i, (block, block_sig)) in self.resources.blocks.clone().iter().enumerate() {
|
for (block, block_sig) in self.resources.blocks.clone().into_iter() {
|
||||||
let is_block0 = i == 0;
|
let is_block0 = block.as_u32() == 0;
|
||||||
builder.switch_to_block(*block);
|
builder.switch_to_block(block);
|
||||||
|
|
||||||
if is_block0 {
|
if is_block0 {
|
||||||
// The first block is special because we must create variables both for the
|
// The first block is special because we must create variables both for the
|
||||||
@@ -1143,7 +1267,7 @@ where
|
|||||||
// Define variables for the block params
|
// Define variables for the block params
|
||||||
for (i, ty) in block_sig.iter().enumerate() {
|
for (i, ty) in block_sig.iter().enumerate() {
|
||||||
let var = self.get_variable_of_type(*ty)?;
|
let var = self.get_variable_of_type(*ty)?;
|
||||||
let block_param = builder.block_params(*block)[i];
|
let block_param = builder.block_params(block)[i];
|
||||||
builder.def_var(var, block_param);
|
builder.def_var(var, block_param);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1151,7 +1275,7 @@ where
|
|||||||
// Generate block instructions
|
// Generate block instructions
|
||||||
self.generate_instructions(&mut builder)?;
|
self.generate_instructions(&mut builder)?;
|
||||||
|
|
||||||
self.finalize_block(&mut builder)?;
|
self.finalize_block(&mut builder, block)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.seal_all_blocks();
|
builder.seal_all_blocks();
|
||||||
|
|||||||
Reference in New Issue
Block a user