Skip to content

Commit e72b11a

Browse files
committed
codegen: do not set attributes on foreign function imports
1 parent 6106b05 commit e72b11a

File tree

8 files changed

+132
-47
lines changed

8 files changed

+132
-47
lines changed

compiler/rustc_codegen_llvm/src/abi.rs

+89-27
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,14 @@ use smallvec::SmallVec;
2727
use std::cmp;
2828

2929
pub trait ArgAttributesExt {
30-
fn apply_attrs_to_llfn(&self, idx: AttributePlace, cx: &CodegenCx<'_, '_>, llfn: &Value);
30+
fn apply_attrs_to_llfn(
31+
&self,
32+
idx: AttributePlace,
33+
cx: &CodegenCx<'_, '_>,
34+
llfn: &Value,
35+
is_foreign_fn: bool,
36+
force_set_align: bool,
37+
);
3138
fn apply_attrs_to_callsite(
3239
&self,
3340
idx: AttributePlace,
@@ -47,7 +54,16 @@ const OPTIMIZATION_ATTRIBUTES: [(ArgAttribute, llvm::AttributeKind); 5] = [
4754
(ArgAttribute::NoUndef, llvm::AttributeKind::NoUndef),
4855
];
4956

50-
fn get_attrs<'ll>(this: &ArgAttributes, cx: &CodegenCx<'ll, '_>) -> SmallVec<[&'ll Attribute; 8]> {
57+
/// `is_foreign_fn_decl` indicates whether the attributes should be computed for a foreign function declaration,
58+
/// where we emit non-essential attributes.
59+
/// See <https://github.com/rust-lang/rust/issues/46188> for background and discussion.
60+
/// `force_set_align` indicates whether the alignment is ABI-relevant.
61+
fn get_attrs<'ll>(
62+
this: &ArgAttributes,
63+
cx: &CodegenCx<'ll, '_>,
64+
is_foreign_fn_decl: bool,
65+
force_set_align: bool,
66+
) -> SmallVec<[&'ll Attribute; 8]> {
5167
let mut regular = this.regular;
5268

5369
let mut attrs = SmallVec::new();
@@ -58,15 +74,31 @@ fn get_attrs<'ll>(this: &ArgAttributes, cx: &CodegenCx<'ll, '_>) -> SmallVec<[&'
5874
attrs.push(llattr.create_attr(cx.llcx));
5975
}
6076
}
61-
if let Some(align) = this.pointee_align {
62-
attrs.push(llvm::CreateAlignmentAttr(cx.llcx, align.bytes()));
77+
if force_set_align {
78+
if let Some(align) = this.pointee_align {
79+
attrs.push(llvm::CreateAlignmentAttr(cx.llcx, align.bytes()));
80+
}
6381
}
6482
match this.arg_ext {
6583
ArgExtension::None => {}
6684
ArgExtension::Zext => attrs.push(llvm::AttributeKind::ZExt.create_attr(cx.llcx)),
6785
ArgExtension::Sext => attrs.push(llvm::AttributeKind::SExt.create_attr(cx.llcx)),
6886
}
6987

88+
// For foreign function declarations, this is it.
89+
if is_foreign_fn_decl {
90+
return attrs;
91+
}
92+
93+
// If we didn't add alignment yet, do it now.
94+
// HACK: this should really be inside the "only if optimizations" below,
95+
// but to avoid tedious codegen test changes, we have it here.
96+
if !force_set_align {
97+
if let Some(align) = this.pointee_align {
98+
attrs.push(llvm::CreateAlignmentAttr(cx.llcx, align.bytes()));
99+
}
100+
}
101+
70102
// Only apply remaining attributes when optimizing
71103
if cx.sess().opts.optimize != config::OptLevel::No {
72104
let deref = this.pointee_size.bytes();
@@ -96,8 +128,15 @@ fn get_attrs<'ll>(this: &ArgAttributes, cx: &CodegenCx<'ll, '_>) -> SmallVec<[&'
96128
}
97129

98130
impl ArgAttributesExt for ArgAttributes {
99-
fn apply_attrs_to_llfn(&self, idx: AttributePlace, cx: &CodegenCx<'_, '_>, llfn: &Value) {
100-
let attrs = get_attrs(self, cx);
131+
fn apply_attrs_to_llfn(
132+
&self,
133+
idx: AttributePlace,
134+
cx: &CodegenCx<'_, '_>,
135+
llfn: &Value,
136+
is_foreign_fn: bool,
137+
force_set_align: bool,
138+
) {
139+
let attrs = get_attrs(self, cx, is_foreign_fn, force_set_align);
101140
attributes::apply_to_llfn(llfn, idx, &attrs);
102141
}
103142

@@ -107,7 +146,10 @@ impl ArgAttributesExt for ArgAttributes {
107146
cx: &CodegenCx<'_, '_>,
108147
callsite: &Value,
109148
) {
110-
let attrs = get_attrs(self, cx);
149+
// It's not a declaration at all, so in particular not a foreign function declaration.
150+
// We also don't need to force setting the alignment.
151+
let attrs =
152+
get_attrs(self, cx, /*is_foreign_fn_decl*/ false, /*force_set_align*/ false);
111153
attributes::apply_to_callsite(callsite, idx, &attrs);
112154
}
113155
}
@@ -310,7 +352,7 @@ pub trait FnAbiLlvmExt<'ll, 'tcx> {
310352
fn llvm_type(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type;
311353
fn ptr_to_llvm_type(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type;
312354
fn llvm_cconv(&self) -> llvm::CallConv;
313-
fn apply_attrs_llfn(&self, cx: &CodegenCx<'ll, 'tcx>, llfn: &'ll Value);
355+
fn apply_attrs_llfn(&self, cx: &CodegenCx<'ll, 'tcx>, llfn: &'ll Value, is_foreign_fn: bool);
314356
fn apply_attrs_callsite(&self, bx: &mut Builder<'_, 'll, 'tcx>, callsite: &'ll Value);
315357
}
316358

@@ -396,32 +438,46 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
396438
self.conv.into()
397439
}
398440

399-
fn apply_attrs_llfn(&self, cx: &CodegenCx<'ll, 'tcx>, llfn: &'ll Value) {
441+
fn apply_attrs_llfn(&self, cx: &CodegenCx<'ll, 'tcx>, llfn: &'ll Value, is_foreign_fn: bool) {
400442
let mut func_attrs = SmallVec::<[_; 3]>::new();
401-
if self.ret.layout.abi.is_uninhabited() {
402-
func_attrs.push(llvm::AttributeKind::NoReturn.create_attr(cx.llcx));
403-
}
404-
if !self.can_unwind {
405-
func_attrs.push(llvm::AttributeKind::NoUnwind.create_attr(cx.llcx));
443+
if !is_foreign_fn {
444+
if self.ret.layout.abi.is_uninhabited() {
445+
func_attrs.push(llvm::AttributeKind::NoReturn.create_attr(cx.llcx));
446+
}
447+
if !self.can_unwind {
448+
func_attrs.push(llvm::AttributeKind::NoUnwind.create_attr(cx.llcx));
449+
}
406450
}
407451
if let Conv::RiscvInterrupt { kind } = self.conv {
408452
func_attrs.push(llvm::CreateAttrStringValue(cx.llcx, "interrupt", kind.as_str()));
409453
}
410454
attributes::apply_to_llfn(llfn, llvm::AttributePlace::Function, &{ func_attrs });
411455

412456
let mut i = 0;
413-
let mut apply = |attrs: &ArgAttributes| {
414-
attrs.apply_attrs_to_llfn(llvm::AttributePlace::Argument(i), cx, llfn);
457+
let mut apply_next_arg = |attrs: &ArgAttributes, force_set_align: bool| {
458+
attrs.apply_attrs_to_llfn(
459+
llvm::AttributePlace::Argument(i),
460+
cx,
461+
llfn,
462+
is_foreign_fn,
463+
force_set_align,
464+
);
415465
i += 1;
416466
i - 1
417467
};
418468
match &self.ret.mode {
419469
PassMode::Direct(attrs) => {
420-
attrs.apply_attrs_to_llfn(llvm::AttributePlace::ReturnValue, cx, llfn);
470+
attrs.apply_attrs_to_llfn(
471+
llvm::AttributePlace::ReturnValue,
472+
cx,
473+
llfn,
474+
is_foreign_fn,
475+
/*force_set_align*/ false,
476+
);
421477
}
422478
PassMode::Indirect { attrs, meta_attrs: _, on_stack } => {
423479
assert!(!on_stack);
424-
let i = apply(attrs);
480+
let i = apply_next_arg(attrs, /*force_set_align*/ false);
425481
let sret = llvm::CreateStructRetAttr(
426482
cx.llcx,
427483
cx.type_array(cx.type_i8(), self.ret.layout.size.bytes()),
@@ -441,15 +497,21 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
441497
}
442498
}
443499
PassMode::Cast { cast, pad_i32: _ } => {
444-
cast.attrs.apply_attrs_to_llfn(llvm::AttributePlace::ReturnValue, cx, llfn);
500+
cast.attrs.apply_attrs_to_llfn(
501+
llvm::AttributePlace::ReturnValue,
502+
cx,
503+
llfn,
504+
is_foreign_fn,
505+
/*force_set_align*/ false,
506+
);
445507
}
446508
_ => {}
447509
}
448510
for arg in self.args.iter() {
449511
match &arg.mode {
450512
PassMode::Ignore => {}
451513
PassMode::Indirect { attrs, meta_attrs: None, on_stack: true } => {
452-
let i = apply(attrs);
514+
let i = apply_next_arg(attrs, /*force_set_align*/ true);
453515
let byval = llvm::CreateByValAttr(
454516
cx.llcx,
455517
cx.type_array(cx.type_i8(), arg.layout.size.bytes()),
@@ -458,22 +520,22 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
458520
}
459521
PassMode::Direct(attrs)
460522
| PassMode::Indirect { attrs, meta_attrs: None, on_stack: false } => {
461-
apply(attrs);
523+
apply_next_arg(attrs, /*force_set_align*/ false);
462524
}
463525
PassMode::Indirect { attrs, meta_attrs: Some(meta_attrs), on_stack } => {
464526
assert!(!on_stack);
465-
apply(attrs);
466-
apply(meta_attrs);
527+
apply_next_arg(attrs, /*force_set_align*/ false);
528+
apply_next_arg(meta_attrs, /*force_set_align*/ false);
467529
}
468530
PassMode::Pair(a, b) => {
469-
apply(a);
470-
apply(b);
531+
apply_next_arg(a, /*force_set_align*/ false);
532+
apply_next_arg(b, /*force_set_align*/ false);
471533
}
472534
PassMode::Cast { cast, pad_i32 } => {
473535
if *pad_i32 {
474-
apply(&ArgAttributes::new());
536+
apply_next_arg(&ArgAttributes::new(), /*force_set_align*/ false);
475537
}
476-
apply(&cast.attrs);
538+
apply_next_arg(&cast.attrs, /*force_set_align*/ false);
477539
}
478540
}
479541
}

compiler/rustc_codegen_llvm/src/declare.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,12 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
137137
llvm::Visibility::Default,
138138
fn_abi.llvm_type(self),
139139
);
140-
fn_abi.apply_attrs_llfn(self, llfn);
140+
// If this is for a foreign instance, do *not* add the attributes.
141+
// There will be no body attached to this, and having attributes attached to a declaration
142+
// means that if there are multiple declarations in different modules, one of them will win
143+
// and be used for *all* modules, even though it may never be called!
144+
let is_foreign_fn = instance.is_some_and(|i| self.tcx.is_foreign_item(i.def_id()));
145+
fn_abi.apply_attrs_llfn(self, llfn, is_foreign_fn);
141146

142147
if self.tcx.sess.is_sanitizer_cfi_enabled() {
143148
if let Some(instance) = instance {

tests/codegen/align-byval.rs

-12
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,6 @@ extern "C" {
223223

224224
// x86_64-windows: declare void @natural_align_2(
225225
// x86_64-windows-NOT: byval
226-
// x86_64-windows-SAME: align 2{{.*}})
227226

228227
// i686-linux: declare void @natural_align_2({{.*}}byval([34 x i8]) align 4{{.*}})
229228

@@ -238,7 +237,6 @@ extern "C" {
238237

239238
// x86_64-windows: declare void @force_align_4(
240239
// x86_64-windows-NOT: byval
241-
// x86_64-windows-SAME: align 4{{.*}})
242240

243241
// i686-linux: declare void @force_align_4({{.*}}byval([20 x i8]) align 4{{.*}})
244242

@@ -253,7 +251,6 @@ extern "C" {
253251

254252
// x86_64-windows: declare void @natural_align_8(
255253
// x86_64-windows-NOT: byval
256-
// x86_64-windows-SAME: align 8{{.*}})
257254

258255
// i686-linux: declare void @natural_align_8({{.*}}byval([24 x i8]) align 4{{.*}})
259256

@@ -268,13 +265,11 @@ extern "C" {
268265

269266
// x86_64-windows: declare void @force_align_8(
270267
// x86_64-windows-NOT: byval
271-
// x86_64-windows-SAME: align 8{{.*}})
272268

273269
// i686-linux: declare void @force_align_8({{.*}}byval([24 x i8]) align 4{{.*}})
274270

275271
// i686-windows: declare void @force_align_8(
276272
// i686-windows-NOT: byval
277-
// i686-windows-SAME: align 8{{.*}})
278273
fn force_align_8(x: ForceAlign8);
279274

280275
// m68k: declare void @lower_fa8({{.*}}byval([24 x i8]) align 4{{.*}})
@@ -285,7 +280,6 @@ extern "C" {
285280

286281
// x86_64-windows: declare void @lower_fa8(
287282
// x86_64-windows-NOT: byval
288-
// x86_64-windows-SAME: align 8{{.*}})
289283

290284
// i686-linux: declare void @lower_fa8({{.*}}byval([24 x i8]) align 4{{.*}})
291285

@@ -300,13 +294,11 @@ extern "C" {
300294

301295
// x86_64-windows: declare void @wrapped_fa8(
302296
// x86_64-windows-NOT: byval
303-
// x86_64-windows-SAME: align 8{{.*}})
304297

305298
// i686-linux: declare void @wrapped_fa8({{.*}}byval([24 x i8]) align 4{{.*}})
306299

307300
// i686-windows: declare void @wrapped_fa8(
308301
// i686-windows-NOT: byval
309-
// i686-windows-SAME: align 8{{.*}})
310302
fn wrapped_fa8(x: WrappedFA8);
311303

312304
// m68k: declare void @transparent_fa8({{.*}}byval([24 x i8]) align 8{{.*}})
@@ -317,13 +309,11 @@ extern "C" {
317309

318310
// x86_64-windows: declare void @transparent_fa8(
319311
// x86_64-windows-NOT: byval
320-
// x86_64-windows-SAME: align 8{{.*}})
321312

322313
// i686-linux: declare void @transparent_fa8({{.*}}byval([24 x i8]) align 4{{.*}})
323314

324315
// i686-windows: declare void @transparent_fa8(
325316
// i686-windows-NOT: byval
326-
// i686-windows-SAME: align 8{{.*}})
327317
fn transparent_fa8(x: TransparentFA8);
328318

329319
// m68k: declare void @force_align_16({{.*}}byval([80 x i8]) align 16{{.*}})
@@ -334,12 +324,10 @@ extern "C" {
334324

335325
// x86_64-windows: declare void @force_align_16(
336326
// x86_64-windows-NOT: byval
337-
// x86_64-windows-SAME: align 16{{.*}})
338327

339328
// i686-linux: declare void @force_align_16({{.*}}byval([80 x i8]) align 4{{.*}})
340329

341330
// i686-windows: declare void @force_align_16(
342331
// i686-windows-NOT: byval
343-
// i686-windows-SAME: align 16{{.*}})
344332
fn force_align_16(x: ForceAlign16);
345333
}

tests/codegen/box-uninit-bytes.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,6 @@ pub fn box_lotsa_padding() -> Box<LotsaPadding> {
4141

4242
// Hide the `allocalign` attribute in the declaration of __rust_alloc
4343
// from the CHECK-NOT above, and also verify the attributes got set reasonably.
44-
// CHECK: declare {{(dso_local )?}}noalias noundef ptr @__rust_alloc(i{{[0-9]+}} noundef, i{{[0-9]+}} allocalign noundef) unnamed_addr [[RUST_ALLOC_ATTRS:#[0-9]+]]
44+
// CHECK: declare {{(dso_local )?}}noalias ptr @__rust_alloc(i{{[0-9]+}}, i{{[0-9]+}} allocalign) unnamed_addr [[RUST_ALLOC_ATTRS:#[0-9]+]]
4545

4646
// CHECK-DAG: attributes [[RUST_ALLOC_ATTRS]] = { {{.*}} allockind("alloc,uninitialized,aligned") allocsize(0) {{(uwtable )?}}"alloc-family"="__rust_alloc" {{.*}} }

tests/codegen/declare-no-attrs.rs

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//@ compile-flags: -C opt-level=1
2+
3+
#![no_builtins]
4+
#![crate_type = "lib"]
5+
6+
// Make sure that there are no attributes attached to this delcaration.
7+
// If there were, they could be used even for other call sites using a different
8+
// item importing the same function!
9+
// See <https://github.com/rust-lang/rust/issues/46188> for details.
10+
#[allow(improper_ctypes)]
11+
extern "C" {
12+
// CHECK: declare ptr @malloc(i64)
13+
pub fn malloc(x: u64) -> &'static mut ();
14+
}
15+
16+
// `malloc` needs to be referenced or else it will disappear entirely.
17+
#[no_mangle]
18+
pub fn use_it() -> usize {
19+
let m: unsafe extern "C" fn(_) -> _ = malloc;
20+
malloc as *const () as usize
21+
}

tests/codegen/no_builtins-at-crate.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@ pub unsafe extern "C" fn __aeabi_memcpy(dest: *mut u8, src: *const u8, size: usi
1515

1616
// CHECK: declare
1717
// CHECK-SAME: @memcpy
18-
// CHECK-SAME: #0
18+
// CHECK-SAME: #1
1919
extern "C" {
2020
pub fn memcpy(dest: *mut u8, src: *const u8, n: usize) -> *mut u8;
2121
}
2222

2323
// CHECK: attributes #0
2424
// CHECK-SAME: "no-builtins"
25+
// CHECK: attributes #1
26+
// CHECK-SAME: "no-builtins"

tests/codegen/unwind-extern-imports.rs

+11-4
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,25 @@
44
#![crate_type = "lib"]
55

66
extern "C" {
7-
// CHECK: Function Attrs:{{.*}}nounwind
8-
// CHECK-NEXT: declare{{.*}}void @extern_fn
97
fn extern_fn();
108
}
119

1210
extern "C-unwind" {
13-
// CHECK-NOT: nounwind
14-
// CHECK: declare{{.*}}void @c_unwind_extern_fn
1511
fn c_unwind_extern_fn();
1612
}
1713

14+
// The attributes are at the call sites, not the declaration.
15+
16+
// CHECK-LABEL: @force_declare
17+
#[no_mangle]
1818
pub unsafe fn force_declare() {
19+
// Attributes with `nounwind` here (also see check below).
20+
// CHECK: call void @extern_fn() #1
1921
extern_fn();
22+
// No attributes here.
23+
// CHECK: call void @c_unwind_extern_fn()
2024
c_unwind_extern_fn();
2125
}
26+
27+
// CHECK: attributes #1
28+
// CHECK-SAME: nounwind

0 commit comments

Comments
 (0)