Started to implement liveness analysis
authorBenjamin Segovia <segovia.benjamin@gmail.com>
Tue, 13 Mar 2012 19:33:57 +0000 (19:33 +0000)
committerKeith Packard <keithp@keithp.com>
Fri, 10 Aug 2012 23:15:38 +0000 (16:15 -0700)
backend/src/CMakeLists.txt
backend/src/ir/context.cpp
backend/src/ir/function.cpp
backend/src/ir/function.hpp
backend/src/ir/instruction.hpp
backend/src/ir/liveness.cpp
backend/src/utest/utest_llvm.cpp

index 0a56316..9307d9f 100644 (file)
@@ -35,6 +35,7 @@ else (GBE_USE_BLOB)
     ir/constant.hpp
     ir/instruction.cpp
     ir/instruction.hpp
+    ir/liveness.cpp
     ir/register.cpp
     ir/register.hpp
     ir/function.cpp
index 460b802..110a0a7 100644 (file)
@@ -51,7 +51,7 @@ namespace ir {
     // Check first that all branch instructions point to valid labels
     for (auto it = usedLabels->begin(); it != usedLabels->end(); ++it)
       GBE_ASSERTM(*it != LABEL_IS_POINTED, "A label is used and not defined");
-
+    fn->computeCFG();
     GBE_DELETE(usedLabels);
     const StackElem elem = fnStack.back();
     fnStack.pop_back();
index 9fb1c92..8244ef4 100644 (file)
@@ -65,6 +65,34 @@ namespace ir {
     }
   }
 
+  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;
index 94b49ed..76d39bd 100644 (file)
@@ -29,7 +29,7 @@
 #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>
@@ -37,6 +37,9 @@
 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
@@ -70,8 +73,14 @@ namespace ir {
     /*! 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
@@ -145,6 +154,8 @@ namespace ir {
     }
     /*! 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 */
@@ -164,8 +175,8 @@ namespace ir {
     /*! 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
index 4f00dd4..1f37311 100644 (file)
@@ -161,7 +161,7 @@ namespace ir {
   /*! Select instructions writes src0 to dst if cond is true. Otherwise, it
    *  writes src1
    */
-  class SelectInstruction {
+  class SelectInstruction : public Instruction {
   public:
     /*! Get the type of both sources */
     Type getType(void) const;
index 7e55fac..5a86808 100644 (file)
  * \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 */
 
index 76ed389..d0a165e 100644 (file)
@@ -76,8 +76,8 @@ runTests:
   GBE_ASSERT(dummyKernel != NULL);
   fclose(dummyKernel);
 
+  UTEST_EXPECT_SUCCESS(utestLLVM2Gen("loop.ll"));
   //UTEST_EXPECT_SUCCESS(utestLLVM2Gen("function_param.ll"));
-  //UTEST_EXPECT_SUCCESS(utestLLVM2Gen("loop.ll"));
   UTEST_EXPECT_SUCCESS(utestLLVM2Gen("function.ll"));
   //UTEST_EXPECT_SUCCESS(utestLLVM2Gen("mad.ll"));
 #if 0