diff --git a/include/llvm/CodeGen/FastISel.h b/include/llvm/CodeGen/FastISel.h index 9c5af234ca4..fceaccef5f6 100644 --- a/include/llvm/CodeGen/FastISel.h +++ b/include/llvm/CodeGen/FastISel.h @@ -16,7 +16,10 @@ #define LLVM_CODEGEN_FASTISEL_H #include "llvm/ADT/DenseMap.h" +#include "llvm/CodeGen/CallingConvLower.h" #include "llvm/CodeGen/MachineBasicBlock.h" +#include "llvm/Target/TargetLowering.h" +#include "llvm/IR/CallingConv.h" namespace llvm { @@ -47,6 +50,144 @@ class Value; /// This is a fast-path instruction selection class that generates poor code and /// doesn't support illegal types or non-trivial lowering, but runs quickly. class FastISel { + public: + struct ArgListEntry { + Value *Val; + Type *Ty; + bool isSExt : 1; + bool isZExt : 1; + bool isInReg : 1; + bool isSRet : 1; + bool isNest : 1; + bool isByVal : 1; + bool isInAlloca : 1; + bool isReturned : 1; + uint16_t Alignment; + + ArgListEntry() + : Val(nullptr), Ty(nullptr), isSExt(false), isZExt(false), isInReg(false), + isSRet(false), isNest(false), isByVal(false), isInAlloca(false), + isReturned(false), Alignment(0) { } + + void setAttributes(ImmutableCallSite *CS, unsigned AttrIdx); + }; + typedef std::vector ArgListTy; + + struct CallLoweringInfo { + Type *RetTy; + bool RetSExt : 1; + bool RetZExt : 1; + bool IsVarArg : 1; + bool IsInReg : 1; + bool DoesNotReturn : 1; + bool IsReturnValueUsed : 1; + + // IsTailCall should be modified by implementations of + // FastLowerCall that perform tail call conversions. + bool IsTailCall; + + unsigned NumFixedArgs; + CallingConv::ID CallConv; + const Value *Callee; + const char *SymName; + ArgListTy Args; + ImmutableCallSite *CS; + MachineInstr *Call; + unsigned ResultReg; + unsigned NumResultRegs; + + SmallVector OutVals; + SmallVector OutFlags; + SmallVector OutRegs; + SmallVector Ins; + SmallVector InRegs; + + CallLoweringInfo() + : RetTy(nullptr), RetSExt(false), RetZExt(false), IsVarArg(false), + IsInReg(false), DoesNotReturn(false), IsReturnValueUsed(true), + IsTailCall(false), NumFixedArgs(-1), CallConv(CallingConv::C), + Callee(nullptr), SymName(nullptr), CS(nullptr), Call(nullptr), + ResultReg(0), NumResultRegs(0) + {} + + CallLoweringInfo &setCallee(Type *ResultTy, FunctionType *FuncTy, + const Value *Target, ArgListTy &&ArgsList, + ImmutableCallSite &Call) { + RetTy = ResultTy; + Callee = Target; + + IsInReg = Call.paramHasAttr(0, Attribute::InReg); + DoesNotReturn = Call.doesNotReturn(); + IsVarArg = FuncTy->isVarArg(); + IsReturnValueUsed = !Call.getInstruction()->use_empty(); + RetSExt = Call.paramHasAttr(0, Attribute::SExt); + RetZExt = Call.paramHasAttr(0, Attribute::ZExt); + + CallConv = Call.getCallingConv(); + NumFixedArgs = FuncTy->getNumParams(); + Args = std::move(ArgsList); + + CS = &Call; + + return *this; + } + + CallLoweringInfo &setCallee(Type *ResultTy, FunctionType *FuncTy, + const char *Target, ArgListTy &&ArgsList, + ImmutableCallSite &Call, + unsigned FixedArgs = ~0U) { + RetTy = ResultTy; + Callee = Call.getCalledValue(); + SymName = Target; + + IsInReg = Call.paramHasAttr(0, Attribute::InReg); + DoesNotReturn = Call.doesNotReturn(); + IsVarArg = FuncTy->isVarArg(); + IsReturnValueUsed = !Call.getInstruction()->use_empty(); + RetSExt = Call.paramHasAttr(0, Attribute::SExt); + RetZExt = Call.paramHasAttr(0, Attribute::ZExt); + + CallConv = Call.getCallingConv(); + NumFixedArgs = (FixedArgs == ~0U) ? FuncTy->getNumParams() : FixedArgs; + Args = std::move(ArgsList); + + CS = &Call; + + return *this; + } + + CallLoweringInfo &setCallee(CallingConv::ID CC, Type *ResultTy, + const Value *Target, ArgListTy &&ArgsList, + unsigned FixedArgs = ~0U) { + RetTy = ResultTy; + Callee = Target; + CallConv = CC; + NumFixedArgs = (FixedArgs == ~0U) ? Args.size() : FixedArgs; + Args = std::move(ArgsList); + return *this; + } + + CallLoweringInfo &setTailCall(bool Value = true) { + IsTailCall = Value; + return *this; + } + + ArgListTy &getArgs() { + return Args; + } + + void clearOuts() { + OutVals.clear(); + OutFlags.clear(); + OutRegs.clear(); + } + + void clearIns() { + Ins.clear(); + InRegs.clear(); + } + }; + protected: DenseMap LocalValueMap; FunctionLoweringInfo &FuncInfo; @@ -173,15 +314,18 @@ protected: /// process fails to select an instruction. This gives targets a chance to /// emit code for anything that doesn't fit into FastISel's framework. It /// returns true if it was successful. - virtual bool - TargetSelectInstruction(const Instruction *I) = 0; + virtual bool TargetSelectInstruction(const Instruction *I) = 0; /// This method is called by target-independent code to do target specific /// argument lowering. It returns true if it was successful. virtual bool FastLowerArguments(); - /// This method is called by target-independent code to do target specific - /// intrinsic lowering. It returns true if it was successful. + /// \brief This method is called by target-independent code to do target + /// specific call lowering. It returns true if it was successful. + virtual bool FastLowerCall(CallLoweringInfo &CLI); + + /// \brief This method is called by target-independent code to do target + /// specific intrinsic lowering. It returns true if it was successful. virtual bool FastLowerIntrinsicCall(const IntrinsicInst *II); /// This method is called by target-independent code to request that an @@ -386,6 +530,9 @@ protected: /// \brief Create a machine mem operand from the given instruction. MachineMemOperand *createMachineMemOperandFor(const Instruction *I) const; + bool LowerCallTo(const CallInst *CI, const char *SymName, unsigned NumArgs); + bool LowerCallTo(CallLoweringInfo &CLI); + private: bool SelectBinaryOp(const User *I, unsigned ISDOpcode); @@ -394,7 +541,8 @@ private: bool SelectGetElementPtr(const User *I); bool SelectStackmap(const CallInst *I); - bool SelectCall(const User *I); + bool LowerCall(const CallInst *I); + bool SelectCall(const User *Call); bool SelectIntrinsicCall(const IntrinsicInst *II); bool SelectBitCast(const User *I); diff --git a/lib/CodeGen/SelectionDAG/FastISel.cpp b/lib/CodeGen/SelectionDAG/FastISel.cpp index f68843e54a6..f77343b3f7c 100644 --- a/lib/CodeGen/SelectionDAG/FastISel.cpp +++ b/lib/CodeGen/SelectionDAG/FastISel.cpp @@ -39,6 +39,7 @@ // //===----------------------------------------------------------------------===// +#include "llvm/CodeGen/Analysis.h" #include "llvm/CodeGen/FastISel.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/Statistic.h" @@ -74,6 +75,21 @@ STATISTIC(NumFastIselSuccessTarget, "Number of insts selected by " "target-specific selector"); STATISTIC(NumFastIselDead, "Number of dead insts removed on failure"); +/// \brief Set CallLoweringInfo attribute flags based on a call instruction +/// and called function attributes. +void FastISel::ArgListEntry::setAttributes(ImmutableCallSite *CS, + unsigned AttrIdx) { + isSExt = CS->paramHasAttr(AttrIdx, Attribute::SExt); + isZExt = CS->paramHasAttr(AttrIdx, Attribute::ZExt); + isInReg = CS->paramHasAttr(AttrIdx, Attribute::InReg); + isSRet = CS->paramHasAttr(AttrIdx, Attribute::StructRet); + isNest = CS->paramHasAttr(AttrIdx, Attribute::Nest); + isByVal = CS->paramHasAttr(AttrIdx, Attribute::ByVal); + isInAlloca = CS->paramHasAttr(AttrIdx, Attribute::InAlloca); + isReturned = CS->paramHasAttr(AttrIdx, Attribute::Returned); + Alignment = CS->getParamAlignment(AttrIdx); +} + /// startNewBlock - Set the current block to which generated machine /// instructions will be appended, and clear the local CSE map. /// @@ -662,6 +678,193 @@ bool FastISel::SelectStackmap(const CallInst *I) { return true; } +/// Returns an AttributeSet representing the attributes applied to the return +/// value of the given call. +static AttributeSet getReturnAttrs(FastISel::CallLoweringInfo &CLI) { + SmallVector Attrs; + if (CLI.RetSExt) + Attrs.push_back(Attribute::SExt); + if (CLI.RetZExt) + Attrs.push_back(Attribute::ZExt); + if (CLI.IsInReg) + Attrs.push_back(Attribute::InReg); + + return AttributeSet::get(CLI.RetTy->getContext(), AttributeSet::ReturnIndex, + Attrs); +} + +bool FastISel::LowerCallTo(const CallInst *CI, const char *SymName, + unsigned NumArgs) { + ImmutableCallSite CS(CI); + + PointerType *PT = cast(CS.getCalledValue()->getType()); + FunctionType *FTy = cast(PT->getElementType()); + Type *RetTy = FTy->getReturnType(); + + ArgListTy Args; + Args.reserve(NumArgs); + + // Populate the argument list. + // Attributes for args start at offset 1, after the return attribute. + for (unsigned ArgI = 0; ArgI != NumArgs; ++ArgI) { + Value *V = CI->getOperand(ArgI); + + assert(!V->getType()->isEmptyTy() && "Empty type passed to intrinsic."); + + ArgListEntry Entry; + Entry.Val = V; + Entry.Ty = V->getType(); + Entry.setAttributes(&CS, ArgI + 1); + Args.push_back(Entry); + } + + CallLoweringInfo CLI; + CLI.setCallee(RetTy, FTy, SymName, std::move(Args), CS, NumArgs); + + return LowerCallTo(CLI); +} + +bool FastISel::LowerCallTo(CallLoweringInfo &CLI) { + // Handle the incoming return values from the call. + CLI.clearIns(); + SmallVector RetTys; + ComputeValueVTs(TLI, CLI.RetTy, RetTys); + + SmallVector Outs; + GetReturnInfo(CLI.RetTy, getReturnAttrs(CLI), Outs, TLI); + + bool CanLowerReturn = TLI.CanLowerReturn(CLI.CallConv, *FuncInfo.MF, + CLI.IsVarArg, Outs, + CLI.RetTy->getContext()); + + // FIXME: sret demotion isn't supported yet - bail out. + if (!CanLowerReturn) + return false; + + for (unsigned I = 0, E = RetTys.size(); I != E; ++I) { + EVT VT = RetTys[I]; + MVT RegisterVT = TLI.getRegisterType(CLI.RetTy->getContext(), VT); + unsigned NumRegs = TLI.getNumRegisters(CLI.RetTy->getContext(), VT); + for (unsigned i = 0; i != NumRegs; ++i) { + ISD::InputArg MyFlags; + MyFlags.VT = RegisterVT; + MyFlags.ArgVT = VT; + MyFlags.Used = CLI.IsReturnValueUsed; + if (CLI.RetSExt) + MyFlags.Flags.setSExt(); + if (CLI.RetZExt) + MyFlags.Flags.setZExt(); + if (CLI.IsInReg) + MyFlags.Flags.setInReg(); + CLI.Ins.push_back(MyFlags); + } + } + + // Handle all of the outgoing arguments. + CLI.clearOuts(); + for (auto &Arg : CLI.getArgs()) { + Type *FinalType = Arg.Ty; + if (Arg.isByVal) + FinalType = cast(Arg.Ty)->getElementType(); + bool NeedsRegBlock = TLI.functionArgumentNeedsConsecutiveRegisters( + FinalType, CLI.CallConv, CLI.IsVarArg); + + ISD::ArgFlagsTy Flags; + if (Arg.isZExt) + Flags.setZExt(); + if (Arg.isSExt) + Flags.setSExt(); + if (Arg.isInReg) + Flags.setInReg(); + if (Arg.isSRet) + Flags.setSRet(); + if (Arg.isByVal) + Flags.setByVal(); + if (Arg.isInAlloca) { + Flags.setInAlloca(); + // Set the byval flag for CCAssignFn callbacks that don't know about + // inalloca. This way we can know how many bytes we should've allocated + // and how many bytes a callee cleanup function will pop. If we port + // inalloca to more targets, we'll have to add custom inalloca handling in + // the various CC lowering callbacks. + Flags.setByVal(); + } + if (Arg.isByVal || Arg.isInAlloca) { + PointerType *Ty = cast(Arg.Ty); + Type *ElementTy = Ty->getElementType(); + unsigned FrameSize = DL.getTypeAllocSize(ElementTy); + // For ByVal, alignment should come from FE. BE will guess if this info is + // not there, but there are cases it cannot get right. + unsigned FrameAlign = Arg.Alignment; + if (!FrameAlign) + FrameAlign = TLI.getByValTypeAlignment(ElementTy); + Flags.setByValSize(FrameSize); + Flags.setByValAlign(FrameAlign); + } + if (Arg.isNest) + Flags.setNest(); + if (NeedsRegBlock) + Flags.setInConsecutiveRegs(); + unsigned OriginalAlignment = DL.getABITypeAlignment(Arg.Ty); + Flags.setOrigAlign(OriginalAlignment); + + CLI.OutVals.push_back(Arg.Val); + CLI.OutFlags.push_back(Flags); + } + + if (!FastLowerCall(CLI)) + return false; + + // Set all unused physreg defs as dead. + assert(CLI.Call && "No call instruction specified."); + CLI.Call->setPhysRegsDeadExcept(CLI.InRegs, TRI); + + if (CLI.NumResultRegs && CLI.CS) + UpdateValueMap(CLI.CS->getInstruction(), CLI.ResultReg, CLI.NumResultRegs); + + return true; +} + +bool FastISel::LowerCall(const CallInst *CI) { + ImmutableCallSite CS(CI); + + PointerType *PT = cast(CS.getCalledValue()->getType()); + FunctionType *FuncTy = cast(PT->getElementType()); + Type *RetTy = FuncTy->getReturnType(); + + ArgListTy Args; + ArgListEntry Entry; + Args.reserve(CS.arg_size()); + + for (ImmutableCallSite::arg_iterator i = CS.arg_begin(), e = CS.arg_end(); + i != e; ++i) { + Value *V = *i; + + // Skip empty types + if (V->getType()->isEmptyTy()) + continue; + + Entry.Val = V; + Entry.Ty = V->getType(); + + // Skip the first return-type Attribute to get to params. + Entry.setAttributes(&CS, i - CS.arg_begin() + 1); + Args.push_back(Entry); + } + + // Check if target-independent constraints permit a tail call here. + // Target-dependent constraints are checked within FastLowerCall. + bool IsTailCall = CI->isTailCall(); + if (IsTailCall && !isInTailCallPosition(CS, TM, TLI)) + IsTailCall = false; + + CallLoweringInfo CLI; + CLI.setCallee(RetTy, FuncTy, CI->getCalledValue(), std::move(Args), CS) + .setTailCall(IsTailCall); + + return LowerCallTo(CLI); +} + bool FastISel::SelectCall(const User *I) { const CallInst *Call = cast(I); @@ -700,8 +903,7 @@ bool FastISel::SelectCall(const User *I) { // since they tend to be inlined. flushLocalValueMap(); - // An arbitrary call. Bail. - return false; + return LowerCall(Call); } bool FastISel::SelectIntrinsicCall(const IntrinsicInst *II) { @@ -1229,6 +1431,10 @@ bool FastISel::FastLowerArguments() { return false; } +bool FastISel::FastLowerCall(CallLoweringInfo &/*CLI*/) { + return false; +} + bool FastISel::FastLowerIntrinsicCall(const IntrinsicInst */*II*/) { return false; }