Skip to content

Commit c5b4fc2

Browse files
committed
Don't require extern "llvm-intrinsic" return structs to be #[repr(packed)]
1 parent df87fa6 commit c5b4fc2

6 files changed

Lines changed: 192 additions & 93 deletions

File tree

‎compiler/rustc_codegen_gcc/src/intrinsic/mod.rs‎

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use rustc_codegen_ssa::{MemFlags, RetagInfo};
2222
use rustc_data_structures::fx::FxHashSet;
2323
#[cfg(feature = "master")]
2424
use rustc_middle::ty::layout::FnAbiOf;
25-
use rustc_middle::ty::layout::LayoutOf;
25+
use rustc_middle::ty::layout::{HasTypingEnv, LayoutOf};
2626
use rustc_middle::ty::{self, Instance, Ty};
2727
use rustc_session::config::OptLevel;
2828
use rustc_span::{Span, Symbol, bug, span_bug, sym};
@@ -556,6 +556,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc
556556
&mut self,
557557
instance: ty::Instance<'tcx>,
558558
args: &[OperandRef<'tcx, Self::Value>],
559+
return_slot: ReturnSlot<Self::Value>,
559560
is_cleanup: bool,
560561
) -> Self::Value {
561562
let func = if let Some(&func) = self.intrinsic_instances.borrow().get(&instance) {
@@ -630,7 +631,33 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc
630631
self.apply_attrs_to_cleanup_callsite(llret);
631632
}
632633

633-
llret
634+
let fn_sig = match *instance.ty(self.tcx, self.typing_env()).kind() {
635+
ty::FnDef(def_id, args) => self.tcx.instantiate_bound_regions_with_erased(
636+
self.tcx
637+
.fn_sig(def_id)
638+
.instantiate(self.tcx, args.no_bound_vars().unwrap())
639+
.skip_norm_wip(),
640+
),
641+
_ => unreachable!(),
642+
};
643+
let ret_layout = self.layout_of(fn_sig.output());
644+
645+
if !ret_layout.is_zst() && ret_layout.is_aggregate() {
646+
let fields =
647+
rustc_codegen_ssa::mir::intrinsic_return_struct_non_zst_fields(self.cx, ret_layout)
648+
.into_iter()
649+
.enumerate()
650+
.map(|(i, (_, offset))| (offset, self.extract_value(llret, i as u64)))
651+
.collect();
652+
rustc_codegen_ssa::mir::store_intrinsic_return_struct(
653+
self,
654+
ret_layout,
655+
return_slot,
656+
fields,
657+
)
658+
} else {
659+
llret
660+
}
634661
}
635662

636663
fn abort_immediate(&mut self) {

‎compiler/rustc_codegen_llvm/src/intrinsic.rs‎

Lines changed: 44 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -895,6 +895,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
895895
&mut self,
896896
instance: ty::Instance<'tcx>,
897897
args: &[OperandRef<'tcx, Self::Value>],
898+
return_slot: ReturnSlot<Self::Value>,
898899
_is_cleanup: bool,
899900
) -> Self::Value {
900901
let tcx = self.tcx();
@@ -909,10 +910,15 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
909910
assert!(!fn_sig.c_variadic());
910911

911912
let ret_layout = self.layout_of(fn_sig.output());
912-
let llreturn_ty = if ret_layout.is_zst() {
913-
self.type_void()
913+
let (llreturn_ty, return_struct_fields) = if ret_layout.is_zst() {
914+
(self.type_void(), vec![])
915+
} else if ret_layout.is_aggregate() {
916+
let fields =
917+
rustc_codegen_ssa::mir::intrinsic_return_struct_non_zst_fields(self.cx, ret_layout);
918+
let llvm_fields = fields.iter().copied().map(|(t, _)| t).collect::<Vec<_>>();
919+
(self.type_struct(&llvm_fields, false), fields)
914920
} else {
915-
ret_layout.immediate_llvm_type(self)
921+
(ret_layout.immediate_llvm_type(self), vec![])
916922
};
917923

918924
let mut llargument_tys = Vec::with_capacity(fn_sig.inputs().len());
@@ -996,14 +1002,33 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
9961002
)
9971003
};
9981004

999-
let src_ty = self.val_ty(llret);
1000-
let dest_ty = llreturn_ty;
1001-
assert!(
1002-
can_autocast(self, dest_ty, src_ty),
1003-
"Cannot match `{src_ty:?}` (expected) with `{dest_ty:?}` (found) in `{fn_ptr:?}`"
1004-
);
1005+
if return_struct_fields.is_empty() {
1006+
let src_ty = self.val_ty(llret);
1007+
let dest_ty = llreturn_ty;
1008+
assert!(
1009+
can_autocast(self, dest_ty, src_ty),
1010+
"Cannot match `{src_ty:?}` (expected) with `{dest_ty:?}` (found) in `{fn_ptr:?}`"
1011+
);
10051012

1006-
autocast(self, llret, src_ty, dest_ty)
1013+
autocast(self, llret, src_ty, dest_ty)
1014+
} else {
1015+
let fields = return_struct_fields.into_iter().enumerate().map(|(i, (dest_ty, offset))| {
1016+
let value = self.extract_value(llret, i as u64);
1017+
let src_ty = self.val_ty(value);
1018+
assert!(
1019+
can_autocast(self, dest_ty, src_ty),
1020+
"Cannot match `{src_ty:?}` (expected) with `{dest_ty:?}` (found) in `{fn_ptr:?}`"
1021+
);
1022+
let value = autocast(self, value, src_ty, dest_ty);
1023+
(offset, value)
1024+
}).collect();
1025+
rustc_codegen_ssa::mir::store_intrinsic_return_struct(
1026+
self,
1027+
ret_layout,
1028+
return_slot,
1029+
fields,
1030+
)
1031+
}
10071032
}
10081033

10091034
fn abort_immediate(&mut self) {
@@ -1089,23 +1114,6 @@ fn can_autocast<'ll>(cx: &CodegenCx<'ll, '_>, rust_ty: &'ll Type, llvm_ty: &'ll
10891114
}
10901115

10911116
match cx.type_kind(llvm_ty) {
1092-
// Some LLVM intrinsics return **non-packed** structs, but they can't be mimicked from Rust
1093-
// due to auto field-alignment in non-packed structs (packed structs are represented in LLVM
1094-
// as, well, packed structs, so they won't match with those either)
1095-
TypeKind::Struct if cx.type_kind(rust_ty) == TypeKind::Struct => {
1096-
let rust_element_tys = cx.struct_element_types(rust_ty);
1097-
let llvm_element_tys = cx.struct_element_types(llvm_ty);
1098-
1099-
if rust_element_tys.len() != llvm_element_tys.len() {
1100-
return false;
1101-
}
1102-
1103-
iter::zip(rust_element_tys, llvm_element_tys).all(
1104-
|(rust_element_ty, llvm_element_ty)| {
1105-
can_autocast(cx, rust_element_ty, llvm_element_ty)
1106-
},
1107-
)
1108-
}
11091117
TypeKind::Vector => {
11101118
let llvm_element_ty = cx.element_type(llvm_ty);
11111119
let element_count = cx.vector_length(llvm_ty) as u64;
@@ -1152,19 +1160,6 @@ fn autocast<'ll>(
11521160
return val;
11531161
}
11541162
match (bx.type_kind(src_ty), bx.type_kind(dest_ty)) {
1155-
// re-pack structs
1156-
(TypeKind::Struct, TypeKind::Struct) => {
1157-
let mut ret = bx.const_poison(dest_ty);
1158-
for (idx, (src_element_ty, dest_element_ty)) in
1159-
iter::zip(bx.struct_element_types(src_ty), bx.struct_element_types(dest_ty))
1160-
.enumerate()
1161-
{
1162-
let elt = bx.extract_value(val, idx as u64);
1163-
let casted_elt = autocast(bx, elt, src_element_ty, dest_element_ty);
1164-
ret = bx.insert_value(ret, casted_elt, idx as u64);
1165-
}
1166-
ret
1167-
}
11681163
// cast from the i1xN vector type to the primitive type
11691164
(TypeKind::Vector, TypeKind::Integer) if bx.element_type(src_ty) == bx.type_i1() => {
11701165
let vector_length = bx.vector_length(src_ty) as u64;
@@ -1258,9 +1253,17 @@ fn intrinsic_fn<'ll, 'tcx>(
12581253
let llvm_argument_tys = bx.func_params_types(llvm_fn_ty);
12591254
let llvm_is_variadic = bx.func_is_variadic(llvm_fn_ty);
12601255

1256+
let (rust_return_tys, llvm_return_tys) = if bx.type_kind(rust_return_ty) == TypeKind::Struct
1257+
&& bx.type_kind(llvm_return_ty) == TypeKind::Struct
1258+
{
1259+
(bx.struct_element_types(rust_return_ty), bx.struct_element_types(llvm_return_ty))
1260+
} else {
1261+
(vec![rust_return_ty], vec![llvm_return_ty])
1262+
};
12611263
let is_correct_signature = !llvm_is_variadic
12621264
&& rust_argument_tys.len() == llvm_argument_tys.len()
1263-
&& iter::once((rust_return_ty, llvm_return_ty))
1265+
&& rust_return_tys.len() == llvm_return_tys.len()
1266+
&& iter::zip(rust_return_tys, llvm_return_tys)
12641267
.chain(iter::zip(rust_argument_tys, llvm_argument_tys))
12651268
.all(|(rust_ty, llvm_ty)| can_autocast(bx, rust_ty, llvm_ty));
12661269

‎compiler/rustc_codegen_ssa/src/mir/block.rs‎

Lines changed: 82 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
use std::cmp;
21
use std::ops::Range;
2+
use std::{assert_matches, cmp};
33

44
use rustc_abi::{
55
Align, ArmCall, BackendRepr, CanonAbi, ExternAbi, FieldsShape, HasDataLayout, Reg, Size,
@@ -1164,38 +1164,36 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
11641164
let result_layout =
11651165
self.cx.layout_of(self.monomorphized_place_ty(destination.as_ref()));
11661166

1167-
let return_dest = if result_layout.is_zst() {
1168-
ReturnDest::Nothing
1169-
} else if let Some(index) = destination.as_local() {
1170-
match self.locals[index] {
1171-
LocalRef::Place(dest) => ReturnDest::Store(dest),
1172-
LocalRef::UnsizedPlace(_) => bug!("return type must be sized"),
1173-
LocalRef::PendingOperand => {
1174-
// Handle temporary places, specifically `Operand` ones, as
1175-
// they don't have `alloca`s.
1176-
ReturnDest::DirectOperand(index)
1177-
}
1178-
LocalRef::Operand(_) => bug!("place local already assigned to"),
1167+
let return_mode = if result_layout.is_zst() {
1168+
PassMode::Ignore
1169+
} else if let BackendRepr::ScalarPair { .. } = result_layout.backend_repr {
1170+
PassMode::Pair(ArgAttributes::new(), ArgAttributes::new())
1171+
} else if result_layout.is_aggregate() {
1172+
PassMode::Indirect {
1173+
attrs: ArgAttributes::new(),
1174+
address_space: None,
1175+
mode: IndirectMode::Pointer,
11791176
}
11801177
} else {
1181-
ReturnDest::Store(self.codegen_place(bx, destination.as_ref()))
1178+
PassMode::Direct(ArgAttributes::new())
11821179
};
1180+
let return_abi = ArgAbi { layout: result_layout, mode: return_mode };
1181+
let (return_dest, return_slot) = self.make_return_dest(bx, destination, &return_abi);
11831182

11841183
let args =
11851184
args.into_iter().map(|arg| self.codegen_operand(bx, &arg.node)).collect::<Vec<_>>();
11861185

11871186
self.set_debug_loc(bx, source_info);
11881187

1189-
let llret =
1190-
bx.codegen_llvm_intrinsic_call(instance, &args, self.mir[helper.bb].is_cleanup);
1188+
let llret = bx.codegen_llvm_intrinsic_call(
1189+
instance,
1190+
&args,
1191+
return_slot,
1192+
self.mir[helper.bb].is_cleanup,
1193+
);
11911194

11921195
if let Some(target) = target {
1193-
self.store_return(
1194-
bx,
1195-
return_dest,
1196-
&ArgAbi { layout: result_layout, mode: PassMode::Direct(ArgAttributes::new()) },
1197-
llret,
1198-
);
1196+
self.store_return(bx, return_dest, &return_abi, llret);
11991197
return helper.funclet_br(self, bx, target, mergeable_succ, &[]);
12001198
} else {
12011199
bx.unreachable();
@@ -2501,7 +2499,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
25012499
}
25022500
}
25032501

2504-
// Stores the return value of a function call into it's final location.
2502+
/// Stores the return value of a function call into it's final location.
25052503
fn store_return(
25062504
&mut self,
25072505
bx: &mut Bx,
@@ -2603,3 +2601,64 @@ pub fn store_cast<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
26032601
bx.store(value, ptr, align);
26042602
};
26052603
}
2604+
2605+
pub fn intrinsic_return_struct_non_zst_fields<'tcx, Cx: CodegenMethods<'tcx>>(
2606+
cx: &Cx,
2607+
ret_layout: TyAndLayout<'tcx>,
2608+
) -> Vec<(Cx::Type, Size)> {
2609+
let ret_layout = ret_layout.peel_transparent_wrappers_from_non_1zst(cx);
2610+
match ret_layout.ty.kind() {
2611+
ty::Adt(adt_def, _) if adt_def.is_struct() => {}
2612+
ty::Tuple(_) => {}
2613+
_ => bug!("invalid intrinsic return type: {ret_layout:?}"),
2614+
}
2615+
let fields = (0..ret_layout.fields.count())
2616+
.filter_map(|i| {
2617+
let field = ret_layout.field(cx, i);
2618+
assert!(
2619+
field.is_zst() || !field.is_aggregate(),
2620+
"invalid intrinsic return field: {field:?}"
2621+
);
2622+
(!field.is_zst())
2623+
.then(|| (cx.immediate_backend_type(field), ret_layout.fields.offset(i)))
2624+
})
2625+
.collect::<Vec<_>>();
2626+
assert!(fields.len() >= 2, "invalid intrinsic return type: {ret_layout:?}");
2627+
fields
2628+
}
2629+
2630+
pub fn store_intrinsic_return_struct<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
2631+
bx: &mut Bx,
2632+
ret_layout: TyAndLayout<'tcx>,
2633+
return_slot: ReturnSlot<Bx::Value>,
2634+
fields: Vec<(Size, Bx::Value)>,
2635+
) -> Bx::Value {
2636+
match ret_layout.backend_repr {
2637+
BackendRepr::ScalarPair { a: _, b: _, b_offset } => {
2638+
assert_matches!(return_slot, ReturnSlot::Direct);
2639+
let a_index = if fields[0].0 == b_offset { 1 } else { 0 };
2640+
let b_index = if fields[1].0 == b_offset { 1 } else { 0 };
2641+
assert_ne!(a_index, b_index);
2642+
let mut pair = bx.cx().const_poison(bx.immediate_backend_type(ret_layout));
2643+
pair = bx.insert_value(pair, fields[a_index].1, 0);
2644+
bx.insert_value(pair, fields[b_index].1, 1)
2645+
}
2646+
BackendRepr::Memory { sized: true } => {
2647+
let ReturnSlot::Indirect(dest) = return_slot else {
2648+
bug!("BackendRepr::Memory return with no return slot")
2649+
};
2650+
let dest_align = ret_layout.align.abi;
2651+
for (offset, value) in fields.into_iter() {
2652+
let pointer = if offset.bytes() != 0 {
2653+
bx.inbounds_ptradd(dest, bx.const_usize(offset.bytes()))
2654+
} else {
2655+
dest
2656+
};
2657+
bx.store(value, pointer, dest_align.restrict_for_offset(offset));
2658+
}
2659+
// Return value is ignored as return_slot is indirect
2660+
dest
2661+
}
2662+
_ => bug!("unexpected aggregate return layout: {ret_layout:?}"),
2663+
}
2664+
}

‎compiler/rustc_codegen_ssa/src/mir/mod.rs‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@ mod retag;
2828
mod rvalue;
2929
mod statement;
3030

31-
pub use self::block::store_cast;
31+
pub use self::block::{
32+
intrinsic_return_struct_non_zst_fields, store_cast, store_intrinsic_return_struct,
33+
};
3234
use self::debuginfo::{FunctionDebugContext, PerLocalVarDebugInfo};
3335
use self::operand::{OperandRef, OperandValue};
3436
use self::place::PlaceRef;

‎compiler/rustc_codegen_ssa/src/traits/intrinsic.rs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use crate::RetagInfo;
66
use crate::mir::IntrinsicResult;
77
use crate::mir::operand::OperandRef;
88
use crate::mir::place::PlaceValue;
9+
use crate::traits::ReturnSlot;
910

1011
pub trait IntrinsicCallBuilderMethods<'tcx>: BackendTypes {
1112
/// Higher-level interface to emitting calls to intrinsics
@@ -35,6 +36,7 @@ pub trait IntrinsicCallBuilderMethods<'tcx>: BackendTypes {
3536
&mut self,
3637
instance: ty::Instance<'tcx>,
3738
args: &[OperandRef<'tcx, Self::Value>],
39+
return_slot: ReturnSlot<Self::Value>,
3840
is_cleanup: bool,
3941
) -> Self::Value;
4042

0 commit comments

Comments
 (0)