Skip to content

Commit d8344ff

Browse files
committed
Add ffi_const and ffi_pure extern fn attributes
1 parent d173180 commit d8344ff

18 files changed

+170
-0
lines changed

src/librustc/hir/mod.rs

+23
Original file line numberDiff line numberDiff line change
@@ -2489,6 +2489,29 @@ bitflags! {
24892489
/// #[used], indicates that LLVM can't eliminate this function (but the
24902490
/// linker can!)
24912491
const USED = 1 << 9;
2492+
/// #[ffi_pure], indicates that a function has no effects except for
2493+
/// its return value, that its return value depends only on the
2494+
/// parameters and/or global variables, and that its return value does
2495+
/// not change between two consecutive calls (for example, because of
2496+
/// volatile access to the global variables).
2497+
///
2498+
/// Such a function can be subject to common sub-expression elimination
2499+
/// and loop optimization just as an arithmetic operator would be. Some
2500+
/// common examples of pure functions are `strlen` or `memcmp`.
2501+
const FFI_PURE = 1 << 10;
2502+
/// #[ffi_const], indicates that a function has no effects except for
2503+
/// its return value and that its return value depends only on the
2504+
/// value of the function parameters.
2505+
///
2506+
/// This attribute is stricter than `#[ffi_pure]`, since the function
2507+
/// return value is not allowed to depend on anything that is not the
2508+
/// value of the function parameters, like global memory, or other
2509+
/// values, like dereferencing a pointer function parameter to read
2510+
/// another value.
2511+
///
2512+
/// A `#[ffi_const]` function that returns void is a `nop` in the
2513+
/// abstract machine.
2514+
const FFI_CONST = 1 << 11;
24922515
}
24932516
}
24942517

src/librustc_codegen_llvm/attributes.rs

+6
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,12 @@ pub fn from_fn_attrs(
223223
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::COLD) {
224224
Attribute::Cold.apply_llfn(Function, llfn);
225225
}
226+
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::FFI_PURE) {
227+
Attribute::ReadOnly.apply_llfn(Function, llfn);
228+
}
229+
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::FFI_CONST) {
230+
Attribute::ReadNone.apply_llfn(Function, llfn);
231+
}
226232
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
227233
naked(llfn, true);
228234
}

src/librustc_codegen_llvm/llvm/ffi.rs

+1
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ pub enum Attribute {
116116
SanitizeMemory = 22,
117117
NonLazyBind = 23,
118118
OptimizeNone = 24,
119+
ReadNone = 25,
119120
}
120121

121122
/// LLVMIntPredicate

src/librustc_typeck/collect.rs

+24
Original file line numberDiff line numberDiff line change
@@ -2270,6 +2270,30 @@ fn codegen_fn_attrs<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, id: DefId) -> Codegen
22702270
codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR;
22712271
} else if attr.check_name("unwind") {
22722272
codegen_fn_attrs.flags |= CodegenFnAttrFlags::UNWIND;
2273+
} else if attr.check_name("ffi_pure") {
2274+
if tcx.is_foreign_item(id) {
2275+
codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_PURE;
2276+
} else {
2277+
// `#[ffi_pure]` is only allowed `extern fn`s
2278+
struct_span_err!(
2279+
tcx.sess,
2280+
attr.span,
2281+
E0724,
2282+
"`#[ffi_pure]` may only be used on `extern fn`s"
2283+
).emit();
2284+
}
2285+
} else if attr.check_name("ffi_const") {
2286+
if tcx.is_foreign_item(id) {
2287+
codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_CONST;
2288+
} else {
2289+
// `#[ffi_const]` is only allowed `extern fn`s
2290+
struct_span_err!(
2291+
tcx.sess,
2292+
attr.span,
2293+
E0725,
2294+
"`#[ffi_const]` may only be used on `extern fn`s"
2295+
).emit();
2296+
}
22732297
} else if attr.check_name("rustc_allocator_nounwind") {
22742298
codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_ALLOCATOR_NOUNWIND;
22752299
} else if attr.check_name("naked") {

src/librustc_typeck/diagnostics.rs

+2
Original file line numberDiff line numberDiff line change
@@ -4720,4 +4720,6 @@ register_diagnostics! {
47204720
E0698, // type inside generator must be known in this context
47214721
E0719, // duplicate values for associated type binding
47224722
E0722, // Malformed #[optimize] attribute
4723+
E0724, // `#[ffi_pure]` is only allowed `extern fn`s
4724+
E0725, // `#[ffi_const]` is only allowed `extern fn`s
47234725
}

src/libsyntax/feature_gate.rs

+16
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,12 @@ declare_features! (
290290
// The `repr(i128)` annotation for enums.
291291
(active, repr128, "1.16.0", Some(35118), None),
292292

293+
// Allows the use of `#[ffi_pure]` on extern functions.
294+
(active, ffi_pure, "1.34.0", Some(58329), None),
295+
296+
// Allows the use of `#[ffi_const]` on extern functions.
297+
(active, ffi_const, "1.34.0", Some(58328), None),
298+
293299
// The `unadjusted` ABI; perma-unstable.
294300
//
295301
// rustc internal
@@ -1124,6 +1130,16 @@ pub const BUILTIN_ATTRIBUTES: &[(&str, AttributeType, AttributeTemplate, Attribu
11241130
"the `#[naked]` attribute \
11251131
is an experimental feature",
11261132
cfg_fn!(naked_functions))),
1133+
("ffi_pure", Whitelisted, template!(Word), Gated(Stability::Unstable,
1134+
"ffi_pure",
1135+
"the `#[ffi_pure]` attribute \
1136+
is an experimental feature",
1137+
cfg_fn!(ffi_pure))),
1138+
("ffi_const", Whitelisted, template!(Word), Gated(Stability::Unstable,
1139+
"ffi_const",
1140+
"the `#[ffi_const]` attribute \
1141+
is an experimental feature",
1142+
cfg_fn!(ffi_const))),
11271143
("target_feature", Whitelisted, template!(List: r#"enable = "name""#), Ungated),
11281144
("export_name", Whitelisted, template!(NameValueStr: "name"), Ungated),
11291145
("inline", Whitelisted, template!(Word, List: "always|never"), Ungated),

src/rustllvm/RustWrapper.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,8 @@ static Attribute::AttrKind fromRust(LLVMRustAttribute Kind) {
170170
return Attribute::OptimizeForSize;
171171
case ReadOnly:
172172
return Attribute::ReadOnly;
173+
case ReadNone:
174+
return Attribute::ReadNone;
173175
case SExt:
174176
return Attribute::SExt;
175177
case StructRet:

src/rustllvm/rustllvm.h

+1
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ enum LLVMRustAttribute {
8585
SanitizeMemory = 22,
8686
NonLazyBind = 23,
8787
OptimizeNone = 24,
88+
ReadNone = 25,
8889
};
8990

9091
typedef struct OpaqueRustString *RustStringRef;

src/test/codegen/ffi-const.rs

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// compile-flags: -C no-prepopulate-passes
2+
#![crate_type = "lib"]
3+
#![feature(ffi_const)]
4+
5+
// CHECK-LABEL: @bar()
6+
#[no_mangle]
7+
pub fn bar() { unsafe { foo() } }
8+
9+
10+
extern {
11+
// CHECK-LABEL: @foo() unnamed_addr #1
12+
// CHECK: attributes #1 = { {{.*}}readnone{{.*}} }
13+
#[no_mangle]
14+
#[ffi_const]
15+
pub fn foo();
16+
}

src/test/codegen/ffi-pure.rs

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// compile-flags: -C no-prepopulate-passes
2+
#![crate_type = "lib"]
3+
#![feature(ffi_pure)]
4+
5+
// CHECK-LABEL: @bar()
6+
#[no_mangle]
7+
pub fn bar() { unsafe { foo() } }
8+
9+
extern {
10+
// CHECK-LABEL: @foo() unnamed_addr #1
11+
// CHECK: attributes #1 = { {{.*}}readonly{{.*}} }
12+
#[ffi_pure] pub fn foo();
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// ignore-tidy-linelength
2+
#![crate_type = "lib"]
3+
4+
extern {
5+
#[ffi_const] //~ ERROR the `#[ffi_const]` attribute is an experimental feature (see issue #58314)
6+
pub fn foo();
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error[E0658]: the `#[ffi_const]` attribute is an experimental feature (see issue #58314)
2+
--> $DIR/feature-gate-ffi_const.rs:5:5
3+
|
4+
LL | #[ffi_const] //~ ERROR the `#[ffi_const]` attribute is an experimental feature (see issue #58314)
5+
| ^^^^^^^^^^^^
6+
|
7+
= help: add #![feature(ffi_const)] to the crate attributes to enable
8+
9+
error: aborting due to previous error
10+
11+
For more information about this error, try `rustc --explain E0658`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// ignore-tidy-linelength
2+
#![crate_type = "lib"]
3+
4+
extern {
5+
#[ffi_pure] //~ ERROR the `#[ffi_pure]` attribute is an experimental feature (see issue #58314)
6+
pub fn foo();
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error[E0658]: the `#[ffi_pure]` attribute is an experimental feature (see issue #58314)
2+
--> $DIR/feature-gate-ffi_pure.rs:5:5
3+
|
4+
LL | #[ffi_pure] //~ ERROR the `#[ffi_pure]` attribute is an experimental feature (see issue #58314)
5+
| ^^^^^^^^^^^
6+
|
7+
= help: add #![feature(ffi_pure)] to the crate attributes to enable
8+
9+
error: aborting due to previous error
10+
11+
For more information about this error, try `rustc --explain E0658`.

src/test/ui/ffi_const.rs

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// ignore-tidy-linelength
2+
#![feature(ffi_const)]
3+
#![crate_type = "lib"]
4+
5+
#[ffi_const] //~ ERROR `#[ffi_const]` may only be used on `extern fn`s [E0725]
6+
pub fn foo() {}

src/test/ui/ffi_const.stderr

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
error[E0725]: `#[ffi_const]` may only be used on `extern fn`s
2+
--> $DIR/ffi_const.rs:5:1
3+
|
4+
LL | #[ffi_const] //~ ERROR `#[ffi_const]` may only be used on `extern fn`s [E0725]
5+
| ^^^^^^^^^^^^
6+
7+
error: aborting due to previous error
8+
9+
For more information about this error, try `rustc --explain E0725`.

src/test/ui/ffi_pure.rs

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// ignore-tidy-linelength
2+
#![feature(ffi_pure)]
3+
#![crate_type = "lib"]
4+
5+
#[ffi_pure] //~ ERROR `#[ffi_pure]` may only be used on `extern fn`s [E0724]
6+
pub fn foo() {}

src/test/ui/ffi_pure.stderr

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
error[E0724]: `#[ffi_pure]` may only be used on `extern fn`s
2+
--> $DIR/ffi_pure.rs:5:1
3+
|
4+
LL | #[ffi_pure] //~ ERROR `#[ffi_pure]` may only be used on `extern fn`s [E0724]
5+
| ^^^^^^^^^^^
6+
7+
error: aborting due to previous error
8+
9+
For more information about this error, try `rustc --explain E0724`.

0 commit comments

Comments
 (0)