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 @@ -6942,14 +6942,18 @@ return false; } - State.AllocateStack(alignTo(ByValSize, PtrByteSize), PtrByteSize); - - for (unsigned I = 0, E = ByValSize; I < E; I += PtrByteSize) { + const unsigned StackSize = alignTo(ByValSize, PtrByteSize); + unsigned Offset = State.AllocateStack(StackSize, PtrByteSize); + for (const unsigned E = Offset + StackSize; Offset < E; + Offset += PtrByteSize) { if (unsigned Reg = State.AllocateReg(IsPPC64 ? GPR_64 : GPR_32)) State.addLoc(CCValAssign::getReg(ValNo, ValVT, Reg, RegVT, LocInfo)); - else - report_fatal_error( - "Pass-by-value arguments are only supported in registers."); + else { + State.addLoc(CCValAssign::getMem(ValNo, MVT::INVALID_SIMPLE_VALUE_TYPE, + Offset, MVT::INVALID_SIMPLE_VALUE_TYPE, + LocInfo)); + break; + } } return false; } @@ -7376,12 +7380,13 @@ unsigned LoadOffset = 0; // Initialize registers, which are fully occupied by the by-val argument. - while (I != E && LoadOffset + PtrByteSize <= ByValSize) { + while (I != E && ArgLocs[I].isRegLoc() && + LoadOffset + PtrByteSize <= ByValSize) { SDValue Load = GetLoad(PtrVT, LoadOffset); MemOpChains.push_back(Load.getValue(1)); LoadOffset += PtrByteSize; const CCValAssign &ByValVA = ArgLocs[I++]; - assert(ByValVA.isRegLoc() && ByValVA.getValNo() == ValNo && + assert(ByValVA.getValNo() == ValNo && "Unexpected location for pass-by-value argument."); RegsToPass.push_back(std::make_pair(ByValVA.getLocReg(), Load)); } @@ -7389,15 +7394,32 @@ if (LoadOffset == ByValSize) continue; - const unsigned ResidueBytes = ByValSize % PtrByteSize; - assert(ResidueBytes != 0 && LoadOffset + PtrByteSize > ByValSize && - "Unexpected register residue for by-value argument."); + // There must be one more loc to handle the remainder. + assert(ArgLocs[I].getValNo() == ValNo && + "Expected additional location for by-value argument."); + + if (ArgLocs[I].isMemLoc()) { + assert(LoadOffset < ByValSize && "Unexpected memloc for by-val arg."); + const CCValAssign &ByValVA = ArgLocs[I++]; + ISD::ArgFlagsTy MemcpyFlags = Flags; + // Only memcpy the bytes that don't pass in register. + MemcpyFlags.setByValSize(ByValSize - LoadOffset); + Chain = CallSeqStart = createMemcpyOutsideCallSeq( + (LoadOffset != 0) ? DAG.getObjectPtrOffset(dl, Arg, LoadOffset) + : Arg, + DAG.getObjectPtrOffset(dl, StackPtr, ByValVA.getLocMemOffset()), + CallSeqStart, MemcpyFlags, DAG, dl); + continue; + } // Initialize the final register residue. // Any residue that occupies the final by-val arg register must be // left-justified on AIX. Loads must be a power-of-2 size and cannot be // larger than the ByValSize. For example: a 7 byte by-val arg requires 4, // 2 and 1 byte loads. + const unsigned ResidueBytes = ByValSize % PtrByteSize; + assert(ResidueBytes != 0 && LoadOffset + PtrByteSize > ByValSize && + "Unexpected register residue for by-value argument."); SDValue ResidueVal; for (unsigned Bytes = 0; Bytes != ResidueBytes;) { const unsigned N = PowerOf2Floor(ResidueBytes - Bytes); @@ -7427,8 +7449,6 @@ } const CCValAssign &ByValVA = ArgLocs[I++]; - assert(ByValVA.isRegLoc() && ByValVA.getValNo() == ValNo && - "Additional register location expected for by-value argument."); RegsToPass.push_back(std::make_pair(ByValVA.getLocReg(), ResidueVal)); continue; } diff --git a/llvm/test/CodeGen/PowerPC/aix-cc-byval-limitation1.ll b/llvm/test/CodeGen/PowerPC/aix-cc-byval-limitation1.ll --- a/llvm/test/CodeGen/PowerPC/aix-cc-byval-limitation1.ll +++ b/llvm/test/CodeGen/PowerPC/aix-cc-byval-limitation1.ll @@ -3,14 +3,9 @@ %struct.S = type { [65 x i8] } -define void @bar() { +define void @foo(%struct.S* byval(%struct.S) align 1 %s) { entry: - %s1 = alloca %struct.S, align 1 - %agg.tmp = alloca %struct.S, align 1 - call void @foo(%struct.S* byval(%struct.S) align 1 %agg.tmp) ret void } -declare void @foo(%struct.S* byval(%struct.S) align 1) - -; CHECK: LLVM ERROR: Pass-by-value arguments are only supported in registers. +; CHECK: LLVM ERROR: Passing ByVals split between registers and stack not yet implemented. diff --git a/llvm/test/CodeGen/PowerPC/aix-cc-byval-limitation2.ll b/llvm/test/CodeGen/PowerPC/aix-cc-byval-limitation2.ll --- a/llvm/test/CodeGen/PowerPC/aix-cc-byval-limitation2.ll +++ b/llvm/test/CodeGen/PowerPC/aix-cc-byval-limitation2.ll @@ -1,16 +1,11 @@ ; RUN: not --crash llc -mtriple powerpc-ibm-aix-xcoff < %s 2>&1 | FileCheck %s ; RUN: not --crash llc -mtriple powerpc64-ibm-aix-xcoff < %s 2>&1 | FileCheck %s -%struct.S = type { [1 x i8] } +%struct.S = type { i8 } -define void @bar() { +define void @foo(i32 %i1, i32 %i2, i32 %i3, i32 %i4, i32 %i5, i32 %i6, i32 %i7, i32 %i8, %struct.S* byval(%struct.S) align 1 %s) { entry: - %s1 = alloca %struct.S, align 1 - %agg.tmp = alloca %struct.S, align 1 - call void @foo(i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7, i32 8, %struct.S* byval(%struct.S) align 1 %agg.tmp) ret void } -declare void @foo(i32, i32, i32, i32, i32, i32, i32, i32, %struct.S* byval(%struct.S) align 1) - -; CHECK: LLVM ERROR: Pass-by-value arguments are only supported in registers. +; CHECK: LLVM ERROR: ByVal arguments passed on stack not implemented yet diff --git a/llvm/test/CodeGen/PowerPC/aix-cc-byval-mem.ll b/llvm/test/CodeGen/PowerPC/aix-cc-byval-mem.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/PowerPC/aix-cc-byval-mem.ll @@ -0,0 +1,166 @@ +; RUN: llc -verify-machineinstrs -mcpu=pwr4 -mattr=-altivec \ +; RUN: -mtriple powerpc-ibm-aix-xcoff < %s | \ +; RUN: FileCheck --check-prefixes=CHECK,32BIT %s + +; RUN: llc -verify-machineinstrs -mcpu=pwr4 -mattr=-altivec \ +; RUN: -mtriple powerpc64-ibm-aix-xcoff < %s | \ +; RUN: FileCheck --check-prefixes=CHECK,64BIT %s + +%struct_S1 = type { i8 } + +@gS1 = external global %struct_S1, align 1 + +define void @call_test_byval_mem1() { +entry: + call void @test_byval_mem1(i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7, i32 8, %struct_S1* byval(%struct_S1) align 1 @gS1) + ret void +} + +declare void @test_byval_mem1(i32, i32, i32, i32, i32, i32, i32, i32, %struct_S1* byval(%struct_S1) align 1) + +; CHECK-LABEL: .call_test_byval_mem1: + +; 32BIT: stwu 1, -64(1) +; 32BIT: lwz [[REG1:[0-9]+]], LC{{[0-9]+}}(2) +; 32BIT: lbz [[REG2:[0-9]+]], 0([[REG1]]) +; 32BIT: stb [[REG2]], 56(1) +; 32BIT: bl .test_byval_mem1 +; 32BIT: addi 1, 1, 64 + +; 64BIT: stdu 1, -128(1) +; 64BIT: ld [[REG1:[0-9]+]], LC{{[0-9]+}}(2) +; 64BIT: lbz [[REG2:[0-9]+]], 0([[REG1]]) +; 64BIT: stb [[REG2]], 112(1) +; 64BIT: bl .test_byval_mem1 +; 64BIT: addi 1, 1, 128 + + +%struct_S256 = type { [256 x i8] } + +@gS256 = external global %struct_S256, align 1 + +define void @call_test_byval_mem2() { +entry: + call void @test_byval_mem2(i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7, i32 8, %struct_S256* byval(%struct_S256) align 1 @gS256) + ret void +} + +declare void @test_byval_mem2(i32, i32, i32, i32, i32, i32, i32, i32, %struct_S256* byval(%struct_S256) align 1) + +; CHECK-LABEL: .call_test_byval_mem2: + +; 32BIT: stwu 1, -320(1) +; 32BIT-DAG: addi 3, 1, 56 +; 32BIT-DAG: lwz 4, LC{{[0-9]+}}(2) +; 32BIT-DAG: li 5, 256 +; 32BIT-NEXT: bl .memcpy +; 32BIT: bl .test_byval_mem2 +; 32BIT: addi 1, 1, 320 + +; 64BIT: stdu 1, -368(1) +; 64BIT-DAG: addi 3, 1, 112 +; 64BIT-DAG: ld 4, LC{{[0-9]+}}(2) +; 64BIT-DAG: li 5, 256 +; 64BIT-NEXT: bl .memcpy +; 64BIT: bl .test_byval_mem2 +; 64BIT: addi 1, 1, 368 + + +%struct_S57 = type { [57 x i8] } + +@gS57 = external global %struct_S57, align 1 + +define void @call_test_byval_mem3() { +entry: + call void @test_byval_mem3(i32 42, float 0x40091EB860000000, %struct_S57* byval(%struct_S57) align 1 @gS57) + ret void +} + +declare void @test_byval_mem3(i32, float, %struct_S57* byval(%struct_S57) align 1) + +; CHECK-LABEL: .call_test_byval_mem3: + +; 32BIT: stwu 1, -96(1) +; 32BIT-DAG: lwz [[REG:[0-9]+]], LC{{[0-9]+}}(2) +; 32BIT-DAG: addi 3, 1, 56 +; 32BIT-DAG: addi 4, [[REG]], 24 +; 32BIT-DAG: li 5, 33 +; 32BIT-NEXT: bl .memcpy +; 32BIT-DAG: lwz 5, 0([[REG]]) +; 32BIT-DAG: lwz 6, 4([[REG]]) +; 32BIT-DAG: lwz 7, 8([[REG]]) +; 32BIT-DAG: lwz 8, 12([[REG]]) +; 32BIT-DAG: lwz 9, 16([[REG]]) +; 32BIT-DAG: lwz 10, 20([[REG]]) +; 32BIT: bl .test_byval_mem3 +; 32BIT: addi 1, 1, 96 + +; 64BIT: stdu 1, -128(1) +; 64BIT-DAG: ld [[REG1:[0-9]+]], LC{{[0-9]+}}(2) +; 64BIT-DAG: ld [[REG2:[0-9]+]], 48([[REG1]]) +; 64BIT-DAG: std [[REG2]], 112(1) +; 64BIT-DAG: lbz [[REG3:[0-9]+]], 56([[REG1]]) +; 64BIT-DAG: stb [[REG3]], 120(1) +; 64BIT-DAG: ld 5, 0([[REG1]]) +; 64BIT-DAG: ld 6, 8([[REG1]]) +; 64BIT-DAG: ld 7, 16([[REG1]]) +; 64BIT-DAG: ld 8, 24([[REG1]]) +; 64BIT-DAG: ld 9, 32([[REG1]]) +; 64BIT-DAG: ld 10, 40([[REG1]]) +; 64BIT: bl .test_byval_mem3 +; 64BIT: addi 1, 1, 128 + + +%struct_S31 = type { [31 x i8] } + +@gS31 = external global %struct_S31, align 1 + +define void @call_test_byval_mem4() { +entry: + call void @test_byval_mem4(i32 42, %struct_S31* byval(%struct_S31) align 1 @gS31, %struct_S256* byval(%struct_S256) align 1 @gS256) + ret void +} + +declare void @test_byval_mem4(i32, %struct_S31* byval(%struct_S31) align 1, %struct_S256* byval(%struct_S256) align 1) + +; 32BIT: stwu 1, -320(1) +; 32BIT-DAG: lwz [[REG1:[0-9]+]], LC{{[0-9]+}}(2) +; 32BIT-DAG: lhz [[REG2:[0-9]+]], 28([[REG1]]) +; 32BIT-DAG: sth [[REG2]], 56(1) +; 32BIT-DAG: lbz [[REG3:[0-9]+]], 30([[REG1]]) +; 32BIT-DAG: stb [[REG3]], 58(1) +; 32BIT-DAG: addi 3, 1, 60 +; 32BIT-DAG: 4, LC{{[0-9]+}}(2) +; 32BIT-DAG: li 5, 256 +; 32BIT-NEXT: bl .memcpy +; 32BIT-DAG: lwz 4, 0(13) +; 32BIT-DAG: lwz 5, 4(13) +; 32BIT-DAG: lwz 6, 8(13) +; 32BIT-DAG: lwz 7, 12(13) +; 32BIT-DAG: lwz 8, 16(13) +; 32BIT-DAG: lwz 9, 20(13) +; 32BIT-DAG: lwz 10, 24(13) +; 32BIT: bl .test_byval_mem4 +; 32BIT: addi 1, 1, 320 + +; 64BIT: stdu 1, -352(1) +; 64BIT-DAG: ld [[REG1:[0-9]+]], LC{{[0-9]+}}(2) +; 64BIT-DAG: addi 3, 1, 112 +; 64BIT-DAG: addi 4, [[REG1]], 24 +; 64BIT-DAG: li 5, 232 +; 64BIT-NEXT: bl .memcpy +; 64BIT-DAG: ld [[REG2:[0-9]+]], LC{{[0-9]+}}(2) +; 64BIT-DAG: ld 4, 0([[REG2]]) +; 64BIT-DAG: ld 5, 8([[REG2]]) +; 64BIT-DAG: ld 6, 16([[REG2]]) +; 64BIT-DAG: lwz [[REG3:[0-9]+]], 24([[REG2]]) +; 64BIT-DAG: lhz [[REG4:[0-9]+]], 28([[REG2]]) +; 64BIT-DAG: lbz 7, 30([[REG2]]) +; 64BIT-DAG: rlwinm 7, 7, 8, 16, 23 +; 64BIT-DAG: rlwimi 7, [[REG4]], 16, 0, 15 +; 64BIT-DAG: rldimi 7, [[REG3]], 32, 0 +; 64BIT-DAG: ld 8, 0([[REG1]]) +; 64BIT-DAG: ld 9, 8([[REG1]]) +; 64BIT-DAG: ld 10, 16([[REG1]]) +; 64BIT: bl .test_byval_mem4 +; 64BIT: addi 1, 1, 352 diff --git a/llvm/test/CodeGen/PowerPC/aix-cc-byval-split.ll b/llvm/test/CodeGen/PowerPC/aix-cc-byval-split.ll --- a/llvm/test/CodeGen/PowerPC/aix-cc-byval-split.ll +++ b/llvm/test/CodeGen/PowerPC/aix-cc-byval-split.ll @@ -4,7 +4,7 @@ ; RUN: not --crash llc -mtriple powerpc64-ibm-aix-xcoff -stop-after=machine-cp \ ; RUN: -mcpu=pwr4 -mattr=-altivec -verify-machineinstrs 2>&1 < %s | FileCheck %s -; CHECK: LLVM ERROR: Pass-by-value arguments are only supported in registers. +; CHECK: LLVM ERROR: Passing ByVals split between registers and stack not yet implemented. %struct.Spill = type { [12 x i64 ] } @GS = external global %struct.Spill, align 4