Skip to content

Commit 021bbbd

Browse files
committed
Check for AutoUpgraded intrinsics, and lint on uses of deprecated/unknown intrinsics
1 parent acdd589 commit 021bbbd

File tree

9 files changed

+204
-12
lines changed

9 files changed

+204
-12
lines changed

compiler/rustc_codegen_llvm/src/declare.rs

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use rustc_codegen_ssa::traits::TypeMembershipCodegenMethods;
1818
use rustc_data_structures::fx::FxIndexSet;
1919
use rustc_middle::ty::{Instance, Ty};
2020
use rustc_sanitizers::{cfi, kcfi};
21+
use rustc_session::lint::builtin::{DEPRECATED_LLVM_INTRINSIC, UNKNOWN_LLVM_INTRINSIC};
2122
use rustc_target::callconv::FnAbi;
2223
use smallvec::SmallVec;
2324
use tracing::debug;
@@ -156,31 +157,63 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
156157
// check if the intrinsic signatures match
157158
if !fn_abi.verify_intrinsic_signature(self, llvm_fn_ty) {
158159
self.tcx.dcx().emit_fatal(errors::IntrinsicSignatureMismatch {
159-
name,
160+
name,
160161
llvm_fn_ty: &format!("{llvm_fn_ty:?}"),
161162
rust_fn_ty: &format!("{:?}", fn_abi.rust_signature(self)),
162163
span: span(),
163164
});
164165
}
165166
}
166167

167-
// Function addresses in Rust are never significant, allowing functions to
168-
// be merged.
168+
// Function addresses in Rust are never significant, allowing functions to
169+
// be merged.
169170
let llfn = declare_raw_fn(
170-
self,
171-
name,
172-
fn_abi.llvm_cconv(self),
173-
llvm::UnnamedAddr::Global,
174-
llvm::Visibility::Default,
175-
signature.fn_ty(),
176-
);
171+
self,
172+
name,
173+
fn_abi.llvm_cconv(self),
174+
llvm::UnnamedAddr::Global,
175+
llvm::Visibility::Default,
176+
signature.fn_ty(),
177+
);
177178

178179
if signature.intrinsic().is_none() {
179180
// Don't apply any attributes to intrinsics, they will be applied by AutoUpgrade
180181
fn_abi.apply_attrs_llfn(self, llfn, instance);
181182
}
182183

183-
// todo: check for upgrades, and emit error if not upgradable
184+
if let FunctionSignature::MaybeInvalid(..) = signature {
185+
let mut new_llfn = None;
186+
let can_upgrade =
187+
unsafe { llvm::LLVMRustUpgradeIntrinsicFunction(llfn, &mut new_llfn, false) };
188+
189+
// we can emit diagnostics for local crates only
190+
if let Some(instance) = instance
191+
&& let Some(local_def_id) = instance.def_id().as_local()
192+
{
193+
let hir_id = self.tcx.local_def_id_to_hir_id(local_def_id);
194+
let span = self.tcx.def_span(local_def_id);
195+
196+
if can_upgrade {
197+
// not all intrinsics are upgraded to some other intrinsics, most are upgraded to instruction sequences
198+
let msg = if let Some(new_llfn) = new_llfn {
199+
format!(
200+
"using deprecated intrinsic `{name}`, `{}` can be used instead",
201+
str::from_utf8(&llvm::get_value_name(new_llfn)).unwrap()
202+
)
203+
} else {
204+
format!("using deprecated intrinsic `{name}`")
205+
};
206+
self.tcx.node_lint(DEPRECATED_LLVM_INTRINSIC, hir_id, |d| {
207+
d.primary_message(msg).span(span);
208+
});
209+
} else {
210+
// This is either plain wrong, or this can be caused by incompatible LLVM versions, we let the user decide
211+
self.tcx.node_lint(UNKNOWN_LLVM_INTRINSIC, hir_id, |d| {
212+
d.primary_message(format!("invalid LLVM Intrinsic `{name}`")).span(span);
213+
});
214+
}
215+
}
216+
}
184217

185218
if self.tcx.sess.is_sanitizer_cfi_enabled() {
186219
if let Some(instance) = instance {

compiler/rustc_codegen_llvm/src/llvm/ffi.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1146,6 +1146,11 @@ unsafe extern "C" {
11461146
ParamTypes: *const &'a Type,
11471147
ParamCount: size_t,
11481148
) -> &'a Value;
1149+
pub(crate) fn LLVMRustUpgradeIntrinsicFunction<'a>(
1150+
Fn: &'a Value,
1151+
NewFn: &mut Option<&'a Value>,
1152+
CanUpgradeDebugIntrinsicsToRecords: bool,
1153+
) -> bool;
11491154

11501155
// Operations on parameters
11511156
pub(crate) fn LLVMIsAArgument(Val: &Value) -> Option<&Value>;

compiler/rustc_lint_defs/src/builtin.rs

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ declare_lint_pass! {
3535
DEPENDENCY_ON_UNIT_NEVER_TYPE_FALLBACK,
3636
DEPRECATED,
3737
DEPRECATED_IN_FUTURE,
38+
DEPRECATED_LLVM_INTRINSIC,
3839
DEPRECATED_SAFE_2024,
3940
DEPRECATED_WHERE_CLAUSE_LOCATION,
4041
DUPLICATE_MACRO_ATTRIBUTES,
@@ -118,6 +119,7 @@ declare_lint_pass! {
118119
UNKNOWN_CRATE_TYPES,
119120
UNKNOWN_DIAGNOSTIC_ATTRIBUTES,
120121
UNKNOWN_LINTS,
122+
UNKNOWN_LLVM_INTRINSIC,
121123
UNNAMEABLE_TEST_ITEMS,
122124
UNNAMEABLE_TYPES,
123125
UNREACHABLE_CODE,
@@ -5263,3 +5265,79 @@ declare_lint! {
52635265
report_in_deps: false,
52645266
};
52655267
}
5268+
5269+
declare_lint! {
5270+
/// The `unknown_llvm_intrinsic` lint detects usage of unknown LLVM intrinsics.
5271+
///
5272+
/// ### Example
5273+
///
5274+
/// ```rust,compile_fail
5275+
/// #![feature(link_llvm_intrinsics, abi_unadjusted)]
5276+
///
5277+
/// unsafe extern "unadjusted" {
5278+
/// #[link_name = "llvm.abcde"]
5279+
/// fn foo();
5280+
/// }
5281+
///
5282+
/// #[inline(never)]
5283+
/// pub fn main() {
5284+
/// unsafe { foo() }
5285+
/// }
5286+
/// ```
5287+
///
5288+
/// {{produces}}
5289+
///
5290+
/// ### Explanation
5291+
///
5292+
/// Linking to an unknown LLVM intrinsic may cause linker errors (in general it's UB),
5293+
/// so this lint captures those undesirable scenarios.
5294+
pub UNKNOWN_LLVM_INTRINSIC,
5295+
Deny,
5296+
"detects uses of unknown LLVM intrinsics",
5297+
@feature_gate = link_llvm_intrinsics;
5298+
}
5299+
5300+
declare_lint! {
5301+
/// The `deprecated_llvm_intrinsic` lint detects usage of deprecated LLVM intrinsics.
5302+
///
5303+
/// ### Example
5304+
///
5305+
/// ```rust,ignore (requires x86)
5306+
/// #![cfg(any(target_arch = "x86", target_arch = "x86_64"))]
5307+
/// #![feature(link_llvm_intrinsics, abi_unadjusted)]
5308+
/// #![deny(deprecated_llvm_intrinsic)]
5309+
///
5310+
/// unsafe extern "unadjusted" {
5311+
/// #[link_name = "llvm.x86.addcarryx.u32"]
5312+
/// fn foo(a: u8, b: u32, c: u32, d: &mut u32) -> u8;
5313+
/// }
5314+
///
5315+
/// #[inline(never)]
5316+
/// #[target_feature(enable = "adx")]
5317+
/// pub fn bar(a: u8, b: u32, c: u32, d: &mut u32) -> u8 {
5318+
/// unsafe { foo(a, b, c, d) }
5319+
/// }
5320+
/// ```
5321+
///
5322+
/// This will produce:
5323+
///
5324+
/// ```text
5325+
/// error: Using deprecated intrinsic `llvm.x86.addcarryx.u32`
5326+
/// --> example.rs:7:5
5327+
/// |
5328+
/// 7 | fn foo(a: u8, b: u32, c: u32, d: &mut u32) -> u8;
5329+
/// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
5330+
/// |
5331+
/// ```
5332+
///
5333+
/// ### Explanation
5334+
///
5335+
/// LLVM periodically updates its list of intrinsics. Removed intrinsics are unlikely
5336+
/// to be removed, but they may optimize less well than their new versions, so it's
5337+
/// best to use the new version. Also, some deprecated intrinsics might have buggy
5338+
/// behavior
5339+
pub DEPRECATED_LLVM_INTRINSIC,
5340+
Allow,
5341+
"detects uses of deprecated LLVM intrinsics",
5342+
@feature_gate = link_llvm_intrinsics;
5343+
}

compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "llvm/ADT/StringRef.h"
1010
#include "llvm/BinaryFormat/Magic.h"
1111
#include "llvm/Bitcode/BitcodeWriter.h"
12+
#include "llvm/IR/AutoUpgrade.h"
1213
#include "llvm/IR/DIBuilder.h"
1314
#include "llvm/IR/DebugInfoMetadata.h"
1415
#include "llvm/IR/DiagnosticHandler.h"
@@ -1623,6 +1624,17 @@ extern "C" void LLVMRustGetMangledName(LLVMValueRef V, RustStringRef Str) {
16231624
Mangler().getNameWithPrefix(OS, GV, true);
16241625
}
16251626

1627+
extern "C" bool
1628+
LLVMRustUpgradeIntrinsicFunction(LLVMValueRef Fn, LLVMValueRef *NewFn,
1629+
bool canUpgradeDebugIntrinsicsToRecords) {
1630+
Function *F = unwrap<Function>(Fn);
1631+
Function *NewF = nullptr;
1632+
bool CanUpgrade =
1633+
UpgradeIntrinsicFunction(F, NewF, canUpgradeDebugIntrinsicsToRecords);
1634+
*NewFn = wrap(NewF);
1635+
return CanUpgrade;
1636+
}
1637+
16261638
extern "C" int32_t LLVMRustGetElementTypeArgIndex(LLVMValueRef CallSite) {
16271639
auto *CB = unwrap<CallBase>(CallSite);
16281640
switch (CB->getIntrinsicID()) {

tests/run-make/simd-ffi/simd.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ extern "C" {
3535
fn integer(a: i32x4, b: i32x4) -> i32x4;
3636
// vmaxq_s32
3737
#[cfg(target_arch = "aarch64")]
38-
#[link_name = "llvm.aarch64.neon.maxs.v4i32"]
38+
#[link_name = "llvm.aarch64.neon.smax.v4i32"]
3939
fn integer(a: i32x4, b: i32x4) -> i32x4;
4040

4141
// Use a generic LLVM intrinsic to do type checking on other platforms
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//@ add-minicore
2+
//@ build-fail
3+
//@ compile-flags: --target aarch64-unknown-linux-gnu
4+
//@ needs-llvm-components: aarch64
5+
#![feature(no_core, lang_items, link_llvm_intrinsics, abi_unadjusted, repr_simd, simd_ffi)]
6+
#![no_std]
7+
#![no_core]
8+
#![allow(internal_features, non_camel_case_types, improper_ctypes)]
9+
#![crate_type = "lib"]
10+
11+
extern crate minicore;
12+
use minicore::*;
13+
14+
#[repr(simd)]
15+
pub struct i8x8([i8; 8]);
16+
17+
extern "unadjusted" {
18+
#[deny(deprecated_llvm_intrinsic)]
19+
#[link_name = "llvm.aarch64.neon.rbit.v8i8"]
20+
fn foo(a: i8x8) -> i8x8;
21+
//~^ ERROR: using deprecated intrinsic `llvm.aarch64.neon.rbit.v8i8`, `llvm.bitreverse.v8i8` can be used instead
22+
}
23+
24+
#[target_feature(enable = "neon")]
25+
pub unsafe fn bar(a: i8x8) -> i8x8 {
26+
foo(a)
27+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error: using deprecated intrinsic `llvm.aarch64.neon.rbit.v8i8`, `llvm.bitreverse.v8i8` can be used instead
2+
--> $DIR/deprecated-llvm-intrinsic.rs:20:5
3+
|
4+
LL | fn foo(a: i8x8) -> i8x8;
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
note: the lint level is defined here
8+
--> $DIR/deprecated-llvm-intrinsic.rs:18:12
9+
|
10+
LL | #[deny(deprecated_llvm_intrinsic)]
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^
12+
13+
error: aborting due to 1 previous error
14+
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//@ build-fail
2+
3+
#![feature(link_llvm_intrinsics, abi_unadjusted)]
4+
5+
extern "unadjusted" {
6+
#[link_name = "llvm.abcde"]
7+
fn foo();
8+
//~^ ERROR: invalid LLVM Intrinsic `llvm.abcde`
9+
}
10+
11+
pub fn main() {
12+
unsafe { foo() }
13+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
error: invalid LLVM Intrinsic `llvm.abcde`
2+
--> $DIR/invalid-llvm-intrinsic.rs:7:5
3+
|
4+
LL | fn foo();
5+
| ^^^^^^^^^
6+
|
7+
= note: `#[deny(unknown_llvm_intrinsic)]` on by default
8+
9+
error: aborting due to 1 previous error
10+

0 commit comments

Comments
 (0)