mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2024-12-15 20:29:48 +00:00
2debd48ca7
The old naming scheme (load/use/def/store) can be traced back to an old linear scan article, but the names don't match how slots are actually used. The load and store slots are not needed after the deferred spill code insertion framework was deleted. The use and def slots don't make any sense because we are using half-open intervals as is customary in C code, but the names suggest closed intervals. In reality, these slots were used to distinguish early-clobber defs from normal defs. The new naming scheme also has 4 slots, but the names match how the slots are really used. This is a purely mechanical renaming, but some of the code makes a lot more sense now. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@144503 91177308-0d34-0410-b5e6-96231b3b80d8
1014 lines
34 KiB
C++
1014 lines
34 KiB
C++
//===-- llvm/CodeGen/RenderMachineFunction.cpp - MF->HTML -----s-----------===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#define DEBUG_TYPE "rendermf"
|
|
|
|
#include "RenderMachineFunction.h"
|
|
|
|
#include "VirtRegMap.h"
|
|
|
|
#include "llvm/Function.h"
|
|
#include "llvm/Module.h"
|
|
#include "llvm/ADT/SmallVector.h"
|
|
#include "llvm/CodeGen/LiveIntervalAnalysis.h"
|
|
#include "llvm/CodeGen/MachineFunction.h"
|
|
#include "llvm/CodeGen/MachineInstr.h"
|
|
#include "llvm/CodeGen/MachineRegisterInfo.h"
|
|
#include "llvm/Support/CommandLine.h"
|
|
#include "llvm/Support/Debug.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
#include "llvm/Target/TargetMachine.h"
|
|
|
|
#include <sstream>
|
|
|
|
using namespace llvm;
|
|
|
|
char RenderMachineFunction::ID = 0;
|
|
INITIALIZE_PASS_BEGIN(RenderMachineFunction, "rendermf",
|
|
"Render machine functions (and related info) to HTML pages",
|
|
false, false)
|
|
INITIALIZE_PASS_DEPENDENCY(SlotIndexes)
|
|
INITIALIZE_PASS_DEPENDENCY(LiveIntervals)
|
|
INITIALIZE_PASS_END(RenderMachineFunction, "rendermf",
|
|
"Render machine functions (and related info) to HTML pages",
|
|
false, false)
|
|
|
|
static cl::opt<std::string>
|
|
outputFileSuffix("rmf-file-suffix",
|
|
cl::desc("Appended to function name to get output file name "
|
|
"(default: \".html\")"),
|
|
cl::init(".html"), cl::Hidden);
|
|
|
|
static cl::opt<std::string>
|
|
machineFuncsToRender("rmf-funcs",
|
|
cl::desc("Comma separated list of functions to render"
|
|
", or \"*\"."),
|
|
cl::init(""), cl::Hidden);
|
|
|
|
static cl::opt<std::string>
|
|
pressureClasses("rmf-classes",
|
|
cl::desc("Register classes to render pressure for."),
|
|
cl::init(""), cl::Hidden);
|
|
|
|
static cl::opt<std::string>
|
|
showIntervals("rmf-intervals",
|
|
cl::desc("Live intervals to show alongside code."),
|
|
cl::init(""), cl::Hidden);
|
|
|
|
static cl::opt<bool>
|
|
filterEmpty("rmf-filter-empty-intervals",
|
|
cl::desc("Don't display empty intervals."),
|
|
cl::init(true), cl::Hidden);
|
|
|
|
static cl::opt<bool>
|
|
showEmptyIndexes("rmf-empty-indexes",
|
|
cl::desc("Render indexes not associated with instructions or "
|
|
"MBB starts."),
|
|
cl::init(false), cl::Hidden);
|
|
|
|
static cl::opt<bool>
|
|
useFancyVerticals("rmf-fancy-verts",
|
|
cl::desc("Use SVG for vertical text."),
|
|
cl::init(true), cl::Hidden);
|
|
|
|
static cl::opt<bool>
|
|
prettyHTML("rmf-pretty-html",
|
|
cl::desc("Pretty print HTML. For debugging the renderer only.."),
|
|
cl::init(false), cl::Hidden);
|
|
|
|
|
|
namespace llvm {
|
|
|
|
bool MFRenderingOptions::renderingOptionsProcessed;
|
|
std::set<std::string> MFRenderingOptions::mfNamesToRender;
|
|
bool MFRenderingOptions::renderAllMFs = false;
|
|
|
|
std::set<std::string> MFRenderingOptions::classNamesToRender;
|
|
bool MFRenderingOptions::renderAllClasses = false;
|
|
|
|
std::set<std::pair<unsigned, unsigned> >
|
|
MFRenderingOptions::intervalNumsToRender;
|
|
unsigned MFRenderingOptions::intervalTypesToRender = ExplicitOnly;
|
|
|
|
template <typename OutputItr>
|
|
void MFRenderingOptions::splitComaSeperatedList(const std::string &s,
|
|
OutputItr outItr) {
|
|
std::string::const_iterator curPos = s.begin();
|
|
std::string::const_iterator nextComa = std::find(curPos, s.end(), ',');
|
|
while (nextComa != s.end()) {
|
|
std::string elem;
|
|
std::copy(curPos, nextComa, std::back_inserter(elem));
|
|
*outItr = elem;
|
|
++outItr;
|
|
curPos = llvm::next(nextComa);
|
|
nextComa = std::find(curPos, s.end(), ',');
|
|
}
|
|
|
|
if (curPos != s.end()) {
|
|
std::string elem;
|
|
std::copy(curPos, s.end(), std::back_inserter(elem));
|
|
*outItr = elem;
|
|
++outItr;
|
|
}
|
|
}
|
|
|
|
void MFRenderingOptions::processOptions() {
|
|
if (!renderingOptionsProcessed) {
|
|
processFuncNames();
|
|
processRegClassNames();
|
|
processIntervalNumbers();
|
|
renderingOptionsProcessed = true;
|
|
}
|
|
}
|
|
|
|
void MFRenderingOptions::processFuncNames() {
|
|
if (machineFuncsToRender == "*") {
|
|
renderAllMFs = true;
|
|
} else {
|
|
splitComaSeperatedList(machineFuncsToRender,
|
|
std::inserter(mfNamesToRender,
|
|
mfNamesToRender.begin()));
|
|
}
|
|
}
|
|
|
|
void MFRenderingOptions::processRegClassNames() {
|
|
if (pressureClasses == "*") {
|
|
renderAllClasses = true;
|
|
} else {
|
|
splitComaSeperatedList(pressureClasses,
|
|
std::inserter(classNamesToRender,
|
|
classNamesToRender.begin()));
|
|
}
|
|
}
|
|
|
|
void MFRenderingOptions::processIntervalNumbers() {
|
|
std::set<std::string> intervalRanges;
|
|
splitComaSeperatedList(showIntervals,
|
|
std::inserter(intervalRanges,
|
|
intervalRanges.begin()));
|
|
std::for_each(intervalRanges.begin(), intervalRanges.end(),
|
|
processIntervalRange);
|
|
}
|
|
|
|
void MFRenderingOptions::processIntervalRange(
|
|
const std::string &intervalRangeStr) {
|
|
if (intervalRangeStr == "*") {
|
|
intervalTypesToRender |= All;
|
|
} else if (intervalRangeStr == "virt-nospills*") {
|
|
intervalTypesToRender |= VirtNoSpills;
|
|
} else if (intervalRangeStr == "spills*") {
|
|
intervalTypesToRender |= VirtSpills;
|
|
} else if (intervalRangeStr == "virt*") {
|
|
intervalTypesToRender |= AllVirt;
|
|
} else if (intervalRangeStr == "phys*") {
|
|
intervalTypesToRender |= AllPhys;
|
|
} else {
|
|
std::istringstream iss(intervalRangeStr);
|
|
unsigned reg1, reg2;
|
|
if ((iss >> reg1 >> std::ws)) {
|
|
if (iss.eof()) {
|
|
intervalNumsToRender.insert(std::make_pair(reg1, reg1 + 1));
|
|
} else {
|
|
char c;
|
|
iss >> c;
|
|
if (c == '-' && (iss >> reg2)) {
|
|
intervalNumsToRender.insert(std::make_pair(reg1, reg2 + 1));
|
|
} else {
|
|
dbgs() << "Warning: Invalid interval range \""
|
|
<< intervalRangeStr << "\" in -rmf-intervals. Skipping.\n";
|
|
}
|
|
}
|
|
} else {
|
|
dbgs() << "Warning: Invalid interval number \""
|
|
<< intervalRangeStr << "\" in -rmf-intervals. Skipping.\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
void MFRenderingOptions::setup(MachineFunction *mf,
|
|
const TargetRegisterInfo *tri,
|
|
LiveIntervals *lis,
|
|
const RenderMachineFunction *rmf) {
|
|
this->mf = mf;
|
|
this->tri = tri;
|
|
this->lis = lis;
|
|
this->rmf = rmf;
|
|
|
|
clear();
|
|
}
|
|
|
|
void MFRenderingOptions::clear() {
|
|
regClassesTranslatedToCurrentFunction = false;
|
|
regClassSet.clear();
|
|
|
|
intervalsTranslatedToCurrentFunction = false;
|
|
intervalSet.clear();
|
|
}
|
|
|
|
void MFRenderingOptions::resetRenderSpecificOptions() {
|
|
intervalSet.clear();
|
|
intervalsTranslatedToCurrentFunction = false;
|
|
}
|
|
|
|
bool MFRenderingOptions::shouldRenderCurrentMachineFunction() const {
|
|
processOptions();
|
|
|
|
return (renderAllMFs ||
|
|
mfNamesToRender.find(mf->getFunction()->getName()) !=
|
|
mfNamesToRender.end());
|
|
}
|
|
|
|
const MFRenderingOptions::RegClassSet& MFRenderingOptions::regClasses() const{
|
|
translateRegClassNamesToCurrentFunction();
|
|
return regClassSet;
|
|
}
|
|
|
|
const MFRenderingOptions::IntervalSet& MFRenderingOptions::intervals() const {
|
|
translateIntervalNumbersToCurrentFunction();
|
|
return intervalSet;
|
|
}
|
|
|
|
bool MFRenderingOptions::renderEmptyIndexes() const {
|
|
return showEmptyIndexes;
|
|
}
|
|
|
|
bool MFRenderingOptions::fancyVerticals() const {
|
|
return useFancyVerticals;
|
|
}
|
|
|
|
void MFRenderingOptions::translateRegClassNamesToCurrentFunction() const {
|
|
if (!regClassesTranslatedToCurrentFunction) {
|
|
processOptions();
|
|
for (TargetRegisterInfo::regclass_iterator rcItr = tri->regclass_begin(),
|
|
rcEnd = tri->regclass_end();
|
|
rcItr != rcEnd; ++rcItr) {
|
|
const TargetRegisterClass *trc = *rcItr;
|
|
if (renderAllClasses ||
|
|
classNamesToRender.find(trc->getName()) !=
|
|
classNamesToRender.end()) {
|
|
regClassSet.insert(trc);
|
|
}
|
|
}
|
|
regClassesTranslatedToCurrentFunction = true;
|
|
}
|
|
}
|
|
|
|
void MFRenderingOptions::translateIntervalNumbersToCurrentFunction() const {
|
|
if (!intervalsTranslatedToCurrentFunction) {
|
|
processOptions();
|
|
|
|
// If we're not just doing explicit then do a copy over all matching
|
|
// types.
|
|
if (intervalTypesToRender != ExplicitOnly) {
|
|
for (LiveIntervals::iterator liItr = lis->begin(), liEnd = lis->end();
|
|
liItr != liEnd; ++liItr) {
|
|
LiveInterval *li = liItr->second;
|
|
|
|
if (filterEmpty && li->empty())
|
|
continue;
|
|
|
|
if ((TargetRegisterInfo::isPhysicalRegister(li->reg) &&
|
|
(intervalTypesToRender & AllPhys))) {
|
|
intervalSet.insert(li);
|
|
} else if (TargetRegisterInfo::isVirtualRegister(li->reg)) {
|
|
if (((intervalTypesToRender & VirtNoSpills) && !rmf->isSpill(li)) ||
|
|
((intervalTypesToRender & VirtSpills) && rmf->isSpill(li))) {
|
|
intervalSet.insert(li);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// If we need to process the explicit list...
|
|
if (intervalTypesToRender != All) {
|
|
for (std::set<std::pair<unsigned, unsigned> >::const_iterator
|
|
regRangeItr = intervalNumsToRender.begin(),
|
|
regRangeEnd = intervalNumsToRender.end();
|
|
regRangeItr != regRangeEnd; ++regRangeItr) {
|
|
const std::pair<unsigned, unsigned> &range = *regRangeItr;
|
|
for (unsigned reg = range.first; reg != range.second; ++reg) {
|
|
if (lis->hasInterval(reg)) {
|
|
intervalSet.insert(&lis->getInterval(reg));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
intervalsTranslatedToCurrentFunction = true;
|
|
}
|
|
}
|
|
|
|
// ---------- TargetRegisterExtraInformation implementation ----------
|
|
|
|
TargetRegisterExtraInfo::TargetRegisterExtraInfo()
|
|
: mapsPopulated(false) {
|
|
}
|
|
|
|
void TargetRegisterExtraInfo::setup(MachineFunction *mf,
|
|
MachineRegisterInfo *mri,
|
|
const TargetRegisterInfo *tri,
|
|
LiveIntervals *lis) {
|
|
this->mf = mf;
|
|
this->mri = mri;
|
|
this->tri = tri;
|
|
this->lis = lis;
|
|
}
|
|
|
|
void TargetRegisterExtraInfo::reset() {
|
|
if (!mapsPopulated) {
|
|
initWorst();
|
|
//initBounds();
|
|
initCapacity();
|
|
mapsPopulated = true;
|
|
}
|
|
|
|
resetPressureAndLiveStates();
|
|
}
|
|
|
|
void TargetRegisterExtraInfo::clear() {
|
|
prWorst.clear();
|
|
vrWorst.clear();
|
|
capacityMap.clear();
|
|
pressureMap.clear();
|
|
//liveStatesMap.clear();
|
|
mapsPopulated = false;
|
|
}
|
|
|
|
void TargetRegisterExtraInfo::initWorst() {
|
|
assert(!mapsPopulated && prWorst.empty() && vrWorst.empty() &&
|
|
"Worst map already initialised?");
|
|
|
|
// Start with the physical registers.
|
|
for (unsigned preg = 1; preg < tri->getNumRegs(); ++preg) {
|
|
WorstMapLine &pregLine = prWorst[preg];
|
|
|
|
for (TargetRegisterInfo::regclass_iterator rcItr = tri->regclass_begin(),
|
|
rcEnd = tri->regclass_end();
|
|
rcItr != rcEnd; ++rcItr) {
|
|
const TargetRegisterClass *trc = *rcItr;
|
|
|
|
unsigned numOverlaps = 0;
|
|
for (TargetRegisterClass::iterator rItr = trc->begin(),
|
|
rEnd = trc->end();
|
|
rItr != rEnd; ++rItr) {
|
|
unsigned trcPReg = *rItr;
|
|
if (tri->regsOverlap(preg, trcPReg))
|
|
++numOverlaps;
|
|
}
|
|
|
|
pregLine[trc] = numOverlaps;
|
|
}
|
|
}
|
|
|
|
// Now the register classes.
|
|
for (TargetRegisterInfo::regclass_iterator rc1Itr = tri->regclass_begin(),
|
|
rcEnd = tri->regclass_end();
|
|
rc1Itr != rcEnd; ++rc1Itr) {
|
|
const TargetRegisterClass *trc1 = *rc1Itr;
|
|
WorstMapLine &classLine = vrWorst[trc1];
|
|
|
|
for (TargetRegisterInfo::regclass_iterator rc2Itr = tri->regclass_begin();
|
|
rc2Itr != rcEnd; ++rc2Itr) {
|
|
const TargetRegisterClass *trc2 = *rc2Itr;
|
|
|
|
unsigned worst = 0;
|
|
|
|
for (TargetRegisterClass::iterator trc1Itr = trc1->begin(),
|
|
trc1End = trc1->end();
|
|
trc1Itr != trc1End; ++trc1Itr) {
|
|
unsigned trc1Reg = *trc1Itr;
|
|
unsigned trc1RegWorst = 0;
|
|
|
|
for (TargetRegisterClass::iterator trc2Itr = trc2->begin(),
|
|
trc2End = trc2->end();
|
|
trc2Itr != trc2End; ++trc2Itr) {
|
|
unsigned trc2Reg = *trc2Itr;
|
|
if (tri->regsOverlap(trc1Reg, trc2Reg))
|
|
++trc1RegWorst;
|
|
}
|
|
if (trc1RegWorst > worst) {
|
|
worst = trc1RegWorst;
|
|
}
|
|
}
|
|
|
|
if (worst != 0) {
|
|
classLine[trc2] = worst;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
unsigned TargetRegisterExtraInfo::getWorst(
|
|
unsigned reg,
|
|
const TargetRegisterClass *trc) const {
|
|
const WorstMapLine *wml = 0;
|
|
if (TargetRegisterInfo::isPhysicalRegister(reg)) {
|
|
PRWorstMap::const_iterator prwItr = prWorst.find(reg);
|
|
assert(prwItr != prWorst.end() && "Missing prWorst entry.");
|
|
wml = &prwItr->second;
|
|
} else {
|
|
const TargetRegisterClass *regTRC = mri->getRegClass(reg);
|
|
VRWorstMap::const_iterator vrwItr = vrWorst.find(regTRC);
|
|
assert(vrwItr != vrWorst.end() && "Missing vrWorst entry.");
|
|
wml = &vrwItr->second;
|
|
}
|
|
|
|
WorstMapLine::const_iterator wmlItr = wml->find(trc);
|
|
if (wmlItr == wml->end())
|
|
return 0;
|
|
|
|
return wmlItr->second;
|
|
}
|
|
|
|
void TargetRegisterExtraInfo::initCapacity() {
|
|
assert(!mapsPopulated && capacityMap.empty() &&
|
|
"Capacity map already initialised?");
|
|
|
|
for (TargetRegisterInfo::regclass_iterator rcItr = tri->regclass_begin(),
|
|
rcEnd = tri->regclass_end();
|
|
rcItr != rcEnd; ++rcItr) {
|
|
const TargetRegisterClass *trc = *rcItr;
|
|
unsigned capacity = trc->getRawAllocationOrder(*mf).size();
|
|
|
|
if (capacity != 0)
|
|
capacityMap[trc] = capacity;
|
|
}
|
|
}
|
|
|
|
unsigned TargetRegisterExtraInfo::getCapacity(
|
|
const TargetRegisterClass *trc) const {
|
|
CapacityMap::const_iterator cmItr = capacityMap.find(trc);
|
|
assert(cmItr != capacityMap.end() &&
|
|
"vreg with unallocable register class");
|
|
return cmItr->second;
|
|
}
|
|
|
|
void TargetRegisterExtraInfo::resetPressureAndLiveStates() {
|
|
pressureMap.clear();
|
|
//liveStatesMap.clear();
|
|
|
|
// Iterate over all slots.
|
|
|
|
|
|
// Iterate over all live intervals.
|
|
for (LiveIntervals::iterator liItr = lis->begin(),
|
|
liEnd = lis->end();
|
|
liItr != liEnd; ++liItr) {
|
|
LiveInterval *li = liItr->second;
|
|
|
|
if (TargetRegisterInfo::isPhysicalRegister(li->reg))
|
|
continue;
|
|
|
|
// For all ranges in the current interal.
|
|
for (LiveInterval::iterator lrItr = li->begin(),
|
|
lrEnd = li->end();
|
|
lrItr != lrEnd; ++lrItr) {
|
|
LiveRange *lr = &*lrItr;
|
|
|
|
// For all slots in the current range.
|
|
for (SlotIndex i = lr->start; i != lr->end; i = i.getNextSlot()) {
|
|
|
|
// Record increased pressure at index for all overlapping classes.
|
|
for (TargetRegisterInfo::regclass_iterator
|
|
rcItr = tri->regclass_begin(),
|
|
rcEnd = tri->regclass_end();
|
|
rcItr != rcEnd; ++rcItr) {
|
|
const TargetRegisterClass *trc = *rcItr;
|
|
|
|
if (trc->getRawAllocationOrder(*mf).empty())
|
|
continue;
|
|
|
|
unsigned worstAtI = getWorst(li->reg, trc);
|
|
|
|
if (worstAtI != 0) {
|
|
pressureMap[i][trc] += worstAtI;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
unsigned TargetRegisterExtraInfo::getPressureAtSlot(
|
|
const TargetRegisterClass *trc,
|
|
SlotIndex i) const {
|
|
PressureMap::const_iterator pmItr = pressureMap.find(i);
|
|
if (pmItr == pressureMap.end())
|
|
return 0;
|
|
const PressureMapLine &pmLine = pmItr->second;
|
|
PressureMapLine::const_iterator pmlItr = pmLine.find(trc);
|
|
if (pmlItr == pmLine.end())
|
|
return 0;
|
|
return pmlItr->second;
|
|
}
|
|
|
|
bool TargetRegisterExtraInfo::classOverCapacityAtSlot(
|
|
const TargetRegisterClass *trc,
|
|
SlotIndex i) const {
|
|
return (getPressureAtSlot(trc, i) > getCapacity(trc));
|
|
}
|
|
|
|
// ---------- MachineFunctionRenderer implementation ----------
|
|
|
|
void RenderMachineFunction::Spacer::print(raw_ostream &os) const {
|
|
if (!prettyHTML)
|
|
return;
|
|
for (unsigned i = 0; i < ns; ++i) {
|
|
os << " ";
|
|
}
|
|
}
|
|
|
|
RenderMachineFunction::Spacer RenderMachineFunction::s(unsigned ns) const {
|
|
return Spacer(ns);
|
|
}
|
|
|
|
raw_ostream& operator<<(raw_ostream &os, const RenderMachineFunction::Spacer &s) {
|
|
s.print(os);
|
|
return os;
|
|
}
|
|
|
|
template <typename Iterator>
|
|
std::string RenderMachineFunction::escapeChars(Iterator sBegin, Iterator sEnd) const {
|
|
std::string r;
|
|
|
|
for (Iterator sItr = sBegin; sItr != sEnd; ++sItr) {
|
|
char c = *sItr;
|
|
|
|
switch (c) {
|
|
case '<': r.append("<"); break;
|
|
case '>': r.append(">"); break;
|
|
case '&': r.append("&"); break;
|
|
case ' ': r.append(" "); break;
|
|
case '\"': r.append("""); break;
|
|
default: r.push_back(c); break;
|
|
}
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
RenderMachineFunction::LiveState
|
|
RenderMachineFunction::getLiveStateAt(const LiveInterval *li,
|
|
SlotIndex i) const {
|
|
const MachineInstr *mi = sis->getInstructionFromIndex(i);
|
|
|
|
// For uses/defs recorded use/def indexes override current liveness and
|
|
// instruction operands (Only for the interval which records the indexes).
|
|
// FIXME: This is all wrong, uses and defs share the same slots.
|
|
if (i.isEarlyClobber() || i.isRegister()) {
|
|
UseDefs::const_iterator udItr = useDefs.find(li);
|
|
if (udItr != useDefs.end()) {
|
|
const SlotSet &slotSet = udItr->second;
|
|
if (slotSet.count(i)) {
|
|
if (i.isEarlyClobber()) {
|
|
return Used;
|
|
}
|
|
// else
|
|
return Defined;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If the slot is a load/store, or there's no info in the use/def set then
|
|
// use liveness and instruction operand info.
|
|
if (li->liveAt(i)) {
|
|
|
|
if (mi == 0) {
|
|
if (vrm == 0 ||
|
|
(vrm->getStackSlot(li->reg) == VirtRegMap::NO_STACK_SLOT)) {
|
|
return AliveReg;
|
|
} else {
|
|
return AliveStack;
|
|
}
|
|
} else {
|
|
if (i.isRegister() && mi->definesRegister(li->reg, tri)) {
|
|
return Defined;
|
|
} else if (i.isEarlyClobber() && mi->readsRegister(li->reg)) {
|
|
return Used;
|
|
} else {
|
|
if (vrm == 0 ||
|
|
(vrm->getStackSlot(li->reg) == VirtRegMap::NO_STACK_SLOT)) {
|
|
return AliveReg;
|
|
} else {
|
|
return AliveStack;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return Dead;
|
|
}
|
|
|
|
RenderMachineFunction::PressureState
|
|
RenderMachineFunction::getPressureStateAt(const TargetRegisterClass *trc,
|
|
SlotIndex i) const {
|
|
if (trei.getPressureAtSlot(trc, i) == 0) {
|
|
return Zero;
|
|
} else if (trei.classOverCapacityAtSlot(trc, i)){
|
|
return High;
|
|
}
|
|
return Low;
|
|
}
|
|
|
|
/// \brief Render a machine instruction.
|
|
void RenderMachineFunction::renderMachineInstr(raw_ostream &os,
|
|
const MachineInstr *mi) const {
|
|
std::string s;
|
|
raw_string_ostream oss(s);
|
|
oss << *mi;
|
|
|
|
os << escapeChars(oss.str());
|
|
}
|
|
|
|
template <typename T>
|
|
void RenderMachineFunction::renderVertical(const Spacer &indent,
|
|
raw_ostream &os,
|
|
const T &t) const {
|
|
if (ro.fancyVerticals()) {
|
|
os << indent << "<object\n"
|
|
<< indent + s(2) << "class=\"obj\"\n"
|
|
<< indent + s(2) << "type=\"image/svg+xml\"\n"
|
|
<< indent + s(2) << "width=\"14px\"\n"
|
|
<< indent + s(2) << "height=\"55px\"\n"
|
|
<< indent + s(2) << "data=\"data:image/svg+xml,\n"
|
|
<< indent + s(4) << "<svg xmlns='http://www.w3.org/2000/svg'>\n"
|
|
<< indent + s(6) << "<text x='-55' y='10' "
|
|
"font-family='Courier' font-size='12' "
|
|
"transform='rotate(-90)' "
|
|
"text-rendering='optimizeSpeed' "
|
|
"fill='#000'>" << t << "</text>\n"
|
|
<< indent + s(4) << "</svg>\">\n"
|
|
<< indent << "</object>\n";
|
|
} else {
|
|
std::ostringstream oss;
|
|
oss << t;
|
|
std::string tStr(oss.str());
|
|
|
|
os << indent;
|
|
for (std::string::iterator tStrItr = tStr.begin(), tStrEnd = tStr.end();
|
|
tStrItr != tStrEnd; ++tStrItr) {
|
|
os << *tStrItr << "<br/>";
|
|
}
|
|
os << "\n";
|
|
}
|
|
}
|
|
|
|
void RenderMachineFunction::insertCSS(const Spacer &indent,
|
|
raw_ostream &os) const {
|
|
os << indent << "<style type=\"text/css\">\n"
|
|
<< indent + s(2) << "body { font-color: black; }\n"
|
|
<< indent + s(2) << "table.code td { font-family: monospace; "
|
|
"border-width: 0px; border-style: solid; "
|
|
"border-bottom: 1px solid #dddddd; white-space: nowrap; }\n"
|
|
<< indent + s(2) << "table.code td.p-z { background-color: #000000; }\n"
|
|
<< indent + s(2) << "table.code td.p-l { background-color: #00ff00; }\n"
|
|
<< indent + s(2) << "table.code td.p-h { background-color: #ff0000; }\n"
|
|
<< indent + s(2) << "table.code td.l-n { background-color: #ffffff; }\n"
|
|
<< indent + s(2) << "table.code td.l-d { background-color: #ff0000; }\n"
|
|
<< indent + s(2) << "table.code td.l-u { background-color: #ffff00; }\n"
|
|
<< indent + s(2) << "table.code td.l-r { background-color: #000000; }\n"
|
|
<< indent + s(2) << "table.code td.l-s { background-color: #770000; }\n"
|
|
<< indent + s(2) << "table.code th { border-width: 0px; "
|
|
"border-style: solid; }\n"
|
|
<< indent << "</style>\n";
|
|
}
|
|
|
|
void RenderMachineFunction::renderFunctionSummary(
|
|
const Spacer &indent, raw_ostream &os,
|
|
const char * const renderContextStr) const {
|
|
os << indent << "<h1>Function: " << mf->getFunction()->getName()
|
|
<< "</h1>\n"
|
|
<< indent << "<h2>Rendering context: " << renderContextStr << "</h2>\n";
|
|
}
|
|
|
|
|
|
void RenderMachineFunction::renderPressureTableLegend(
|
|
const Spacer &indent,
|
|
raw_ostream &os) const {
|
|
os << indent << "<h2>Rendering Pressure Legend:</h2>\n"
|
|
<< indent << "<table class=\"code\">\n"
|
|
<< indent + s(2) << "<tr>\n"
|
|
<< indent + s(4) << "<th>Pressure</th><th>Description</th>"
|
|
"<th>Appearance</th>\n"
|
|
<< indent + s(2) << "</tr>\n"
|
|
<< indent + s(2) << "<tr>\n"
|
|
<< indent + s(4) << "<td>No Pressure</td>"
|
|
"<td>No physical registers of this class requested.</td>"
|
|
"<td class=\"p-z\"> </td>\n"
|
|
<< indent + s(2) << "</tr>\n"
|
|
<< indent + s(2) << "<tr>\n"
|
|
<< indent + s(4) << "<td>Low Pressure</td>"
|
|
"<td>Sufficient physical registers to meet demand.</td>"
|
|
"<td class=\"p-l\"> </td>\n"
|
|
<< indent + s(2) << "</tr>\n"
|
|
<< indent + s(2) << "<tr>\n"
|
|
<< indent + s(4) << "<td>High Pressure</td>"
|
|
"<td>Potentially insufficient physical registers to meet demand.</td>"
|
|
"<td class=\"p-h\"> </td>\n"
|
|
<< indent + s(2) << "</tr>\n"
|
|
<< indent << "</table>\n";
|
|
}
|
|
|
|
template <typename CellType>
|
|
void RenderMachineFunction::renderCellsWithRLE(
|
|
const Spacer &indent, raw_ostream &os,
|
|
const std::pair<CellType, unsigned> &rleAccumulator,
|
|
const std::map<CellType, std::string> &cellTypeStrs) const {
|
|
|
|
if (rleAccumulator.second == 0)
|
|
return;
|
|
|
|
typename std::map<CellType, std::string>::const_iterator ctsItr =
|
|
cellTypeStrs.find(rleAccumulator.first);
|
|
|
|
assert(ctsItr != cellTypeStrs.end() && "No string for given cell type.");
|
|
|
|
os << indent + s(4) << "<td class=\"" << ctsItr->second << "\"";
|
|
if (rleAccumulator.second > 1)
|
|
os << " colspan=" << rleAccumulator.second;
|
|
os << "></td>\n";
|
|
}
|
|
|
|
|
|
void RenderMachineFunction::renderCodeTablePlusPI(const Spacer &indent,
|
|
raw_ostream &os) const {
|
|
|
|
std::map<LiveState, std::string> lsStrs;
|
|
lsStrs[Dead] = "l-n";
|
|
lsStrs[Defined] = "l-d";
|
|
lsStrs[Used] = "l-u";
|
|
lsStrs[AliveReg] = "l-r";
|
|
lsStrs[AliveStack] = "l-s";
|
|
|
|
std::map<PressureState, std::string> psStrs;
|
|
psStrs[Zero] = "p-z";
|
|
psStrs[Low] = "p-l";
|
|
psStrs[High] = "p-h";
|
|
|
|
// Open the table...
|
|
|
|
os << indent << "<table cellpadding=0 cellspacing=0 class=\"code\">\n"
|
|
<< indent + s(2) << "<tr>\n";
|
|
|
|
// Render the header row...
|
|
|
|
os << indent + s(4) << "<th>index</th>\n"
|
|
<< indent + s(4) << "<th>instr</th>\n";
|
|
|
|
// Render class names if necessary...
|
|
if (!ro.regClasses().empty()) {
|
|
for (MFRenderingOptions::RegClassSet::const_iterator
|
|
rcItr = ro.regClasses().begin(),
|
|
rcEnd = ro.regClasses().end();
|
|
rcItr != rcEnd; ++rcItr) {
|
|
const TargetRegisterClass *trc = *rcItr;
|
|
os << indent + s(4) << "<th>\n";
|
|
renderVertical(indent + s(6), os, trc->getName());
|
|
os << indent + s(4) << "</th>\n";
|
|
}
|
|
}
|
|
|
|
// FIXME: Is there a nicer way to insert space between columns in HTML?
|
|
if (!ro.regClasses().empty() && !ro.intervals().empty())
|
|
os << indent + s(4) << "<th> </th>\n";
|
|
|
|
// Render interval numbers if necessary...
|
|
if (!ro.intervals().empty()) {
|
|
for (MFRenderingOptions::IntervalSet::const_iterator
|
|
liItr = ro.intervals().begin(),
|
|
liEnd = ro.intervals().end();
|
|
liItr != liEnd; ++liItr) {
|
|
|
|
const LiveInterval *li = *liItr;
|
|
os << indent + s(4) << "<th>\n";
|
|
renderVertical(indent + s(6), os, li->reg);
|
|
os << indent + s(4) << "</th>\n";
|
|
}
|
|
}
|
|
|
|
os << indent + s(2) << "</tr>\n";
|
|
|
|
// End header row, start with the data rows...
|
|
|
|
MachineInstr *mi = 0;
|
|
|
|
// Data rows:
|
|
for (SlotIndex i = sis->getZeroIndex(); i != sis->getLastIndex();
|
|
i = i.getNextSlot()) {
|
|
|
|
// Render the slot column.
|
|
os << indent + s(2) << "<tr height=6ex>\n";
|
|
|
|
// Render the code column.
|
|
if (i.isBlock()) {
|
|
MachineBasicBlock *mbb = sis->getMBBFromIndex(i);
|
|
mi = sis->getInstructionFromIndex(i);
|
|
|
|
if (i == sis->getMBBStartIdx(mbb) || mi != 0 ||
|
|
ro.renderEmptyIndexes()) {
|
|
os << indent + s(4) << "<td rowspan=4>" << i << " </td>\n"
|
|
<< indent + s(4) << "<td rowspan=4>\n";
|
|
|
|
if (i == sis->getMBBStartIdx(mbb)) {
|
|
os << indent + s(6) << "BB#" << mbb->getNumber() << ": \n";
|
|
} else if (mi != 0) {
|
|
os << indent + s(6) << " ";
|
|
renderMachineInstr(os, mi);
|
|
} else {
|
|
// Empty interval - leave blank.
|
|
}
|
|
os << indent + s(4) << "</td>\n";
|
|
} else {
|
|
i = i.getDeadSlot(); // <- Will be incremented to the next index.
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Render the class columns.
|
|
if (!ro.regClasses().empty()) {
|
|
std::pair<PressureState, unsigned> psRLEAccumulator(Zero, 0);
|
|
for (MFRenderingOptions::RegClassSet::const_iterator
|
|
rcItr = ro.regClasses().begin(),
|
|
rcEnd = ro.regClasses().end();
|
|
rcItr != rcEnd; ++rcItr) {
|
|
const TargetRegisterClass *trc = *rcItr;
|
|
PressureState newPressure = getPressureStateAt(trc, i);
|
|
|
|
if (newPressure == psRLEAccumulator.first) {
|
|
++psRLEAccumulator.second;
|
|
} else {
|
|
renderCellsWithRLE(indent + s(4), os, psRLEAccumulator, psStrs);
|
|
psRLEAccumulator.first = newPressure;
|
|
psRLEAccumulator.second = 1;
|
|
}
|
|
}
|
|
renderCellsWithRLE(indent + s(4), os, psRLEAccumulator, psStrs);
|
|
}
|
|
|
|
// FIXME: Is there a nicer way to insert space between columns in HTML?
|
|
if (!ro.regClasses().empty() && !ro.intervals().empty())
|
|
os << indent + s(4) << "<td width=2em></td>\n";
|
|
|
|
if (!ro.intervals().empty()) {
|
|
std::pair<LiveState, unsigned> lsRLEAccumulator(Dead, 0);
|
|
for (MFRenderingOptions::IntervalSet::const_iterator
|
|
liItr = ro.intervals().begin(),
|
|
liEnd = ro.intervals().end();
|
|
liItr != liEnd; ++liItr) {
|
|
const LiveInterval *li = *liItr;
|
|
LiveState newLiveness = getLiveStateAt(li, i);
|
|
|
|
if (newLiveness == lsRLEAccumulator.first) {
|
|
++lsRLEAccumulator.second;
|
|
} else {
|
|
renderCellsWithRLE(indent + s(4), os, lsRLEAccumulator, lsStrs);
|
|
lsRLEAccumulator.first = newLiveness;
|
|
lsRLEAccumulator.second = 1;
|
|
}
|
|
}
|
|
renderCellsWithRLE(indent + s(4), os, lsRLEAccumulator, lsStrs);
|
|
}
|
|
os << indent + s(2) << "</tr>\n";
|
|
}
|
|
|
|
os << indent << "</table>\n";
|
|
|
|
if (!ro.regClasses().empty())
|
|
renderPressureTableLegend(indent, os);
|
|
}
|
|
|
|
void RenderMachineFunction::renderFunctionPage(
|
|
raw_ostream &os,
|
|
const char * const renderContextStr) const {
|
|
os << "<html>\n"
|
|
<< s(2) << "<head>\n"
|
|
<< s(4) << "<title>" << fqn << "</title>\n";
|
|
|
|
insertCSS(s(4), os);
|
|
|
|
os << s(2) << "<head>\n"
|
|
<< s(2) << "<body >\n";
|
|
|
|
renderFunctionSummary(s(4), os, renderContextStr);
|
|
|
|
os << s(4) << "<br/><br/><br/>\n";
|
|
|
|
//renderLiveIntervalInfoTable(" ", os);
|
|
|
|
os << s(4) << "<br/><br/><br/>\n";
|
|
|
|
renderCodeTablePlusPI(s(4), os);
|
|
|
|
os << s(2) << "</body>\n"
|
|
<< "</html>\n";
|
|
}
|
|
|
|
void RenderMachineFunction::getAnalysisUsage(AnalysisUsage &au) const {
|
|
au.addRequired<SlotIndexes>();
|
|
au.addRequired<LiveIntervals>();
|
|
au.setPreservesAll();
|
|
MachineFunctionPass::getAnalysisUsage(au);
|
|
}
|
|
|
|
bool RenderMachineFunction::runOnMachineFunction(MachineFunction &fn) {
|
|
|
|
mf = &fn;
|
|
mri = &mf->getRegInfo();
|
|
tri = mf->getTarget().getRegisterInfo();
|
|
lis = &getAnalysis<LiveIntervals>();
|
|
sis = &getAnalysis<SlotIndexes>();
|
|
|
|
trei.setup(mf, mri, tri, lis);
|
|
ro.setup(mf, tri, lis, this);
|
|
spillIntervals.clear();
|
|
spillFor.clear();
|
|
useDefs.clear();
|
|
|
|
fqn = mf->getFunction()->getParent()->getModuleIdentifier() + "." +
|
|
mf->getFunction()->getName().str();
|
|
|
|
return false;
|
|
}
|
|
|
|
void RenderMachineFunction::releaseMemory() {
|
|
trei.clear();
|
|
ro.clear();
|
|
spillIntervals.clear();
|
|
spillFor.clear();
|
|
useDefs.clear();
|
|
}
|
|
|
|
void RenderMachineFunction::rememberUseDefs(const LiveInterval *li) {
|
|
|
|
if (!ro.shouldRenderCurrentMachineFunction())
|
|
return;
|
|
|
|
for (MachineRegisterInfo::reg_iterator rItr = mri->reg_begin(li->reg),
|
|
rEnd = mri->reg_end();
|
|
rItr != rEnd; ++rItr) {
|
|
const MachineInstr *mi = &*rItr;
|
|
if (mi->readsRegister(li->reg)) {
|
|
useDefs[li].insert(lis->getInstructionIndex(mi).getRegSlot(true));
|
|
}
|
|
if (mi->definesRegister(li->reg)) {
|
|
useDefs[li].insert(lis->getInstructionIndex(mi).getRegSlot());
|
|
}
|
|
}
|
|
}
|
|
|
|
void RenderMachineFunction::rememberSpills(
|
|
const LiveInterval *li,
|
|
const std::vector<LiveInterval*> &spills) {
|
|
|
|
if (!ro.shouldRenderCurrentMachineFunction())
|
|
return;
|
|
|
|
for (std::vector<LiveInterval*>::const_iterator siItr = spills.begin(),
|
|
siEnd = spills.end();
|
|
siItr != siEnd; ++siItr) {
|
|
const LiveInterval *spill = *siItr;
|
|
spillIntervals[li].insert(spill);
|
|
spillFor[spill] = li;
|
|
}
|
|
}
|
|
|
|
bool RenderMachineFunction::isSpill(const LiveInterval *li) const {
|
|
SpillForMap::const_iterator sfItr = spillFor.find(li);
|
|
if (sfItr == spillFor.end())
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
void RenderMachineFunction::renderMachineFunction(
|
|
const char *renderContextStr,
|
|
const VirtRegMap *vrm,
|
|
const char *renderSuffix) {
|
|
if (!ro.shouldRenderCurrentMachineFunction())
|
|
return;
|
|
|
|
this->vrm = vrm;
|
|
trei.reset();
|
|
|
|
std::string rpFileName(mf->getFunction()->getName().str() +
|
|
(renderSuffix ? renderSuffix : "") +
|
|
outputFileSuffix);
|
|
|
|
std::string errMsg;
|
|
raw_fd_ostream outFile(rpFileName.c_str(), errMsg, raw_fd_ostream::F_Binary);
|
|
|
|
renderFunctionPage(outFile, renderContextStr);
|
|
|
|
ro.resetRenderSpecificOptions();
|
|
}
|
|
|
|
std::string RenderMachineFunction::escapeChars(const std::string &s) const {
|
|
return escapeChars(s.begin(), s.end());
|
|
}
|
|
|
|
}
|