curr = curr->getSuccessor();
}
}
+ /*! Apply the given functor on all instructions (reverse order) */
+ template <typename T>
+ INLINE void rforeach(const T &functor) const {
+ Instruction *curr = last;
+ while (curr) {
+ functor(*curr);
+ curr = curr->getPredecessor();
+ }
+ }
/*! Get the parent function */
Function &getParent(void) { return fn; }
const Function &getParent(void) const { return fn; }
this->immediates.push_back(imm);
return index;
}
- /*! Allocate a new instruction (with the growing pool) */
- template <typename... Args>
- INLINE Instruction *newInstruction(Args... args) {
- return new (insnPool.allocate()) Instruction(args...);
- }
- /*! Deallocate an instruction (with the growing pool) */
- INLINE void deleteInstruction(Instruction *insn) {
- insnPool.deallocate(insn);
- }
+ /*! Fast allocation / deallocation of instructions */
+ DECL_POOL(Instruction, insnPool);
/*! Get input argument */
INLINE const FunctionInput &getInput(uint32_t ID) const {
GBE_ASSERT(ID < inputNum() && inputs[ID] != NULL);
vector<Immediate> immediates; //!< All immediate values in the function
vector<BasicBlock*> blocks; //!< All chained basic blocks
RegisterFile file; //!< RegisterDatas used by the instructions
- GrowingPool<Instruction> insnPool; //!< For fast instruction allocation
- Profile profile; //!< Current function profile
+ Profile profile; //!< Current function profile
GBE_CLASS(Function);
};
Instruction *predecessor;//!< Previous instruction in the basic block
Instruction *successor; //!< Next instruction in the basic block
BasicBlock *parent; //!< The basic block containing the instruction
+ GBE_CLASS(Instruction); //!< Use internal allocators
};
/*! Output the instruction string in the given stream */
}
void Liveness::initBlock(const BasicBlock &bb) {
- GBE_ASSERT(liveness.find(&bb) == liveness.end());
+ GBE_ASSERT(liveness.contains(&bb) == false);
BlockInfo *info = GBE_NEW(BlockInfo, bb);
// Traverse all instructions to handle UEVar and VarKill
bb.foreach([this, info](const Instruction &insn) {
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())
+ if (info.varKill.contains(reg) == false)
info.upwardUsed.insert(reg);
}
// A destination is a killed value
forEachSuccessor([&changed](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) {
- if (killSet.find(*living) != end) continue;
- if (info.liveOut.find(*living) != info.liveOut.end()) continue;
+ if (killSet.contains(*living)) continue;
+ if (info.liveOut.contains(*living)) continue;
info.liveOut.insert(*living);
changed = true;
}
BlockInfo(const BasicBlock &bb) : bb(bb) {}
const BasicBlock &bb;
INLINE bool inUpwardUsed(Register reg) const {
- return upwardUsed.find(reg) != upwardUsed.end();
+ return upwardUsed.contains(reg);
}
INLINE bool inLiveOut(Register reg) const {
- return liveOut.find(reg) != liveOut.end();
+ return liveOut.contains(reg);
}
INLINE bool inVarKill(Register reg) const {
- return varKill.find(reg) != varKill.end();
+ return varKill.contains(reg);
}
UEVar upwardUsed;
LiveOut liveOut;
typedef map<const BasicBlock*, BlockInfo*> Info;
/*! Return the complete liveness info */
INLINE const Info &getLiveness(void) const { return liveness; }
+ /*! Return the complete block info */
+ INLINE const BlockInfo &getBlockInfo(const BasicBlock &bb) const {
+ auto it = liveness.find(&bb);
+ GBE_ASSERT(it != liveness.end() && it->second != NULL);
+ return *it->second;
+ }
/*! Return the function the liveness was computed on */
INLINE const Function &getFunction(void) const { return fn; }
private:
namespace gbe {
namespace ir {
- GraphUseDef::GraphUseDef(const Liveness &liveness) {
+ /*! To build the chains (i.e. basically the DAG of values), we are going to
+ * iterate on liveout definitions: for each block and for each variable
+ * (ir::Register) alive at the end of the block (in Block::LiveOut), we are
+ * computing the set of all possible value definitions. Using these value
+ * definitions, we will finally transfer these sets to the successors to get
+ * the ud / du chains
+ *
+ * LiveOutSet contains the set of definitions for each basic block
+ */
+ class LiveOutSet
+ {
+ public:
+ LiveOutSet(const Liveness &liveness, const FunctionDAG &dag);
+ ~LiveOutSet(void);
+ /*! One set per register */
+ typedef set<const ValueDef*> RegDefSet;
+ /*! We have one map of liveout register per block */
+ typedef map<Register, RegDefSet*> BlockDefMap;
+ /*! All the block definitions map in the functions */
+ typedef map<const BasicBlock*, BlockDefMap*> FunctionDefMap;
+ FunctionDefMap defMap; //!< All per-block data
+ const Liveness &liveness; //!< Contains LiveOut information
+ const FunctionDAG &dag; //!< Structure we are building
+ DECL_POOL(RegDefSet, regDefSetPool);
+ DECL_POOL(BlockDefMap, blockDefMapPool);
+ };
+
+ LiveOutSet::LiveOutSet(const Liveness &liveness, const FunctionDAG &dag) :
+ liveness(liveness), dag(dag)
+ {
const Function &fn = liveness.getFunction();
+ // Iterate over each block
+ fn.foreachBlock([&](const BasicBlock &bb) {
+ GBE_ASSERT(defMap.find(&bb) == defMap.end());
+
+ // Allocate a map of register definition
+ auto blockDefMap = this->newBlockDefMap();
+ defMap.insert(std::make_pair(&bb, blockDefMap));
+
+ // We only consider liveout registers
+ auto info = this->liveness.getBlockInfo(bb);
+ auto liveOut = info.liveOut;
+ for (auto it = liveOut.begin(); it != liveOut.end(); ++it) {
+ GBE_ASSERT(blockDefMap->find(*it) == blockDefMap->end());
+ auto regDefSet = this->newRegDefSet();
+ blockDefMap->insert(std::make_pair(*it, regDefSet));
+ }
+
+ // Now traverse the blocks backwards and find the definition of each
+ // liveOut register
+ set<Register> defined; // Liveout registers for which we found a def
+ bb.rforeach([&](const Instruction &insn) {
+ const uint32_t dstNum = insn.getDstNum();
+ for (uint32_t dstID = 0; dstID < dstNum; ++dstID) {
+ const Register reg = insn.getDstIndex(fn, dstID);
+ // We only take the most recent definition
+ if (defined.contains(reg) == false) continue;
+ // Not in LiveOut, so does not matter
+ if (info.inLiveOut(reg) == false) continue;
+ // Insert the outgoing definition for this register
+ auto regDefSet = blockDefMap->find(reg);
+ const ValueDef *def = this->dag.getDefAddress(insn, dstID);
+ GBE_ASSERT(regDefSet != blockDefMap->end() && def != NULL);
+ regDefSet->second->insert(def);
+ }
+ });
+ });
+ }
+
+ LiveOutSet::~LiveOutSet(void) {
+ for (auto it = defMap.begin(); it != defMap.end(); ++it) {
+ BlockDefMap *block = it->second;
+ for (auto regSet = block->begin();regSet != block->end(); ++regSet)
+ this->deleteRegDefSet(regSet->second);
+ this->deleteBlockDefMap(block);
+ }
+ }
+
+ FunctionDAG::FunctionDAG(const Liveness &liveness) {
+ const Function &fn = liveness.getFunction();
+ LiveOutSet p(liveness, *this);
+ // We first start with empty chains
+ udEmpty = this->newUDChain(); udEmpty->second = NULL;
+ duEmpty = this->newDUChain(); duEmpty->second = NULL;
+
// First create the chains and insert them in their respective maps
- fn.foreachInstruction([this](const Instruction &insn) {
+ fn.foreachInstruction([this, udEmpty, duEmpty](const Instruction &insn) {
+
// sources == value uses
const uint32_t srcNum = insn.getSrcNum();
for (uint32_t srcID = 0; srcID < srcNum; ++srcID) {
ValueUse *valueUse = this->newValueUse(insn, srcID);
- UDChain *udChain = this->newUDChain();
- udChain->first = valueUse;
- udGraph.insert(std::make_pair(*valueUse, udChain));
+ udGraph.insert(std::make_pair(*valueUse, udEmpty));
}
// destinations == value defs
const uint32_t dstNum = insn.getDstNum();
for (uint32_t dstID = 0; dstID < dstNum; ++dstID) {
ValueDef *valueDef = this->newValueDef(insn, dstID);
- DUChain *duChain = this->newDUChain();
- duChain->first = valueDef;
- duGraph.insert(std::make_pair(*valueDef, duChain));
+ duGraph.insert(std::make_pair(*valueDef, duEmpty));
}
});
for (uint32_t inputID = 0; inputID < inputNum; ++inputID) {
const FunctionInput &input = fn.getInput(inputID);
ValueDef *valueDef = this->newValueDef(input);
- DUChain *duChain = this->newDUChain();
- duChain->first = valueDef;
- duGraph.insert(std::make_pair(*valueDef, duChain));
+ duGraph.insert(std::make_pair(*valueDef, duEmpty));
+ }
+ }
+
+/*! Helper to deallocate objects */
+#define PTR_RELEASE(TYPE, VAR) \
+ do { \
+ if (VAR && destroyed.contains(VAR) == false) { \
+ destroyed.insert(VAR); \
+ delete##TYPE(VAR); \
+ } \
+ } while (0)
+
+ FunctionDAG::~FunctionDAG(void) {
+
+ // We track the already destroyed pointers
+ set<void*> destroyed;
+
+ // Release the empty ud-chains and du-chains
+ PTR_RELEASE(ValueUse, udEmpty->second);
+ PTR_RELEASE(ValueDef, duEmpty->second);
+ PTR_RELEASE(UDChain, udEmpty);
+ PTR_RELEASE(DUChain, duEmpty);
+
+ // We free all the ud-chains
+ for (auto it = udGraph.begin(); it != udGraph.end(); ++it) {
+ auto udChain = it->second;
+ auto defs = udChain->first;
+ for (auto def = defs.begin(); def != defs.end(); ++def)
+ PTR_RELEASE(ValueDef, *def);
+ PTR_RELEASE(ValueUse, udChain->second);
+ PTR_RELEASE(UDChain, udChain);
}
+
+ // We free all the du-chains
+ for (auto it = duGraph.begin(); it != duGraph.end(); ++it) {
+ auto duChain = it->second;
+ auto uses = duChain->first;
+ for (auto use = uses.begin(); use != uses.end(); ++use)
+ PTR_RELEASE(ValueUse, *use);
+ PTR_RELEASE(ValueDef, duChain->second);
+ PTR_RELEASE(DUChain, duChain);
+ }
+ }
+#undef PTR_RELEASE
+
+ const DUChain &FunctionDAG::getDUChain(const Instruction &insn, uint32_t dstID) const {
+ const ValueDef def(insn, dstID);
+ auto it = duGraph.find(def);
+ GBE_ASSERT(it != duGraph.end());
+ return *it->second;
}
+ const DUChain &FunctionDAG::getDUChain(const FunctionInput &input) const {
+ const ValueDef def(input);
+ auto it = duGraph.find(def);
+ GBE_ASSERT(it != duGraph.end());
+ return *it->second;
+ }
+
+ const UDChain &FunctionDAG::getUDChain(const Instruction &insn, uint32_t srcID) const {
+ const ValueUse use(insn, srcID);
+ auto it = udGraph.find(use);
+ GBE_ASSERT(it != udGraph.end());
+ return *it->second;
+ }
+ const ValueUseSet &FunctionDAG::getUse(const Instruction &insn, uint32_t dstID) const {
+ const DUChain &chain = this->getDUChain(insn, dstID);
+ return chain.first;
+ }
+ const ValueUseSet &FunctionDAG::getUse(const FunctionInput &input) const {
+ const DUChain &chain = this->getDUChain(input);
+ return chain.first;
+ }
+ const ValueDefSet &FunctionDAG::getDef(const Instruction &insn, uint32_t srcID) const {
+ const UDChain &chain = this->getUDChain(insn, srcID);
+ return chain.first;
+ }
+ const ValueDef *FunctionDAG::getDefAddress(const Instruction &insn, uint32_t dstID) const {
+ const DUChain &chain = this->getDUChain(insn, dstID);
+ return chain.second;
+ }
+ const ValueDef *FunctionDAG::getDefAddress(const FunctionInput &input) const {
+ const DUChain &chain = this->getDUChain(input);
+ return chain.second;
+ }
+ const ValueUse *FunctionDAG::getUseAddress(const Instruction &insn, uint32_t srcID) const {
+ const UDChain &chain = this->getUDChain(insn, srcID);
+ return chain.second;
+ }
} /* namespace ir */
} /* namespace gbe */
INSTRUCTION_DST = 1
};
/*! Build a value from an instruction destination */
- ValueDef(Instruction &insn, uint32_t dstID = 0u) : type(INSTRUCTION_DST) {
+ ValueDef(const Instruction &insn, uint32_t dstID = 0u) :
+ type(INSTRUCTION_DST)
+ {
this->data.insn = &insn;
this->data.dstID = dstID;
}
/*! Build a value from a function argument */
- ValueDef(FunctionInput &input) : type(FUNCTION_INPUT) {
+ ValueDef(const FunctionInput &input) : type(FUNCTION_INPUT) {
this->data.input = &input;
}
/*! Get the type of the value */
INLINE Type getType(void) const { return type; }
/*! Get the instruction (only if this is a instruction value) */
- INLINE Instruction *getInstruction(void) const {
+ INLINE const Instruction &getInstruction(void) const {
GBE_ASSERT(type == INSTRUCTION_DST);
- return data.insn;
+ return *data.insn;
}
/*! Get the destination ID (only if this is a instruction value) */
INLINE uint32_t getDstID(void) const {
return data.dstID;
}
/*! Get the function input (only if this is a function argument) */
- INLINE FunctionInput *getFunctionInput(void) const {
+ INLINE const FunctionInput &getFunctionInput(void) const {
GBE_ASSERT(type == FUNCTION_INPUT);
- return data.input;
+ return *data.input;
}
private:
union Data {
/*! Instruction destination or ... */
struct {
- Instruction *insn; //<! Instruction itself
- uint32_t dstID; //<! Which destination we take into account
+ const Instruction *insn; //<! Instruction itself
+ uint32_t dstID; //<! Which destination we take into account
};
/*! ... function argument */
- FunctionInput *input;
+ const FunctionInput *input;
} data;
/*!< Function argument or instruction dst? */
Type type;
};
/*! Compare two value definitions (used in maps) */
- INLINE bool operator < (const ValueDef &def0, const ValueDef &def1) {
+ INLINE bool operator< (const ValueDef &def0, const ValueDef &def1) {
const ValueDef::Type type0 = def0.getType();
const ValueDef::Type type1 = def1.getType();
if (type0 != type1) return uint32_t(type0) < uint32_t(type1);
if (type0 == ValueDef::FUNCTION_INPUT) {
- const FunctionInput *in0 = def0.getFunctionInput();
- const FunctionInput *in1 = def1.getFunctionInput();
+ const FunctionInput *in0 = &def0.getFunctionInput();
+ const FunctionInput *in1 = &def1.getFunctionInput();
return uintptr_t(in0) < uintptr_t(in1);
} else {
- const Instruction *insn0 = def0.getInstruction();
- const Instruction *insn1 = def1.getInstruction();
+ const Instruction *insn0 = &def0.getInstruction();
+ const Instruction *insn1 = &def1.getInstruction();
if (insn0 != insn1) return uintptr_t(insn0) < uintptr_t(insn1);
const uint32_t dst0 = def0.getDstID();
- const uint32_t dst1 = def0.getDstID();
+ const uint32_t dst1 = def1.getDstID();
return dst0 < dst1;
}
}
ValueUse(const Instruction &insn, uint32_t srcID = 0u) :
insn(&insn), srcID(srcID) {}
/*! Get the instruction of the use */
- const Instruction *getInstruction(void) const { return insn; }
+ const Instruction &getInstruction(void) const { return *insn; }
/*! Get the source index for this use */
uint32_t getSrcID(void) const { return srcID; }
private:
};
/*! Compare two value uses (used in maps) */
- INLINE bool operator < (const ValueUse &use0, const ValueUse &use1) {
- const Instruction *insn0 = use0.getInstruction();
- const Instruction *insn1 = use1.getInstruction();
+ INLINE bool operator< (const ValueUse &use0, const ValueUse &use1) {
+ const Instruction *insn0 = &use0.getInstruction();
+ const Instruction *insn1 = &use1.getInstruction();
if (insn0 != insn1) return uintptr_t(insn0) < uintptr_t(insn1);
const uint32_t src0 = use0.getSrcID();
- const uint32_t src1 = use0.getSrcID();
+ const uint32_t src1 = use1.getSrcID();
return src0 < src1;
}
/*! All uses of a definition */
- typedef std::pair<ValueDef*, set<ValueUse*>> DUChain;
+ typedef set<ValueUse*> ValueUseSet;
+ typedef std::pair<ValueUseSet, ValueDef*> DUChain;
/*! All possible definitions for a use */
- typedef std::pair<ValueUse*, set<ValueDef*>> UDChain;
+ typedef set<ValueDef*> ValueDefSet;
+ typedef std::pair<ValueDefSet, ValueUse*> UDChain;
/*! Get the chains (in both directions) for the complete program */
- class GraphUseDef
+ class FunctionDAG
{
public:
/*! Build the complete DU/UD graphs for the program included in liveness */
- GraphUseDef(const Liveness &liveness);
+ FunctionDAG(const Liveness &liveness);
+ /*! Free all the resources */
+ ~FunctionDAG(void);
+ /*! Get the du-chain for the given instruction and destination */
+ const DUChain &getDUChain(const Instruction &insn, uint32_t dstID) const;
+ /*! Get the du-chain for the given function input */
+ const DUChain &getDUChain(const FunctionInput &input) const;
+ /*! Get the ud-chain for the instruction and source */
+ const UDChain &getUDChain(const Instruction &insn, uint32_t srcID) const;
+ /*! Get the use set for the given definition */
+ const ValueUseSet &getUse(const Instruction &insn, uint32_t dstID) const;
+ /*! Get the use set for the function argument */
+ const ValueUseSet &getUse(const FunctionInput &input) const;
+ /*! Get the definition set for the given source */
+ const ValueDefSet &getDef(const Instruction &insn, uint32_t srcID) const;
+ /*! Get the pointer to the definition *as stored in the DAG* */
+ const ValueDef *getDefAddress(const Instruction &insn, uint32_t dstID) const;
+ /*! Get the pointer to the definition *as stored in the DAG* */
+ const ValueDef *getDefAddress(const FunctionInput &input) const;
+ /*! Get the pointer to the use *as stored in the DAG* */
+ const ValueUse *getUseAddress(const Instruction &insn, uint32_t srcID) const;
/*! The UDChain for each definition use */
typedef map<ValueUse, UDChain*> UDGraph;
/*! The DUChain for each definition */
typedef map<ValueDef, DUChain*> DUGraph;
private:
- UDGraph udGraph; //!< All the UD chains
- DUGraph duGraph; //!< All the DU chains
- GrowingPool<ValueUse> valueUsePool; //!< Allocate the value uses
- GrowingPool<ValueDef> valueDefPool; //!< Allocate the value defs
- GrowingPool<UDChain> udChainPool; //!< Allocate all the ud-chains
- GrowingPool<DUChain> duChainPool; //!< Allocate all the du-chains
-#define DECL_ALLOCATE_DEALLOCATE(TYPE, POOL) \
- template <typename... Args> \
- INLINE TYPE *new##TYPE(Args... args) { \
- return new (POOL.allocate()) TYPE(args...); \
- } \
- INLINE void delete##TYPE(TYPE *ptr) { POOL.deallocate(ptr); }
- DECL_ALLOCATE_DEALLOCATE(ValueDef, valueDefPool)
- DECL_ALLOCATE_DEALLOCATE(ValueUse, valueUsePool)
- DECL_ALLOCATE_DEALLOCATE(UDChain, udChainPool)
- DECL_ALLOCATE_DEALLOCATE(DUChain, duChainPool)
-#undef DECL_ALLOCATE_DEALLOCATE
- GBE_CLASS(GraphUseDef);
+ UDGraph udGraph; //!< All the UD chains
+ DUGraph duGraph; //!< All the DU chains
+ UDChain *udEmpty; //!< For all empty ud chains
+ DUChain *duEmpty; //!< For all empty du chains
+ DECL_POOL(ValueDef, valueDefPool); //!< Fast ValueDef allocation
+ DECL_POOL(ValueUse, valueUsePool); //!< Fast ValueUse allocation
+ DECL_POOL(UDChain, udChainPool); //!< Fast UDChain allocation
+ DECL_POOL(DUChain, duChainPool); //!< Fast DUChain allocation
+ GBE_CLASS(FunctionDAG); //!< Use internal allocators
};
} /* namespace ir */
else \
return gbe::memFree(ptr); \
} \
+ void* operator new(size_t size, void *p) { return p; } \
+ void* operator new[](size_t size, void *p) { return p; } \
/*! Declare a class with custom allocators */
#define GBE_CLASS(TYPE) \
GBE_STRUCT(TYPE) \
private:
-/*! Declare an aligned structure */
-#define GBE_ALIGNED_STRUCT(ALIGN) \
- void* operator new(size_t size) { return gbe::alignedMalloc(size, ALIGN); } \
- void* operator new[](size_t size) { return gbe::alignedMalloc(size, ALIGN); } \
- void operator delete(void* ptr) { gbe::alignedFree(ptr); } \
- void operator delete[](void* ptr) { gbe::alignedFree(ptr); }
-
-/*! Declare an aligned class */
-#define GBE_ALIGNED_CLASS(ALIGN) \
-public: \
- GBE_ALIGNED_STRUCT(ALIGN) \
-private:
-
/*! Macros to handle allocation position */
#define GBE_NEW(T,...) \
gbe::_MemDebuggerInsertAlloc(new T(__VA_ARGS__), __FILE__, __FUNCTION__, __LINE__)
void *freeList; //!< Elements that have been deallocated
GBE_CLASS(GrowingPool);
};
+
+/*! Helper macros to build and destroy objects with a pool */
+#define DECL_POOL(TYPE, POOL) \
+ GrowingPool<TYPE> POOL; \
+ template <typename... Args> \
+ INLINE TYPE *new##TYPE(Args... args) { \
+ return new (POOL.allocate()) TYPE(args...); \
+ } \
+ INLINE void delete##TYPE(TYPE *ptr) { \
+ ptr->~TYPE(); \
+ POOL.deallocate(ptr); \
+ }
} /* namespace gbe */
#endif /* __GBE_ALLOC_HPP__ */
parent_type(first, last, comp, a) {}
/*! Copy constructor */
INLINE map(const map& x) : parent_type(x) {}
+ /*! Better than using find if we do not care about the iterator itself */
+ INLINE bool contains(const Key &key) const {
+ return this->find(key) != this->end();
+ }
GBE_CLASS(map);
};
} /* namespace gbe */
parent_type(first, last, comp, a) {}
/*! Copy constructor */
INLINE set(const set& x) : parent_type(x) {}
+ /*! Better than using find if we do not care about the iterator itself */
+ INLINE bool contains(const Key &key) const {
+ return this->find(key) != this->end();
+ }
GBE_CLASS(set);
};
#include "llvm/llvm_to_gen.hpp"
#include "ir/unit.hpp"
#include "ir/liveness.hpp"
+#include "ir/value.hpp"
#include <cstdlib>
namespace gbe
unit.apply([](ir::Function &fn) {
ir::Liveness liveness(fn);
+ ir::FunctionDAG dag(liveness);
std::cout << liveness << std::endl;
});
}