}
}
+ void Function::computeCFG(void) {
+ // Clear possible previously computed CFG
+ this->apply([this](BasicBlock &bb) {
+ bb.successors.clear();
+ bb.predecessors.clear();
+ });
+ // Update it. Do not forget that a branch can also jump to the next block
+ BasicBlock *jumpToNext = NULL;
+ this->apply([this, &jumpToNext](BasicBlock &bb) {
+ if (jumpToNext) {
+ jumpToNext->successors.insert(&bb);
+ bb.predecessors.insert(jumpToNext);
+ jumpToNext = NULL;
+ }
+ if (bb.last == NULL) return;
+ GBE_ASSERT(bb.last->isMemberOf<BranchInstruction>() == true);
+ const BranchInstruction &insn = cast<BranchInstruction>(*bb.last);
+ if (insn.getOpcode() == OP_BRA) {
+ const LabelIndex label = insn.getLabelIndex();
+ BasicBlock *target = this->blocks[label];
+ GBE_ASSERT(target != NULL);
+ target->predecessors.insert(&bb);
+ bb.successors.insert(target);
+ if (insn.isPredicated() == true) jumpToNext = &bb;
+ }
+ });
+ }
+
std::ostream &operator<< (std::ostream &out, const Function &fn)
{
out << ".decl_function " << fn.getName() << std::endl;
#include "ir/instruction.hpp"
#include "ir/profile.hpp"
#include "sys/vector.hpp"
-#include "sys/list.hpp"
+#include "sys/set.hpp"
#include "sys/alloc.hpp"
#include <ostream>
namespace gbe {
namespace ir {
+ /*! Commonly used in the CFG */
+ typedef set<BasicBlock*> BlockSet;
+
/*! Function basic blocks really belong to a function since:
* 1 - registers used in the basic blocks belongs to the function register
* file
/*! Get the parent function */
Function &getParent(void) { return fn; }
const Function &getParent(void) const { return fn; }
+ /*! Get the successors */
+ const BlockSet &getSuccessorSet(void) const { return successors; }
+ /*! Get the predecessors */
+ const BlockSet &getPredecessorSet(void) const { return predecessors; }
private:
friend class Function; //!< Owns the basic blocks
+ BlockSet predecessors; //!< Incoming blocks
+ BlockSet successors; //!< Outgoing blocks
Instruction *first; //!< First instruction in the block
Instruction *last; //!< Last instruction in the block
Function &fn; //!< Function the block belongs to
}
/*! Create a new label (still not bound to a basic block) */
LabelIndex newLabel(void);
+ /*! Create the control flow graph */
+ void computeCFG(void);
/*! Number of registers in the register file */
INLINE uint32_t regNum(void) const { return file.regNum(); }
/*! Number of register tuples in the register file */
/*! Apply the given functor on all basic blocks */
template <typename T>
INLINE void apply(const T &functor) const {
- for (auto it = blocks.begin; it != blocks.end(); ++it)
- functor(*it);
+ for (auto it = blocks.begin(); it != blocks.end(); ++it)
+ functor(**it);
}
private:
friend class Context; //!< Can freely modify a function
* \file liveness.cpp
* \author Benjamin Segovia <benjamin.segovia@intel.com>
*/
-#include <sys/map.hpp>
-#include <sys/set.hpp>
+#include "ir/function.hpp"
+#include "sys/map.hpp"
+#include "sys/set.hpp"
+
+namespace gbe {
+namespace ir {
-namespace gbe
-{
/*! Compute liveness of each register */
class LivenessInfo
{
public:
- LivenessInfo(Function &fn) : fn(fn) {}
+ LivenessInfo(Function &fn);
+ ~LivenessInfo(void);
/*! Set of variables used upwards in the block (before a definition) */
- typedef set<Register> UsedVar;
+ typedef set<Register> UEVar;
/*! Set of variables alive at the exit of the block */
typedef set<Register> LiveOut;
/*! Set of variables actually killed in each block */
- typedef set<Register> Kill;
+ typedef set<Register> VarKill;
/*! Per-block info */
struct BlockInfo {
+ BlockInfo(const BasicBlock &bb) : bb(bb) {}
+ const BasicBlock &bb;
UEVar upwardUsed;
LiveOut liveOut;
- Kill kill;
+ VarKill varKill;
};
/*! Gives for each block the variables alive at entry / exit */
- typedef map<BasicBlock*, BlockInfo> BlockLiveness;
+ typedef map<const BasicBlock*, BlockInfo*> Liveness;
+ private:
+ /*! Store the liveness of all blocks */
+ Liveness liveness;
/*! Compute the liveness for this function */
Function &fn;
+ /*! Initialize UEVar and VarKill per block */
+ void initBlock(const BasicBlock &bb);
+ /*! Initialize UEVar and VarKill per instruction */
+ void initInstruction(BlockInfo &info, const Instruction &insn);
+ /*! Now really compute LiveOut based on UEVar and VarKill */
+ void computeLiveOut(void);
+ /*! Actually do something for each successor of *all* blocks */
+ template <typename T>
+ void forEachSuccessor(const T &functor) {
+ // Iterate on all blocks
+ for (auto it = liveness.begin(); it != liveness.end(); ++it) {
+ BlockInfo &info = *it->second;
+ const BasicBlock &bb = info.bb;
+ const BlockSet set = bb.getSuccessorSet();
+ // Iterate over all successors
+ for (auto other = set.begin(); other != set.end(); ++other) {
+ auto otherInfo = liveness.find(*other);
+ GBE_ASSERT(otherInfo != liveness.end() && otherInfo->second != NULL);
+ functor(info, *otherInfo->second);
+ }
+ }
+ }
};
LivenessInfo::LivenessInfo(Function &fn) : fn(fn) {
-
+ // Initialize UEVar and VarKill for each block
+ fn.apply([this](const BasicBlock &bb) { this->initBlock(bb); });
+ // Now with iterative analysis, we compute liveout sets
+ this->computeLiveOut();
+ }
+
+ LivenessInfo::~LivenessInfo(void) {
+ for (auto it = liveness.begin(); it != liveness.end(); ++it)
+ GBE_SAFE_DELETE(it->second);
+ }
+
+ void LivenessInfo::initBlock(const BasicBlock &bb) {
+ GBE_ASSERT(liveness.find(&bb) == liveness.end());
+ BlockInfo *info = GBE_NEW(BlockInfo, bb);
+ // Traverse all instructions to handle UEVar and VarKill
+ bb.apply([this, info](const Instruction &insn) {
+ this->initInstruction(*info, insn);
+ });
+ liveness[&bb] = info;
+ }
+
+ void LivenessInfo::initInstruction(BlockInfo &info, const Instruction &insn) {
+ const uint32_t srcNum = insn.getSrcNum();
+ const uint32_t dstNum = insn.getDstNum();
+ const Function &fn = info.bb.getParent();
+ // First look for used before killed
+ for (uint32_t srcID = 0; srcID < srcNum; ++srcID) {
+ const Register reg = insn.getSrcIndex(fn, srcID);
+ // Not killed -> it is really an upward use
+ if (info.varKill.find(reg) == info.varKill.end())
+ info.upwardUsed.insert(reg);
+ }
+ // A destination is a killed value
+ for (uint32_t dstID = 0; dstID < dstNum; ++dstID) {
+ const Register reg = insn.getDstIndex(fn, dstID);
+ info.varKill.insert(reg);
+ }
+ }
+ void LivenessInfo::computeLiveOut(void) {
+ // First insert the UEVar from the successors
+ forEachSuccessor([](BlockInfo &info, const BlockInfo &succ) {
+ const UEVar &ueVarSet = succ.upwardUsed;
+ // Iterate over all the registers in the UEVar of our successor
+ for (auto ueVar = ueVarSet.begin(); ueVar != ueVarSet.end(); ++ueVar)
+ info.liveOut.insert(*ueVar);
+ });
+ int counter = 0;
+ // Now iterate on liveOut
+ bool changed = true;
+ while (changed) {
+ changed = false;
+ forEachSuccessor([&changed, &counter](BlockInfo &info, const BlockInfo &succ) {
+ const UEVar &killSet = succ.varKill;
+ const LiveOut &liveOut = succ.liveOut;
+ auto end = killSet.end();
+ // Iterate over all the registers in the UEVar of our successor
+ for (auto living = liveOut.begin(); living != liveOut.end(); ++living) {
+ counter++;
+ if (killSet.find(*living) != end) continue;
+ if (info.liveOut.find(*living) != info.liveOut.end()) continue;
+ info.liveOut.insert(*living);
+ changed = true;
+ }
+ });
+ }
+ std::cout << counter << std::endl;
}
+} /* namespace ir */
} /* namespace gbe */