Skip to content

Commit 18d6d38

Browse files
eddybFirestar99
authored andcommitted
linker/inline: use OpPhi instead of OpVariable for return values.
1 parent fc0db1b commit 18d6d38

File tree

1 file changed

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

1 file changed

+67
-69
lines changed

crates/rustc_codegen_spirv/src/linker/inline.rs

+67-69
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,6 @@ pub fn inline(sess: &Session, module: &mut Module) -> super::Result<()> {
9494
header,
9595
debug_string_source: &mut module.debug_string_source,
9696
annotations: &mut module.annotations,
97-
types_global_values: &mut module.types_global_values,
9897

9998
legal_globals,
10099

@@ -493,7 +492,6 @@ struct Inliner<'a, 'b> {
493492
header: &'b mut ModuleHeader,
494493
debug_string_source: &'b mut Vec<Instruction>,
495494
annotations: &'b mut Vec<Instruction>,
496-
types_global_values: &'b mut Vec<Instruction>,
497495

498496
legal_globals: FxHashMap<Word, LegalGlobal>,
499497
functions_that_may_abort: FxHashSet<Word>,
@@ -523,29 +521,6 @@ impl Inliner<'_, '_> {
523521
}
524522
}
525523

526-
fn ptr_ty(&mut self, pointee: Word) -> Word {
527-
// TODO: This is horribly slow, fix this
528-
let existing = self.types_global_values.iter().find(|inst| {
529-
inst.class.opcode == Op::TypePointer
530-
&& inst.operands[0].unwrap_storage_class() == StorageClass::Function
531-
&& inst.operands[1].unwrap_id_ref() == pointee
532-
});
533-
if let Some(existing) = existing {
534-
return existing.result_id.unwrap();
535-
}
536-
let inst_id = self.id();
537-
self.types_global_values.push(Instruction::new(
538-
Op::TypePointer,
539-
None,
540-
Some(inst_id),
541-
vec![
542-
Operand::StorageClass(StorageClass::Function),
543-
Operand::IdRef(pointee),
544-
],
545-
));
546-
inst_id
547-
}
548-
549524
fn inline_fn(
550525
&mut self,
551526
function: &mut Function,
@@ -622,15 +597,19 @@ impl Inliner<'_, '_> {
622597
.insert(caller.def_id().unwrap());
623598
}
624599

625-
let call_result_type = {
600+
let mut maybe_call_result_phi = {
626601
let ty = call_inst.result_type.unwrap();
627602
if ty == self.op_type_void_id {
628603
None
629604
} else {
630-
Some(ty)
605+
Some(Instruction::new(
606+
Op::Phi,
607+
Some(ty),
608+
Some(call_inst.result_id.unwrap()),
609+
vec![],
610+
))
631611
}
632612
};
633-
let call_result_id = call_inst.result_id.unwrap();
634613

635614
// Get the debug "source location" instruction that applies to the call.
636615
let custom_ext_inst_set_import = self.custom_ext_inst_set_import;
@@ -667,17 +646,12 @@ impl Inliner<'_, '_> {
667646
});
668647
let mut rewrite_rules = callee_parameters.zip(call_arguments).collect();
669648

670-
let return_variable = if call_result_type.is_some() {
671-
Some(self.id())
672-
} else {
673-
None
674-
};
675649
let return_jump = self.id();
676650
// Rewrite OpReturns of the callee.
677651
let mut inlined_callee_blocks = self.get_inlined_blocks(
678652
callee,
679653
call_debug_src_loc_inst,
680-
return_variable,
654+
maybe_call_result_phi.as_mut(),
681655
return_jump,
682656
);
683657
// Clone the IDs of the callee, because otherwise they'd be defined multiple times if the
@@ -686,6 +660,55 @@ impl Inliner<'_, '_> {
686660
apply_rewrite_rules(&rewrite_rules, &mut inlined_callee_blocks);
687661
self.apply_rewrite_for_decorations(&rewrite_rules);
688662

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

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

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

738740
// Insert the post-call block, after all the inlined callee blocks.
@@ -899,7 +901,7 @@ impl Inliner<'_, '_> {
899901
&mut self,
900902
callee: &Function,
901903
call_debug_src_loc_inst: Option<&Instruction>,
902-
return_variable: Option<Word>,
904+
mut maybe_call_result_phi: Option<&mut Instruction>,
903905
return_jump: Word,
904906
) -> Vec<Block> {
905907
let Self {
@@ -997,17 +999,13 @@ impl Inliner<'_, '_> {
997999
if let Op::Return | Op::ReturnValue = terminator.class.opcode {
9981000
if Op::ReturnValue == terminator.class.opcode {
9991001
let return_value = terminator.operands[0].id_ref_any().unwrap();
1000-
block.instructions.push(Instruction::new(
1001-
Op::Store,
1002-
None,
1003-
None,
1004-
vec![
1005-
Operand::IdRef(return_variable.unwrap()),
1006-
Operand::IdRef(return_value),
1007-
],
1008-
));
1002+
let call_result_phi = maybe_call_result_phi.as_deref_mut().unwrap();
1003+
call_result_phi.operands.extend([
1004+
Operand::IdRef(return_value),
1005+
Operand::IdRef(block.label_id().unwrap()),
1006+
]);
10091007
} else {
1010-
assert!(return_variable.is_none());
1008+
assert!(maybe_call_result_phi.is_none());
10111009
}
10121010
terminator =
10131011
Instruction::new(Op::Branch, None, None, vec![Operand::IdRef(return_jump)]);

0 commit comments

Comments
 (0)