Liveness Analysis Pass

llvm-svn: 197254
This commit is contained in:
Andrew Trick 2013-12-13 18:37:03 +00:00
parent 8df84fa2f2
commit 8d6a658430
17 changed files with 514 additions and 78 deletions

View File

@ -145,6 +145,10 @@ class MachineFrameInfo {
/// to builtin \@llvm.returnaddress.
bool ReturnAddressTaken;
/// HasStackmap - This boolean keeps track of whether there is a call
/// to builtin \@llvm.experimental.stackmap or \@llvm.experimental.patchpoint.
bool HasStackMap;
/// StackSize - The prolog/epilog code inserter calculates the final stack
/// offsets for all of the fixed size objects, updating the Objects list
/// above. It then updates StackSize to contain the number of bytes that need
@ -235,6 +239,7 @@ public:
HasVarSizedObjects = false;
FrameAddressTaken = false;
ReturnAddressTaken = false;
HasStackMap = false;
AdjustsStack = false;
HasCalls = false;
StackProtectorIdx = -1;
@ -280,6 +285,12 @@ public:
bool isReturnAddressTaken() const { return ReturnAddressTaken; }
void setReturnAddressIsTaken(bool s) { ReturnAddressTaken = s; }
/// hasStackMap - This method may be called any time after instruction
/// selection is complete to determine if there is a call to builtin
/// \@llvm.experimental.stackmap or \@llvm.experimental.patchpoint.
bool hasStackMap() const { return HasStackMap; }
void setHasStackMap(bool s = true) { HasStackMap = s; }
/// getObjectIndexBegin - Return the minimum frame object index.
///
int getObjectIndexBegin() const { return -NumFixedObjects; }

View File

@ -426,6 +426,15 @@ public:
OperandRecycler.deallocate(Cap, Array);
}
/// \brief Allocate and initialize a register mask with @p NumRegister bits.
uint32_t *allocateRegisterMask(unsigned NumRegister) {
unsigned Size = (NumRegister + 31) / 32;
uint32_t *Mask = Allocator.Allocate<uint32_t>(Size);
for (unsigned i = 0; i != Size; ++i)
Mask[i] = 0;
return Mask;
}
/// allocateMemRefsArray - Allocate an array to hold MachineMemOperand
/// pointers. This array is owned by the MachineFunction.
MachineInstr::mmo_iterator allocateMemRefsArray(unsigned long Num);

View File

@ -56,6 +56,7 @@ public:
MO_GlobalAddress, ///< Address of a global value
MO_BlockAddress, ///< Address of a basic block
MO_RegisterMask, ///< Mask of preserved registers.
MO_RegisterLiveOut, ///< Mask of live-out registers.
MO_Metadata, ///< Metadata reference (for debug info)
MO_MCSymbol ///< MCSymbol reference (for debug/eh info)
};
@ -153,7 +154,7 @@ private:
const ConstantFP *CFP; // For MO_FPImmediate.
const ConstantInt *CI; // For MO_CImmediate. Integers > 64bit.
int64_t ImmVal; // For MO_Immediate.
const uint32_t *RegMask; // For MO_RegisterMask.
const uint32_t *RegMask; // For MO_RegisterMask and MO_RegisterLiveOut.
const MDNode *MD; // For MO_Metadata.
MCSymbol *Sym; // For MO_MCSymbol
@ -246,6 +247,8 @@ public:
bool isBlockAddress() const { return OpKind == MO_BlockAddress; }
/// isRegMask - Tests if this is a MO_RegisterMask operand.
bool isRegMask() const { return OpKind == MO_RegisterMask; }
/// isRegLiveOut - Tests if this is a MO_RegisterLiveOut operand.
bool isRegLiveOut() const { return OpKind == MO_RegisterLiveOut; }
/// isMetadata - Tests if this is a MO_Metadata operand.
bool isMetadata() const { return OpKind == MO_Metadata; }
bool isMCSymbol() const { return OpKind == MO_MCSymbol; }
@ -476,6 +479,12 @@ public:
return Contents.RegMask;
}
/// getRegLiveOut - Returns a bit mask of live-out registers.
const uint32_t *getRegLiveOut() const {
assert(isRegLiveOut() && "Wrong MachineOperand accessor");
return Contents.RegMask;
}
const MDNode *getMetadata() const {
assert(isMetadata() && "Wrong MachineOperand accessor");
return Contents.MD;
@ -659,6 +668,12 @@ public:
Op.Contents.RegMask = Mask;
return Op;
}
static MachineOperand CreateRegLiveOut(const uint32_t *Mask) {
assert(Mask && "Missing live-out register mask");
MachineOperand Op(MachineOperand::MO_RegisterLiveOut);
Op.Contents.RegMask = Mask;
return Op;
}
static MachineOperand CreateMetadata(const MDNode *Meta) {
MachineOperand Op(MachineOperand::MO_Metadata);
Op.Contents.MD = Meta;

View File

@ -568,6 +568,11 @@ namespace llvm {
/// bundles (created earlier, e.g. during pre-RA scheduling).
extern char &FinalizeMachineBundlesID;
/// StackMapLiveness - This pass analyses the register live-out set of
/// stackmap/patchpoint intrinsics and attaches the calculated information to
/// the intrinsic for later emission to the StackMap.
extern char &StackMapLivenessID;
} // End llvm namespace
#endif

View File

@ -0,0 +1,67 @@
//===--- StackMapLivenessAnalysis - StackMap Liveness Analysis --*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This pass calculates the liveness for each basic block in a function and
// attaches the register live-out information to a stackmap or patchpoint
// intrinsic if present.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_STACKMAP_LIVENESS_ANALYSIS_H
#define LLVM_CODEGEN_STACKMAP_LIVENESS_ANALYSIS_H
#include "llvm/CodeGen/LivePhysRegs.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
namespace llvm {
/// \brief This pass calculates the liveness information for each basic block in
/// a function and attaches the register live-out information to a stackmap or
/// patchpoint intrinsic if present.
///
/// This is an optional pass that has to be explicitely enabled via the
/// -enable-stackmap-liveness flag. The pass skips functions that don't have any
/// stackmap or patchpoint intrinsics. The information provided by this pass is
/// optional and not required by the aformentioned intrinsics to function.
class StackMapLiveness : public MachineFunctionPass {
MachineFunction *MF;
const TargetRegisterInfo *TRI;
LivePhysRegs LiveRegs;
public:
static char ID;
/// \brief Default construct and initialize the pass.
StackMapLiveness();
/// \brief Tell the pass manager which passes we depend on and what
/// information we preserve.
virtual void getAnalysisUsage(AnalysisUsage &AU) const;
/// \brief Calculate the liveness information for the given machine function.
virtual bool runOnMachineFunction(MachineFunction &MF);
private:
/// \brief Performs the actual liveness calculation for the function.
bool calculateLiveness();
/// \brief Add the current register live set to the instruction.
void addLiveOutSetToMI(MachineInstr &MI);
/// \brief Create a register mask and initialize it with the registers from
/// the register live set.
uint32_t *createRegisterMask() const;
/// \brief Print the current register live set for debugging.
void printLiveOutSet(raw_ostream &OS) const;
};
} // end llvm namespace
#endif // end LLVM_CODEGEN_STACKMAP_LIVENESS_ANALYSIS_H

View File

@ -92,6 +92,20 @@ public:
: LocType(LocType), Size(Size), Reg(Reg), Offset(Offset) {}
};
struct LiveOutReg {
unsigned short Reg;
unsigned short RegNo;
unsigned short Size;
LiveOutReg() : Reg(0), RegNo(0), Size(0) {}
LiveOutReg(unsigned short Reg, unsigned short RegNo, unsigned short Size)
: Reg(Reg), RegNo(RegNo), Size(Size) {}
// Only sort by the dwarf register number.
bool operator< (const LiveOutReg &LO) const { return RegNo < LO.RegNo; }
static bool isInvalid(const LiveOutReg &LO) { return LO.Reg == 0; }
};
// OpTypes are used to encode information about the following logical
// operand (which may consist of several MachineOperands) for the
// OpParser.
@ -114,15 +128,18 @@ public:
private:
typedef SmallVector<Location, 8> LocationVec;
typedef SmallVector<LiveOutReg, 8> LiveOutVec;
struct CallsiteInfo {
const MCExpr *CSOffsetExpr;
unsigned ID;
LocationVec Locations;
LiveOutVec LiveOuts;
CallsiteInfo() : CSOffsetExpr(0), ID(0) {}
CallsiteInfo(const MCExpr *CSOffsetExpr, unsigned ID,
LocationVec Locations)
: CSOffsetExpr(CSOffsetExpr), ID(ID), Locations(Locations) {}
LocationVec &Locations, LiveOutVec &LiveOuts)
: CSOffsetExpr(CSOffsetExpr), ID(ID), Locations(Locations),
LiveOuts(LiveOuts) {}
};
typedef std::vector<CallsiteInfo> CallsiteInfoList;
@ -154,8 +171,15 @@ private:
/// Parse
std::pair<Location, MachineInstr::const_mop_iterator>
parseOperand(MachineInstr::const_mop_iterator MOI,
MachineInstr::const_mop_iterator MOE);
MachineInstr::const_mop_iterator MOE) const;
/// \brief Create a live-out register record for the given register @p Reg.
LiveOutReg createLiveOutReg(unsigned Reg, const MCRegisterInfo &MCRI,
const TargetRegisterInfo *TRI) const;
/// \brief Parse the register live-out mask and return a vector of live-out
/// registers that need to be recorded in the stackmap.
LiveOutVec parseRegisterLiveOutMask(const uint32_t *Mask) const;
/// This should be called by the MC lowering code _immediately_ before
/// lowering the MI to an MCInst. It records where the operands for the

View File

@ -266,6 +266,7 @@ void initializeLoopVectorizePass(PassRegistry&);
void initializeSLPVectorizerPass(PassRegistry&);
void initializeBBVectorizePass(PassRegistry&);
void initializeMachineFunctionPrinterPassPass(PassRegistry&);
void initializeStackMapLivenessPass(PassRegistry&);
}
#endif

View File

@ -97,6 +97,7 @@ add_llvm_library(LLVMCodeGen
StackColoring.cpp
StackProtector.cpp
StackSlotColoring.cpp
StackMapLivenessAnalysis.cpp
StackMaps.cpp
TailDuplication.cpp
TargetFrameLoweringImpl.cpp

View File

@ -69,6 +69,7 @@ void llvm::initializeCodeGen(PassRegistry &Registry) {
initializeVirtRegRewriterPass(Registry);
initializeLowerIntrinsicsPass(Registry);
initializeMachineFunctionPrinterPassPass(Registry);
initializeStackMapLivenessPass(Registry);
}
void LLVMInitializeCodeGen(LLVMPassRegistryRef R) {

View File

@ -199,7 +199,8 @@ bool MachineOperand::isIdenticalTo(const MachineOperand &Other) const {
case MachineOperand::MO_BlockAddress:
return getBlockAddress() == Other.getBlockAddress() &&
getOffset() == Other.getOffset();
case MO_RegisterMask:
case MachineOperand::MO_RegisterMask:
case MachineOperand::MO_RegisterLiveOut:
return getRegMask() == Other.getRegMask();
case MachineOperand::MO_MCSymbol:
return getMCSymbol() == Other.getMCSymbol();
@ -241,6 +242,7 @@ hash_code llvm::hash_value(const MachineOperand &MO) {
return hash_combine(MO.getType(), MO.getTargetFlags(),
MO.getBlockAddress(), MO.getOffset());
case MachineOperand::MO_RegisterMask:
case MachineOperand::MO_RegisterLiveOut:
return hash_combine(MO.getType(), MO.getTargetFlags(), MO.getRegMask());
case MachineOperand::MO_Metadata:
return hash_combine(MO.getType(), MO.getTargetFlags(), MO.getMetadata());
@ -368,6 +370,9 @@ void MachineOperand::print(raw_ostream &OS, const TargetMachine *TM) const {
case MachineOperand::MO_RegisterMask:
OS << "<regmask>";
break;
case MachineOperand::MO_RegisterLiveOut:
OS << "<regliveout>";
break;
case MachineOperand::MO_Metadata:
OS << '<';
WriteAsOperand(OS, getMetadata(), /*PrintType=*/false);

View File

@ -69,6 +69,8 @@ static cl::opt<bool> DisableCGP("disable-cgp", cl::Hidden,
cl::desc("Disable Codegen Prepare"));
static cl::opt<bool> DisableCopyProp("disable-copyprop", cl::Hidden,
cl::desc("Disable Copy Propagation pass"));
static cl::opt<bool> EnableStackMapLiveness("enable-stackmap-liveness",
cl::Hidden, cl::desc("Enable StackMap Liveness Analysis Pass"));
static cl::opt<bool> PrintLSR("print-lsr-output", cl::Hidden,
cl::desc("Print LLVM IR produced by the loop-reduce pass"));
static cl::opt<bool> PrintISelInput("print-isel-input", cl::Hidden,
@ -536,6 +538,9 @@ void TargetPassConfig::addMachinePasses() {
if (addPreEmitPass())
printAndVerify("After PreEmit passes");
if (EnableStackMapLiveness)
addPass(&StackMapLivenessID);
}
/// Add passes that optimize machine instructions in SSA form.

View File

@ -6886,6 +6886,9 @@ void SelectionDAGBuilder::visitStackmap(const CallInst &CI) {
DAG.ReplaceAllUsesWith(Call, MN);
DAG.DeleteNode(Call);
// Inform the Frame Information that we have a stackmap in this function.
FuncInfo.MF->getFrameInfo()->setHasStackMap();
}
/// \brief Lower llvm.experimental.patchpoint directly to its target opcode.
@ -7025,6 +7028,9 @@ void SelectionDAGBuilder::visitPatchpoint(const CallInst &CI) {
} else
DAG.ReplaceAllUsesWith(Call, MN);
DAG.DeleteNode(Call);
// Inform the Frame Information that we have a stackmap in this function.
FuncInfo.MF->getFrameInfo()->setHasStackMap();
}
/// TargetLowering::LowerCallTo - This is the default LowerCallTo

View File

@ -0,0 +1,128 @@
//===-- StackMapLivenessAnalysis.cpp - StackMap live Out Analysis ----------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements the StackMap Liveness analysis pass. The pass calculates
// the liveness for each basic block in a function and attaches the register
// live-out information to a stackmap or patchpoint intrinsic if present.
//
//===----------------------------------------------------------------------===//
#define DEBUG_TYPE "stackmaps"
#include "llvm/ADT/Statistic.h"
#include "llvm/CodeGen/MachineFrameInfo.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineFunctionAnalysis.h"
#include "llvm/CodeGen/Passes.h"
#include "llvm/CodeGen/StackMapLivenessAnalysis.h"
#include "llvm/Support/Debug.h"
using namespace llvm;
STATISTIC(NumStackMapFuncVisited, "Number of functions visited");
STATISTIC(NumStackMapFuncSkipped, "Number of functions skipped");
STATISTIC(NumBBsVisited, "Number of basic blocks visited");
STATISTIC(NumBBsHaveNoStackmap, "Number of basic blocks with no stackmap");
STATISTIC(NumStackMaps, "Number of StackMaps visited");
char StackMapLiveness::ID = 0;
char &llvm::StackMapLivenessID = StackMapLiveness::ID;
INITIALIZE_PASS(StackMapLiveness, "stackmap-liveness",
"StackMap Liveness Analysis", false, false)
/// Default construct and initialize the pass.
StackMapLiveness::StackMapLiveness() : MachineFunctionPass(ID) {
initializeStackMapLivenessPass(*PassRegistry::getPassRegistry());
}
/// Tell the pass manager which passes we depend on and what information we
/// preserve.
void StackMapLiveness::getAnalysisUsage(AnalysisUsage &AU) const {
// We preserve all information.
AU.setPreservesAll();
AU.setPreservesCFG();
// Default dependencie for all MachineFunction passes.
AU.addRequired<MachineFunctionAnalysis>();
}
/// Calculate the liveness information for the given machine function.
bool StackMapLiveness::runOnMachineFunction(MachineFunction &_MF) {
DEBUG(dbgs() << "********** COMPUTING STACKMAP LIVENESS: "
<< _MF.getName() << " **********\n");
MF = &_MF;
TRI = MF->getTarget().getRegisterInfo();
++NumStackMapFuncVisited;
// Skip this function if there are no stackmaps.
if (!MF->getFrameInfo()->hasStackMap()) {
++NumStackMapFuncSkipped;
return false;
}
return calculateLiveness();
}
/// Performs the actual liveness calculation for the function.
bool StackMapLiveness::calculateLiveness() {
bool HasChanged = false;
// For all basic blocks in the function.
for (MachineFunction::iterator MBBI = MF->begin(), MBBE = MF->end();
MBBI != MBBE; ++MBBI) {
DEBUG(dbgs() << "****** BB " << MBBI->getName() << " ******\n");
LiveRegs.init(TRI);
LiveRegs.addLiveOuts(MBBI);
bool HasStackMap = false;
// Reverse iterate over all instructions and add the current live register
// set to an instruction if we encounter a stackmap or patchpoint
// instruction.
for (MachineBasicBlock::reverse_iterator I = MBBI->rbegin(),
E = MBBI->rend(); I != E; ++I) {
if (I->getOpcode() == TargetOpcode::STACKMAP ||
I->getOpcode() == TargetOpcode::PATCHPOINT) {
addLiveOutSetToMI(*I);
HasChanged = true;
HasStackMap = true;
++NumStackMaps;
}
LiveRegs.stepBackward(*I);
}
++NumBBsVisited;
if (!HasStackMap)
++NumBBsHaveNoStackmap;
}
return HasChanged;
}
/// Add the current register live set to the instruction.
void StackMapLiveness::addLiveOutSetToMI(MachineInstr &MI) {
uint32_t *Mask = createRegisterMask();
MachineOperand MO = MachineOperand::CreateRegLiveOut(Mask);
MI.addOperand(*MF, MO);
DEBUG(dbgs() << " " << MI);
DEBUG(printLiveOutSet(dbgs()));
}
/// Create a register mask and initialize it with the registers from the
/// register live set.
uint32_t *StackMapLiveness::createRegisterMask() const {
// The mask is owned and cleaned up by the Machine Function.
uint32_t *Mask = MF->allocateRegisterMask(TRI->getNumRegs());
for (LivePhysRegs::const_iterator RI = LiveRegs.begin(), RE = LiveRegs.end();
RI != RE; ++RI)
Mask[*RI / 32] |= 1U << (*RI % 32);
return Mask;
}
/// Print the current register live set for debugging.
void StackMapLiveness::printLiveOutSet(raw_ostream &OS) const {
OS << " Register live-out:";
for (LivePhysRegs::const_iterator RI = LiveRegs.begin(), RE = LiveRegs.end();
RI != RE; ++RI)
OS << " " << TRI->getName(*RI);
OS << "\n";
}

View File

@ -68,10 +68,10 @@ unsigned PatchPointOpers::getNextScratchIdx(unsigned StartIdx) const {
std::pair<StackMaps::Location, MachineInstr::const_mop_iterator>
StackMaps::parseOperand(MachineInstr::const_mop_iterator MOI,
MachineInstr::const_mop_iterator MOE) {
MachineInstr::const_mop_iterator MOE) const {
const MachineOperand &MOP = *MOI;
assert(!MOP.isRegMask() && (!MOP.isReg() || !MOP.isImplicit()) &&
"Register mask and implicit operands should not be processed.");
assert((!MOP.isReg() || !MOP.isImplicit()) &&
"Implicit operands should not be processed.");
if (MOP.isImm()) {
// Verify anyregcc
@ -106,6 +106,9 @@ StackMaps::parseOperand(MachineInstr::const_mop_iterator MOI,
}
}
if (MOP.isRegMask() || MOP.isRegLiveOut())
return std::make_pair(Location(), ++MOI);
// Otherwise this is a reg operand. The physical register number will
// ultimately be encoded as a DWARF regno. The stack map also records the size
// of a spill slot that can hold the register content. (The runtime can
@ -120,6 +123,66 @@ StackMaps::parseOperand(MachineInstr::const_mop_iterator MOI,
Location(Location::Register, RC->getSize(), MOP.getReg(), 0), ++MOI);
}
/// Go up the super-register chain until we hit a valid dwarf register number.
static short getDwarfRegNum(unsigned Reg, const MCRegisterInfo &MCRI,
const TargetRegisterInfo *TRI) {
int RegNo = MCRI.getDwarfRegNum(Reg, false);
for (MCSuperRegIterator SR(Reg, TRI);
SR.isValid() && RegNo < 0; ++SR)
RegNo = TRI->getDwarfRegNum(*SR, false);
assert(RegNo >= 0 && "Invalid Dwarf register number.");
return (unsigned short) RegNo;
}
/// Create a live-out register record for the given register Reg.
StackMaps::LiveOutReg
StackMaps::createLiveOutReg(unsigned Reg, const MCRegisterInfo &MCRI,
const TargetRegisterInfo *TRI) const {
unsigned RegNo = getDwarfRegNum(Reg, MCRI, TRI);
unsigned Size = TRI->getMinimalPhysRegClass(Reg)->getSize();
unsigned LLVMRegNo = MCRI.getLLVMRegNum(RegNo, false);
unsigned SubRegIdx = MCRI.getSubRegIndex(LLVMRegNo, Reg);
unsigned Offset = 0;
if (SubRegIdx)
Offset = MCRI.getSubRegIdxOffset(SubRegIdx) / 8;
return LiveOutReg(Reg, RegNo, Offset + Size);
}
/// Parse the register live-out mask and return a vector of live-out registers
/// that need to be recorded in the stackmap.
StackMaps::LiveOutVec
StackMaps::parseRegisterLiveOutMask(const uint32_t *Mask) const {
assert(Mask && "No register mask specified");
const TargetRegisterInfo *TRI = AP.TM.getRegisterInfo();
MCContext &OutContext = AP.OutStreamer.getContext();
const MCRegisterInfo &MCRI = *OutContext.getRegisterInfo();
LiveOutVec LiveOuts;
for (unsigned Reg = 0, NumRegs = TRI->getNumRegs(); Reg != NumRegs; ++Reg)
if ((Mask[Reg / 32] >> Reg % 32) & 1)
LiveOuts.push_back(createLiveOutReg(Reg, MCRI, TRI));
std::sort(LiveOuts.begin(), LiveOuts.end());
for (LiveOutVec::iterator I = LiveOuts.begin(), E = LiveOuts.end();
I != E; ++I) {
if (!I->Reg)
continue;
for (LiveOutVec::iterator II = next(I); II != E; ++II) {
if (I->RegNo != II->RegNo)
break;
I->Size = std::max(I->Size, II->Size);
if (TRI->isSuperRegister(I->Reg, II->Reg))
I->Reg = II->Reg;
II->Reg = 0;
}
}
LiveOuts.erase(std::remove_if(LiveOuts.begin(), LiveOuts.end(),
LiveOutReg::isInvalid), LiveOuts.end());
return LiveOuts;
}
void StackMaps::recordStackMapOpers(const MachineInstr &MI, uint32_t ID,
MachineInstr::const_mop_iterator MOI,
MachineInstr::const_mop_iterator MOE,
@ -129,7 +192,8 @@ void StackMaps::recordStackMapOpers(const MachineInstr &MI, uint32_t ID,
MCSymbol *MILabel = OutContext.CreateTempSymbol();
AP.OutStreamer.EmitLabel(MILabel);
LocationVec CallsiteLocs;
LocationVec Locations;
LiveOutVec LiveOuts;
if (recordResult) {
std::pair<Location, MachineInstr::const_mop_iterator> ParseResult =
@ -138,7 +202,7 @@ void StackMaps::recordStackMapOpers(const MachineInstr &MI, uint32_t ID,
Location &Loc = ParseResult.first;
assert(Loc.LocType == Location::Register &&
"Stackmap return location must be a register.");
CallsiteLocs.push_back(Loc);
Locations.push_back(Loc);
}
while (MOI != MOE) {
@ -151,7 +215,9 @@ void StackMaps::recordStackMapOpers(const MachineInstr &MI, uint32_t ID,
Loc.Offset = ConstPool.getConstantIndex(Loc.Offset);
}
CallsiteLocs.push_back(Loc);
// Skip the register mask and register live-out mask
if (Loc.LocType != Location::Unprocessed)
Locations.push_back(Loc);
}
const MCExpr *CSOffsetExpr = MCBinaryExpr::CreateSub(
@ -159,21 +225,23 @@ void StackMaps::recordStackMapOpers(const MachineInstr &MI, uint32_t ID,
MCSymbolRefExpr::Create(AP.CurrentFnSym, OutContext),
OutContext);
CSInfos.push_back(CallsiteInfo(CSOffsetExpr, ID, CallsiteLocs));
if (MOI->isRegLiveOut())
LiveOuts = parseRegisterLiveOutMask(MOI->getRegLiveOut());
CSInfos.push_back(CallsiteInfo(CSOffsetExpr, ID, Locations, LiveOuts));
}
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()))
if (MOI->isRegLiveOut() || (MOI->isReg() && MOI->isImplicit()))
break;
return MOI;
}
void StackMaps::recordStackMap(const MachineInstr &MI) {
assert(MI.getOpcode() == TargetOpcode::STACKMAP && "exected stackmap");
assert(MI.getOpcode() == TargetOpcode::STACKMAP && "expected stackmap");
int64_t ID = MI.getOperand(0).getImm();
assert((int32_t)ID == ID && "Stack maps hold 32-bit IDs");
@ -183,7 +251,7 @@ void StackMaps::recordStackMap(const MachineInstr &MI) {
}
void StackMaps::recordPatchPoint(const MachineInstr &MI) {
assert(MI.getOpcode() == TargetOpcode::PATCHPOINT && "exected stackmap");
assert(MI.getOpcode() == TargetOpcode::PATCHPOINT && "expected patchpoint");
PatchPointOpers opers(&MI);
int64_t ID = opers.getMetaOper(PatchPointOpers::IDPos).getImm();
@ -222,6 +290,11 @@ void StackMaps::recordPatchPoint(const MachineInstr &MI) {
/// uint16 : Dwarf RegNum
/// int32 : Offset
/// }
/// uint16 : NumLiveOuts
/// LiveOuts[NumLiveOuts]
/// uint16 : Dwarf RegNum
/// uint8 : Reserved
/// uint8 : Size in Bytes
/// }
///
/// Location Encoding, Type, Value:
@ -274,6 +347,7 @@ void StackMaps::serializeToStackMapSection() {
unsigned CallsiteID = CSII->ID;
const LocationVec &CSLocs = CSII->Locations;
const LiveOutVec &LiveOuts = CSII->LiveOuts;
DEBUG(dbgs() << WSMP << "callsite " << CallsiteID << "\n");
@ -281,11 +355,12 @@ void StackMaps::serializeToStackMapSection() {
// runtime than crash in case of in-process compilation. Currently, we do
// simple overflow checks, but we may eventually communicate other
// compilation errors this way.
if (CSLocs.size() > UINT16_MAX) {
if (CSLocs.size() > UINT16_MAX || LiveOuts.size() > UINT16_MAX) {
AP.OutStreamer.EmitIntValue(UINT32_MAX, 4); // Invalid ID.
AP.OutStreamer.EmitValue(CSII->CSOffsetExpr, 4);
AP.OutStreamer.EmitIntValue(0, 2); // Reserved.
AP.OutStreamer.EmitIntValue(0, 2); // 0 locations.
AP.OutStreamer.EmitIntValue(0, 2); // 0 live-out registers.
continue;
}
@ -362,6 +437,24 @@ void StackMaps::serializeToStackMapSection() {
AP.OutStreamer.EmitIntValue(RegNo, 2);
AP.OutStreamer.EmitIntValue(Offset, 4);
}
DEBUG(dbgs() << WSMP << " has " << LiveOuts.size()
<< " live-out registers\n");
AP.OutStreamer.EmitIntValue(LiveOuts.size(), 2);
operIdx = 0;
for (LiveOutVec::const_iterator LI = LiveOuts.begin(), LE = LiveOuts.end();
LI != LE; ++LI, ++operIdx) {
DEBUG(dbgs() << WSMP << " LO " << operIdx << ": "
<< MCRI.getName(LI->Reg)
<< " [encoding: .short " << LI->RegNo
<< ", .byte 0, .byte " << LI->Size << "]\n");
AP.OutStreamer.EmitIntValue(LI->RegNo, 2);
AP.OutStreamer.EmitIntValue(0, 1);
AP.OutStreamer.EmitIntValue(LI->Size, 1);
}
}
AP.OutStreamer.AddBlankLine();

View File

@ -11,7 +11,6 @@
; CHECK-NEXT: .long 8
; test
; CHECK-NEXT: .long 0
; CHECK-LABEL: .long L{{.*}}-_test
; CHECK-NEXT: .short 0
; 3 locations
@ -38,7 +37,6 @@ entry:
}
; property access 1 - %obj is an anyreg call argument and should therefore be in a register
; CHECK-NEXT: .long 1
; CHECK-LABEL: .long L{{.*}}-_property_access1
; CHECK-NEXT: .short 0
; 2 locations
@ -61,7 +59,6 @@ entry:
}
; property access 2 - %obj is an anyreg call argument and should therefore be in a register
; CHECK-NEXT: .long 2
; CHECK-LABEL: .long L{{.*}}-_property_access2
; CHECK-NEXT: .short 0
; 2 locations
@ -85,7 +82,6 @@ entry:
}
; property access 3 - %obj is a frame index
; CHECK-NEXT: .long 3
; CHECK-LABEL: .long L{{.*}}-_property_access3
; CHECK-NEXT: .short 0
; 2 locations
@ -109,7 +105,6 @@ entry:
}
; anyreg_test1
; CHECK-NEXT: .long 4
; CHECK-LABEL: .long L{{.*}}-_anyreg_test1
; CHECK-NEXT: .short 0
; 14 locations
@ -192,7 +187,6 @@ entry:
}
; anyreg_test2
; CHECK-NEXT: .long 5
; CHECK-LABEL: .long L{{.*}}-_anyreg_test2
; CHECK-NEXT: .short 0
; 14 locations
@ -278,7 +272,6 @@ entry:
;
; <rdar://problem/15432754> [JS] Assertion: "Folded a def to a non-store!"
;
; CHECK-LABEL: .long 12
; CHECK-LABEL: .long L{{.*}}-_patchpoint_spilldef
; CHECK-NEXT: .short 0
; CHECK-NEXT: .short 3
@ -308,7 +301,6 @@ entry:
;
; <rdar://problem/15487687> [JS] AnyRegCC argument ends up being spilled
;
; CHECK-LABEL: .long 13
; CHECK-LABEL: .long L{{.*}}-_patchpoint_spillargs
; CHECK-NEXT: .short 0
; CHECK-NEXT: .short 5

View File

@ -0,0 +1,85 @@
; RUN: llc < %s -mtriple=x86_64-apple-darwin -mcpu=corei7-avx -disable-fp-elim | FileCheck %s
; RUN: llc < %s -mtriple=x86_64-apple-darwin -mcpu=corei7-avx -disable-fp-elim -enable-stackmap-liveness| FileCheck -check-prefix=LIVE %s
;
; Note: Print verbose stackmaps using -debug-only=stackmaps.
; CHECK-LABEL: .section __LLVM_STACKMAPS,__llvm_stackmaps
; CHECK-NEXT: __LLVM_StackMaps:
; CHECK-NEXT: .long 0
; Num LargeConstants
; CHECK-NEXT: .long 0
; Num Callsites
; CHECK-NEXT: .long 3
; CHECK-LABEL: .long L{{.*}}-_liveness
; CHECK-NEXT: .short 0
; CHECK-NEXT: .short 0
; CHECK-NEXT: .short 0
; LIVE-LABEL: .long L{{.*}}-_liveness
; LIVE-NEXT: .short 0
; LIVE-NEXT: .short 0
; LIVE-NEXT: .short 2
; LIVE-NEXT: .short 7
; LIVE-NEXT: .byte 0
; LIVE-NEXT: .byte 8
; LIVE-NEXT: .short 19
; LIVE-NEXT: .byte 0
; LIVE-NEXT: .byte 16
; CHECK-LABEL: .long L{{.*}}-_liveness
; CHECK-NEXT: .short 0
; CHECK-NEXT: .short 0
; CHECK-NEXT: .short 0
; LIVE-LABEL: .long L{{.*}}-_liveness
; LIVE-NEXT: .short 0
; LIVE-NEXT: .short 0
; LIVE-NEXT: .short 6
; LIVE-NEXT: .short 0
; LIVE-NEXT: .byte 0
; LIVE-NEXT: .byte 2
; LIVE-NEXT: .short 7
; LIVE-NEXT: .byte 0
; LIVE-NEXT: .byte 8
; LIVE-NEXT: .short 8
; LIVE-NEXT: .byte 0
; LIVE-NEXT: .byte 8
; LIVE-NEXT: .short 17
; LIVE-NEXT: .byte 0
; LIVE-NEXT: .byte 32
; LIVE-NEXT: .short 18
; LIVE-NEXT: .byte 0
; LIVE-NEXT: .byte 32
; LIVE-NEXT: .short 19
; LIVE-NEXT: .byte 0
; LIVE-NEXT: .byte 16
; CHECK-LABEL: .long L{{.*}}-_liveness
; CHECK-NEXT: .short 0
; CHECK-NEXT: .short 0
; CHECK-NEXT: .short 0
; LIVE-LABEL: .long L{{.*}}-_liveness
; LIVE-NEXT: .short 0
; LIVE-NEXT: .short 0
; LIVE-NEXT: .short 2
; LIVE-NEXT: .short 7
; LIVE-NEXT: .byte 0
; LIVE-NEXT: .byte 8
; LIVE-NEXT: .short 19
; LIVE-NEXT: .byte 0
; LIVE-NEXT: .byte 16
define void @liveness() {
entry:
%a1 = call <2 x double> asm sideeffect "", "={xmm2}"() nounwind
call void (i32, i32, ...)* @llvm.experimental.stackmap(i32 1, i32 5)
%a2 = call i64 asm sideeffect "", "={r8}"() nounwind
%a3 = call i8 asm sideeffect "", "={ah}"() nounwind
%a4 = call <4 x double> asm sideeffect "", "={ymm0}"() nounwind
%a5 = call <4 x double> asm sideeffect "", "={ymm1}"() nounwind
call void (i32, i32, ...)* @llvm.experimental.stackmap(i32 2, i32 5)
call void asm sideeffect "", "{r8},{ah},{ymm0},{ymm1}"(i64 %a2, i8 %a3, <4 x double> %a4, <4 x double> %a5) nounwind
call void (i32, i32, ...)* @llvm.experimental.stackmap(i32 3, i32 5)
call void asm sideeffect "", "{xmm2}"(<2 x double> %a1) nounwind
ret void
}
declare void @llvm.experimental.stackmap(i32, i32, ...)

View File

@ -47,8 +47,7 @@ entry:
; Inline OSR Exit
;
; CHECK-NEXT: .long 3
; CHECK-NEXT: .long L{{.*}}-_osrinline
; CHECK-LABEL: .long L{{.*}}-_osrinline
; CHECK-NEXT: .short 0
; CHECK-NEXT: .short 2
; CHECK-NEXT: .byte 1
@ -72,8 +71,7 @@ entry:
;
; 2 live variables in register.
;
; CHECK-NEXT: .long 4
; CHECK-NEXT: .long L{{.*}}-_osrcold
; CHECK-LABEL: .long L{{.*}}-_osrcold
; CHECK-NEXT: .short 0
; CHECK-NEXT: .short 2
; CHECK-NEXT: .byte 1
@ -98,8 +96,7 @@ ret:
}
; Property Read
; CHECK-NEXT: .long 5
; CHECK-NEXT: .long L{{.*}}-_propertyRead
; CHECK-LABEL: .long L{{.*}}-_propertyRead
; CHECK-NEXT: .short 0
; CHECK-NEXT: .short 2
; CHECK-NEXT: .byte 1
@ -119,8 +116,7 @@ entry:
}
; Property Write
; CHECK-NEXT: .long 6
; CHECK-NEXT: .long L{{.*}}-_propertyWrite
; CHECK-LABEL: .long L{{.*}}-_propertyWrite
; CHECK-NEXT: .short 0
; CHECK-NEXT: .short 2
; CHECK-NEXT: .byte 1
@ -142,8 +138,7 @@ entry:
;
; 2 live variables in registers.
;
; CHECK-NEXT: .long 7
; CHECK-NEXT: .long L{{.*}}-_jsVoidCall
; CHECK-LABEL: .long L{{.*}}-_jsVoidCall
; CHECK-NEXT: .short 0
; CHECK-NEXT: .short 2
; CHECK-NEXT: .byte 1
@ -165,8 +160,7 @@ entry:
;
; 2 live variables in registers.
;
; CHECK: .long 8
; CHECK-NEXT: .long L{{.*}}-_jsIntCall
; CHECK-LABEL: .long L{{.*}}-_jsIntCall
; CHECK-NEXT: .short 0
; CHECK-NEXT: .short 2
; CHECK-NEXT: .byte 1
@ -189,16 +183,15 @@ entry:
;
; Verify 17 stack map entries.
;
; CHECK: .long 11
; CHECK-NEXT: .long L{{.*}}-_spilledValue
; CHECK-NEXT: .short 0
; CHECK-NEXT: .short 17
; CHECK-LABEL: .long L{{.*}}-_spilledValue
; CHECK-NEXT: .short 0
; CHECK-NEXT: .short 17
;
; Check that at least one is a spilled entry from RBP.
; Location: Indirect RBP + ...
; CHECK: .byte 3
; CHECK-NEXT: .byte 8
; CHECK-NEXT: .short 6
; CHECK: .byte 3
; CHECK-NEXT: .byte 8
; CHECK-NEXT: .short 6
define void @spilledValue(i64 %arg0, i64 %arg1, i64 %arg2, i64 %arg3, i64 %arg4, i64 %l0, i64 %l1, i64 %l2, i64 %l3, i64 %l4, i64 %l5, i64 %l6, i64 %l7, i64 %l8, i64 %l9, i64 %l10, i64 %l11, i64 %l12, i64 %l13, i64 %l14, i64 %l15, i64 %l16) {
entry:
call void (i32, i32, i8*, i32, ...)* @llvm.experimental.patchpoint.void(i32 11, i32 15, i8* null, i32 5, i64 %arg0, i64 %arg1, i64 %arg2, i64 %arg3, i64 %arg4, i64 %l0, i64 %l1, i64 %l2, i64 %l3, i64 %l4, i64 %l5, i64 %l6, i64 %l7, i64 %l8, i64 %l9, i64 %l10, i64 %l11, i64 %l12, i64 %l13, i64 %l14, i64 %l15, i64 %l16)
@ -209,16 +202,15 @@ entry:
;
; Verify 17 stack map entries.
;
; CHECK: .long 12
; CHECK-LABEL: .long L{{.*}}-_spilledStackMapValue
; CHECK-NEXT: .short 0
; CHECK-NEXT: .short 17
; CHECK-LABEL: .long L{{.*}}-_spilledStackMapValue
; CHECK-NEXT: .short 0
; CHECK-NEXT: .short 17
;
; Check that at least one is a spilled entry from RBP.
; Location: Indirect RBP + ...
; CHECK: .byte 3
; CHECK-NEXT: .byte 8
; CHECK-NEXT: .short 6
; CHECK: .byte 3
; CHECK-NEXT: .byte 8
; CHECK-NEXT: .short 6
define webkit_jscc void @spilledStackMapValue(i64 %l0, i64 %l1, i64 %l2, i64 %l3, i64 %l4, i64 %l5, i64 %l6, i64 %l7, i64 %l8, i64 %l9, i64 %l10, i64 %l11, i64 %l12, i64 %l13, i64 %l14, i64 %l15, i64 %l16) {
entry:
call void (i32, i32, ...)* @llvm.experimental.stackmap(i32 12, i32 15, i64 %l0, i64 %l1, i64 %l2, i64 %l3, i64 %l4, i64 %l5, i64 %l6, i64 %l7, i64 %l8, i64 %l9, i64 %l10, i64 %l11, i64 %l12, i64 %l13, i64 %l14, i64 %l15, i64 %l16)
@ -227,17 +219,16 @@ entry:
; Spill a subregister stackmap operand.
;
; CHECK: .long 13
; CHECK-LABEL: .long L{{.*}}-_spillSubReg
; CHECK-NEXT: .short 0
; CHECK-LABEL: .long L{{.*}}-_spillSubReg
; CHECK-NEXT: .short 0
; 4 locations
; CHECK-NEXT: .short 1
; CHECK-NEXT: .short 1
;
; Check that the subregister operand is a 4-byte spill.
; Location: Indirect, 4-byte, RBP + ...
; CHECK: .byte 3
; CHECK-NEXT: .byte 4
; CHECK-NEXT: .short 6
; CHECK: .byte 3
; CHECK-NEXT: .byte 4
; CHECK-NEXT: .short 6
define void @spillSubReg(i64 %arg) #0 {
bb:
br i1 undef, label %bb1, label %bb2
@ -268,24 +259,23 @@ bb61:
; Map a single byte subregister. There is no DWARF register number, so
; we expect the register to be encoded with the proper size and spill offset. We don't know which
;
; CHECK: .long 14
; CHECK-LABEL: .long L{{.*}}-_subRegOffset
; CHECK-NEXT: .short 0
; CHECK-LABEL: .long L{{.*}}-_subRegOffset
; CHECK-NEXT: .short 0
; 2 locations
; CHECK-NEXT: .short 2
; CHECK-NEXT: .short 2
;
; Check that the subregister operands are 1-byte spills.
; Location 0: Register, 4-byte, AL
; CHECK-NEXT: .byte 1
; CHECK-NEXT: .byte 1
; CHECK-NEXT: .short 0
; CHECK-NEXT: .long 0
; CHECK-NEXT: .byte 1
; CHECK-NEXT: .byte 1
; CHECK-NEXT: .short 0
; CHECK-NEXT: .long 0
;
; Location 1: Register, 4-byte, BL
; CHECK-NEXT: .byte 1
; CHECK-NEXT: .byte 1
; CHECK-NEXT: .short 3
; CHECK-NEXT: .long 0
; CHECK-NEXT: .byte 1
; CHECK-NEXT: .byte 1
; CHECK-NEXT: .short 3
; CHECK-NEXT: .long 0
define void @subRegOffset(i16 %arg) {
%v = mul i16 %arg, 5
%a0 = trunc i16 %v to i8
@ -299,11 +289,10 @@ define void @subRegOffset(i16 %arg) {
; Map a constant value.
;
; CHECK: .long 15
; CHECK-LABEL: .long L{{.*}}-_liveConstant
; CHECK-NEXT: .short 0
; CHECK-LABEL: .long L{{.*}}-_liveConstant
; CHECK-NEXT: .short 0
; 1 location
; CHECK-NEXT: .short 1
; CHECK-NEXT: .short 1
; Loc 0: SmallConstant
; CHECK-NEXT: .byte 4
; CHECK-NEXT: .byte 8
@ -318,7 +307,6 @@ define void @liveConstant() {
; Directly map an alloca's address.
;
; Callsite 16
; CHECK: .long 16
; CHECK-LABEL: .long L{{.*}}-_directFrameIdx
; CHECK-NEXT: .short 0
; 1 location
@ -328,9 +316,9 @@ define void @liveConstant() {
; CHECK-NEXT: .byte 8
; CHECK-NEXT: .short 6
; CHECK-NEXT: .long
; Callsite 17
; CHECK-NEXT: .long 17
; CHECK-NEXT: .long L{{.*}}-_directFrameIdx
; CHECK-LABEL: .long L{{.*}}-_directFrameIdx
; CHECK-NEXT: .short 0
; 2 locations
; CHECK-NEXT: .short 2