Skip to content

Commit

Permalink
Interference graph and correct register allocation (#103)
Browse files Browse the repository at this point in the history
Fixes #74
  • Loading branch information
doe300 authored Jun 28, 2018
1 parent cade43a commit 910cce4
Show file tree
Hide file tree
Showing 8 changed files with 266 additions and 200 deletions.
153 changes: 153 additions & 0 deletions src/analysis/InterferenceGraph.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/*
* Author: doe300
*
* See the file "LICENSE" for the full license governing this code.
*/

#include "InterferenceGraph.h"

#include "../Method.h"
#include "../Profiler.h"
#include "ControlFlowGraph.h"
#include "DebugGraph.h"
#include "LivenessAnalysis.h"

using namespace vc4c;
using namespace vc4c::analysis;

FastSet<InterferenceNode*> InterferenceGraph::findOverfullNodes(std::size_t numNeighbors)
{
FastSet<InterferenceNode*> results;

for(auto& node : nodes)
{
if(node.second.getEdgesSize() >= numNeighbors)
results.emplace(&node.second);
}

return results;
}

std::unique_ptr<InterferenceGraph> InterferenceGraph::createGraph(Method& method)
{
PROFILE_START(createInterferenceGraph);
std::unique_ptr<InterferenceGraph> graph(new InterferenceGraph());

FastMap<BasicBlock*, LivenessAnalysis> livenesses;
livenesses.reserve(method.getCFG().getNodes().size());
for(auto& block : method)
{
auto& blockAnalysis = livenesses[&block];
blockAnalysis(block);
}

// tracks local life-ranges stemming from local being used in succeeding blocks (and written in this block or
// before)
FastMap<const intermediate::IntermediateInstruction*, FastSet<Local*>> additionalLocals;
for(auto& pair : livenesses)
{
if(!pair.second.getStartResult().empty())
{
// there are dependencies from other blocks. For each local, walk the CFG back until we meet the write or
// until we meet another liveness-range of the local
// TODO is there a better/more efficient way?
for(auto& local : pair.second.getStartResult())
{
FastSet<BasicBlock*> blocksVisited;
InstructionVisitor v{
[&](InstructionWalker& it) -> InstructionVisitResult {
// skip the own label itself
if(it.get() == pair.first->getLabel())
return InstructionVisitResult::CONTINUE;
if(it.has<intermediate::BranchLabel>())
{
if(it.getBasicBlock()->isStartOfMethod())
// do not repeat work-group loop
return InstructionVisitResult::STOP_BRANCH;
if(blocksVisited.find(it.getBasicBlock()) != blocksVisited.end())
// loop, abort after one iteration
return InstructionVisitResult::STOP_BRANCH;
blocksVisited.emplace(it.getBasicBlock());
}

auto addLocalsIt = additionalLocals.find(it.get());
auto& lives = livenesses.at(it.getBasicBlock()).getResult(it.get());
if(it->writesLocal(local) || lives.find(local) != lives.end() ||
(addLocalsIt != additionalLocals.end() &&
addLocalsIt->second.find(const_cast<Local*>(local)) != addLocalsIt->second.end()))
return InstructionVisitResult::STOP_BRANCH;
additionalLocals[it.get()].emplace(const_cast<Local*>(local));
return InstructionVisitResult::CONTINUE;
},
false, true};
v.visitReverse(pair.first->begin(), &method.getCFG());
}
}
}

for(auto& block : method)
{
auto& blockAnalysis = livenesses[&block];
for(auto it = block.begin(); !it.isEndOfBlock(); it.nextInBlock())
{
// combined operations can write multiple locals
const auto combInstr = it.get<const intermediate::CombinedOperation>();
if(combInstr && combInstr->op1 && combInstr->op1->hasValueType(ValueType::LOCAL) && combInstr->op2 &&
combInstr->op2->hasValueType(ValueType::LOCAL) &&
combInstr->op1->getOutput()->local != combInstr->op2->getOutput()->local)
{
graph->getOrCreateNode(combInstr->op1->getOutput()->local)
.getOrCreateEdge(
&graph->getOrCreateNode(combInstr->op2->getOutput()->local), InterferenceType::USED_TOGETHER)
.data = InterferenceType::USED_TOGETHER;
}
// instructions in general can read multiple locals
FastSet<Local*> localsRead;
// we have a maximum of 4 locals per (combined) instruction
localsRead.reserve(4);
it->forUsedLocals([&](const Local* loc, LocalUse::Type type) {
if(has_flag(type, LocalUse::Type::READER) && !loc->type.isLabelType())
localsRead.emplace(const_cast<Local*>(loc));
});
if(localsRead.size() > 1)
{
for(auto locIt = localsRead.begin(); locIt != localsRead.end(); ++locIt)
{
auto& firstNode = graph->getOrCreateNode(*locIt);
auto locIt2 = locIt;
for(++locIt2; locIt2 != localsRead.end(); ++locIt2)
{
firstNode.getOrCreateEdge(&graph->getOrCreateNode(*locIt2), InterferenceType::USED_TOGETHER)
.data = InterferenceType::USED_TOGETHER;
}
}
}

auto blockResults = blockAnalysis.getResult(it.get());
auto addIt = additionalLocals.find(it.get());
if(addIt != additionalLocals.end())
blockResults.insert(addIt->second.begin(), addIt->second.end());

for(auto locIt = blockResults.begin(); locIt != blockResults.end(); ++locIt)
{
auto& firstNode = graph->getOrCreateNode(const_cast<Local*>(*locIt));
auto locIt2 = locIt;
for(++locIt2; locIt2 != blockResults.end(); ++locIt2)
{
firstNode.getOrCreateEdge(
&graph->getOrCreateNode(const_cast<Local*>(*locIt2)), InterferenceType::USED_SIMULTANEOUSLY);
}
}
}
}

PROFILE_END(createInterferenceGraph);

#ifdef DEBUG_MODE
auto nameFunc = [](const Local* loc) -> std::string { return loc->name; };
auto edgeFunc = [](InterferenceType type) -> bool { return !has_flag(type, InterferenceType::USED_TOGETHER); };
DebugGraph<Local*, InterferenceType, Directionality::UNDIRECTED>::dumpGraph(
*graph.get(), "/tmp/vc4c-interference.dot", nameFunc, edgeFunc);
#endif
return graph;
}
59 changes: 59 additions & 0 deletions src/analysis/InterferenceGraph.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Author: doe300
*
* See the file "LICENSE" for the full license governing this code.
*/

#include "../Graph.h"

#include <memory>

#ifndef VC4C_INTERFERENCE_GRAPH
#define VC4C_INTERFERENCE_GRAPH

namespace vc4c
{
class Local;
class Method;

namespace analysis
{
/*
* The type of interference between two locals
*/
enum class InterferenceType
{
/*
* Two locals are in use at the same time, but do not block each other's register-file (only the specific
* register)
*/
USED_SIMULTANEOUSLY = 1,
/*
* Two locals are in use (as inputs or outputs) by the same instructions, so they block each other's
* register-file (at least for physical files A and B)
*/
USED_TOGETHER = 2
};

using InterferenceNode = vc4c::Node<vc4c::Local*, InterferenceType, vc4c::Directionality::UNDIRECTED>;
using Interference = InterferenceNode::EdgeType;

/*
* The interference graph connects locals by how they interfere which each other (are live at the same time)
*/
class InterferenceGraph : public Graph<vc4c::Local*, InterferenceNode>
{
public:
using NodeType = InterferenceNode;
using EdgeType = Interference;
/*
* Returns a set of nodes which have more than the given number of neighbors
*/
FastSet<InterferenceNode*> findOverfullNodes(std::size_t numNeighbors);

static std::unique_ptr<InterferenceGraph> createGraph(Method& method);
};
} /* namespace analysis */
} /* namespace vc4c */

#endif /* VC4C_INTERFERENCE_GRAPH */
29 changes: 25 additions & 4 deletions src/analysis/LivenessAnalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,25 @@ using namespace vc4c::analysis;
LivenessAnalysis::LivenessAnalysis() : LocalAnalysis(LivenessAnalysis::analyzeLiveness, LivenessAnalysis::to_string) {}

FastSet<const Local*> LivenessAnalysis::analyzeLiveness(const intermediate::IntermediateInstruction* instr,
const FastSet<const Local*>& nextResult, FastSet<const Local*>& conditionalWrites)
const FastSet<const Local*>& nextResult,
std::pair<FastSet<const Local*>, FastMap<const Local*, ConditionCode>>& cache)
{
auto& conditionalWrites = cache.first;
auto& conditionalReads = cache.second;

FastSet<const Local*> result(nextResult);

if(instr->hasValueType(ValueType::LOCAL) &&
!instr->hasDecoration(vc4c::intermediate::InstructionDecorations::ELEMENT_INSERTION))
{
if(instr->hasConditionalExecution())
{
if(conditionalWrites.find(instr->getOutput()->local) != conditionalWrites.end())
auto condReadIt = conditionalReads.find(instr->getOutput()->local);
if(condReadIt != conditionalReads.end() && condReadIt->second == instr->conditional &&
condReadIt->first->getSingleWriter() == instr)
// the local only exists within a conditional block (e.g. temporary within the same flag)
result.erase(instr->getOutput()->local);
else if(conditionalWrites.find(instr->getOutput()->local) != conditionalWrites.end())
result.erase(instr->getOutput()->local);
else
conditionalWrites.emplace(instr->getOutput()->local);
Expand All @@ -35,15 +44,27 @@ FastSet<const Local*> LivenessAnalysis::analyzeLiveness(const intermediate::Inte
if(combInstr)
{
if(combInstr->op1)
result = analyzeLiveness(combInstr->op1.get(), result, conditionalWrites);
result = analyzeLiveness(combInstr->op1.get(), result, cache);
if(combInstr->op2)
result = analyzeLiveness(combInstr->op2.get(), result, conditionalWrites);
result = analyzeLiveness(combInstr->op2.get(), result, cache);
}

for(const Value& arg : instr->getArguments())
{
if(arg.hasType(ValueType::LOCAL) && !arg.local->type.isLabelType())
{
result.emplace(arg.local);
if(instr->hasConditionalExecution())
{
// there exist locals which only exist if a certain condition is met, so check this
auto condReadIt = conditionalReads.find(arg.local);
// if the local is read with different conditions, it must exist in any case
if(condReadIt != conditionalReads.end() && condReadIt->second != instr->conditional)
conditionalReads.erase(condReadIt);
else
conditionalReads.emplace(arg.local, instr->conditional);
}
}
}

return result;
Expand Down
7 changes: 4 additions & 3 deletions src/analysis/LivenessAnalysis.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ namespace vc4c
*
* Also called Live Variable Analysis (https://en.wikipedia.org/wiki/Live_variable_analysis)
*/
class LivenessAnalysis
: public LocalAnalysis<AnalysisDirection::BACKWARD, FastSet<const Local*>, FastSet<const Local*>>
class LivenessAnalysis : public LocalAnalysis<AnalysisDirection::BACKWARD, FastSet<const Local*>,
std::pair<FastSet<const Local*>, FastMap<const Local*, ConditionCode>>>
{
public:
explicit LivenessAnalysis();
Expand All @@ -43,7 +43,8 @@ namespace vc4c
* - any other live local remains live
*/
static FastSet<const Local*> analyzeLiveness(const intermediate::IntermediateInstruction* instr,
const FastSet<const Local*>& nextResult, FastSet<const Local*>& conditionalWrites);
const FastSet<const Local*>& nextResult,
std::pair<FastSet<const Local*>, FastMap<const Local*, ConditionCode>>& cache);

static std::string to_string(const FastSet<const Local*>& liveLocals);
};
Expand Down
2 changes: 2 additions & 0 deletions src/analysis/sources.list
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ target_sources(${VC4C_LIBRARY_NAME}
${CMAKE_CURRENT_LIST_DIR}/DebugGraph.h
${CMAKE_CURRENT_LIST_DIR}/DependencyGraph.cpp
${CMAKE_CURRENT_LIST_DIR}/DependencyGraph.h
${CMAKE_CURRENT_LIST_DIR}/InterferenceGraph.cpp
${CMAKE_CURRENT_LIST_DIR}/InterferenceGraph.h
${CMAKE_CURRENT_LIST_DIR}/LifetimeGraph.cpp
${CMAKE_CURRENT_LIST_DIR}/LifetimeGraph.h
${CMAKE_CURRENT_LIST_DIR}/LivenessAnalysis.h
Expand Down
Loading

0 comments on commit 910cce4

Please sign in to comment.