forked from OSchip/llvm-project
Add an abstraction to handle patchpoint operands.
Hard-coded operand indices were scattered throughout lowering stages and layers. It was super bug prone. llvm-svn: 195093
This commit is contained in:
parent
05c5a93283
commit
d4e3dc6d14
|
@ -20,6 +20,60 @@ namespace llvm {
|
||||||
class AsmPrinter;
|
class AsmPrinter;
|
||||||
class MCExpr;
|
class MCExpr;
|
||||||
|
|
||||||
|
/// \brief MI-level patchpoint operands.
|
||||||
|
///
|
||||||
|
/// MI patchpoint operations take the form:
|
||||||
|
/// [<def>], <id>, <numBytes>, <target>, <numArgs>, <cc>, ...
|
||||||
|
///
|
||||||
|
/// Note that IR/SD patchpoints do not have the <def> or <cc> operands.
|
||||||
|
///
|
||||||
|
/// Patchpoints following the anyregcc convention are handled specially. For
|
||||||
|
/// these, the stack map also records the location of the return value and
|
||||||
|
/// arguments.
|
||||||
|
class PatchPointOpers {
|
||||||
|
public:
|
||||||
|
/// Enumerate the meta operands.
|
||||||
|
enum { IDPos, NBytesPos, TargetPos, NArgPos, CCPos, MetaEnd };
|
||||||
|
private:
|
||||||
|
const MachineInstr *MI;
|
||||||
|
bool HasDef;
|
||||||
|
bool IsAnyReg;
|
||||||
|
public:
|
||||||
|
explicit PatchPointOpers(const MachineInstr *MI);
|
||||||
|
|
||||||
|
bool isAnyReg() const { return IsAnyReg; }
|
||||||
|
bool hasDef() const { return HasDef; }
|
||||||
|
|
||||||
|
unsigned getMetaIdx(unsigned Pos = 0) const {
|
||||||
|
assert(Pos < MetaEnd && "Meta operand index out of range.");
|
||||||
|
return (HasDef ? 1 : 0) + Pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
const MachineOperand &getMetaOper(unsigned Pos) {
|
||||||
|
return MI->getOperand(getMetaIdx(Pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned getArgIdx() const { return getMetaIdx() + MetaEnd; }
|
||||||
|
|
||||||
|
/// Get the operand index of the variable list of non-argument operands.
|
||||||
|
/// These hold the "live state".
|
||||||
|
unsigned getVarIdx() const {
|
||||||
|
return getMetaIdx() + MetaEnd
|
||||||
|
+ MI->getOperand(getMetaIdx(NArgPos)).getImm();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the index at which stack map locations will be recorded.
|
||||||
|
/// Arguments are not recorded unless the anyregcc convention is used.
|
||||||
|
unsigned getStackMapStartIdx() const {
|
||||||
|
if (IsAnyReg)
|
||||||
|
return getArgIdx();
|
||||||
|
return getVarIdx();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \brief Get the next scratch register operand index.
|
||||||
|
unsigned getNextScratchIdx(unsigned StartIdx = 0) const;
|
||||||
|
};
|
||||||
|
|
||||||
class StackMaps {
|
class StackMaps {
|
||||||
public:
|
public:
|
||||||
struct Location {
|
struct Location {
|
||||||
|
@ -48,15 +102,13 @@ public:
|
||||||
StackMaps(AsmPrinter &AP, OperandParser OpParser)
|
StackMaps(AsmPrinter &AP, OperandParser OpParser)
|
||||||
: AP(AP), OpParser(OpParser) {}
|
: AP(AP), OpParser(OpParser) {}
|
||||||
|
|
||||||
/// This should be called by the MC lowering code _immediately_ before
|
/// \brief Generate a stackmap record for a stackmap instruction.
|
||||||
/// lowering the MI to an MCInst. It records where the operands for the
|
///
|
||||||
/// instruction are stored, and outputs a label to record the offset of
|
/// MI must be a raw STACKMAP, not a PATCHPOINT.
|
||||||
/// the call from the start of the text section. In special cases (e.g. AnyReg
|
void recordStackMap(const MachineInstr &MI);
|
||||||
/// calling convention) the return register is also recorded if requested.
|
|
||||||
void recordStackMap(const MachineInstr &MI, uint32_t ID,
|
/// \brief Generate a stackmap record for a patchpoint instruction.
|
||||||
MachineInstr::const_mop_iterator MOI,
|
void recordPatchPoint(const MachineInstr &MI);
|
||||||
MachineInstr::const_mop_iterator MOE,
|
|
||||||
bool recordResult = false);
|
|
||||||
|
|
||||||
/// If there is any stack map data, create a stack map section and serialize
|
/// If there is any stack map data, create a stack map section and serialize
|
||||||
/// the map info into it. This clears the stack map data structures
|
/// the map info into it. This clears the stack map data structures
|
||||||
|
@ -64,7 +116,6 @@ public:
|
||||||
void serializeToStackMapSection();
|
void serializeToStackMapSection();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
typedef SmallVector<Location, 8> LocationVec;
|
typedef SmallVector<Location, 8> LocationVec;
|
||||||
|
|
||||||
struct CallsiteInfo {
|
struct CallsiteInfo {
|
||||||
|
@ -103,6 +154,16 @@ private:
|
||||||
OperandParser OpParser;
|
OperandParser OpParser;
|
||||||
CallsiteInfoList CSInfos;
|
CallsiteInfoList CSInfos;
|
||||||
ConstantPool ConstPool;
|
ConstantPool ConstPool;
|
||||||
|
|
||||||
|
/// This should be called by the MC lowering code _immediately_ before
|
||||||
|
/// lowering the MI to an MCInst. It records where the operands for the
|
||||||
|
/// instruction are stored, and outputs a label to record the offset of
|
||||||
|
/// the call from the start of the text section. In special cases (e.g. AnyReg
|
||||||
|
/// calling convention) the return register is also recorded if requested.
|
||||||
|
void recordStackMapOpers(const MachineInstr &MI, uint32_t ID,
|
||||||
|
MachineInstr::const_mop_iterator MOI,
|
||||||
|
MachineInstr::const_mop_iterator MOE,
|
||||||
|
bool recordResult = false);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,44 @@
|
||||||
|
|
||||||
using namespace llvm;
|
using namespace llvm;
|
||||||
|
|
||||||
void StackMaps::recordStackMap(const MachineInstr &MI, uint32_t ID,
|
PatchPointOpers::PatchPointOpers(const MachineInstr *MI):
|
||||||
|
MI(MI),
|
||||||
|
HasDef(MI->getOperand(0).isReg() && MI->getOperand(0).isDef() &&
|
||||||
|
!MI->getOperand(0).isImplicit()),
|
||||||
|
IsAnyReg(MI->getOperand(getMetaIdx(CCPos)).getImm() == CallingConv::AnyReg) {
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
{
|
||||||
|
unsigned CheckStartIdx = 0, e = MI->getNumOperands();
|
||||||
|
while (CheckStartIdx < e && MI->getOperand(CheckStartIdx).isReg() &&
|
||||||
|
MI->getOperand(CheckStartIdx).isDef() &&
|
||||||
|
!MI->getOperand(CheckStartIdx).isImplicit())
|
||||||
|
++CheckStartIdx;
|
||||||
|
|
||||||
|
assert(getMetaIdx() == CheckStartIdx &&
|
||||||
|
"Unexpected additonal definition in Patchpoint intrinsic.");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned PatchPointOpers::getNextScratchIdx(unsigned StartIdx) const {
|
||||||
|
if (!StartIdx)
|
||||||
|
StartIdx = getVarIdx();
|
||||||
|
|
||||||
|
// Find the next scratch register (implicit def and early clobber)
|
||||||
|
unsigned ScratchIdx = StartIdx, e = MI->getNumOperands();
|
||||||
|
while (ScratchIdx < e &&
|
||||||
|
!(MI->getOperand(ScratchIdx).isReg() &&
|
||||||
|
MI->getOperand(ScratchIdx).isDef() &&
|
||||||
|
MI->getOperand(ScratchIdx).isImplicit() &&
|
||||||
|
MI->getOperand(ScratchIdx).isEarlyClobber()))
|
||||||
|
++ScratchIdx;
|
||||||
|
|
||||||
|
assert(ScratchIdx != e && "No scratch register available");
|
||||||
|
return ScratchIdx;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StackMaps::recordStackMapOpers(const MachineInstr &MI, uint32_t ID,
|
||||||
MachineInstr::const_mop_iterator MOI,
|
MachineInstr::const_mop_iterator MOI,
|
||||||
MachineInstr::const_mop_iterator MOE,
|
MachineInstr::const_mop_iterator MOE,
|
||||||
bool recordResult) {
|
bool recordResult) {
|
||||||
|
@ -73,6 +110,49 @@ void StackMaps::recordStackMap(const MachineInstr &MI, uint32_t ID,
|
||||||
CSInfos.push_back(CallsiteInfo(CSOffsetExpr, ID, CallsiteLocs));
|
CSInfos.push_back(CallsiteInfo(CSOffsetExpr, ID, CallsiteLocs));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static MachineInstr::const_mop_iterator
|
||||||
|
getStackMapEndMOP(MachineInstr::const_mop_iterator MOI,
|
||||||
|
MachineInstr::const_mop_iterator MOE) {
|
||||||
|
for (; MOI != MOE; ++MOI)
|
||||||
|
if (MOI->isRegMask() || (MOI->isReg() && MOI->isImplicit()))
|
||||||
|
break;
|
||||||
|
|
||||||
|
return MOI;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StackMaps::recordStackMap(const MachineInstr &MI) {
|
||||||
|
assert(MI.getOpcode() == TargetOpcode::STACKMAP && "exected stackmap");
|
||||||
|
|
||||||
|
int64_t ID = MI.getOperand(0).getImm();
|
||||||
|
assert((int32_t)ID == ID && "Stack maps hold 32-bit IDs");
|
||||||
|
recordStackMapOpers(MI, ID, llvm::next(MI.operands_begin(), 2),
|
||||||
|
getStackMapEndMOP(MI.operands_begin(),
|
||||||
|
MI.operands_end()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void StackMaps::recordPatchPoint(const MachineInstr &MI) {
|
||||||
|
assert(MI.getOpcode() == TargetOpcode::PATCHPOINT && "exected stackmap");
|
||||||
|
|
||||||
|
PatchPointOpers opers(&MI);
|
||||||
|
int64_t ID = opers.getMetaOper(PatchPointOpers::IDPos).getImm();
|
||||||
|
assert((int32_t)ID == ID && "Stack maps hold 32-bit IDs");
|
||||||
|
MachineInstr::const_mop_iterator MOI =
|
||||||
|
llvm::next(MI.operands_begin(), opers.getStackMapStartIdx());
|
||||||
|
recordStackMapOpers(MI, ID, MOI, getStackMapEndMOP(MOI, MI.operands_end()),
|
||||||
|
opers.isAnyReg() && opers.hasDef());
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
// verify anyregcc
|
||||||
|
LocationVec &Locations = CSInfos.back().Locations;
|
||||||
|
if (opers.isAnyReg()) {
|
||||||
|
unsigned NArgs = opers.getMetaOper(PatchPointOpers::NArgPos).getImm();
|
||||||
|
for (unsigned i = 0, e = (opers.hasDef() ? NArgs+1 : NArgs); i != e; ++i)
|
||||||
|
assert(Locations[i].LocType == Location::Register &&
|
||||||
|
"anyreg arg must be in reg.");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
/// serializeToStackMapSection conceptually populates the following fields:
|
/// serializeToStackMapSection conceptually populates the following fields:
|
||||||
///
|
///
|
||||||
/// uint32 : Reserved (header)
|
/// uint32 : Reserved (header)
|
||||||
|
|
|
@ -716,6 +716,9 @@ X86AsmPrinter::stackmapOperandParser(MachineInstr::const_mop_iterator MOI,
|
||||||
"Register mask and implicit operands should not be processed.");
|
"Register mask and implicit operands should not be processed.");
|
||||||
|
|
||||||
if (MOP.isImm()) {
|
if (MOP.isImm()) {
|
||||||
|
// Verify anyregcc
|
||||||
|
// [<def>], <id>, <numBytes>, <target>, <numArgs>, <cc>, ...
|
||||||
|
|
||||||
switch (MOP.getImm()) {
|
switch (MOP.getImm()) {
|
||||||
default: llvm_unreachable("Unrecognized operand type.");
|
default: llvm_unreachable("Unrecognized operand type.");
|
||||||
case StackMaps::DirectMemRefOp: {
|
case StackMaps::DirectMemRefOp: {
|
||||||
|
@ -756,82 +759,33 @@ X86AsmPrinter::stackmapOperandParser(MachineInstr::const_mop_iterator MOI,
|
||||||
Location(Location::Register, RC->getSize(), MOP.getReg(), 0), ++MOI);
|
Location(Location::Register, RC->getSize(), MOP.getReg(), 0), ++MOI);
|
||||||
}
|
}
|
||||||
|
|
||||||
static MachineInstr::const_mop_iterator
|
// Lower a stackmap of the form:
|
||||||
getStackMapEndMOP(MachineInstr::const_mop_iterator MOI,
|
// <id>, <shadowBytes>, ...
|
||||||
MachineInstr::const_mop_iterator MOE) {
|
|
||||||
for (; MOI != MOE; ++MOI)
|
|
||||||
if (MOI->isRegMask() || (MOI->isReg() && MOI->isImplicit()))
|
|
||||||
break;
|
|
||||||
|
|
||||||
return MOI;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void LowerSTACKMAP(MCStreamer &OutStreamer,
|
static void LowerSTACKMAP(MCStreamer &OutStreamer,
|
||||||
StackMaps &SM,
|
StackMaps &SM,
|
||||||
const MachineInstr &MI)
|
const MachineInstr &MI)
|
||||||
{
|
{
|
||||||
int64_t ID = MI.getOperand(0).getImm();
|
|
||||||
unsigned NumNOPBytes = MI.getOperand(1).getImm();
|
unsigned NumNOPBytes = MI.getOperand(1).getImm();
|
||||||
|
SM.recordStackMap(MI);
|
||||||
assert((int32_t)ID == ID && "Stack maps hold 32-bit IDs");
|
|
||||||
SM.recordStackMap(MI, ID, llvm::next(MI.operands_begin(), 2),
|
|
||||||
getStackMapEndMOP(MI.operands_begin(), MI.operands_end()));
|
|
||||||
// Emit padding.
|
// Emit padding.
|
||||||
|
// FIXME: These nops ensure that the stackmap's shadow is covered by
|
||||||
|
// instructions from the same basic block, but the nops should not be
|
||||||
|
// necessary if instructions from the same block follow the stackmap.
|
||||||
for (unsigned i = 0; i < NumNOPBytes; ++i)
|
for (unsigned i = 0; i < NumNOPBytes; ++i)
|
||||||
OutStreamer.EmitInstruction(MCInstBuilder(X86::NOOP));
|
OutStreamer.EmitInstruction(MCInstBuilder(X86::NOOP));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lower a patchpoint of the form:
|
// Lower a patchpoint of the form:
|
||||||
// [<def>], <id>, <numBytes>, <target>, <numArgs>
|
// [<def>], <id>, <numBytes>, <target>, <numArgs>, <cc>, ...
|
||||||
static void LowerPATCHPOINT(MCStreamer &OutStreamer,
|
static void LowerPATCHPOINT(MCStreamer &OutStreamer,
|
||||||
StackMaps &SM,
|
StackMaps &SM,
|
||||||
const MachineInstr &MI) {
|
const MachineInstr &MI) {
|
||||||
bool hasDef = MI.getOperand(0).isReg() && MI.getOperand(0).isDef() &&
|
SM.recordPatchPoint(MI);
|
||||||
!MI.getOperand(0).isImplicit();
|
|
||||||
unsigned StartIdx = hasDef ? 1 : 0;
|
|
||||||
#ifndef NDEBUG
|
|
||||||
{
|
|
||||||
unsigned StartIdx2 = 0, e = MI.getNumOperands();
|
|
||||||
while (StartIdx2 < e && MI.getOperand(StartIdx2).isReg() &&
|
|
||||||
MI.getOperand(StartIdx2).isDef() &&
|
|
||||||
!MI.getOperand(StartIdx2).isImplicit())
|
|
||||||
++StartIdx2;
|
|
||||||
|
|
||||||
assert(StartIdx == StartIdx2 &&
|
|
||||||
"Unexpected additonal definition in Patchpoint intrinsic.");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Find the first scratch register (implicit def and early clobber)
|
|
||||||
unsigned ScratchIdx = StartIdx, e = MI.getNumOperands();
|
|
||||||
while (ScratchIdx < e &&
|
|
||||||
!(MI.getOperand(ScratchIdx).isReg() &&
|
|
||||||
MI.getOperand(ScratchIdx).isDef() &&
|
|
||||||
MI.getOperand(ScratchIdx).isImplicit() &&
|
|
||||||
MI.getOperand(ScratchIdx).isEarlyClobber()))
|
|
||||||
++ScratchIdx;
|
|
||||||
|
|
||||||
assert(ScratchIdx != e && "No scratch register available");
|
|
||||||
|
|
||||||
int64_t ID = MI.getOperand(StartIdx).getImm();
|
|
||||||
assert((int32_t)ID == ID && "Stack maps hold 32-bit IDs");
|
|
||||||
|
|
||||||
// Get the number of arguments participating in the call. This number was
|
|
||||||
// adjusted during call lowering by subtracting stack args.
|
|
||||||
bool isAnyRegCC = MI.getOperand(StartIdx + 4).getImm() == CallingConv::AnyReg;
|
|
||||||
assert(((hasDef && isAnyRegCC) || !hasDef) &&
|
|
||||||
"Only Patchpoints with AnyReg calling convention may have a result");
|
|
||||||
int64_t StackMapIdx = isAnyRegCC ? StartIdx + 5 :
|
|
||||||
StartIdx + 5 + MI.getOperand(StartIdx + 3).getImm();
|
|
||||||
assert(StackMapIdx <= MI.getNumOperands() &&
|
|
||||||
"Patchpoint intrinsic dropped arguments.");
|
|
||||||
|
|
||||||
SM.recordStackMap(MI, ID, llvm::next(MI.operands_begin(), StackMapIdx),
|
|
||||||
getStackMapEndMOP(MI.operands_begin(), MI.operands_end()),
|
|
||||||
isAnyRegCC && hasDef);
|
|
||||||
|
|
||||||
|
PatchPointOpers opers(&MI);
|
||||||
|
unsigned ScratchIdx = opers.getNextScratchIdx();
|
||||||
unsigned EncodedBytes = 0;
|
unsigned EncodedBytes = 0;
|
||||||
int64_t CallTarget = MI.getOperand(StartIdx + 2).getImm();
|
int64_t CallTarget = opers.getMetaOper(PatchPointOpers::TargetPos).getImm();
|
||||||
if (CallTarget) {
|
if (CallTarget) {
|
||||||
// Emit MOV to materialize the target address and the CALL to target.
|
// Emit MOV to materialize the target address and the CALL to target.
|
||||||
// This is encoded with 12-13 bytes, depending on which register is used.
|
// This is encoded with 12-13 bytes, depending on which register is used.
|
||||||
|
@ -845,11 +799,11 @@ static void LowerPATCHPOINT(MCStreamer &OutStreamer,
|
||||||
.addReg(MI.getOperand(ScratchIdx).getReg()));
|
.addReg(MI.getOperand(ScratchIdx).getReg()));
|
||||||
}
|
}
|
||||||
// Emit padding.
|
// Emit padding.
|
||||||
unsigned NumNOPBytes = MI.getOperand(StartIdx + 1).getImm();
|
unsigned NumBytes = opers.getMetaOper(PatchPointOpers::NBytesPos).getImm();
|
||||||
assert(NumNOPBytes >= EncodedBytes &&
|
assert(NumBytes >= EncodedBytes &&
|
||||||
"Patchpoint can't request size less than the length of a call.");
|
"Patchpoint can't request size less than the length of a call.");
|
||||||
|
|
||||||
for (unsigned i = EncodedBytes; i < NumNOPBytes; ++i)
|
for (unsigned i = EncodedBytes; i < NumBytes; ++i)
|
||||||
OutStreamer.EmitInstruction(MCInstBuilder(X86::NOOP));
|
OutStreamer.EmitInstruction(MCInstBuilder(X86::NOOP));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue