// If the bb has ret instruction, add it to the work list set.
const Instruction *lastInsn = bb.getLastInstruction();
const ir::Opcode op = lastInsn->getOpcode();
+ struct BlockInfo * info = liveness[&bb];
if (op == OP_RET) {
- struct BlockInfo * info = liveness[&bb];
- workList.push_back(info);
+ workSet.insert(info);
info->liveOut.insert(ocl::retVal);
+ } else if (op == OP_BRA) {
+ // If this is a backward jump, put it to the extra work list.
+ if (((BranchInstruction*)lastInsn)->getLabelIndex() < bb.getLabelIndex())
+ extraWorkSet.insert(info);
}
});
// Now with iterative analysis, we compute liveout and livein sets
this->computeLiveInOut();
-
+ for (auto it : extraWorkSet) {
+ for (auto reg : it->liveOut) {
+ it->extraLiveIn.insert(reg);
+ }
+ }
+ this->computeExtraLiveInOut();
}
Liveness::~Liveness(void) {
// Use simple backward data flow analysis to solve the liveness problem.
void Liveness::computeLiveInOut(void) {
- do {
- struct BlockInfo *currInfo = workList.pop_front();
+ while(!workSet.empty()) {
+ auto currInfo = *workSet.begin();
+ workSet.erase(currInfo);
for (auto currOutVar : currInfo->liveOut)
if (!currInfo->varKill.contains(currOutVar))
currInfo->upwardUsed.insert(currOutVar);
auto changed = prevInfo->liveOut.insert(currInVar);
if (changed.second) isChanged = true;
}
- if (isChanged ) workList.push_back(prevInfo);
+ if (isChanged )
+ workSet.insert(prevInfo);
}
- } while (!workList.empty());
-
+ };
#if 0
fn.foreachBlock([this](const BasicBlock &bb){
printf("label %d:\n", bb.getLabelIndex());
BlockInfo *info = liveness[&bb];
auto &outVarSet = info->liveOut;
auto &inVarSet = info->upwardUsed;
+ auto &extraInVarSet = info->extraLiveIn;
+ auto &extraOutVarSet = info->extraLiveOut;
+ printf("\n\tin Lives: ");
+ for (auto inVar : inVarSet) {
+ printf("%d ", inVar);
+ }
+ printf("\n");
printf("\tout Lives: ");
for (auto outVar : outVarSet) {
printf("%d ", outVar);
}
+ printf("\n");
+
+ });
+#endif
+ }
+
+/*
+ Consider the following scenario, %100's normal liveness will start from Ln-1's
+ position. In normal analysis, the Ln-1 is not Ln's predecessor, thus the liveness
+ of %100 will be passed to Ln and then will not be passed to L0.
+
+ But considering we are running on a multilane with predication's vector machine.
+ The unconditional BR in Ln-1 may be removed and it will enter Ln with a subset of
+ the revert set of Ln-1's predication. For example when running Ln-1, the active lane
+ is 0-7, then at Ln the active lane is 8-15. Then at the end of Ln, a subset of 8-15
+ will jump to L0. If a register %10 is allocated the same GRF as %100, given the fact
+ that their normal liveness doesn't overlapped, the a subset of 8-15 lanes will be
+ modified. If the %10 and %100 are the same vector data type, then we are fine. But if
+ %100 is a float vector, and the %10 is a bool or short vector, then we hit a bug here.
+
+L0:
+ ...
+ %10 = 5
+ ...
+Ln-1:
+ %100 = 2
+ BR Ln+1
+
+Ln:
+ ...
+ BR(%xxx) L0
+
+Ln+1:
+ %101 = %100 + 2;
+ ...
+
+ The solution to fix this issue is to build another liveness data. We will start with
+ those BBs with backward jump. Then pass all the liveOut register as extra liveIn
+ of current BB and then forward this extra liveIn to all the blocks. This is very similar
+ to the normal liveness analysis just with reverse direction.
+*/
+ void Liveness::computeExtraLiveInOut(void) {
+ while(!extraWorkSet.empty()) {
+ struct BlockInfo *currInfo = *extraWorkSet.begin();
+ extraWorkSet.erase(currInfo);
+ for (auto currInVar : currInfo->extraLiveIn)
+ currInfo->extraLiveOut.insert(currInVar);
+ bool isChanged = false;
+ for (auto succ : currInfo->bb.getSuccessorSet()) {
+ BlockInfo *succInfo = liveness[succ];
+ for (auto currOutVar : currInfo->extraLiveOut) {
+ bool changed = false;
+ if (!succInfo->upwardUsed.contains(currOutVar)) {
+ auto it = succInfo->extraLiveIn.insert(currOutVar);
+ changed = it.second;
+ }
+ if (changed) isChanged = true;
+ }
+ if (isChanged)
+ extraWorkSet.insert(succInfo);}
+ };
+#if 0
+ fn.foreachBlock([this](const BasicBlock &bb){
+ printf("label %d:\n", bb.getLabelIndex());
+ BlockInfo *info = liveness[&bb];
+ auto &outVarSet = info->liveOut;
+ auto &inVarSet = info->upwardUsed;
+ auto &extraInVarSet = info->extraLiveIn;
+ auto &extraOutVarSet = info->extraLiveOut;
printf("\n\tin Lives: ");
for (auto inVar : inVarSet) {
printf("%d ", inVar);
}
+ printf("\n\textra in Lives: ");
+ for (auto inVar : extraInVarSet) {
+ printf("%d ", inVar);
+ }
printf("\n");
+ printf("\tout Lives: ");
+ for (auto outVar : outVarSet) {
+ printf("%d ", outVar);
+ }
+ printf("\n\textra out Lives: ");
+ for (auto outVar : extraOutVarSet) {
+ printf("%d ", outVar);
+ }
+ printf("\n");
+
});
#endif
}
+
/*! To pretty print the livfeness info */
static const uint32_t prettyInsnStrSize = 48;
static const uint32_t prettyRegStrSize = 5;
typedef set<Register> VarKill;
/*! Per-block info */
struct BlockInfo : public NonCopyable {
- BlockInfo(const BasicBlock &bb) : bb(bb), inWorkList(false) {}
+ BlockInfo(const BasicBlock &bb) : bb(bb) {}
const BasicBlock &bb;
INLINE bool inUpwardUsed(Register reg) const {
return upwardUsed.contains(reg);
INLINE bool inVarKill(Register reg) const {
return varKill.contains(reg);
}
+ UEVar extraLiveIn;
+ LiveOut extraLiveOut;
UEVar upwardUsed;
LiveOut liveOut;
VarKill varKill;
- bool inWorkList;
};
/*! Gives for each block the variables alive at entry / exit */
typedef map<const BasicBlock*, BlockInfo*> Info;
return info.upwardUsed;
}
+ /*! Get the set of extra registers alive at the end of the block */
+ const LiveOut &getExtraLiveOut(const BasicBlock *bb) const {
+ const BlockInfo &info = this->getBlockInfo(bb);
+ return info.extraLiveOut;
+ }
+ /*! Get the set of extra registers alive at the beginning of the block */
+ const UEVar &getExtraLiveIn(const BasicBlock *bb) const {
+ const BlockInfo &info = this->getBlockInfo(bb);
+ return info.extraLiveIn;
+ }
+
/*! Return the function the liveness was computed on */
INLINE const Function &getFunction(void) const { return fn; }
/*! Actually do something for each successor / predecessor of *all* blocks */
void initInstruction(BlockInfo &info, const Instruction &insn);
/*! Now really compute LiveOut based on UEVar and VarKill */
void computeLiveInOut(void);
+ void computeExtraLiveInOut(void);
/*! Set of work list block which has exit(return) instruction */
- typedef std::list <struct BlockInfo*> BlockInfoList;
-
- /*! helper structure to maintain the liveness work list.
- don't add the block if it is already in the list. */
- struct LivenessWorkList : BlockInfoList {
-
- void push_back(struct BlockInfo *info) {
- if (info->inWorkList)
- return;
- info->inWorkList = true;
- BlockInfoList::push_back(info);
- }
-
- struct BlockInfo * pop_front() {
- struct BlockInfo *biFront = front();
- BlockInfoList::pop_front();
- if (biFront)
- biFront->inWorkList = false;
- return biFront;
- }
-
- } workList;
+ typedef set <struct BlockInfo*> WorkSet;
+ WorkSet workSet, extraWorkSet;
/*! Use custom allocators */
GBE_CLASS(Liveness);