Skip to content

Commit 5329959

Browse files
committed
codegen llvm intrinsics based on their name, and verify the rust signature against it
1 parent 1695bbf commit 5329959

File tree

13 files changed

+332
-33
lines changed

13 files changed

+332
-33
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: 69 additions & 19 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, Option<&'ll Value>);
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,11 +397,55 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
391397
llargument_tys.push(llarg_ty);
392398
}
393399

394-
if self.c_variadic {
395-
cx.type_variadic_func(&llargument_tys, llreturn_ty)
396-
} else {
397-
cx.type_func(&llargument_tys, llreturn_ty)
400+
llargument_tys
401+
}
402+
403+
fn llvm_type(&self, cx: &CodegenCx<'ll, 'tcx>, name: &[u8]) -> (&'ll Type, Option<&'ll Value>) {
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, llfn)) = cx.get_intrinsic_from_name(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+
439+
return (fn_ty, Some(llfn));
398440
}
441+
442+
let fn_ty = if self.c_variadic {
443+
cx.type_variadic_func(&actual_argument_tys, actual_return_ty)
444+
} else {
445+
cx.type_func(&actual_argument_tys, actual_return_ty)
446+
};
447+
448+
(fn_ty, None)
399449
}
400450

401451
fn ptr_to_llvm_type(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type {

compiler/rustc_codegen_llvm/src/context.rs

Lines changed: 174 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use std::marker::PhantomData;
55
use std::ops::Deref;
66
use std::str;
77

8-
use rustc_abi::{HasDataLayout, Size, TargetDataLayout, VariantIdx};
8+
use rustc_abi::{AddressSpace, HasDataLayout, Size, TargetDataLayout, VariantIdx};
99
use rustc_codegen_ssa::back::versioned_llvm_target;
1010
use rustc_codegen_ssa::base::{wants_msvc_seh, wants_wasm_eh};
1111
use rustc_codegen_ssa::common::TypeKind;
@@ -858,6 +858,179 @@ impl<'ll, CX: Borrow<SCx<'ll>>> GenericCx<'ll, CX> {
858858
)
859859
}
860860
}
861+
862+
/// Returns the llvm intrinsic ID and type parameters for an llvm intrinsic
863+
/// `full_name` must start with `b"llvm."`
864+
///
865+
/// Returns `None` if this is not a valid intrinsic name
866+
fn parse_intrinsic_name(&self, full_name: &[u8]) -> Option<(llvm::Intrinsic, Vec<&'ll Type>)> {
867+
let intrinsic = llvm::Intrinsic::lookup(full_name)?;
868+
let mut type_params = Vec::new();
869+
870+
let base_name = intrinsic.base_name();
871+
assert!(full_name.starts_with(base_name));
872+
873+
if intrinsic.is_overloaded() {
874+
let mut remainder = &full_name[base_name.len()..];
875+
while !remainder.is_empty() {
876+
if remainder[0] != b'.' {
877+
let &last_ty = type_params.last()?;
878+
let last_ty_was_ptr = match self.type_kind(last_ty) {
879+
TypeKind::Pointer => true,
880+
TypeKind::Array | TypeKind::Vector | TypeKind::ScalableVector => {
881+
self.type_kind(self.element_type(last_ty)) == TypeKind::Pointer
882+
}
883+
_ => false,
884+
};
885+
886+
if last_ty_was_ptr {
887+
// little support for deprecated type-specific pointers, we can disambiguate them only in these cases
888+
let (_element_type, new_remainder) = self.demangle_type_str(remainder)?;
889+
remainder = new_remainder;
890+
891+
if remainder.is_empty() {
892+
break;
893+
}
894+
if remainder[0] != b'.' {
895+
return None;
896+
}
897+
}
898+
}
899+
let (type_param, new_remainder) = self.demangle_type_str(&remainder[1..])?;
900+
type_params.push(type_param);
901+
remainder = new_remainder;
902+
}
903+
904+
if type_params.is_empty() {
905+
// overloaded intrinsic must have at least 1 type parameter
906+
return None;
907+
}
908+
} else {
909+
if full_name != base_name {
910+
// for non-overloaded intrinsic, the base name is the whole name
911+
return None;
912+
}
913+
}
914+
915+
Some((intrinsic, type_params))
916+
}
917+
918+
/// Inverse of the llvm function `getMangledTypeStr` (well, not perfect, because the
919+
/// mangling is not fully reversible, but this is the best solution we currently have)
920+
///
921+
/// FIXME(future): find out a better way to do this
922+
fn demangle_type_str<'a>(&self, mangled: &'a [u8]) -> Option<(&'ll Type, &'a [u8])> {
923+
fn parse_int(string: &[u8]) -> Option<(u64, &[u8])> {
924+
let mut position = 0;
925+
let mut number = 0;
926+
927+
while let Some(&digit @ b'0'..=b'9') = string.get(position) {
928+
number = 10 * number + (digit - b'0') as u64;
929+
position += 1;
930+
}
931+
932+
if position != 0 { Some((number, &string[position..])) } else { None }
933+
}
934+
935+
Some(if mangled.starts_with(b"isVoid") {
936+
(self.type_void(), &mangled[6..])
937+
} else if mangled.starts_with(b"Metadata") {
938+
(self.type_metadata(), &mangled[8..])
939+
} else if mangled.starts_with(b"f16") {
940+
(self.type_f16(), &mangled[3..])
941+
} else if mangled.starts_with(b"bf16") {
942+
(self.type_bf16(), &mangled[4..])
943+
} else if mangled.starts_with(b"f32") {
944+
(self.type_f32(), &mangled[3..])
945+
} else if mangled.starts_with(b"f64") {
946+
(self.type_f64(), &mangled[3..])
947+
} else if mangled.starts_with(b"f80") {
948+
(self.type_x86f80(), &mangled[3..])
949+
} else if mangled.starts_with(b"f128") {
950+
(self.type_f128(), &mangled[4..])
951+
} else if mangled.starts_with(b"ppcf128") {
952+
(self.type_ppcf128(), &mangled[7..])
953+
} else if mangled.starts_with(b"x86amx") {
954+
(self.type_x86amx(), &mangled[6..])
955+
} else if mangled.starts_with(b"i") {
956+
// integer type
957+
let (int_width, remainder) = parse_int(&mangled[1..])?;
958+
(self.type_ix(int_width), remainder)
959+
} else if mangled.starts_with(b"p") {
960+
// pointer type
961+
let (address_space, remainder) = parse_int(&mangled[1..])?;
962+
let address_space = AddressSpace(address_space.try_into().unwrap());
963+
(self.type_ptr_ext(address_space), remainder)
964+
} else if mangled.starts_with(b"v") {
965+
// vector type
966+
let (element_count, remainder) = parse_int(&mangled[1..])?;
967+
let (element_type, remainder) = self.demangle_type_str(remainder)?;
968+
(self.type_vector(element_type, element_count), remainder)
969+
} else if mangled.starts_with(b"nxv") {
970+
// scalable vector
971+
let (element_count, remainder) = parse_int(&mangled[3..])?;
972+
let (element_type, remainder) = self.demangle_type_str(remainder)?;
973+
(self.type_scalable_vector(element_type, element_count), remainder)
974+
} else if mangled.starts_with(b"a") {
975+
// array type
976+
let (element_count, remainder) = parse_int(&mangled[1..])?;
977+
let (element_type, remainder) = self.demangle_type_str(remainder)?;
978+
(self.type_array(element_type, element_count), remainder)
979+
} else if mangled.starts_with(b"sl_") {
980+
// literal struct
981+
let mut remainder = &mangled[3..];
982+
let mut fields = Vec::new();
983+
loop {
984+
if remainder.is_empty() {
985+
// struct types should end with an `s`
986+
return None;
987+
}
988+
if remainder[0] == b's'
989+
&& !remainder.starts_with(b"s_")
990+
&& !remainder.starts_with(b"sl_")
991+
{
992+
break (self.type_struct(&fields, true), &remainder[1..]);
993+
}
994+
let (field, new_remainder) = self.demangle_type_str(remainder)?;
995+
fields.push(field);
996+
remainder = new_remainder;
997+
}
998+
} else if mangled.starts_with(b"f_") {
999+
// function type
1000+
let (return_type, mut remainder) = self.demangle_type_str(&mangled[2..])?;
1001+
let mut parameters = Vec::new();
1002+
loop {
1003+
if remainder.is_empty() {
1004+
// function types should end with an `f`
1005+
return None;
1006+
}
1007+
if remainder.starts_with(b"varargf") {
1008+
break (self.type_variadic_func(&parameters, return_type), &remainder[7..]);
1009+
}
1010+
if remainder[0] == b'f' && !remainder.starts_with(b"f_") {
1011+
break (self.type_func(&parameters, return_type), &remainder[1..]);
1012+
}
1013+
let (parameter, new_remainder) = self.demangle_type_str(remainder)?;
1014+
parameters.push(parameter);
1015+
remainder = new_remainder;
1016+
}
1017+
} else if mangled.starts_with(b"s_") || mangled.starts_with(b"t") {
1018+
// non-literal structs and target types
1019+
// these types can't be parsed unambiguously, because they have their name in the mangling
1020+
unimplemented!("non-literal structs and TargetExt types are not supported yet")
1021+
} else {
1022+
// invalid type
1023+
return None;
1024+
})
1025+
}
1026+
1027+
pub(crate) fn get_intrinsic_from_name(
1028+
&self,
1029+
full_name: &[u8],
1030+
) -> Option<(&'ll Type, &'ll Value)> {
1031+
let (intrinsic, type_params) = self.parse_intrinsic_name(full_name)?;
1032+
Some(self.get_intrinsic(intrinsic, &type_params))
1033+
}
8611034
}
8621035

8631036
impl<'ll> CodegenCx<'ll, '_> {

compiler/rustc_codegen_llvm/src/declare.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,12 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
150150
) -> &'ll Value {
151151
debug!("declare_rust_fn(name={:?}, fn_abi={:?})", name, fn_abi);
152152

153+
let (fn_ty, llfn) = fn_abi.llvm_type(self, name.as_ref());
154+
if let Some(llfn) = llfn {
155+
// we can't apply any attributes or attach any metadata to llvm intrinsics
156+
return llfn;
157+
}
158+
153159
// Function addresses in Rust are never significant, allowing functions to
154160
// be merged.
155161
let llfn = declare_raw_fn(
@@ -158,7 +164,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
158164
fn_abi.llvm_cconv(self),
159165
llvm::UnnamedAddr::Global,
160166
llvm::Visibility::Default,
161-
fn_abi.llvm_type(self),
167+
fn_ty,
162168
);
163169
fn_abi.apply_attrs_llfn(self, llfn, instance);
164170

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()).0;
10931093
let llfn = cx.declare_fn(name, fn_abi, None);
10941094
cx.set_frame_pointer_type(llfn);
10951095
cx.apply_target_cpu_attr(llfn);

0 commit comments

Comments
 (0)