diff --git a/lib/Target/X86/X86FastISel.cpp b/lib/Target/X86/X86FastISel.cpp index 08ad7fa11d7..4eeefae6318 100644 --- a/lib/Target/X86/X86FastISel.cpp +++ b/lib/Target/X86/X86FastISel.cpp @@ -1527,6 +1527,8 @@ static unsigned computeBytesPopedByCalle(const X86Subtarget &Subtarget, return 0; if (!CS.paramHasAttr(1, Attribute::StructRet)) return 0; + if (CS.paramHasAttr(1, Attribute::InReg)) + return 0; return 4; } diff --git a/lib/Target/X86/X86ISelLowering.cpp b/lib/Target/X86/X86ISelLowering.cpp index 4ccb0a391eb..40283d87f87 100644 --- a/lib/Target/X86/X86ISelLowering.cpp +++ b/lib/Target/X86/X86ISelLowering.cpp @@ -1717,21 +1717,37 @@ X86TargetLowering::LowerCallResult(SDValue Chain, SDValue InFlag, /// CallIsStructReturn - Determines whether a call uses struct return /// semantics. -static bool CallIsStructReturn(const SmallVectorImpl &Outs) { +enum StructReturnType { + NotStructReturn, + RegStructReturn, + StackStructReturn +}; +static StructReturnType +callIsStructReturn(const SmallVectorImpl &Outs) { if (Outs.empty()) - return false; + return NotStructReturn; - return Outs[0].Flags.isSRet(); + const ISD::ArgFlagsTy &Flags = Outs[0].Flags; + if (!Flags.isSRet()) + return NotStructReturn; + if (Flags.isInReg()) + return RegStructReturn; + return StackStructReturn; } /// ArgsAreStructReturn - Determines whether a function uses struct /// return semantics. -static bool -ArgsAreStructReturn(const SmallVectorImpl &Ins) { +static StructReturnType +argsAreStructReturn(const SmallVectorImpl &Ins) { if (Ins.empty()) - return false; + return NotStructReturn; - return Ins[0].Flags.isSRet(); + const ISD::ArgFlagsTy &Flags = Ins[0].Flags; + if (!Flags.isSRet()) + return NotStructReturn; + if (Flags.isInReg()) + return RegStructReturn; + return StackStructReturn; } /// CreateCopyOfByValArgument - Make a copy of an aggregate at address specified @@ -2072,7 +2088,7 @@ X86TargetLowering::LowerFormalArguments(SDValue Chain, FuncInfo->setBytesToPopOnReturn(0); // Callee pops nothing. // If this is an sret function, the return should pop the hidden pointer. if (!Is64Bit && !IsTailCallConvention(CallConv) && !IsWindows && - ArgsAreStructReturn(Ins)) + argsAreStructReturn(Ins) == StackStructReturn) FuncInfo->setBytesToPopOnReturn(4); } @@ -2162,7 +2178,7 @@ X86TargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI, bool Is64Bit = Subtarget->is64Bit(); bool IsWin64 = Subtarget->isTargetWin64(); bool IsWindows = Subtarget->isTargetWindows(); - bool IsStructRet = CallIsStructReturn(Outs); + StructReturnType SR = callIsStructReturn(Outs); bool IsSibcall = false; if (MF.getTarget().Options.DisableTailCalls) @@ -2171,8 +2187,9 @@ X86TargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI, if (isTailCall) { // Check if it's really possible to do a tail call. isTailCall = IsEligibleForTailCallOptimization(Callee, CallConv, - isVarArg, IsStructRet, MF.getFunction()->hasStructRetAttr(), - Outs, OutVals, Ins, DAG); + isVarArg, SR != NotStructReturn, + MF.getFunction()->hasStructRetAttr(), + Outs, OutVals, Ins, DAG); // Sibcalls are automatically detected tailcalls which do not require // ABI changes. @@ -2548,7 +2565,7 @@ X86TargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI, getTargetMachine().Options.GuaranteedTailCallOpt)) NumBytesForCalleeToPush = NumBytes; // Callee pops everything else if (!Is64Bit && !IsTailCallConvention(CallConv) && !IsWindows && - IsStructRet) + SR == StackStructReturn) // If this is a call to a struct-return function, the callee // pops the hidden struct pointer, so we have to push it back. // This is common for Darwin/X86, Linux & Mingw32 targets. diff --git a/test/CodeGen/X86/inreg.ll b/test/CodeGen/X86/inreg.ll index a6a7ebada38..6653cfb14ed 100644 --- a/test/CodeGen/X86/inreg.ll +++ b/test/CodeGen/X86/inreg.ll @@ -1,19 +1,46 @@ -; RUN: llc < %s -mtriple=i686-pc-linux -mcpu=corei7 | FileCheck %s +; RUN: llc < %s -mtriple=i686-pc-linux -mcpu=corei7 | FileCheck --check-prefix=DAG %s +; RUN: llc < %s -mtriple=i686-pc-linux -mcpu=corei7 -O0 | FileCheck --check-prefix=FAST %s -%struct.s = type { double, float } +%struct.s1 = type { double, float } -define void @g() nounwind { +define void @g1() nounwind { entry: - %tmp = alloca %struct.s, align 4 - call void @f(%struct.s* inreg sret %tmp, i32 inreg 41, i32 inreg 42, i32 43) + %tmp = alloca %struct.s1, align 4 + call void @f(%struct.s1* inreg sret %tmp, i32 inreg 41, i32 inreg 42, i32 43) ret void - ; CHECK: g: - ; CHECK: subl {{.*}}, %esp - ; CHECK-NEXT: $43, (%esp) - ; CHECK-NEXT: leal 16(%esp), %eax - ; CHECK-NEXT: movl $41, %edx - ; CHECK-NEXT: movl $42, %ecx - ; CHECK-NEXT: calll f + ; DAG: g1: + ; DAG: subl $[[AMT:.*]], %esp + ; DAG-NEXT: $43, (%esp) + ; DAG-NEXT: leal 16(%esp), %eax + ; DAG-NEXT: movl $41, %edx + ; DAG-NEXT: movl $42, %ecx + ; DAG-NEXT: calll f + ; DAG-NEXT: addl $[[AMT]], %esp + ; DAG-NEXT: ret + + ; FAST: g1: + ; FAST: subl $[[AMT:.*]], %esp + ; FAST-NEXT: leal 8(%esp), %eax + ; FAST-NEXT: movl $41, %edx + ; FAST-NEXT: movl $42, %ecx + ; FAST: $43, (%esp) + ; FAST: calll f + ; FAST-NEXT: addl $[[AMT]], %esp + ; FAST: ret } -declare void @f(%struct.s* inreg sret, i32 inreg, i32 inreg, i32) +declare void @f(%struct.s1* inreg sret, i32 inreg, i32 inreg, i32) + +%struct.s2 = type {} + +define void @g2(%struct.s2* inreg sret %agg.result) nounwind { +entry: + ret void + ; DAG: g2 + ; DAG-NOT: ret $4 + ; DAG: .size g2 + + ; FAST: g2 + ; FAST-NOT: ret $4 + ; FAST: .size g2 +}