Skip to content

Commit 85019dc

Browse files
committed
Auto merge of #151733 - jdonszelmann:eii-on-apple, r=<try>
Use function shims to make sure EII works on apple targets try-job: aarch64-apple try-job: test-various
2 parents c043085 + 91064bf commit 85019dc

File tree

6 files changed

+174
-22
lines changed

6 files changed

+174
-22
lines changed

‎compiler/rustc_codegen_llvm/src/llvm/mod.rs‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,10 @@ pub(crate) fn set_comdat(llmod: &Module, llglobal: &Value, name: &CStr) {
284284
}
285285
}
286286

287+
pub(crate) fn count_params(llfn: &Value) -> c_uint {
288+
LLVMCountParams(llfn)
289+
}
290+
287291
/// Safe wrapper around `LLVMGetParam`, because segfaults are no fun.
288292
pub(crate) fn get_param(llfn: &Value, index: c_uint) -> &Value {
289293
unsafe {

‎compiler/rustc_codegen_llvm/src/mono_item.rs‎

Lines changed: 115 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use std::borrow::Cow;
12
use std::ffi::CString;
23

34
use rustc_abi::AddressSpace;
@@ -6,13 +7,17 @@ use rustc_hir::attrs::Linkage;
67
use rustc_hir::def::DefKind;
78
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
89
use rustc_middle::bug;
10+
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
911
use rustc_middle::mir::mono::Visibility;
1012
use rustc_middle::ty::layout::{FnAbiOf, HasTypingEnv, LayoutOf};
11-
use rustc_middle::ty::{self, Instance, TypeVisitableExt};
13+
use rustc_middle::ty::{self, Instance, Ty, TypeVisitableExt};
1214
use rustc_session::config::CrateType;
15+
use rustc_target::callconv::{FnAbi, PassMode};
1316
use rustc_target::spec::{Arch, RelocModel};
1417
use tracing::debug;
1518

19+
use crate::abi::FnAbiLlvmExt;
20+
use crate::builder::Builder;
1621
use crate::context::CodegenCx;
1722
use crate::errors::SymbolAlreadyDefined;
1823
use crate::type_of::LayoutLlvmExt;
@@ -46,7 +51,7 @@ impl<'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> {
4651
self.assume_dso_local(g, false);
4752

4853
let attrs = self.tcx.codegen_instance_attrs(instance.def);
49-
self.add_aliases(g, &attrs.foreign_item_symbol_aliases);
54+
self.add_static_aliases(g, &attrs.foreign_item_symbol_aliases);
5055

5156
self.instances.borrow_mut().insert(instance, g);
5257
}
@@ -60,11 +65,29 @@ impl<'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> {
6065
) {
6166
assert!(!instance.args.has_infer());
6267

63-
let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty());
68+
let attrs = self.tcx.codegen_instance_attrs(instance.def);
69+
70+
let lldecl =
71+
self.predefine_without_aliases(instance, &attrs, linkage, visibility, symbol_name);
72+
self.add_function_aliases(instance, lldecl, &attrs, &attrs.foreign_item_symbol_aliases);
73+
74+
self.instances.borrow_mut().insert(instance, lldecl);
75+
}
76+
}
77+
78+
impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
79+
fn predefine_without_aliases(
80+
&self,
81+
instance: Instance<'tcx>,
82+
attrs: &Cow<'_, CodegenFnAttrs>,
83+
linkage: Linkage,
84+
visibility: Visibility,
85+
symbol_name: &str,
86+
) -> &'ll llvm::Value {
87+
let fn_abi: &FnAbi<'tcx, Ty<'tcx>> = self.fn_abi_of_instance(instance, ty::List::empty());
6488
let lldecl = self.declare_fn(symbol_name, fn_abi, Some(instance));
6589
llvm::set_linkage(lldecl, base::linkage_to_llvm(linkage));
66-
let attrs = self.tcx.codegen_instance_attrs(instance.def);
67-
base::set_link_section(lldecl, &attrs);
90+
base::set_link_section(lldecl, attrs);
6891
if (linkage == Linkage::LinkOnceODR || linkage == Linkage::WeakODR)
6992
&& self.tcx.sess.target.supports_comdat()
7093
{
@@ -76,20 +99,45 @@ impl<'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> {
7699

77100
self.assume_dso_local(lldecl, false);
78101

79-
self.add_aliases(lldecl, &attrs.foreign_item_symbol_aliases);
80-
81-
self.instances.borrow_mut().insert(instance, lldecl);
102+
lldecl
82103
}
83-
}
84104

85-
impl CodegenCx<'_, '_> {
86-
fn add_aliases(&self, aliasee: &llvm::Value, aliases: &[(DefId, Linkage, Visibility)]) {
105+
/// LLVM has the concept of an `alias`.
106+
/// We need this for the "externally implementable items" feature,
107+
/// though it's generally useful.
108+
///
109+
/// On macos, though this might be a more general problem, function symbols
110+
/// have a fixed target architecture. This is necessary, since macos binaries
111+
/// may contain code for both ARM and x86 macs.
112+
///
113+
/// LLVM *can* add attributes for target architecture to function symbols,
114+
/// cannot do so for statics, but importantly, also cannot for aliases
115+
/// *even* when aliases may refer to a function symbol.
116+
///
117+
/// This is not a problem: instead of using LLVM aliases, we can just generate
118+
/// a new function symbol (with target architecture!) which effectively comes down to:
119+
///
120+
/// ```ignore (illustrative example)
121+
/// fn alias_name(...args) {
122+
/// original_name(...args)
123+
/// }
124+
/// ```
125+
///
126+
/// That's also an alias.
127+
///
128+
/// This does mean that the alias symbol has a different address than the original symbol
129+
/// (assuming no optimizations by LLVM occur). This is unacceptable for statics.
130+
/// So for statics we do want to use LLVM aliases, which is fine,
131+
/// since for those we don't care about target architecture anyway.
132+
///
133+
/// So, this function is for static aliases. See [`add_function_aliases`](Self::add_function_aliases) for the alternative.
134+
fn add_static_aliases(&self, aliasee: &llvm::Value, aliases: &[(DefId, Linkage, Visibility)]) {
87135
let ty = self.get_type_of_global(aliasee);
88136

89137
for (alias, linkage, visibility) in aliases {
90138
let symbol_name = self.tcx.symbol_name(Instance::mono(self.tcx, *alias));
139+
tracing::debug!("STATIC ALIAS: {alias:?} {linkage:?} {visibility:?}");
91140

92-
tracing::debug!("ALIAS: {alias:?} {linkage:?} {visibility:?}");
93141
let lldecl = llvm::add_alias(
94142
self.llmod,
95143
ty,
@@ -103,6 +151,61 @@ impl CodegenCx<'_, '_> {
103151
}
104152
}
105153

154+
/// See [`add_static_aliases`](Self::add_static_aliases) for docs.
155+
fn add_function_aliases(
156+
&self,
157+
aliasee_instance: Instance<'tcx>,
158+
aliasee: &'ll llvm::Value,
159+
attrs: &Cow<'_, CodegenFnAttrs>,
160+
aliases: &[(DefId, Linkage, Visibility)],
161+
) {
162+
for (alias, linkage, visibility) in aliases {
163+
let symbol_name = self.tcx.symbol_name(Instance::mono(self.tcx, *alias));
164+
tracing::debug!("FUNCTION ALIAS: {alias:?} {linkage:?} {visibility:?}");
165+
166+
// predefine another copy of the original instance
167+
// with a new symbol name
168+
let alias_lldecl = self.predefine_without_aliases(
169+
aliasee_instance,
170+
attrs,
171+
*linkage,
172+
*visibility,
173+
symbol_name.name,
174+
);
175+
176+
let fn_abi: &FnAbi<'tcx, Ty<'tcx>> =
177+
self.fn_abi_of_instance(aliasee_instance, ty::List::empty());
178+
179+
// both the alias and the aliasee have the same ty
180+
let fn_ty = fn_abi.llvm_type(self);
181+
let start_llbb = Builder::append_block(self, alias_lldecl, "start");
182+
let mut start_bx = Builder::build(self, start_llbb);
183+
184+
let num_params = llvm::count_params(alias_lldecl);
185+
let mut args = Vec::with_capacity(num_params as usize);
186+
for index in 0..num_params {
187+
args.push(llvm::get_param(alias_lldecl, index));
188+
}
189+
190+
let call = start_bx.call(
191+
fn_ty,
192+
Some(attrs),
193+
Some(fn_abi),
194+
aliasee,
195+
&args,
196+
None,
197+
Some(aliasee_instance),
198+
);
199+
200+
match &fn_abi.ret.mode {
201+
PassMode::Ignore | PassMode::Indirect { .. } => start_bx.ret_void(),
202+
PassMode::Direct(_) | PassMode::Pair { .. } | PassMode::Cast { .. } => {
203+
start_bx.ret(call)
204+
}
205+
}
206+
}
207+
}
208+
106209
/// A definition or declaration can be assumed to be local to a group of
107210
/// libraries that form a single DSO or executable.
108211
/// Marks the local as DSO if so.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
//@ no-prefer-dynamic
2+
//@ needs-unwind
3+
//@ exec-env:RUST_BACKTRACE=1
4+
#![crate_type = "rlib"]
5+
#![feature(extern_item_impls)]
6+
7+
#[eii(eii1)]
8+
pub fn decl1(x: u64) {
9+
panic!("{}", x);
10+
}

‎tests/ui/eii/default/call_default.rs‎

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,6 @@
55
//@ ignore-backends: gcc
66
// FIXME: linking on windows (speciifcally mingw) not yet supported, see tracking issue #125418
77
//@ ignore-windows
8-
// Functions can have target-cpu applied. On apple-darwin this is super important,
9-
// since you can have binaries which mix x86 and aarch64 code that are compatible
10-
// with both architectures. So we can't just reject target_cpu on EIIs since apple
11-
// puts them on by default. The problem: we generate aliases. And aliases cannot
12-
// get target_cpu applied to them. So, instead we should, in the case of functions,
13-
// generate a shim function. For statics aliases should keep working in theory.
14-
// In fact, aliases are only necessary for statics. For functions we could just
15-
// always generate a shim and a previous version of EII did so but I was sad
16-
// that that'd never support statics.
17-
//@ ignore-macos
188
// Tests EIIs with default implementations.
199
// When there's no explicit declaration, the default should be called from the declaring crate.
2010
#![feature(extern_item_impls)]
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//@ no-prefer-dynamic
2+
//@ aux-build: decl_with_default_panics.rs
3+
//@ edition: 2021
4+
//@ run-pass
5+
//@ needs-unwind
6+
//@ exec-env:RUST_BACKTRACE=1
7+
//@ ignore-backends: gcc
8+
// FIXME: linking on windows (speciifcally mingw) not yet supported, see tracking issue #125418
9+
//@ ignore-windows
10+
// A small test to make sure that unwinding works properly.
11+
//
12+
// Functions can have target-cpu applied. On apple-darwin this is super important,
13+
// since you can have binaries which mix x86 and aarch64 code that are compatible
14+
// with both architectures. So we can't just reject target_cpu on EIIs since apple
15+
// puts them on by default. The problem: we generate aliases. And aliases cannot
16+
// get target_cpu applied to them. So, instead we should, in the case of functions,
17+
// generate a shim function. For statics aliases should keep working.
18+
// However, to make this work properly,
19+
// on LLVM we generate shim functions instead of function aliases.
20+
// Little extra functions that look like
21+
// ```
22+
// function alias_symbol(*args) {return (tailcall) aliasee(*args);}
23+
// ```
24+
// This is a simple test to make sure that we can unwind through these,
25+
// and that this wrapper function effectively doesn't show up in the trace.
26+
#![feature(extern_item_impls)]
27+
28+
extern crate decl_with_default_panics;
29+
30+
fn main() {
31+
let result = std::panic::catch_unwind(|| {
32+
decl_with_default_panics::decl1(10);
33+
});
34+
assert!(result.is_err());
35+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
2+
thread 'main' ($TID) panicked at $DIR/auxiliary/decl_with_default_panics.rs:14:5:
3+
10
4+
stack backtrace:
5+
0: __rustc::rust_begin_unwind
6+
1: core::panicking::panic_fmt
7+
2: core::panicking::panic_display::<u64>
8+
3: decl_with_default_panics::_::decl1
9+
4: call_default_panics::main
10+
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

0 commit comments

Comments
 (0)