Skip to content
This repository was archived by the owner on Oct 31, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions crates/rustc_codegen_spirv/src/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,7 @@ fn do_link(
dce: env::var("NO_DCE").is_err(),
compact_ids: env::var("NO_COMPACT_IDS").is_err(),
inline: legalize,
destructure: legalize,
mem2reg: legalize,
structurize: env::var("NO_STRUCTURIZE").is_err(),
emit_multiple_modules: cg_args.module_output_type == ModuleOutputType::Multiple,
Expand Down
1 change: 1 addition & 0 deletions crates/rustc_codegen_spirv/src/linker/dce.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ fn instruction_is_pure(inst: &Instruction) -> bool {
| InBoundsPtrAccessChain
| CompositeConstruct
| CompositeExtract
| CompositeInsert
| CopyObject
| Transpose
| ConvertFToU
Expand Down
74 changes: 74 additions & 0 deletions crates/rustc_codegen_spirv/src/linker/destructure_composites.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
//! Simplify `OpCompositeExtract` pointing to `OpCompositeConstruct`s / `OpCompositeInsert`s.
//! Such constructions arise after inlining, when using multi-argument closures
//! (and other `Fn*` trait implementations). These composites can frequently be invalid,
//! containing pointers, `OpFunctionArgument`s, etc. After simplification, components
//! will become valid targets for `OpLoad`/`OpStore`.
use super::apply_rewrite_rules;
use rspirv::dr::{Function, Instruction};
use rspirv::spirv::Op;
use rustc_data_structures::fx::FxHashMap;

pub fn destructure_composites(function: &mut Function) {
let mut rewrite_rules = FxHashMap::default();
let reference: FxHashMap<_, _> = function
.all_inst_iter()
.filter_map(|inst| match inst.class.opcode {
Op::CompositeConstruct => Some((inst.result_id.unwrap(), inst.clone())),
Op::CompositeInsert if inst.operands.len() == 3 => {
Some((inst.result_id.unwrap(), inst.clone()))
}
_ => None,
})
.collect();
for inst in function.all_inst_iter_mut() {
if inst.class.opcode == Op::CompositeExtract && inst.operands.len() == 2 {
let mut composite = inst.operands[0].unwrap_id_ref();
let index = inst.operands[1].unwrap_literal_int32();

let origin = loop {
if let Some(inst) = reference.get(&composite) {
match inst.class.opcode {
Op::CompositeInsert => {
let insert_index = inst.operands[2].unwrap_literal_int32();
if insert_index == index {
break Some(inst.operands[0].unwrap_id_ref());
}
composite = inst.operands[1].unwrap_id_ref();
}
Op::CompositeConstruct => {
break inst.operands.get(index as usize).map(|o| o.unwrap_id_ref());
}
_ => unreachable!(),
}
} else {
break None;
}
};

if let Some(origin_id) = origin {
rewrite_rules.insert(
inst.result_id.unwrap(),
rewrite_rules.get(&origin_id).map_or(origin_id, |id| *id),
);
*inst = Instruction::new(Op::Nop, None, None, vec![]);
continue;
}
}
}

// Transitive closure computation
let mut closed_rewrite_rules = rewrite_rules.clone();
for (_, value) in closed_rewrite_rules.iter_mut() {
while let Some(next) = rewrite_rules.get(value) {
*value = *next;
}
}

// Remove instructions replaced by NOPs, as well as unused composite values.
for block in function.blocks.iter_mut() {
block
.instructions
.retain(|inst| inst.class.opcode != Op::Nop);
}
apply_rewrite_rules(&closed_rewrite_rules, &mut function.blocks);
}
16 changes: 11 additions & 5 deletions crates/rustc_codegen_spirv/src/linker/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
mod test;

mod dce;
mod destructure_composites;
mod duplicates;
mod import_export_link;
mod inline;
Expand All @@ -27,6 +28,7 @@ pub struct Options {
pub dce: bool,
pub inline: bool,
pub mem2reg: bool,
pub destructure: bool,
pub structurize: bool,
pub emit_multiple_modules: bool,
pub name_variables: bool,
Expand Down Expand Up @@ -228,6 +230,10 @@ pub fn link(sess: &Session, mut inputs: Vec<Module>, opts: &Options) -> Result<L
// mem2reg produces minimal SSA form, not pruned, so DCE the dead ones
dce::dce_phi(func);
}
if opts.destructure {
let _timer = sess.timer("link_destructure");
destructure_composites::destructure_composites(func);
}
}
}

Expand All @@ -240,11 +246,6 @@ pub fn link(sess: &Session, mut inputs: Vec<Module>, opts: &Options) -> Result<L
}
}

{
let _timer = sess.timer("link_remove_duplicate_lines");
duplicates::remove_duplicate_lines(&mut output);
}

if opts.name_variables {
let _timer = sess.timer("link_name_variables");
simple_passes::name_variables_pass(&mut output);
Expand Down Expand Up @@ -289,6 +290,11 @@ pub fn link(sess: &Session, mut inputs: Vec<Module>, opts: &Options) -> Result<L
dce::dce(output);
}

{
let _timer = sess.timer("link_remove_duplicate_lines");
duplicates::remove_duplicate_lines(output);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than running this again, please move the original one here and only run it once. The original is where it is now because none of the later passes could modify function contents, but now with #691, DCE can.


if opts.compact_ids {
let _timer = sess.timer("link_compact_ids");
// compact the ids https://github.com/KhronosGroup/SPIRV-Tools/blob/e02f178a716b0c3c803ce31b9df4088596537872/source/opt/compact_ids_pass.cpp#L43
Expand Down
1 change: 1 addition & 0 deletions crates/rustc_codegen_spirv/src/linker/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ fn assemble_and_link(binaries: &[&[u8]]) -> Result<Module, String> {
compact_ids: true,
dce: false,
inline: false,
destructure: false,
mem2reg: false,
structurize: false,
emit_multiple_modules: false,
Expand Down
38 changes: 18 additions & 20 deletions tests/ui/dis/index_user_dst.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,32 @@
OpLine %5 7 12
%6 = OpAccessChain %7 %8 %9
%10 = OpArrayLength %11 %8 0
OpLine %5 7 0
%12 = OpCompositeInsert %13 %6 %14 0
OpLine %5 8 21
%15 = OpULessThan %16 %9 %10
%12 = OpULessThan %13 %9 %10
OpLine %5 8 21
OpSelectionMerge %17 None
OpBranchConditional %15 %18 %19
%18 = OpLabel
OpSelectionMerge %14 None
OpBranchConditional %12 %15 %16
%15 = OpLabel
OpLine %5 8 21
%20 = OpInBoundsAccessChain %21 %6 %9
%22 = OpLoad %23 %20
%17 = OpInBoundsAccessChain %18 %6 %9
%19 = OpLoad %20 %17
OpLine %5 10 1
OpReturn
%19 = OpLabel
%16 = OpLabel
OpLine %5 8 21
OpBranch %24
%24 = OpLabel
OpBranch %21
%21 = OpLabel
OpBranch %22
%22 = OpLabel
%23 = OpPhi %13 %24 %21 %24 %25
OpLoopMerge %26 %25 None
OpBranchConditional %23 %27 %26
%27 = OpLabel
OpBranch %25
%25 = OpLabel
%26 = OpPhi %16 %27 %24 %27 %28
OpLoopMerge %29 %28 None
OpBranchConditional %26 %30 %29
%30 = OpLabel
OpBranch %28
%28 = OpLabel
OpBranch %25
%29 = OpLabel
OpBranch %22
%26 = OpLabel
OpUnreachable
%17 = OpLabel
%14 = OpLabel
OpUnreachable
OpFunctionEnd
16 changes: 16 additions & 0 deletions tests/ui/lang/control_flow/closure_multi.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// build-pass

use spirv_std;

fn closure_user<F: FnMut(&u32, u32)>(ptr: &u32, xmax: u32, mut callback: F) {
for i in 0..xmax {
callback(ptr, i);
}
}

#[spirv(fragment)]
pub fn main(ptr: &mut u32) {
closure_user(ptr, 10, |ptr, i| {
if *ptr == i { spirv_std::arch::kill(); }
});
}