From 419f376564d17eaef7e5b4a77b066668e4272ecc Mon Sep 17 00:00:00 2001 From: Bill Schmidt Date: Wed, 19 Sep 2012 15:42:13 +0000 Subject: [PATCH] Small structs for PPC64 SVR4 must be passed right-justified in registers. lib/Target/PowerPC/PPCISelLowering.{h,cpp} Rename LowerFormalArguments_Darwin to LowerFormalArguments_Darwin_Or_64SVR4. Rename LowerFormalArguments_SVR4 to LowerFormalArguments_32SVR4. Receive small structs right-justified in LowerFormalArguments_Darwin_Or_64SVR4. Rename LowerCall_Darwin to LowerCall_Darwin_Or_64SVR4. Rename LowerCall_SVR4 to LowerCall_32SVR4. Pass small structs right-justified in LowerCall_Darwin_Or_64SVR4. test/CodeGen/PowerPC/structsinregs.ll New test. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@164228 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Target/PowerPC/PPCISelLowering.cpp | 175 +++++++++++++++------ lib/Target/PowerPC/PPCISelLowering.h | 29 ++-- test/CodeGen/PowerPC/structsinregs.ll | 204 +++++++++++++++++++++++++ 3 files changed, 347 insertions(+), 61 deletions(-) create mode 100644 test/CodeGen/PowerPC/structsinregs.ll diff --git a/lib/Target/PowerPC/PPCISelLowering.cpp b/lib/Target/PowerPC/PPCISelLowering.cpp index 3b22ac4acce..2e8fa1842ae 100644 --- a/lib/Target/PowerPC/PPCISelLowering.cpp +++ b/lib/Target/PowerPC/PPCISelLowering.cpp @@ -1717,16 +1717,16 @@ PPCTargetLowering::LowerFormalArguments(SDValue Chain, SmallVectorImpl &InVals) const { if (PPCSubTarget.isSVR4ABI() && !PPCSubTarget.isPPC64()) { - return LowerFormalArguments_SVR4(Chain, CallConv, isVarArg, Ins, - dl, DAG, InVals); - } else { - return LowerFormalArguments_Darwin(Chain, CallConv, isVarArg, Ins, + return LowerFormalArguments_32SVR4(Chain, CallConv, isVarArg, Ins, dl, DAG, InVals); + } else { + return LowerFormalArguments_Darwin_Or_64SVR4(Chain, CallConv, isVarArg, Ins, + dl, DAG, InVals); } } SDValue -PPCTargetLowering::LowerFormalArguments_SVR4( +PPCTargetLowering::LowerFormalArguments_32SVR4( SDValue Chain, CallingConv::ID CallConv, bool isVarArg, const SmallVectorImpl @@ -1944,7 +1944,7 @@ PPCTargetLowering::LowerFormalArguments_SVR4( } SDValue -PPCTargetLowering::LowerFormalArguments_Darwin( +PPCTargetLowering::LowerFormalArguments_Darwin_Or_64SVR4( SDValue Chain, CallingConv::ID CallConv, bool isVarArg, const SmallVectorImpl @@ -1959,6 +1959,7 @@ PPCTargetLowering::LowerFormalArguments_Darwin( EVT PtrVT = DAG.getTargetLoweringInfo().getPointerTy(); bool isPPC64 = PtrVT == MVT::i64; + bool isSVR4ABI = PPCSubTarget.isSVR4ABI(); // Potential tail calls could cause overwriting of argument stack slots. bool isImmutable = !(getTargetMachine().Options.GuaranteedTailCallOpt && (CallConv == CallingConv::Fast)); @@ -2019,10 +2020,12 @@ PPCTargetLowering::LowerFormalArguments_Darwin( default: llvm_unreachable("Unhandled argument type!"); case MVT::i32: case MVT::f32: - VecArgOffset += isPPC64 ? 8 : 4; + VecArgOffset += 4; break; case MVT::i64: // PPC64 case MVT::f64: + // FIXME: We are guaranteed to be !isPPC64 at this point. + // Does MVT::i64 apply? VecArgOffset += 8; break; case MVT::v4f32: @@ -2076,8 +2079,11 @@ PPCTargetLowering::LowerFormalArguments_Darwin( // ObjSize is the true size, ArgSize rounded up to multiple of registers. ObjSize = Flags.getByValSize(); ArgSize = ((ObjSize + PtrByteSize - 1)/PtrByteSize) * PtrByteSize; - // Objects of size 1 and 2 are right justified, everything else is - // left justified. This means the memory address is adjusted forwards. + // FOR DARWIN: Objects of size 1 and 2 are right justified, everything + // else is left justified. This means the memory address is adjusted + // forwards. + // FOR 64-BIT SVR4: All aggregates smaller than 8 bytes must be passed + // right-justified. if (ObjSize==1 || ObjSize==2) { CurArgOffset = CurArgOffset + (4 - ObjSize); } @@ -2085,7 +2091,8 @@ PPCTargetLowering::LowerFormalArguments_Darwin( int FI = MFI->CreateFixedObject(ObjSize, CurArgOffset, true); SDValue FIN = DAG.getFrameIndex(FI, PtrVT); InVals.push_back(FIN); - if (ObjSize==1 || ObjSize==2) { + if (ObjSize==1 || ObjSize==2 || + (ObjSize==4 && isSVR4ABI)) { if (GPR_idx != Num_GPR_Regs) { unsigned VReg; if (isPPC64) @@ -2093,10 +2100,11 @@ PPCTargetLowering::LowerFormalArguments_Darwin( else VReg = MF.addLiveIn(GPR[GPR_idx], &PPC::GPRCRegClass); SDValue Val = DAG.getCopyFromReg(Chain, dl, VReg, PtrVT); + EVT ObjType = (ObjSize == 1 ? MVT::i8 : + (ObjSize == 2 ? MVT::i16 : MVT::i32)); SDValue Store = DAG.getTruncStore(Val.getValue(1), dl, Val, FIN, MachinePointerInfo(), - ObjSize==1 ? MVT::i8 : MVT::i16, - false, false, 0); + ObjType, false, false, 0); MemOps.push_back(Store); ++GPR_idx; } @@ -2107,8 +2115,8 @@ PPCTargetLowering::LowerFormalArguments_Darwin( } for (unsigned j = 0; j < ArgSize; j += PtrByteSize) { // Store whatever pieces of the object are in registers - // to memory. ArgVal will be address of the beginning of - // the object. + // to memory. ArgOffset will be the address of the beginning + // of the object. if (GPR_idx != Num_GPR_Regs) { unsigned VReg; if (isPPC64) @@ -2118,7 +2126,16 @@ PPCTargetLowering::LowerFormalArguments_Darwin( int FI = MFI->CreateFixedObject(PtrByteSize, ArgOffset, true); SDValue FIN = DAG.getFrameIndex(FI, PtrVT); SDValue Val = DAG.getCopyFromReg(Chain, dl, VReg, PtrVT); - SDValue Store = DAG.getStore(Val.getValue(1), dl, Val, FIN, + SDValue Shifted = Val; + + // For 64-bit SVR4, small structs come in right-adjusted. + // Shift them left so the following logic works as expected. + if (ObjSize < 8 && isSVR4ABI) { + SDValue ShiftAmt = DAG.getConstant(64 - 8 * ObjSize, PtrVT); + Shifted = DAG.getNode(ISD::SHL, dl, PtrVT, Val, ShiftAmt); + } + + SDValue Store = DAG.getStore(Val.getValue(1), dl, Shifted, FIN, MachinePointerInfo(), false, false, 0); MemOps.push_back(Store); @@ -2308,8 +2325,8 @@ PPCTargetLowering::LowerFormalArguments_Darwin( return Chain; } -/// CalculateParameterAndLinkageAreaSize - Get the size of the paramter plus -/// linkage area for the Darwin ABI. +/// CalculateParameterAndLinkageAreaSize - Get the size of the parameter plus +/// linkage area for the Darwin ABI, or the 64-bit SVR4 ABI. static unsigned CalculateParameterAndLinkageAreaSize(SelectionDAG &DAG, bool isPPC64, @@ -2718,7 +2735,7 @@ unsigned PrepareCall(SelectionDAG &DAG, SDValue &Callee, SDValue &InFlag, // Thus for a call through a function pointer, the following actions need // to be performed: // 1. Save the TOC of the caller in the TOC save area of its stack - // frame (this is done in LowerCall_Darwin()). + // frame (this is done in LowerCall_Darwin_Or_64SVR4()). // 2. Load the address of the function entry point from the function // descriptor. // 3. Load the TOC of the callee from the function descriptor into r2. @@ -2969,25 +2986,25 @@ PPCTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI, Ins, DAG); if (PPCSubTarget.isSVR4ABI() && !PPCSubTarget.isPPC64()) - return LowerCall_SVR4(Chain, Callee, CallConv, isVarArg, - isTailCall, Outs, OutVals, Ins, - dl, DAG, InVals); + return LowerCall_32SVR4(Chain, Callee, CallConv, isVarArg, + isTailCall, Outs, OutVals, Ins, + dl, DAG, InVals); - return LowerCall_Darwin(Chain, Callee, CallConv, isVarArg, - isTailCall, Outs, OutVals, Ins, - dl, DAG, InVals); + return LowerCall_Darwin_Or_64SVR4(Chain, Callee, CallConv, isVarArg, + isTailCall, Outs, OutVals, Ins, + dl, DAG, InVals); } SDValue -PPCTargetLowering::LowerCall_SVR4(SDValue Chain, SDValue Callee, - CallingConv::ID CallConv, bool isVarArg, - bool isTailCall, - const SmallVectorImpl &Outs, - const SmallVectorImpl &OutVals, - const SmallVectorImpl &Ins, - DebugLoc dl, SelectionDAG &DAG, - SmallVectorImpl &InVals) const { - // See PPCTargetLowering::LowerFormalArguments_SVR4() for a description +PPCTargetLowering::LowerCall_32SVR4(SDValue Chain, SDValue Callee, + CallingConv::ID CallConv, bool isVarArg, + bool isTailCall, + const SmallVectorImpl &Outs, + const SmallVectorImpl &OutVals, + const SmallVectorImpl &Ins, + DebugLoc dl, SelectionDAG &DAG, + SmallVectorImpl &InVals) const { + // See PPCTargetLowering::LowerFormalArguments_32SVR4() for a description // of the 32-bit SVR4 ABI stack frame layout. assert((CallConv == CallingConv::C || @@ -3192,7 +3209,7 @@ PPCTargetLowering::LowerCall_SVR4(SDValue Chain, SDValue Callee, } SDValue -PPCTargetLowering::LowerCall_Darwin(SDValue Chain, SDValue Callee, +PPCTargetLowering::LowerCall_Darwin_Or_64SVR4(SDValue Chain, SDValue Callee, CallingConv::ID CallConv, bool isVarArg, bool isTailCall, const SmallVectorImpl &Outs, @@ -3201,6 +3218,8 @@ PPCTargetLowering::LowerCall_Darwin(SDValue Chain, SDValue Callee, DebugLoc dl, SelectionDAG &DAG, SmallVectorImpl &InVals) const { + bool isSVR4ABI = PPCSubTarget.isSVR4ABI(); + unsigned NumOps = Outs.size(); EVT PtrVT = DAG.getTargetLoweringInfo().getPointerTy(); @@ -3308,12 +3327,22 @@ PPCTargetLowering::LowerCall_Darwin(SDValue Chain, SDValue Callee, } // FIXME memcpy is used way more than necessary. Correctness first. + // Note: "by value" is code for passing a structure by value, not + // basic types. if (Flags.isByVal()) { + // Note: Size includes alignment padding, so + // struct x { short a; char b; } + // will have Size = 4. With #pragma pack(1), it will have Size = 3. + // These are the proper values we need for right-justifying the + // aggregate in a parameter register for 64-bit SVR4. unsigned Size = Flags.getByValSize(); - if (Size==1 || Size==2) { - // Very small objects are passed right-justified. - // Everything else is passed left-justified. - EVT VT = (Size==1) ? MVT::i8 : MVT::i16; + // FOR DARWIN ONLY: Very small objects are passed right-justified. + // Everything else is passed left-justified. + // FOR 64-BIT SVR4: All aggregates smaller than 8 bytes must + // be passed right-justified. + if (Size==1 || Size==2 || + (Size==4 && isSVR4ABI)) { + EVT VT = (Size==1) ? MVT::i8 : ((Size==2) ? MVT::i16 : MVT::i32); if (GPR_idx != NumGPRs) { SDValue Load = DAG.getExtLoad(ISD::EXTLOAD, dl, PtrVT, Chain, Arg, MachinePointerInfo(), VT, @@ -3341,15 +3370,67 @@ PPCTargetLowering::LowerCall_Darwin(SDValue Chain, SDValue Callee, // Copy entire object into memory. There are cases where gcc-generated // code assumes it is there, even if it could be put entirely into // registers. (This is not what the doc says.) - SDValue MemcpyCall = CreateCopyOfByValArgument(Arg, PtrOff, - CallSeqStart.getNode()->getOperand(0), - Flags, DAG, dl); - // This must go outside the CALLSEQ_START..END. - SDValue NewCallSeqStart = DAG.getCALLSEQ_START(MemcpyCall, - CallSeqStart.getNode()->getOperand(1)); - DAG.ReplaceAllUsesWith(CallSeqStart.getNode(), NewCallSeqStart.getNode()); - Chain = CallSeqStart = NewCallSeqStart; - // And copy the pieces of it that fit into registers. + + // FIXME: The above statement is likely due to a misunderstanding of the + // documents. At least for 64-bit SVR4, all arguments must be copied + // into the parameter area BY THE CALLEE in the event that the callee + // takes the address of any formal argument. That has not yet been + // implemented. However, it is reasonable to use the stack area as a + // staging area for the register load. + + // Skip this for small aggregates under 64-bit SVR4, as we will use + // the same slot for a right-justified copy, below. + if (Size >= 8 || !isSVR4ABI) { + SDValue MemcpyCall = CreateCopyOfByValArgument(Arg, PtrOff, + CallSeqStart.getNode()->getOperand(0), + Flags, DAG, dl); + // This must go outside the CALLSEQ_START..END. + SDValue NewCallSeqStart = DAG.getCALLSEQ_START(MemcpyCall, + CallSeqStart.getNode()->getOperand(1)); + DAG.ReplaceAllUsesWith(CallSeqStart.getNode(), + NewCallSeqStart.getNode()); + Chain = CallSeqStart = NewCallSeqStart; + } + + // FOR 64-BIT SVR4: When a register is available, pass the + // aggregate right-justified. + if (isSVR4ABI && Size < 8 && GPR_idx != NumGPRs) { + // The easiest way to get this right-justified in a register + // is to copy the structure into the rightmost portion of a + // local variable slot, then load the whole slot into the + // register. + // FIXME: The memcpy seems to produce pretty awful code for + // small aggregates, particularly for packed ones. + // FIXME: It would be preferable to use the slot in the + // parameter save area instead of a new local variable. + SDValue Const = DAG.getConstant(8 - Size, PtrOff.getValueType()); + SDValue AddPtr = DAG.getNode(ISD::ADD, dl, PtrVT, PtrOff, Const); + SDValue MemcpyCall = CreateCopyOfByValArgument(Arg, AddPtr, + CallSeqStart.getNode()->getOperand(0), + Flags, DAG, dl); + + // Place the memcpy outside the CALLSEQ_START..END. + SDValue NewCallSeqStart = DAG.getCALLSEQ_START(MemcpyCall, + CallSeqStart.getNode()->getOperand(1)); + DAG.ReplaceAllUsesWith(CallSeqStart.getNode(), + NewCallSeqStart.getNode()); + Chain = CallSeqStart = NewCallSeqStart; + + // Load the slot into the register. + SDValue Load = DAG.getLoad(PtrVT, dl, Chain, PtrOff, + MachinePointerInfo(), + false, false, false, 0); + MemOpChains.push_back(Load.getValue(1)); + RegsToPass.push_back(std::make_pair(GPR[GPR_idx++], Load)); + + // Done with this argument. + ArgOffset += PtrByteSize; + continue; + } + + // For small aggregates (Darwin only) and aggregates >= PtrByteSize, + // copy the pieces of the object that fit into registers from the + // parameter save area. for (unsigned j=0; j &Ins, DebugLoc dl, SelectionDAG &DAG, SmallVectorImpl &InVals) const; SDValue - LowerFormalArguments_SVR4(SDValue Chain, - CallingConv::ID CallConv, bool isVarArg, - const SmallVectorImpl &Ins, - DebugLoc dl, SelectionDAG &DAG, - SmallVectorImpl &InVals) const; + LowerFormalArguments_32SVR4(SDValue Chain, + CallingConv::ID CallConv, bool isVarArg, + const SmallVectorImpl &Ins, + DebugLoc dl, SelectionDAG &DAG, + SmallVectorImpl &InVals) const; SDValue - LowerCall_Darwin(SDValue Chain, SDValue Callee, CallingConv::ID CallConv, + LowerCall_Darwin_Or_64SVR4(SDValue Chain, SDValue Callee, + CallingConv::ID CallConv, bool isVarArg, bool isTailCall, const SmallVectorImpl &Outs, const SmallVectorImpl &OutVals, @@ -488,13 +489,13 @@ namespace llvm { DebugLoc dl, SelectionDAG &DAG, SmallVectorImpl &InVals) const; SDValue - LowerCall_SVR4(SDValue Chain, SDValue Callee, CallingConv::ID CallConv, - bool isVarArg, bool isTailCall, - const SmallVectorImpl &Outs, - const SmallVectorImpl &OutVals, - const SmallVectorImpl &Ins, - DebugLoc dl, SelectionDAG &DAG, - SmallVectorImpl &InVals) const; + LowerCall_32SVR4(SDValue Chain, SDValue Callee, CallingConv::ID CallConv, + bool isVarArg, bool isTailCall, + const SmallVectorImpl &Outs, + const SmallVectorImpl &OutVals, + const SmallVectorImpl &Ins, + DebugLoc dl, SelectionDAG &DAG, + SmallVectorImpl &InVals) const; }; } diff --git a/test/CodeGen/PowerPC/structsinregs.ll b/test/CodeGen/PowerPC/structsinregs.ll new file mode 100644 index 00000000000..8b283ea7f6e --- /dev/null +++ b/test/CodeGen/PowerPC/structsinregs.ll @@ -0,0 +1,204 @@ +; RUN: llc -O0 -disable-fp-elim < %s | FileCheck %s +target datalayout = "E-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v128:128:128-n32:64" +target triple = "powerpc64-unknown-linux-gnu" + +%struct.s1 = type { i8 } +%struct.s2 = type { i16 } +%struct.s4 = type { i32 } +%struct.t1 = type { i8 } +%struct.t3 = type <{ i16, i8 }> +%struct.t5 = type <{ i32, i8 }> +%struct.t6 = type <{ i32, i16 }> +%struct.t7 = type <{ i32, i16, i8 }> +%struct.s3 = type { i16, i8 } +%struct.s5 = type { i32, i8 } +%struct.s6 = type { i32, i16 } +%struct.s7 = type { i32, i16, i8 } +%struct.t2 = type <{ i16 }> +%struct.t4 = type <{ i32 }> + +@caller1.p1 = private unnamed_addr constant %struct.s1 { i8 1 }, align 1 +@caller1.p2 = private unnamed_addr constant %struct.s2 { i16 2 }, align 2 +@caller1.p3 = private unnamed_addr constant { i16, i8, i8 } { i16 4, i8 8, i8 undef }, align 2 +@caller1.p4 = private unnamed_addr constant %struct.s4 { i32 16 }, align 4 +@caller1.p5 = private unnamed_addr constant { i32, i8, [3 x i8] } { i32 32, i8 64, [3 x i8] undef }, align 4 +@caller1.p6 = private unnamed_addr constant { i32, i16, [2 x i8] } { i32 128, i16 256, [2 x i8] undef }, align 4 +@caller1.p7 = private unnamed_addr constant { i32, i16, i8, i8 } { i32 512, i16 1024, i8 -3, i8 undef }, align 4 +@caller2.p1 = private unnamed_addr constant %struct.t1 { i8 1 }, align 1 +@caller2.p2 = private unnamed_addr constant { i16 } { i16 2 }, align 1 +@caller2.p3 = private unnamed_addr constant %struct.t3 <{ i16 4, i8 8 }>, align 1 +@caller2.p4 = private unnamed_addr constant { i32 } { i32 16 }, align 1 +@caller2.p5 = private unnamed_addr constant %struct.t5 <{ i32 32, i8 64 }>, align 1 +@caller2.p6 = private unnamed_addr constant %struct.t6 <{ i32 128, i16 256 }>, align 1 +@caller2.p7 = private unnamed_addr constant %struct.t7 <{ i32 512, i16 1024, i8 -3 }>, align 1 + +define i32 @caller1() nounwind { +entry: + %p1 = alloca %struct.s1, align 1 + %p2 = alloca %struct.s2, align 2 + %p3 = alloca %struct.s3, align 2 + %p4 = alloca %struct.s4, align 4 + %p5 = alloca %struct.s5, align 4 + %p6 = alloca %struct.s6, align 4 + %p7 = alloca %struct.s7, align 4 + %0 = bitcast %struct.s1* %p1 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* %0, i8* getelementptr inbounds (%struct.s1* @caller1.p1, i32 0, i32 0), i64 1, i32 1, i1 false) + %1 = bitcast %struct.s2* %p2 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* %1, i8* bitcast (%struct.s2* @caller1.p2 to i8*), i64 2, i32 2, i1 false) + %2 = bitcast %struct.s3* %p3 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* %2, i8* bitcast ({ i16, i8, i8 }* @caller1.p3 to i8*), i64 4, i32 2, i1 false) + %3 = bitcast %struct.s4* %p4 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* %3, i8* bitcast (%struct.s4* @caller1.p4 to i8*), i64 4, i32 4, i1 false) + %4 = bitcast %struct.s5* %p5 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* %4, i8* bitcast ({ i32, i8, [3 x i8] }* @caller1.p5 to i8*), i64 8, i32 4, i1 false) + %5 = bitcast %struct.s6* %p6 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* %5, i8* bitcast ({ i32, i16, [2 x i8] }* @caller1.p6 to i8*), i64 8, i32 4, i1 false) + %6 = bitcast %struct.s7* %p7 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* %6, i8* bitcast ({ i32, i16, i8, i8 }* @caller1.p7 to i8*), i64 8, i32 4, i1 false) + %call = call i32 @callee1(%struct.s1* byval %p1, %struct.s2* byval %p2, %struct.s3* byval %p3, %struct.s4* byval %p4, %struct.s5* byval %p5, %struct.s6* byval %p6, %struct.s7* byval %p7) + ret i32 %call + +; CHECK: ld 9, 128(31) +; CHECK: ld 8, 136(31) +; CHECK: ld 7, 144(31) +; CHECK: lwz 6, 152(31) +; CHECK: lwz 5, 160(31) +; CHECK: lhz 4, 168(31) +; CHECK: lbz 3, 176(31) +} + +declare void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture, i8* nocapture, i64, i32, i1) nounwind + +define internal i32 @callee1(%struct.s1* byval %v1, %struct.s2* byval %v2, %struct.s3* byval %v3, %struct.s4* byval %v4, %struct.s5* byval %v5, %struct.s6* byval %v6, %struct.s7* byval %v7) nounwind { +entry: + %a = getelementptr inbounds %struct.s1* %v1, i32 0, i32 0 + %0 = load i8* %a, align 1 + %conv = zext i8 %0 to i32 + %a1 = getelementptr inbounds %struct.s2* %v2, i32 0, i32 0 + %1 = load i16* %a1, align 2 + %conv2 = sext i16 %1 to i32 + %add = add nsw i32 %conv, %conv2 + %a3 = getelementptr inbounds %struct.s3* %v3, i32 0, i32 0 + %2 = load i16* %a3, align 2 + %conv4 = sext i16 %2 to i32 + %add5 = add nsw i32 %add, %conv4 + %a6 = getelementptr inbounds %struct.s4* %v4, i32 0, i32 0 + %3 = load i32* %a6, align 4 + %add7 = add nsw i32 %add5, %3 + %a8 = getelementptr inbounds %struct.s5* %v5, i32 0, i32 0 + %4 = load i32* %a8, align 4 + %add9 = add nsw i32 %add7, %4 + %a10 = getelementptr inbounds %struct.s6* %v6, i32 0, i32 0 + %5 = load i32* %a10, align 4 + %add11 = add nsw i32 %add9, %5 + %a12 = getelementptr inbounds %struct.s7* %v7, i32 0, i32 0 + %6 = load i32* %a12, align 4 + %add13 = add nsw i32 %add11, %6 + ret i32 %add13 + +; CHECK: std 9, 96(1) +; CHECK: std 8, 88(1) +; CHECK: std 7, 80(1) +; CHECK: stw 6, 72(1) +; CHECK: stw 5, 64(1) +; CHECK: sth 4, 58(1) +; CHECK: stb 3, 51(1) +; CHECK: lha {{[0-9]+}}, 58(1) +; CHECK: lbz {{[0-9]+}}, 51(1) +; CHECK: lha {{[0-9]+}}, 64(1) +; CHECK: lwz {{[0-9]+}}, 72(1) +; CHECK: lwz {{[0-9]+}}, 80(1) +; CHECK: lwz {{[0-9]+}}, 88(1) +; CHECK: lwz {{[0-9]+}}, 96(1) +} + +define i32 @caller2() nounwind { +entry: + %p1 = alloca %struct.t1, align 1 + %p2 = alloca %struct.t2, align 1 + %p3 = alloca %struct.t3, align 1 + %p4 = alloca %struct.t4, align 1 + %p5 = alloca %struct.t5, align 1 + %p6 = alloca %struct.t6, align 1 + %p7 = alloca %struct.t7, align 1 + %0 = bitcast %struct.t1* %p1 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* %0, i8* getelementptr inbounds (%struct.t1* @caller2.p1, i32 0, i32 0), i64 1, i32 1, i1 false) + %1 = bitcast %struct.t2* %p2 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* %1, i8* bitcast ({ i16 }* @caller2.p2 to i8*), i64 2, i32 1, i1 false) + %2 = bitcast %struct.t3* %p3 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* %2, i8* bitcast (%struct.t3* @caller2.p3 to i8*), i64 3, i32 1, i1 false) + %3 = bitcast %struct.t4* %p4 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* %3, i8* bitcast ({ i32 }* @caller2.p4 to i8*), i64 4, i32 1, i1 false) + %4 = bitcast %struct.t5* %p5 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* %4, i8* bitcast (%struct.t5* @caller2.p5 to i8*), i64 5, i32 1, i1 false) + %5 = bitcast %struct.t6* %p6 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* %5, i8* bitcast (%struct.t6* @caller2.p6 to i8*), i64 6, i32 1, i1 false) + %6 = bitcast %struct.t7* %p7 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* %6, i8* bitcast (%struct.t7* @caller2.p7 to i8*), i64 7, i32 1, i1 false) + %call = call i32 @callee2(%struct.t1* byval %p1, %struct.t2* byval %p2, %struct.t3* byval %p3, %struct.t4* byval %p4, %struct.t5* byval %p5, %struct.t6* byval %p6, %struct.t7* byval %p7) + ret i32 %call + +; CHECK: stb {{[0-9]+}}, 71(1) +; CHECK: sth {{[0-9]+}}, 69(1) +; CHECK: stb {{[0-9]+}}, 87(1) +; CHECK: stw {{[0-9]+}}, 83(1) +; CHECK: sth {{[0-9]+}}, 94(1) +; CHECK: stw {{[0-9]+}}, 90(1) +; CHECK: stb {{[0-9]+}}, 103(1) +; CHECK: sth {{[0-9]+}}, 101(1) +; CHECK: stw {{[0-9]+}}, 97(1) +; CHECK: ld 9, 96(1) +; CHECK: ld 8, 88(1) +; CHECK: ld 7, 80(1) +; CHECK: lwz 6, 152(31) +; CHECK: ld 5, 64(1) +; CHECK: lhz 4, 168(31) +; CHECK: lbz 3, 176(31) +} + +define internal i32 @callee2(%struct.t1* byval %v1, %struct.t2* byval %v2, %struct.t3* byval %v3, %struct.t4* byval %v4, %struct.t5* byval %v5, %struct.t6* byval %v6, %struct.t7* byval %v7) nounwind { +entry: + %a = getelementptr inbounds %struct.t1* %v1, i32 0, i32 0 + %0 = load i8* %a, align 1 + %conv = zext i8 %0 to i32 + %a1 = getelementptr inbounds %struct.t2* %v2, i32 0, i32 0 + %1 = load i16* %a1, align 1 + %conv2 = sext i16 %1 to i32 + %add = add nsw i32 %conv, %conv2 + %a3 = getelementptr inbounds %struct.t3* %v3, i32 0, i32 0 + %2 = load i16* %a3, align 1 + %conv4 = sext i16 %2 to i32 + %add5 = add nsw i32 %add, %conv4 + %a6 = getelementptr inbounds %struct.t4* %v4, i32 0, i32 0 + %3 = load i32* %a6, align 1 + %add7 = add nsw i32 %add5, %3 + %a8 = getelementptr inbounds %struct.t5* %v5, i32 0, i32 0 + %4 = load i32* %a8, align 1 + %add9 = add nsw i32 %add7, %4 + %a10 = getelementptr inbounds %struct.t6* %v6, i32 0, i32 0 + %5 = load i32* %a10, align 1 + %add11 = add nsw i32 %add9, %5 + %a12 = getelementptr inbounds %struct.t7* %v7, i32 0, i32 0 + %6 = load i32* %a12, align 1 + %add13 = add nsw i32 %add11, %6 + ret i32 %add13 + +; CHECK: sldi 9, 9, 8 +; CHECK: sldi 8, 8, 16 +; CHECK: sldi 7, 7, 24 +; CHECK: sldi 5, 5, 40 +; CHECK: stw 6, 72(1) +; CHECK: sth 4, 58(1) +; CHECK: stb 3, 51(1) +; CHECK: std 9, 96(1) +; CHECK: std 8, 88(1) +; CHECK: std 7, 80(1) +; CHECK: std 5, 64(1) +; CHECK: lha {{[0-9]+}}, 58(1) +; CHECK: lbz {{[0-9]+}}, 51(1) +; CHECK: lha {{[0-9]+}}, 64(1) +; CHECK: lwz {{[0-9]+}}, 72(1) +; CHECK: lwz {{[0-9]+}}, 80(1) +; CHECK: lwz {{[0-9]+}}, 88(1) +; CHECK: lwz {{[0-9]+}}, 96(1) +}