From 228aee184022e8b598906875e670bdce7e0be46f Mon Sep 17 00:00:00 2001 From: Bee Date: Sat, 24 Oct 2026 23:33:06 +0100 Subject: [PATCH] Don't require `extern "llvm-intrinsic"` return structs to be `#[repr(packed)]` --- .../rustc_codegen_gcc/src/intrinsic/mod.rs | 31 +++- compiler/rustc_codegen_llvm/src/intrinsic.rs | 85 +++++------ compiler/rustc_codegen_ssa/src/mir/block.rs | 139 ++++++++++++++---- compiler/rustc_codegen_ssa/src/mir/mod.rs | 4 +- .../rustc_codegen_ssa/src/traits/intrinsic.rs | 1 + tests/codegen-llvm/inject-autocast.rs | 58 ++++---- 6 files changed, 217 insertions(+), 101 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index ab1c886d3f9ea..a02c45c10ff4c 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -22,7 +22,7 @@ use rustc_codegen_ssa::{MemFlags, RetagInfo}; use rustc_data_structures::fx::FxHashSet; #[cfg(feature = "master")] use rustc_middle::ty::layout::FnAbiOf; -use rustc_middle::ty::layout::LayoutOf; +use rustc_middle::ty::layout::{HasTypingEnv, LayoutOf}; use rustc_middle::ty::{self, Instance, Ty}; use rustc_session::config::OptLevel; use rustc_span::{Span, Symbol, bug, span_bug, sym}; @@ -556,6 +556,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc &mut self, instance: ty::Instance<'tcx>, args: &[OperandRef<'tcx, Self::Value>], + memory_return_place: Option>, is_cleanup: bool, ) -> Self::Value { let func = if let Some(&func) = self.intrinsic_instances.borrow().get(&instance) { @@ -602,7 +603,33 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc self.apply_attrs_to_cleanup_callsite(llret); } - llret + let fn_sig = match *instance.ty(self.tcx, self.typing_env()).kind() { + ty::FnDef(def_id, args) => self.tcx.instantiate_bound_regions_with_erased( + self.tcx + .fn_sig(def_id) + .instantiate(self.tcx, args.no_bound_vars().unwrap()) + .skip_norm_wip(), + ), + _ => unreachable!(), + }; + let ret_layout = self.layout_of(fn_sig.output()); + + if !ret_layout.is_zst() && ret_layout.is_aggregate() { + let fields = + rustc_codegen_ssa::mir::intrinsic_return_struct_non_zst_fields(self.cx, ret_layout) + .into_iter() + .enumerate() + .map(|(i, (_, offset))| (offset, self.extract_value(llret, i as u64))) + .collect(); + rustc_codegen_ssa::mir::store_intrinsic_return_struct( + self, + ret_layout, + memory_return_place, + fields, + ) + } else { + llret + } } fn abort_immediate(&mut self) { diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 2b529aa2c5360..f991d8143ef19 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -897,6 +897,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { &mut self, instance: ty::Instance<'tcx>, args: &[OperandRef<'tcx, Self::Value>], + memory_return_place: Option>, _is_cleanup: bool, ) -> Self::Value { let tcx = self.tcx(); @@ -911,10 +912,15 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { assert!(!fn_sig.c_variadic()); let ret_layout = self.layout_of(fn_sig.output()); - let llreturn_ty = if ret_layout.is_zst() { - self.type_void() + let (llreturn_ty, return_struct_fields) = if ret_layout.is_zst() { + (self.type_void(), vec![]) + } else if ret_layout.is_aggregate() { + let fields = + rustc_codegen_ssa::mir::intrinsic_return_struct_non_zst_fields(self.cx, ret_layout); + let llvm_fields = fields.iter().copied().map(|(t, _)| t).collect::>(); + (self.type_struct(&llvm_fields, false), fields) } else { - ret_layout.immediate_llvm_type(self) + (ret_layout.immediate_llvm_type(self), vec![]) }; let mut llargument_tys = Vec::with_capacity(fn_sig.inputs().len()); @@ -970,14 +976,33 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { ) }; - let src_ty = self.val_ty(llret); - let dest_ty = llreturn_ty; - assert!( - can_autocast(self, dest_ty, src_ty), - "Cannot match `{src_ty:?}` (expected) with `{dest_ty:?}` (found) in `{fn_ptr:?}`" - ); + if return_struct_fields.is_empty() { + let src_ty = self.val_ty(llret); + let dest_ty = llreturn_ty; + assert!( + can_autocast(self, dest_ty, src_ty), + "Cannot match `{src_ty:?}` (expected) with `{dest_ty:?}` (found) in `{fn_ptr:?}`" + ); - autocast(self, llret, src_ty, dest_ty) + autocast(self, llret, src_ty, dest_ty) + } else { + let fields = return_struct_fields.into_iter().enumerate().map(|(i, (dest_ty, offset))| { + let value = self.extract_value(llret, i as u64); + let src_ty = self.val_ty(value); + assert!( + can_autocast(self, dest_ty, src_ty), + "Cannot match `{src_ty:?}` (expected) with `{dest_ty:?}` (found) in `{fn_ptr:?}`" + ); + let value = autocast(self, value, src_ty, dest_ty); + (offset, value) + }).collect(); + rustc_codegen_ssa::mir::store_intrinsic_return_struct( + self, + ret_layout, + memory_return_place, + fields, + ) + } } fn abort_immediate(&mut self) { @@ -1063,23 +1088,6 @@ fn can_autocast<'ll>(cx: &CodegenCx<'ll, '_>, rust_ty: &'ll Type, llvm_ty: &'ll } match cx.type_kind(llvm_ty) { - // Some LLVM intrinsics return **non-packed** structs, but they can't be mimicked from Rust - // due to auto field-alignment in non-packed structs (packed structs are represented in LLVM - // as, well, packed structs, so they won't match with those either) - TypeKind::Struct if cx.type_kind(rust_ty) == TypeKind::Struct => { - let rust_element_tys = cx.struct_element_types(rust_ty); - let llvm_element_tys = cx.struct_element_types(llvm_ty); - - if rust_element_tys.len() != llvm_element_tys.len() { - return false; - } - - iter::zip(rust_element_tys, llvm_element_tys).all( - |(rust_element_ty, llvm_element_ty)| { - can_autocast(cx, rust_element_ty, llvm_element_ty) - }, - ) - } _ if let Some((llvm_element_ty, element_count)) = cx.vector_element_type_and_length(llvm_ty) => { @@ -1123,19 +1131,6 @@ fn autocast<'ll>( return val; } match (bx.type_kind(src_ty), bx.type_kind(dest_ty)) { - // re-pack structs - (TypeKind::Struct, TypeKind::Struct) => { - let mut ret = bx.const_poison(dest_ty); - for (idx, (src_element_ty, dest_element_ty)) in - iter::zip(bx.struct_element_types(src_ty), bx.struct_element_types(dest_ty)) - .enumerate() - { - let elt = bx.extract_value(val, idx as u64); - let casted_elt = autocast(bx, elt, src_element_ty, dest_element_ty); - ret = bx.insert_value(ret, casted_elt, idx as u64); - } - ret - } // cast from the i1xN vector type to the primitive type (TypeKind::Vector, TypeKind::Integer) if let Some((elem, vector_length)) = bx.vector_element_type_and_length(src_ty) @@ -1233,9 +1228,17 @@ fn intrinsic_fn<'ll, 'tcx>( let llvm_argument_tys = bx.func_params_types(llvm_fn_ty); let llvm_is_variadic = bx.func_is_variadic(llvm_fn_ty); + let (rust_return_tys, llvm_return_tys) = if bx.type_kind(rust_return_ty) == TypeKind::Struct + && bx.type_kind(llvm_return_ty) == TypeKind::Struct + { + (bx.struct_element_types(rust_return_ty), bx.struct_element_types(llvm_return_ty)) + } else { + (vec![rust_return_ty], vec![llvm_return_ty]) + }; let is_correct_signature = !llvm_is_variadic && rust_argument_tys.len() == llvm_argument_tys.len() - && iter::once((rust_return_ty, llvm_return_ty)) + && rust_return_tys.len() == llvm_return_tys.len() + && iter::zip(rust_return_tys, llvm_return_tys) .chain(iter::zip(rust_argument_tys, llvm_argument_tys)) .all(|(rust_ty, llvm_ty)| can_autocast(bx, rust_ty, llvm_ty)); diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 5fe6e5bf4b3d2..73570f5056ba0 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -2,7 +2,8 @@ use std::cmp; use std::ops::Range; use rustc_abi::{ - Align, ArmCall, CanonAbi, ExternAbi, FieldsShape, HasDataLayout, Size, VariantIdx, Variants, + Align, ArmCall, BackendRepr, CanonAbi, ExternAbi, FieldsShape, HasDataLayout, Size, VariantIdx, + Variants, }; use rustc_ast as ast; use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; @@ -1166,38 +1167,42 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let result_layout = self.cx.layout_of(self.monomorphized_place_ty(destination.as_ref())); - let return_dest = if result_layout.is_zst() { - ReturnDest::Nothing - } else if let Some(index) = destination.as_local() { - match self.locals[index] { - LocalRef::Place(dest) => ReturnDest::Store(dest), - LocalRef::UnsizedPlace(_) => bug!("return type must be sized"), - LocalRef::PendingOperand => { - // Handle temporary places, specifically `Operand` ones, as - // they don't have `alloca`s. - ReturnDest::DirectOperand(index) - } - LocalRef::Operand(_) => bug!("place local already assigned to"), + let return_mode = if result_layout.is_zst() { + PassMode::Ignore + } else if let BackendRepr::ScalarPair { .. } = result_layout.backend_repr { + PassMode::Pair(ArgAttributes::new(), ArgAttributes::new()) + } else if result_layout.is_aggregate() { + PassMode::Indirect { + attrs: ArgAttributes::new(), + address_space: None, + mode: IndirectMode::Pointer, } } else { - ReturnDest::Store(self.codegen_place(bx, destination.as_ref())) + PassMode::Direct(ArgAttributes::new()) }; + let return_abi = ArgAbi { layout: result_layout, mode: return_mode }; + let (return_dest, memory_return_place) = + self.make_return_place(bx, destination, &return_abi); + assert_eq!( + memory_return_place.is_some(), + !result_layout.is_zst() + && matches!(result_layout.backend_repr, BackendRepr::Memory { sized: true }) + ); let args = args.into_iter().map(|arg| self.codegen_operand(bx, &arg.node)).collect::>(); self.set_debug_loc(bx, source_info); - let llret = - bx.codegen_llvm_intrinsic_call(instance, &args, self.mir[helper.bb].is_cleanup); + let llret = bx.codegen_llvm_intrinsic_call( + instance, + &args, + memory_return_place, + self.mir[helper.bb].is_cleanup, + ); if let Some(target) = target { - self.store_return( - bx, - return_dest, - &ArgAbi { layout: result_layout, mode: PassMode::Direct(ArgAttributes::new()) }, - llret, - ); + self.store_return(bx, return_dest, &return_abi, llret); return helper.funclet_br(self, bx, target, mergeable_succ, &[]); } else { bx.unreachable(); @@ -2424,9 +2429,25 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { dest: mir::Place<'tcx>, fn_ret: &ArgAbi<'tcx, Ty<'tcx>>, ) -> (ReturnDest<'tcx, Bx::Value>, ReturnSlot) { + let (dest, place) = self.make_return_place(bx, dest, fn_ret); + ( + dest, + match place { + None => ReturnSlot::Direct, + Some(place) => ReturnSlot::Indirect(place.llval), + }, + ) + } + + fn make_return_place( + &mut self, + bx: &mut Bx, + dest: mir::Place<'tcx>, + fn_ret: &ArgAbi<'tcx, Ty<'tcx>>, + ) -> (ReturnDest<'tcx, Bx::Value>, Option>) { // If the return is ignored, we can just return a do-nothing `ReturnDest`. if fn_ret.is_ignore() { - return (ReturnDest::Nothing, ReturnSlot::Direct); + return (ReturnDest::Nothing, None); } let dest = if let Some(index) = dest.as_local() { match self.locals[index] { @@ -2440,12 +2461,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // but the calling convention has an indirect return. let tmp = PlaceRef::alloca(bx, fn_ret.layout); tmp.storage_live(bx); - ( - ReturnDest::IndirectOperand(tmp, index), - ReturnSlot::Indirect(tmp.val.llval), - ) + (ReturnDest::IndirectOperand(tmp, index), Some(tmp.val)) } else { - (ReturnDest::DirectOperand(index), ReturnSlot::Direct) + (ReturnDest::DirectOperand(index), None) }; } LocalRef::Operand(_) => { @@ -2465,13 +2483,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // to create a temporary. span_bug!(self.mir.span, "can't directly store to unaligned value"); } - (ReturnDest::Nothing, ReturnSlot::Indirect(dest.val.llval)) + (ReturnDest::Nothing, Some(dest.val)) } else { - (ReturnDest::Store(dest), ReturnSlot::Direct) + (ReturnDest::Store(dest), None) } } - // Stores the return value of a function call into it's final location. + /// Stores the return value of a function call into it's final location. fn store_return( &mut self, bx: &mut Bx, @@ -2573,3 +2591,62 @@ pub fn store_cast<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( bx.store(value, ptr, align); }; } + +pub fn intrinsic_return_struct_non_zst_fields<'tcx, Cx: CodegenMethods<'tcx>>( + cx: &Cx, + ret_layout: TyAndLayout<'tcx>, +) -> Vec<(Cx::Type, Size)> { + let ret_layout = ret_layout.peel_transparent_wrappers_from_non_1zst(cx); + match ret_layout.ty.kind() { + ty::Adt(adt_def, _) if adt_def.is_struct() => {} + ty::Tuple(_) => {} + _ => bug!("invalid intrinsic return type: {ret_layout:?}"), + } + let fields = (0..ret_layout.fields.count()) + .filter_map(|i| { + let field = ret_layout.field(cx, i); + assert!( + field.is_zst() || !field.is_aggregate(), + "invalid intrinsic return field: {field:?}" + ); + (!field.is_zst()) + .then(|| (cx.immediate_backend_type(field), ret_layout.fields.offset(i))) + }) + .collect::>(); + assert!(fields.len() >= 2, "invalid intrinsic return type: {ret_layout:?}"); + fields +} + +pub fn store_intrinsic_return_struct<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( + bx: &mut Bx, + ret_layout: TyAndLayout<'tcx>, + memory_return_place: Option>, + fields: Vec<(Size, Bx::Value)>, +) -> Bx::Value { + match ret_layout.backend_repr { + BackendRepr::ScalarPair { a: _, b: _, b_offset } => { + assert!(memory_return_place.is_none()); + let a_index = if fields[0].0 == b_offset { 1 } else { 0 }; + let b_index = if fields[1].0 == b_offset { 1 } else { 0 }; + assert_ne!(a_index, b_index); + let mut pair = bx.cx().const_poison(bx.immediate_backend_type(ret_layout)); + pair = bx.insert_value(pair, fields[a_index].1, 0); + bx.insert_value(pair, fields[b_index].1, 1) + } + BackendRepr::Memory { sized: true } => { + let dest = + memory_return_place.expect("BackendRepr::Memory return with no return place"); + for (offset, value) in fields.into_iter() { + let pointer = if offset.bytes() != 0 { + bx.inbounds_ptradd(dest.llval, bx.const_usize(offset.bytes())) + } else { + dest.llval + }; + bx.store(value, pointer, dest.align.restrict_for_offset(offset)); + } + // Return value is ignored as result was stored in `memory_return_place` + dest.llval + } + _ => bug!("unexpected aggregate return layout: {ret_layout:?}"), + } +} diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index 6cb9e33f4680d..652dc95685acf 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -28,7 +28,9 @@ mod retag; mod rvalue; mod statement; -pub use self::block::store_cast; +pub use self::block::{ + intrinsic_return_struct_non_zst_fields, store_cast, store_intrinsic_return_struct, +}; use self::debuginfo::{FunctionDebugContext, PerLocalVarDebugInfo}; use self::operand::{OperandRef, OperandValue}; use self::place::PlaceRef; diff --git a/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs b/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs index 530597c616ff9..49a8eedc8898b 100644 --- a/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs @@ -35,6 +35,7 @@ pub trait IntrinsicCallBuilderMethods<'tcx>: BackendTypes { &mut self, instance: ty::Instance<'tcx>, args: &[OperandRef<'tcx, Self::Value>], + memory_return_place: Option>, is_cleanup: bool, ) -> Self::Value; diff --git a/tests/codegen-llvm/inject-autocast.rs b/tests/codegen-llvm/inject-autocast.rs index f07153c17daf7..b2c0ab0e7b809 100644 --- a/tests/codegen-llvm/inject-autocast.rs +++ b/tests/codegen-llvm/inject-autocast.rs @@ -6,9 +6,7 @@ use std::simd::{f32x4, i16x8, i64x2}; -#[repr(C, packed)] pub struct Bar(u32, i64x2, i64x2, i64x2, i64x2, i64x2, i64x2); -// CHECK: %Bar = type <{ i32, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64> }> #[repr(simd)] pub struct Tile([i8; 1024]); @@ -21,21 +19,27 @@ pub unsafe fn struct_autocast(key_metadata: u32, key: i64x2) -> Bar { fn foo(key_metadata: u32, key: i64x2) -> Bar; } - // CHECK: [[A:%[0-9]+]] = call { i32, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64> } @llvm.x86.encodekey128(i32 {{.*}}, <2 x i64> {{.*}}) - // CHECK: [[B:%[0-9]+]] = extractvalue { i32, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64> } [[A]], 0 - // CHECK: [[C:%[0-9]+]] = insertvalue %Bar poison, i32 [[B]], 0 - // CHECK: [[D:%[0-9]+]] = extractvalue { i32, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64> } [[A]], 1 - // CHECK: [[E:%[0-9]+]] = insertvalue %Bar [[C]], <2 x i64> [[D]], 1 - // CHECK: [[F:%[0-9]+]] = extractvalue { i32, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64> } [[A]], 2 - // CHECK: [[G:%[0-9]+]] = insertvalue %Bar [[E]], <2 x i64> [[F]], 2 - // CHECK: [[H:%[0-9]+]] = extractvalue { i32, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64> } [[A]], 3 - // CHECK: [[I:%[0-9]+]] = insertvalue %Bar [[G]], <2 x i64> [[H]], 3 - // CHECK: [[J:%[0-9]+]] = extractvalue { i32, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64> } [[A]], 4 - // CHECK: [[K:%[0-9]+]] = insertvalue %Bar [[I]], <2 x i64> [[J]], 4 - // CHECK: [[L:%[0-9]+]] = extractvalue { i32, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64> } [[A]], 5 - // CHECK: [[M:%[0-9]+]] = insertvalue %Bar [[K]], <2 x i64> [[L]], 5 - // CHECK: [[N:%[0-9]+]] = extractvalue { i32, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64> } [[A]], 6 - // CHECK: insertvalue %Bar [[M]], <2 x i64> [[N]], 6 + // CHECK: %0 = call { i32, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64> } @llvm.x86.encodekey128(i32 %key_metadata, <2 x i64> %key) + // CHECK-NEXT: %1 = extractvalue { i32, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64> } %0, 0 + // CHECK-NEXT: %2 = extractvalue { i32, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64> } %0, 1 + // CHECK-NEXT: %3 = extractvalue { i32, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64> } %0, 2 + // CHECK-NEXT: %4 = extractvalue { i32, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64> } %0, 3 + // CHECK-NEXT: %5 = extractvalue { i32, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64> } %0, 4 + // CHECK-NEXT: %6 = extractvalue { i32, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64> } %0, 5 + // CHECK-NEXT: %7 = extractvalue { i32, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64> } %0, 6 + // CHECK-NEXT: %8 = getelementptr inbounds i8, ptr %_0, i64 96 + // CHECK-NEXT: store i32 %1, ptr %8, align 16 + // CHECK-NEXT: store <2 x i64> %2, ptr %_0, align 16 + // CHECK-NEXT: %9 = getelementptr inbounds i8, ptr %_0, i64 16 + // CHECK-NEXT: store <2 x i64> %3, ptr %9, align 16 + // CHECK-NEXT: %10 = getelementptr inbounds i8, ptr %_0, i64 32 + // CHECK-NEXT: store <2 x i64> %4, ptr %10, align 16 + // CHECK-NEXT: %11 = getelementptr inbounds i8, ptr %_0, i64 48 + // CHECK-NEXT: store <2 x i64> %5, ptr %11, align 16 + // CHECK-NEXT: %12 = getelementptr inbounds i8, ptr %_0, i64 64 + // CHECK-NEXT: store <2 x i64> %6, ptr %12, align 16 + // CHECK-NEXT: %13 = getelementptr inbounds i8, ptr %_0, i64 80 + // CHECK-NEXT: store <2 x i64> %7, ptr %13, align 16 foo(key_metadata, key) } @@ -47,15 +51,17 @@ pub unsafe fn struct_with_i1_vector_autocast(a: i64x2, b: i64x2) -> (u8, u8) { fn foo(a: i64x2, b: i64x2) -> (u8, u8); } - // CHECK: [[A:%[0-9]+]] = call { <2 x i1>, <2 x i1> } @llvm.x86.avx512.vp2intersect.q.128(<2 x i64> {{.*}}, <2 x i64> {{.*}}) - // CHECK: [[B:%[0-9]+]] = extractvalue { <2 x i1>, <2 x i1> } [[A]], 0 - // CHECK: [[C:%[0-9]+]] = shufflevector <2 x i1> [[B]], <2 x i1> zeroinitializer, <8 x i32> - // CHECK: [[D:%[0-9]+]] = bitcast <8 x i1> [[C]] to i8 - // CHECK: [[E:%[0-9]+]] = insertvalue { i8, i8 } poison, i8 [[D]], 0 - // CHECK: [[F:%[0-9]+]] = extractvalue { <2 x i1>, <2 x i1> } [[A]], 1 - // CHECK: [[G:%[0-9]+]] = shufflevector <2 x i1> [[F]], <2 x i1> zeroinitializer, <8 x i32> - // CHECK: [[H:%[0-9]+]] = bitcast <8 x i1> [[G]] to i8 - // CHECK: insertvalue { i8, i8 } [[E]], i8 [[H]], 1 + // CHECK: %0 = call { <2 x i1>, <2 x i1> } @llvm.x86.avx512.vp2intersect.q.128(<2 x i64> %a, <2 x i64> %b) + // CHECK-NEXT: %1 = extractvalue { <2 x i1>, <2 x i1> } %0, 0 + // CHECK-NEXT: %2 = shufflevector <2 x i1> %1, <2 x i1> zeroinitializer, <8 x i32> + // CHECK-NEXT: %3 = bitcast <8 x i1> %2 to i8 + // CHECK-NEXT: %4 = extractvalue { <2 x i1>, <2 x i1> } %0, 1 + // CHECK-NEXT: %5 = shufflevector <2 x i1> %4, <2 x i1> zeroinitializer, <8 x i32> + // CHECK-NEXT: %6 = bitcast <8 x i1> %5 to i8 + // CHECK-NEXT: %7 = insertvalue { i8, i8 } poison, i8 %3, 0 + // CHECK-NEXT: %8 = insertvalue { i8, i8 } %7, i8 %6, 1 + // CHECK-NEXT: %_0.0 = extractvalue { i8, i8 } %8, 0 + // CHECK-NEXT: %_0.1 = extractvalue { i8, i8 } %8, 1 foo(a, b) }