Skip to content

Commit

Permalink
Implement -finstrument-functions.
Browse files Browse the repository at this point in the history
Closes #1839.
  • Loading branch information
LemonBoy committed Oct 20, 2016
1 parent 86d11c8 commit 43fa944
Show file tree
Hide file tree
Showing 7 changed files with 82 additions and 0 deletions.
1 change: 1 addition & 0 deletions ddmd/globals.h
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ struct Param
OUTPUTFLAG output_bc;
OUTPUTFLAG output_s;
OUTPUTFLAG output_o;
bool instrumentFunctions;
bool useInlineAsm;
bool verbose_cg;
bool hasObjectiveC;
Expand Down
5 changes: 5 additions & 0 deletions driver/cl_options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,11 @@ cl::opt<std::string> usefileInstrProf(
cl::ValueRequired);
#endif

cl::opt<bool, true> instrumentFunctions(
"finstrument-functions",
cl::desc("Instrument function entry and exit with profiling calls"),
cl::location(global.params.instrumentFunctions));

static cl::extrahelp footer(
"\n"
"-d-debug can also be specified without options, in which case it enables "
Expand Down
6 changes: 6 additions & 0 deletions gen/functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -973,6 +973,9 @@ void DtoDefineFunction(FuncDeclaration *fd, bool linkageAvailableExternally) {
// debug info - after all allocas, but before any llvm.dbg.declare etc
gIR->DBuilder.EmitFuncStart(fd);

if (global.params.instrumentFunctions && fd->emitInstrumentation)
emitInstrumentationFn("__cyg_profile_func_enter");

// this hack makes sure the frame pointer elimination optimization is
// disabled.
// this this eliminates a bunch of inline asm related issues.
Expand Down Expand Up @@ -1088,6 +1091,9 @@ void DtoDefineFunction(FuncDeclaration *fd, bool linkageAvailableExternally) {
// llvm requires all basic blocks to end with a TerminatorInst but DMD does
// not put a return statement in automatically, so we do it here.

if (global.params.instrumentFunctions && fd->emitInstrumentation)
emitInstrumentationFn("__cyg_profile_func_exit");

// pass the previous block into this block
gIR->DBuilder.EmitStopPoint(fd->endloc);
if (func->getReturnType() == LLType::getVoidTy(gIR->context())) {
Expand Down
3 changes: 3 additions & 0 deletions gen/statements.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,9 @@ class ToIRVisitor : public Visitor {
FuncDeclaration *const fd = f->decl;
LLFunction *const llFunc = f->func;

if (global.params.instrumentFunctions && fd->emitInstrumentation)
emitInstrumentationFn("__cyg_profile_func_exit");

// is there a return value expression?
if (stmt->exp || (!stmt->exp && (llFunc == irs->mainFunc))) {
// if the function's return type is void, it uses sret
Expand Down
29 changes: 29 additions & 0 deletions gen/tollvm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -782,3 +782,32 @@ LLValue *DtoAggrPaint(LLValue *aggr, LLType *as) {
V = DtoBitCast(V, as->getContainedType(1));
return gIR->ir->CreateInsertValue(res, V, 1);
}

////////////////////////////////////////////////////////////////////////////////

void emitInstrumentationFn(const char *fnName) {
LLType *VoidPtrTy = getVoidPtrType();

// Generate the prototype to call either:
// void __cyg_profile_func_enter (void *callee, void *caller)
// void __cyg_profile_func_exit (void *callee, void *caller)
LLFunction *fn = gIR->module.getFunction(fnName);
if (!fn) {
LLType *VoidTy = LLType::getVoidTy(gIR->context());
LLType *Tys[] = {VoidPtrTy, VoidPtrTy};
auto fty = LLFunctionType::get(VoidTy, Tys, false);
fn = LLFunction::Create(fty, LLGlobalValue::ExternalLinkage, fnName,
&gIR->module);
}

// Grab the address of the calling function
auto *caller =
gIR->ir->CreateCall(GET_INTRINSIC_DECL(returnaddress), DtoConstInt(1));
auto callee = DtoBitCast(gIR->topfunc(), VoidPtrTy);

#if LDC_LLVM_VER >= 307
gIR->ir->CreateCall(fn, {callee, caller});
#else
gIR->ir->CreateCall2(fn, callee, caller);
#endif
}
5 changes: 5 additions & 0 deletions gen/tollvm.h
Original file line number Diff line number Diff line change
Expand Up @@ -200,4 +200,9 @@ void DtoMemCpy(LLValue *dst, LLValue *src, bool withPadding = false,
*/
LLValue *DtoMemCmp(LLValue *lhs, LLValue *rhs, LLValue *nbytes);

/**
* Generate a call to __cyg_profile_func_{enter,exit}
*/
void emitInstrumentationFn(const char *fName);

#endif // LDC_GEN_TOLLVM_H
33 changes: 33 additions & 0 deletions tests/codegen/instrumentation.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// RUN: %ldc -c -output-ll -finstrument-functions -of=%t.ll %s && FileCheck %s < %t.ll

void fun0 () {
// CHECK: %1 = call i8* @llvm.returnaddress(i32 1)
// CHECK-NEXT: call void @__cyg_profile_func_enter
// CHECK-NEXT: %2 = call i8* @llvm.returnaddress(i32 1)
// CHECK-NEXT: call void @__cyg_profile_func_exit
// CHECK-NEXT: ret void
return;
}

pragma(LDC_profile_instr, false)
int fun1 (int x) {
// CHECK: %x = alloca i32, align 4
// CHECK-NEXT: store i32 %x_arg, i32* %x
// CHECK-NEXT: ret i32 42
return 42;
}

bool fun2 (int x) {
// CHECK: %1 = call i8* @llvm.returnaddress(i32 1)
// CHECK-NEXT: call void @__cyg_profile_func_enter
if (x < 10)
// CHECK: if:
// CHECK-NEXT: %{{.}} = call i8* @llvm.returnaddress(i32 1)
// CHECK-NEXT: call void @__cyg_profile_func_exit
return true;

// CHECK: endif:
// CHECK-NEXT: %{{.}} = call i8* @llvm.returnaddress(i32 1)
// CHECK-NEXT: call void @__cyg_profile_func_exit
return false;
}

0 comments on commit 43fa944

Please sign in to comment.