On Wed, Dec 25, 2013 at 02:28:48AM +0000, Song, Ruiling wrote:
>
> See my two comments
> -----Original Message-----
> From: [email protected]
> [mailto:[email protected]] On Behalf Of Zhigang Gong
> Sent: Tuesday, December 24, 2013 2:29 PM
> To: Yang, Rong R
> Cc: Gong, Zhigang; [email protected]
> Subject: Re: [Beignet] [PATCH] GBE: rewrite the liveness analysis routine.
>
> On Tue, Dec 24, 2013 at 05:27:43AM +0000, Yang, Rong R wrote:
> > One question.
> >
> >
> > -----Original Message-----
> > From: [email protected]
> > [mailto:[email protected]] On Behalf Of Zhigang
> > Gong
> > Sent: Friday, December 20, 2013 3:27 PM
> > To: [email protected]
> > Cc: Gong, Zhigang
> > Subject: [Beignet] [PATCH] GBE: rewrite the liveness analysis routine.
> >
> > The previous implementation has two problems:
> >
> > 1. At the liveness analysis phase, the liveIn and liveOut computation is
> > incorrect. The liveIn is not a static information it should be computed
> > along with the liveOut during the backward data flow analysis.
> >
> > 2. At the register allocation phase, it only considers the liveOut
> > information. Actually, we also need to consider the liveIn information.
> >
> > Signed-off-by: Zhigang Gong <[email protected]>
> > ---
> > backend/src/backend/gen_context.hpp | 4 ++
> > backend/src/backend/gen_reg_allocation.cpp | 10 +++-
> > backend/src/ir/liveness.cpp | 78
> > ++++++++++++++++++----------
> > backend/src/ir/liveness.hpp | 13 ++++-
> > backend/src/ir/profile.cpp | 3 +-
> > backend/src/ir/profile.hpp | 3 +-
> > 6 files changed, 81 insertions(+), 30 deletions(-)
> >
> > diff --git a/backend/src/backend/gen_context.hpp
> > b/backend/src/backend/gen_context.hpp
> > index f8ef8e0..b8c2bca 100644
> > --- a/backend/src/backend/gen_context.hpp
> > +++ b/backend/src/backend/gen_context.hpp
> > @@ -76,6 +76,10 @@ namespace gbe
> > INLINE const ir::Liveness::LiveOut &getLiveOut(const ir::BasicBlock
> > *bb) const {
> > return this->liveness->getLiveOut(bb);
> > }
> > + /*! Get the LiveIn information for the given block */
> > + INLINE const ir::Liveness::UEVar &getLiveIn(const ir::BasicBlock *bb)
> > const {
> > + return this->liveness->getLiveIn(bb);
> > + }
> >
> > void collectShifter(GenRegister dest, GenRegister src);
> > void loadTopHalf(GenRegister dest, GenRegister src); diff --git
> > a/backend/src/backend/gen_reg_allocation.cpp
> > b/backend/src/backend/gen_reg_allocation.cpp
> > index 30f9e38..50746e0 100644
> > --- a/backend/src/backend/gen_reg_allocation.cpp
> > +++ b/backend/src/backend/gen_reg_allocation.cpp
> > @@ -569,6 +569,7 @@ namespace gbe
> > int32_t insnID = 0;
> > for (auto &block : *selection.blockList) {
> > int32_t lastID = insnID;
> > + int32_t firstID = insnID;
> > // Update the intervals of each used register. Note that we do not
> > // register allocate R0, so we skip all sub-registers in r0
> > for (auto &insn : block.insnList) { @@ -619,9 +620,16 @@ namespace
> > gbe
> > insnID++;
> > }
> >
> > + // All registers alive at the begining of the block must update
> > their intervals.
> > + const ir::BasicBlock *bb = block.bb;
> > + const ir::Liveness::UEVar &liveIn = ctx.getLiveIn(bb);
> > + for (auto reg : liveIn) {
> > + this->intervals[reg].minID =
> > std::min(this->intervals[reg].minID, firstID);
> > + this->intervals[reg].maxID =
> > std::max(this->intervals[reg].maxID, firstID);
> > + }
> > +
> > >>>>> Is it should use lastID to calc maxID? And using firstID to calc
> > >>>>> minID in liveOut.
> I just discussed with Ruiling about this same question. It seems that we
> could ignore the maxID calculating in liveIn loop and the minID calculating
> in liveOut loop. If the following assumption is corret: I didn't remove both
> of them in this patch is just for safety.
>
> After discussion, I think we can change it to as below:
>
> for (auto reg : liveIn)
> this->intervals[reg].minID = std::min(this->intervals[reg].minID, firstID);
>
> for (auto reg : liveOut)
> this->intervals[reg].maxID = std::max(this->intervals[reg].maxID, lastID);
>
> Any comments for the above change?
> I prefer this change to make code clean.
> >
> > // All registers alive at the end of the block must have their
> > intervals
> > // updated as well
> > - const ir::BasicBlock *bb = block.bb;
> > const ir::Liveness::LiveOut &liveOut = ctx.getLiveOut(bb);
> > for (auto reg : liveOut) {
> > this->intervals[reg].minID =
> > std::min(this->intervals[reg].minID, lastID); diff --git
> > a/backend/src/ir/liveness.cpp b/backend/src/ir/liveness.cpp index
> > b0a4314..29d96c4 100644
> > --- a/backend/src/ir/liveness.cpp
> > +++ b/backend/src/ir/liveness.cpp
> > @@ -29,9 +29,20 @@ namespace ir {
> >
> > Liveness::Liveness(Function &fn) : fn(fn) {
> > // Initialize UEVar and VarKill for each block
> > - fn.foreachBlock([this](const BasicBlock &bb) { this->initBlock(bb); });
> > - // Now with iterative analysis, we compute liveout sets
> > - this->computeLiveOut();
> > + fn.foreachBlock([this](const BasicBlock &bb) {
> > + this->initBlock(bb);
> > + // If the bb has ret instruction, add it to the work list set.
> > + const Instruction *lastInsn = bb.getLastInstruction();
> > + const ir::Opcode op = lastInsn->getOpcode();
> > + if (op == OP_RET) {
> > + WorkList.push_back(&bb);
> > + BlockInfo *info = liveness[&bb];
> > + info->liveOut.insert(ocl::retVal);
> > + }
> > + });
> > + // Now with iterative analysis, we compute liveout and livein sets
> > + this->computeLiveInOut();
> > +
> > }
> >
> > Liveness::~Liveness(void) {
> > @@ -65,30 +76,45 @@ namespace ir {
> > }
> > }
> >
> > - void Liveness::computeLiveOut(void) {
> > - // First insert the UEVar from the successors
> > - foreach<DF_SUCC>([](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) info.liveOut.insert(ueVar);
> > - });
> > - // Now iterate on liveOut
> > - bool changed = true;
> > - while (changed) {
> > - changed = false;
> > - foreach<DF_SUCC>([&changed](BlockInfo &info, const BlockInfo &succ) {
> > - const UEVar &killSet = succ.varKill;
> > - const LiveOut &liveOut = succ.liveOut;
> > - // Iterate over all the registers in the UEVar of our successor
> > - for (auto living : liveOut) {
> > - if (killSet.contains(living)) continue;
> > - if (info.liveOut.contains(living)) continue;
> > - info.liveOut.insert(living);
> > - changed = true;
> > +// Use simple backward data flow analysis to solve the liveness problem.
> > + void Liveness::computeLiveInOut(void) {
> > + do {
> > + const BasicBlock *curr = WorkList.front();
> > + WorkList.pop_front();
> > + BlockInfo *info = liveness[curr];
> > + for (auto currOutVar : info->liveOut)
> > + if (!info->varKill.contains(currOutVar))
> > + info->upwardUsed.insert(currOutVar);
> > + bool isChanged = false;
> > + for (auto prev : curr->getPredecessorSet()) {
> > + BlockInfo *prevInfo = liveness[prev];
> > + for (auto currInVar : info->upwardUsed) {
> > + auto changed = prevInfo->liveOut.insert(currInVar);
> > + if (changed.second) isChanged = true;
> > }
> > - });
> > - }
> > - }
> > + // XXX is it possible one block is appended twice?
>
> Consider BBs like below:
> A:
> Def %1
> Def %2
> BR C
> B:
> Use %2
> C:
> Use %1
> When you processing use %1 in C, you will add B and A into the worklist. Then
> you go on processing use %2 in B, you will again add A into the worklist. Am
> I right?
> So, if you can deal with it easily, just add it. Or you can change the
> comment and leave it for later refine.
Right, your analysis is correct. There is an optimization opportunity, but
I'm not 100% sure whether we should remove the possible duplicate appending
of the same block, and that's reason why I put a XXX there.
Thanks for pointing this out which make me think it over again and I think
we could avoid inserting a block to the work list if it is still there.
Thanks,
Zhigang Gong.
> > + if (isChanged) WorkList.push_back(prev);
> > + }
> > + } 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;
> > + printf("\tout Lives: ");
> > + for (auto outVar : outVarSet) {
> > + printf("%d ", outVar);
> > + }
> > + printf("\n\tin Lives: ");
> > + for (auto inVar : inVarSet) {
> > + printf("%d ", inVar);
> > + }
> > + printf("\n");
> > + });
> > +#endif
> > + }
> >
> > /*! To pretty print the livfeness info */
> > static const uint32_t prettyInsnStrSize = 48; diff --git
> > a/backend/src/ir/liveness.hpp b/backend/src/ir/liveness.hpp index
> > ea5a157..16cd789 100644
> > --- a/backend/src/ir/liveness.hpp
> > +++ b/backend/src/ir/liveness.hpp
> > @@ -24,6 +24,7 @@
> > #ifndef __GBE_IR_LIVENESS_HPP__
> > #define __GBE_IR_LIVENESS_HPP__
> >
> > +#include <list>
> > #include "sys/map.hpp"
> > #include "sys/set.hpp"
> > #include "ir/register.hpp"
> > @@ -87,6 +88,12 @@ namespace ir {
> > const BlockInfo &info = this->getBlockInfo(bb);
> > return info.liveOut;
> > }
> > + /*! Get the set of registers alive at the beginning of the block */
> > + const UEVar &getLiveIn(const BasicBlock *bb) const {
> > + const BlockInfo &info = this->getBlockInfo(bb);
> > + return info.upwardUsed;
> > + }
> > +
> > /*! 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 */ @@ -119,9 +126,13 @@ namespace ir {
> > /*! 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);
> > + void computeLiveInOut(void);
> > + /*! Set of work list block which has exit(return) instruction */
> > + std::list <const BasicBlock*> WorkList;
> > +
> > /*! Use custom allocators */
> > GBE_CLASS(Liveness);
> > +
> > };
> >
> > /*! Output a nice ASCII reprensation of the liveness */ diff --git
> > a/backend/src/ir/profile.cpp b/backend/src/ir/profile.cpp index
> > 10e0c59..b693c2b 100644
> > --- a/backend/src/ir/profile.cpp
> > +++ b/backend/src/ir/profile.cpp
> > @@ -40,7 +40,7 @@ namespace ir {
> > "stack_pointer",
> > "block_ip",
> > "barrier_id", "thread_number",
> > - "work_dimension", "sampler_info"
> > + "work_dimension", "sampler_info", "retVal"
> > };
> >
> > #if GBE_DEBUG
> > @@ -77,6 +77,7 @@ namespace ir {
> > DECL_NEW_REG(FAMILY_DWORD, threadn);
> > DECL_NEW_REG(FAMILY_DWORD, workdim);
> > DECL_NEW_REG(FAMILY_WORD, samplerinfo);
> > + DECL_NEW_REG(FAMILY_WORD, retVal);
> > }
> > #undef DECL_NEW_REG
> >
> > diff --git a/backend/src/ir/profile.hpp b/backend/src/ir/profile.hpp
> > index 89dd69f..2f16741 100644
> > --- a/backend/src/ir/profile.hpp
> > +++ b/backend/src/ir/profile.hpp
> > @@ -65,7 +65,8 @@ namespace ir {
> > static const Register threadn = Register(21); // number of threads
> > static const Register workdim = Register(22); // work dimention.
> > static const Register samplerinfo = Register(23); // store sampler
> > info.
> > - static const uint32_t regNum = 24; // number of special
> > registers
> > + static const Register retVal = Register(24); // helper register to
> > do data flow analysis.
> > + static const uint32_t regNum = 25; // number of special
> > registers
> > extern const char *specialRegMean[]; // special register
> > name.
> > } /* namespace ocl */
> >
> > --
> > 1.7.9.5
> >
> > _______________________________________________
> > Beignet mailing list
> > [email protected]
> > http://lists.freedesktop.org/mailman/listinfo/beignet
> > _______________________________________________
> > Beignet mailing list
> > [email protected]
> > http://lists.freedesktop.org/mailman/listinfo/beignet
> _______________________________________________
> Beignet mailing list
> [email protected]
> http://lists.freedesktop.org/mailman/listinfo/beignet
_______________________________________________
Beignet mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/beignet