Index: lib/Target/AArch64/AArch64CallingConvention.td =================================================================== --- lib/Target/AArch64/AArch64CallingConvention.td +++ lib/Target/AArch64/AArch64CallingConvention.td @@ -16,6 +16,11 @@ /// CCIfBigEndian - Match only if we're in big endian mode. class CCIfBigEndian : CCIf<"State.getMachineFunction().getDataLayout().isBigEndian()", A>; +/// CCIfNotSubtarget - Match if the current subtarget doesn't has a feature F. +class CCIfNotSubtarget + : CCIf" + "(State.getMachineFunction().getSubtarget()).", F), + A>; //===----------------------------------------------------------------------===// // ARM AAPCS64 Calling Convention @@ -35,7 +40,8 @@ CCBitConvertToType>>, // An SRet is passed in X8, not X0 like a normal pointer parameter. - CCIfSRet>>, + CCIfNotSubtarget<"isTargetWindows()", + CCIfSRet>>>, // Put ByVal arguments directly on the stack. Minimum size and alignment of a // slot is 64-bit. Index: lib/Target/AArch64/AArch64ISelLowering.cpp =================================================================== --- lib/Target/AArch64/AArch64ISelLowering.cpp +++ lib/Target/AArch64/AArch64ISelLowering.cpp @@ -3208,6 +3208,43 @@ } } + // Struct returns on Windows. + // Note: This has been based on X86 implementation. + if (IsWin64) { + for (unsigned I = 0; I <= 1 && I < Ins.size(); ++I) { + if (!Ins[I].Flags.isSRet()) + continue; + + if (Ins[I].Flags.isInReg() || I == 1) { + // If isInReg: "sret inreg" on a parameter indicates a non-POD struct + // return. And if these attributes are on param0 then callee needs to + // save X0. + + // If I == 1: If param1 is marked "sret", it means param0 is "this". + // This is how we identify instance methods. In this case, the + // address of the struct is passed in X1. So in the function epilog + // we need to copy X1 to X0. + + unsigned Reg = FuncInfo->getSRetReturnReg(); + if (!Reg) { + MVT PtrTy = getPointerTy(DAG.getDataLayout()); + Reg = MF.getRegInfo().createVirtualRegister(getRegClassFor(PtrTy)); + FuncInfo->setSRetReturnReg(Reg); + } + + SDValue Copy = + DAG.getCopyToReg(DAG.getEntryNode(), DL, Reg, InVals[I]); + Chain = DAG.getNode(ISD::TokenFactor, DL, MVT::Other, Copy, Chain); + + if (I == 1) { + // The address of the struct is passed in X1. + CCInfo.AllocateReg(AArch64::X1); + } + } + break; + } + } + unsigned StackArgSize = CCInfo.getNextStackOffset(); bool TailCallOpt = MF.getTarget().Options.GuaranteedTailCallOpt; if (DoesCalleeRestoreStack(CallConv, TailCallOpt)) { @@ -3924,6 +3961,9 @@ const SmallVectorImpl &Outs, const SmallVectorImpl &OutVals, const SDLoc &DL, SelectionDAG &DAG) const { + auto &MF = DAG.getMachineFunction(); + auto *FuncInfo = MF.getInfo(); + CCAssignFn *RetCC = CallConv == CallingConv::WebKit_JS ? RetCC_AArch64_WebKit_JS : RetCC_AArch64_AAPCS; @@ -3962,6 +4002,25 @@ Flag = Chain.getValue(1); RetOps.push_back(DAG.getRegister(VA.getLocReg(), VA.getLocVT())); } + + // Struct returns on Windows. + // Note: This has been based on X86 implementation. + bool IsWin64 = + Subtarget->isCallingConvWin64(MF.getFunction().getCallingConv()); + if (IsWin64) { + if (unsigned SRetReg = FuncInfo->getSRetReturnReg()) { + SDValue Val = DAG.getCopyFromReg(RetOps[0], DL, SRetReg, + getPointerTy(MF.getDataLayout())); + + unsigned RetValReg = AArch64::X0; + Chain = DAG.getCopyToReg(Chain, DL, RetValReg, Val, Flag); + Flag = Chain.getValue(1); + + RetOps.push_back( + DAG.getRegister(RetValReg, getPointerTy(DAG.getDataLayout()))); + } + } + const AArch64RegisterInfo *TRI = Subtarget->getRegisterInfo(); const MCPhysReg *I = TRI->getCalleeSavedRegsViaCopy(&DAG.getMachineFunction()); Index: lib/Target/AArch64/AArch64MachineFunctionInfo.h =================================================================== --- lib/Target/AArch64/AArch64MachineFunctionInfo.h +++ lib/Target/AArch64/AArch64MachineFunctionInfo.h @@ -91,6 +91,11 @@ /// other stack allocations. bool CalleeSaveStackHasFreeSpace = false; + /// SRetReturnReg - sret lowering includes returning the value of the + /// returned struct in a register. This field holds the virtual register into + /// which the sret argument is passed. + unsigned SRetReturnReg = 0; + /// Has a value when it is known whether or not the function uses a /// redzone, and no value otherwise. /// Initialized during frame lowering, unless the function has the noredzone @@ -165,6 +170,9 @@ unsigned getVarArgsFPRSize() const { return VarArgsFPRSize; } void setVarArgsFPRSize(unsigned Size) { VarArgsFPRSize = Size; } + unsigned getSRetReturnReg() const { return SRetReturnReg; } + void setSRetReturnReg(unsigned Reg) { SRetReturnReg = Reg; } + unsigned getJumpTableEntrySize(int Idx) const { auto It = JumpTableEntryInfo.find(Idx); if (It != JumpTableEntryInfo.end())