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 @@ -6978,6 +6978,30 @@ return DAG.getNode(ISD::TRUNCATE, dl, ValVT, ArgValue); } +static unsigned roundToMultiple(unsigned Size, unsigned Multiple) { + // Round up. + const unsigned Count = (Size + Multiple - 1) / Multiple; + return Count * Multiple; +} + +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 +7039,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 +7057,47 @@ if (VA.isMemLoc() && VA.needsCustom()) continue; + if (Flags.isByVal()) { + if (!VA.isRegLoc()) + report_fatal_error( + "ByVal arguments passed on stack not implemented yet."); + + // First create the FrameIndex for the object, and add it to the In vals. + const unsigned TrueSize = Flags.getByValSize(); + const unsigned StackSize = roundToMultiple(TrueSize, PtrByteSize); + + if (TrueSize > 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 int FI = MF.getFrameInfo().CreateFixedObject( + StackSize, Offset, /* IsImmutable */ false, /* IsAliased */ true); + SDValue FIN = DAG.getFrameIndex(FI, PtrVT); + + InVals.push_back(FIN); + + // For zero sized ByVals no more is needed. + if (TrueSize == 0) + continue; + + 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. + 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 +7145,9 @@ PPCFunctionInfo *FuncInfo = MF.getInfo(); FuncInfo->setMinReservedArea(CallerReservedArea); + if (!MemOps.empty()) + Chain = DAG.getNode(ISD::TokenFactor, dl, MVT::Other, MemOps); + return Chain; } 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 @@ -19,12 +19,10 @@ define void @call_test_byval_1Byte() { entry: - call void @test_byval_1Byte(%struct.S1* byval(%struct.S1) align 1 @gS1) + %call = call zeroext i8 @test_byval_1Byte(%struct.S1* byval(%struct.S1) align 1 @gS1) ret void } -declare void @test_byval_1Byte(%struct.S1* byval(%struct.S1) align 1) - ; CHECK-LABEL: name: call_test_byval_1Byte{{.*}} ; 32BIT: ADJCALLSTACKDOWN 56, 0, implicit-def dead $r1, implicit $r1 @@ -60,18 +58,51 @@ ; ASM64-NEXT: nop ; ASM64-NEXT: addi 1, 1, 112 +define zeroext i8 @test_byval_1Byte(%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, + +; 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 + +; 64BIT: fixedStack: +; 64BIT-NEXT: - { id: 0, type: default, offset: 48, size: 8, alignment: 16, + +; 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 @@ -107,18 +138,52 @@ ; ASM64-NEXT: nop ; ASM64-NEXT: addi 1, 1, 112 +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, + +; 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, + +; 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 { [3 x i8] } @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 i8 @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. @@ -163,18 +228,54 @@ ; ASM64-NEXT: bl .test_byval_3Byte ; ASM64-NEXT: nop + +define zeroext i8 @test_byval_3Byte(%struct.S3* byval(%struct.S3) align 1 %s) { +entry: + %arrayidx = getelementptr inbounds %struct.S3, %struct.S3* %s, i32 0, i32 0, i32 2 + %0 = load i8, i8* %arrayidx, align 1 + ret i8 %0 +} + +; CHECK-LABEL: name: test_byval_3Byte + +; 32BIT: fixedStack: +; 32BIT-NEXT: - { id: 0, type: default, offset: 24, size: 4, alignment: 8, + +; 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 2, %fixed-stack.0 :: (dereferenceable load 1 + +; 64BIT: fixedStack: +; 64BIT-NEXT: - { id: 0, type: default, offset: 48, size: 8, alignment: 16, + +; 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 2, %fixed-stack.0 :: (dereferenceable load 1 + + +; CHECKASM-LABEL: .test_byval_3Byte: + +; ASM32: stw 3, 24(1) +; ASM32-NEXT: lbz 3, 26(1) +; ASM32-NEXT: blr + +; ASM64: std 3, 48(1) +; ASM64-NEXT: lbz 3, 50(1) +; ASM64-NEXT: blr + + %struct.S4 = type { [4 x i8] } @gS4 = external global %struct.S4, align 1 define void @call_test_byval_4Byte() { entry: - call void @test_byval_4Byte(%struct.S4* byval(%struct.S4) align 1 @gS4) + %call = call zeroext i8 @test_byval_4Byte(%struct.S4* byval(%struct.S4) align 1 @gS4) ret void } -declare void @test_byval_4Byte(%struct.S4* byval(%struct.S4) align 1) - ; CHECK-LABEL: name: call_test_byval_4Byte{{.*}} ; 32BIT: ADJCALLSTACKDOWN 56, 0, implicit-def dead $r1, implicit $r1 @@ -206,3 +307,41 @@ ; ASM64-NEXT: bl .test_byval_4Byte ; ASM64-NEXT: nop ; ASM64-NEXT: addi 1, 1, 112 + + +define zeroext i8 @test_byval_4Byte(%struct.S4* byval(%struct.S4) align 1 %s) { +entry: + %arrayidx = getelementptr inbounds %struct.S4, %struct.S4* %s, i32 0, i32 0, i32 3 + %0 = load i8, i8* %arrayidx, align 1 + ret i8 %0 +} + +; CHECK-LABEL: name: test_byval_4Byte + +; 32BIT: fixedStack: +; 32BIT-NEXT: - { id: 0, type: default, offset: 24, size: 4, alignment: 8, + +; 32BIT: bb.0.entry: +; 32BIT-NEXT: liveins: $r3 +; 32BIT-DAG: renamable $r[[SCRATCH:[0-9]+]] = COPY $r3 +; 32BIT-DAG: renamable $r3 = RLWINM $r3, 0, 24, 31 +; 32BIT-NEXT: STW killed renamable $r[[SCRATCH]], 0, %fixed-stack.0 :: (store 4 into %fixed-stack.0, align 8) + +; 64BIT: fixedStack: +; 64BIT-NEXT: - { id: 0, type: default, offset: 48, size: 8, alignment: 16, + +; 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 3, %fixed-stack.0 :: (dereferenceable load 1 + +; CHECKASM-LABEL: .test_byval_4Byte: + +; ASM32: mr [[SCRATCH:[0-9]+]], 3 +; ASM32-NEXT: clrlwi 3, 3, 24 +; ASM32-NEXT: stw [[SCRATCH]], 24(1) +; ASM32-NEXT: blr + +; ASM64: std 3, 48(1) +; ASM64-NEXT: lbz 3, 51(1) +; 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