Skip to content

Commit 3e4faa5

Browse files
committed
Codegen **non-overloaded** LLVM intrinsics using their name
1 parent cc87afd commit 3e4faa5

File tree

13 files changed

+224
-46
lines changed

13 files changed

+224
-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: 140 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use std::borrow::Borrow;
2-
use std::cmp;
2+
use std::{cmp, iter};
33

44
use libc::c_uint;
55
use rustc_abi::{
@@ -300,8 +300,39 @@ impl<'ll, 'tcx> ArgAbiBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
300300
}
301301
}
302302

303+
pub(crate) enum FunctionSignature<'ll> {
304+
/// The signature is obtained directly from LLVM, and **may not match the Rust signature**
305+
Intrinsic(&'ll Type),
306+
/// The name starts with `llvm.`, but can't obtain the intrinsic ID. May be invalid or upgradable
307+
MaybeInvalidIntrinsic(&'ll Type),
308+
/// Just the Rust signature
309+
Rust(&'ll Type),
310+
}
311+
312+
impl<'ll> FunctionSignature<'ll> {
313+
pub(crate) fn fn_ty(&self) -> &'ll Type {
314+
match self {
315+
FunctionSignature::Intrinsic(fn_ty)
316+
| FunctionSignature::MaybeInvalidIntrinsic(fn_ty)
317+
| FunctionSignature::Rust(fn_ty) => fn_ty,
318+
}
319+
}
320+
}
321+
303322
pub(crate) trait FnAbiLlvmExt<'ll, 'tcx> {
304-
fn llvm_type(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type;
323+
fn llvm_return_type(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type;
324+
fn llvm_argument_types(&self, cx: &CodegenCx<'ll, 'tcx>) -> Vec<&'ll Type>;
325+
/// When `do_verify` is set, this function performs checks for the signature of LLVM intrinsics
326+
/// and emits a fatal error if it doesn't match. These checks are important,but somewhat expensive
327+
/// So they are only used at function definitions, not at callsites
328+
fn llvm_type(
329+
&self,
330+
cx: &CodegenCx<'ll, 'tcx>,
331+
name: &[u8],
332+
do_verify: bool,
333+
) -> FunctionSignature<'ll>;
334+
/// **If this function is an LLVM intrinsic** checks if the LLVM signature provided matches with this
335+
fn verify_intrinsic_signature(&self, cx: &CodegenCx<'ll, 'tcx>, llvm_ty: &'ll Type) -> bool;
305336
fn ptr_to_llvm_type(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type;
306337
fn llvm_cconv(&self, cx: &CodegenCx<'ll, 'tcx>) -> llvm::CallConv;
307338

@@ -314,30 +345,38 @@ pub(crate) trait FnAbiLlvmExt<'ll, 'tcx> {
314345
);
315346

316347
/// Apply attributes to a function call.
317-
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+
);
318354
}
319355

320356
impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
321-
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+
322369
// Ignore "extra" args from the call site for C variadic functions.
323370
// Only the "fixed" args are part of the LLVM function signature.
324371
let args =
325372
if self.c_variadic { &self.args[..self.fixed_count as usize] } else { &self.args };
326373

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

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

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

387-
if self.c_variadic {
388-
cx.type_variadic_func(&llargument_tys, llreturn_ty)
426+
llargument_tys
427+
}
428+
429+
fn verify_intrinsic_signature(&self, cx: &CodegenCx<'ll, 'tcx>, llvm_fn_ty: &'ll Type) -> bool {
430+
let rust_return_ty = self.llvm_return_type(cx);
431+
let rust_argument_tys = self.llvm_argument_types(cx);
432+
433+
let llvm_return_ty = cx.get_return_type(llvm_fn_ty);
434+
let llvm_argument_tys = cx.func_params_types(llvm_fn_ty);
435+
let llvm_is_variadic = cx.func_is_variadic(llvm_fn_ty);
436+
437+
if self.c_variadic != llvm_is_variadic || rust_argument_tys.len() != llvm_argument_tys.len()
438+
{
439+
return false;
440+
}
441+
442+
// todo: add bypasses for types not accessible from Rust here
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 = cx.intrinsic_type(intrinsic, &[]);
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 superceded 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+
477+
maybe_invalid = true;
478+
}
479+
}
480+
481+
let return_ty = self.llvm_return_type(cx);
482+
let argument_tys = self.llvm_argument_types(cx);
483+
484+
let fn_ty = if self.c_variadic {
485+
cx.type_variadic_func(&argument_tys, return_ty)
389486
} else {
390-
cx.type_func(&llargument_tys, llreturn_ty)
487+
cx.type_func(&argument_tys, return_ty)
488+
};
489+
490+
if maybe_invalid {
491+
FunctionSignature::MaybeInvalidIntrinsic(fn_ty)
492+
} else {
493+
FunctionSignature::Rust(fn_ty)
391494
}
392495
}
393496

@@ -530,7 +633,23 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
530633
}
531634
}
532635

533-
fn apply_attrs_callsite(&self, bx: &mut Builder<'_, 'll, 'tcx>, callsite: &'ll Value) {
636+
fn apply_attrs_callsite(
637+
&self,
638+
bx: &mut Builder<'_, 'll, 'tcx>,
639+
callsite: &'ll Value,
640+
llfn: &'ll Value,
641+
) {
642+
// if we are using the LLVM signature, use the LLVM attributes otherwise it might be problematic
643+
let name = llvm::get_value_name(llfn);
644+
if name.starts_with(b"llvm.")
645+
&& let Some(intrinsic) = llvm::Intrinsic::lookup(name)
646+
{
647+
// FIXME: also do this for overloaded intrinsics
648+
if !intrinsic.is_overloaded() {
649+
return;
650+
}
651+
}
652+
534653
let mut func_attrs = SmallVec::<[_; 2]>::new();
535654
if self.ret.layout.is_uninhabited() {
536655
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
@@ -379,7 +379,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
379379
)
380380
};
381381
if let Some(fn_abi) = fn_abi {
382-
fn_abi.apply_attrs_callsite(self, invoke);
382+
fn_abi.apply_attrs_callsite(self, invoke, llfn);
383383
}
384384
invoke
385385
}
@@ -1372,7 +1372,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
13721372
)
13731373
};
13741374
if let Some(fn_abi) = fn_abi {
1375-
fn_abi.apply_attrs_callsite(self, call);
1375+
fn_abi.apply_attrs_callsite(self, call, llfn);
13761376
}
13771377
call
13781378
}
@@ -1694,7 +1694,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
16941694
)
16951695
};
16961696
if let Some(fn_abi) = fn_abi {
1697-
fn_abi.apply_attrs_callsite(self, callbr);
1697+
fn_abi.apply_attrs_callsite(self, callbr, llfn);
16981698
}
16991699
callbr
17001700
}

compiler/rustc_codegen_llvm/src/context.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1058,6 +1058,21 @@ impl<'ll> CodegenCx<'ll, '_> {
10581058
self.eh_catch_typeinfo.set(Some(eh_catch_typeinfo));
10591059
eh_catch_typeinfo
10601060
}
1061+
1062+
pub(crate) fn intrinsic_type(
1063+
&self,
1064+
intrinsic: llvm::Intrinsic,
1065+
type_params: &[&'ll Type],
1066+
) -> &'ll Type {
1067+
unsafe {
1068+
llvm::LLVMIntrinsicGetType(
1069+
self.llcx(),
1070+
intrinsic.id(),
1071+
type_params.as_ptr(),
1072+
type_params.len(),
1073+
)
1074+
}
1075+
}
10611076
}
10621077

10631078
impl CodegenCx<'_, '_> {

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::common::AsCCharPtr;
2727
use crate::context::{CodegenCx, GenericCx, SCx, SimpleCx};
2828
use crate::llvm::AttributePlace::Function;
@@ -150,17 +150,34 @@ 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),
162-
);
163-
fn_abi.apply_attrs_llfn(self, llfn, instance);
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+
);
177+
fn_abi.apply_attrs_llfn(self, llfn, instance);
178+
}
179+
180+
// todo: check for upgrades, and emit error if not upgradable
164181

165182
if self.tcx.sess.is_sanitizer_cfi_enabled() {
166183
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
@@ -1054,7 +1054,7 @@ fn gen_fn<'a, 'll, 'tcx>(
10541054
codegen: &mut dyn FnMut(Builder<'a, 'll, 'tcx>),
10551055
) -> (&'ll Type, &'ll Value) {
10561056
let fn_abi = cx.fn_abi_of_fn_ptr(rust_fn_sig, ty::List::empty());
1057-
let llty = fn_abi.llvm_type(cx);
1057+
let llty = fn_abi.llvm_type(cx, name.as_bytes(), true).fn_ty();
10581058
let llfn = cx.declare_fn(name, fn_abi, None);
10591059
cx.set_frame_pointer_type(llfn);
10601060
cx.apply_target_cpu_attr(llfn);

compiler/rustc_codegen_llvm/src/llvm/ffi.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1060,6 +1060,7 @@ unsafe extern "C" {
10601060
) -> &'a Type;
10611061
pub(crate) fn LLVMCountParamTypes(FunctionTy: &Type) -> c_uint;
10621062
pub(crate) fn LLVMGetParamTypes<'a>(FunctionTy: &'a Type, Dest: *mut &'a Type);
1063+
pub(crate) fn LLVMIsFunctionVarArg(FunctionTy: &Type) -> Bool;
10631064

10641065
// Operations on struct types
10651066
pub(crate) fn LLVMStructTypeInContext<'a>(
@@ -1206,6 +1207,12 @@ unsafe extern "C" {
12061207
ParamCount: size_t,
12071208
NameLength: *mut size_t,
12081209
) -> *mut c_char;
1210+
pub(crate) fn LLVMIntrinsicGetType<'a>(
1211+
C: &'a Context,
1212+
ID: NonZero<c_uint>,
1213+
ParamTypes: *const &'a Type,
1214+
ParamCount: size_t,
1215+
) -> &'a Type;
12091216

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

compiler/rustc_codegen_llvm/src/llvm/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,10 @@ impl Intrinsic {
339339
NonZero::new(id).map(|id| Self { id })
340340
}
341341

342+
pub(crate) fn id(self) -> NonZero<c_uint> {
343+
self.id
344+
}
345+
342346
pub(crate) fn is_overloaded(self) -> bool {
343347
unsafe { LLVMIntrinsicIsOverloaded(self.id) == True }
344348
}

compiler/rustc_codegen_llvm/src/type_.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,10 @@ impl<'ll, CX: Borrow<SCx<'ll>>> GenericCx<'ll, CX> {
8484
args
8585
}
8686
}
87+
88+
pub(crate) fn func_is_variadic(&self, ty: &'ll Type) -> bool {
89+
unsafe { llvm::LLVMIsFunctionVarArg(ty) == True }
90+
}
8791
}
8892
impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
8993
pub(crate) fn type_bool(&self) -> &'ll Type {
@@ -293,8 +297,12 @@ impl<'ll, 'tcx> LayoutTypeCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
293297
fn cast_backend_type(&self, ty: &CastTarget) -> &'ll Type {
294298
ty.llvm_type(self)
295299
}
296-
fn fn_decl_backend_type(&self, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> &'ll Type {
297-
fn_abi.llvm_type(self)
300+
fn fn_decl_backend_type(
301+
&self,
302+
fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
303+
fn_ptr: &'ll Value,
304+
) -> &'ll Type {
305+
fn_abi.llvm_type(self, llvm::get_value_name(fn_ptr), false).fn_ty()
298306
}
299307
fn fn_ptr_backend_type(&self, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> &'ll Type {
300308
fn_abi.ptr_to_llvm_type(self)

0 commit comments

Comments
 (0)