Skip to content

Commit aed5691

Browse files
committed
Check for upgradability of intrinsics
- Emit notes when can't match, and fall back to default implementation
1 parent 2ce841e commit aed5691

File tree

9 files changed

+155
-102
lines changed

9 files changed

+155
-102
lines changed

compiler/rustc_codegen_llvm/src/abi.rs

Lines changed: 70 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -308,10 +308,29 @@ impl<'ll, 'tcx> ArgAbiBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
308308
}
309309
}
310310

311+
pub(crate) enum FunctionSignature<'ll> {
312+
/// The signature is obtained directly from LLVM, and **may not match the Rust signature**
313+
Intrinsic(llvm::Intrinsic, &'ll Type),
314+
/// The name starts with `llvm.`, but can't obtain the intrinsic ID. May be invalid or upgradable
315+
MaybeInvalidIntrinsic(&'ll Type),
316+
/// Just the Rust signature
317+
Rust(&'ll Type),
318+
}
319+
320+
impl<'ll> FunctionSignature<'ll> {
321+
pub(crate) fn fn_ty(&self) -> &'ll Type {
322+
match self {
323+
FunctionSignature::Intrinsic(_, fn_ty)
324+
| FunctionSignature::MaybeInvalidIntrinsic(fn_ty)
325+
| FunctionSignature::Rust(fn_ty) => fn_ty,
326+
}
327+
}
328+
}
329+
311330
pub(crate) trait FnAbiLlvmExt<'ll, 'tcx> {
312331
fn llvm_return_type(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type;
313332
fn llvm_argument_types(&self, cx: &CodegenCx<'ll, 'tcx>) -> Vec<&'ll Type>;
314-
fn llvm_type(&self, cx: &CodegenCx<'ll, 'tcx>, name: &[u8]) -> &'ll Type;
333+
fn llvm_type(&self, cx: &CodegenCx<'ll, 'tcx>, name: &[u8]) -> FunctionSignature<'ll>;
315334
fn ptr_to_llvm_type(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type;
316335
fn llvm_cconv(&self, cx: &CodegenCx<'ll, 'tcx>) -> llvm::CallConv;
317336

@@ -327,57 +346,25 @@ pub(crate) trait FnAbiLlvmExt<'ll, 'tcx> {
327346
fn apply_attrs_callsite(&self, bx: &mut Builder<'_, 'll, 'tcx>, callsite: &'ll Value);
328347
}
329348

330-
/// checks that the Rust signature of a **non-overloaded** llvm intrinsic is correct
331-
fn match_intrinsic_signature<'ll>(
332-
cx: &CodegenCx<'ll, '_>,
333-
intrinsic: llvm::Intrinsic,
349+
/// checks that the Rust signature of a llvm intrinsic is correct
350+
fn verify_intrinsic_signature<'ll, 'tcx>(
351+
cx: &CodegenCx<'ll, 'tcx>,
352+
fn_ty: &'ll Type,
334353
rust_return_ty: &'ll Type,
335354
rust_argument_tys: &[&'ll Type],
336-
name: &[u8],
337-
) -> &'ll Type {
338-
macro_rules! error {
339-
($($t:tt)*) => {
340-
cx.tcx.dcx().fatal(format!($($t)*, fn_name=str::from_utf8(name).unwrap()))
341-
};
342-
}
343-
344-
let base_name = intrinsic.base_name();
345-
if name != base_name {
346-
error!(
347-
"Unsupported overload `{fn_name}` for non-overloaded intrinsic `{}`",
348-
str::from_utf8(base_name).unwrap()
349-
);
350-
}
351-
352-
let fn_ty = cx.intrinsic_type(intrinsic, &[]);
353-
355+
rust_is_variadic: bool,
356+
) -> bool {
354357
let llvm_return_ty = cx.get_return_type(fn_ty);
355358
let llvm_argument_tys = cx.func_params_types(fn_ty);
359+
let llvm_is_variadic = cx.func_is_variadic(fn_ty);
356360

357-
if rust_argument_tys.len() != llvm_argument_tys.len() {
358-
error!(
359-
"Intrinsic signature mismatch: expected {} arguments for `{fn_name}`, found {} arguments",
360-
llvm_argument_tys.len(),
361-
rust_argument_tys.len()
362-
);
361+
if rust_is_variadic != llvm_is_variadic || rust_argument_tys.len() != llvm_argument_tys.len() {
362+
return false;
363363
}
364364

365-
if !cx.equate_ty(rust_return_ty, llvm_return_ty) {
366-
error!(
367-
"Intrinsic signature mismatch: could not match `{rust_return_ty:?}` (found) with {llvm_return_ty:?} (expected) as return type for `{fn_name}`"
368-
);
369-
}
370-
for (idx, (&rust_argument_ty, llvm_argument_ty)) in
371-
iter::zip(rust_argument_tys, llvm_argument_tys).enumerate()
372-
{
373-
if !cx.equate_ty(rust_argument_ty, llvm_argument_ty) {
374-
error!(
375-
"Intrinsic signature mismatch: could not match `{rust_return_ty:?}` (found) with {llvm_return_ty:?} (expected) as argument {idx} for `{fn_name}`"
376-
);
377-
}
378-
}
379-
380-
fn_ty
365+
iter::once((&rust_return_ty, &llvm_return_ty))
366+
.chain(iter::zip(rust_argument_tys, &llvm_argument_tys))
367+
.all(|(rust_ty, llvm_ty)| cx.equate_ty(rust_ty, llvm_ty))
381368
}
382369

383370
impl<'ll, CX: Borrow<SCx<'ll>>> GenericCx<'ll, CX> {
@@ -407,19 +394,12 @@ impl<'ll, CX: Borrow<SCx<'ll>>> GenericCx<'ll, CX> {
407394
vector_size_bits == 8192
408395
}
409396
TypeKind::BFloat => rust_ty == self.type_i16(),
410-
TypeKind::Vector if self.type_kind(rust_ty) == TypeKind::Vector => {
397+
TypeKind::Vector => {
411398
let llvm_element_count = self.vector_length(llvm_ty);
412-
let rust_element_count = self.vector_length(rust_ty);
413-
414-
if llvm_element_count != rust_element_count {
415-
return false;
416-
}
417-
418399
let llvm_element_ty = self.element_type(llvm_ty);
419-
let rust_element_ty = self.element_type(rust_ty);
420400

421401
if llvm_element_ty == self.type_bf16() {
422-
rust_element_ty == self.type_i16()
402+
rust_ty == self.type_vector(self.type_i16(), llvm_element_count as u64)
423403
} else {
424404
false
425405
}
@@ -502,41 +482,62 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
502482
llargument_tys
503483
}
504484

505-
fn llvm_type(&self, cx: &CodegenCx<'ll, 'tcx>, name: &[u8]) -> &'ll Type {
485+
fn llvm_type(&self, cx: &CodegenCx<'ll, 'tcx>, name: &[u8]) -> FunctionSignature<'ll> {
506486
let return_ty = self.llvm_return_type(cx);
507487
let argument_tys = self.llvm_argument_types(cx);
508488

489+
let mut maybe_invalid = false;
490+
509491
if name.starts_with(b"llvm.") {
510-
if let Some(intrinsic) = llvm::Intrinsic::lookup(name)
511-
&& !intrinsic.is_overloaded()
512-
{
492+
if let Some(intrinsic) = llvm::Intrinsic::lookup(name) {
513493
if !intrinsic.is_overloaded() {
514494
// FIXME: also do this for overloaded intrinsics
515-
return match_intrinsic_signature(
495+
let llvm_fn_ty = cx.intrinsic_type(intrinsic, &[]);
496+
if verify_intrinsic_signature(
516497
cx,
517-
intrinsic,
498+
llvm_fn_ty,
518499
return_ty,
519500
&argument_tys,
520-
name,
521-
);
501+
self.c_variadic,
502+
) {
503+
return FunctionSignature::Intrinsic(intrinsic, llvm_fn_ty);
504+
} else {
505+
// FIXME: ideally we would like to hard error, but there are edge cases like `i1xN`
506+
// and differences between struct representations that we have to tackle first
507+
508+
let rust_fn_ty = if self.c_variadic {
509+
cx.type_variadic_func(&argument_tys, return_ty)
510+
} else {
511+
cx.type_func(&argument_tys, return_ty)
512+
};
513+
cx.tcx.dcx().note(format!(
514+
"Intrinsic signature mismatch for `{}`: expected signature `{llvm_fn_ty:?}`, found signature `{rust_fn_ty:?}`",
515+
str::from_utf8(name).unwrap()
516+
));
517+
// fallback to the rust signature
518+
return FunctionSignature::Rust(rust_fn_ty);
519+
}
522520
}
523521
} else {
524522
// it's one of 2 cases,
525523
// - either the base name is invalid
526524
// - it has been superceded by something else, so the intrinsic was removed entirely
527-
//
528-
// anyway, let's log it
529-
tracing::debug!(
530-
"Couldn't find intrinsic `{}`, either invalid or deprecated",
531-
str::from_utf8(name).unwrap()
532-
);
525+
// to check for upgrades, we need the `llfn`, so we defer it for now
526+
527+
maybe_invalid = true;
533528
}
534529
}
535530

536-
if self.c_variadic {
531+
let fn_ty = if self.c_variadic {
537532
cx.type_variadic_func(&argument_tys, return_ty)
538533
} else {
539534
cx.type_func(&argument_tys, return_ty)
535+
};
536+
537+
if maybe_invalid {
538+
FunctionSignature::MaybeInvalidIntrinsic(fn_ty)
539+
} else {
540+
FunctionSignature::Rust(fn_ty)
540541
}
541542
}
542543

compiler/rustc_codegen_llvm/src/builder.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1705,10 +1705,12 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
17051705
let actual_ty = self.cx.val_ty(ret);
17061706

17071707
if expected_ty != actual_ty {
1708-
warn!(
1709-
"Type mismatch in function call of {llfn:?}. \
1710-
Expected {expected_ty:?} for return type, got {actual_ty:?}"
1708+
assert!(
1709+
self.cx.equate_ty(expected_ty, actual_ty),
1710+
"type mismatch in function call of `{llfn:?}`. \
1711+
Can't match `{expected_ty:?}` (expected) as return type with `{actual_ty:?}` (found)",
17111712
);
1713+
17121714
if self.cx.type_kind(actual_ty) == TypeKind::X86_AMX {
17131715
self.cast_tile_to_vector(ret, expected_ty)
17141716
} else {

compiler/rustc_codegen_llvm/src/declare.rs

Lines changed: 48 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use rustc_target::callconv::FnAbi;
2222
use smallvec::SmallVec;
2323
use tracing::debug;
2424

25-
use crate::abi::FnAbiLlvmExt;
25+
use crate::abi::{FnAbiLlvmExt, FunctionSignature};
2626
use crate::common::AsCCharPtr;
2727
use crate::context::{CodegenCx, GenericCx, SCx};
2828
use crate::llvm::AttributePlace::Function;
@@ -150,17 +150,53 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
150150
) -> &'ll Value {
151151
debug!("declare_rust_fn(name={:?}, fn_abi={:?})", name, fn_abi);
152152

153-
// Function addresses in Rust are never significant, allowing functions to
154-
// be merged.
155-
let llfn = declare_raw_fn(
156-
self,
157-
name,
158-
fn_abi.llvm_cconv(self),
159-
llvm::UnnamedAddr::Global,
160-
llvm::Visibility::Default,
161-
fn_abi.llvm_type(self, name.as_bytes()),
162-
);
163-
fn_abi.apply_attrs_llfn(self, llfn, instance);
153+
let signature = fn_abi.llvm_type(self, name.as_bytes());
154+
let llfn;
155+
156+
if let FunctionSignature::Intrinsic(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.as_bytes(),
161+
fn_abi.llvm_cconv(self),
162+
llvm::UnnamedAddr::Global,
163+
llvm::Visibility::Default,
164+
fn_ty,
165+
);
166+
unsafe {
167+
llvm::LLVMRustSetIntrinsicAttributes(self.llcx, llfn, intrinsic.id());
168+
}
169+
} else {
170+
// Function addresses in Rust are never significant, allowing functions to
171+
// be merged.
172+
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+
fn_abi.apply_attrs_llfn(self, llfn, instance);
181+
}
182+
183+
if let FunctionSignature::MaybeInvalidIntrinsic(..) = signature {
184+
let mut new_llfn = None;
185+
let can_upgrade =
186+
unsafe { llvm::LLVMRustUpgradeIntrinsicFunction(llfn, &mut new_llfn, false) };
187+
188+
if can_upgrade {
189+
// not all intrinsics are upgraded to some other intrinsics, most are upgraded to instruction sequences
190+
if let Some(new_llfn) = new_llfn {
191+
self.tcx.dcx().note(format!(
192+
"Using deprecated intrinsic `{name}`, `{}` can be used instead",
193+
str::from_utf8(llvm::get_value_name(new_llfn)).unwrap()
194+
));
195+
}
196+
} else {
197+
self.tcx.dcx().fatal(format!("Invalid LLVM intrinsic: `{name}`"))
198+
}
199+
}
164200

165201
if self.tcx.sess.is_sanitizer_cfi_enabled() {
166202
if let Some(instance) = instance {

compiler/rustc_codegen_llvm/src/intrinsic.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1089,7 +1089,7 @@ fn gen_fn<'a, 'll, 'tcx>(
10891089
codegen: &mut dyn FnMut(Builder<'a, 'll, 'tcx>),
10901090
) -> (&'ll Type, &'ll Value) {
10911091
let fn_abi = cx.fn_abi_of_fn_ptr(rust_fn_sig, ty::List::empty());
1092-
let llty = fn_abi.llvm_type(cx, name.as_bytes());
1092+
let llty = fn_abi.llvm_type(cx, name.as_bytes()).fn_ty();
10931093
let llfn = cx.declare_fn(name, fn_abi, None);
10941094
cx.set_frame_pointer_type(llfn);
10951095
cx.apply_target_cpu_attr(llfn);

compiler/rustc_codegen_llvm/src/llvm/ffi.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1062,6 +1062,7 @@ unsafe extern "C" {
10621062
) -> &'a Type;
10631063
pub(crate) fn LLVMCountParamTypes(FunctionTy: &Type) -> c_uint;
10641064
pub(crate) fn LLVMGetParamTypes<'a>(FunctionTy: &'a Type, Dest: *mut &'a Type);
1065+
pub(crate) fn LLVMIsFunctionVarArg(FunctionTy: &Type) -> Bool;
10651066

10661067
// Operations on struct types
10671068
pub(crate) fn LLVMStructTypeInContext<'a>(
@@ -1204,10 +1205,6 @@ unsafe extern "C" {
12041205
ParamTypes: *const &'a Type,
12051206
ParamCount: size_t,
12061207
) -> &'a Type;
1207-
pub(crate) fn LLVMRustIntrinsicGetBaseName(
1208-
ID: c_uint,
1209-
NameLength: *mut size_t,
1210-
) -> *const c_char;
12111208
pub(crate) fn LLVMIntrinsicIsOverloaded(ID: c_uint) -> Bool;
12121209
pub(crate) fn LLVMIntrinsicCopyOverloadedName2<'a>(
12131210
Mod: &'a Module,
@@ -1216,6 +1213,12 @@ unsafe extern "C" {
12161213
ParamCount: size_t,
12171214
NameLength: *mut size_t,
12181215
) -> *mut c_char;
1216+
pub(crate) fn LLVMRustUpgradeIntrinsicFunction<'a>(
1217+
Fn: &'a Value,
1218+
NewFn: &mut Option<&'a Value>,
1219+
CanUpgradeDebugIntrinsicsToRecords: bool,
1220+
) -> bool;
1221+
pub(crate) fn LLVMRustSetIntrinsicAttributes<'a>(C: &'a Context, Fn: &'a Value, ID: c_uint);
12191222

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

compiler/rustc_codegen_llvm/src/llvm/mod.rs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -346,12 +346,6 @@ impl Intrinsic {
346346
unsafe { LLVMIntrinsicIsOverloaded(self.id) == True }
347347
}
348348

349-
pub(crate) fn base_name<'ll>(self) -> &'ll [u8] {
350-
let mut len = 0;
351-
let ptr = unsafe { LLVMRustIntrinsicGetBaseName(self.id, &mut len) };
352-
unsafe { slice::from_raw_parts(ptr.cast(), len) }
353-
}
354-
355349
pub(crate) fn overloaded_name<'ll>(
356350
self,
357351
llmod: &'ll Module,

compiler/rustc_codegen_llvm/src/type_.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ impl<'ll, CX: Borrow<SCx<'ll>>> GenericCx<'ll, CX> {
6868
args
6969
}
7070
}
71+
72+
pub(crate) fn func_is_variadic(&self, ty: &'ll Type) -> bool {
73+
unsafe { llvm::LLVMIsFunctionVarArg(ty) == True }
74+
}
7175
}
7276
impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
7377
pub(crate) fn type_bool(&self) -> &'ll Type {
@@ -288,7 +292,7 @@ impl<'ll, 'tcx> LayoutTypeCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
288292
fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
289293
fn_ptr: &'ll Value,
290294
) -> &'ll Type {
291-
fn_abi.llvm_type(self, llvm::get_value_name(fn_ptr))
295+
fn_abi.llvm_type(self, llvm::get_value_name(fn_ptr)).fn_ty()
292296
}
293297
fn fn_ptr_backend_type(&self, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> &'ll Type {
294298
fn_abi.ptr_to_llvm_type(self)

0 commit comments

Comments
 (0)