//===-- RISCVCallLowering.cpp - Call lowering -------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // /// \file /// This file implements the lowering of LLVM calls to machine code calls for /// GlobalISel. // //===----------------------------------------------------------------------===// #include "RISCVCallLowering.h" #include "RISCVISelLowering.h" #include "RISCVMachineFunctionInfo.h" #include "RISCVSubtarget.h" #include "llvm/CodeGen/Analysis.h" #include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h" #include "llvm/CodeGen/MachineFrameInfo.h" using namespace llvm; namespace { struct RISCVOutgoingValueAssigner : public CallLowering::OutgoingValueAssigner { private: // The function used internally to assign args - we ignore the AssignFn stored // by OutgoingValueAssigner since RISC-V implements its CC using a custom // function with a different signature. RISCVTargetLowering::RISCVCCAssignFn *RISCVAssignFn; // Whether this is assigning args for a return. bool IsRet; public: RISCVOutgoingValueAssigner( RISCVTargetLowering::RISCVCCAssignFn *RISCVAssignFn_, bool IsRet) : CallLowering::OutgoingValueAssigner(nullptr), RISCVAssignFn(RISCVAssignFn_), IsRet(IsRet) {} bool assignArg(unsigned ValNo, EVT OrigVT, MVT ValVT, MVT LocVT, CCValAssign::LocInfo LocInfo, const CallLowering::ArgInfo &Info, ISD::ArgFlagsTy Flags, CCState &State) override { MachineFunction &MF = State.getMachineFunction(); const DataLayout &DL = MF.getDataLayout(); const RISCVSubtarget &Subtarget = MF.getSubtarget(); if (RISCVAssignFn(DL, Subtarget.getTargetABI(), ValNo, ValVT, LocVT, LocInfo, Flags, State, Info.IsFixed, IsRet, Info.Ty, *Subtarget.getTargetLowering(), /*FirstMaskArgument=*/std::nullopt)) return true; StackSize = State.getStackSize(); return false; } }; struct RISCVOutgoingValueHandler : public CallLowering::OutgoingValueHandler { RISCVOutgoingValueHandler(MachineIRBuilder &B, MachineRegisterInfo &MRI, MachineInstrBuilder MIB) : OutgoingValueHandler(B, MRI), MIB(MIB), Subtarget(MIRBuilder.getMF().getSubtarget()) {} Register getStackAddress(uint64_t MemSize, int64_t Offset, MachinePointerInfo &MPO, ISD::ArgFlagsTy Flags) override { MachineFunction &MF = MIRBuilder.getMF(); LLT p0 = LLT::pointer(0, Subtarget.getXLen()); LLT sXLen = LLT::scalar(Subtarget.getXLen()); if (!SPReg) SPReg = MIRBuilder.buildCopy(p0, Register(RISCV::X2)).getReg(0); auto OffsetReg = MIRBuilder.buildConstant(sXLen, Offset); auto AddrReg = MIRBuilder.buildPtrAdd(p0, SPReg, OffsetReg); MPO = MachinePointerInfo::getStack(MF, Offset); return AddrReg.getReg(0); } void assignValueToAddress(Register ValVReg, Register Addr, LLT MemTy, const MachinePointerInfo &MPO, const CCValAssign &VA) override { MachineFunction &MF = MIRBuilder.getMF(); uint64_t LocMemOffset = VA.getLocMemOffset(); // TODO: Move StackAlignment to subtarget and share with FrameLowering. auto MMO = MF.getMachineMemOperand(MPO, MachineMemOperand::MOStore, MemTy, commonAlignment(Align(16), LocMemOffset)); Register ExtReg = extendRegister(ValVReg, VA); MIRBuilder.buildStore(ExtReg, Addr, *MMO); } void assignValueToReg(Register ValVReg, Register PhysReg, const CCValAssign &VA) override { // If we're passing an f32 value into an i64, anyextend before copying. if (VA.getLocVT() == MVT::i64 && VA.getValVT() == MVT::f32) ValVReg = MIRBuilder.buildAnyExt(LLT::scalar(64), ValVReg).getReg(0); Register ExtReg = extendRegister(ValVReg, VA); MIRBuilder.buildCopy(PhysReg, ExtReg); MIB.addUse(PhysReg, RegState::Implicit); } unsigned assignCustomValue(CallLowering::ArgInfo &Arg, ArrayRef VAs, std::function *Thunk) override { assert(VAs.size() >= 2 && "Expected at least 2 VAs."); const CCValAssign &VALo = VAs[0]; const CCValAssign &VAHi = VAs[1]; assert(VAHi.needsCustom() && "Value doesn't need custom handling"); assert(VALo.getValNo() == VAHi.getValNo() && "Values belong to different arguments"); assert(VALo.getLocVT() == MVT::i32 && VAHi.getLocVT() == MVT::i32 && VALo.getValVT() == MVT::f64 && VAHi.getValVT() == MVT::f64 && "unexpected custom value"); Register NewRegs[] = {MRI.createGenericVirtualRegister(LLT::scalar(32)), MRI.createGenericVirtualRegister(LLT::scalar(32))}; MIRBuilder.buildUnmerge(NewRegs, Arg.Regs[0]); if (VAHi.isMemLoc()) { LLT MemTy(VAHi.getLocVT()); MachinePointerInfo MPO; Register StackAddr = getStackAddress( MemTy.getSizeInBytes(), VAHi.getLocMemOffset(), MPO, Arg.Flags[0]); assignValueToAddress(NewRegs[1], StackAddr, MemTy, MPO, const_cast(VAHi)); } auto assignFunc = [=]() { assignValueToReg(NewRegs[0], VALo.getLocReg(), VALo); if (VAHi.isRegLoc()) assignValueToReg(NewRegs[1], VAHi.getLocReg(), VAHi); }; if (Thunk) { *Thunk = assignFunc; return 2; } assignFunc(); return 2; } private: MachineInstrBuilder MIB; // Cache the SP register vreg if we need it more than once in this call site. Register SPReg; const RISCVSubtarget &Subtarget; }; struct RISCVIncomingValueAssigner : public CallLowering::IncomingValueAssigner { private: // The function used internally to assign args - we ignore the AssignFn stored // by IncomingValueAssigner since RISC-V implements its CC using a custom // function with a different signature. RISCVTargetLowering::RISCVCCAssignFn *RISCVAssignFn; // Whether this is assigning args from a return. bool IsRet; public: RISCVIncomingValueAssigner( RISCVTargetLowering::RISCVCCAssignFn *RISCVAssignFn_, bool IsRet) : CallLowering::IncomingValueAssigner(nullptr), RISCVAssignFn(RISCVAssignFn_), IsRet(IsRet) {} bool assignArg(unsigned ValNo, EVT OrigVT, MVT ValVT, MVT LocVT, CCValAssign::LocInfo LocInfo, const CallLowering::ArgInfo &Info, ISD::ArgFlagsTy Flags, CCState &State) override { MachineFunction &MF = State.getMachineFunction(); const DataLayout &DL = MF.getDataLayout(); const RISCVSubtarget &Subtarget = MF.getSubtarget(); if (LocVT.isScalableVector()) MF.getInfo()->setIsVectorCall(); if (RISCVAssignFn(DL, Subtarget.getTargetABI(), ValNo, ValVT, LocVT, LocInfo, Flags, State, /*IsFixed=*/true, IsRet, Info.Ty, *Subtarget.getTargetLowering(), /*FirstMaskArgument=*/std::nullopt)) return true; StackSize = State.getStackSize(); return false; } }; struct RISCVIncomingValueHandler : public CallLowering::IncomingValueHandler { RISCVIncomingValueHandler(MachineIRBuilder &B, MachineRegisterInfo &MRI) : IncomingValueHandler(B, MRI), Subtarget(MIRBuilder.getMF().getSubtarget()) {} Register getStackAddress(uint64_t MemSize, int64_t Offset, MachinePointerInfo &MPO, ISD::ArgFlagsTy Flags) override { MachineFrameInfo &MFI = MIRBuilder.getMF().getFrameInfo(); int FI = MFI.CreateFixedObject(MemSize, Offset, /*Immutable=*/true); MPO = MachinePointerInfo::getFixedStack(MIRBuilder.getMF(), FI); return MIRBuilder.buildFrameIndex(LLT::pointer(0, Subtarget.getXLen()), FI) .getReg(0); } void assignValueToAddress(Register ValVReg, Register Addr, LLT MemTy, const MachinePointerInfo &MPO, const CCValAssign &VA) override { MachineFunction &MF = MIRBuilder.getMF(); auto MMO = MF.getMachineMemOperand(MPO, MachineMemOperand::MOLoad, MemTy, inferAlignFromPtrInfo(MF, MPO)); MIRBuilder.buildLoad(ValVReg, Addr, *MMO); } void assignValueToReg(Register ValVReg, Register PhysReg, const CCValAssign &VA) override { markPhysRegUsed(PhysReg); IncomingValueHandler::assignValueToReg(ValVReg, PhysReg, VA); } unsigned assignCustomValue(CallLowering::ArgInfo &Arg, ArrayRef VAs, std::function *Thunk) override { assert(VAs.size() >= 2 && "Expected at least 2 VAs."); const CCValAssign &VALo = VAs[0]; const CCValAssign &VAHi = VAs[1]; assert(VAHi.needsCustom() && "Value doesn't need custom handling"); assert(VALo.getValNo() == VAHi.getValNo() && "Values belong to different arguments"); assert(VALo.getLocVT() == MVT::i32 && VAHi.getLocVT() == MVT::i32 && VALo.getValVT() == MVT::f64 && VAHi.getValVT() == MVT::f64 && "unexpected custom value"); Register NewRegs[] = {MRI.createGenericVirtualRegister(LLT::scalar(32)), MRI.createGenericVirtualRegister(LLT::scalar(32))}; if (VAHi.isMemLoc()) { LLT MemTy(VAHi.getLocVT()); MachinePointerInfo MPO; Register StackAddr = getStackAddress( MemTy.getSizeInBytes(), VAHi.getLocMemOffset(), MPO, Arg.Flags[0]); assignValueToAddress(NewRegs[1], StackAddr, MemTy, MPO, const_cast(VAHi)); } assignValueToReg(NewRegs[0], VALo.getLocReg(), VALo); if (VAHi.isRegLoc()) assignValueToReg(NewRegs[1], VAHi.getLocReg(), VAHi); MIRBuilder.buildMergeLikeInstr(Arg.Regs[0], NewRegs); return 2; } /// How the physical register gets marked varies between formal /// parameters (it's a basic-block live-in), and a call instruction /// (it's an implicit-def of the BL). virtual void markPhysRegUsed(MCRegister PhysReg) = 0; private: const RISCVSubtarget &Subtarget; }; struct RISCVFormalArgHandler : public RISCVIncomingValueHandler { RISCVFormalArgHandler(MachineIRBuilder &B, MachineRegisterInfo &MRI) : RISCVIncomingValueHandler(B, MRI) {} void markPhysRegUsed(MCRegister PhysReg) override { MIRBuilder.getMRI()->addLiveIn(PhysReg); MIRBuilder.getMBB().addLiveIn(PhysReg); } }; struct RISCVCallReturnHandler : public RISCVIncomingValueHandler { RISCVCallReturnHandler(MachineIRBuilder &B, MachineRegisterInfo &MRI, MachineInstrBuilder &MIB) : RISCVIncomingValueHandler(B, MRI), MIB(MIB) {} void markPhysRegUsed(MCRegister PhysReg) override { MIB.addDef(PhysReg, RegState::Implicit); } MachineInstrBuilder MIB; }; } // namespace RISCVCallLowering::RISCVCallLowering(const RISCVTargetLowering &TLI) : CallLowering(&TLI) {} /// Return true if scalable vector with ScalarTy is legal for lowering. static bool isLegalElementTypeForRVV(Type *EltTy, const RISCVSubtarget &Subtarget) { if (EltTy->isPointerTy()) return Subtarget.is64Bit() ? Subtarget.hasVInstructionsI64() : true; if (EltTy->isIntegerTy(1) || EltTy->isIntegerTy(8) || EltTy->isIntegerTy(16) || EltTy->isIntegerTy(32)) return true; if (EltTy->isIntegerTy(64)) return Subtarget.hasVInstructionsI64(); if (EltTy->isHalfTy()) return Subtarget.hasVInstructionsF16(); if (EltTy->isBFloatTy()) return Subtarget.hasVInstructionsBF16(); if (EltTy->isFloatTy()) return Subtarget.hasVInstructionsF32(); if (EltTy->isDoubleTy()) return Subtarget.hasVInstructionsF64(); return false; } // TODO: Support all argument types. // TODO: Remove IsLowerArgs argument by adding support for vectors in lowerCall. static bool isSupportedArgumentType(Type *T, const RISCVSubtarget &Subtarget, bool IsLowerArgs = false) { // TODO: Integers larger than 2*XLen are passed indirectly which is not // supported yet. if (T->isIntegerTy()) return T->getIntegerBitWidth() <= Subtarget.getXLen() * 2; if (T->isFloatTy() || T->isDoubleTy()) return true; if (T->isPointerTy()) return true; // TODO: Support fixed vector types. if (IsLowerArgs && T->isVectorTy() && Subtarget.hasVInstructions() && T->isScalableTy() && isLegalElementTypeForRVV(T->getScalarType(), Subtarget)) return true; return false; } // TODO: Only integer, pointer and aggregate types are supported now. // TODO: Remove IsLowerRetVal argument by adding support for vectors in // lowerCall. static bool isSupportedReturnType(Type *T, const RISCVSubtarget &Subtarget, bool IsLowerRetVal = false) { // TODO: Integers larger than 2*XLen are passed indirectly which is not // supported yet. if (T->isIntegerTy()) return T->getIntegerBitWidth() <= Subtarget.getXLen() * 2; if (T->isFloatTy() || T->isDoubleTy()) return true; if (T->isPointerTy()) return true; if (T->isArrayTy()) return isSupportedReturnType(T->getArrayElementType(), Subtarget); if (T->isStructTy()) { auto StructT = cast(T); for (unsigned i = 0, e = StructT->getNumElements(); i != e; ++i) if (!isSupportedReturnType(StructT->getElementType(i), Subtarget)) return false; return true; } if (IsLowerRetVal && T->isVectorTy() && Subtarget.hasVInstructions() && T->isScalableTy() && isLegalElementTypeForRVV(T->getScalarType(), Subtarget)) return true; return false; } bool RISCVCallLowering::lowerReturnVal(MachineIRBuilder &MIRBuilder, const Value *Val, ArrayRef VRegs, MachineInstrBuilder &Ret) const { if (!Val) return true; const RISCVSubtarget &Subtarget = MIRBuilder.getMF().getSubtarget(); if (!isSupportedReturnType(Val->getType(), Subtarget, /*IsLowerRetVal=*/true)) return false; MachineFunction &MF = MIRBuilder.getMF(); const DataLayout &DL = MF.getDataLayout(); const Function &F = MF.getFunction(); CallingConv::ID CC = F.getCallingConv(); ArgInfo OrigRetInfo(VRegs, Val->getType(), 0); setArgFlags(OrigRetInfo, AttributeList::ReturnIndex, DL, F); SmallVector SplitRetInfos; splitToValueTypes(OrigRetInfo, SplitRetInfos, DL, CC); RISCVOutgoingValueAssigner Assigner( CC == CallingConv::Fast ? RISCV::CC_RISCV_FastCC : RISCV::CC_RISCV, /*IsRet=*/true); RISCVOutgoingValueHandler Handler(MIRBuilder, MF.getRegInfo(), Ret); return determineAndHandleAssignments(Handler, Assigner, SplitRetInfos, MIRBuilder, CC, F.isVarArg()); } bool RISCVCallLowering::lowerReturn(MachineIRBuilder &MIRBuilder, const Value *Val, ArrayRef VRegs, FunctionLoweringInfo &FLI) const { assert(!Val == VRegs.empty() && "Return value without a vreg"); MachineInstrBuilder Ret = MIRBuilder.buildInstrNoInsert(RISCV::PseudoRET); if (!lowerReturnVal(MIRBuilder, Val, VRegs, Ret)) return false; MIRBuilder.insertInstr(Ret); return true; } /// If there are varargs that were passed in a0-a7, the data in those registers /// must be copied to the varargs save area on the stack. void RISCVCallLowering::saveVarArgRegisters( MachineIRBuilder &MIRBuilder, CallLowering::IncomingValueHandler &Handler, IncomingValueAssigner &Assigner, CCState &CCInfo) const { MachineFunction &MF = MIRBuilder.getMF(); const RISCVSubtarget &Subtarget = MF.getSubtarget(); unsigned XLenInBytes = Subtarget.getXLen() / 8; ArrayRef ArgRegs = RISCV::getArgGPRs(Subtarget.getTargetABI()); MachineRegisterInfo &MRI = MF.getRegInfo(); unsigned Idx = CCInfo.getFirstUnallocated(ArgRegs); MachineFrameInfo &MFI = MF.getFrameInfo(); RISCVMachineFunctionInfo *RVFI = MF.getInfo(); // Size of the vararg save area. For now, the varargs save area is either // zero or large enough to hold a0-a7. int VarArgsSaveSize = XLenInBytes * (ArgRegs.size() - Idx); int FI; // If all registers are allocated, then all varargs must be passed on the // stack and we don't need to save any argregs. if (VarArgsSaveSize == 0) { int VaArgOffset = Assigner.StackSize; FI = MFI.CreateFixedObject(XLenInBytes, VaArgOffset, true); } else { int VaArgOffset = -VarArgsSaveSize; FI = MFI.CreateFixedObject(VarArgsSaveSize, VaArgOffset, true); // If saving an odd number of registers then create an extra stack slot to // ensure that the frame pointer is 2*XLEN-aligned, which in turn ensures // offsets to even-numbered registered remain 2*XLEN-aligned. if (Idx % 2) { MFI.CreateFixedObject(XLenInBytes, VaArgOffset - static_cast(XLenInBytes), true); VarArgsSaveSize += XLenInBytes; } const LLT p0 = LLT::pointer(MF.getDataLayout().getAllocaAddrSpace(), Subtarget.getXLen()); const LLT sXLen = LLT::scalar(Subtarget.getXLen()); auto FIN = MIRBuilder.buildFrameIndex(p0, FI); auto Offset = MIRBuilder.buildConstant( MRI.createGenericVirtualRegister(sXLen), XLenInBytes); // Copy the integer registers that may have been used for passing varargs // to the vararg save area. const MVT XLenVT = Subtarget.getXLenVT(); for (unsigned I = Idx; I < ArgRegs.size(); ++I) { const Register VReg = MRI.createGenericVirtualRegister(sXLen); Handler.assignValueToReg( VReg, ArgRegs[I], CCValAssign::getReg(I + MF.getFunction().getNumOperands(), XLenVT, ArgRegs[I], XLenVT, CCValAssign::Full)); auto MPO = MachinePointerInfo::getFixedStack(MF, FI, (I - Idx) * XLenInBytes); MIRBuilder.buildStore(VReg, FIN, MPO, inferAlignFromPtrInfo(MF, MPO)); FIN = MIRBuilder.buildPtrAdd(MRI.createGenericVirtualRegister(p0), FIN.getReg(0), Offset); } } // Record the frame index of the first variable argument which is a value // necessary to G_VASTART. RVFI->setVarArgsFrameIndex(FI); RVFI->setVarArgsSaveSize(VarArgsSaveSize); } bool RISCVCallLowering::lowerFormalArguments(MachineIRBuilder &MIRBuilder, const Function &F, ArrayRef> VRegs, FunctionLoweringInfo &FLI) const { // Early exit if there are no arguments. varargs are not part of F.args() but // must be lowered. if (F.arg_empty() && !F.isVarArg()) return true; const RISCVSubtarget &Subtarget = MIRBuilder.getMF().getSubtarget(); for (auto &Arg : F.args()) { if (!isSupportedArgumentType(Arg.getType(), Subtarget, /*IsLowerArgs=*/true)) return false; } MachineFunction &MF = MIRBuilder.getMF(); const DataLayout &DL = MF.getDataLayout(); CallingConv::ID CC = F.getCallingConv(); SmallVector SplitArgInfos; unsigned Index = 0; for (auto &Arg : F.args()) { // Construct the ArgInfo object from destination register and argument type. ArgInfo AInfo(VRegs[Index], Arg.getType(), Index); setArgFlags(AInfo, Index + AttributeList::FirstArgIndex, DL, F); // Handle any required merging from split value types from physical // registers into the desired VReg. ArgInfo objects are constructed // correspondingly and appended to SplitArgInfos. splitToValueTypes(AInfo, SplitArgInfos, DL, CC); ++Index; } RISCVIncomingValueAssigner Assigner( CC == CallingConv::Fast ? RISCV::CC_RISCV_FastCC : RISCV::CC_RISCV, /*IsRet=*/false); RISCVFormalArgHandler Handler(MIRBuilder, MF.getRegInfo()); SmallVector ArgLocs; CCState CCInfo(CC, F.isVarArg(), MIRBuilder.getMF(), ArgLocs, F.getContext()); if (!determineAssignments(Assigner, SplitArgInfos, CCInfo) || !handleAssignments(Handler, SplitArgInfos, CCInfo, ArgLocs, MIRBuilder)) return false; if (F.isVarArg()) saveVarArgRegisters(MIRBuilder, Handler, Assigner, CCInfo); return true; } bool RISCVCallLowering::lowerCall(MachineIRBuilder &MIRBuilder, CallLoweringInfo &Info) const { MachineFunction &MF = MIRBuilder.getMF(); const DataLayout &DL = MF.getDataLayout(); const Function &F = MF.getFunction(); CallingConv::ID CC = F.getCallingConv(); const RISCVSubtarget &Subtarget = MIRBuilder.getMF().getSubtarget(); for (auto &AInfo : Info.OrigArgs) { if (!isSupportedArgumentType(AInfo.Ty, Subtarget)) return false; } if (!Info.OrigRet.Ty->isVoidTy() && !isSupportedReturnType(Info.OrigRet.Ty, Subtarget)) return false; MachineInstrBuilder CallSeqStart = MIRBuilder.buildInstr(RISCV::ADJCALLSTACKDOWN); SmallVector SplitArgInfos; SmallVector Outs; for (auto &AInfo : Info.OrigArgs) { // Handle any required unmerging of split value types from a given VReg into // physical registers. ArgInfo objects are constructed correspondingly and // appended to SplitArgInfos. splitToValueTypes(AInfo, SplitArgInfos, DL, CC); } // TODO: Support tail calls. Info.IsTailCall = false; // Select the recommended relocation type R_RISCV_CALL_PLT. if (!Info.Callee.isReg()) Info.Callee.setTargetFlags(RISCVII::MO_CALL); MachineInstrBuilder Call = MIRBuilder .buildInstrNoInsert(Info.Callee.isReg() ? RISCV::PseudoCALLIndirect : RISCV::PseudoCALL) .add(Info.Callee); const TargetRegisterInfo *TRI = Subtarget.getRegisterInfo(); Call.addRegMask(TRI->getCallPreservedMask(MF, Info.CallConv)); RISCVOutgoingValueAssigner ArgAssigner( CC == CallingConv::Fast ? RISCV::CC_RISCV_FastCC : RISCV::CC_RISCV, /*IsRet=*/false); RISCVOutgoingValueHandler ArgHandler(MIRBuilder, MF.getRegInfo(), Call); if (!determineAndHandleAssignments(ArgHandler, ArgAssigner, SplitArgInfos, MIRBuilder, CC, Info.IsVarArg)) return false; MIRBuilder.insertInstr(Call); CallSeqStart.addImm(ArgAssigner.StackSize).addImm(0); MIRBuilder.buildInstr(RISCV::ADJCALLSTACKUP) .addImm(ArgAssigner.StackSize) .addImm(0); // If Callee is a reg, since it is used by a target specific // instruction, it must have a register class matching the // constraint of that instruction. if (Call->getOperand(0).isReg()) constrainOperandRegClass(MF, *TRI, MF.getRegInfo(), *Subtarget.getInstrInfo(), *Subtarget.getRegBankInfo(), *Call, Call->getDesc(), Call->getOperand(0), 0); if (Info.OrigRet.Ty->isVoidTy()) return true; SmallVector SplitRetInfos; splitToValueTypes(Info.OrigRet, SplitRetInfos, DL, CC); RISCVIncomingValueAssigner RetAssigner( CC == CallingConv::Fast ? RISCV::CC_RISCV_FastCC : RISCV::CC_RISCV, /*IsRet=*/true); RISCVCallReturnHandler RetHandler(MIRBuilder, MF.getRegInfo(), Call); if (!determineAndHandleAssignments(RetHandler, RetAssigner, SplitRetInfos, MIRBuilder, CC, Info.IsVarArg)) return false; return true; }