Skip to content

Commit acdd589

Browse files
committed
Codegen non-overloaded LLVM intrinsics using their name
1 parent 1ef7943 commit acdd589

File tree

17 files changed

+241
-45
lines changed

17 files changed

+241
-45
lines changed

compiler/rustc_codegen_gcc/src/type_of.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::fmt::Write;
22

3-
use gccjit::{Struct, Type};
3+
use gccjit::{RValue, Struct, Type};
44
use rustc_abi as abi;
55
use rustc_abi::Primitive::*;
66
use rustc_abi::{
@@ -373,7 +373,11 @@ impl<'gcc, 'tcx> LayoutTypeCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
373373
unimplemented!();
374374
}
375375

376-
fn fn_decl_backend_type(&self, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> Type<'gcc> {
376+
fn fn_decl_backend_type(
377+
&self,
378+
fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
379+
_fn_ptr: RValue<'gcc>,
380+
) -> Type<'gcc> {
377381
// FIXME(antoyo): Should we do something with `FnAbiGcc::fn_attributes`?
378382
let FnAbiGcc { return_type, arguments_type, is_c_variadic, .. } = fn_abi.gcc_type(self);
379383
self.context.new_function_pointer_type(None, return_type, &arguments_type, is_c_variadic)

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: 122 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::cmp;
1+
use std::{cmp, iter};
22

33
use libc::c_uint;
44
use rustc_abi::{
@@ -307,8 +307,46 @@ impl<'ll, 'tcx> ArgAbiBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
307307
}
308308
}
309309

310+
pub(crate) enum FunctionSignature<'ll> {
311+
/// This is an LLVM intrinsic, the signature is obtained directly from LLVM, and **may not match the Rust signature**
312+
LLVMSignature(llvm::Intrinsic, &'ll Type),
313+
/// This is an LLVM intrinsic, but the signature is just the Rust signature.
314+
/// FIXME: this should ideally not exist, we should be using the LLVM signature for all LLVM intrinsics
315+
RustSignature(llvm::Intrinsic, &'ll Type),
316+
/// The name starts with `llvm.`, but can't obtain the intrinsic ID. May be invalid or upgradable
317+
MaybeInvalid(&'ll Type),
318+
/// Just the Rust signature
319+
NotIntrinsic(&'ll Type),
320+
}
321+
322+
impl<'ll> FunctionSignature<'ll> {
323+
pub(crate) fn fn_ty(&self) -> &'ll Type {
324+
match self {
325+
FunctionSignature::LLVMSignature(_, fn_ty)
326+
| FunctionSignature::RustSignature(_, fn_ty)
327+
| FunctionSignature::MaybeInvalid(fn_ty)
328+
| FunctionSignature::NotIntrinsic(fn_ty) => fn_ty,
329+
}
330+
}
331+
332+
pub(crate) fn intrinsic(&self) -> Option<llvm::Intrinsic> {
333+
match self {
334+
FunctionSignature::RustSignature(intrinsic, _)
335+
| FunctionSignature::LLVMSignature(intrinsic, _) => Some(*intrinsic),
336+
_ => None,
337+
}
338+
}
339+
}
340+
310341
pub(crate) trait FnAbiLlvmExt<'ll, 'tcx> {
311-
fn llvm_type(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type;
342+
fn llvm_return_type(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type;
343+
fn llvm_argument_types(&self, cx: &CodegenCx<'ll, 'tcx>) -> Vec<&'ll Type>;
344+
fn llvm_type(&self, cx: &CodegenCx<'ll, 'tcx>, name: &[u8]) -> FunctionSignature<'ll>;
345+
/// The normal Rust signature for this
346+
fn rust_signature(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type;
347+
/// **If this function is an LLVM intrinsic** checks if the LLVM signature provided matches with this
348+
fn verify_intrinsic_signature(&self, cx: &CodegenCx<'ll, 'tcx>, llvm_ty: &'ll Type) -> bool;
349+
312350
fn ptr_to_llvm_type(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type;
313351
fn llvm_cconv(&self, cx: &CodegenCx<'ll, 'tcx>) -> llvm::CallConv;
314352

@@ -321,30 +359,39 @@ pub(crate) trait FnAbiLlvmExt<'ll, 'tcx> {
321359
);
322360

323361
/// Apply attributes to a function call.
324-
fn apply_attrs_callsite(&self, bx: &mut Builder<'_, 'll, 'tcx>, callsite: &'ll Value);
362+
fn apply_attrs_callsite(
363+
&self,
364+
bx: &mut Builder<'_, 'll, 'tcx>,
365+
callsite: &'ll Value,
366+
llfn: &'ll Value,
367+
);
325368
}
326369

327370
impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
328-
fn llvm_type(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type {
371+
fn llvm_return_type(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type {
372+
match &self.ret.mode {
373+
PassMode::Ignore => cx.type_void(),
374+
PassMode::Direct(_) | PassMode::Pair(..) => self.ret.layout.immediate_llvm_type(cx),
375+
PassMode::Cast { cast, pad_i32: _ } => cast.llvm_type(cx),
376+
PassMode::Indirect { .. } => cx.type_void(),
377+
}
378+
}
379+
380+
fn llvm_argument_types(&self, cx: &CodegenCx<'ll, 'tcx>) -> Vec<&'ll Type> {
381+
let indirect_return = matches!(self.ret.mode, PassMode::Indirect { .. });
382+
329383
// Ignore "extra" args from the call site for C variadic functions.
330384
// Only the "fixed" args are part of the LLVM function signature.
331385
let args =
332386
if self.c_variadic { &self.args[..self.fixed_count as usize] } else { &self.args };
333387

334388
// This capacity calculation is approximate.
335-
let mut llargument_tys = Vec::with_capacity(
336-
self.args.len() + if let PassMode::Indirect { .. } = self.ret.mode { 1 } else { 0 },
337-
);
389+
let mut llargument_tys =
390+
Vec::with_capacity(args.len() + if indirect_return { 1 } else { 0 });
338391

339-
let llreturn_ty = match &self.ret.mode {
340-
PassMode::Ignore => cx.type_void(),
341-
PassMode::Direct(_) | PassMode::Pair(..) => self.ret.layout.immediate_llvm_type(cx),
342-
PassMode::Cast { cast, pad_i32: _ } => cast.llvm_type(cx),
343-
PassMode::Indirect { .. } => {
344-
llargument_tys.push(cx.type_ptr());
345-
cx.type_void()
346-
}
347-
};
392+
if indirect_return {
393+
llargument_tys.push(cx.type_ptr());
394+
}
348395

349396
for arg in args {
350397
// Note that the exact number of arguments pushed here is carefully synchronized with
@@ -391,10 +438,56 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
391438
llargument_tys.push(llarg_ty);
392439
}
393440

441+
llargument_tys
442+
}
443+
444+
fn verify_intrinsic_signature(&self, cx: &CodegenCx<'ll, 'tcx>, llvm_fn_ty: &'ll Type) -> bool {
445+
let rust_return_ty = self.llvm_return_type(cx);
446+
let rust_argument_tys = self.llvm_argument_types(cx);
447+
448+
let llvm_return_ty = cx.get_return_type(llvm_fn_ty);
449+
let llvm_argument_tys = cx.func_params_types(llvm_fn_ty);
450+
let llvm_is_variadic = cx.func_is_variadic(llvm_fn_ty);
451+
452+
if self.c_variadic != llvm_is_variadic || rust_argument_tys.len() != llvm_argument_tys.len()
453+
{
454+
return false;
455+
}
456+
457+
iter::once((rust_return_ty, llvm_return_ty))
458+
.chain(iter::zip(rust_argument_tys, llvm_argument_tys))
459+
.all(|(rust_ty, llvm_ty)| rust_ty == llvm_ty)
460+
}
461+
462+
fn rust_signature(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type {
463+
let return_ty = self.llvm_return_type(cx);
464+
let argument_tys = self.llvm_argument_types(cx);
465+
394466
if self.c_variadic {
395-
cx.type_variadic_func(&llargument_tys, llreturn_ty)
467+
cx.type_variadic_func(&argument_tys, return_ty)
396468
} else {
397-
cx.type_func(&llargument_tys, llreturn_ty)
469+
cx.type_func(&argument_tys, return_ty)
470+
}
471+
}
472+
473+
fn llvm_type(&self, cx: &CodegenCx<'ll, 'tcx>, name: &[u8]) -> FunctionSignature<'ll> {
474+
if name.starts_with(b"llvm.") {
475+
if let Some(intrinsic) = llvm::Intrinsic::lookup(name) {
476+
if !intrinsic.is_overloaded() {
477+
// FIXME: also do this for overloaded intrinsics
478+
FunctionSignature::LLVMSignature(intrinsic, intrinsic.get_type(cx.llcx, &[]))
479+
} else {
480+
FunctionSignature::RustSignature(intrinsic, self.rust_signature(cx))
481+
}
482+
} else {
483+
// it's one of 2 cases,
484+
// - either the base name is invalid
485+
// - it has been superseded by something else, so the intrinsic was removed entirely
486+
// to check for upgrades, we need the `llfn`, so we defer it for now
487+
FunctionSignature::MaybeInvalid(self.rust_signature(cx))
488+
}
489+
} else {
490+
FunctionSignature::NotIntrinsic(self.rust_signature(cx))
398491
}
399492
}
400493

@@ -574,7 +667,17 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
574667
}
575668
}
576669

577-
fn apply_attrs_callsite(&self, bx: &mut Builder<'_, 'll, 'tcx>, callsite: &'ll Value) {
670+
fn apply_attrs_callsite(
671+
&self,
672+
bx: &mut Builder<'_, 'll, 'tcx>,
673+
callsite: &'ll Value,
674+
llfn: &'ll Value,
675+
) {
676+
// Don't apply any attributes to LLVM intrinsics, they will be applied by AutoUpgrade
677+
if llvm::get_value_name(llfn).starts_with(b"llvm.") {
678+
return;
679+
}
680+
578681
let mut func_attrs = SmallVec::<[_; 2]>::new();
579682
if self.ret.layout.is_uninhabited() {
580683
func_attrs.push(llvm::AttributeKind::NoReturn.create_attr(bx.cx.llcx));

compiler/rustc_codegen_llvm/src/builder.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
442442
)
443443
};
444444
if let Some(fn_abi) = fn_abi {
445-
fn_abi.apply_attrs_callsite(self, invoke);
445+
fn_abi.apply_attrs_callsite(self, invoke, llfn);
446446
}
447447
invoke
448448
}
@@ -1436,7 +1436,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
14361436
}
14371437

14381438
if let Some(fn_abi) = fn_abi {
1439-
fn_abi.apply_attrs_callsite(self, call);
1439+
fn_abi.apply_attrs_callsite(self, call, llfn);
14401440
}
14411441
call
14421442
}
@@ -1779,7 +1779,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
17791779
)
17801780
};
17811781
if let Some(fn_abi) = fn_abi {
1782-
fn_abi.apply_attrs_callsite(self, callbr);
1782+
fn_abi.apply_attrs_callsite(self, callbr, llfn);
17831783
}
17841784
callbr
17851785
}

compiler/rustc_codegen_llvm/src/declare.rs

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

25-
use crate::abi::FnAbiLlvmExt;
26-
use crate::attributes;
25+
use crate::abi::{FnAbiLlvmExt, FunctionSignature};
2726
use crate::common::AsCCharPtr;
2827
use crate::context::{CodegenCx, GenericCx, SCx, SimpleCx};
2928
use crate::llvm::AttributePlace::Function;
3029
use crate::llvm::{self, FromGeneric, Type, Value, Visibility};
30+
use crate::{attributes, errors};
3131

3232
/// Declare a function with a SimpleCx.
3333
///
@@ -148,17 +148,39 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
148148
) -> &'ll Value {
149149
debug!("declare_rust_fn(name={:?}, fn_abi={:?})", name, fn_abi);
150150

151-
// Function addresses in Rust are never significant, allowing functions to
152-
// be merged.
151+
let signature = fn_abi.llvm_type(self, name.as_bytes());
152+
153+
let span = || instance.map(|instance| self.tcx.def_span(instance.def_id()));
154+
155+
if let FunctionSignature::LLVMSignature(_, llvm_fn_ty) = signature {
156+
// check if the intrinsic signatures match
157+
if !fn_abi.verify_intrinsic_signature(self, llvm_fn_ty) {
158+
self.tcx.dcx().emit_fatal(errors::IntrinsicSignatureMismatch {
159+
name,
160+
llvm_fn_ty: &format!("{llvm_fn_ty:?}"),
161+
rust_fn_ty: &format!("{:?}", fn_abi.rust_signature(self)),
162+
span: span(),
163+
});
164+
}
165+
}
166+
167+
// Function addresses in Rust are never significant, allowing functions to
168+
// be merged.
153169
let llfn = declare_raw_fn(
154-
self,
155-
name,
156-
fn_abi.llvm_cconv(self),
157-
llvm::UnnamedAddr::Global,
158-
llvm::Visibility::Default,
159-
fn_abi.llvm_type(self),
160-
);
161-
fn_abi.apply_attrs_llfn(self, llfn, instance);
170+
self,
171+
name,
172+
fn_abi.llvm_cconv(self),
173+
llvm::UnnamedAddr::Global,
174+
llvm::Visibility::Default,
175+
signature.fn_ty(),
176+
);
177+
178+
if signature.intrinsic().is_none() {
179+
// Don't apply any attributes to intrinsics, they will be applied by AutoUpgrade
180+
fn_abi.apply_attrs_llfn(self, llfn, instance);
181+
}
182+
183+
// todo: check for upgrades, and emit error if not upgradable
162184

163185
if self.tcx.sess.is_sanitizer_cfi_enabled() {
164186
if let Some(instance) = instance {

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
@@ -1073,7 +1073,7 @@ fn gen_fn<'a, 'll, 'tcx>(
10731073
codegen: &mut dyn FnMut(Builder<'a, 'll, 'tcx>),
10741074
) -> (&'ll Type, &'ll Value) {
10751075
let fn_abi = cx.fn_abi_of_fn_ptr(rust_fn_sig, ty::List::empty());
1076-
let llty = fn_abi.llvm_type(cx);
1076+
let llty = fn_abi.llvm_type(cx, name.as_bytes()).fn_ty();
10771077
let llfn = cx.declare_fn(name, fn_abi, None);
10781078
cx.set_frame_pointer_type(llfn);
10791079
cx.apply_target_cpu_attr(llfn);

compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,6 @@ unsafe extern "C" {
7373
pub(crate) fn LLVMDumpModule(M: &Module);
7474
pub(crate) fn LLVMDumpValue(V: &Value);
7575
pub(crate) fn LLVMGetFunctionCallConv(F: &Value) -> c_uint;
76-
pub(crate) fn LLVMGetReturnType(T: &Type) -> &Type;
7776
pub(crate) fn LLVMGetParams(Fnc: &Value, params: *mut &Value);
7877
pub(crate) fn LLVMGetNamedFunction(M: &Module, Name: *const c_char) -> Option<&Value>;
7978
}

compiler/rustc_codegen_llvm/src/llvm/ffi.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -983,6 +983,8 @@ unsafe extern "C" {
983983
) -> &'a Type;
984984
pub(crate) fn LLVMCountParamTypes(FunctionTy: &Type) -> c_uint;
985985
pub(crate) fn LLVMGetParamTypes<'a>(FunctionTy: &'a Type, Dest: *mut &'a Type);
986+
pub(crate) fn LLVMGetReturnType(FunctionTy: &Type) -> &Type;
987+
pub(crate) fn LLVMIsFunctionVarArg(FunctionTy: &Type) -> Bool;
986988

987989
// Operations on struct types
988990
pub(crate) fn LLVMStructTypeInContext<'a>(
@@ -1131,6 +1133,13 @@ unsafe extern "C" {
11311133

11321134
// Operations about llvm intrinsics
11331135
pub(crate) fn LLVMLookupIntrinsicID(Name: *const c_char, NameLen: size_t) -> c_uint;
1136+
pub(crate) fn LLVMIntrinsicIsOverloaded(ID: NonZero<c_uint>) -> Bool;
1137+
pub(crate) fn LLVMIntrinsicGetType<'a>(
1138+
C: &'a Context,
1139+
ID: NonZero<c_uint>,
1140+
ParamTypes: *const &'a Type,
1141+
ParamCount: size_t,
1142+
) -> &'a Type;
11341143
pub(crate) fn LLVMGetIntrinsicDeclaration<'a>(
11351144
Mod: &'a Module,
11361145
ID: NonZero<c_uint>,

compiler/rustc_codegen_llvm/src/llvm/mod.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,14 @@ impl Intrinsic {
311311
NonZero::new(id).map(|id| Self { id })
312312
}
313313

314+
pub(crate) fn is_overloaded(self) -> bool {
315+
unsafe { LLVMIntrinsicIsOverloaded(self.id).is_true() }
316+
}
317+
318+
pub(crate) fn get_type<'ll>(self, llcx: &'ll Context, type_params: &[&'ll Type]) -> &'ll Type {
319+
unsafe { LLVMIntrinsicGetType(llcx, self.id, type_params.as_ptr(), type_params.len()) }
320+
}
321+
314322
pub(crate) fn get_declaration<'ll>(
315323
self,
316324
llmod: &'ll Module,

0 commit comments

Comments
 (0)