mirror of
https://github.com/EmbarkStudios/rust-gpu.git
synced 2024-11-22 06:45:13 +00:00
Introducing rspiv::Builder into the structurizer. (#253)
* Replaced most manual spirv manipulation with rspirv builder * Replaced most manual spirv manipulation with rspirv builder * removed braces * prefer slices over vec
This commit is contained in:
parent
bedbc4dc0f
commit
6353505e9e
@ -134,10 +134,12 @@ pub fn link(sess: &Session, mut inputs: Vec<Module>, opts: &Options) -> Result<M
|
|||||||
dce::dce(&mut output);
|
dce::dce(&mut output);
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.structurize {
|
let mut output = if opts.structurize {
|
||||||
let _timer = sess.timer("link_structurize");
|
let _timer = sess.timer("link_structurize");
|
||||||
structurizer::structurize(sess, &mut output);
|
structurizer::structurize(sess, output)
|
||||||
}
|
} else {
|
||||||
|
output
|
||||||
|
};
|
||||||
|
|
||||||
{
|
{
|
||||||
let _timer = sess.timer("link_block_ordering_pass_and_mem2reg");
|
let _timer = sess.timer("link_block_ordering_pass_and_mem2reg");
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
// This pass inserts merge instructions for structured control flow with the assumption the spir-v is reducible.
|
// This pass inserts merge instructions for structured control flow with the assumption the spir-v is reducible.
|
||||||
|
|
||||||
use super::id;
|
|
||||||
use super::simple_passes::outgoing_edges;
|
use super::simple_passes::outgoing_edges;
|
||||||
use rspirv::dr::{Block, Instruction, Module, ModuleHeader, Operand};
|
use rspirv::dr::{Block, Builder, InsertPoint, Instruction, Module, Operand};
|
||||||
use rspirv::spirv::{Op, SelectionControl, Word};
|
use rspirv::spirv::{Op, SelectionControl, Word};
|
||||||
use rustc_session::Session;
|
use rustc_session::Session;
|
||||||
use std::collections::{HashMap, VecDeque};
|
use std::collections::{HashMap, VecDeque};
|
||||||
@ -106,69 +105,51 @@ impl ControlFlowInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_debug_names(&self) -> Vec<(Word, String)> {
|
fn set_names(&self, builder: &mut Builder) {
|
||||||
let mut retval = Vec::new();
|
|
||||||
|
|
||||||
for loop_info in &self.loops {
|
for loop_info in &self.loops {
|
||||||
retval.push((loop_info.header_id, "loop_header".to_string()));
|
builder.name(loop_info.header_id, "loop_header".to_string());
|
||||||
retval.push((loop_info.merge_id, "loop_merge".to_string()));
|
builder.name(loop_info.merge_id, "loop_merge".to_string());
|
||||||
retval.push((loop_info.continue_id, "loop_continue".to_string()));
|
builder.name(loop_info.continue_id, "loop_continue".to_string());
|
||||||
}
|
}
|
||||||
for id in &self.if_merge_ids {
|
for id in &self.if_merge_ids {
|
||||||
retval.push((*id, "if_merge".to_string()));
|
builder.name(*id, "if_merge".to_string());
|
||||||
}
|
}
|
||||||
for id in &self.switch_merge_ids {
|
for id in &self.switch_merge_ids {
|
||||||
retval.push((*id, "switch_merge".to_string()));
|
builder.name(*id, "switch_merge".to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
retval
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn structurize(sess: &Session, module: &mut Module) {
|
pub fn structurize(sess: &Session, module: Module) -> Module {
|
||||||
let mut debug_names = Vec::new();
|
let mut builder = Builder::new_from_module(module);
|
||||||
|
|
||||||
for func in &mut module.functions {
|
for func_idx in 0..builder.module_ref().functions.len() {
|
||||||
let mut cf_info = ControlFlowInfo::new();
|
let mut cf_info = ControlFlowInfo::new();
|
||||||
|
|
||||||
insert_loop_merge_on_conditional_branch(
|
builder.select_function(Some(func_idx)).unwrap();
|
||||||
&mut module.header.as_mut().unwrap(),
|
|
||||||
&mut func.blocks,
|
|
||||||
&mut cf_info,
|
|
||||||
);
|
|
||||||
|
|
||||||
retarget_loop_children_if_needed(&mut func.blocks, &cf_info);
|
insert_loop_merge_on_conditional_branch(&mut builder, &mut cf_info);
|
||||||
|
retarget_loop_children_if_needed(&mut builder, &cf_info);
|
||||||
insert_selection_merge_on_conditional_branch(
|
insert_selection_merge_on_conditional_branch(sess, &mut builder, &mut cf_info);
|
||||||
sess,
|
defer_loop_internals(&mut builder, &cf_info);
|
||||||
&mut module.header.as_mut().unwrap(),
|
cf_info.set_names(&mut builder);
|
||||||
&mut func.blocks,
|
|
||||||
&mut cf_info,
|
|
||||||
);
|
|
||||||
|
|
||||||
defer_loop_internals(
|
|
||||||
&mut module.header.as_mut().unwrap(),
|
|
||||||
&mut func.blocks,
|
|
||||||
&cf_info,
|
|
||||||
&mut debug_names,
|
|
||||||
&mut module.types_global_values,
|
|
||||||
);
|
|
||||||
|
|
||||||
debug_names.extend(cf_info.get_debug_names());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (id, name) in debug_names {
|
builder.module()
|
||||||
module.debugs.push(Instruction::new(
|
|
||||||
Op::Name,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
vec![Operand::IdRef(id), Operand::LiteralString(name)],
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_block_index_from_id(blocks: &[Block], id: &Word) -> usize {
|
fn get_blocks_mut(builder: &mut Builder) -> &mut Vec<Block> {
|
||||||
for (i, block) in blocks.iter().enumerate() {
|
let function = builder.selected_function().unwrap();
|
||||||
|
&mut builder.module_mut().functions[function].blocks
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_blocks_ref(builder: &Builder) -> &[Block] {
|
||||||
|
let function = builder.selected_function().unwrap();
|
||||||
|
&builder.module_ref().functions[function].blocks
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_block_index_from_id(builder: &Builder, id: &Word) -> usize {
|
||||||
|
for (i, block) in get_blocks_ref(builder).iter().enumerate() {
|
||||||
if block.label_id() == Some(*id) {
|
if block.label_id() == Some(*id) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
@ -177,8 +158,24 @@ fn find_block_index_from_id(blocks: &[Block], id: &Word) -> usize {
|
|||||||
panic!("Failed to find block from id {}", id);
|
panic!("Failed to find block from id {}", id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! get_block_mut {
|
||||||
|
($builder:expr, $idx:expr) => {
|
||||||
|
&mut get_blocks_mut($builder)[$idx]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! get_block_ref {
|
||||||
|
($builder:expr, $idx:expr) => {
|
||||||
|
&get_blocks_ref($builder)[$idx]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn idx_to_id(builder: &mut Builder, idx: usize) -> Word {
|
||||||
|
get_blocks_ref(builder)[idx].label_id().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
// some times break will yeet themselfs out of a parent loop by skipping the merge block. This prevents that.
|
// some times break will yeet themselfs out of a parent loop by skipping the merge block. This prevents that.
|
||||||
fn retarget_loop_children_if_needed(blocks: &mut [Block], cf_info: &ControlFlowInfo) {
|
fn retarget_loop_children_if_needed(builder: &mut Builder, cf_info: &ControlFlowInfo) {
|
||||||
for loop_info in &cf_info.loops {
|
for loop_info in &cf_info.loops {
|
||||||
let LoopInfo {
|
let LoopInfo {
|
||||||
header_id: header,
|
header_id: header,
|
||||||
@ -190,8 +187,8 @@ fn retarget_loop_children_if_needed(blocks: &mut [Block], cf_info: &ControlFlowI
|
|||||||
next.push_back(*header);
|
next.push_back(*header);
|
||||||
|
|
||||||
while let Some(front) = next.pop_front() {
|
while let Some(front) = next.pop_front() {
|
||||||
let block_idx = find_block_index_from_id(blocks, &front);
|
let block_idx = find_block_index_from_id(builder, &front);
|
||||||
let mut new_edges = outgoing_edges(&blocks[block_idx]);
|
let mut new_edges = outgoing_edges(get_block_ref!(builder, block_idx));
|
||||||
|
|
||||||
// Make sure we are not looping or going into child loops.
|
// Make sure we are not looping or going into child loops.
|
||||||
for loop_info in &cf_info.loops {
|
for loop_info in &cf_info.loops {
|
||||||
@ -211,9 +208,9 @@ fn retarget_loop_children_if_needed(blocks: &mut [Block], cf_info: &ControlFlowI
|
|||||||
|
|
||||||
if new_edges.len() == 1 {
|
if new_edges.len() == 1 {
|
||||||
// if front branches to a block that is the child of a merge, retarget it.
|
// if front branches to a block that is the child of a merge, retarget it.
|
||||||
if block_is_parent_of(*merge, new_edges[0], blocks) {
|
if block_is_parent_of(builder, *merge, new_edges[0]) {
|
||||||
// retarget front to branch to merge.
|
// retarget front to branch to merge.
|
||||||
let front_block = &mut blocks[block_idx];
|
let front_block = get_block_mut!(builder, block_idx);
|
||||||
(*front_block
|
(*front_block
|
||||||
.instructions
|
.instructions
|
||||||
.last_mut()
|
.last_mut()
|
||||||
@ -229,9 +226,9 @@ fn retarget_loop_children_if_needed(blocks: &mut [Block], cf_info: &ControlFlowI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn incoming_edges(id: Word, blocks: &[Block]) -> Vec<Word> {
|
fn incoming_edges(id: Word, builder: &mut Builder) -> Vec<Word> {
|
||||||
let mut incoming_edges = Vec::new();
|
let mut incoming_edges = Vec::new();
|
||||||
for block in blocks {
|
for block in get_blocks_ref(builder) {
|
||||||
let out = outgoing_edges(block);
|
let out = outgoing_edges(block);
|
||||||
if out.contains(&id) {
|
if out.contains(&id) {
|
||||||
incoming_edges.push(block.label_id().unwrap());
|
incoming_edges.push(block.label_id().unwrap());
|
||||||
@ -241,168 +238,94 @@ fn incoming_edges(id: Word, blocks: &[Block]) -> Vec<Word> {
|
|||||||
incoming_edges
|
incoming_edges
|
||||||
}
|
}
|
||||||
|
|
||||||
fn num_incoming_edges(id: Word, blocks: &[Block]) -> usize {
|
fn num_incoming_edges(id: Word, builder: &mut Builder) -> usize {
|
||||||
incoming_edges(id, blocks).len()
|
incoming_edges(id, builder).len()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Turn a block into a conditional branch that either goes to yes or goes to merge.
|
// Turn a block into a conditional branch that either goes to yes or goes to merge.
|
||||||
fn change_block_to_switch(
|
fn change_block_to_switch(
|
||||||
blocks: &mut [Block],
|
builder: &mut Builder,
|
||||||
block_id: Word,
|
block_id: Word,
|
||||||
cases: &[Word],
|
cases: &[Word],
|
||||||
merge: Word,
|
merge: Word,
|
||||||
condition: Word,
|
condition: Word,
|
||||||
) {
|
) {
|
||||||
let cb_idx = find_block_index_from_id(blocks, &block_id);
|
let cb_idx = find_block_index_from_id(builder, &block_id);
|
||||||
let cb_block = &mut blocks[cb_idx];
|
builder.select_block(Some(cb_idx)).unwrap();
|
||||||
|
|
||||||
// selection merge
|
let target: Vec<(Operand, Word)> = cases
|
||||||
let merge_operands = vec![
|
.iter()
|
||||||
Operand::IdRef(merge),
|
.enumerate()
|
||||||
Operand::SelectionControl(SelectionControl::NONE),
|
.map(|(i, id)| (Operand::LiteralInt32(i as u32 + 1), *id))
|
||||||
];
|
.collect();
|
||||||
*cb_block.instructions.last_mut().unwrap() =
|
|
||||||
Instruction::new(Op::SelectionMerge, None, None, merge_operands);
|
|
||||||
|
|
||||||
// conditional branch
|
builder.pop_instruction().unwrap();
|
||||||
let mut switch_operands = vec![Operand::IdRef(condition), Operand::IdRef(merge)];
|
builder
|
||||||
|
.selection_merge(merge, SelectionControl::NONE)
|
||||||
for (i, label_id) in cases.iter().enumerate() {
|
.unwrap();
|
||||||
let literal = i as u32 + 1;
|
builder.switch(condition, merge, target).unwrap();
|
||||||
switch_operands.push(Operand::LiteralInt32(literal));
|
|
||||||
switch_operands.push(Operand::IdRef(*label_id));
|
|
||||||
}
|
|
||||||
|
|
||||||
cb_block
|
|
||||||
.instructions
|
|
||||||
.push(Instruction::new(Op::Switch, None, None, switch_operands));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_or_create_int_constant(
|
|
||||||
opcode: Op,
|
|
||||||
constants: &mut Vec<Instruction>,
|
|
||||||
header: &mut ModuleHeader,
|
|
||||||
type_result_id: Word,
|
|
||||||
value: u32,
|
|
||||||
) -> Word {
|
|
||||||
// create
|
|
||||||
let result_id = id(header);
|
|
||||||
let new_constant = Instruction::new(
|
|
||||||
opcode,
|
|
||||||
Some(type_result_id),
|
|
||||||
Some(result_id),
|
|
||||||
vec![Operand::LiteralInt32(value)],
|
|
||||||
);
|
|
||||||
constants.push(new_constant);
|
|
||||||
|
|
||||||
result_id
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_or_create_type_constant(
|
|
||||||
opcode: Op,
|
|
||||||
constants: &mut Vec<Instruction>,
|
|
||||||
header: &mut ModuleHeader,
|
|
||||||
) -> Word {
|
|
||||||
// find
|
|
||||||
for constant in constants.iter() {
|
|
||||||
if constant.class.opcode == opcode {
|
|
||||||
return constant.result_id.unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// create
|
|
||||||
let result_id = id(header);
|
|
||||||
let new_constant = Instruction::new(opcode, None, Some(result_id), vec![]);
|
|
||||||
constants.push(new_constant);
|
|
||||||
|
|
||||||
result_id
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// detect the intermediate break block by checking whether a block that branches to a merge block has 2 parents.
|
// detect the intermediate break block by checking whether a block that branches to a merge block has 2 parents.
|
||||||
fn defer_loop_internals(
|
fn defer_loop_internals(builder: &mut Builder, cf_info: &ControlFlowInfo) {
|
||||||
header: &mut ModuleHeader,
|
|
||||||
blocks: &mut Vec<Block>,
|
|
||||||
cf_info: &ControlFlowInfo,
|
|
||||||
debug_names: &mut Vec<(Word, String)>,
|
|
||||||
constants: &mut Vec<Instruction>,
|
|
||||||
) {
|
|
||||||
for loop_info in &cf_info.loops {
|
for loop_info in &cf_info.loops {
|
||||||
// find all blocks that branch to a merge block.
|
// find all blocks that branch to a merge block.
|
||||||
let mut possible_intermediate_block_idexes = Vec::new();
|
let mut possible_intermediate_block_idexes = Vec::new();
|
||||||
for (i, block) in blocks.iter().enumerate() {
|
for (i, block) in get_blocks_ref(builder).iter().enumerate() {
|
||||||
let out = outgoing_edges(block);
|
let out = outgoing_edges(block);
|
||||||
if out.len() == 1 && out[0] == loop_info.merge_id {
|
if out.len() == 1 && out[0] == loop_info.merge_id {
|
||||||
possible_intermediate_block_idexes.push(i)
|
possible_intermediate_block_idexes.push(i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// check how many incoming edges the branch has and use that to collect a list of intermediate blocks.
|
// check how many incoming edges the branch has and use that to collect a list of intermediate blocks.
|
||||||
let mut intermediate_blocks = Vec::new();
|
let mut intermediate_block_ids = Vec::new();
|
||||||
for i in possible_intermediate_block_idexes {
|
for i in possible_intermediate_block_idexes {
|
||||||
let intermediate_block = &blocks[i];
|
let intermediate_block_id = idx_to_id(builder, i);
|
||||||
let num_incoming_edges =
|
let num_incoming_edges = num_incoming_edges(intermediate_block_id, builder);
|
||||||
num_incoming_edges(intermediate_block.label_id().unwrap(), blocks);
|
|
||||||
if num_incoming_edges > 1 {
|
if num_incoming_edges > 1 {
|
||||||
intermediate_blocks.push(i);
|
intermediate_block_ids.push(intermediate_block_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !intermediate_blocks.is_empty() {
|
if !intermediate_block_ids.is_empty() {
|
||||||
let intermediate_block_ids: Vec<Word> = intermediate_blocks
|
|
||||||
.iter()
|
|
||||||
.map(|idx| blocks[*idx].label_id().unwrap())
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
// Create a new empty block.
|
// Create a new empty block.
|
||||||
let old_merge_block_id = split_block(header, blocks, loop_info.merge_id, false);
|
let old_merge_block_id = split_block(builder, loop_info.merge_id, false);
|
||||||
|
|
||||||
// Create Phi
|
// Create Phi
|
||||||
let phi_result_id = id(header);
|
let phi_result_id = builder.id();
|
||||||
let int_type_id = find_or_create_type_constant(Op::TypeInt, constants, header);
|
let int_type_id = builder.type_int(32, 1);
|
||||||
let const_0_id =
|
let const_0_id = builder.constant_u32(int_type_id, 0);
|
||||||
find_or_create_int_constant(Op::Constant, constants, header, int_type_id, 0);
|
|
||||||
|
|
||||||
let mut phi_operands = vec![];
|
let mut phi_operands = vec![];
|
||||||
for (intermediate_i, intermediate_block_id) in intermediate_block_ids.iter().enumerate()
|
for (intermediate_i, intermediate_block_id) in intermediate_block_ids.iter().enumerate()
|
||||||
{
|
{
|
||||||
let intermediate_i = intermediate_i as u32 + 1;
|
let intermediate_i = intermediate_i as u32 + 1;
|
||||||
let intermediate_block_idx =
|
let const_x_id = builder.constant_u32(int_type_id, intermediate_i);
|
||||||
find_block_index_from_id(blocks, intermediate_block_id);
|
let t = incoming_edges(*intermediate_block_id, builder);
|
||||||
let intermediate_block_id = blocks[intermediate_block_idx].label_id().unwrap();
|
|
||||||
let const_x_id = find_or_create_int_constant(
|
|
||||||
Op::Constant,
|
|
||||||
constants,
|
|
||||||
header,
|
|
||||||
int_type_id,
|
|
||||||
intermediate_i,
|
|
||||||
);
|
|
||||||
let t = incoming_edges(intermediate_block_id, blocks);
|
|
||||||
for blocks_that_go_to_intermediate in t {
|
for blocks_that_go_to_intermediate in t {
|
||||||
phi_operands.push(Operand::IdRef(const_x_id));
|
phi_operands.push((const_x_id, blocks_that_go_to_intermediate));
|
||||||
phi_operands.push(Operand::IdRef(blocks_that_go_to_intermediate));
|
|
||||||
}
|
}
|
||||||
debug_names.push((intermediate_block_id, "deferred".to_string()));
|
builder.name(*intermediate_block_id, "deferred".to_string());
|
||||||
}
|
}
|
||||||
phi_operands.push(Operand::IdRef(const_0_id));
|
phi_operands.push((const_0_id, loop_info.header_id));
|
||||||
phi_operands.push(Operand::IdRef(loop_info.header_id));
|
|
||||||
let phi_inst = Instruction::new(
|
|
||||||
Op::Phi,
|
|
||||||
Some(int_type_id),
|
|
||||||
Some(phi_result_id),
|
|
||||||
phi_operands,
|
|
||||||
);
|
|
||||||
|
|
||||||
// add phi to the empty merge block.
|
builder
|
||||||
{
|
.select_block(Some(find_block_index_from_id(builder, &loop_info.merge_id)))
|
||||||
let merge_block_idx = find_block_index_from_id(blocks, &loop_info.merge_id);
|
.unwrap();
|
||||||
let merge_block = &mut blocks[merge_block_idx];
|
builder
|
||||||
merge_block.instructions.insert(0, phi_inst);
|
.insert_phi(
|
||||||
}
|
InsertPoint::Begin,
|
||||||
|
int_type_id,
|
||||||
|
Some(phi_result_id),
|
||||||
|
phi_operands,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
// point all intermediate blocks to the new empty merge block.
|
// point all intermediate blocks to the new empty merge block.
|
||||||
for intermediate_block_id in intermediate_block_ids.iter() {
|
for intermediate_block_id in intermediate_block_ids.iter() {
|
||||||
for incoming_id in incoming_edges(*intermediate_block_id, blocks) {
|
for incoming_id in incoming_edges(*intermediate_block_id, builder) {
|
||||||
let incoming_idx = find_block_index_from_id(blocks, &incoming_id);
|
let incoming_idx = find_block_index_from_id(builder, &incoming_id);
|
||||||
let incoming_block = &mut blocks[incoming_idx];
|
let incoming_block = get_block_mut!(builder, incoming_idx);
|
||||||
|
|
||||||
for operand in &mut incoming_block.instructions.last_mut().unwrap().operands {
|
for operand in &mut incoming_block.instructions.last_mut().unwrap().operands {
|
||||||
if *operand == Operand::IdRef(*intermediate_block_id) {
|
if *operand == Operand::IdRef(*intermediate_block_id) {
|
||||||
@ -414,7 +337,7 @@ fn defer_loop_internals(
|
|||||||
|
|
||||||
// Create a switch statement of all intermediate blocks.
|
// Create a switch statement of all intermediate blocks.
|
||||||
change_block_to_switch(
|
change_block_to_switch(
|
||||||
blocks,
|
builder,
|
||||||
loop_info.merge_id,
|
loop_info.merge_id,
|
||||||
&intermediate_block_ids,
|
&intermediate_block_ids,
|
||||||
old_merge_block_id,
|
old_merge_block_id,
|
||||||
@ -424,8 +347,8 @@ fn defer_loop_internals(
|
|||||||
// point intermediate blocks to the old merge block.
|
// point intermediate blocks to the old merge block.
|
||||||
for intermediate_block_id in intermediate_block_ids.iter() {
|
for intermediate_block_id in intermediate_block_ids.iter() {
|
||||||
let intermediate_block_idx =
|
let intermediate_block_idx =
|
||||||
find_block_index_from_id(blocks, intermediate_block_id);
|
find_block_index_from_id(builder, intermediate_block_id);
|
||||||
for operand in &mut blocks[intermediate_block_idx]
|
for operand in &mut get_block_mut!(builder, intermediate_block_idx)
|
||||||
.instructions
|
.instructions
|
||||||
.last_mut()
|
.last_mut()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@ -441,14 +364,14 @@ fn defer_loop_internals(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// "Combines" all continue blocks into 1 and returns the ID of the continue block.
|
// "Combines" all continue blocks into 1 and returns the ID of the continue block.
|
||||||
fn eliminate_multiple_continue_blocks(blocks: &mut Vec<Block>, header: Word) -> Word {
|
fn eliminate_multiple_continue_blocks(builder: &mut Builder, header: Word) -> Word {
|
||||||
// Find all possible continue blocks.
|
// Find all possible continue blocks.
|
||||||
let mut continue_blocks = Vec::new();
|
let mut continue_blocks = Vec::new();
|
||||||
for block in blocks.iter() {
|
for block in get_blocks_ref(builder) {
|
||||||
let block_id = block.label_id().unwrap();
|
let block_id = block.label_id().unwrap();
|
||||||
if ends_in_branch(block) {
|
if ends_in_branch(block) {
|
||||||
let edge = outgoing_edges(block)[0];
|
let edge = outgoing_edges(block)[0];
|
||||||
if edge == header && block_is_parent_of(header, block_id, blocks) {
|
if edge == header && block_is_parent_of(builder, header, block_id) {
|
||||||
continue_blocks.push(block_id);
|
continue_blocks.push(block_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -457,8 +380,8 @@ fn eliminate_multiple_continue_blocks(blocks: &mut Vec<Block>, header: Word) ->
|
|||||||
if continue_blocks.len() > 1 {
|
if continue_blocks.len() > 1 {
|
||||||
let continue_block_id = continue_blocks.last().unwrap();
|
let continue_block_id = continue_blocks.last().unwrap();
|
||||||
for block_id in continue_blocks.iter().take(continue_blocks.len() - 1) {
|
for block_id in continue_blocks.iter().take(continue_blocks.len() - 1) {
|
||||||
let idx = find_block_index_from_id(blocks, block_id);
|
let idx = find_block_index_from_id(builder, block_id);
|
||||||
let block = &mut blocks[idx];
|
let block = get_block_mut!(builder, idx);
|
||||||
for op in &mut block.instructions.last_mut().unwrap().operands {
|
for op in &mut block.instructions.last_mut().unwrap().operands {
|
||||||
if *op == Operand::IdRef(header) {
|
if *op == Operand::IdRef(header) {
|
||||||
*op = Operand::IdRef(*continue_block_id);
|
*op = Operand::IdRef(*continue_block_id);
|
||||||
@ -472,13 +395,13 @@ fn eliminate_multiple_continue_blocks(blocks: &mut Vec<Block>, header: Word) ->
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn block_leads_into_break(blocks: &[Block], cf_info: &ControlFlowInfo, start: Word) -> bool {
|
fn block_leads_into_break(builder: &Builder, cf_info: &ControlFlowInfo, start: Word) -> bool {
|
||||||
let mut next: VecDeque<Word> = VecDeque::new();
|
let mut next: VecDeque<Word> = VecDeque::new();
|
||||||
next.push_back(start);
|
next.push_back(start);
|
||||||
|
|
||||||
while let Some(front) = next.pop_front() {
|
while let Some(front) = next.pop_front() {
|
||||||
let block_idx = find_block_index_from_id(blocks, &front);
|
let block_idx = find_block_index_from_id(builder, &front);
|
||||||
let mut new_edges = outgoing_edges(&blocks[block_idx]);
|
let mut new_edges = outgoing_edges(get_block_ref!(builder, block_idx));
|
||||||
|
|
||||||
// Make sure we are not looping.
|
// Make sure we are not looping.
|
||||||
for loop_info in &cf_info.loops {
|
for loop_info in &cf_info.loops {
|
||||||
@ -492,13 +415,14 @@ fn block_leads_into_break(blocks: &[Block], cf_info: &ControlFlowInfo, start: Wo
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Skip inner branches. TODO: is this correct?
|
// Skip inner branches. TODO: is this correct?
|
||||||
if ends_in_branch_conditional(&blocks[find_block_index_from_id(blocks, &front)]) {
|
if ends_in_branch_conditional(get_block_ref!(builder, block_idx)) {
|
||||||
new_edges.clear();
|
new_edges.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
// if front is a merge block return true
|
// if front is a merge block return true
|
||||||
for loop_info in &cf_info.loops {
|
for loop_info in &cf_info.loops {
|
||||||
if front == loop_info.merge_id && block_is_parent_of(loop_info.header_id, start, blocks)
|
if front == loop_info.merge_id
|
||||||
|
&& block_is_parent_of(builder, loop_info.header_id, start)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -510,9 +434,9 @@ fn block_leads_into_break(blocks: &[Block], cf_info: &ControlFlowInfo, start: Wo
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
fn block_leads_into_continue(blocks: &[Block], cf_info: &ControlFlowInfo, start: Word) -> bool {
|
fn block_leads_into_continue(builder: &Builder, cf_info: &ControlFlowInfo, start: Word) -> bool {
|
||||||
let start_idx = find_block_index_from_id(blocks, &start);
|
let start_idx = find_block_index_from_id(builder, &start);
|
||||||
let new_edges = outgoing_edges(&blocks[start_idx]);
|
let new_edges = outgoing_edges(get_block_ref!(builder, start_idx));
|
||||||
for loop_info in &cf_info.loops {
|
for loop_info in &cf_info.loops {
|
||||||
if new_edges.len() == 1 && loop_info.continue_id == new_edges[0] {
|
if new_edges.len() == 1 && loop_info.continue_id == new_edges[0] {
|
||||||
return true;
|
return true;
|
||||||
@ -523,7 +447,12 @@ fn block_leads_into_continue(blocks: &[Block], cf_info: &ControlFlowInfo, start:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// every branch from a reaches b.
|
// every branch from a reaches b.
|
||||||
fn block_is_reverse_idom_of(blocks: &[Block], cf_info: &ControlFlowInfo, a: Word, b: Word) -> bool {
|
fn block_is_reverse_idom_of(
|
||||||
|
builder: &Builder,
|
||||||
|
cf_info: &ControlFlowInfo,
|
||||||
|
a: Word,
|
||||||
|
b: Word,
|
||||||
|
) -> bool {
|
||||||
let mut next: VecDeque<Word> = VecDeque::new();
|
let mut next: VecDeque<Word> = VecDeque::new();
|
||||||
next.push_back(a);
|
next.push_back(a);
|
||||||
|
|
||||||
@ -531,13 +460,13 @@ fn block_is_reverse_idom_of(blocks: &[Block], cf_info: &ControlFlowInfo, a: Word
|
|||||||
processed.push(a); // ensures we are not looping.
|
processed.push(a); // ensures we are not looping.
|
||||||
|
|
||||||
while let Some(front) = next.pop_front() {
|
while let Some(front) = next.pop_front() {
|
||||||
let block_idx = find_block_index_from_id(blocks, &front);
|
let block_idx = find_block_index_from_id(builder, &front);
|
||||||
|
|
||||||
if front == b {
|
if front == b {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut new_edges = outgoing_edges(&blocks[block_idx]);
|
let mut new_edges = outgoing_edges(get_block_ref!(builder, block_idx));
|
||||||
|
|
||||||
// Skip loop bodies by jumping to the merge block is we hit a header block.
|
// Skip loop bodies by jumping to the merge block is we hit a header block.
|
||||||
for loop_info in &cf_info.loops {
|
for loop_info in &cf_info.loops {
|
||||||
@ -549,7 +478,7 @@ fn block_is_reverse_idom_of(blocks: &[Block], cf_info: &ControlFlowInfo, a: Word
|
|||||||
|
|
||||||
for loop_info in &cf_info.loops {
|
for loop_info in &cf_info.loops {
|
||||||
// Make sure we are not looping.
|
// Make sure we are not looping.
|
||||||
if block_is_parent_of(loop_info.header_id, a, blocks)
|
if block_is_parent_of(builder, loop_info.header_id, a)
|
||||||
&& new_edges.contains(&loop_info.header_id)
|
&& new_edges.contains(&loop_info.header_id)
|
||||||
{
|
{
|
||||||
let index = new_edges
|
let index = new_edges
|
||||||
@ -560,7 +489,7 @@ fn block_is_reverse_idom_of(blocks: &[Block], cf_info: &ControlFlowInfo, a: Word
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Make sure we are not continuing after a merge.
|
// Make sure we are not continuing after a merge.
|
||||||
if block_is_parent_of(loop_info.header_id, a, blocks) && front == loop_info.merge_id {
|
if block_is_parent_of(builder, loop_info.header_id, a) && front == loop_info.merge_id {
|
||||||
new_edges.clear();
|
new_edges.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -582,13 +511,13 @@ fn block_is_reverse_idom_of(blocks: &[Block], cf_info: &ControlFlowInfo, a: Word
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
fn get_possible_merge_positions(
|
fn get_possible_merge_positions(
|
||||||
blocks: &[Block],
|
builder: &Builder,
|
||||||
cf_info: &ControlFlowInfo,
|
cf_info: &ControlFlowInfo,
|
||||||
start: Word,
|
start: Word,
|
||||||
) -> Vec<usize> {
|
) -> Vec<usize> {
|
||||||
let mut retval = Vec::new();
|
let mut retval = Vec::new();
|
||||||
for (idx, block) in blocks.iter().enumerate() {
|
for (idx, block) in get_blocks_ref(builder).iter().enumerate() {
|
||||||
if block_is_reverse_idom_of(blocks, cf_info, start, block.label_id().unwrap()) {
|
if block_is_reverse_idom_of(builder, cf_info, start, block.label_id().unwrap()) {
|
||||||
retval.push(idx);
|
retval.push(idx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -596,7 +525,7 @@ fn get_possible_merge_positions(
|
|||||||
retval
|
retval
|
||||||
}
|
}
|
||||||
|
|
||||||
fn block_is_parent_of(parent: Word, child: Word, blocks: &[Block]) -> bool {
|
fn block_is_parent_of(builder: &Builder, parent: Word, child: Word) -> bool {
|
||||||
let mut next: VecDeque<Word> = VecDeque::new();
|
let mut next: VecDeque<Word> = VecDeque::new();
|
||||||
next.push_back(parent);
|
next.push_back(parent);
|
||||||
|
|
||||||
@ -604,8 +533,8 @@ fn block_is_parent_of(parent: Word, child: Word, blocks: &[Block]) -> bool {
|
|||||||
processed.push(parent); // ensures we are not looping.
|
processed.push(parent); // ensures we are not looping.
|
||||||
|
|
||||||
while let Some(front) = next.pop_front() {
|
while let Some(front) = next.pop_front() {
|
||||||
let block_idx = find_block_index_from_id(blocks, &front);
|
let block_idx = find_block_index_from_id(builder, &front);
|
||||||
let mut new_edges = outgoing_edges(&blocks[block_idx]);
|
let mut new_edges = outgoing_edges(get_block_ref!(builder, block_idx));
|
||||||
|
|
||||||
for id in &processed {
|
for id in &processed {
|
||||||
if let Some(i) = new_edges.iter().position(|x| x == id) {
|
if let Some(i) = new_edges.iter().position(|x| x == id) {
|
||||||
@ -626,7 +555,7 @@ fn block_is_parent_of(parent: Word, child: Word, blocks: &[Block]) -> bool {
|
|||||||
|
|
||||||
// Returns the idx of the branch that loops.
|
// Returns the idx of the branch that loops.
|
||||||
fn get_looping_branch_from_block(
|
fn get_looping_branch_from_block(
|
||||||
blocks: &[Block],
|
builder: &Builder,
|
||||||
cf_info: &ControlFlowInfo,
|
cf_info: &ControlFlowInfo,
|
||||||
start: Word,
|
start: Word,
|
||||||
) -> Option<usize> {
|
) -> Option<usize> {
|
||||||
@ -641,17 +570,18 @@ fn get_looping_branch_from_block(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let block_idx = find_block_index_from_id(blocks, &front);
|
let block_idx = find_block_index_from_id(builder, &front);
|
||||||
let mut new_edges = outgoing_edges(&blocks[block_idx]);
|
let mut new_edges = outgoing_edges(get_block_ref!(builder, block_idx));
|
||||||
|
|
||||||
let edge_it = new_edges.iter().find(|&x| x == &start); // Check if the new_edges contain the start
|
let edge_it = new_edges.iter().find(|&x| x == &start); // Check if the new_edges contain the start
|
||||||
if new_edges.len() == 1 {
|
if new_edges.len() == 1 {
|
||||||
if let Some(edge_it) = edge_it {
|
if let Some(edge_it) = edge_it {
|
||||||
// loop over the orginal edges to find which branch is looping
|
// loop over the orginal edges to find which branch is looping
|
||||||
let start_edges = outgoing_edges(&blocks[find_block_index_from_id(blocks, &start)]);
|
let start_idx = find_block_index_from_id(builder, &front);
|
||||||
|
let start_edges = outgoing_edges(get_block_ref!(builder, start_idx));
|
||||||
|
|
||||||
for (i, start_edge) in start_edges.iter().enumerate() {
|
for (i, start_edge) in start_edges.iter().enumerate() {
|
||||||
if start_edge == edge_it || block_is_parent_of(*start_edge, *edge_it, blocks) {
|
if start_edge == edge_it || block_is_parent_of(builder, *start_edge, *edge_it) {
|
||||||
return Some(i);
|
return Some(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -687,36 +617,23 @@ fn ends_in_return(block: &Block) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Returns the new id assigned to the original block.
|
// Returns the new id assigned to the original block.
|
||||||
fn split_block(
|
fn split_block(builder: &mut Builder, block_to_split: Word, retarget: bool) -> Word {
|
||||||
header: &mut ModuleHeader,
|
|
||||||
blocks: &mut Vec<Block>,
|
|
||||||
block_to_split: Word,
|
|
||||||
retarget: bool,
|
|
||||||
) -> Word {
|
|
||||||
// create new block with old id.
|
|
||||||
let block_to_split_index = find_block_index_from_id(blocks, &block_to_split);
|
|
||||||
let orignial_block = &mut blocks[block_to_split_index];
|
|
||||||
let original_id = orignial_block.label_id().unwrap();
|
|
||||||
let mut new_block = Block::new();
|
|
||||||
new_block.label = orignial_block.label.clone();
|
|
||||||
// assign old block new id.
|
// assign old block new id.
|
||||||
let new_original_block_id = id(header);
|
let new_original_block_id = builder.id();
|
||||||
|
let block_to_split_index = find_block_index_from_id(builder, &block_to_split);
|
||||||
|
let orignial_block = get_block_mut!(builder, block_to_split_index);
|
||||||
orignial_block.label.as_mut().unwrap().result_id = Some(new_original_block_id);
|
orignial_block.label.as_mut().unwrap().result_id = Some(new_original_block_id);
|
||||||
|
// create new block with old id.
|
||||||
|
builder.begin_block(Some(block_to_split)).unwrap();
|
||||||
// new block branches to old block.
|
// new block branches to old block.
|
||||||
let branch_inst = Instruction::new(
|
builder.branch(new_original_block_id).unwrap();
|
||||||
Op::Branch,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
vec![Operand::IdRef(new_original_block_id)],
|
|
||||||
);
|
|
||||||
new_block.instructions.push(branch_inst);
|
|
||||||
if retarget {
|
if retarget {
|
||||||
// update all merge ops to point the the old block with its new id.
|
// update all merge ops to point the the old block with its new id.
|
||||||
for block in blocks.iter_mut() {
|
for block in get_blocks_mut(builder) {
|
||||||
for inst in &mut block.instructions {
|
for inst in &mut block.instructions {
|
||||||
if inst.class.opcode == Op::LoopMerge || inst.class.opcode == Op::SelectionMerge {
|
if inst.class.opcode == Op::LoopMerge || inst.class.opcode == Op::SelectionMerge {
|
||||||
for operand in &mut inst.operands {
|
for operand in &mut inst.operands {
|
||||||
if *operand == Operand::IdRef(original_id) {
|
if *operand == Operand::IdRef(block_to_split) {
|
||||||
*operand = Operand::IdRef(new_original_block_id);
|
*operand = Operand::IdRef(new_original_block_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -725,35 +642,25 @@ fn split_block(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// insert new block before the old block.
|
|
||||||
blocks.insert(block_to_split_index, new_block);
|
|
||||||
new_original_block_id
|
new_original_block_id
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_unreachable_block(header: &mut ModuleHeader, blocks: &mut Vec<Block>) -> Word {
|
fn make_unreachable_block(builder: &mut Builder) -> Word {
|
||||||
let id = id(header);
|
let id = builder.id();
|
||||||
let mut new_block = Block::new();
|
builder.begin_block(Some(id)).unwrap();
|
||||||
new_block.label = Some(Instruction::new(Op::Label, None, Some(id), vec![]));
|
builder.unreachable().unwrap();
|
||||||
// new block is unreachable
|
|
||||||
new_block
|
|
||||||
.instructions
|
|
||||||
.push(Instruction::new(Op::Unreachable, None, None, vec![]));
|
|
||||||
|
|
||||||
// insert new block at the end
|
|
||||||
blocks.push(new_block);
|
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert_selection_merge_on_conditional_branch(
|
pub fn insert_selection_merge_on_conditional_branch(
|
||||||
sess: &Session,
|
sess: &Session,
|
||||||
header: &mut ModuleHeader,
|
builder: &mut Builder,
|
||||||
blocks: &mut Vec<Block>,
|
|
||||||
cf_info: &mut ControlFlowInfo,
|
cf_info: &mut ControlFlowInfo,
|
||||||
) {
|
) {
|
||||||
let mut branch_conditional_ops = Vec::new();
|
let mut branch_conditional_ops = Vec::new();
|
||||||
|
|
||||||
// Find conditional branches that are not loops
|
// Find conditional branches that are not loops
|
||||||
for block in blocks.iter() {
|
for block in get_blocks_ref(builder) {
|
||||||
if ends_in_branch_conditional(block)
|
if ends_in_branch_conditional(block)
|
||||||
&& !cf_info.id_is_loops_header(block.label_id().unwrap())
|
&& !cf_info.id_is_loops_header(block.label_id().unwrap())
|
||||||
{
|
{
|
||||||
@ -770,11 +677,11 @@ pub fn insert_selection_merge_on_conditional_branch(
|
|||||||
None => id,
|
None => id,
|
||||||
};
|
};
|
||||||
|
|
||||||
let bi = find_block_index_from_id(blocks, id);
|
let bi = find_block_index_from_id(builder, id);
|
||||||
let out = outgoing_edges(&blocks[bi]);
|
let out = outgoing_edges(&get_blocks_ref(builder)[bi]);
|
||||||
let id = &blocks[bi].label_id().unwrap();
|
let id = idx_to_id(builder, bi);
|
||||||
let a_nexts = get_possible_merge_positions(blocks, cf_info, out[0]);
|
let a_nexts = get_possible_merge_positions(builder, cf_info, out[0]);
|
||||||
let b_nexts = get_possible_merge_positions(blocks, cf_info, out[1]);
|
let b_nexts = get_possible_merge_positions(builder, cf_info, out[1]);
|
||||||
|
|
||||||
// Check for a matching possible merge position.
|
// Check for a matching possible merge position.
|
||||||
let mut first_merge = None;
|
let mut first_merge = None;
|
||||||
@ -789,45 +696,37 @@ pub fn insert_selection_merge_on_conditional_branch(
|
|||||||
|
|
||||||
let merge_block_id = if let Some(idx) = first_merge {
|
let merge_block_id = if let Some(idx) = first_merge {
|
||||||
// We found a existing block that we can use as a merge block!
|
// We found a existing block that we can use as a merge block!
|
||||||
blocks[idx].label_id().unwrap()
|
idx_to_id(builder, idx)
|
||||||
} else {
|
} else {
|
||||||
let a_first_idx = find_block_index_from_id(blocks, &out[0]);
|
let a_first_id = out[0];
|
||||||
let b_first_idx = find_block_index_from_id(blocks, &out[1]);
|
let b_first_id = out[1];
|
||||||
let a_first_id = blocks[a_first_idx].label_id().unwrap();
|
|
||||||
let b_first_id = blocks[b_first_idx].label_id().unwrap();
|
|
||||||
let a_last_idx = match a_nexts.last() {
|
let a_last_idx = match a_nexts.last() {
|
||||||
Some(last) => *last,
|
Some(last) => *last,
|
||||||
None => find_block_index_from_id(blocks, &out[0]),
|
None => find_block_index_from_id(builder, &out[0]),
|
||||||
};
|
};
|
||||||
let b_last_idx = match b_nexts.last() {
|
let b_last_idx = match b_nexts.last() {
|
||||||
Some(last) => *last,
|
Some(last) => *last,
|
||||||
None => find_block_index_from_id(blocks, &out[1]),
|
None => find_block_index_from_id(builder, &out[1]),
|
||||||
};
|
};
|
||||||
|
|
||||||
let branch_a_breaks = block_leads_into_break(blocks, cf_info, a_first_id);
|
let branch_a_breaks = block_leads_into_break(builder, cf_info, a_first_id);
|
||||||
let branch_b_breaks = block_leads_into_break(blocks, cf_info, b_first_id);
|
let branch_b_breaks = block_leads_into_break(builder, cf_info, b_first_id);
|
||||||
let branch_a_continues = block_leads_into_continue(blocks, cf_info, a_first_id);
|
let branch_a_continues = block_leads_into_continue(builder, cf_info, a_first_id);
|
||||||
let branch_b_continues = block_leads_into_continue(blocks, cf_info, b_first_id);
|
let branch_b_continues = block_leads_into_continue(builder, cf_info, b_first_id);
|
||||||
let branch_a_returns = ends_in_return(&blocks[a_last_idx]);
|
let branch_a_returns = ends_in_return(get_block_ref!(builder, a_last_idx));
|
||||||
let branch_b_returns = ends_in_return(&blocks[b_last_idx]);
|
let branch_b_returns = ends_in_return(get_block_ref!(builder, b_last_idx));
|
||||||
|
|
||||||
if ((branch_a_breaks || branch_a_continues) && (branch_b_breaks || branch_b_continues))
|
if ((branch_a_breaks || branch_a_continues) && (branch_b_breaks || branch_b_continues))
|
||||||
|| branch_a_returns && branch_b_returns
|
|| branch_a_returns && branch_b_returns
|
||||||
{
|
{
|
||||||
// (fully unreachable) insert a rando block and mark as merge.
|
// (fully unreachable) insert a rando block and mark as merge.
|
||||||
make_unreachable_block(header, blocks)
|
make_unreachable_block(builder)
|
||||||
} else if branch_a_breaks || branch_a_continues || branch_a_returns {
|
} else if branch_a_breaks || branch_a_continues || branch_a_returns {
|
||||||
// (partially unreachable) merge block becomes branch b immediatly
|
// (partially unreachable) merge block becomes branch b immediatly
|
||||||
blocks[b_first_idx].label_id().unwrap()
|
b_first_id
|
||||||
} else if branch_b_breaks || branch_b_continues || branch_b_returns {
|
} else if branch_b_breaks || branch_b_continues || branch_b_returns {
|
||||||
// (partially unreachable) merge block becomes branch a immediatly
|
// (partially unreachable) merge block becomes branch a immediatly
|
||||||
blocks[a_first_idx].label_id().unwrap()
|
a_first_id
|
||||||
} else if branch_a_returns {
|
|
||||||
// (partially unreachable) merge block becomes end/start of b.
|
|
||||||
sess.fatal("UNIMPLEMENTED, A partially unreachable case was detected on a.");
|
|
||||||
} else if branch_b_returns {
|
|
||||||
// (partially unreachable) merge block becomes end/start of a.
|
|
||||||
sess.fatal("UNIMPLEMENTED, A partially unreachable case was detected on b.");
|
|
||||||
} else {
|
} else {
|
||||||
// In theory this should never happen.
|
// In theory this should never happen.
|
||||||
sess.fatal("UNEXPECTED, Unknown exit detected.");
|
sess.fatal("UNEXPECTED, Unknown exit detected.");
|
||||||
@ -835,7 +734,7 @@ pub fn insert_selection_merge_on_conditional_branch(
|
|||||||
};
|
};
|
||||||
|
|
||||||
if cf_info.used(merge_block_id) {
|
if cf_info.used(merge_block_id) {
|
||||||
let new_id = split_block(header, blocks, merge_block_id, true);
|
let new_id = split_block(builder, merge_block_id, true);
|
||||||
cf_info.retarget(merge_block_id, new_id);
|
cf_info.retarget(merge_block_id, new_id);
|
||||||
|
|
||||||
if branch_conditional_ops.contains(&merge_block_id) {
|
if branch_conditional_ops.contains(&merge_block_id) {
|
||||||
@ -851,8 +750,8 @@ pub fn insert_selection_merge_on_conditional_branch(
|
|||||||
cf_info.if_merge_ids.push(merge_block_id);
|
cf_info.if_merge_ids.push(merge_block_id);
|
||||||
|
|
||||||
// Insert the merge instruction
|
// Insert the merge instruction
|
||||||
let bi = find_block_index_from_id(blocks, id); // after this we don't insert or remove blocks
|
let bi = find_block_index_from_id(builder, &id); // after this we don't insert or remove blocks
|
||||||
let block = &mut blocks[bi];
|
let block = get_block_mut!(builder, bi);
|
||||||
let merge_inst = Instruction::new(Op::SelectionMerge, None, None, merge_operands);
|
let merge_inst = Instruction::new(Op::SelectionMerge, None, None, merge_operands);
|
||||||
block
|
block
|
||||||
.instructions
|
.instructions
|
||||||
@ -861,18 +760,17 @@ pub fn insert_selection_merge_on_conditional_branch(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert_loop_merge_on_conditional_branch(
|
pub fn insert_loop_merge_on_conditional_branch(
|
||||||
header: &mut ModuleHeader,
|
builder: &mut Builder,
|
||||||
blocks: &mut Vec<Block>,
|
|
||||||
cf_info: &mut ControlFlowInfo,
|
cf_info: &mut ControlFlowInfo,
|
||||||
) {
|
) {
|
||||||
let mut branch_conditional_ops = Vec::new();
|
let mut branch_conditional_ops = Vec::new();
|
||||||
|
|
||||||
// Find conditional branches that are loops, and find which branch is the one that loops.
|
// Find conditional branches that are loops, and find which branch is the one that loops.
|
||||||
for block in blocks.iter() {
|
for block in get_blocks_ref(builder) {
|
||||||
if ends_in_branch_conditional(block) {
|
if ends_in_branch_conditional(block) {
|
||||||
let block_id = block.label_id().unwrap();
|
let block_id = block.label_id().unwrap();
|
||||||
if let Some(looping_branch_idx) =
|
if let Some(looping_branch_idx) =
|
||||||
get_looping_branch_from_block(blocks, cf_info, block_id)
|
get_looping_branch_from_block(builder, cf_info, block_id)
|
||||||
{
|
{
|
||||||
branch_conditional_ops.push((block_id, looping_branch_idx));
|
branch_conditional_ops.push((block_id, looping_branch_idx));
|
||||||
cf_info.loops.push(LoopInfo {
|
cf_info.loops.push(LoopInfo {
|
||||||
@ -883,7 +781,6 @@ pub fn insert_loop_merge_on_conditional_branch(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut modified_ids = HashMap::new();
|
let mut modified_ids = HashMap::new();
|
||||||
|
|
||||||
// Figure out which branch loops and which branch should merge, also find any potential break ops.
|
// Figure out which branch loops and which branch should merge, also find any potential break ops.
|
||||||
@ -894,14 +791,14 @@ pub fn insert_loop_merge_on_conditional_branch(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let merge_branch_idx = (looping_branch_idx + 1) % 2;
|
let merge_branch_idx = (looping_branch_idx + 1) % 2;
|
||||||
let bi = find_block_index_from_id(blocks, &id);
|
let bi = find_block_index_from_id(builder, &id);
|
||||||
let out = outgoing_edges(&blocks[bi]);
|
let out = outgoing_edges(&get_blocks_ref(builder)[bi]);
|
||||||
|
|
||||||
let continue_block_id = eliminate_multiple_continue_blocks(blocks, id);
|
let continue_block_id = eliminate_multiple_continue_blocks(builder, id);
|
||||||
let merge_block_id = out[merge_branch_idx];
|
let merge_block_id = out[merge_branch_idx];
|
||||||
|
|
||||||
if cf_info.used(continue_block_id) {
|
if cf_info.used(continue_block_id) {
|
||||||
let new_id = split_block(header, blocks, continue_block_id, true);
|
let new_id = split_block(builder, continue_block_id, true);
|
||||||
cf_info.retarget(continue_block_id, new_id);
|
cf_info.retarget(continue_block_id, new_id);
|
||||||
|
|
||||||
if branch_conditional_ops.contains(&(continue_block_id, *looping_branch_idx)) {
|
if branch_conditional_ops.contains(&(continue_block_id, *looping_branch_idx)) {
|
||||||
@ -909,7 +806,7 @@ pub fn insert_loop_merge_on_conditional_branch(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if cf_info.used(merge_block_id) {
|
if cf_info.used(merge_block_id) {
|
||||||
let new_id = split_block(header, blocks, merge_block_id, true);
|
let new_id = split_block(builder, merge_block_id, true);
|
||||||
cf_info.retarget(merge_block_id, new_id);
|
cf_info.retarget(merge_block_id, new_id);
|
||||||
|
|
||||||
if branch_conditional_ops.contains(&(merge_block_id, *looping_branch_idx)) {
|
if branch_conditional_ops.contains(&(merge_block_id, *looping_branch_idx)) {
|
||||||
@ -917,8 +814,8 @@ pub fn insert_loop_merge_on_conditional_branch(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let bi = find_block_index_from_id(blocks, &id); // after this we don't insert or remove blocks
|
let bi = find_block_index_from_id(builder, &id); // after this we don't insert or remove blocks
|
||||||
let check_block = &mut blocks[bi];
|
let check_block = &mut get_blocks_mut(builder)[bi];
|
||||||
|
|
||||||
let merge_operands = vec![
|
let merge_operands = vec![
|
||||||
Operand::IdRef(merge_block_id),
|
Operand::IdRef(merge_block_id),
|
||||||
|
@ -328,3 +328,22 @@ pub fn main(i: Input<i32>) {
|
|||||||
}
|
}
|
||||||
"#);
|
"#);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn cf_defer() {
|
||||||
|
val(r#"
|
||||||
|
#[allow(unused_attributes)]
|
||||||
|
#[spirv(fragment)]
|
||||||
|
pub fn main(i: Input<i32>) {
|
||||||
|
while i.load() < 32 {
|
||||||
|
let current_position = 0;
|
||||||
|
if i.load() < current_position {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if i.load() < current_position {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#);
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user