Skip to content

Commit 385036e

Browse files
committed
codegen non-overloaded llvm intrinsics based on their name, and verify the rust signature against it
1 parent f4ba07f commit 385036e

File tree

14 files changed

+245
-61
lines changed

14 files changed

+245
-61
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: 110 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::borrow::Borrow;
22
use std::cmp;
33

4+
use itertools::zip_eq;
45
use libc::c_uint;
56
use rustc_abi::{BackendRepr, HasDataLayout, Primitive, Reg, RegKind, Size};
67
use rustc_codegen_ssa::MemFlags;
@@ -308,7 +309,9 @@ impl<'ll, 'tcx> ArgAbiBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
308309
}
309310

310311
pub(crate) trait FnAbiLlvmExt<'ll, 'tcx> {
311-
fn llvm_type(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type;
312+
fn llvm_return_type(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type;
313+
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;
312315
fn ptr_to_llvm_type(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type;
313316
fn llvm_cconv(&self, cx: &CodegenCx<'ll, 'tcx>) -> llvm::CallConv;
314317

@@ -324,27 +327,83 @@ pub(crate) trait FnAbiLlvmExt<'ll, 'tcx> {
324327
fn apply_attrs_callsite(&self, bx: &mut Builder<'_, 'll, 'tcx>, callsite: &'ll Value);
325328
}
326329

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,
334+
rust_return_ty: &'ll Type,
335+
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+
354+
let llvm_return_ty = cx.get_return_type(fn_ty);
355+
let llvm_argument_tys = cx.func_params_types(fn_ty);
356+
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+
);
363+
}
364+
365+
if 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+
zip_eq(rust_argument_tys, llvm_argument_tys).enumerate()
372+
{
373+
if 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
381+
}
382+
327383
impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
328-
fn llvm_type(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type {
384+
fn llvm_return_type(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type {
385+
match &self.ret.mode {
386+
PassMode::Ignore => cx.type_void(),
387+
PassMode::Direct(_) | PassMode::Pair(..) => self.ret.layout.immediate_llvm_type(cx),
388+
PassMode::Cast { cast, pad_i32: _ } => cast.llvm_type(cx),
389+
PassMode::Indirect { .. } => cx.type_void(),
390+
}
391+
}
392+
393+
fn llvm_argument_types(&self, cx: &CodegenCx<'ll, 'tcx>) -> Vec<&'ll Type> {
394+
let indirect_return = matches!(self.ret.mode, PassMode::Indirect { .. });
395+
329396
// Ignore "extra" args from the call site for C variadic functions.
330397
// Only the "fixed" args are part of the LLVM function signature.
331398
let args =
332399
if self.c_variadic { &self.args[..self.fixed_count as usize] } else { &self.args };
333400

334-
// 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-
);
401+
let mut llargument_tys =
402+
Vec::with_capacity(args.len() + if indirect_return { 1 } else { 0 });
338403

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-
};
404+
if indirect_return {
405+
llargument_tys.push(cx.type_ptr());
406+
}
348407

349408
for arg in args {
350409
// Note that the exact number of arguments pushed here is carefully synchronized with
@@ -391,10 +450,44 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
391450
llargument_tys.push(llarg_ty);
392451
}
393452

453+
llargument_tys
454+
}
455+
456+
fn llvm_type(&self, cx: &CodegenCx<'ll, 'tcx>, name: &[u8]) -> &'ll Type {
457+
let return_ty = self.llvm_return_type(cx);
458+
let argument_tys = self.llvm_argument_types(cx);
459+
460+
if name.starts_with(b"llvm.") {
461+
if let Some(intrinsic) = llvm::Intrinsic::lookup(name)
462+
&& !intrinsic.is_overloaded()
463+
{
464+
if !intrinsic.is_overloaded() {
465+
// FIXME: also do this for overloaded intrinsics
466+
return match_intrinsic_signature(
467+
cx,
468+
intrinsic,
469+
return_ty,
470+
&argument_tys,
471+
name,
472+
);
473+
}
474+
} else {
475+
// it's one of 2 cases,
476+
// - either the base name is invalid
477+
// - it has been superceded by something else, so the intrinsic was removed entirely
478+
//
479+
// anyway, let's log it
480+
tracing::warn!(
481+
"Couldn't find intrinsic `{}`, either invalid or deprecated",
482+
str::from_utf8(name).unwrap()
483+
);
484+
}
485+
}
486+
394487
if self.c_variadic {
395-
cx.type_variadic_func(&llargument_tys, llreturn_ty)
488+
cx.type_variadic_func(&argument_tys, return_ty)
396489
} else {
397-
cx.type_func(&llargument_tys, llreturn_ty)
490+
cx.type_func(&argument_tys, return_ty)
398491
}
399492
}
400493

compiler/rustc_codegen_llvm/src/builder.rs

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ use tracing::{debug, instrument};
3131

3232
use crate::abi::FnAbiLlvmExt;
3333
use crate::attributes;
34-
use crate::common::Funclet;
34+
use crate::common::{AsCCharPtr, Funclet};
3535
use crate::context::{CodegenCx, FullCx, GenericCx, SCx};
3636
use crate::llvm::{
3737
self, AtomicOrdering, AtomicRmwBinOp, BasicBlock, False, GEPNoWrapFlags, Metadata, True,
@@ -1611,15 +1611,33 @@ impl<'a, 'll, CX: Borrow<SCx<'ll>>> GenericBuilder<'a, 'll, CX> {
16111611
type_params: &[&'ll Type],
16121612
args: &[&'ll Value],
16131613
) -> &'ll Value {
1614+
// todo: cache?
1615+
16141616
let intrinsic = llvm::Intrinsic::lookup(base_name.as_bytes())
16151617
.unwrap_or_else(|| bug!("Intrinsic `{base_name}` not found"));
1616-
let (fn_ty, llfn) = self.cx.get_intrinsic(intrinsic, type_params);
1618+
1619+
let fn_ty = self.cx.intrinsic_type(intrinsic, type_params);
1620+
1621+
let full_name = if intrinsic.is_overloaded() {
1622+
&intrinsic.overloaded_name(self.cx.llmod(), type_params)
1623+
} else {
1624+
assert!(type_params.is_empty());
1625+
base_name.as_bytes()
1626+
};
1627+
let llfn = unsafe {
1628+
llvm::LLVMRustGetOrInsertFunction(
1629+
self.cx.llmod(),
1630+
full_name.as_c_char_ptr(),
1631+
full_name.len(),
1632+
fn_ty,
1633+
)
1634+
};
16171635
self.simple_call(fn_ty, llfn, args)
16181636
}
16191637
}
16201638

16211639
impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
1622-
fn call_lifetime_intrinsic(&mut self, intrinsic: &str, ptr: &'ll Value, size: Size) {
1640+
fn call_lifetime_intrinsic(&mut self, intrinsic: &'static str, ptr: &'ll Value, size: Size) {
16231641
let size = size.bytes();
16241642
if size == 0 {
16251643
return;

compiler/rustc_codegen_llvm/src/context.rs

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -836,25 +836,17 @@ impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
836836
}
837837

838838
impl<'ll, CX: Borrow<SCx<'ll>>> GenericCx<'ll, CX> {
839-
pub(crate) fn get_intrinsic(
839+
pub(crate) fn intrinsic_type(
840840
&self,
841841
intrinsic: llvm::Intrinsic,
842842
type_params: &[&'ll Type],
843-
) -> (&'ll Type, &'ll Value) {
843+
) -> &'ll Type {
844844
unsafe {
845-
(
846-
llvm::LLVMIntrinsicGetType(
847-
self.llcx(),
848-
intrinsic.id(),
849-
type_params.as_ptr(),
850-
type_params.len(),
851-
),
852-
llvm::LLVMGetIntrinsicDeclaration(
853-
self.llmod(),
854-
intrinsic.id(),
855-
type_params.as_ptr(),
856-
type_params.len(),
857-
),
845+
llvm::LLVMIntrinsicGetType(
846+
self.llcx(),
847+
intrinsic.id(),
848+
type_params.as_ptr(),
849+
type_params.len(),
858850
)
859851
}
860852
}

compiler/rustc_codegen_llvm/src/declare.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
158158
fn_abi.llvm_cconv(self),
159159
llvm::UnnamedAddr::Global,
160160
llvm::Visibility::Default,
161-
fn_abi.llvm_type(self),
161+
fn_abi.llvm_type(self, name.as_bytes()),
162162
);
163163
fn_abi.apply_attrs_llfn(self, llfn, instance);
164164

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);
1092+
let llty = fn_abi.llvm_type(cx, name.as_bytes());
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: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1077,7 +1077,6 @@ unsafe extern "C" {
10771077

10781078
// Operations on other types
10791079
pub(crate) fn LLVMVoidTypeInContext(C: &Context) -> &Type;
1080-
pub(crate) fn LLVMMetadataTypeInContext(C: &Context) -> &Type;
10811080

10821081
// Operations on all values
10831082
pub(crate) fn LLVMTypeOf(Val: &Value) -> &Type;
@@ -1196,18 +1195,24 @@ unsafe extern "C" {
11961195

11971196
// Operations about llvm intrinsics
11981197
pub(crate) fn LLVMLookupIntrinsicID(Name: *const c_char, NameLen: size_t) -> c_uint;
1199-
pub(crate) fn LLVMGetIntrinsicDeclaration<'a>(
1200-
M: &'a Module,
1201-
ID: c_uint,
1202-
ParamTypes: *const &'a Type,
1203-
ParamCount: size_t,
1204-
) -> &'a Value;
12051198
pub(crate) fn LLVMIntrinsicGetType<'a>(
12061199
C: &'a Context,
12071200
ID: c_uint,
12081201
ParamTypes: *const &'a Type,
12091202
ParamCount: size_t,
12101203
) -> &'a Type;
1204+
pub(crate) fn LLVMRustIntrinsicGetBaseName(
1205+
ID: c_uint,
1206+
NameLength: *mut size_t,
1207+
) -> *const c_char;
1208+
pub(crate) fn LLVMIntrinsicIsOverloaded(ID: c_uint) -> Bool;
1209+
pub(crate) fn LLVMIntrinsicCopyOverloadedName2<'a>(
1210+
Mod: &'a Module,
1211+
ID: c_uint,
1212+
ParamTypes: *const &'a Type,
1213+
ParamCount: size_t,
1214+
NameLength: *mut size_t,
1215+
) -> *mut c_char;
12111216

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

compiler/rustc_codegen_llvm/src/llvm/mod.rs

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
#![allow(non_snake_case)]
22

33
use std::ffi::{CStr, CString};
4-
use std::ptr;
54
use std::str::FromStr;
65
use std::string::FromUtf8Error;
6+
use std::{ptr, slice};
77

88
use libc::c_uint;
99
use rustc_abi::{Align, Size, WrappingRange};
@@ -341,6 +341,66 @@ impl Intrinsic {
341341
pub(crate) fn id(self) -> c_uint {
342342
self.id
343343
}
344+
345+
pub(crate) fn is_overloaded(self) -> bool {
346+
unsafe { LLVMIntrinsicIsOverloaded(self.id) == True }
347+
}
348+
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+
355+
pub(crate) fn overloaded_name<'ll>(
356+
self,
357+
llmod: &'ll Module,
358+
type_params: &[&'ll Type],
359+
) -> CSlice<u8> {
360+
let mut len = 0;
361+
let ptr = unsafe {
362+
LLVMIntrinsicCopyOverloadedName2(
363+
llmod,
364+
self.id,
365+
type_params.as_ptr(),
366+
type_params.len(),
367+
&mut len,
368+
)
369+
};
370+
unsafe { CSlice::from_raw(ptr.cast(), len) }
371+
}
372+
}
373+
374+
pub(crate) struct CSlice<T: Copy> {
375+
ptr: *mut T,
376+
len: usize,
377+
}
378+
379+
impl<T: Copy> CSlice<T> {
380+
pub(crate) unsafe fn from_raw(ptr: *mut T, len: usize) -> Self {
381+
assert!(std::mem::size_of::<T>() != 0, "ZSTs aren't supported yet");
382+
Self { ptr, len }
383+
}
384+
}
385+
386+
impl<T: Copy> std::ops::Deref for CSlice<T> {
387+
type Target = [T];
388+
389+
fn deref(&self) -> &Self::Target {
390+
unsafe { slice::from_raw_parts(self.ptr, self.len) }
391+
}
392+
}
393+
394+
impl<T: Copy> std::ops::DerefMut for CSlice<T> {
395+
fn deref_mut(&mut self) -> &mut Self::Target {
396+
unsafe { slice::from_raw_parts_mut(self.ptr, self.len) }
397+
}
398+
}
399+
400+
impl<T: Copy> Drop for CSlice<T> {
401+
fn drop(&mut self) {
402+
unsafe { libc::free(self.ptr.cast()) }
403+
}
344404
}
345405

346406
/// Safe wrapper for `LLVMSetValueName2` from a byte slice

0 commit comments

Comments
 (0)