Skip to content

Commit dcbdc8c

Browse files
At opt-level=0, apply only ABI-affecting attributes to functions
This should provide a small perf improvement for debug builds, and should more than cancel out the regression from adding noundef, which was only significant in debug builds.
1 parent 73a7423 commit dcbdc8c

File tree

2 files changed

+115
-41
lines changed

2 files changed

+115
-41
lines changed

compiler/rustc_codegen_llvm/src/abi.rs

+52-41
Original file line numberDiff line numberDiff line change
@@ -13,34 +13,14 @@ use rustc_middle::bug;
1313
use rustc_middle::ty::layout::LayoutOf;
1414
pub use rustc_middle::ty::layout::{FAT_PTR_ADDR, FAT_PTR_EXTRA};
1515
use rustc_middle::ty::Ty;
16+
use rustc_session::config;
1617
use rustc_target::abi::call::ArgAbi;
1718
pub use rustc_target::abi::call::*;
1819
use rustc_target::abi::{self, HasDataLayout, Int};
1920
pub use rustc_target::spec::abi::Abi;
2021

2122
use libc::c_uint;
2223

23-
macro_rules! for_each_kind {
24-
($flags: ident, $f: ident, $($kind: ident),+) => ({
25-
$(if $flags.contains(ArgAttribute::$kind) { $f(llvm::Attribute::$kind) })+
26-
})
27-
}
28-
29-
trait ArgAttributeExt {
30-
fn for_each_kind<F>(&self, f: F)
31-
where
32-
F: FnMut(llvm::Attribute);
33-
}
34-
35-
impl ArgAttributeExt for ArgAttribute {
36-
fn for_each_kind<F>(&self, mut f: F)
37-
where
38-
F: FnMut(llvm::Attribute),
39-
{
40-
for_each_kind!(self, f, NoAlias, NoCapture, NonNull, ReadOnly, InReg, NoUndef)
41-
}
42-
}
43-
4424
pub trait ArgAttributesExt {
4525
fn apply_attrs_to_llfn(&self, idx: AttributePlace, cx: &CodegenCx<'_, '_>, llfn: &Value);
4626
fn apply_attrs_to_callsite(
@@ -58,10 +38,36 @@ fn should_use_mutable_noalias(cx: &CodegenCx<'_, '_>) -> bool {
5838
cx.tcx.sess.opts.debugging_opts.mutable_noalias.unwrap_or(true)
5939
}
6040

41+
const ABI_AFFECTING_ATTRIBUTES: [(ArgAttribute, llvm::Attribute); 1] =
42+
[(ArgAttribute::InReg, llvm::Attribute::InReg)];
43+
44+
const OPTIMIZATION_ATTRIBUTES: [(ArgAttribute, llvm::Attribute); 5] = [
45+
(ArgAttribute::NoAlias, llvm::Attribute::NoAlias),
46+
(ArgAttribute::NoCapture, llvm::Attribute::NoCapture),
47+
(ArgAttribute::NonNull, llvm::Attribute::NonNull),
48+
(ArgAttribute::ReadOnly, llvm::Attribute::ReadOnly),
49+
(ArgAttribute::NoUndef, llvm::Attribute::NoUndef),
50+
];
51+
6152
impl ArgAttributesExt for ArgAttributes {
6253
fn apply_attrs_to_llfn(&self, idx: AttributePlace, cx: &CodegenCx<'_, '_>, llfn: &Value) {
6354
let mut regular = self.regular;
6455
unsafe {
56+
// ABI-affecting attributes must always be applied
57+
for (attr, llattr) in ABI_AFFECTING_ATTRIBUTES {
58+
if regular.contains(attr) {
59+
llattr.apply_llfn(idx, llfn);
60+
}
61+
}
62+
match self.arg_ext {
63+
ArgExtension::None => {}
64+
ArgExtension::Zext => llvm::Attribute::ZExt.apply_llfn(idx, llfn),
65+
ArgExtension::Sext => llvm::Attribute::SExt.apply_llfn(idx, llfn),
66+
}
67+
// Only apply remaining attributes when optimizing
68+
if cx.sess().opts.optimize == config::OptLevel::No {
69+
return;
70+
}
6571
let deref = self.pointee_size.bytes();
6672
if deref != 0 {
6773
if regular.contains(ArgAttribute::NonNull) {
@@ -74,19 +80,14 @@ impl ArgAttributesExt for ArgAttributes {
7480
if let Some(align) = self.pointee_align {
7581
llvm::LLVMRustAddAlignmentAttr(llfn, idx.as_uint(), align.bytes() as u32);
7682
}
77-
regular.for_each_kind(|attr| attr.apply_llfn(idx, llfn));
83+
for (attr, llattr) in OPTIMIZATION_ATTRIBUTES {
84+
if regular.contains(attr) {
85+
llattr.apply_llfn(idx, llfn);
86+
}
87+
}
7888
if regular.contains(ArgAttribute::NoAliasMutRef) && should_use_mutable_noalias(cx) {
7989
llvm::Attribute::NoAlias.apply_llfn(idx, llfn);
8090
}
81-
match self.arg_ext {
82-
ArgExtension::None => {}
83-
ArgExtension::Zext => {
84-
llvm::Attribute::ZExt.apply_llfn(idx, llfn);
85-
}
86-
ArgExtension::Sext => {
87-
llvm::Attribute::SExt.apply_llfn(idx, llfn);
88-
}
89-
}
9091
}
9192
}
9293

@@ -98,6 +99,21 @@ impl ArgAttributesExt for ArgAttributes {
9899
) {
99100
let mut regular = self.regular;
100101
unsafe {
102+
// ABI-affecting attributes must always be applied
103+
for (attr, llattr) in ABI_AFFECTING_ATTRIBUTES {
104+
if regular.contains(attr) {
105+
llattr.apply_callsite(idx, callsite);
106+
}
107+
}
108+
match self.arg_ext {
109+
ArgExtension::None => {}
110+
ArgExtension::Zext => llvm::Attribute::ZExt.apply_callsite(idx, callsite),
111+
ArgExtension::Sext => llvm::Attribute::SExt.apply_callsite(idx, callsite),
112+
}
113+
// Only apply remaining attributes when optimizing
114+
if cx.sess().opts.optimize == config::OptLevel::No {
115+
return;
116+
}
101117
let deref = self.pointee_size.bytes();
102118
if deref != 0 {
103119
if regular.contains(ArgAttribute::NonNull) {
@@ -118,19 +134,14 @@ impl ArgAttributesExt for ArgAttributes {
118134
align.bytes() as u32,
119135
);
120136
}
121-
regular.for_each_kind(|attr| attr.apply_callsite(idx, callsite));
137+
for (attr, llattr) in OPTIMIZATION_ATTRIBUTES {
138+
if regular.contains(attr) {
139+
llattr.apply_callsite(idx, callsite);
140+
}
141+
}
122142
if regular.contains(ArgAttribute::NoAliasMutRef) && should_use_mutable_noalias(cx) {
123143
llvm::Attribute::NoAlias.apply_callsite(idx, callsite);
124144
}
125-
match self.arg_ext {
126-
ArgExtension::None => {}
127-
ArgExtension::Zext => {
128-
llvm::Attribute::ZExt.apply_callsite(idx, callsite);
129-
}
130-
ArgExtension::Sext => {
131-
llvm::Attribute::SExt.apply_callsite(idx, callsite);
132-
}
133-
}
134145
}
135146
}
136147
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// compile-flags: -C opt-level=0 -C no-prepopulate-passes
2+
3+
// This test checks that arguments/returns in opt-level=0 builds,
4+
// while lacking attributes used for optimization, still have ABI-affecting attributes.
5+
6+
#![crate_type = "lib"]
7+
#![feature(rustc_attrs)]
8+
9+
pub struct S {
10+
_field: [i32; 8],
11+
}
12+
13+
// CHECK: define zeroext i1 @boolean(i1 zeroext %x)
14+
#[no_mangle]
15+
pub fn boolean(x: bool) -> bool {
16+
x
17+
}
18+
19+
// CHECK-LABEL: @boolean_call
20+
#[no_mangle]
21+
pub fn boolean_call(x: bool, f: fn(bool) -> bool) -> bool {
22+
// CHECK: call zeroext i1 %f(i1 zeroext %x)
23+
f(x)
24+
}
25+
26+
// CHECK: define i32* @borrow(i32* %x)
27+
#[no_mangle]
28+
pub fn borrow(x: &i32) -> &i32 {
29+
x
30+
}
31+
32+
// CHECK-LABEL: @borrow_call
33+
#[no_mangle]
34+
pub fn borrow_call(x: &i32, f: fn(&i32) -> &i32) -> &i32 {
35+
// CHECK: call i32* %f(i32* %x)
36+
f(x)
37+
}
38+
39+
// CHECK: define void @struct_(%S* sret(%S){{( %0)?}}, %S* %x)
40+
#[no_mangle]
41+
pub fn struct_(x: S) -> S {
42+
x
43+
}
44+
45+
// CHECK-LABEL: @struct_call
46+
#[no_mangle]
47+
pub fn struct_call(x: S, f: fn(S) -> S) -> S {
48+
// CHECK: call void %f(%S* sret(%S){{( %0)?}}, %S* %{{.+}})
49+
f(x)
50+
}
51+
52+
// CHECK: define { i8, i8 } @enum_(i1 zeroext %x.0, i8 %x.1)
53+
#[no_mangle]
54+
pub fn enum_(x: Option<u8>) -> Option<u8> {
55+
x
56+
}
57+
58+
// CHECK-LABEL: @enum_call
59+
#[no_mangle]
60+
pub fn enum_call(x: Option<u8>, f: fn(Option<u8>) -> Option<u8>) -> Option<u8> {
61+
// CHECK: call { i8, i8 } %f(i1 zeroext %x.0, i8 %x.1)
62+
f(x)
63+
}

0 commit comments

Comments
 (0)