[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