From 596fc8225487f887989688472df132602fe6a57d Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Thu, 20 Oct 2016 00:35:14 +0200 Subject: [PATCH] Implement -finstrument-functions. Closes #1839. --- driver/cl_options.cpp | 5 +++++ driver/cl_options.h | 1 + gen/functions.cpp | 4 ++++ gen/runtime.cpp | 36 +++++++++++++++++++++++++++++++++ gen/runtime.h | 4 ++++ gen/statements.cpp | 2 ++ tests/codegen/instrumentation.d | 29 ++++++++++++++++++++++++++ 7 files changed, 81 insertions(+) create mode 100644 tests/codegen/instrumentation.d diff --git a/driver/cl_options.cpp b/driver/cl_options.cpp index d82aba970f1..b7be19bdb01 100644 --- a/driver/cl_options.cpp +++ b/driver/cl_options.cpp @@ -455,6 +455,11 @@ cl::opt usefileInstrProf( cl::ValueRequired); #endif +cl::opt + instrumentFunctions("finstrument-functions", + cl::desc("Instrument function entry and exit with " + "GCC-compatible profiling calls")); + static cl::extrahelp footer( "\n" "-d-debug can also be specified without options, in which case it enables " diff --git a/driver/cl_options.h b/driver/cl_options.h index ef3388403f7..a2b8b49e1d0 100644 --- a/driver/cl_options.h +++ b/driver/cl_options.h @@ -87,6 +87,7 @@ extern cl::opt nestedTemplateDepth; extern cl::opt genfileInstrProf; extern cl::opt usefileInstrProf; #endif +extern cl::opt instrumentFunctions; // Arguments to -d-debug extern std::vector debugArgs; diff --git a/gen/functions.cpp b/gen/functions.cpp index 287eee75019..840e8c35116 100644 --- a/gen/functions.cpp +++ b/gen/functions.cpp @@ -962,6 +962,8 @@ void DtoDefineFunction(FuncDeclaration *fd, bool linkageAvailableExternally) { // debug info - after all allocas, but before any llvm.dbg.declare etc gIR->DBuilder.EmitFuncStart(fd); + emitInstrumentationFnEnter(fd); + // this hack makes sure the frame pointer elimination optimization is // disabled. // this this eliminates a bunch of inline asm related issues. @@ -1077,6 +1079,8 @@ 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. + emitInstrumentationFnLeave(fd); + // pass the previous block into this block gIR->DBuilder.EmitStopPoint(fd->endloc); if (func->getReturnType() == LLType::getVoidTy(gIR->context())) { diff --git a/gen/runtime.cpp b/gen/runtime.cpp index d46fc0ac5bb..4fe29d2b5c2 100644 --- a/gen/runtime.cpp +++ b/gen/runtime.cpp @@ -21,6 +21,7 @@ #include "ir/irfunction.h" #include "ir/irtype.h" #include "ir/irtypefunction.h" +#include "driver/cl_options.h" #include "ldcbindings.h" #include "mars.h" #include "module.h" @@ -329,6 +330,16 @@ static void buildRuntimeModule() { ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// + // void __cyg_profile_func_enter(void *callee, void *caller) + // void __cyg_profile_func_exit(void *callee, void *caller) + createFwdDecl(LINKc, voidTy, + {"__cyg_profile_func_exit", "__cyg_profile_func_enter"}, + {voidPtrTy, voidPtrTy}, {}, Attr_NoUnwind); + + ////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////// + // void _d_assert(string file, uint line) // void _d_arraybounds(string file, uint line) createFwdDecl(LINKc, Type::tvoid, {"_d_assert", "_d_arraybounds"}, @@ -739,3 +750,28 @@ static void buildRuntimeModule() { } } } + +static void emitInstrumentationFn(const char *name) { + LLFunction *fn = getRuntimeFunction(Loc(), gIR->module, name); + + // Grab the address of the calling function + auto *caller = + gIR->ir->CreateCall(GET_INTRINSIC_DECL(returnaddress), DtoConstInt(1)); + auto callee = DtoBitCast(gIR->topfunc(), getVoidPtrType()); + +#if LDC_LLVM_VER >= 307 + gIR->ir->CreateCall(fn, {callee, caller}); +#else + gIR->ir->CreateCall2(fn, callee, caller); +#endif +} + +void emitInstrumentationFnEnter(FuncDeclaration *decl) { + if (opts::instrumentFunctions && decl->emitInstrumentation) + emitInstrumentationFn("__cyg_profile_func_enter"); +} + +void emitInstrumentationFnLeave(FuncDeclaration *decl) { + if (opts::instrumentFunctions && decl->emitInstrumentation) + emitInstrumentationFn("__cyg_profile_func_exit"); +} diff --git a/gen/runtime.h b/gen/runtime.h index 3c6118391a5..10bd26d95d7 100644 --- a/gen/runtime.h +++ b/gen/runtime.h @@ -21,6 +21,7 @@ class Module; } struct Loc; +struct FuncDeclaration; // D runtime support helpers bool initRuntime(); @@ -32,4 +33,7 @@ llvm::Function *getRuntimeFunction(const Loc &loc, llvm::Module &target, llvm::GlobalVariable * getRuntimeGlobal(const Loc &loc, llvm::Module &target, const char *name); +void emitInstrumentationFnEnter(FuncDeclaration *decl); +void emitInstrumentationFnLeave(FuncDeclaration *decl); + #endif // LDC_GEN_RUNTIME_H diff --git a/gen/statements.cpp b/gen/statements.cpp index 4595f49ed09..0e730ca7ef9 100644 --- a/gen/statements.cpp +++ b/gen/statements.cpp @@ -134,6 +134,8 @@ class ToIRVisitor : public Visitor { FuncDeclaration *const fd = f->decl; LLFunction *const llFunc = f->func; + emitInstrumentationFnLeave(fd); + // 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 diff --git a/tests/codegen/instrumentation.d b/tests/codegen/instrumentation.d new file mode 100644 index 00000000000..a5b85ca2856 --- /dev/null +++ b/tests/codegen/instrumentation.d @@ -0,0 +1,29 @@ +// RUN: %ldc -c -output-ll -finstrument-functions -of=%t.ll %s && FileCheck %s < %t.ll + +void fun0 () { + // CHECK-LABEL: define{{.*}} @{{.*}}4fun0FZv + // CHECK: call void @__cyg_profile_func_enter + // CHECK: call void @__cyg_profile_func_exit + // CHECK-NEXT: ret + return; +} + +pragma(LDC_profile_instr, false) +int fun1 (int x) { + // CHECK-LABEL: define{{.*}} @{{.*}}4fun1FiZi + // CHECK-NOT: call void @__cyg_profile_func_enter + // CHECK-NOT: call void @__cyg_profile_func_exit + return 42; +} + +bool fun2 (int x) { + // CHECK-LABEL: define{{.*}} @{{.*}}4fun2FiZb + if (x < 10) + // CHECK: call void @__cyg_profile_func_exit + // CHECK-NEXT: ret + return true; + + // CHECK: call void @__cyg_profile_func_exit + // CHECK-NEXT: ret + return false; +}