diff --git a/llvm/lib/Target/PowerPC/PPCISelLowering.cpp b/llvm/lib/Target/PowerPC/PPCISelLowering.cpp --- a/llvm/lib/Target/PowerPC/PPCISelLowering.cpp +++ b/llvm/lib/Target/PowerPC/PPCISelLowering.cpp @@ -6861,9 +6861,14 @@ const unsigned ByValSize = ArgFlags.getByValSize(); - // An empty aggregate parameter takes up no storage and no registers. - if (ByValSize == 0) + // An empty aggregate parameter takes up no storage and no registers, + // but needs a MemLoc for a stack slot for the formal arguments side. + if (ByValSize == 0) { + State.addLoc(CCValAssign::getMem(ValNo, MVT::INVALID_SIMPLE_VALUE_TYPE, + State.getNextStackOffset(), RegVT, + LocInfo)); return false; + } if (ByValSize <= PtrByteSize) { State.AllocateStack(PtrByteSize, PtrByteSize); @@ -6978,6 +6983,24 @@ return DAG.getNode(ISD::TRUNCATE, dl, ValVT, ArgValue); } +static unsigned mapArgRegToOffsetAIX(unsigned Reg, const PPCFrameLowering *FL) { + const unsigned LASize = FL->getLinkageSize(); + + if (PPC::GPRCRegClass.contains(Reg)) { + assert(Reg >= PPC::R3 && Reg <= PPC::R10 && + "Reg must be a valid argument register!"); + return LASize + 4 * (Reg - PPC::R3); + } + + if (PPC::G8RCRegClass.contains(Reg)) { + assert(Reg >= PPC::X3 && Reg <= PPC::X10 && + "Reg must be a valid argument register!"); + return LASize + 8 * (Reg - PPC::X3); + } + + llvm_unreachable("Only general purpose registers expected."); +} + SDValue PPCTargetLowering::LowerFormalArguments_AIX( SDValue Chain, CallingConv::ID CallConv, bool isVarArg, const SmallVectorImpl &Ins, const SDLoc &dl, @@ -7015,12 +7038,12 @@ CCInfo.AllocateStack(LinkageSize, PtrByteSize); CCInfo.AnalyzeFormalArguments(Ins, CC_AIX); + SmallVector MemOps; + for (CCValAssign &VA : ArgLocs) { EVT ValVT = VA.getValVT(); MVT LocVT = VA.getLocVT(); ISD::ArgFlagsTy Flags = Ins[VA.getValNo()].Flags; - assert(!Flags.isByVal() && - "Passing structure by value is unimplemented for formal arguments."); assert((VA.isRegLoc() || VA.isMemLoc()) && "Unexpected location for function call argument."); @@ -7033,6 +7056,59 @@ if (VA.isMemLoc() && VA.needsCustom()) continue; + if (Flags.isByVal() && VA.isMemLoc()) { + if (Flags.getByValSize() != 0) + report_fatal_error( + "ByVal arguments passed on stack not implemented yet"); + + const int FI = MF.getFrameInfo().CreateFixedObject( + PtrByteSize, VA.getLocMemOffset(), /* IsImmutable */ false, + /* IsAliased */ true); + SDValue FIN = DAG.getFrameIndex(FI, PtrVT); + InVals.push_back(FIN); + + continue; + } + + if (Flags.isByVal()) { + assert(VA.isRegLoc() && "MemLocs should already be handled."); + + const unsigned ByValSize = Flags.getByValSize(); + if (ByValSize > PtrByteSize) + report_fatal_error("Formal arguments greater then register size not " + "implemented yet."); + + const MCPhysReg ArgReg = VA.getLocReg(); + const PPCFrameLowering *FL = Subtarget.getFrameLowering(); + const unsigned Offset = mapArgRegToOffsetAIX(ArgReg, FL); + + const unsigned StackSize = alignTo(ByValSize, PtrByteSize); + const int FI = MF.getFrameInfo().CreateFixedObject( + StackSize, Offset, /* IsImmutable */ false, /* IsAliased */ true); + SDValue FIN = DAG.getFrameIndex(FI, PtrVT); + + InVals.push_back(FIN); + + const unsigned VReg = MF.addLiveIn(ArgReg, IsPPC64 ? &PPC::G8RCRegClass + : &PPC::GPRCRegClass); + + // Since the callers side has left justified the aggregate in the + // register, we can simply store the entire register into the stack + // slot. + // The store to the fixedstack object is needed becuase accessing a + // field of the ByVal will use a gep and load. Ideally we will optimize + // to extracting the value from the register directly, and elide the + // stores when the arguments address is not taken, but that will need to + // be future work. + SDValue CopyFrom = DAG.getCopyFromReg(Chain, dl, VReg, LocVT); + SDValue Store = + DAG.getStore(CopyFrom.getValue(1), dl, CopyFrom, FIN, + MachinePointerInfo::getFixedStack(MF, FI, 0)); + + MemOps.push_back(Store); + continue; + } + if (VA.isRegLoc()) { MVT::SimpleValueType SVT = ValVT.getSimpleVT().SimpleTy; unsigned VReg = @@ -7080,6 +7156,9 @@ PPCFunctionInfo *FuncInfo = MF.getInfo(); FuncInfo->setMinReservedArea(CallerReservedArea); + if (!MemOps.empty()) + Chain = DAG.getNode(ISD::TokenFactor, dl, MVT::Other, MemOps); + return Chain; } @@ -7156,8 +7235,13 @@ if (Flags.isByVal()) { const unsigned ByValSize = Flags.getByValSize(); + + // Nothing to do for zero-sized ByVals on the caller side. + if (!ByValSize) + continue; + assert( - VA.isRegLoc() && ByValSize > 0 && ByValSize <= PtrByteSize && + VA.isRegLoc() && ByValSize <= PtrByteSize && "Pass-by-value arguments are only supported in a single register."); // Loads must be a power-of-2 size and cannot be larger than the diff --git a/llvm/test/CodeGen/PowerPC/aix-cc-byval.ll b/llvm/test/CodeGen/PowerPC/aix-cc-byval.ll --- a/llvm/test/CodeGen/PowerPC/aix-cc-byval.ll +++ b/llvm/test/CodeGen/PowerPC/aix-cc-byval.ll @@ -22,11 +22,10 @@ define void @call_test_byval_1Byte() { entry: %s0 = alloca %struct.S0, align 8 - call void @test_byval_1Byte(%struct.S0* byval(%struct.S0) align 1 %s0, %struct.S1* byval(%struct.S1) align 1 @gS1) + %call = call zeroext i8 @test_byval_1Byte(%struct.S0* byval(%struct.S0) align 1 %s0, %struct.S1* byval(%struct.S1) align 1 @gS1) ret void } -declare void @test_byval_1Byte(%struct.S0* byval(%struct.S0) align 1, %struct.S1* byval(%struct.S1) align 1) ; CHECK-LABEL: name: call_test_byval_1Byte{{.*}} @@ -63,18 +62,59 @@ ; ASM64-NEXT: nop ; ASM64-NEXT: addi 1, 1, 128 + +define zeroext i8 @test_byval_1Byte(%struct.S0* byval(%struct.S0) align 1 %s0, %struct.S1* byval(%struct.S1) align 1 %s) { +entry: + %arrayidx = getelementptr inbounds %struct.S1, %struct.S1* %s, i32 0, i32 0, i32 0 + %0 = load i8, i8* %arrayidx, align 1 + ret i8 %0 +} + +; CHECK-LABEL: name: test_byval_1Byte + +; 32BIT: fixedStack: +; 32BIT-NEXT: - { id: 0, type: default, offset: 24, size: 4, alignment: 8, stack-id: default, +; 32BIT-NEXT: isImmutable: false, isAliased: true, callee-saved-register: '', callee-saved-restored: true, +; 32BIT: - { id: 1, type: default, offset: 24, size: 4, alignment: 8, stack-id: default, +; 32BIT-NEXT: isImmutable: false, isAliased: true, callee-saved-register: '', callee-saved-restored: true, + +; 32BIT: bb.0.entry: +; 32BIT-NEXT: liveins: $r3 +; 32BIT: STW killed renamable $r3, 0, %fixed-stack.0 :: (store 4 into %fixed-stack.0, align 8) +; 32BIT-NEXT: renamable $r3 = LBZ 0, %fixed-stack.0 :: (dereferenceable load 1 +; 32BIT-NEXT: BLR + +; 64BIT: fixedStack: +; 64BIT-NEXT: - { id: 0, type: default, offset: 48, size: 8, alignment: 16, stack-id: default, +; 64BIT-NEXT: isImmutable: false, isAliased: true, callee-saved-register: '', callee-saved-restored: true, +; 64BIT: - { id: 1, type: default, offset: 48, size: 8, alignment: 16, stack-id: default, +; 64BIT-NEXT: isImmutable: false, isAliased: true, callee-saved-register: '', callee-saved-restored: true, + +; 64BIT: bb.0.entry: +; 64BIT-NEXT: liveins: $x3 +; 64BIT: STD killed renamable $x3, 0, %fixed-stack.0 :: (store 8 into %fixed-stack.0, align 16) +; 64BIT-NEXT: renamable $x3 = LBZ8 0, %fixed-stack.0 :: (dereferenceable load 1 + +; CHECKASM-LABEL: .test_byval_1Byte: + +; ASM32: stw 3, 24(1) +; ASM32-NEXT: lbz 3, 24(1) +; ASM32-NEXT: blr + +; ASM64: std 3, 48(1) +; ASM64-NEXT: lbz 3, 48(1) +; ASM64-NEXT: blr + %struct.S2 = type { [2 x i8] } @gS2 = external global %struct.S2, align 1 define void @call_test_byval_2Byte() { entry: - call void @test_byval_2Byte(%struct.S2* byval(%struct.S2) align 1 @gS2) + %call = call zeroext i8 @test_byval_2Byte(%struct.S2* byval(%struct.S2) align 1 @gS2) ret void } -declare void @test_byval_2Byte(%struct.S2* byval(%struct.S2) align 1) - ; CHECK-LABEL: name: call_test_byval_2Byte{{.*}} ; 32BIT: ADJCALLSTACKDOWN 56, 0, implicit-def dead $r1, implicit $r1 @@ -110,18 +150,51 @@ ; ASM64-NEXT: nop ; ASM64-NEXT: addi 1, 1, 112 -%struct.S3 = type <{ i8, i16 }> +define zeroext i8 @test_byval_2Byte(%struct.S2* byval(%struct.S2) align 1 %s) { +entry: + %arrayidx = getelementptr inbounds %struct.S2, %struct.S2* %s, i32 0, i32 0, i32 1 + %0 = load i8, i8* %arrayidx, align 1 + ret i8 %0 +} + +; CHECK-LABEL: name: test_byval_2Byte +; 32BIT: fixedStack: +; 32BIT-NEXT: - { id: 0, type: default, offset: 24, size: 4, alignment: 8, stack-id: default, + +; 32BIT: bb.0.entry: +; 32BIT-NEXT: liveins: $r3 +; 32BIT: STW killed renamable $r3, 0, %fixed-stack.0 :: (store 4 into %fixed-stack.0, align 8) +; 32BIT-NEXT: renamable $r3 = LBZ 1, %fixed-stack.0 :: (dereferenceable load 1 + +; 64BIT: fixedStack: +; 64BIT-NEXT: - { id: 0, type: default, offset: 48, size: 8, alignment: 16, stack-id: default, + +; 64BIT: bb.0.entry: +; 64BIT-NEXT: liveins: $x3 +; 64BIT: STD killed renamable $x3, 0, %fixed-stack.0 :: (store 8 into %fixed-stack.0, align 16) +; 64BIT-NEXT: renamable $x3 = LBZ8 1, %fixed-stack.0 :: (dereferenceable load 1 + +; CHECKASM-LABEL: .test_byval_2Byte: + +; ASM32: stw 3, 24(1) +; ASM32-NEXT: lbz 3, 25(1) +; ASM32-NEXT: blr + +; ASM64: std 3, 48(1) +; ASM64-NEXT: lbz 3, 49(1) +; ASM64-NEXT: blr + + +%struct.S3 = type <{ i8, i16 }> @gS3 = external global %struct.S3, align 1 define void @call_test_byval_3Byte() { entry: - call void @test_byval_3Byte(%struct.S3* byval(%struct.S3) align 1 @gS3) + %call = call zeroext i16 @test_byval_3Byte(%struct.S3* byval(%struct.S3) align 1 @gS3) ret void } -declare void @test_byval_3Byte(%struct.S3* byval(%struct.S3) align 1) - ; CHECK-LABEL: name: call_test_byval_3Byte{{.*}} ; The DAG block permits some invalid inputs for the benefit of allowing more valid orderings. @@ -166,6 +239,44 @@ ; ASM64-NEXT: bl .test_byval_3Byte ; ASM64-NEXT: nop + +define zeroext i16 @test_byval_3Byte(%struct.S3* byval(%struct.S3) align 1 %s) { +entry: + %gep = getelementptr inbounds %struct.S3, %struct.S3* %s, i32 0, i32 1 + %0 = load i16, i16* %gep, align 1 + ret i16 %0 +} + +; CHECK-LABEL: name: test_byval_3Byte + +; 32BIT: fixedStack: +; 32BIT-NEXT: - { id: 0, type: default, offset: 24, size: 4, alignment: 8, stack-id: default, + +; 32BIT: bb.0.entry: +; 32BIT-NEXT: liveins: $r3 +; 32BIT: STW killed renamable $r3, 0, %fixed-stack.0 :: (store 4 into %fixed-stack.0, align 8) +; 32BIT-NEXT: renamable $r3 = LHZ 1, %fixed-stack.0 :: (dereferenceable load 2 + +; 64BIT: fixedStack: +; 64BIT-NEXT: - { id: 0, type: default, offset: 48, size: 8, alignment: 16, stack-id: default, + +; 64BIT: bb.0.entry: +; 64BIT-NEXT: liveins: $x3 +; 64BIT: STD killed renamable $x3, 0, %fixed-stack.0 :: (store 8 into %fixed-stack.0, align 16) +; 64BIT-NEXT: renamable $x3 = LHZ8 1, %fixed-stack.0 :: (dereferenceable load 2 + + +; CHECKASM-LABEL: .test_byval_3Byte: + +; ASM32: stw 3, 24(1) +; ASM32-NEXT: lhz 3, 25(1) +; ASM32-NEXT: blr + +; ASM64: std 3, 48(1) +; ASM64-NEXT: lhz 3, 49(1) +; ASM64-NEXT: blr + + %struct.S4 = type { [4 x i8] } %struct.S4A = type { i32 } @@ -175,12 +286,10 @@ entry: %s0 = alloca %struct.S0, align 8 %s4a = alloca %struct.S4A, align 4 - call void @test_byval_4Byte(%struct.S4* byval(%struct.S4) align 1 @gS4, %struct.S0* byval(%struct.S0) align 1 %s0, %struct.S4A* byval(%struct.S4A) align 4 %s4a) + %call = call signext i32 @test_byval_4Byte(%struct.S4* byval(%struct.S4) align 1 @gS4, %struct.S0* byval(%struct.S0) align 1 %s0, %struct.S4A* byval(%struct.S4A) align 4 %s4a) ret void } -declare void @test_byval_4Byte(%struct.S4* byval(%struct.S4) align 1, %struct.S0* byval(%struct.S0) align 1, %struct.S4A* byval(%struct.S4A) align 4) - ; CHECK-LABEL: name: call_test_byval_4Byte{{.*}} ; 32BIT: ADJCALLSTACKDOWN 56, 0, implicit-def dead $r1, implicit $r1 @@ -219,3 +328,66 @@ ; ASM64-NEXT: nop ; ASM64-NEXT: addi 1, 1, 128 + +define signext i32 @test_byval_4Byte(%struct.S4* byval(%struct.S4) align 1 %s, %struct.S0* byval(%struct.S0) align 1, %struct.S4A* byval(%struct.S4A) align 4 %s4a) { +entry: + %arrayidx = getelementptr inbounds %struct.S4, %struct.S4* %s, i32 0, i32 0, i32 3 + %gep = getelementptr inbounds %struct.S4A, %struct.S4A* %s4a, i32 0, i32 0 + %1 = load i8, i8* %arrayidx, align 1 + %2 = load i32, i32* %gep, align 4 + %conv = zext i8 %1 to i32 + %add = add nsw i32 %2, %conv + ret i32 %add +} + +; CHECK-LABEL: name: test_byval_4Byte + +; 32BIT: fixedStack: +; 32BIT-NEXT: - { id: 0, type: default, offset: 28, size: 4, alignment: 4, stack-id: default, +; 32BIT-NEXT: isImmutable: false, isAliased: true, callee-saved-register: '', callee-saved-restored: true, +; 32BIT: - { id: 1, type: default, offset: 28, size: 4, alignment: 4, stack-id: default, +; 32BIT-NEXT: isImmutable: false, isAliased: true, callee-saved-register: '', callee-saved-restored: true, +; 32BIT: - { id: 2, type: default, offset: 24, size: 4, alignment: 8, stack-id: default, +; 32BIT-NEXT: isImmutable: false, isAliased: true, callee-saved-register: '', callee-saved-restored: true, + +; 32BIT: bb.0.entry: +; 32BIT-NEXT: liveins: $r3 +; 32BIT: STW renamable $r3, 0, %fixed-stack.2 :: (store 4 into %fixed-stack.2, align 8) +; 32BIT-DAG: STW killed renamable $r4, 0, %fixed-stack.0 :: (store 4 into %fixed-stack.0) +; 32BIT-DAG: renamable $r[[SCRATCH:[0-9]+]] = RLWINM killed renamable $r3, 0, 24, 31 +; 32BIT-DAG: renamable $r3 = nsw ADD4 renamable $r4, killed renamable $r[[SCRATCH]] +; 32BIT: BLR + +; 64BIT: fixedStack: +; 64BIT-NEXT: - { id: 0, type: default, offset: 56, size: 8, alignment: 8, stack-id: default, +; 64BIT-NEXT: isImmutable: false, isAliased: true, callee-saved-register: '', callee-saved-restored: true, +; 64BIT: - { id: 1, type: default, offset: 56, size: 8, alignment: 8, stack-id: default, +; 64BIT-NEXT: isImmutable: false, isAliased: true, callee-saved-register: '', callee-saved-restored: true, +; 64BIT: - { id: 2, type: default, offset: 48, size: 8, alignment: 16, stack-id: default, +; 64BIT-NEXT: isImmutable: false, isAliased: true, callee-saved-register: '', callee-saved-restored: true, + +; 64BIT: bb.0.entry: +; 64BIT-NEXT: liveins: $x3 +; 64BIT: STD killed renamable $x3, 0, %fixed-stack.2 :: (store 8 into %fixed-stack.2, align 16) +; 64BIT-NEXT: STD killed renamable $x4, 0, %fixed-stack.0 :: (store 8 into %fixed-stack.0) +; 64BIT-DAG: renamable $r[[SCRATCH1:[0-9]+]] = LBZ 3, %fixed-stack.2 :: (dereferenceable load 1 +; 64BIT-DAG: renamable $r[[SCRATCH2:[0-9]+]] = LWZ 0, %fixed-stack.0 :: (dereferenceable load 4 +; 64BIT-NEXT: renamable $r[[SCRATCH3:[0-9]+]] = nsw ADD4 killed renamable $r[[SCRATCH2]], killed renamable $r[[SCRATCH1]] +; 64BIT-NEXT: renamable $x3 = EXTSW_32_64 killed renamable $r[[SCRATCH3]] +; 64BIT-NEXT: BLR8 + +; CHECKASM-LABEL: .test_byval_4Byte: + +; ASM32: stw 3, 24(1) +; ASM32-DAG: stw 4, 28(1) +; ASM32-DAG: clrlwi [[SCRATCH:[0-9]+]], 3, 24 +; ASM32-DAG: add 3, 4, [[SCRATCH]] +; ASM32-NEXT: blr + +; ASM64: std 3, 48(1) +; ASM64-NEXT: std 4, 56(1) +; ASM64-DAG: lbz [[SCRATCH1:[0-9]+]], 51(1) +; ASM64-DAG: lwz [[SCRATCH2:[0-9]+]], 56(1) +; ASM64-NEXT: add [[SCRATCH3:[0-9]+]], [[SCRATCH2]], [[SCRATCH1]] +; ASM64-NEXT: extsw 3, [[SCRATCH3]] +; ASM64-NEXT: blr diff --git a/llvm/test/CodeGen/PowerPC/aix64-cc-byval.ll b/llvm/test/CodeGen/PowerPC/aix64-cc-byval.ll --- a/llvm/test/CodeGen/PowerPC/aix64-cc-byval.ll +++ b/llvm/test/CodeGen/PowerPC/aix64-cc-byval.ll @@ -12,12 +12,10 @@ define void @call_test_byval_5Byte() { entry: - call void @test_byval_5Byte(%struct.S5* byval(%struct.S5) align 1 @gS5) + %call = call zeroext i8 @test_byval_5Byte(%struct.S5* byval(%struct.S5) align 1 @gS5) ret void } -declare void @test_byval_5Byte(%struct.S5* byval(%struct.S5) align 1) - ; CHECK-LABEL: name: call_test_byval_5Byte{{.*}} ; ASM-LABEL: .call_test_byval_5Byte: @@ -42,18 +40,40 @@ ; ASM-NEXT: bl .test_byval_5Byte ; ASM-NEXT: nop + +define zeroext i8 @test_byval_5Byte(%struct.S5* byval(%struct.S5) align 1 %s) { +entry: + %arrayidx = getelementptr inbounds %struct.S5, %struct.S5* %s, i32 0, i32 0, i32 4 + %0 = load i8, i8* %arrayidx, align 1 + ret i8 %0 +} + +; CHECK-LABEL: name: test_byval_5Byte + +; CHECK: fixedStack: +; CHECK-NEXT: - { id: 0, type: default, offset: 48, size: 8, alignment: 16, +; CHECK: bb.0.entry: +; CHECK-NEXT: liveins: $x3 +; CHECK: STD killed renamable $x3, 0, %fixed-stack.0 :: (store 8 into %fixed-stack.0, align 16) +; CHECK-NEXT: renamable $x3 = LBZ8 4, %fixed-stack.0 :: (dereferenceable load 1 + +; CHECKASM-LABEL: .test_byval_5Byte: + +; ASM: std 3, 48(1) +; ASM-NEXT: lbz 3, 52(1) +; ASM-NEXT: blr + + %struct.S6 = type { [6 x i8] } @gS6 = external global %struct.S6, align 1 define void @call_test_byval_6Byte() { entry: - call void @test_byval_6Byte(%struct.S6* byval(%struct.S6) align 1 @gS6) + %call = call zeroext i8 @test_byval_6Byte(%struct.S6* byval(%struct.S6) align 1 @gS6) ret void } -declare void @test_byval_6Byte(%struct.S6* byval(%struct.S6) align 1) - ; CHECK-LABEL: name: call_test_byval_6Byte{{.*}} ; ASM-LABEL: .call_test_byval_6Byte: @@ -78,18 +98,39 @@ ; ASM-NEXT: bl .test_byval_6Byte ; ASM-NEXT: nop + +define zeroext i8 @test_byval_6Byte(%struct.S6* byval(%struct.S6) align 1 %s) { +entry: + %arrayidx = getelementptr inbounds %struct.S6, %struct.S6* %s, i32 0, i32 0, i32 5 + %0 = load i8, i8* %arrayidx, align 1 + ret i8 %0 +} + +; CHECK-LABEL: name: test_byval_6Byte + +; CHECK: fixedStack: +; CHECK-NEXT: - { id: 0, type: default, offset: 48, size: 8, alignment: 16, +; CHECK: bb.0.entry: +; CHECK-NEXT: liveins: $x3 +; CHECK: STD killed renamable $x3, 0, %fixed-stack.0 :: (store 8 into %fixed-stack.0, align 16) +; CHECK-NEXT: renamable $x3 = LBZ8 5, %fixed-stack.0 :: (dereferenceable load 1 + +; CHECKASM-LABEL: .test_byval_6Byte: + +; ASM: std 3, 48(1) +; ASM-NEXT: lbz 3, 53(1) +; ASM-NEXT: blr + %struct.S7 = type { [7 x i8] } @gS7 = external global %struct.S7, align 1 define void @call_test_byval_7Byte() { entry: - call void @test_byval_7Byte(%struct.S7* byval(%struct.S7) align 1 @gS7) + %call = call zeroext i8 @test_byval_7Byte(%struct.S7* byval(%struct.S7) align 1 @gS7) ret void } -declare void @test_byval_7Byte(%struct.S7* byval(%struct.S7) align 1) - ; CHECK-LABEL: name: call_test_byval_7Byte{{.*}} ; ASM-LABEL: .call_test_byval_7Byte: @@ -118,18 +159,40 @@ ; ASM-NEXT: bl .test_byval_7Byte ; ASM-NEXT: nop + +define zeroext i8 @test_byval_7Byte(%struct.S7* byval(%struct.S7) align 1 %s) { +entry: + %arrayidx = getelementptr inbounds %struct.S7, %struct.S7* %s, i32 0, i32 0, i32 6 + %0 = load i8, i8* %arrayidx, align 1 + ret i8 %0 +} + +; CHECK-LABEL: name: test_byval_7Byte + +; CHECK: fixedStack: +; CHECK-NEXT: - { id: 0, type: default, offset: 48, size: 8, alignment: 16, +; CHECK: bb.0.entry: +; CHECK-NEXT: liveins: $x3 +; CHECK: STD killed renamable $x3, 0, %fixed-stack.0 :: (store 8 into %fixed-stack.0, align 16) +; CHECK-NEXT: renamable $x3 = LBZ8 6, %fixed-stack.0 :: (dereferenceable load 1 + +; CHECKASM-LABEL: .test_byval_7Byte: + +; ASM: std 3, 48(1) +; ASM-NEXT: lbz 3, 54(1) +; ASM-NEXT: blr + + %struct.S8 = type { [8 x i8] } @gS8 = external global %struct.S8, align 1 define void @call_test_byval_8Byte() { entry: - call void @test_byval_8Byte(%struct.S8* byval(%struct.S8) align 1 @gS8) + %call = call zeroext i8 @test_byval_8Byte(%struct.S8* byval(%struct.S8) align 1 @gS8) ret void } -declare void @test_byval_8Byte(%struct.S8* byval(%struct.S8) align 1) - ; CHECK-LABEL: name: call_test_byval_8Byte{{.*}} ; ASM-LABEL: .call_test_byval_8Byte: @@ -145,3 +208,29 @@ ; ASM-NEXT: ld 3, 0([[REGADDR]]) ; ASM-NEXT: bl .test_byval_8Byte ; ASM-NEXT: nop + + +define zeroext i8 @test_byval_8Byte(%struct.S8* byval(%struct.S8) align 1 %s) { +entry: + %arrayidx = getelementptr inbounds %struct.S8, %struct.S8* %s, i32 0, i32 0, i32 7 + %0 = load i8, i8* %arrayidx, align 1 + ret i8 %0 +} + +; CHECK-LABEL: name: test_byval_8Byte + +; CHECK: fixedStack: +; CHECK-NEXT: - { id: 0, type: default, offset: 48, size: 8, alignment: 16, +; CHECK: bb.0.entry: +; CHECK-NEXT: liveins: $x3 +; CHECK: renamable $x[[SCRATCH:[0-9]+]] = COPY $x3 +; CHECK-DAG: renamable $x3 = RLDICL $x3, 0, 56 +; CHECK-DAG: STD killed renamable $x[[SCRATCH]], 0, %fixed-stack.0 :: (store 8 into %fixed-stack.0, align 16) + + +; CHECKASM-LABEL: .test_byval_8Byte: + +; ASM: mr [[SCRATCH:[0-9]+]], 3 +; ASM-DAG: clrldi 3, 3, 56 +; ASM-DAG: std [[SCRATCH]], 48(1) +; ASM-NEXT: blr