#define LLVM_CODEGEN_MACHINECODEEMITTER_H
#include "llvm/Support/DataTypes.h"
+#include "llvm/Support/DebugLoc.h"
namespace llvm {
(*(uint64_t*)Addr) = (uint64_t)Value;
}
-
+ /// processDebugLoc - Records debug location information about a
+ /// MachineInstruction. This is called before emitting any bytes associated
+ /// with the instruction. Even if successive instructions have the same debug
+ /// location, this method will be called for each one.
+ virtual void processDebugLoc(DebugLoc DL) {}
+
/// emitLabel - Emits a label
virtual void emitLabel(uint64_t LabelID) = 0;
#define LLVM_EXECUTION_ENGINE_JIT_EVENTLISTENER_H
#include "llvm/Support/DataTypes.h"
+#include "llvm/Support/DebugLoc.h"
+
+#include <vector>
namespace llvm {
class Function;
+class MachineFunction;
/// Empty for now, but this object will contain all details about the
/// generated machine code that a Listener might care about.
struct JITEvent_EmittedFunctionDetails {
+ const MachineFunction *MF;
+
+ struct LineStart {
+ // The address at which the current line changes.
+ uintptr_t Address;
+ // The new location information. These can be translated to
+ // DebugLocTuples using MF->getDebugLocTuple().
+ DebugLoc Loc;
+ };
+ // This holds line boundary information sorted by address.
+ std::vector<LineStart> LineStarts;
};
/// JITEventListener - This interface is used by the JIT to notify clients about
GlobalVariable *CompileUnit;
unsigned Line, Col;
+ DebugLocTuple()
+ : CompileUnit(0), Line(~0U), Col(~0U) {};
+
DebugLocTuple(GlobalVariable *v, unsigned l, unsigned c)
: CompileUnit(v), Line(l), Col(c) {};
case Intrinsic::dbg_func_start: {
DwarfWriter *DW = DAG.getDwarfWriter();
DbgFuncStartInst &FSI = cast<DbgFuncStartInst>(I);
- if (!isValidDebugInfoIntrinsic(FSI, CodeGenOpt::None) || !DW
- || !DW->ShouldEmitDwarfDebug())
+ if (!isValidDebugInfoIntrinsic(FSI, CodeGenOpt::None))
return 0;
MachineFunction &MF = DAG.getMachineFunction();
// Record the source line.
setCurDebugLoc(ExtractDebugLocation(FSI, MF.getDebugLocInfo()));
+ if (!DW || !DW->ShouldEmitDwarfDebug())
+ return 0;
DebugLocTuple PrevLocTpl = MF.getDebugLocTuple(PrevLoc);
DISubprogram SP(cast<GlobalVariable>(FSI.getSubprogram()));
DICompileUnit CU(PrevLocTpl.CompileUnit);
// This is a beginning of a new function.
MF.setDefaultDebugLoc(ExtractDebugLocation(FSI, MF.getDebugLocInfo()));
-
+
+ if (!DW || !DW->ShouldEmitDwarfDebug())
+ return 0;
// llvm.dbg.func_start also defines beginning of function scope.
DW->RecordRegionStart(cast<GlobalVariable>(FSI.getSubprogram()));
return 0;
// CurFn - The llvm function being emitted. Only valid during
// finishFunction().
const Function *CurFn;
-
+
+ /// Information about emitted code, which is passed to the
+ /// JITEventListeners. This is reset in startFunction and used in
+ /// finishFunction.
+ JITEvent_EmittedFunctionDetails EmissionDetails;
+
// CurFnStubUses - For a given Function, a vector of stubs that it
// references. This facilitates the JIT detecting that a stub is no
// longer used, so that it may be deallocated.
// in the JITResolver's ExternalFnToStubMap.
StringMap<void *> ExtFnStubs;
+ DebugLocTuple PrevDLT;
+
public:
JITEmitter(JIT &jit, JITMemoryManager *JMM) : Resolver(jit), CurFn(0) {
MemMgr = JMM ? JMM : JITMemoryManager::CreateDefaultMemManager();
/// MachineRelocations that reference external functions by name.
const StringMap<void*> &getExternalFnStubs() const { return ExtFnStubs; }
+ virtual void processDebugLoc(DebugLoc DL);
+
virtual void emitLabel(uint64_t LabelID) {
if (LabelLocations.size() <= LabelID)
LabelLocations.resize((LabelID+1)*2);
FnRefs.insert(CurFn);
}
+void JITEmitter::processDebugLoc(DebugLoc DL) {
+ if (!DL.isUnknown()) {
+ DebugLocTuple CurDLT = EmissionDetails.MF->getDebugLocTuple(DL);
+
+ if (CurDLT.CompileUnit != 0 && PrevDLT != CurDLT) {
+ JITEvent_EmittedFunctionDetails::LineStart NextLine;
+ NextLine.Address = getCurrentPCValue();
+ NextLine.Loc = DL;
+ EmissionDetails.LineStarts.push_back(NextLine);
+ }
+
+ PrevDLT = CurDLT;
+ }
+}
+
static unsigned GetConstantPoolSizeInBytes(MachineConstantPool *MCP,
const TargetData *TD) {
const std::vector<MachineConstantPoolEntry> &Constants = MCP->getConstants();
TheJIT->updateGlobalMapping(F.getFunction(), CurBufferPtr);
MBBLocations.clear();
+
+ EmissionDetails.MF = &F;
+ EmissionDetails.LineStarts.clear();
}
bool JITEmitter::finishFunction(MachineFunction &F) {
// Invalidate the icache if necessary.
sys::Memory::InvalidateInstructionCache(FnStart, FnEnd-FnStart);
- JITEvent_EmittedFunctionDetails Details;
TheJIT->NotifyFunctionEmitted(*F.getFunction(), FnStart, FnEnd-FnStart,
- Details);
+ EmissionDetails);
DOUT << "JIT: Finished CodeGen of [" << (void*)FnStart
<< "] Function: " << F.getFunction()->getName()
#define DEBUG_TYPE "oprofile-jit-event-listener"
#include "llvm/Function.h"
+#include "llvm/Analysis/DebugInfo.h"
+#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/ExecutionEngine/JITEventListener.h"
#include "llvm/Support/Debug.h"
#include "llvm/System/Errno.h"
}
}
+class FilenameCache {
+ // Holds the filename of each CompileUnit, so that we can pass the
+ // pointer into oprofile. These char*s are freed in the destructor.
+ DenseMap<GlobalVariable*, char*> Filenames;
+ // Used as the scratch space in DICompileUnit::getFilename().
+ std::string TempFilename;
+
+ public:
+ const char* getFilename(GlobalVariable *CompileUnit) {
+ char *&Filename = Filenames[CompileUnit];
+ if (Filename == NULL) {
+ DICompileUnit CU(CompileUnit);
+ Filename = strdup(CU.getFilename(TempFilename).c_str());
+ }
+ return Filename;
+ }
+ ~FilenameCache() {
+ for (DenseMap<GlobalVariable*, char*>::iterator
+ I = Filenames.begin(), E = Filenames.end(); I != E;++I) {
+ free(I->second);
+ }
+ }
+};
+
+static debug_line_info LineStartToOProfileFormat(
+ const MachineFunction &MF, FilenameCache &Filenames,
+ uintptr_t Address, DebugLoc Loc) {
+ debug_line_info Result;
+ Result.vma = Address;
+ const DebugLocTuple& tuple = MF.getDebugLocTuple(Loc);
+ Result.lineno = tuple.Line;
+ Result.filename = Filenames.getFilename(tuple.CompileUnit);
+ DOUT << "Mapping " << reinterpret_cast<void*>(Result.vma) << " to "
+ << Result.filename << ":" << Result.lineno << "\n";
+ return Result;
+}
+
// Adds the just-emitted function to the symbol table.
void OProfileJITEventListener::NotifyFunctionEmitted(
const Function &F, void *FnStart, size_t FnSize,
- const EmittedFunctionDetails &) {
+ const EmittedFunctionDetails &Details) {
const char *const FnName = F.getNameStart();
assert(FnName != 0 && FnStart != 0 && "Bad symbol to add");
if (op_write_native_code(Agent, FnName,
FnStart, FnSize) == -1) {
DOUT << "Failed to tell OProfile about native function " << FnName
<< " at [" << FnStart << "-" << ((char*)FnStart + FnSize) << "]\n";
+ return;
+ }
+
+ // Now we convert the line number information from the address/DebugLoc format
+ // in Details to the address/filename/lineno format that OProfile expects.
+ // OProfile 0.9.4 (and maybe later versions) has a bug that causes it to
+ // ignore line numbers for addresses above 4G.
+ FilenameCache Filenames;
+ std::vector<debug_line_info> LineInfo;
+ LineInfo.reserve(1 + Details.LineStarts.size());
+ if (!Details.MF->getDefaultDebugLoc().isUnknown()) {
+ LineInfo.push_back(LineStartToOProfileFormat(
+ *Details.MF, Filenames,
+ reinterpret_cast<uintptr_t>(FnStart),
+ Details.MF->getDefaultDebugLoc()));
+ }
+ for (std::vector<EmittedFunctionDetails::LineStart>::const_iterator
+ I = Details.LineStarts.begin(), E = Details.LineStarts.end();
+ I != E; ++I) {
+ LineInfo.push_back(LineStartToOProfileFormat(
+ *Details.MF, Filenames, I->Address, I->Loc));
+ }
+ if (!LineInfo.empty()) {
+ if (op_write_debug_line_info(Agent, FnStart,
+ LineInfo.size(), &*LineInfo.begin()) == -1) {
+ DOUT << "Failed to tell OProfile about line numbers for native function "
+ << FnName << " at [" << FnStart << "-" << ((char*)FnStart + FnSize)
+ << "]\n";
+ }
}
}
const TargetInstrDesc *Desc) {
DOUT << MI;
+ MCE.processDebugLoc(MI.getDebugLoc());
+
unsigned Opcode = Desc->Opcode;
// Emit the lock opcode prefix as needed.
llvm_unreachable(0);
}
}
-