Skip to content

Commit b247a7b

Browse files
committed
Codegen **non-overloaded** LLVM intrinsics using their name
1 parent 5767910 commit b247a7b

File tree

13 files changed

+216
-46
lines changed

13 files changed

+216
-46
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/src/abi.rs

Lines changed: 137 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
use std::cmp;
1+
use std::borrow::Borrow;
2+
use std::{cmp, iter};
23

34
use libc::c_uint;
45
use rustc_abi::{
@@ -301,8 +302,37 @@ impl<'ll, 'tcx> ArgAbiBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
301302
}
302303
}
303304

305+
pub(crate) enum FunctionSignature<'ll> {
306+
/// The signature is obtained directly from LLVM, and **may not match the Rust signature**
307+
Intrinsic(&'ll Type),
308+
/// The name starts with `llvm.`, but can't obtain the intrinsic ID. May be invalid or upgradable
309+
MaybeInvalidIntrinsic(&'ll Type),
310+
/// Just the Rust signature
311+
Rust(&'ll Type),
312+
}
313+
314+
impl<'ll> FunctionSignature<'ll> {
315+
pub(crate) fn fn_ty(&self) -> &'ll Type {
316+
match self {
317+
FunctionSignature::Intrinsic(fn_ty)
318+
| FunctionSignature::MaybeInvalidIntrinsic(fn_ty)
319+
| FunctionSignature::Rust(fn_ty) => fn_ty,
320+
}
321+
}
322+
}
323+
304324
pub(crate) trait FnAbiLlvmExt<'ll, 'tcx> {
305-
fn llvm_type(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type;
325+
fn llvm_return_type(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type;
326+
fn llvm_argument_types(&self, cx: &CodegenCx<'ll, 'tcx>) -> Vec<&'ll Type>;
327+
fn llvm_type(
328+
&self,
329+
cx: &CodegenCx<'ll, 'tcx>,
330+
name: &[u8],
331+
do_verify: bool,
332+
) -> FunctionSignature<'ll>;
333+
/// **If this function is an LLVM intrinsic** checks if the LLVM signature provided matches with this
334+
fn verify_intrinsic_signature(&self, cx: &CodegenCx<'ll, 'tcx>, llvm_ty: &'ll Type) -> bool;
335+
306336
fn ptr_to_llvm_type(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type;
307337
fn llvm_cconv(&self, cx: &CodegenCx<'ll, 'tcx>) -> llvm::CallConv;
308338

@@ -315,30 +345,39 @@ pub(crate) trait FnAbiLlvmExt<'ll, 'tcx> {
315345
);
316346

317347
/// Apply attributes to a function call.
318-
fn apply_attrs_callsite(&self, bx: &mut Builder<'_, 'll, 'tcx>, callsite: &'ll Value);
348+
fn apply_attrs_callsite(
349+
&self,
350+
bx: &mut Builder<'_, 'll, 'tcx>,
351+
callsite: &'ll Value,
352+
llfn: &'ll Value,
353+
);
319354
}
320355

321356
impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
322-
fn llvm_type(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type {
357+
fn llvm_return_type(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type {
358+
match &self.ret.mode {
359+
PassMode::Ignore => cx.type_void(),
360+
PassMode::Direct(_) | PassMode::Pair(..) => self.ret.layout.immediate_llvm_type(cx),
361+
PassMode::Cast { cast, pad_i32: _ } => cast.llvm_type(cx),
362+
PassMode::Indirect { .. } => cx.type_void(),
363+
}
364+
}
365+
366+
fn llvm_argument_types(&self, cx: &CodegenCx<'ll, 'tcx>) -> Vec<&'ll Type> {
367+
let indirect_return = matches!(self.ret.mode, PassMode::Indirect { .. });
368+
323369
// Ignore "extra" args from the call site for C variadic functions.
324370
// Only the "fixed" args are part of the LLVM function signature.
325371
let args =
326372
if self.c_variadic { &self.args[..self.fixed_count as usize] } else { &self.args };
327373

328374
// This capacity calculation is approximate.
329-
let mut llargument_tys = Vec::with_capacity(
330-
self.args.len() + if let PassMode::Indirect { .. } = self.ret.mode { 1 } else { 0 },
331-
);
375+
let mut llargument_tys =
376+
Vec::with_capacity(args.len() + if indirect_return { 1 } else { 0 });
332377

333-
let llreturn_ty = match &self.ret.mode {
334-
PassMode::Ignore => cx.type_void(),
335-
PassMode::Direct(_) | PassMode::Pair(..) => self.ret.layout.immediate_llvm_type(cx),
336-
PassMode::Cast { cast, pad_i32: _ } => cast.llvm_type(cx),
337-
PassMode::Indirect { .. } => {
338-
llargument_tys.push(cx.type_ptr());
339-
cx.type_void()
340-
}
341-
};
378+
if indirect_return {
379+
llargument_tys.push(cx.type_ptr());
380+
}
342381

343382
for arg in args {
344383
// Note that the exact number of arguments pushed here is carefully synchronized with
@@ -385,10 +424,72 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
385424
llargument_tys.push(llarg_ty);
386425
}
387426

388-
if self.c_variadic {
389-
cx.type_variadic_func(&llargument_tys, llreturn_ty)
427+
llargument_tys
428+
}
429+
430+
fn verify_intrinsic_signature(&self, cx: &CodegenCx<'ll, 'tcx>, llvm_fn_ty: &'ll Type) -> bool {
431+
let rust_return_ty = self.llvm_return_type(cx);
432+
let rust_argument_tys = self.llvm_argument_types(cx);
433+
434+
let llvm_return_ty = cx.get_return_type(llvm_fn_ty);
435+
let llvm_argument_tys = cx.func_params_types(llvm_fn_ty);
436+
let llvm_is_variadic = cx.func_is_variadic(llvm_fn_ty);
437+
438+
if self.c_variadic != llvm_is_variadic || rust_argument_tys.len() != llvm_argument_tys.len()
439+
{
440+
return false;
441+
}
442+
443+
iter::once((rust_return_ty, llvm_return_ty))
444+
.chain(iter::zip(rust_argument_tys, llvm_argument_tys))
445+
.all(|(rust_ty, llvm_ty)| rust_ty == llvm_ty)
446+
}
447+
448+
fn llvm_type(
449+
&self,
450+
cx: &CodegenCx<'ll, 'tcx>,
451+
name: &[u8],
452+
do_verify: bool,
453+
) -> FunctionSignature<'ll> {
454+
let mut maybe_invalid = false;
455+
456+
if name.starts_with(b"llvm.") {
457+
if let Some(intrinsic) = llvm::Intrinsic::lookup(name) {
458+
if !intrinsic.is_overloaded() {
459+
// FIXME: also do this for overloaded intrinsics
460+
let llvm_fn_ty = intrinsic.get_type(cx.llcx, &[]);
461+
if do_verify {
462+
if !self.verify_intrinsic_signature(cx, llvm_fn_ty) {
463+
cx.tcx.dcx().fatal(format!(
464+
"Intrinsic signature mismatch for `{}`: expected signature `{llvm_fn_ty:?}`",
465+
str::from_utf8(name).unwrap()
466+
));
467+
}
468+
}
469+
return FunctionSignature::Intrinsic(llvm_fn_ty);
470+
}
471+
} else {
472+
// it's one of 2 cases,
473+
// - either the base name is invalid
474+
// - it has been superseded by something else, so the intrinsic was removed entirely
475+
// to check for upgrades, we need the `llfn`, so we defer it for now
476+
maybe_invalid = true;
477+
}
478+
}
479+
480+
let return_ty = self.llvm_return_type(cx);
481+
let argument_tys = self.llvm_argument_types(cx);
482+
483+
let fn_ty = if self.c_variadic {
484+
cx.type_variadic_func(&argument_tys, return_ty)
390485
} else {
391-
cx.type_func(&llargument_tys, llreturn_ty)
486+
cx.type_func(&argument_tys, return_ty)
487+
};
488+
489+
if maybe_invalid {
490+
FunctionSignature::MaybeInvalidIntrinsic(fn_ty)
491+
} else {
492+
FunctionSignature::Rust(fn_ty)
392493
}
393494
}
394495

@@ -546,7 +647,23 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
546647
}
547648
}
548649

549-
fn apply_attrs_callsite(&self, bx: &mut Builder<'_, 'll, 'tcx>, callsite: &'ll Value) {
650+
fn apply_attrs_callsite(
651+
&self,
652+
bx: &mut Builder<'_, 'll, 'tcx>,
653+
callsite: &'ll Value,
654+
llfn: &'ll Value,
655+
) {
656+
// if we are using the LLVM signature, use the LLVM attributes otherwise it might be problematic
657+
let name = llvm::get_value_name(llfn);
658+
if name.starts_with(b"llvm.")
659+
&& let Some(intrinsic) = llvm::Intrinsic::lookup(&name)
660+
{
661+
// FIXME: also do this for overloaded intrinsics
662+
if !intrinsic.is_overloaded() {
663+
return;
664+
}
665+
}
666+
550667
let mut func_attrs = SmallVec::<[_; 2]>::new();
551668
if self.ret.layout.is_uninhabited() {
552669
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
@@ -443,7 +443,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
443443
)
444444
};
445445
if let Some(fn_abi) = fn_abi {
446-
fn_abi.apply_attrs_callsite(self, invoke);
446+
fn_abi.apply_attrs_callsite(self, invoke, llfn);
447447
}
448448
invoke
449449
}
@@ -1445,7 +1445,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
14451445
}
14461446

14471447
if let Some(fn_abi) = fn_abi {
1448-
fn_abi.apply_attrs_callsite(self, call);
1448+
fn_abi.apply_attrs_callsite(self, call, llfn);
14491449
}
14501450
call
14511451
}
@@ -1785,7 +1785,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
17851785
)
17861786
};
17871787
if let Some(fn_abi) = fn_abi {
1788-
fn_abi.apply_attrs_callsite(self, callbr);
1788+
fn_abi.apply_attrs_callsite(self, callbr, llfn);
17891789
}
17901790
callbr
17911791
}

compiler/rustc_codegen_llvm/src/declare.rs

Lines changed: 29 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::attributes;
2727
use crate::common::AsCCharPtr;
2828
use crate::context::{CodegenCx, GenericCx, SCx, SimpleCx};
@@ -148,17 +148,34 @@ 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.
153-
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);
151+
let signature = fn_abi.llvm_type(self, name.as_bytes(), true);
152+
let llfn;
153+
154+
if let FunctionSignature::Intrinsic(fn_ty) = signature {
155+
// intrinsics have a specified set of attributes, so we don't use the `FnAbi` set for them
156+
llfn = declare_simple_fn(
157+
self,
158+
name,
159+
fn_abi.llvm_cconv(self),
160+
llvm::UnnamedAddr::Global,
161+
llvm::Visibility::Default,
162+
fn_ty,
163+
);
164+
} else {
165+
// Function addresses in Rust are never significant, allowing functions to
166+
// be merged.
167+
llfn = declare_raw_fn(
168+
self,
169+
name,
170+
fn_abi.llvm_cconv(self),
171+
llvm::UnnamedAddr::Global,
172+
llvm::Visibility::Default,
173+
signature.fn_ty(),
174+
);
175+
fn_abi.apply_attrs_llfn(self, llfn, instance);
176+
}
177+
178+
// todo: check for upgrades, and emit error if not upgradable
162179

163180
if self.tcx.sess.is_sanitizer_cfi_enabled() {
164181
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
@@ -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(), true).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
@@ -972,6 +972,8 @@ unsafe extern "C" {
972972
) -> &'a Type;
973973
pub(crate) fn LLVMCountParamTypes(FunctionTy: &Type) -> c_uint;
974974
pub(crate) fn LLVMGetParamTypes<'a>(FunctionTy: &'a Type, Dest: *mut &'a Type);
975+
pub(crate) fn LLVMGetReturnType(FunctionTy: &Type) -> &Type;
976+
pub(crate) fn LLVMIsFunctionVarArg(FunctionTy: &Type) -> Bool;
975977

976978
// Operations on struct types
977979
pub(crate) fn LLVMStructTypeInContext<'a>(
@@ -1119,6 +1121,13 @@ unsafe extern "C" {
11191121

11201122
// Operations about llvm intrinsics
11211123
pub(crate) fn LLVMLookupIntrinsicID(Name: *const c_char, NameLen: size_t) -> c_uint;
1124+
pub(crate) fn LLVMIntrinsicIsOverloaded(ID: NonZero<c_uint>) -> Bool;
1125+
pub(crate) fn LLVMIntrinsicGetType<'a>(
1126+
C: &'a Context,
1127+
ID: NonZero<c_uint>,
1128+
ParamTypes: *const &'a Type,
1129+
ParamCount: size_t,
1130+
) -> &'a Type;
11221131
pub(crate) fn LLVMGetIntrinsicDeclaration<'a>(
11231132
Mod: &'a Module,
11241133
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,

compiler/rustc_codegen_llvm/src/type_.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ impl<'ll, CX: Borrow<SCx<'ll>>> GenericCx<'ll, CX> {
6868
unsafe { llvm::LLVMVectorType(ty, len as c_uint) }
6969
}
7070

71+
pub(crate) fn get_return_type(&self, ty: &'ll Type) -> &'ll Type {
72+
unsafe { llvm::LLVMGetReturnType(ty) }
73+
}
74+
7175
pub(crate) fn func_params_types(&self, ty: &'ll Type) -> Vec<&'ll Type> {
7276
unsafe {
7377
let n_args = llvm::LLVMCountParamTypes(ty) as usize;
@@ -77,6 +81,10 @@ impl<'ll, CX: Borrow<SCx<'ll>>> GenericCx<'ll, CX> {
7781
args
7882
}
7983
}
84+
85+
pub(crate) fn func_is_variadic(&self, ty: &'ll Type) -> bool {
86+
unsafe { llvm::LLVMIsFunctionVarArg(ty).is_true() }
87+
}
8088
}
8189
impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
8290
pub(crate) fn type_bool(&self) -> &'ll Type {
@@ -286,8 +294,12 @@ impl<'ll, 'tcx> LayoutTypeCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
286294
fn cast_backend_type(&self, ty: &CastTarget) -> &'ll Type {
287295
ty.llvm_type(self)
288296
}
289-
fn fn_decl_backend_type(&self, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> &'ll Type {
290-
fn_abi.llvm_type(self)
297+
fn fn_decl_backend_type(
298+
&self,
299+
fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
300+
fn_ptr: &'ll Value,
301+
) -> &'ll Type {
302+
fn_abi.llvm_type(self, &llvm::get_value_name(fn_ptr), false).fn_ty()
291303
}
292304
fn fn_ptr_backend_type(&self, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> &'ll Type {
293305
fn_abi.ptr_to_llvm_type(self)

0 commit comments

Comments
 (0)