Skip to content

Commit 8ea915a

Browse files
eddybFirestar99
authored andcommitted
linker/inline: fix OpVariable debuginfo collection and insertion.
1 parent 483420e commit 8ea915a

File tree

1 file changed

+150
-52
lines changed
  • crates/rustc_codegen_spirv/src/linker

1 file changed

+150
-52
lines changed

crates/rustc_codegen_spirv/src/linker/inline.rs

Lines changed: 150 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
1515
use rustc_errors::ErrorGuaranteed;
1616
use rustc_session::Session;
1717
use smallvec::SmallVec;
18-
use std::mem::{self, take};
18+
use std::mem;
1919

2020
type FunctionMap = FxHashMap<Word, Function>;
2121

@@ -658,15 +658,13 @@ impl Inliner<'_, '_> {
658658
if let Some(call_result_type) = call_result_type {
659659
// Generate the storage space for the return value: Do this *after* the split above,
660660
// because if block_idx=0, inserting a variable here shifts call_index.
661-
insert_opvariables(
662-
&mut caller.blocks[0],
663-
[Instruction::new(
664-
Op::Variable,
665-
Some(self.ptr_ty(call_result_type)),
666-
Some(return_variable.unwrap()),
667-
vec![Operand::StorageClass(StorageClass::Function)],
668-
)],
661+
let ret_var_inst = Instruction::new(
662+
Op::Variable,
663+
Some(self.ptr_ty(call_result_type)),
664+
Some(return_variable.unwrap()),
665+
vec![Operand::StorageClass(StorageClass::Function)],
669666
);
667+
self.insert_opvariables(&mut caller.blocks[0], [ret_var_inst]);
670668
}
671669

672670
// Insert non-entry inlined callee blocks just after the pre-call block.
@@ -712,52 +710,115 @@ impl Inliner<'_, '_> {
712710
// Fuse the inlined callee entry block into the pre-call block.
713711
// This is okay because it's illegal to branch to the first BB in a function.
714712
{
713+
// NOTE(eddyb) `OpExtInst`s have a result ID, even if unused, and
714+
// it has to be unique, so this allocates new IDs as-needed.
715+
let instantiate_debuginfo = |this: &mut Self, inst: &Instruction| {
716+
let mut inst = inst.clone();
717+
if let Some(id) = &mut inst.result_id {
718+
*id = this.id();
719+
}
720+
inst
721+
};
722+
723+
let custom_inst_to_inst = |this: &mut Self, inst: CustomInst<_>| {
724+
Instruction::new(
725+
Op::ExtInst,
726+
Some(this.op_type_void_id),
727+
Some(this.id()),
728+
[
729+
Operand::IdRef(this.custom_ext_inst_set_import),
730+
Operand::LiteralExtInstInteger(inst.op() as u32),
731+
]
732+
.into_iter()
733+
.chain(inst.into_operands())
734+
.collect(),
735+
)
736+
};
737+
715738
// Return the subsequence of `insts` made from `OpVariable`s, and any
716739
// debuginfo instructions (which may apply to them), while removing
717740
// *only* `OpVariable`s from `insts` (and keeping debuginfo in both).
718741
let mut steal_vars = |insts: &mut Vec<Instruction>| {
719-
let mut vars_and_debuginfo = vec![];
720-
insts.retain_mut(|inst| {
721-
let is_debuginfo = match inst.class.opcode {
722-
Op::Line | Op::NoLine => true,
723-
Op::ExtInst => {
724-
inst.operands[0].unwrap_id_ref() == self.custom_ext_inst_set_import
725-
&& CustomOp::decode_from_ext_inst(inst).is_debuginfo()
726-
}
727-
_ => false,
728-
};
729-
if is_debuginfo {
730-
// NOTE(eddyb) `OpExtInst`s have a result ID,
731-
// even if unused, and it has to be unique.
732-
let mut inst = inst.clone();
733-
if let Some(id) = &mut inst.result_id {
734-
*id = self.id();
742+
// HACK(eddyb) this duplicates some code from `get_inlined_blocks`,
743+
// but that will be removed once the inliner is refactored to be
744+
// inside-out instead of outside-in (already finished in a branch).
745+
let mut enclosing_inlined_frames = SmallVec::<[_; 8]>::new();
746+
let mut current_debug_src_loc_inst = None;
747+
let mut vars_and_debuginfo_range = 0..0;
748+
while vars_and_debuginfo_range.end < insts.len() {
749+
let inst = &insts[vars_and_debuginfo_range.end];
750+
match inst.class.opcode {
751+
Op::Line => current_debug_src_loc_inst = Some(inst),
752+
Op::NoLine => current_debug_src_loc_inst = None,
753+
Op::ExtInst
754+
if inst.operands[0].unwrap_id_ref()
755+
== self.custom_ext_inst_set_import =>
756+
{
757+
match CustomOp::decode_from_ext_inst(inst) {
758+
CustomOp::SetDebugSrcLoc => current_debug_src_loc_inst = Some(inst),
759+
CustomOp::ClearDebugSrcLoc => current_debug_src_loc_inst = None,
760+
CustomOp::PushInlinedCallFrame => {
761+
enclosing_inlined_frames
762+
.push((current_debug_src_loc_inst.take(), inst));
763+
}
764+
CustomOp::PopInlinedCallFrame => {
765+
if let Some((callsite_debug_src_loc_inst, _)) =
766+
enclosing_inlined_frames.pop()
767+
{
768+
current_debug_src_loc_inst = callsite_debug_src_loc_inst;
769+
}
770+
}
771+
CustomOp::Abort => break,
772+
}
735773
}
736-
vars_and_debuginfo.push(inst);
737-
true
738-
} else if inst.class.opcode == Op::Variable {
739-
// HACK(eddyb) we're removing this `Instruction` from
740-
// `inst`, so it doesn't really matter what we use here.
741-
vars_and_debuginfo.push(mem::replace(
742-
inst,
743-
Instruction::new(Op::Nop, None, None, vec![]),
744-
));
745-
false
746-
} else {
747-
true
774+
Op::Variable => {}
775+
_ => break,
748776
}
749-
});
750-
vars_and_debuginfo
777+
vars_and_debuginfo_range.end += 1;
778+
}
779+
780+
// `vars_and_debuginfo_range.end` indicates where `OpVariable`s
781+
// end and other instructions start (module debuginfo), but to
782+
// split the block in two, both sides of the "cut" need "repair":
783+
// - the variables are missing "inlined call frames" pops, that
784+
// may happen later in the block, and have to be synthesized
785+
// - the non-variables are missing "inlined call frames" pushes,
786+
// that must be recreated to avoid ending up with dangling pops
787+
//
788+
// FIXME(eddyb) this only collects to avoid borrow conflicts,
789+
// between e.g. `enclosing_inlined_frames` and mutating `insts`,
790+
// but also between different uses of `self`.
791+
let all_pops_after_vars: SmallVec<[_; 8]> = enclosing_inlined_frames
792+
.iter()
793+
.map(|_| custom_inst_to_inst(self, CustomInst::PopInlinedCallFrame))
794+
.collect();
795+
let all_repushes_before_non_vars: SmallVec<[_; 8]> =
796+
(enclosing_inlined_frames.into_iter().flat_map(
797+
|(callsite_debug_src_loc_inst, push_inlined_call_frame_inst)| {
798+
(callsite_debug_src_loc_inst.into_iter())
799+
.chain([push_inlined_call_frame_inst])
800+
},
801+
))
802+
.chain(current_debug_src_loc_inst)
803+
.map(|inst| instantiate_debuginfo(self, inst))
804+
.collect();
805+
806+
let vars_and_debuginfo =
807+
insts.splice(vars_and_debuginfo_range, all_repushes_before_non_vars);
808+
let repaired_vars_and_debuginfo = vars_and_debuginfo.chain(all_pops_after_vars);
809+
810+
// FIXME(eddyb) collecting shouldn't be necessary but this is
811+
// nested in a closure, and `splice` borrows the original `Vec`.
812+
repaired_vars_and_debuginfo.collect::<SmallVec<[_; 8]>>()
751813
};
752814

753815
let [mut inlined_callee_entry_block]: [_; 1] =
754816
inlined_callee_blocks.try_into().unwrap();
755817

756818
// Move the `OpVariable`s of the callee to the caller.
757-
insert_opvariables(
758-
&mut caller.blocks[0],
759-
steal_vars(&mut inlined_callee_entry_block.instructions),
760-
);
819+
let callee_vars_and_debuginfo =
820+
steal_vars(&mut inlined_callee_entry_block.instructions);
821+
self.insert_opvariables(&mut caller.blocks[0], callee_vars_and_debuginfo);
761822

762823
caller.blocks[pre_call_block_idx]
763824
.instructions
@@ -990,15 +1051,52 @@ impl Inliner<'_, '_> {
9901051
caller_restore_debuginfo_after_call,
9911052
)
9921053
}
993-
}
9941054

995-
fn insert_opvariables(block: &mut Block, insts: impl IntoIterator<Item = Instruction>) {
996-
let first_non_variable = block
997-
.instructions
998-
.iter()
999-
.position(|inst| inst.class.opcode != Op::Variable);
1000-
let i = first_non_variable.unwrap_or(block.instructions.len());
1001-
block.instructions.splice(i..i, insts);
1055+
fn insert_opvariables(&self, block: &mut Block, insts: impl IntoIterator<Item = Instruction>) {
1056+
// HACK(eddyb) this isn't as efficient as it could be in theory, but it's
1057+
// very important to make sure sure to never insert new instructions in
1058+
// the middle of debuginfo (as it would be affected by it).
1059+
let mut inlined_frames_depth = 0usize;
1060+
let mut outermost_has_debug_src_loc = false;
1061+
let mut last_debugless_var_insertion_point_candidate = None;
1062+
for (i, inst) in block.instructions.iter().enumerate() {
1063+
last_debugless_var_insertion_point_candidate =
1064+
(inlined_frames_depth == 0 && !outermost_has_debug_src_loc).then_some(i);
1065+
1066+
let changed_has_debug_src_loc = match inst.class.opcode {
1067+
Op::Line => true,
1068+
Op::NoLine => false,
1069+
Op::ExtInst
1070+
if inst.operands[0].unwrap_id_ref() == self.custom_ext_inst_set_import =>
1071+
{
1072+
match CustomOp::decode_from_ext_inst(inst) {
1073+
CustomOp::SetDebugSrcLoc => true,
1074+
CustomOp::ClearDebugSrcLoc => false,
1075+
CustomOp::PushInlinedCallFrame => {
1076+
inlined_frames_depth += 1;
1077+
continue;
1078+
}
1079+
CustomOp::PopInlinedCallFrame => {
1080+
inlined_frames_depth = inlined_frames_depth.saturating_sub(1);
1081+
continue;
1082+
}
1083+
CustomOp::Abort => break,
1084+
}
1085+
}
1086+
Op::Variable => continue,
1087+
_ => break,
1088+
};
1089+
1090+
if inlined_frames_depth == 0 {
1091+
outermost_has_debug_src_loc = changed_has_debug_src_loc;
1092+
}
1093+
}
1094+
1095+
// HACK(eddyb) fallback to inserting at the start, which should be correct.
1096+
// FIXME(eddyb) some level of debuginfo repair could prevent needing this.
1097+
let i = last_debugless_var_insertion_point_candidate.unwrap_or(0);
1098+
block.instructions.splice(i..i, insts);
1099+
}
10021100
}
10031101

10041102
fn fuse_trivial_branches(function: &mut Function) {
@@ -1033,7 +1131,7 @@ fn fuse_trivial_branches(function: &mut Function) {
10331131
};
10341132
let pred_insts = &function.blocks[pred].instructions;
10351133
if pred_insts.last().unwrap().class.opcode == Op::Branch {
1036-
let mut dest_insts = take(&mut function.blocks[dest_block].instructions);
1134+
let mut dest_insts = mem::take(&mut function.blocks[dest_block].instructions);
10371135
let pred_insts = &mut function.blocks[pred].instructions;
10381136
pred_insts.pop(); // pop the branch
10391137
pred_insts.append(&mut dest_insts);

0 commit comments

Comments
 (0)