Skip to content

Commit 29ea079

Browse files
committed
codegen llvm intrinsics based on their name, and verify the rust signature against it
1 parent f4ba07f commit 29ea079

File tree

14 files changed

+413
-57
lines changed

14 files changed

+413
-57
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: 78 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::borrow::Borrow;
22
use std::cmp;
3+
use std::iter::zip;
34

45
use libc::c_uint;
56
use rustc_abi::{BackendRepr, HasDataLayout, Primitive, Reg, RegKind, Size};
@@ -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

@@ -325,26 +328,29 @@ pub(crate) trait FnAbiLlvmExt<'ll, 'tcx> {
325328
}
326329

327330
impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
328-
fn llvm_type(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type {
331+
fn llvm_return_type(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type {
332+
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 { .. } => cx.type_void(),
337+
}
338+
}
339+
340+
fn llvm_argument_types(&self, cx: &CodegenCx<'ll, 'tcx>) -> Vec<&'ll Type> {
341+
let indirect_return = matches!(self.ret.mode, PassMode::Indirect { .. });
342+
329343
// Ignore "extra" args from the call site for C variadic functions.
330344
// Only the "fixed" args are part of the LLVM function signature.
331345
let args =
332346
if self.c_variadic { &self.args[..self.fixed_count as usize] } else { &self.args };
333347

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-
);
348+
let mut llargument_tys =
349+
Vec::with_capacity(args.len() + if indirect_return { 1 } else { 0 });
338350

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-
};
351+
if indirect_return {
352+
llargument_tys.push(cx.type_ptr());
353+
}
348354

349355
for arg in args {
350356
// Note that the exact number of arguments pushed here is carefully synchronized with
@@ -391,10 +397,65 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
391397
llargument_tys.push(llarg_ty);
392398
}
393399

400+
llargument_tys
401+
}
402+
403+
fn llvm_type(&self, cx: &CodegenCx<'ll, 'tcx>, name: &[u8]) -> &'ll Type {
404+
let actual_return_ty = self.llvm_return_type(cx);
405+
let actual_argument_tys = self.llvm_argument_types(cx);
406+
407+
let is_llvm_intrinsic = name.starts_with(b"llvm.");
408+
409+
if is_llvm_intrinsic && let Some((_, fn_ty)) = cx.get_intrinsic(name) {
410+
let expected_return_ty = cx.get_return_type(fn_ty);
411+
let expected_argument_tys = cx.func_params_types(fn_ty);
412+
413+
if actual_argument_tys.len() != expected_argument_tys.len() {
414+
cx.tcx.dcx().fatal(format!(
415+
"Intrinsic signature mismatch: expected {} arguments for `{}`, found {} arguments",
416+
expected_argument_tys.len(),
417+
str::from_utf8(name).unwrap(),
418+
actual_argument_tys.len(),
419+
));
420+
}
421+
422+
if actual_return_ty != expected_return_ty {
423+
cx.tcx.dcx().fatal(format!(
424+
"Intrinsic signature mismatch: expected {expected_return_ty:?} as return type for `{}`, found {actual_return_ty:?}",
425+
str::from_utf8(name).unwrap()
426+
));
427+
}
428+
for (idx, (actual_argument_ty, expected_argument_ty)) in
429+
zip(actual_argument_tys, expected_argument_tys).enumerate()
430+
{
431+
if actual_argument_ty != expected_argument_ty {
432+
cx.tcx.dcx().fatal(format!(
433+
"Intrinsic signature mismatch: expected {expected_argument_ty:?} as argument {idx} for `{}`, found {actual_argument_ty:?}",
434+
str::from_utf8(name).unwrap()
435+
));
436+
}
437+
}
438+
// todo: verify using `Intrinsic::getIntrinsicSignature`
439+
440+
return fn_ty;
441+
}
442+
443+
if is_llvm_intrinsic {
444+
// it's one of 2 cases,
445+
// - either the base name is invalid
446+
// - it has been superceded by something else, so the intrinsic was removed entirely
447+
// - our parse isn't recognizing it correctly
448+
// anyway, let's log it
449+
tracing::debug!(
450+
"Couldn't parse intrinsic name `{}`, falling back to default implementation",
451+
str::from_utf8(name).unwrap()
452+
);
453+
}
454+
394455
if self.c_variadic {
395-
cx.type_variadic_func(&llargument_tys, llreturn_ty)
456+
cx.type_variadic_func(&actual_argument_tys, actual_return_ty)
396457
} else {
397-
cx.type_func(&llargument_tys, llreturn_ty)
458+
cx.type_func(&actual_argument_tys, actual_return_ty)
398459
}
399460
}
400461

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;

0 commit comments

Comments
 (0)