[llvm-commits] CVS: llvm/lib/Reoptimizer/Inst/InstManip.cpp InstManip.h Phases.cpp

Joel Stanley jstanley at cs.uiuc.edu
Fri Apr 11 18:56:01 PDT 2003


Changes in directory llvm/lib/Reoptimizer/Inst:

InstManip.cpp updated: 1.5 -> 1.6
InstManip.h updated: 1.5 -> 1.6
Phases.cpp updated: 1.9 -> 1.10

---
Log message:

Started load-candidate selection code, and phase3 is making its initial transformation.


---
Diffs of the changes:

Index: llvm/lib/Reoptimizer/Inst/InstManip.cpp
diff -u llvm/lib/Reoptimizer/Inst/InstManip.cpp:1.5 llvm/lib/Reoptimizer/Inst/InstManip.cpp:1.6
--- llvm/lib/Reoptimizer/Inst/InstManip.cpp:1.5	Fri Apr 11 00:22:20 2003
+++ llvm/lib/Reoptimizer/Inst/InstManip.cpp	Fri Apr 11 18:57:07 2003
@@ -13,30 +13,34 @@
 
 const unsigned InstManip::NOP_INST = 0x01000000;
 
+using std::cout;
+using std::cerr;
+using std::endl;
+
 void InstManip::printRange(unsigned* start, unsigned* end) const
 {
     // Dumps contents (and corresponding disassembly) of memory range given by range
     // to stdout.  TODO: Parameterize by an ostream instance; cannot do this yet
     // because BinInterface is hard-coded to use printf and must be changed.
         
-    std::cout << "Sparc dissassembly of range ["
-              << start << ", " << end << "]:" << std::endl;
+    cout << "Sparc dissassembly of range ["
+              << start << ", " << end << "]:" << endl;
 
     for(; start <= end; ++start) {
-        std::cout << start << " | " 
+        cout << start << " | " 
                   << std::hex << std::setw(8) << std::setfill('0')
                   << *start << " | ";
         sparc_print(*start);
-        std::cout << std::endl;
+        cout << endl;
     }
 }
 
-uint64_t InstManip::skipFunctionHdr(uint64_t addr, VirtualMem* vm) const
+uint64_t InstManip::skipFunctionHdr(uint64_t addr) const
 {
     // For SparcV9, what we're calling the "function header" is the save instruction (if
     // present) that occurs as the first instruction of the function.
     
-    unsigned inst = vm->readInstrFrmVm(addr);
+    unsigned inst = m_pVM->readInstrFrmVm(addr);
     assert(RD_FLD(inst, INSTR_OP) == OP_2 &&
            RD_FLD(inst, INSTR_OP3) == OP3_SAVE &&
            "Unhandled case: non-save instruction in function header");
@@ -124,3 +128,137 @@
            "Unexpected number of instruction in code sequence for branch-always");
 }
 
+void InstManip::findCandidates(uint64_t start,
+                               uint64_t end,
+                               std::vector<InstCandidate>& candidates) 
+{
+    for(uint64_t currAddr = start; currAddr <= end; currAddr += getInstWidth()) {
+        unsigned inst = m_pVM->readInstrFrmVm(currAddr);
+        
+        cout << "findCandidates processing instruction:\t";
+        printInst(m_pVM->readInstrFrmVm(currAddr));
+        cout << endl;
+        
+        InstCandidate cand;
+        if(isCandidateLoad(currAddr, end, cand))
+            cerr << "It's a candidate load!" << endl;
+    }
+}
+
+static inline bool isLoadHalfWord(unsigned inst)
+{
+    // Returns true if inst is an lduh instruction
+    return RD_FLD(inst, INSTR_OP) == OP_3 &&
+        RD_FLD(inst, INSTR_OP3) == OP3_LDUH;
+}
+
+static inline bool isSTH(unsigned inst) 
+{
+    return RD_FLD(inst, INSTR_OP) == OP_3 &&
+        RD_FLD(inst, INSTR_OP3) == OP3_STH;
+}
+
+static inline bool isSTB(unsigned inst) 
+{
+    return RD_FLD(inst, INSTR_OP) == OP_3 &&
+        RD_FLD(inst, INSTR_OP3) == OP3_STB;
+}
+
+static inline unsigned getLoadDest(unsigned inst) 
+{
+    // Assumes that inst is a load instruction, and returns the register ID of its
+    // destination operand.
+    
+    return RD_FLD(inst, INSTR_RD);
+}
+
+static inline unsigned getStoreSrc(unsigned inst)
+{
+    // Assumes that inst is a stb/sth instruction, and returns the register ID of its
+    // source operand (by source, we don't mean rs1 or rs2, but rather rd, which specifies
+    // the register which contains the value being stored)
+
+    return RD_FLD(inst, INSTR_RD);
+}
+
+bool InstManip::isCandidateLoad(uint64_t addr,
+                                uint64_t end,
+                                InstCandidate& cand) 
+{
+    // {{{ Description of heuristic
+    // A candidate load is the first instruction in a sequence (with an arbitrary number
+    // of instructions in between elements of this sequence) that is a "signature" for the
+    // particular load of a volatile variable which needs to be replaced with a call to an
+    // instrumentation function.
+    //
+    // Detecting this candidacy condition is accomplished via the application of a
+    // relatively simple heurstic.  The signature sequence always begins with a "load
+    // half-word" and ends with a "store byte".  However, we cannot guarantee that the
+    // sequence looks like:
+    //
+    // lduh [mem1], %r[d]              |
+    // ...                             |  "Schema 1"
+    // stb %r[d], [mem2]               | 
+    //
+    // although this is a perfectly valid pattern to look for.  However, unoptimized code
+    // will frequently transfer this data using the stack, as in this instruction sequence:
+    //
+    // lduh [mem1] %r[d]               |  
+    // ...                             |
+    // sth  %r[d], [stack loc]         |
+    // ...                             |  "Schema 2"
+    // lduh [stack loc], %r[d']        |
+    // ...                             |
+    // stb %r[d'], [mem2]              |
+    //
+    // The current heurstic catches both of these patterns (designated "direct" and "stack
+    // transfer" respectively), and will be extended as insufficiencies in the heuristic
+    // are revealed.
+    // }}}
+    
+    // Address of potential candidate load is given by 'addr', maximum search address is
+    // given by 'end'
+    
+    unsigned inst = m_pVM->readInstrFrmVm(addr);
+    
+    if(isLoadHalfWord(inst)) {
+        // Search forward until a sth/stb from inst's target register is encountered
+        uint64_t storeAddr = findNextStore(addr, end, getLoadDest(inst));
+        if(!storeAddr)
+            return false; // No store? Can't be a candidate load.
+        
+        // If STB, ... If STH, ...
+
+        unsigned storeInst = m_pVM->readInstrFrmVm(storeAddr);
+        if(isSTH(storeInst)) {
+            cerr << "Discovered sth: " << endl;
+        }
+        else {
+            // STB instruction
+            cerr << "Discovered stb: " << endl;
+        }
+        
+        printInst(storeInst);
+        
+        return true;
+    }
+    
+    return false;
+}
+
+uint64_t InstManip::findNextStore(uint64_t addr,
+                                  uint64_t end,
+                                  unsigned srcReg) 
+{
+    // Sweep the range of addresses starting at addr (up to end) looking for stb or sth
+    // instructions that are storing _from_ 'fromReg'.  Return the first such instance, or
+    // 0 if such an instance cannot be found.
+
+    for(uint64_t currAddr = addr; currAddr <= end; currAddr += getInstWidth()) {
+        unsigned inst = m_pVM->readInstrFrmVm(currAddr);
+        if(isSTH(inst) || isSTB(inst) && getStoreSrc(inst) == srcReg)
+            return currAddr;
+    }
+
+    return 0;
+}


Index: llvm/lib/Reoptimizer/Inst/InstManip.h
diff -u llvm/lib/Reoptimizer/Inst/InstManip.h:1.5 llvm/lib/Reoptimizer/Inst/InstManip.h:1.6
--- llvm/lib/Reoptimizer/Inst/InstManip.h:1.5	Fri Apr 11 00:22:20 2003
+++ llvm/lib/Reoptimizer/Inst/InstManip.h	Fri Apr 11 18:57:07 2003
@@ -3,25 +3,68 @@
 //       date: Tue Apr  8 22:42:14 CDT 2003
 //     fileid: InstManip.h
 //     purpose: InstManip is a wrapper class around any BinInterface macros/mechanisms, as
-//     well as the TraceCache "instruction utilities", all which are SparcV9-specific.
-//     This class exists both for conceptual clarity and to facilitate the hiding of
-//     Sparc-specific code from the Phase2/3 actions (and thus making it easier to use the
-//     transformations on other platforms in the future; we should be able to change which
-//     instruction manipulator object is instantiated, after making the appropriate
-//     superclass, etc).
+//     well as the TraceCache "instruction utilities", all which are (currently)
+//     SparcV9-specific.  This class exists both for conceptual clarity and to facilitate
+//     the hiding of Sparc-specific code from the Phase2/3 actions (and thus making it
+//     easier to use the transformations on other platforms in the future; we should be
+//     able to change which instruction manipulator object is instantiated, after making
+//     the appropriate superclass, etc). 
 
 #ifndef _INCLUDED_INSTMANIP_H
 #define _INCLUDED_INSTMANIP_H
 
 #include <vector>
+#include <algorithm>
 #include "llvm/Reoptimizer/BinInterface/sparcdis.h"
 #include "llvm/Reoptimizer/InstrUtils.h" // getCallInstr, getUndepJumpInstr, etc.
 
 class VirtualMem;
 
+// InstCandidate is a class that represents a location in the code that is determined to
+// be a candidate for instrumentation.  Because the transformation action required for a
+// particular candidate requires auxiliary information (such as other instructions found
+// within the region of the primary candidate instruction), these are encapsulated in the
+// InstCandidate as well.
+
+class InstCandidate 
+{
+  public:
+    enum CandType { DIRECT, STACK_XFER };
+    
+    InstCandidate() {}
+    InstCandidate(CandType type): m_type(type) {}
+
+    void setType(CandType type) { m_type = type;               }
+    bool isDirect() const       { return m_type == DIRECT;     }
+    bool isStackXfer() const    { return m_type == STACK_XFER; }
+
+    const std::vector<std::pair<uint64_t, unsigned> >& getInsts() const 
+    {
+        return m_insts;
+    }
+
+    void push_back(uint64_t addr, unsigned inst) 
+    {
+        m_insts.push_back(std::make_pair(addr, inst));
+    }
+
+  protected:
+    CandType m_type;
+
+    // Each element of this vector holds a (address, inst) pair.
+    std::vector<std::pair<uint64_t, unsigned> > m_insts;
+};
+
 class InstManip 
 {
   public:
+    InstManip(VirtualMem* vm): m_pVM(vm)
+    {
+        assert(vm && "InstManip requires valid VirtualMem instance");
+    }
+
+    typedef std::pair<uint64_t, unsigned> Inst; // (location, inst word) pair
+    
     enum TargetRegister { REG_0, REG_1 };
 
     void            printRange(unsigned* start, unsigned* end) const;
@@ -30,7 +73,7 @@
     inline void     printInst(unsigned inst) const;
     inline void     printInst(unsigned* instAddr) const;
                     
-    uint64_t        skipFunctionHdr(uint64_t addr, VirtualMem* vm) const;
+    uint64_t        skipFunctionHdr(uint64_t addr) const;
                     
     void            generateLoad(uint64_t value,
                                  std::vector<unsigned>& snippet,
@@ -45,6 +88,10 @@
                                          std::vector<unsigned>& snippet,
                                          bool annul = true);
 
+    void            findCandidates(uint64_t start,
+                                   uint64_t end,
+                                   std::vector<InstCandidate>& candidates);
+
     inline unsigned getBranchAlways(uint64_t dest, uint64_t pc, bool annulHigh = true) const;
     inline unsigned getCallInst(uint64_t dest, uint64_t pc) const;
     inline bool     isBranch(unsigned inst) const;
@@ -59,9 +106,21 @@
     unsigned        getInstWidth() const           { return 4;          }
 
   private:
+    InstManip() {}
+
+    bool            isCandidateLoad(uint64_t addr,
+                                    uint64_t end,
+                                    InstCandidate& cand);
+
+    uint64_t        findNextStore(uint64_t addr,
+                                  uint64_t end,
+                                  unsigned srcReg);
+    
     // Branch-always (annul bit high) instruction base (i.e. address not filled in yet)
     static const unsigned BRANCH_ALWAYS_BASE = 0x30480000;
     static const unsigned NOP_INST;
+
+    VirtualMem* m_pVM;
 };
 
 void InstManip::printRange(uint64_t start, uint64_t end) const


Index: llvm/lib/Reoptimizer/Inst/Phases.cpp
diff -u llvm/lib/Reoptimizer/Inst/Phases.cpp:1.9 llvm/lib/Reoptimizer/Inst/Phases.cpp:1.10
--- llvm/lib/Reoptimizer/Inst/Phases.cpp:1.9	Fri Apr 11 00:22:20 2003
+++ llvm/lib/Reoptimizer/Inst/Phases.cpp	Fri Apr 11 18:57:07 2003
@@ -24,20 +24,18 @@
 //
 // PHASE 3:
 //
+//       - Deallocate the parameter structure whenever it is convenient to do so.
+//
 //       1. Replace the original (replaced) instruction at the proper location in the
 //       original code.
 //
-//       2. Delete any slots and/or heap frames which have been marked for deletion by
-//       earlier invocations of phase3/4.
-//
-//       3. Analyze the function and determine the load-volatile candidates.
+//       2. Analyze the function and determine the load-volatile candidates.
 //
-//       4. For each load-volatile candidate,
+//       3. For each load-volatile candidate,
 //
-//           4a.
+//           3a.
 //
-//       5. Deallocate the parameter structure and mark the phase 2 slot for deletion by a
-//       later invocation of phase 3.
+//       4. Deallocate the slot that originated this invocation.
 //
 // PHASE 4:
 //
@@ -117,13 +115,36 @@
     void transformFunction(AddressRange& range);
 
   private:
-    Phase2() {}
+    Phase2(): m_instManip(0) {}
     inline unsigned getSlotSize() const;
     
     TraceCache*     m_pTraceCache;
     InstManip       m_instManip;
 };
 
+// Phase3 is the class that is responsible for making the "phase 3" transformation; the
+// global function phase3() is responsible for constructing a one Phase3 instance per
+// invocation and for deallocating the originating slot.
+
+class Phase3 
+{
+  public:
+    Phase3(Phase3Info* p3info);
+    void transform();
+
+  private:
+    Phase3(): m_instManip(0) {}
+
+    uint64_t    m_startAddr;
+    uint64_t    m_endAddr;
+    TraceCache* m_pTraceCache;
+    InstManip   m_instManip;
+    uint64_t    m_slotDescriptor;
+};
+
+
+//////////////// Phase 2 implementation ////////////////
+
 extern "C" void phase2() 
 {
     Phase2 ph(new TraceCache());
@@ -131,7 +152,8 @@
 }
 
 Phase2::Phase2(TraceCache* tc):
-    m_pTraceCache(tc)
+    m_pTraceCache(tc),
+    m_instManip(tc->getVM())
 {
 }
 
@@ -193,12 +215,12 @@
     // Obtain address of first replacable instruction in function and obtain a new slot from
     // the TraceCache memory manager (i.e., a new slot in the dummy function).
     
-    VirtualMem* vm = m_pTraceCache->getVM();
-    uint64_t repInstAddr = m_instManip.skipFunctionHdr(range.first, vm);
+    uint64_t repInstAddr = m_instManip.skipFunctionHdr(range.first);
     uint64_t slotBase = m_pTraceCache->getMemMgr()->getMemory(getSlotSize());
     assert(slotBase && "Unable to obtain memory from MemoryManger instance");
 
     // Replace instruction at repInstAddr with a branch to start of slot.
+    VirtualMem* vm = m_pTraceCache->getVM();
     unsigned origInst = vm->readInstrFrmVm(repInstAddr);
     assert(!m_instManip.isBranch(origInst) &&
            "Unhandled case: branch instruction first in function body");
@@ -236,28 +258,53 @@
         m_instManip.getGenBranchAlwaysSize();
 }
 
+//////////////// Phase3 implementation ////////////////
+
 void phase3(Phase3Info* p3info)
 {
+    Phase3 p3(p3info);
+    p3info = 0;
+
+    p3.transform();
+
+    // Deallocate the originating slot (i.e. the slot that invoked us).
+    // 
+    // NB: Yes, we are, in fact, deallocating a memory segment (i.e., the slot obtained by
+    // the TraceCache's MemoryManager instance) before returning to it. This is not a
+    // problem for single-threaded codes, because no threads may claim that memory and
+    // write to it.  However, it does indeed pose a problem for multi-threaded codes.  A
+    // modification to the general mechanism itself is required to achieve thread-safety.
+
+    // (TODO)
+}
+
+Phase3::Phase3(Phase3Info* p3info):
+    m_instManip(p3info->getTraceCache()->getVM())
+{
     assert(p3info && "phase3 requires valid Phase3Info ptr");
+
+    m_startAddr = p3info->getStartAddr();
+    m_endAddr = p3info->getEndAddr();
+    m_pTraceCache = p3info->getTraceCache();
+    m_slotDescriptor = p3info->getSlot();
     
     cerr << "================ Begin Phase 3 [" << std::hex
-         << p3info->getStartAddr() << ", " << p3info->getEndAddr()
+         << m_startAddr << ", " << m_endAddr
          << "] ================\n";
 
     // Restore the replaced instruction to its original location (thus effectively
     // removing the branch to the slot created by phase 2 as well)
+    m_pTraceCache->getVM()->writeInstToVM(p3info->getReplaceAddr(), p3info->getOrigInst());
 
-    VirtualMem* vm = p3info->getTraceCache()->getVM();
-    vm->writeInstToVM(p3info->getReplaceAddr(), p3info->getOrigInst());
-
-    // TODO: Delete pending slots/heap frames.
-
-    // Deallocate the parameter structure and mark the phase 2 slot that corresponds to this
-    // invocation of phase 3 as deletable.
-
-    // TODO: Mark p3info->getSlot() as deleted
-    
+    // Deallocate the parameter structure
     delete p3info;
+}
+
+void Phase3::transform()
+{
+    // Gather up the instruction candidates within the function we to transform.
+    vector<InstCandidate> candidates;
+    m_instManip.findCandidates(m_startAddr, m_endAddr, candidates);
 
     cerr << "============================== End Phase 3 ==============================\n";
 }





More information about the llvm-commits mailing list