Skip to content

Commit 3417d24

Browse files
eddybFirestar99
authored andcommitted
linker/inline: use OpPhi instead of OpVariable for return values.
1 parent 924837a commit 3417d24

File tree

1 file changed

+67
-66
lines changed
  • crates/rustc_codegen_spirv/src/linker

1 file changed

+67
-66
lines changed

crates/rustc_codegen_spirv/src/linker/inline.rs

+67-66
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,6 @@ pub fn inline(sess: &Session, module: &mut Module) -> super::Result<()> {
8989
header,
9090
debug_string_source: &mut module.debug_string_source,
9191
annotations: &mut module.annotations,
92-
types_global_values: &mut module.types_global_values,
9392

9493
legal_globals,
9594

@@ -488,7 +487,6 @@ struct Inliner<'a, 'b> {
488487
header: &'b mut ModuleHeader,
489488
debug_string_source: &'b mut Vec<Instruction>,
490489
annotations: &'b mut Vec<Instruction>,
491-
types_global_values: &'b mut Vec<Instruction>,
492490

493491
legal_globals: FxHashMap<Word, LegalGlobal>,
494492
functions_that_may_abort: FxHashSet<Word>,
@@ -518,29 +516,6 @@ impl Inliner<'_, '_> {
518516
}
519517
}
520518

521-
fn ptr_ty(&mut self, pointee: Word) -> Word {
522-
// TODO: This is horribly slow, fix this
523-
let existing = self.types_global_values.iter().find(|inst| {
524-
inst.class.opcode == Op::TypePointer
525-
&& inst.operands[0].unwrap_storage_class() == StorageClass::Function
526-
&& inst.operands[1].unwrap_id_ref() == pointee
527-
});
528-
if let Some(existing) = existing {
529-
return existing.result_id.unwrap();
530-
}
531-
let inst_id = self.id();
532-
self.types_global_values.push(Instruction::new(
533-
Op::TypePointer,
534-
None,
535-
Some(inst_id),
536-
vec![
537-
Operand::StorageClass(StorageClass::Function),
538-
Operand::IdRef(pointee),
539-
],
540-
));
541-
inst_id
542-
}
543-
544519
fn inline_fn(
545520
&mut self,
546521
function: &mut Function,
@@ -617,15 +592,19 @@ impl Inliner<'_, '_> {
617592
.insert(caller.def_id().unwrap());
618593
}
619594

620-
let call_result_type = {
595+
let mut maybe_call_result_phi = {
621596
let ty = call_inst.result_type.unwrap();
622597
if ty == self.op_type_void_id {
623598
None
624599
} else {
625-
Some(ty)
600+
Some(Instruction::new(
601+
Op::Phi,
602+
Some(ty),
603+
Some(call_inst.result_id.unwrap()),
604+
vec![],
605+
))
626606
}
627607
};
628-
let call_result_id = call_inst.result_id.unwrap();
629608

630609
// Get the debug "source location" instruction that applies to the call.
631610
let custom_ext_inst_set_import = self.custom_ext_inst_set_import;
@@ -662,17 +641,12 @@ impl Inliner<'_, '_> {
662641
});
663642
let mut rewrite_rules = callee_parameters.zip(call_arguments).collect();
664643

665-
let return_variable = if call_result_type.is_some() {
666-
Some(self.id())
667-
} else {
668-
None
669-
};
670644
let return_jump = self.id();
671645
// Rewrite OpReturns of the callee.
672646
let mut inlined_callee_blocks = self.get_inlined_blocks(
673647
callee,
674648
call_debug_src_loc_inst,
675-
return_variable,
649+
maybe_call_result_phi.as_mut(),
676650
return_jump,
677651
);
678652
// Clone the IDs of the callee, because otherwise they'd be defined multiple times if the
@@ -681,6 +655,55 @@ impl Inliner<'_, '_> {
681655
apply_rewrite_rules(&rewrite_rules, &mut inlined_callee_blocks);
682656
self.apply_rewrite_for_decorations(&rewrite_rules);
683657

658+
if let Some(call_result_phi) = &mut maybe_call_result_phi {
659+
// HACK(eddyb) new IDs should be generated earlier, to avoid pushing
660+
// callee IDs to `call_result_phi.operands` only to rewrite them here.
661+
for op in &mut call_result_phi.operands {
662+
if let Some(id) = op.id_ref_any_mut() {
663+
if let Some(&rewrite) = rewrite_rules.get(id) {
664+
*id = rewrite;
665+
}
666+
}
667+
}
668+
669+
// HACK(eddyb) this special-casing of the single-return case is
670+
// really necessary for passes like `mem2reg` which are not capable
671+
// of skipping through the extraneous `OpPhi`s on their own.
672+
if let [returned_value, _return_block] = &call_result_phi.operands[..] {
673+
let call_result_id = call_result_phi.result_id.unwrap();
674+
let returned_value_id = returned_value.unwrap_id_ref();
675+
676+
maybe_call_result_phi = None;
677+
678+
// HACK(eddyb) this is a conservative approximation of all the
679+
// instructions that could potentially reference the call result.
680+
let reaching_insts = {
681+
let (pre_call_blocks, call_and_post_call_blocks) =
682+
caller.blocks.split_at_mut(block_idx);
683+
(pre_call_blocks.iter_mut().flat_map(|block| {
684+
block
685+
.instructions
686+
.iter_mut()
687+
.take_while(|inst| inst.class.opcode == Op::Phi)
688+
}))
689+
.chain(
690+
call_and_post_call_blocks
691+
.iter_mut()
692+
.flat_map(|block| &mut block.instructions),
693+
)
694+
};
695+
for reaching_inst in reaching_insts {
696+
for op in &mut reaching_inst.operands {
697+
if let Some(id) = op.id_ref_any_mut() {
698+
if *id == call_result_id {
699+
*id = returned_value_id;
700+
}
701+
}
702+
}
703+
}
704+
}
705+
}
706+
684707
// Split the block containing the `OpFunctionCall` into pre-call vs post-call.
685708
let pre_call_block_idx = block_idx;
686709
#[expect(unused)]
@@ -696,18 +719,6 @@ impl Inliner<'_, '_> {
696719
.unwrap();
697720
assert!(call.class.opcode == Op::FunctionCall);
698721

699-
if let Some(call_result_type) = call_result_type {
700-
// Generate the storage space for the return value: Do this *after* the split above,
701-
// because if block_idx=0, inserting a variable here shifts call_index.
702-
let ret_var_inst = Instruction::new(
703-
Op::Variable,
704-
Some(self.ptr_ty(call_result_type)),
705-
Some(return_variable.unwrap()),
706-
vec![Operand::StorageClass(StorageClass::Function)],
707-
);
708-
self.insert_opvariables(&mut caller.blocks[0], [ret_var_inst]);
709-
}
710-
711722
// Insert non-entry inlined callee blocks just after the pre-call block.
712723
let non_entry_inlined_callee_blocks = inlined_callee_blocks.drain(1..);
713724
let num_non_entry_inlined_callee_blocks = non_entry_inlined_callee_blocks.len();
@@ -716,18 +727,9 @@ impl Inliner<'_, '_> {
716727
non_entry_inlined_callee_blocks,
717728
);
718729

719-
if let Some(call_result_type) = call_result_type {
720-
// Add the load of the result value after the inlined function. Note there's guaranteed no
721-
// OpPhi instructions since we just split this block.
722-
post_call_block_insts.insert(
723-
0,
724-
Instruction::new(
725-
Op::Load,
726-
Some(call_result_type),
727-
Some(call_result_id),
728-
vec![Operand::IdRef(return_variable.unwrap())],
729-
),
730-
);
730+
if let Some(call_result_phi) = maybe_call_result_phi {
731+
// Add the `OpPhi` for the call result value, after the inlined function.
732+
post_call_block_insts.insert(0, call_result_phi);
731733
}
732734

733735
// Insert the post-call block, after all the inlined callee blocks.
@@ -894,7 +896,7 @@ impl Inliner<'_, '_> {
894896
&mut self,
895897
callee: &Function,
896898
call_debug_src_loc_inst: Option<&Instruction>,
897-
return_variable: Option<Word>,
899+
mut maybe_call_result_phi: Option<&mut Instruction>,
898900
return_jump: Word,
899901
) -> Vec<Block> {
900902
let Self {
@@ -990,14 +992,13 @@ impl Inliner<'_, '_> {
990992
if let Op::Return | Op::ReturnValue = terminator.class.opcode {
991993
if Op::ReturnValue == terminator.class.opcode {
992994
let return_value = terminator.operands[0].id_ref_any().unwrap();
993-
block
994-
.instructions
995-
.push(Instruction::new(Op::Store, None, None, vec![
996-
Operand::IdRef(return_variable.unwrap()),
997-
Operand::IdRef(return_value),
998-
]));
995+
let call_result_phi = maybe_call_result_phi.as_deref_mut().unwrap();
996+
call_result_phi.operands.extend([
997+
Operand::IdRef(return_value),
998+
Operand::IdRef(block.label_id().unwrap()),
999+
]);
9991000
} else {
1000-
assert!(return_variable.is_none());
1001+
assert!(maybe_call_result_phi.is_none());
10011002
}
10021003
terminator =
10031004
Instruction::new(Op::Branch, None, None, vec![Operand::IdRef(return_jump)]);

0 commit comments

Comments
 (0)