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:
Viktor Zoutman 2020-11-18 14:43:38 +01:00 committed by GitHub
parent bedbc4dc0f
commit 6353505e9e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 207 additions and 289 deletions

View File

@ -134,10 +134,12 @@ pub fn link(sess: &Session, mut inputs: Vec<Module>, opts: &Options) -> Result<M
dce::dce(&mut output);
}
if opts.structurize {
let mut output = if opts.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");

View File

@ -1,8 +1,7 @@
// 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 rspirv::dr::{Block, Instruction, Module, ModuleHeader, Operand};
use rspirv::dr::{Block, Builder, InsertPoint, Instruction, Module, Operand};
use rspirv::spirv::{Op, SelectionControl, Word};
use rustc_session::Session;
use std::collections::{HashMap, VecDeque};
@ -106,69 +105,51 @@ impl ControlFlowInfo {
}
}
fn get_debug_names(&self) -> Vec<(Word, String)> {
let mut retval = Vec::new();
fn set_names(&self, builder: &mut Builder) {
for loop_info in &self.loops {
retval.push((loop_info.header_id, "loop_header".to_string()));
retval.push((loop_info.merge_id, "loop_merge".to_string()));
retval.push((loop_info.continue_id, "loop_continue".to_string()));
builder.name(loop_info.header_id, "loop_header".to_string());
builder.name(loop_info.merge_id, "loop_merge".to_string());
builder.name(loop_info.continue_id, "loop_continue".to_string());
}
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 {
retval.push((*id, "switch_merge".to_string()));
builder.name(*id, "switch_merge".to_string());
}
retval
}
}
pub fn structurize(sess: &Session, module: &mut Module) {
let mut debug_names = Vec::new();
pub fn structurize(sess: &Session, module: Module) -> Module {
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();
insert_loop_merge_on_conditional_branch(
&mut module.header.as_mut().unwrap(),
&mut func.blocks,
&mut cf_info,
);
builder.select_function(Some(func_idx)).unwrap();
retarget_loop_children_if_needed(&mut func.blocks, &cf_info);
insert_selection_merge_on_conditional_branch(
sess,
&mut module.header.as_mut().unwrap(),
&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());
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(sess, &mut builder, &mut cf_info);
defer_loop_internals(&mut builder, &cf_info);
cf_info.set_names(&mut builder);
}
for (id, name) in debug_names {
module.debugs.push(Instruction::new(
Op::Name,
None,
None,
vec![Operand::IdRef(id), Operand::LiteralString(name)],
))
}
builder.module()
}
fn find_block_index_from_id(blocks: &[Block], id: &Word) -> usize {
for (i, block) in blocks.iter().enumerate() {
fn get_blocks_mut(builder: &mut Builder) -> &mut Vec<Block> {
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) {
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);
}
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.
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 {
let LoopInfo {
header_id: header,
@ -190,8 +187,8 @@ fn retarget_loop_children_if_needed(blocks: &mut [Block], cf_info: &ControlFlowI
next.push_back(*header);
while let Some(front) = next.pop_front() {
let block_idx = find_block_index_from_id(blocks, &front);
let mut new_edges = outgoing_edges(&blocks[block_idx]);
let block_idx = find_block_index_from_id(builder, &front);
let mut new_edges = outgoing_edges(get_block_ref!(builder, block_idx));
// Make sure we are not looping or going into child 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 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.
let front_block = &mut blocks[block_idx];
let front_block = get_block_mut!(builder, block_idx);
(*front_block
.instructions
.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();
for block in blocks {
for block in get_blocks_ref(builder) {
let out = outgoing_edges(block);
if out.contains(&id) {
incoming_edges.push(block.label_id().unwrap());
@ -241,168 +238,94 @@ fn incoming_edges(id: Word, blocks: &[Block]) -> Vec<Word> {
incoming_edges
}
fn num_incoming_edges(id: Word, blocks: &[Block]) -> usize {
incoming_edges(id, blocks).len()
fn num_incoming_edges(id: Word, builder: &mut Builder) -> usize {
incoming_edges(id, builder).len()
}
// Turn a block into a conditional branch that either goes to yes or goes to merge.
fn change_block_to_switch(
blocks: &mut [Block],
builder: &mut Builder,
block_id: Word,
cases: &[Word],
merge: Word,
condition: Word,
) {
let cb_idx = find_block_index_from_id(blocks, &block_id);
let cb_block = &mut blocks[cb_idx];
let cb_idx = find_block_index_from_id(builder, &block_id);
builder.select_block(Some(cb_idx)).unwrap();
// selection merge
let merge_operands = vec![
Operand::IdRef(merge),
Operand::SelectionControl(SelectionControl::NONE),
];
*cb_block.instructions.last_mut().unwrap() =
Instruction::new(Op::SelectionMerge, None, None, merge_operands);
let target: Vec<(Operand, Word)> = cases
.iter()
.enumerate()
.map(|(i, id)| (Operand::LiteralInt32(i as u32 + 1), *id))
.collect();
// conditional branch
let mut switch_operands = vec![Operand::IdRef(condition), Operand::IdRef(merge)];
for (i, label_id) in cases.iter().enumerate() {
let literal = i as u32 + 1;
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
builder.pop_instruction().unwrap();
builder
.selection_merge(merge, SelectionControl::NONE)
.unwrap();
builder.switch(condition, merge, target).unwrap();
}
// detect the intermediate break block by checking whether a block that branches to a merge block has 2 parents.
fn defer_loop_internals(
header: &mut ModuleHeader,
blocks: &mut Vec<Block>,
cf_info: &ControlFlowInfo,
debug_names: &mut Vec<(Word, String)>,
constants: &mut Vec<Instruction>,
) {
fn defer_loop_internals(builder: &mut Builder, cf_info: &ControlFlowInfo) {
for loop_info in &cf_info.loops {
// find all blocks that branch to a merge block.
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);
if out.len() == 1 && out[0] == loop_info.merge_id {
possible_intermediate_block_idexes.push(i)
}
}
// 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 {
let intermediate_block = &blocks[i];
let num_incoming_edges =
num_incoming_edges(intermediate_block.label_id().unwrap(), blocks);
let intermediate_block_id = idx_to_id(builder, i);
let num_incoming_edges = num_incoming_edges(intermediate_block_id, builder);
if num_incoming_edges > 1 {
intermediate_blocks.push(i);
intermediate_block_ids.push(intermediate_block_id);
}
}
if !intermediate_blocks.is_empty() {
let intermediate_block_ids: Vec<Word> = intermediate_blocks
.iter()
.map(|idx| blocks[*idx].label_id().unwrap())
.collect();
if !intermediate_block_ids.is_empty() {
// 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
let phi_result_id = id(header);
let int_type_id = find_or_create_type_constant(Op::TypeInt, constants, header);
let const_0_id =
find_or_create_int_constant(Op::Constant, constants, header, int_type_id, 0);
let phi_result_id = builder.id();
let int_type_id = builder.type_int(32, 1);
let const_0_id = builder.constant_u32(int_type_id, 0);
let mut phi_operands = vec![];
for (intermediate_i, intermediate_block_id) in intermediate_block_ids.iter().enumerate()
{
let intermediate_i = intermediate_i as u32 + 1;
let intermediate_block_idx =
find_block_index_from_id(blocks, intermediate_block_id);
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);
let const_x_id = builder.constant_u32(int_type_id, intermediate_i);
let t = incoming_edges(*intermediate_block_id, builder);
for blocks_that_go_to_intermediate in t {
phi_operands.push(Operand::IdRef(const_x_id));
phi_operands.push(Operand::IdRef(blocks_that_go_to_intermediate));
phi_operands.push((const_x_id, 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(Operand::IdRef(loop_info.header_id));
let phi_inst = Instruction::new(
Op::Phi,
Some(int_type_id),
Some(phi_result_id),
phi_operands,
);
phi_operands.push((const_0_id, loop_info.header_id));
// add phi to the empty merge block.
{
let merge_block_idx = find_block_index_from_id(blocks, &loop_info.merge_id);
let merge_block = &mut blocks[merge_block_idx];
merge_block.instructions.insert(0, phi_inst);
}
builder
.select_block(Some(find_block_index_from_id(builder, &loop_info.merge_id)))
.unwrap();
builder
.insert_phi(
InsertPoint::Begin,
int_type_id,
Some(phi_result_id),
phi_operands,
)
.unwrap();
// point all intermediate blocks to the new empty merge block.
for intermediate_block_id in intermediate_block_ids.iter() {
for incoming_id in incoming_edges(*intermediate_block_id, blocks) {
let incoming_idx = find_block_index_from_id(blocks, &incoming_id);
let incoming_block = &mut blocks[incoming_idx];
for incoming_id in incoming_edges(*intermediate_block_id, builder) {
let incoming_idx = find_block_index_from_id(builder, &incoming_id);
let incoming_block = get_block_mut!(builder, incoming_idx);
for operand in &mut incoming_block.instructions.last_mut().unwrap().operands {
if *operand == Operand::IdRef(*intermediate_block_id) {
@ -414,7 +337,7 @@ fn defer_loop_internals(
// Create a switch statement of all intermediate blocks.
change_block_to_switch(
blocks,
builder,
loop_info.merge_id,
&intermediate_block_ids,
old_merge_block_id,
@ -424,8 +347,8 @@ fn defer_loop_internals(
// point intermediate blocks to the old merge block.
for intermediate_block_id in intermediate_block_ids.iter() {
let intermediate_block_idx =
find_block_index_from_id(blocks, intermediate_block_id);
for operand in &mut blocks[intermediate_block_idx]
find_block_index_from_id(builder, intermediate_block_id);
for operand in &mut get_block_mut!(builder, intermediate_block_idx)
.instructions
.last_mut()
.unwrap()
@ -441,14 +364,14 @@ fn defer_loop_internals(
}
// "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.
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();
if ends_in_branch(block) {
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);
}
}
@ -457,8 +380,8 @@ fn eliminate_multiple_continue_blocks(blocks: &mut Vec<Block>, header: Word) ->
if continue_blocks.len() > 1 {
let continue_block_id = continue_blocks.last().unwrap();
for block_id in continue_blocks.iter().take(continue_blocks.len() - 1) {
let idx = find_block_index_from_id(blocks, block_id);
let block = &mut blocks[idx];
let idx = find_block_index_from_id(builder, block_id);
let block = get_block_mut!(builder, idx);
for op in &mut block.instructions.last_mut().unwrap().operands {
if *op == Operand::IdRef(header) {
*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();
next.push_back(start);
while let Some(front) = next.pop_front() {
let block_idx = find_block_index_from_id(blocks, &front);
let mut new_edges = outgoing_edges(&blocks[block_idx]);
let block_idx = find_block_index_from_id(builder, &front);
let mut new_edges = outgoing_edges(get_block_ref!(builder, block_idx));
// Make sure we are not looping.
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?
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();
}
// if front is a merge block return true
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;
}
@ -510,9 +434,9 @@ fn block_leads_into_break(blocks: &[Block], cf_info: &ControlFlowInfo, start: Wo
false
}
fn block_leads_into_continue(blocks: &[Block], cf_info: &ControlFlowInfo, start: Word) -> bool {
let start_idx = find_block_index_from_id(blocks, &start);
let new_edges = outgoing_edges(&blocks[start_idx]);
fn block_leads_into_continue(builder: &Builder, cf_info: &ControlFlowInfo, start: Word) -> bool {
let start_idx = find_block_index_from_id(builder, &start);
let new_edges = outgoing_edges(get_block_ref!(builder, start_idx));
for loop_info in &cf_info.loops {
if new_edges.len() == 1 && loop_info.continue_id == new_edges[0] {
return true;
@ -523,7 +447,12 @@ fn block_leads_into_continue(blocks: &[Block], cf_info: &ControlFlowInfo, start:
}
// 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();
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.
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 {
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.
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 {
// 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)
{
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.
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();
}
}
@ -582,13 +511,13 @@ fn block_is_reverse_idom_of(blocks: &[Block], cf_info: &ControlFlowInfo, a: Word
true
}
fn get_possible_merge_positions(
blocks: &[Block],
builder: &Builder,
cf_info: &ControlFlowInfo,
start: Word,
) -> Vec<usize> {
let mut retval = Vec::new();
for (idx, block) in blocks.iter().enumerate() {
if block_is_reverse_idom_of(blocks, cf_info, start, block.label_id().unwrap()) {
for (idx, block) in get_blocks_ref(builder).iter().enumerate() {
if block_is_reverse_idom_of(builder, cf_info, start, block.label_id().unwrap()) {
retval.push(idx);
}
}
@ -596,7 +525,7 @@ fn get_possible_merge_positions(
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();
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.
while let Some(front) = next.pop_front() {
let block_idx = find_block_index_from_id(blocks, &front);
let mut new_edges = outgoing_edges(&blocks[block_idx]);
let block_idx = find_block_index_from_id(builder, &front);
let mut new_edges = outgoing_edges(get_block_ref!(builder, block_idx));
for id in &processed {
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.
fn get_looping_branch_from_block(
blocks: &[Block],
builder: &Builder,
cf_info: &ControlFlowInfo,
start: Word,
) -> Option<usize> {
@ -641,17 +570,18 @@ fn get_looping_branch_from_block(
continue;
}
let block_idx = find_block_index_from_id(blocks, &front);
let mut new_edges = outgoing_edges(&blocks[block_idx]);
let block_idx = find_block_index_from_id(builder, &front);
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
if new_edges.len() == 1 {
if let Some(edge_it) = edge_it {
// 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() {
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);
}
}
@ -687,36 +617,23 @@ fn ends_in_return(block: &Block) -> bool {
}
// Returns the new id assigned to the original block.
fn split_block(
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();
fn split_block(builder: &mut Builder, block_to_split: Word, retarget: bool) -> Word {
// 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);
// create new block with old id.
builder.begin_block(Some(block_to_split)).unwrap();
// new block branches to old block.
let branch_inst = Instruction::new(
Op::Branch,
None,
None,
vec![Operand::IdRef(new_original_block_id)],
);
new_block.instructions.push(branch_inst);
builder.branch(new_original_block_id).unwrap();
if retarget {
// 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 {
if inst.class.opcode == Op::LoopMerge || inst.class.opcode == Op::SelectionMerge {
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);
}
}
@ -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
}
fn make_unreachable_block(header: &mut ModuleHeader, blocks: &mut Vec<Block>) -> Word {
let id = id(header);
let mut new_block = Block::new();
new_block.label = Some(Instruction::new(Op::Label, None, Some(id), vec![]));
// 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);
fn make_unreachable_block(builder: &mut Builder) -> Word {
let id = builder.id();
builder.begin_block(Some(id)).unwrap();
builder.unreachable().unwrap();
id
}
pub fn insert_selection_merge_on_conditional_branch(
sess: &Session,
header: &mut ModuleHeader,
blocks: &mut Vec<Block>,
builder: &mut Builder,
cf_info: &mut ControlFlowInfo,
) {
let mut branch_conditional_ops = Vec::new();
// 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)
&& !cf_info.id_is_loops_header(block.label_id().unwrap())
{
@ -770,11 +677,11 @@ pub fn insert_selection_merge_on_conditional_branch(
None => id,
};
let bi = find_block_index_from_id(blocks, id);
let out = outgoing_edges(&blocks[bi]);
let id = &blocks[bi].label_id().unwrap();
let a_nexts = get_possible_merge_positions(blocks, cf_info, out[0]);
let b_nexts = get_possible_merge_positions(blocks, cf_info, out[1]);
let bi = find_block_index_from_id(builder, id);
let out = outgoing_edges(&get_blocks_ref(builder)[bi]);
let id = idx_to_id(builder, bi);
let a_nexts = get_possible_merge_positions(builder, cf_info, out[0]);
let b_nexts = get_possible_merge_positions(builder, cf_info, out[1]);
// Check for a matching possible merge position.
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 {
// We found a existing block that we can use as a merge block!
blocks[idx].label_id().unwrap()
idx_to_id(builder, idx)
} else {
let a_first_idx = find_block_index_from_id(blocks, &out[0]);
let b_first_idx = find_block_index_from_id(blocks, &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_first_id = out[0];
let b_first_id = out[1];
let a_last_idx = match a_nexts.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() {
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_b_breaks = block_leads_into_break(blocks, cf_info, b_first_id);
let branch_a_continues = block_leads_into_continue(blocks, cf_info, a_first_id);
let branch_b_continues = block_leads_into_continue(blocks, cf_info, b_first_id);
let branch_a_returns = ends_in_return(&blocks[a_last_idx]);
let branch_b_returns = ends_in_return(&blocks[b_last_idx]);
let branch_a_breaks = block_leads_into_break(builder, cf_info, a_first_id);
let branch_b_breaks = block_leads_into_break(builder, cf_info, b_first_id);
let branch_a_continues = block_leads_into_continue(builder, cf_info, a_first_id);
let branch_b_continues = block_leads_into_continue(builder, cf_info, b_first_id);
let branch_a_returns = ends_in_return(get_block_ref!(builder, a_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))
|| branch_a_returns && branch_b_returns
{
// (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 {
// (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 {
// (partially unreachable) merge block becomes branch a immediatly
blocks[a_first_idx].label_id().unwrap()
} 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.");
a_first_id
} else {
// In theory this should never happen.
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) {
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);
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);
// Insert the merge instruction
let bi = find_block_index_from_id(blocks, id); // after this we don't insert or remove blocks
let block = &mut blocks[bi];
let bi = find_block_index_from_id(builder, &id); // after this we don't insert or remove blocks
let block = get_block_mut!(builder, bi);
let merge_inst = Instruction::new(Op::SelectionMerge, None, None, merge_operands);
block
.instructions
@ -861,18 +760,17 @@ pub fn insert_selection_merge_on_conditional_branch(
}
pub fn insert_loop_merge_on_conditional_branch(
header: &mut ModuleHeader,
blocks: &mut Vec<Block>,
builder: &mut Builder,
cf_info: &mut ControlFlowInfo,
) {
let mut branch_conditional_ops = Vec::new();
// 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) {
let block_id = block.label_id().unwrap();
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));
cf_info.loops.push(LoopInfo {
@ -883,7 +781,6 @@ pub fn insert_loop_merge_on_conditional_branch(
}
}
}
let mut modified_ids = HashMap::new();
// 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 bi = find_block_index_from_id(blocks, &id);
let out = outgoing_edges(&blocks[bi]);
let bi = find_block_index_from_id(builder, &id);
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];
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);
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) {
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);
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 check_block = &mut blocks[bi];
let bi = find_block_index_from_id(builder, &id); // after this we don't insert or remove blocks
let check_block = &mut get_blocks_mut(builder)[bi];
let merge_operands = vec![
Operand::IdRef(merge_block_id),

View File

@ -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;
}
}
}
"#);
}