Skip to content

Commit e4b4efa

Browse files
committed
Add bypass for i1xN
- Automatically destructure structs if the type doesn't match exactly - Start emitting hard errors on signature mismatches
1 parent 8e65531 commit e4b4efa

File tree

7 files changed

+259
-141
lines changed

7 files changed

+259
-141
lines changed

compiler/rustc_codegen_llvm/src/abi.rs

Lines changed: 95 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,17 @@ impl<'ll> FunctionSignature<'ll> {
330330
pub(crate) trait FnAbiLlvmExt<'ll, 'tcx> {
331331
fn llvm_return_type(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type;
332332
fn llvm_argument_types(&self, cx: &CodegenCx<'ll, 'tcx>) -> Vec<&'ll Type>;
333-
fn llvm_type(&self, cx: &CodegenCx<'ll, 'tcx>, name: &[u8]) -> FunctionSignature<'ll>;
333+
/// When `do_verify` is set, this function performs checks for the signature of LLVM intrinsics
334+
/// and emits a fatal error if it doesn't match. These checks are important,but somewhat expensive
335+
/// So they are only used at function definitions, not at callsites
336+
fn llvm_type(
337+
&self,
338+
cx: &CodegenCx<'ll, 'tcx>,
339+
name: &[u8],
340+
do_verify: bool,
341+
) -> FunctionSignature<'ll>;
342+
/// **If this function is an LLVM intrinsic** checks if the LLVM signature provided matches with this
343+
fn verify_intrinsic_signature(&self, cx: &CodegenCx<'ll, 'tcx>, llvm_ty: &'ll Type) -> bool;
334344
fn ptr_to_llvm_type(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type;
335345
fn llvm_cconv(&self, cx: &CodegenCx<'ll, 'tcx>) -> llvm::CallConv;
336346

@@ -343,31 +353,25 @@ pub(crate) trait FnAbiLlvmExt<'ll, 'tcx> {
343353
);
344354

345355
/// Apply attributes to a function call.
346-
fn apply_attrs_callsite(&self, bx: &mut Builder<'_, 'll, 'tcx>, callsite: &'ll Value);
356+
fn apply_attrs_callsite(
357+
&self,
358+
bx: &mut Builder<'_, 'll, 'tcx>,
359+
callsite: &'ll Value,
360+
llfn: &'ll Value,
361+
);
347362
}
348363

349-
/// checks that the Rust signature of a llvm intrinsic is correct
350-
fn verify_intrinsic_signature<'ll, 'tcx>(
351-
cx: &CodegenCx<'ll, 'tcx>,
352-
fn_ty: &'ll Type,
353-
rust_return_ty: &'ll Type,
354-
rust_argument_tys: &[&'ll Type],
355-
rust_is_variadic: bool,
356-
) -> bool {
357-
let llvm_return_ty = cx.get_return_type(fn_ty);
358-
let llvm_argument_tys = cx.func_params_types(fn_ty);
359-
let llvm_is_variadic = cx.func_is_variadic(fn_ty);
360-
361-
if rust_is_variadic != llvm_is_variadic || rust_argument_tys.len() != llvm_argument_tys.len() {
362-
return false;
364+
impl<'ll, CX: Borrow<SCx<'ll>>> GenericCx<'ll, CX> {
365+
pub(crate) fn set_intrinsic_attributes(
366+
&self,
367+
intrinsic: llvm::Intrinsic,
368+
llfn_or_callsite: &'ll Value,
369+
) {
370+
unsafe {
371+
llvm::LLVMRustSetIntrinsicAttributes(self.llcx(), llfn_or_callsite, intrinsic.id());
372+
}
363373
}
364374

365-
iter::once((&rust_return_ty, &llvm_return_ty))
366-
.chain(iter::zip(rust_argument_tys, &llvm_argument_tys))
367-
.all(|(rust_ty, llvm_ty)| cx.equate_ty(rust_ty, llvm_ty))
368-
}
369-
370-
impl<'ll, CX: Borrow<SCx<'ll>>> GenericCx<'ll, CX> {
371375
pub(crate) fn equate_ty(&self, rust_ty: &'ll Type, llvm_ty: &'ll Type) -> bool {
372376
if rust_ty == llvm_ty {
373377
return true;
@@ -395,15 +399,32 @@ impl<'ll, CX: Borrow<SCx<'ll>>> GenericCx<'ll, CX> {
395399
}
396400
TypeKind::BFloat => rust_ty == self.type_i16(),
397401
TypeKind::Vector => {
398-
let llvm_element_count = self.vector_length(llvm_ty);
402+
let llvm_element_count = self.vector_length(llvm_ty) as u64;
399403
let llvm_element_ty = self.element_type(llvm_ty);
400404

401405
if llvm_element_ty == self.type_bf16() {
402-
rust_ty == self.type_vector(self.type_i16(), llvm_element_count as u64)
406+
rust_ty == self.type_vector(self.type_i16(), llvm_element_count)
407+
} else if llvm_element_ty == self.type_i1() {
408+
let int_width = cmp::max(llvm_element_count.next_power_of_two(), 8);
409+
rust_ty == self.type_ix(int_width)
403410
} else {
404411
false
405412
}
406413
}
414+
TypeKind::Struct if self.type_kind(rust_ty) == TypeKind::Struct => {
415+
let rust_element_tys = self.struct_element_types(rust_ty);
416+
let llvm_element_tys = self.struct_element_types(llvm_ty);
417+
418+
if rust_element_tys.len() != llvm_element_tys.len() {
419+
return false;
420+
}
421+
422+
iter::zip(rust_element_tys, llvm_element_tys).all(
423+
|(rust_element_ty, llvm_element_ty)| {
424+
self.equate_ty(rust_element_ty, llvm_element_ty)
425+
},
426+
)
427+
}
407428
_ => false,
408429
}
409430
}
@@ -482,7 +503,30 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
482503
llargument_tys
483504
}
484505

485-
fn llvm_type(&self, cx: &CodegenCx<'ll, 'tcx>, name: &[u8]) -> FunctionSignature<'ll> {
506+
fn verify_intrinsic_signature(&self, cx: &CodegenCx<'ll, 'tcx>, llvm_fn_ty: &'ll Type) -> bool {
507+
let rust_return_ty = self.llvm_return_type(cx);
508+
let rust_argument_tys = self.llvm_argument_types(cx);
509+
510+
let llvm_return_ty = cx.get_return_type(llvm_fn_ty);
511+
let llvm_argument_tys = cx.func_params_types(llvm_fn_ty);
512+
let llvm_is_variadic = cx.func_is_variadic(llvm_fn_ty);
513+
514+
if self.c_variadic != llvm_is_variadic || rust_argument_tys.len() != llvm_argument_tys.len()
515+
{
516+
return false;
517+
}
518+
519+
iter::once((rust_return_ty, llvm_return_ty))
520+
.chain(iter::zip(rust_argument_tys, llvm_argument_tys))
521+
.all(|(rust_ty, llvm_ty)| cx.equate_ty(rust_ty, llvm_ty))
522+
}
523+
524+
fn llvm_type(
525+
&self,
526+
cx: &CodegenCx<'ll, 'tcx>,
527+
name: &[u8],
528+
do_verify: bool,
529+
) -> FunctionSignature<'ll> {
486530
let return_ty = self.llvm_return_type(cx);
487531
let argument_tys = self.llvm_argument_types(cx);
488532

@@ -493,30 +537,15 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
493537
if !intrinsic.is_overloaded() {
494538
// FIXME: also do this for overloaded intrinsics
495539
let llvm_fn_ty = cx.intrinsic_type(intrinsic, &[]);
496-
if verify_intrinsic_signature(
497-
cx,
498-
llvm_fn_ty,
499-
return_ty,
500-
&argument_tys,
501-
self.c_variadic,
502-
) {
503-
return FunctionSignature::Intrinsic(intrinsic, llvm_fn_ty);
504-
} else {
505-
// FIXME: ideally we would like to hard error, but there are edge cases like `i1xN`
506-
// and differences between struct representations that we have to tackle first
507-
508-
let rust_fn_ty = if self.c_variadic {
509-
cx.type_variadic_func(&argument_tys, return_ty)
510-
} else {
511-
cx.type_func(&argument_tys, return_ty)
512-
};
513-
cx.tcx.dcx().note(format!(
514-
"Intrinsic signature mismatch for `{}`: expected signature `{llvm_fn_ty:?}`, found signature `{rust_fn_ty:?}`",
515-
str::from_utf8(name).unwrap()
516-
));
517-
// fallback to the rust signature
518-
return FunctionSignature::Rust(rust_fn_ty);
540+
if do_verify {
541+
if !self.verify_intrinsic_signature(cx, llvm_fn_ty) {
542+
cx.tcx.dcx().fatal(format!(
543+
"Intrinsic signature mismatch for `{}`: expected signature `{llvm_fn_ty:?}`",
544+
str::from_utf8(name).unwrap()
545+
));
546+
}
519547
}
548+
return FunctionSignature::Intrinsic(intrinsic, llvm_fn_ty);
520549
}
521550
} else {
522551
// it's one of 2 cases,
@@ -674,7 +703,24 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
674703
}
675704
}
676705

677-
fn apply_attrs_callsite(&self, bx: &mut Builder<'_, 'll, 'tcx>, callsite: &'ll Value) {
706+
fn apply_attrs_callsite(
707+
&self,
708+
bx: &mut Builder<'_, 'll, 'tcx>,
709+
callsite: &'ll Value,
710+
llfn: &'ll Value,
711+
) {
712+
// if we are using the LLVM signature, use the LLVM attributes otherwise it might be problematic
713+
let name = llvm::get_value_name(llfn);
714+
if name.starts_with(b"llvm.")
715+
&& let Some(intrinsic) = llvm::Intrinsic::lookup(name)
716+
{
717+
// FIXME: also do this for overloaded intrinsics
718+
if !intrinsic.is_overloaded() {
719+
bx.set_intrinsic_attributes(intrinsic, callsite);
720+
return;
721+
}
722+
}
723+
678724
let mut func_attrs = SmallVec::<[_; 2]>::new();
679725
if self.ret.layout.is_uninhabited() {
680726
func_attrs.push(llvm::AttributeKind::NoReturn.create_attr(bx.cx.llcx));

0 commit comments

Comments
 (0)