From 805a83c0419aa453b78b2a062f46dc7f72137f79 Mon Sep 17 00:00:00 2001 From: Reid Kleckner Date: Fri, 9 May 2014 22:32:13 +0000 Subject: [PATCH] Allow sret on the second parameter as well as the first MSVC always places the implicit sret parameter after the implicit this parameter of instance methods. We used to handle this for x86_thiscallcc by allocating the sret parameter on the stack and leaving the this pointer in ecx, but that doesn't handle alternative calling conventions like cdecl, stdcall, fastcall, or the win64 convention. Instead, change the verifier to allow sret on the second parameter. This also requires changing the Mips and X86 backends to return the argument with the sret parameter, instead of assuming that the sret parameter comes first. The Sparc backend also returns sret parameters in a register, but I wasn't able to update it to handle secondary sret parameters. It currently calls report_fatal_error if you feed it an sret in the second parameter. Reviewers: rafael.espindola, majnemer Differential Revision: http://reviews.llvm.org/D3617 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@208453 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/IR/Function.h | 3 +- lib/IR/Function.cpp | 8 ++-- lib/IR/Verifier.cpp | 9 +++- lib/Target/Mips/MipsISelLowering.cpp | 25 +++++------ lib/Target/Sparc/SparcISelLowering.cpp | 7 ++- lib/Target/X86/X86ISelLowering.cpp | 32 +++++++------- test/CodeGen/Mips/mips64-sret.ll | 25 +++++++---- test/CodeGen/SPARC/sret-secondary.ll | 8 ++++ test/CodeGen/X86/win32_sret.ll | 60 ++++++++++++++++++++++++++ test/Verifier/sret.ll | 7 +++ 10 files changed, 138 insertions(+), 46 deletions(-) create mode 100644 test/CodeGen/SPARC/sret-secondary.ll create mode 100644 test/Verifier/sret.ll diff --git a/include/llvm/IR/Function.h b/include/llvm/IR/Function.h index 1d10b6fd068..3566002ae1d 100644 --- a/include/llvm/IR/Function.h +++ b/include/llvm/IR/Function.h @@ -297,7 +297,8 @@ public: /// @brief Determine if the function returns a structure through first /// pointer argument. bool hasStructRetAttr() const { - return AttributeSets.hasAttribute(1, Attribute::StructRet); + return AttributeSets.hasAttribute(1, Attribute::StructRet) || + AttributeSets.hasAttribute(2, Attribute::StructRet); } /// @brief Determine if the parameter does not alias other parameters. diff --git a/lib/IR/Function.cpp b/lib/IR/Function.cpp index da7774128a1..c1a09686b02 100644 --- a/lib/IR/Function.cpp +++ b/lib/IR/Function.cpp @@ -207,10 +207,10 @@ void Function::eraseFromParent() { // Function Implementation //===----------------------------------------------------------------------===// -Function::Function(FunctionType *Ty, LinkageTypes Linkage, const Twine &name, - Module *ParentModule) - : GlobalValue(PointerType::getUnqual(Ty), Value::FunctionVal, nullptr, 0, - Linkage, name) { +Function::Function(FunctionType *Ty, LinkageTypes Linkage, + const Twine &name, Module *ParentModule) + : GlobalValue(PointerType::getUnqual(Ty), + Value::FunctionVal, nullptr, 0, Linkage, name) { assert(FunctionType::isValidReturnType(getReturnType()) && "invalid return type"); SymTab = new ValueSymbolTable(); diff --git a/lib/IR/Verifier.cpp b/lib/IR/Verifier.cpp index 083f7b5255e..ec287f58fa3 100644 --- a/lib/IR/Verifier.cpp +++ b/lib/IR/Verifier.cpp @@ -820,6 +820,7 @@ void Verifier::VerifyFunctionAttrs(FunctionType *FT, AttributeSet Attrs, bool SawNest = false; bool SawReturned = false; + bool SawSRet = false; for (unsigned i = 0, e = Attrs.getNumSlots(); i != e; ++i) { unsigned Idx = Attrs.getSlotIndex(i); @@ -850,8 +851,12 @@ void Verifier::VerifyFunctionAttrs(FunctionType *FT, AttributeSet Attrs, SawReturned = true; } - if (Attrs.hasAttribute(Idx, Attribute::StructRet)) - Assert1(Idx == 1, "Attribute sret is not on first parameter!", V); + if (Attrs.hasAttribute(Idx, Attribute::StructRet)) { + Assert1(!SawSRet, "Cannot have multiple 'sret' parameters!", V); + Assert1(Idx == 1 || Idx == 2, + "Attribute 'sret' is not on first or second parameter!", V); + SawSRet = true; + } if (Attrs.hasAttribute(Idx, Attribute::InAlloca)) { Assert1(Idx == FT->getNumParams(), diff --git a/lib/Target/Mips/MipsISelLowering.cpp b/lib/Target/Mips/MipsISelLowering.cpp index d152e6781a1..ddcbb60ba9a 100644 --- a/lib/Target/Mips/MipsISelLowering.cpp +++ b/lib/Target/Mips/MipsISelLowering.cpp @@ -2659,20 +2659,21 @@ MipsTargetLowering::LowerFormalArguments(SDValue Chain, InVals.push_back(Load); OutChains.push_back(Load.getValue(1)); } - } - // The mips ABIs for returning structs by value requires that we copy - // the sret argument into $v0 for the return. Save the argument into - // a virtual register so that we can access it from the return points. - if (DAG.getMachineFunction().getFunction()->hasStructRetAttr()) { - unsigned Reg = MipsFI->getSRetReturnReg(); - if (!Reg) { - Reg = MF.getRegInfo().createVirtualRegister( - getRegClassFor(isN64() ? MVT::i64 : MVT::i32)); - MipsFI->setSRetReturnReg(Reg); + // The mips ABIs for returning structs by value requires that we copy + // the sret argument into $v0 for the return. Save the argument into + // a virtual register so that we can access it from the return points. + if (Flags.isSRet()) { + unsigned Reg = MipsFI->getSRetReturnReg(); + if (!Reg) { + Reg = MF.getRegInfo().createVirtualRegister( + getRegClassFor(isN64() ? MVT::i64 : MVT::i32)); + MipsFI->setSRetReturnReg(Reg); + } + SDValue Copy = + DAG.getCopyToReg(DAG.getEntryNode(), DL, Reg, InVals.back()); + Chain = DAG.getNode(ISD::TokenFactor, DL, MVT::Other, Copy, Chain); } - SDValue Copy = DAG.getCopyToReg(DAG.getEntryNode(), DL, Reg, InVals[0]); - Chain = DAG.getNode(ISD::TokenFactor, DL, MVT::Other, Copy, Chain); } if (IsVarArg) diff --git a/lib/Target/Sparc/SparcISelLowering.cpp b/lib/Target/Sparc/SparcISelLowering.cpp index 9457db90544..e733dd8df32 100644 --- a/lib/Target/Sparc/SparcISelLowering.cpp +++ b/lib/Target/Sparc/SparcISelLowering.cpp @@ -355,10 +355,13 @@ LowerFormalArguments_32(SDValue Chain, const unsigned StackOffset = 92; - for (unsigned i = 0, e = ArgLocs.size(); i != e; ++i) { + unsigned InIdx = 0; + for (unsigned i = 0, e = ArgLocs.size(); i != e; ++i, ++InIdx) { CCValAssign &VA = ArgLocs[i]; - if (i == 0 && Ins[i].Flags.isSRet()) { + if (Ins[InIdx].Flags.isSRet()) { + if (InIdx != 0) + report_fatal_error("sparc only supports sret on the first parameter"); // Get SRet from [%fp+64]. int FrameIdx = MF.getFrameInfo()->CreateFixedObject(4, 64, true); SDValue FIPtr = DAG.getFrameIndex(FrameIdx, MVT::i32); diff --git a/lib/Target/X86/X86ISelLowering.cpp b/lib/Target/X86/X86ISelLowering.cpp index bf624017c8e..4ed36906297 100644 --- a/lib/Target/X86/X86ISelLowering.cpp +++ b/lib/Target/X86/X86ISelLowering.cpp @@ -2299,24 +2299,24 @@ X86TargetLowering::LowerFormalArguments(SDValue Chain, MachinePointerInfo(), false, false, false, 0); InVals.push_back(ArgValue); - } - // The x86-64 ABIs require that for returning structs by value we copy - // the sret argument into %rax/%eax (depending on ABI) for the return. - // Win32 requires us to put the sret argument to %eax as well. - // Save the argument into a virtual register so that we can access it - // from the return points. - if (MF.getFunction()->hasStructRetAttr() && - (Subtarget->is64Bit() || Subtarget->isTargetKnownWindowsMSVC())) { - X86MachineFunctionInfo *FuncInfo = MF.getInfo(); - unsigned Reg = FuncInfo->getSRetReturnReg(); - if (!Reg) { - MVT PtrTy = getPointerTy(); - Reg = MF.getRegInfo().createVirtualRegister(getRegClassFor(PtrTy)); - FuncInfo->setSRetReturnReg(Reg); + // The x86-64 ABIs require that for returning structs by value we copy + // the sret argument into %rax/%eax (depending on ABI) for the return. + // Win32 requires us to put the sret argument to %eax as well. + // Save the argument into a virtual register so that we can access it + // from the return points. + if (Ins[i].Flags.isSRet() && + (Subtarget->is64Bit() || Subtarget->isTargetKnownWindowsMSVC())) { + unsigned Reg = FuncInfo->getSRetReturnReg(); + if (!Reg) { + MVT PtrTy = getPointerTy(); + Reg = MF.getRegInfo().createVirtualRegister(getRegClassFor(PtrTy)); + FuncInfo->setSRetReturnReg(Reg); + } + SDValue Copy = + DAG.getCopyToReg(DAG.getEntryNode(), dl, Reg, InVals.back()); + Chain = DAG.getNode(ISD::TokenFactor, dl, MVT::Other, Copy, Chain); } - SDValue Copy = DAG.getCopyToReg(DAG.getEntryNode(), dl, Reg, InVals[0]); - Chain = DAG.getNode(ISD::TokenFactor, dl, MVT::Other, Copy, Chain); } unsigned StackSize = CCInfo.getNextStackOffset(); diff --git a/test/CodeGen/Mips/mips64-sret.ll b/test/CodeGen/Mips/mips64-sret.ll index e01609f3b1e..7a52c3d41d6 100644 --- a/test/CodeGen/Mips/mips64-sret.ll +++ b/test/CodeGen/Mips/mips64-sret.ll @@ -1,16 +1,23 @@ -; RUN: llc -march=mips64el -mcpu=mips64r2 -mattr=n64 -O3 < %s | FileCheck %s +; RUN: llc -march=mips64el -mcpu=mips64r2 -mattr=n64 < %s | FileCheck %s -%struct.S = type { [8 x i32] } +define void @foo(i32* noalias sret %agg.result) nounwind { +entry: +; CHECK-LABEL: foo: +; CHECK: sw {{.*}}, 0($4) +; CHECK: jr $ra +; CHECK-NEXT: move $2, $4 -@g = common global %struct.S zeroinitializer, align 4 + store i32 42, i32* %agg.result + ret void +} -define void @f(%struct.S* noalias sret %agg.result) nounwind { +define void @bar(i32 %v, i32* noalias sret %agg.result) nounwind { entry: -; CHECK: move $2, $4 +; CHECK-LABEL: bar: +; CHECK: sw $4, 0($5) +; CHECK: jr $ra +; CHECK-NEXT: move $2, $5 - %0 = bitcast %struct.S* %agg.result to i8* - call void @llvm.memcpy.p0i8.p0i8.i64(i8* %0, i8* bitcast (%struct.S* @g to i8*), i64 32, i32 4, i1 false) + store i32 %v, i32* %agg.result ret void } - -declare void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture, i8* nocapture, i64, i32, i1) nounwind diff --git a/test/CodeGen/SPARC/sret-secondary.ll b/test/CodeGen/SPARC/sret-secondary.ll new file mode 100644 index 00000000000..4efcabfc6fb --- /dev/null +++ b/test/CodeGen/SPARC/sret-secondary.ll @@ -0,0 +1,8 @@ +; RUN: not llc -march=sparc < %s -o /dev/null 2>&1 | FileCheck %s + +; CHECK: sparc only supports sret on the first parameter + +define void @foo(i32 %a, i32* sret %out) { + store i32 %a, i32* %out + ret void +} diff --git a/test/CodeGen/X86/win32_sret.ll b/test/CodeGen/X86/win32_sret.ll index ca49051bd2f..8728712cece 100644 --- a/test/CodeGen/X86/win32_sret.ll +++ b/test/CodeGen/X86/win32_sret.ll @@ -181,3 +181,63 @@ define void @test6_f(%struct.test6* %x) nounwind { ret void } declare x86_thiscallcc void @test6_g(%struct.test6* sret, %struct.test6*) + +; Flipping the parameters at the IR level generates the same code. +%struct.test7 = type { i32, i32, i32 } +define void @test7_f(%struct.test7* %x) nounwind { +; WIN32-LABEL: _test7_f: +; MINGW_X86-LABEL: _test7_f: +; CYGWIN-LABEL: _test7_f: +; LINUX-LABEL: test7_f: + +; The %x argument is moved to %ecx on all OSs. It will be the this pointer. +; WIN32: movl 8(%ebp), %ecx +; MINGW_X86: movl 8(%ebp), %ecx +; CYGWIN: movl 8(%ebp), %ecx + +; The sret pointer is (%esp) +; WIN32: leal 8(%esp), %[[REG:e[a-d]x]] +; WIN32-NEXT: movl %[[REG]], (%e{{([a-d]x)|(sp)}}) +; MINGW_X86: leal 8(%esp), %[[REG:e[a-d]x]] +; MINGW_X86-NEXT: movl %[[REG]], (%e{{([a-d]x)|(sp)}}) +; CYGWIN: leal 8(%esp), %[[REG:e[a-d]x]] +; CYGWIN-NEXT: movl %[[REG]], (%e{{([a-d]x)|(sp)}}) + + %tmp = alloca %struct.test7, align 4 + call x86_thiscallcc void @test7_g(%struct.test7* %x, %struct.test7* sret %tmp) + ret void +} + +define x86_thiscallcc void @test7_g(%struct.test7* %in, %struct.test7* sret %out) { + %s = getelementptr %struct.test7* %in, i32 0, i32 0 + %d = getelementptr %struct.test7* %out, i32 0, i32 0 + %v = load i32* %s + store i32 %v, i32* %d + call void @clobber_eax() + ret void + +; Make sure we return the second parameter in %eax. +; WIN32-LABEL: _test7_g: +; WIN32: calll _clobber_eax +; WIN32: movl {{.*}}, %eax +; WIN32: retl +} + +declare void @clobber_eax() + +; Test what happens if the first parameter has to be split by codegen. +; Realistically, no frontend will generate code like this, but here it is for +; completeness. +define void @test8_f(i64 inreg %a, i64* sret %out) { + store i64 %a, i64* %out + call void @clobber_eax() + ret void + +; WIN32-LABEL: _test8_f: +; WIN32: movl {{[0-9]+}}(%esp), %[[out:[a-z]+]] +; WIN32-DAG: movl %edx, 4(%[[out]]) +; WIN32-DAG: movl %eax, (%[[out]]) +; WIN32: calll _clobber_eax +; WIN32: movl {{.*}}, %eax +; WIN32: retl +} diff --git a/test/Verifier/sret.ll b/test/Verifier/sret.ll new file mode 100644 index 00000000000..1ddbf1f1a3b --- /dev/null +++ b/test/Verifier/sret.ll @@ -0,0 +1,7 @@ +; RUN: not llvm-as %s -o /dev/null 2>&1 | FileCheck %s + +declare void @a(i32* sret %a, i32* sret %b) +; CHECK: Cannot have multiple 'sret' parameters! + +declare void @b(i32* %a, i32* %b, i32* sret %c) +; CHECK: Attribute 'sret' is not on first or second parameter! -- 2.34.1