Skip to content

Commit 470e191

Browse files
committed
declare imported functions with a nondescript signature
This means when the function is imported with multiple different signatures, they don't clash
1 parent f04f6ca commit 470e191

12 files changed

+112
-39
lines changed

compiler/rustc_ast_passes/src/feature_gate.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
324324
&self,
325325
link_llvm_intrinsics,
326326
i.span,
327-
"linking to LLVM intrinsics is experimental"
327+
"linking to LLVM intrinsics is an internal feature reserved for the standard library"
328328
);
329329
}
330330
}

compiler/rustc_codegen_llvm/src/abi.rs

+22-3
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,12 @@ pub trait FnAbiLlvmExt<'ll, 'tcx> {
321321
);
322322

323323
/// Apply attributes to a function call.
324-
fn apply_attrs_callsite(&self, bx: &mut Builder<'_, 'll, 'tcx>, callsite: &'ll Value);
324+
fn apply_attrs_callsite(
325+
&self,
326+
bx: &mut Builder<'_, 'll, 'tcx>,
327+
callsite: &'ll Value,
328+
instance: Option<ty::Instance<'tcx>>,
329+
);
325330
}
326331

327332
impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
@@ -527,11 +532,18 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
527532

528533
// If the declaration has an associated instance, compute extra attributes based on that.
529534
if let Some(instance) = instance {
530-
llfn_attrs_from_instance(cx, llfn, instance);
535+
llfn_attrs_from_instance(cx, instance, Some(llfn), |place, attrs| {
536+
attributes::apply_to_llfn(llfn, place, attrs)
537+
});
531538
}
532539
}
533540

534-
fn apply_attrs_callsite(&self, bx: &mut Builder<'_, 'll, 'tcx>, callsite: &'ll Value) {
541+
fn apply_attrs_callsite(
542+
&self,
543+
bx: &mut Builder<'_, 'll, 'tcx>,
544+
callsite: &'ll Value,
545+
instance: Option<ty::Instance<'tcx>>,
546+
) {
535547
let mut func_attrs = SmallVec::<[_; 2]>::new();
536548
if self.ret.layout.abi.is_uninhabited() {
537549
func_attrs.push(llvm::AttributeKind::NoReturn.create_attr(bx.cx.llcx));
@@ -649,6 +661,13 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
649661
&[element_type_attr],
650662
);
651663
}
664+
665+
// If the call site has an associated instance, compute extra attributes based on that.
666+
if let Some(instance) = instance {
667+
llfn_attrs_from_instance(bx.cx, instance, None, |place, attrs| {
668+
attributes::apply_to_callsite(callsite, place, attrs)
669+
});
670+
}
652671
}
653672
}
654673

compiler/rustc_codegen_llvm/src/attributes.rs

+18-11
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ use crate::context::CodegenCx;
1414
use crate::errors::{MissingFeatures, SanitizerMemtagRequiresMte, TargetFeatureDisableOrEnable};
1515
use crate::llvm::AttributePlace::Function;
1616
use crate::llvm::{self, AllocKindFlags, Attribute, AttributeKind, AttributePlace, MemoryEffects};
17+
use crate::llvm_util;
1718
use crate::value::Value;
18-
use crate::{attributes, llvm_util};
1919

2020
pub fn apply_to_llfn(llfn: &Value, idx: AttributePlace, attrs: &[&Attribute]) {
2121
if !attrs.is_empty() {
@@ -324,13 +324,18 @@ fn create_alloc_family_attr(llcx: &llvm::Context) -> &llvm::Attribute {
324324
llvm::CreateAttrStringValue(llcx, "alloc-family", "__rust_alloc")
325325
}
326326

327-
/// Helper for `FnAbi::apply_attrs_llfn`:
327+
/// Helper for `FnAbi::apply_attrs_*`:
328328
/// Composite function which sets LLVM attributes for function depending on its AST (`#[attribute]`)
329329
/// attributes.
330+
///
331+
/// `apply_attrs` is called to apply the attributes, so this can be used both for declarations and calls.
332+
/// However, some things are not represented as attributes and and only be set on declarations, so
333+
/// `declare_llfn` should be `Some` if this is a declaration.
330334
pub fn llfn_attrs_from_instance<'ll, 'tcx>(
331335
cx: &CodegenCx<'ll, 'tcx>,
332-
llfn: &'ll Value,
333336
instance: ty::Instance<'tcx>,
337+
declare_llfn: Option<&'ll Value>,
338+
apply_attrs: impl Fn(AttributePlace, &[&Attribute]),
334339
) {
335340
let codegen_fn_attrs = cx.tcx.codegen_fn_attrs(instance.def_id());
336341

@@ -440,7 +445,7 @@ pub fn llfn_attrs_from_instance<'ll, 'tcx>(
440445
to_add.push(create_alloc_family_attr(cx.llcx));
441446
// apply to argument place instead of function
442447
let alloc_align = AttributeKind::AllocAlign.create_attr(cx.llcx);
443-
attributes::apply_to_llfn(llfn, AttributePlace::Argument(1), &[alloc_align]);
448+
apply_attrs(AttributePlace::Argument(1), &[alloc_align]);
444449
to_add.push(llvm::CreateAllocSizeAttr(cx.llcx, 0));
445450
let mut flags = AllocKindFlags::Alloc | AllocKindFlags::Aligned;
446451
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR) {
@@ -451,7 +456,7 @@ pub fn llfn_attrs_from_instance<'ll, 'tcx>(
451456
to_add.push(llvm::CreateAllocKindAttr(cx.llcx, flags));
452457
// apply to return place instead of function (unlike all other attributes applied in this function)
453458
let no_alias = AttributeKind::NoAlias.create_attr(cx.llcx);
454-
attributes::apply_to_llfn(llfn, AttributePlace::ReturnValue, &[no_alias]);
459+
apply_attrs(AttributePlace::ReturnValue, &[no_alias]);
455460
}
456461
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::REALLOCATOR) {
457462
to_add.push(create_alloc_family_attr(cx.llcx));
@@ -461,26 +466,28 @@ pub fn llfn_attrs_from_instance<'ll, 'tcx>(
461466
));
462467
// applies to argument place instead of function place
463468
let allocated_pointer = AttributeKind::AllocatedPointer.create_attr(cx.llcx);
464-
attributes::apply_to_llfn(llfn, AttributePlace::Argument(0), &[allocated_pointer]);
469+
apply_attrs(AttributePlace::Argument(0), &[allocated_pointer]);
465470
// apply to argument place instead of function
466471
let alloc_align = AttributeKind::AllocAlign.create_attr(cx.llcx);
467-
attributes::apply_to_llfn(llfn, AttributePlace::Argument(2), &[alloc_align]);
472+
apply_attrs(AttributePlace::Argument(2), &[alloc_align]);
468473
to_add.push(llvm::CreateAllocSizeAttr(cx.llcx, 3));
469474
let no_alias = AttributeKind::NoAlias.create_attr(cx.llcx);
470-
attributes::apply_to_llfn(llfn, AttributePlace::ReturnValue, &[no_alias]);
475+
apply_attrs(AttributePlace::ReturnValue, &[no_alias]);
471476
}
472477
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::DEALLOCATOR) {
473478
to_add.push(create_alloc_family_attr(cx.llcx));
474479
to_add.push(llvm::CreateAllocKindAttr(cx.llcx, AllocKindFlags::Free));
475480
// applies to argument place instead of function place
476481
let allocated_pointer = AttributeKind::AllocatedPointer.create_attr(cx.llcx);
477-
attributes::apply_to_llfn(llfn, AttributePlace::Argument(0), &[allocated_pointer]);
482+
apply_attrs(AttributePlace::Argument(0), &[allocated_pointer]);
478483
}
479484
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::CMSE_NONSECURE_ENTRY) {
480485
to_add.push(llvm::CreateAttrString(cx.llcx, "cmse_nonsecure_entry"));
481486
}
482487
if let Some(align) = codegen_fn_attrs.alignment {
483-
llvm::set_alignment(llfn, align);
488+
if let Some(llfn) = declare_llfn {
489+
llvm::set_alignment(llfn, align);
490+
}
484491
}
485492
if let Some(backchain) = backchain_attr(cx) {
486493
to_add.push(backchain);
@@ -551,7 +558,7 @@ pub fn llfn_attrs_from_instance<'ll, 'tcx>(
551558
to_add.push(llvm::CreateAttrStringValue(cx.llcx, "target-features", &target_features));
552559
}
553560

554-
attributes::apply_to_llfn(llfn, Function, &to_add);
561+
apply_attrs(Function, &to_add);
555562
}
556563

557564
fn wasm_import_module(tcx: TyCtxt<'_>, id: DefId) -> Option<&String> {

compiler/rustc_codegen_llvm/src/builder.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
262262
)
263263
};
264264
if let Some(fn_abi) = fn_abi {
265-
fn_abi.apply_attrs_callsite(self, invoke);
265+
fn_abi.apply_attrs_callsite(self, invoke, instance);
266266
}
267267
invoke
268268
}
@@ -1307,7 +1307,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
13071307
)
13081308
};
13091309
if let Some(fn_abi) = fn_abi {
1310-
fn_abi.apply_attrs_callsite(self, call);
1310+
fn_abi.apply_attrs_callsite(self, call, instance);
13111311
}
13121312
call
13131313
}
@@ -1645,7 +1645,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
16451645
)
16461646
};
16471647
if let Some(fn_abi) = fn_abi {
1648-
fn_abi.apply_attrs_callsite(self, callbr);
1648+
fn_abi.apply_attrs_callsite(self, callbr, instance);
16491649
}
16501650
callbr
16511651
}

compiler/rustc_codegen_llvm/src/declare.rs

+43-1
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@
1212
//! * When in doubt, define.
1313
1414
use itertools::Itertools;
15-
use rustc_codegen_ssa::traits::TypeMembershipMethods;
15+
use rustc_codegen_ssa::traits::{BaseTypeMethods, TypeMembershipMethods};
1616
use rustc_data_structures::fx::FxIndexSet;
17+
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
1718
use rustc_middle::ty::{Instance, Ty};
1819
use rustc_sanitizers::{cfi, kcfi};
1920
use smallvec::SmallVec;
@@ -127,6 +128,47 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
127128
) -> &'ll Value {
128129
debug!("declare_rust_fn(name={:?}, fn_abi={:?})", name, fn_abi);
129130

131+
// FFI imports need to be treated specially: in Rust one can import the same function
132+
// with different signatures, but LLVM can only import each function once. Within a
133+
// codegen unit, whatever declaration we set up last will "win"; across codegen units,
134+
// LLVM is doing some sort of unspecified merging as part of LTO.
135+
// This is partially unsound due to LLVM bugs (https://github.com/llvm/llvm-project/issues/58976),
136+
// but on the Rust side we try to ensure soundness by making the least amount of promises
137+
// for these imports: no matter the signatures, we declare all functions as `void fn()`.
138+
// If the same symbol is declared both as a function and a static, we just hope that
139+
// doesn't lead to unsoundness (or LLVM crashes)...
140+
let ffi_import = instance.is_some_and(|i| {
141+
self.tcx.is_foreign_item(i.def_id()) && {
142+
// If this is a Rust native allocator function, we want its attributes, so we do not
143+
// treat it like the other FFI imports. If a user declares `__rust_alloc` we're going to have
144+
// one import with the attributes and one with the default signature; that should hopefully be fine
145+
// even if LLVM only sees the default one.
146+
let codegen_fn_attrs = self.tcx.codegen_fn_attrs(i.def_id());
147+
let is_alloc_fn = codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR)
148+
|| codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR_ZEROED)
149+
|| codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::REALLOCATOR)
150+
|| codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::DEALLOCATOR);
151+
!is_alloc_fn
152+
}
153+
}) && {
154+
// If this is calling an LLVM intrinsic, we can't use the `global` trick.
155+
// These are also unstable to call so nobody but us can screw up their signature.
156+
let is_llvm_intrinsic = name.starts_with("llvm.");
157+
!is_llvm_intrinsic
158+
};
159+
if ffi_import {
160+
// This signatures is essentially irrelevant since we set all the attributes at the call
161+
// site.
162+
return declare_raw_fn(
163+
self,
164+
name,
165+
llvm::CallConv::CCallConv,
166+
llvm::UnnamedAddr::Global,
167+
llvm::Visibility::Default,
168+
self.type_func(&[], self.type_void()),
169+
);
170+
}
171+
130172
// Function addresses in Rust are never significant, allowing functions to
131173
// be merged.
132174
let llfn = declare_raw_fn(

tests/codegen/box-uninit-bytes.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use std::mem::MaybeUninit;
88
pub fn box_uninitialized() -> Box<MaybeUninit<usize>> {
99
// CHECK-LABEL: @box_uninitialized
1010
// CHECK-NOT: store
11-
// CHECK-NOT: alloca
11+
// CHECK-NOT: "alloca "
1212
// CHECK-NOT: memcpy
1313
// CHECK-NOT: memset
1414
Box::new(MaybeUninit::uninit())
@@ -19,7 +19,7 @@ pub fn box_uninitialized() -> Box<MaybeUninit<usize>> {
1919
pub fn box_uninitialized2() -> Box<MaybeUninit<[usize; 1024 * 1024]>> {
2020
// CHECK-LABEL: @box_uninitialized2
2121
// CHECK-NOT: store
22-
// CHECK-NOT: alloca
22+
// CHECK-NOT: "alloca ""
2323
// CHECK-NOT: memcpy
2424
// CHECK-NOT: memset
2525
Box::new(MaybeUninit::uninit())
@@ -32,7 +32,7 @@ pub struct LotsaPadding(usize);
3232
#[no_mangle]
3333
pub fn box_lotsa_padding() -> Box<LotsaPadding> {
3434
// CHECK-LABEL: @box_lotsa_padding
35-
// CHECK-NOT: alloca
35+
// CHECK-NOT: "alloca "
3636
// CHECK-NOT: getelementptr
3737
// CHECK-NOT: memcpy
3838
// CHECK-NOT: memset

tests/codegen/iter-repeat-n-trivial-drop.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ pub fn iter_repeat_n_next(it: &mut std::iter::RepeatN<NotCopy>) -> Option<NotCop
4747
#[no_mangle]
4848
// CHECK-LABEL: @vec_extend_via_iter_repeat_n
4949
pub fn vec_extend_via_iter_repeat_n() -> Vec<u8> {
50-
// CHECK: %[[ADDR:.+]] = tail call {{(noalias )?}}noundef dereferenceable_or_null(1234) ptr @__rust_alloc(i64 noundef 1234, i64 noundef 1)
50+
// CHECK: %[[ADDR:.+]] = tail call {{(noalias )?}}noundef dereferenceable_or_null(1234) ptr @__rust_alloc(i64 noundef 1234, i64 {{(allocalign )?}}noundef 1)
5151
// CHECK: tail call void @llvm.memset.p0.i64(ptr noundef nonnull align 1 dereferenceable(1234) %[[ADDR]], i8 42, i64 1234,
5252

5353
let n = 1234_usize;

tests/codegen/no_builtins-at-crate.rs

+2-4
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,13 @@
1010
pub unsafe extern "C" fn __aeabi_memcpy(dest: *mut u8, src: *const u8, size: usize) {
1111
// CHECK: call
1212
// CHECK-SAME: @memcpy(
13+
// CHECK-SAME: #2
1314
memcpy(dest, src, size);
1415
}
1516

16-
// CHECK: declare
17-
// CHECK-SAME: @memcpy
18-
// CHECK-SAME: #0
1917
extern "C" {
2018
pub fn memcpy(dest: *mut u8, src: *const u8, n: usize) -> *mut u8;
2119
}
2220

23-
// CHECK: attributes #0
21+
// CHECK: attributes #2
2422
// CHECK-SAME: "no-builtins"

tests/codegen/pic-relocation-model.rs

+1-4
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,7 @@ pub fn call_foreign_fn() -> u8 {
88
unsafe { foreign_fn() }
99
}
1010

11-
// (Allow but do not require `zeroext` here, because it is not worth effort to
12-
// spell out which targets have it and which ones do not; see rust#97800.)
13-
14-
// CHECK: declare{{( zeroext)?}} i8 @foreign_fn()
11+
// CHECK: declare void @foreign_fn()
1512
extern "C" {
1613
fn foreign_fn() -> u8;
1714
}

tests/codegen/pie-relocation-model.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ pub fn call_foreign_fn() -> u8 {
1313

1414
// External functions are still marked as non-dso_local, since we don't know if the symbol
1515
// is defined in the binary or in the shared library.
16-
// CHECK: declare zeroext i8 @foreign_fn()
16+
// CHECK: declare void @foreign_fn()
1717
extern "C" {
1818
fn foreign_fn() -> u8;
1919
}

tests/codegen/unwind-extern-imports.rs

+15-6
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,21 @@
33

44
#![crate_type = "lib"]
55

6+
// The flag are only at the call sites, not at the declarations.
7+
// CHECK-LABEL: @force_declare
8+
#[no_mangle]
9+
pub unsafe fn force_declare() {
10+
// CHECK: call void @extern_fn() [[NOUNWIND_ATTR:#[0-9]+]]
11+
extern_fn();
12+
// Call without attributes.
13+
// FIXME: Make sure that there truly are no attributes! How can we match "end of line" with FileCheck?
14+
// CHECK: call void @c_unwind_extern_fn()
15+
c_unwind_extern_fn();
16+
}
17+
618
extern "C" {
7-
// CHECK: Function Attrs:{{.*}}nounwind
8-
// CHECK-NEXT: declare{{.*}}void @extern_fn
19+
// CHECK-NOT: nounwind
20+
// CHECK: declare{{.*}}void @extern_fn
921
fn extern_fn();
1022
}
1123

@@ -15,7 +27,4 @@ extern "C-unwind" {
1527
fn c_unwind_extern_fn();
1628
}
1729

18-
pub unsafe fn force_declare() {
19-
extern_fn();
20-
c_unwind_extern_fn();
21-
}
30+
// CHECK: attributes [[NOUNWIND_ATTR]] = { {{.*}}nounwind{{.*}} }

tests/codegen/unwind-landingpad-cold.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88

99
// CHECK-LABEL: @check_cold
1010
// CHECK: {{(call|invoke) void .+}}drop_in_place{{.+}} [[ATTRIBUTES:#[0-9]+]]
11-
// CHECK: attributes [[ATTRIBUTES]] = { cold }
11+
// CHECK: attributes [[ATTRIBUTES]]
12+
// CHECK-SAME: cold
1213
#[no_mangle]
1314
pub fn check_cold(f: fn(), x: Box<u32>) {
1415
// this may unwind

0 commit comments

Comments
 (0)