Skip to content

Commit d88e935

Browse files
committed
Refactor emitting errors to declare.rs, and use fluently-generated error messages
Create lints for deprecated and invalid intrinsics
1 parent f39fea4 commit d88e935

13 files changed

+220
-105
lines changed

compiler/rustc_codegen_llvm/messages.ftl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ codegen_llvm_from_llvm_diag = {$message}
99
1010
codegen_llvm_from_llvm_optimization_diag = {$filename}:{$line}:{$column} {$pass_name} ({$kind}): {$message}
1111
12+
codegen_llvm_intrinsic_signature_mismatch =
13+
intrinsic signature mismatch for `{$name}`: expected signature `{$llvm_fn_ty}`, found `{$rust_fn_ty}`
14+
15+
1216
codegen_llvm_load_bitcode = failed to load bitcode of module "{$name}"
1317
codegen_llvm_load_bitcode_with_llvm_err = failed to load bitcode of module "{$name}": {$llvm_err}
1418

compiler/rustc_codegen_llvm/src/abi.rs

Lines changed: 41 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -306,33 +306,42 @@ impl<'ll, 'tcx> ArgAbiBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
306306
}
307307

308308
pub(crate) enum FunctionSignature<'ll> {
309-
/// The signature is obtained directly from LLVM, and **may not match the Rust signature**
310-
Intrinsic(&'ll Type),
309+
/// This is an LLVM intrinsic, the signature is obtained directly from LLVM, and **may not match the Rust signature**
310+
LLVMSignature(llvm::Intrinsic, &'ll Type),
311+
/// This is an LLVM intrinsic, but the signature is just the Rust signature.
312+
/// FIXME: this should ideally not exist, we should be using the LLVM signature for all LLVM intrinsics
313+
RustSignature(llvm::Intrinsic, &'ll Type),
311314
/// The name starts with `llvm.`, but can't obtain the intrinsic ID. May be invalid or upgradable
312-
MaybeInvalidIntrinsic(&'ll Type),
315+
MaybeInvalid(&'ll Type),
313316
/// Just the Rust signature
314-
Rust(&'ll Type),
317+
NotIntrinsic(&'ll Type),
315318
}
316319

317320
impl<'ll> FunctionSignature<'ll> {
318321
pub(crate) fn fn_ty(&self) -> &'ll Type {
319322
match self {
320-
FunctionSignature::Intrinsic(fn_ty)
321-
| FunctionSignature::MaybeInvalidIntrinsic(fn_ty)
322-
| FunctionSignature::Rust(fn_ty) => fn_ty,
323+
FunctionSignature::LLVMSignature(_, fn_ty)
324+
| FunctionSignature::RustSignature(_, fn_ty)
325+
| FunctionSignature::MaybeInvalid(fn_ty)
326+
| FunctionSignature::NotIntrinsic(fn_ty) => fn_ty,
327+
}
328+
}
329+
330+
pub(crate) fn intrinsic(&self) -> Option<llvm::Intrinsic> {
331+
match self {
332+
FunctionSignature::RustSignature(intrinsic, _)
333+
| FunctionSignature::LLVMSignature(intrinsic, _) => Some(*intrinsic),
334+
_ => None,
323335
}
324336
}
325337
}
326338

327339
pub(crate) trait FnAbiLlvmExt<'ll, 'tcx> {
328340
fn llvm_return_type(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type;
329341
fn llvm_argument_types(&self, cx: &CodegenCx<'ll, 'tcx>) -> Vec<&'ll Type>;
330-
fn llvm_type(
331-
&self,
332-
cx: &CodegenCx<'ll, 'tcx>,
333-
name: &[u8],
334-
do_verify: bool,
335-
) -> FunctionSignature<'ll>;
342+
fn llvm_type(&self, cx: &CodegenCx<'ll, 'tcx>, name: &[u8]) -> FunctionSignature<'ll>;
343+
/// The normal Rust signature for this
344+
fn rust_signature(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type;
336345
/// **If this function is an LLVM intrinsic** checks if the LLVM signature provided matches with this
337346
fn verify_intrinsic_signature(&self, cx: &CodegenCx<'ll, 'tcx>, llvm_ty: &'ll Type) -> bool;
338347

@@ -478,51 +487,35 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
478487
.all(|(rust_ty, llvm_ty)| cx.equate_ty(rust_ty, llvm_ty))
479488
}
480489

481-
fn llvm_type(
482-
&self,
483-
cx: &CodegenCx<'ll, 'tcx>,
484-
name: &[u8],
485-
do_verify: bool,
486-
) -> FunctionSignature<'ll> {
487-
let mut maybe_invalid = false;
490+
fn rust_signature(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type {
491+
let return_ty = self.llvm_return_type(cx);
492+
let argument_tys = self.llvm_argument_types(cx);
488493

494+
if self.c_variadic {
495+
cx.type_variadic_func(&argument_tys, return_ty)
496+
} else {
497+
cx.type_func(&argument_tys, return_ty)
498+
}
499+
}
500+
501+
fn llvm_type(&self, cx: &CodegenCx<'ll, 'tcx>, name: &[u8]) -> FunctionSignature<'ll> {
489502
if name.starts_with(b"llvm.") {
490503
if let Some(intrinsic) = llvm::Intrinsic::lookup(name) {
491504
if !intrinsic.is_overloaded() {
492505
// FIXME: also do this for overloaded intrinsics
493-
let llvm_fn_ty = intrinsic.get_type(cx.llcx, &[]);
494-
if do_verify {
495-
if !self.verify_intrinsic_signature(cx, llvm_fn_ty) {
496-
cx.tcx.dcx().fatal(format!(
497-
"Intrinsic signature mismatch for `{}`: expected signature `{llvm_fn_ty:?}`",
498-
str::from_utf8(name).unwrap()
499-
));
500-
}
501-
}
502-
return FunctionSignature::Intrinsic(llvm_fn_ty);
506+
FunctionSignature::LLVMSignature(intrinsic, intrinsic.get_type(cx.llcx, &[]))
507+
} else {
508+
FunctionSignature::RustSignature(intrinsic, self.rust_signature(cx))
503509
}
504510
} else {
505511
// it's one of 2 cases,
506512
// - either the base name is invalid
507513
// - it has been superseded by something else, so the intrinsic was removed entirely
508514
// to check for upgrades, we need the `llfn`, so we defer it for now
509-
maybe_invalid = true;
515+
FunctionSignature::MaybeInvalid(self.rust_signature(cx))
510516
}
511-
}
512-
513-
let return_ty = self.llvm_return_type(cx);
514-
let argument_tys = self.llvm_argument_types(cx);
515-
516-
let fn_ty = if self.c_variadic {
517-
cx.type_variadic_func(&argument_tys, return_ty)
518517
} else {
519-
cx.type_func(&argument_tys, return_ty)
520-
};
521-
522-
if maybe_invalid {
523-
FunctionSignature::MaybeInvalidIntrinsic(fn_ty)
524-
} else {
525-
FunctionSignature::Rust(fn_ty)
518+
FunctionSignature::NotIntrinsic(self.rust_signature(cx))
526519
}
527520
}
528521

@@ -686,15 +679,9 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
686679
callsite: &'ll Value,
687680
llfn: &'ll Value,
688681
) {
689-
// if we are using the LLVM signature, use the LLVM attributes otherwise it might be problematic
690-
let name = llvm::get_value_name(llfn);
691-
if name.starts_with(b"llvm.")
692-
&& let Some(intrinsic) = llvm::Intrinsic::lookup(&name)
693-
{
694-
// FIXME: also do this for overloaded intrinsics
695-
if !intrinsic.is_overloaded() {
696-
return;
697-
}
682+
// Don't apply any attributes to LLVM intrinsics, they will be applied by AutoUpgrade
683+
if llvm::get_value_name(llfn).starts_with(b"llvm.") {
684+
return;
698685
}
699686

700687
let mut func_attrs = SmallVec::<[_; 2]>::new();

compiler/rustc_codegen_llvm/src/declare.rs

Lines changed: 56 additions & 38 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;
@@ -29,7 +30,7 @@ use crate::llvm::AttributePlace::Function;
2930
use crate::llvm::{FromGeneric, Visibility};
3031
use crate::type_::Type;
3132
use crate::value::Value;
32-
use crate::{attributes, llvm};
33+
use crate::{attributes, errors, llvm};
3334

3435
/// Declare a function with a SimpleCx.
3536
///
@@ -150,52 +151,69 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
150151
) -> &'ll Value {
151152
debug!("declare_rust_fn(name={:?}, fn_abi={:?})", name, fn_abi);
152153

153-
let signature = fn_abi.llvm_type(self, name.as_bytes(), true);
154-
let llfn;
155-
156-
if let FunctionSignature::Intrinsic(fn_ty) = signature {
157-
// intrinsics have a specified set of attributes, so we don't use the `FnAbi` set for them
158-
llfn = declare_simple_fn(
159-
self,
160-
name,
161-
fn_abi.llvm_cconv(self),
162-
llvm::UnnamedAddr::Global,
163-
llvm::Visibility::Default,
164-
fn_ty,
165-
);
166-
} else {
167-
// Function addresses in Rust are never significant, allowing functions to
168-
// be merged.
169-
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-
);
154+
let signature = fn_abi.llvm_type(self, name.as_bytes());
155+
156+
let span = || instance.map(|instance| self.tcx.def_span(instance.def_id()));
157+
158+
if let FunctionSignature::LLVMSignature(_, llvm_fn_ty) = signature {
159+
// check if the intrinsic signatures match
160+
if !fn_abi.verify_intrinsic_signature(self, llvm_fn_ty) {
161+
self.tcx.dcx().emit_fatal(errors::IntrinsicSignatureMismatch {
162+
name,
163+
llvm_fn_ty: &format!("{llvm_fn_ty:?}"),
164+
rust_fn_ty: &format!("{:?}", fn_abi.rust_signature(self)),
165+
span: span(),
166+
});
167+
}
168+
}
169+
170+
// Function addresses in Rust are never significant, allowing functions to
171+
// be merged.
172+
let llfn = declare_raw_fn(
173+
self,
174+
name,
175+
fn_abi.llvm_cconv(self),
176+
llvm::UnnamedAddr::Global,
177+
llvm::Visibility::Default,
178+
signature.fn_ty(),
179+
);
180+
181+
if signature.intrinsic().is_none() {
182+
// Don't apply any attributes to intrinsics, they will be applied by AutoUpgrade
177183
fn_abi.apply_attrs_llfn(self, llfn, instance);
178184
}
179185

180-
if let FunctionSignature::MaybeInvalidIntrinsic(..) = signature {
186+
if let FunctionSignature::MaybeInvalid(..) = signature {
181187
let mut new_llfn = None;
182188
let can_upgrade =
183189
unsafe { llvm::LLVMRustUpgradeIntrinsicFunction(llfn, &mut new_llfn, false) };
184190

185-
if can_upgrade {
186-
// not all intrinsics are upgraded to some other intrinsics, most are upgraded to instruction sequences
187-
if let Some(new_llfn) = new_llfn {
188-
self.tcx.dcx().note(format!(
189-
"Using deprecated intrinsic `{name}`, `{}` can be used instead",
190-
str::from_utf8(&llvm::get_value_name(new_llfn)).unwrap()
191-
));
191+
// we can emit diagnostics for local crates only
192+
if let Some(instance) = instance
193+
&& let Some(local_def_id) = instance.def_id().as_local()
194+
{
195+
let hir_id = self.tcx.local_def_id_to_hir_id(local_def_id);
196+
let span = self.tcx.def_span(local_def_id);
197+
198+
if can_upgrade {
199+
// not all intrinsics are upgraded to some other intrinsics, most are upgraded to instruction sequences
200+
let msg = if let Some(new_llfn) = new_llfn {
201+
format!(
202+
"Using deprecated intrinsic `{name}`, `{}` can be used instead",
203+
str::from_utf8(&llvm::get_value_name(new_llfn)).unwrap()
204+
)
205+
} else {
206+
format!("Using deprecated intrinsic `{name}`")
207+
};
208+
self.tcx.node_lint(DEPRECATED_LLVM_INTRINSIC, hir_id, |d| {
209+
d.primary_message(msg).span(span);
210+
});
192211
} else {
193-
self.tcx.dcx().note(format!(
194-
"Using deprecated intrinsic `{name}`, consider using other intrinsics/instructions"
195-
));
212+
// This is either plain wrong, or this can be caused by incompatible LLVM versions, we let the user decide
213+
self.tcx.node_lint(UNKNOWN_LLVM_INTRINSIC, hir_id, |d| {
214+
d.primary_message(format!("Invalid LLVM Intrinsic `{name}`")).span(span);
215+
});
196216
}
197-
} else {
198-
self.tcx.dcx().fatal(format!("Invalid LLVM intrinsic: `{name}`"))
199217
}
200218
}
201219

compiler/rustc_codegen_llvm/src/errors.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,3 +147,13 @@ pub(crate) struct FixedX18InvalidArch<'a> {
147147
#[derive(Diagnostic)]
148148
#[diag(codegen_llvm_sanitizer_kcfi_arity_requires_llvm_21_0_0)]
149149
pub(crate) struct SanitizerKcfiArityRequiresLLVM2100;
150+
151+
#[derive(Diagnostic)]
152+
#[diag(codegen_llvm_intrinsic_signature_mismatch)]
153+
pub(crate) struct IntrinsicSignatureMismatch<'a> {
154+
pub name: &'a str,
155+
pub llvm_fn_ty: &'a str,
156+
pub rust_fn_ty: &'a str,
157+
#[primary_span]
158+
pub span: Option<Span>,
159+
}

compiler/rustc_codegen_llvm/src/intrinsic.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1075,7 +1075,7 @@ fn gen_fn<'a, 'll, 'tcx>(
10751075
codegen: &mut dyn FnMut(Builder<'a, 'll, 'tcx>),
10761076
) -> (&'ll Type, &'ll Value) {
10771077
let fn_abi = cx.fn_abi_of_fn_ptr(rust_fn_sig, ty::List::empty());
1078-
let llty = fn_abi.llvm_type(cx, name.as_bytes(), true).fn_ty();
1078+
let llty = fn_abi.llvm_type(cx, name.as_bytes()).fn_ty();
10791079
let llfn = cx.declare_fn(name, fn_abi, None);
10801080
cx.set_frame_pointer_type(llfn);
10811081
cx.apply_target_cpu_attr(llfn);

compiler/rustc_codegen_llvm/src/type_.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,7 @@ impl<'ll, 'tcx> LayoutTypeCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
311311
fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
312312
fn_ptr: &'ll Value,
313313
) -> &'ll Type {
314-
fn_abi.llvm_type(self, &llvm::get_value_name(fn_ptr), false).fn_ty()
314+
fn_abi.llvm_type(self, &llvm::get_value_name(fn_ptr)).fn_ty()
315315
}
316316
fn fn_ptr_backend_type(&self, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> &'ll Type {
317317
fn_abi.ptr_to_llvm_type(self)

0 commit comments

Comments
 (0)