mirror of
https://github.com/EmbarkStudios/rust-gpu.git
synced 2024-11-25 08:14:12 +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);
|
||||
}
|
||||
|
||||
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");
|
||||
|
@ -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),
|
||||
phi_operands.push((const_0_id, loop_info.header_id));
|
||||
|
||||
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,
|
||||
);
|
||||
|
||||
// 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);
|
||||
}
|
||||
)
|
||||
.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),
|
||||
|
@ -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