mirror of
https://github.com/RPCSX/llvm.git
synced 2024-12-03 17:31:50 +00:00
[FastISel] Add basic infrastructure to support a target-independent call lowering hook in FastISel. WIP
The infrastructure mimics the call lowering we have already in place for SelectionDAG, but with limitations. For example structure return demotion and non-simple types are not supported (yet). Currently every backend has its own implementation and duplicated code for call lowering. There is also no specified interface that could be called from target-independent code. The target-hook is opt-in and doesn't affect current implementations. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@212848 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
af6721bf06
commit
324b3fc7c8
@ -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<ArgListEntry> 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<Value *, 16> OutVals;
|
||||
SmallVector<ISD::ArgFlagsTy, 16> OutFlags;
|
||||
SmallVector<unsigned, 16> OutRegs;
|
||||
SmallVector<ISD::InputArg, 4> Ins;
|
||||
SmallVector<unsigned, 4> 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<const Value *, unsigned> 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);
|
||||
|
@ -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<Attribute::AttrKind, 2> 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<PointerType>(CS.getCalledValue()->getType());
|
||||
FunctionType *FTy = cast<FunctionType>(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<EVT, 4> RetTys;
|
||||
ComputeValueVTs(TLI, CLI.RetTy, RetTys);
|
||||
|
||||
SmallVector<ISD::OutputArg, 4> 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<PointerType>(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<PointerType>(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<PointerType>(CS.getCalledValue()->getType());
|
||||
FunctionType *FuncTy = cast<FunctionType>(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<CallInst>(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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user