Skip to content

Commit c05b3b9

Browse files
authored
Rollup merge of rust-lang#151733 - jdonszelmann:eii-on-apple, r=oli-obk
Use function shims to make sure EII works on apple targets Use function shims to make sure EII works on apple targets (and generally accepts target-arch attributes) Explainer: rust-lang#146348 (comment) Tracking issue: rust-lang#125418
2 parents 9e79395 + acd2e87 commit c05b3b9

6 files changed

Lines changed: 167 additions & 22 deletions

File tree

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: 108 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;
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;
@@ -45,7 +50,7 @@ impl<'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> {
4550
self.assume_dso_local(g, false);
4651

4752
let attrs = self.tcx.codegen_instance_attrs(instance.def);
48-
self.add_aliases(g, &attrs.foreign_item_symbol_aliases);
53+
self.add_static_aliases(g, &attrs.foreign_item_symbol_aliases);
4954

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

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

85108
self.assume_dso_local(lldecl, false);
86109

87-
self.add_aliases(lldecl, &attrs.foreign_item_symbol_aliases);
88-
89-
self.instances.borrow_mut().insert(instance, lldecl);
110+
lldecl
90111
}
91-
}
92112

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

97145
for (alias, linkage, visibility) in aliases {
98146
let symbol_name = self.tcx.symbol_name(Instance::mono(self.tcx, *alias));
147+
tracing::debug!("STATIC ALIAS: {alias:?} {linkage:?} {visibility:?}");
99148

100-
tracing::debug!("ALIAS: {alias:?} {linkage:?} {visibility:?}");
101149
let lldecl = llvm::add_alias(
102150
self.llmod,
103151
ty,
@@ -111,6 +159,54 @@ impl CodegenCx<'_, '_> {
111159
}
112160
}
113161

162+
/// See [`add_static_aliases`](Self::add_static_aliases) for docs.
163+
fn add_function_aliases(
164+
&self,
165+
aliasee_instance: Instance<'tcx>,
166+
aliasee: &'ll llvm::Value,
167+
attrs: &Cow<'_, CodegenFnAttrs>,
168+
aliases: &[(DefId, Linkage, Visibility)],
169+
) {
170+
for (alias, linkage, visibility) in aliases {
171+
let symbol_name = self.tcx.symbol_name(Instance::mono(self.tcx, *alias));
172+
tracing::debug!("FUNCTION ALIAS: {alias:?} {linkage:?} {visibility:?}");
173+
174+
// predefine another copy of the original instance
175+
// with a new symbol name
176+
let alias_lldecl = self.predefine_without_aliases(
177+
aliasee_instance,
178+
attrs,
179+
*linkage,
180+
*visibility,
181+
symbol_name.name,
182+
);
183+
184+
let fn_abi: &FnAbi<'tcx, Ty<'tcx>> =
185+
self.fn_abi_of_instance(aliasee_instance, ty::List::empty());
186+
187+
// both the alias and the aliasee have the same ty
188+
let fn_ty = fn_abi.llvm_type(self);
189+
let start_llbb = Builder::append_block(self, alias_lldecl, "start");
190+
let mut start_bx = Builder::build(self, start_llbb);
191+
192+
let num_params = llvm::count_params(alias_lldecl);
193+
let mut args = Vec::with_capacity(num_params as usize);
194+
for index in 0..num_params {
195+
args.push(llvm::get_param(alias_lldecl, index));
196+
}
197+
198+
start_bx.tail_call(
199+
fn_ty,
200+
Some(attrs),
201+
fn_abi,
202+
aliasee,
203+
&args,
204+
None,
205+
Some(aliasee_instance),
206+
);
207+
}
208+
}
209+
114210
/// A definition or declaration can be assumed to be local to a group of
115211
/// libraries that form a single DSO or executable.
116212
/// 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)