From: Chris Lattner Date: Tue, 22 Jun 2010 00:03:40 +0000 (+0000) Subject: implement support for -finstrument-functions, patch by Nelson X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=7255a2d997b15beae82e627052fdb1b2474495c2;p=clang implement support for -finstrument-functions, patch by Nelson Elhage! git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@106507 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/Attr.h b/include/clang/AST/Attr.h index b67b53b10e..0f0215a7c8 100644 --- a/include/clang/AST/Attr.h +++ b/include/clang/AST/Attr.h @@ -300,6 +300,7 @@ DEF_SIMPLE_ATTR(Deprecated); DEF_SIMPLE_ATTR(GNUInline); DEF_SIMPLE_ATTR(Malloc); DEF_SIMPLE_ATTR(NoReturn); +DEF_SIMPLE_ATTR(NoInstrumentFunction); class SectionAttr : public AttrWithString { public: diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index 4b85bb20db..98871d2620 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -261,6 +261,11 @@ def NoReturn : Attr { let Namespaces = ["", "std"]; } +def NoInstrumentFunction : Attr { + let Spellings = ["no_instrument_function"]; + let Subjects = [Function]; +} + def NoThrow : Attr { let Spellings = ["nothrow"]; } diff --git a/include/clang/Driver/CC1Options.td b/include/clang/Driver/CC1Options.td index ab9f902743..c1de8b1d3a 100644 --- a/include/clang/Driver/CC1Options.td +++ b/include/clang/Driver/CC1Options.td @@ -123,6 +123,8 @@ def fno_common : Flag<"-fno-common">, HelpText<"Compile common globals like normal definitions">; def no_implicit_float : Flag<"-no-implicit-float">, HelpText<"Don't generate implicit floating point instructions (x86-only)">; +def finstrument_functions : Flag<"-finstrument-functions">, + HelpText<"Generate calls to instrument function entry and exit">; def fno_merge_all_constants : Flag<"-fno-merge-all-constants">, HelpText<"Disallow merging of constants.">; def fno_threadsafe_statics : Flag<"-fno-threadsafe-statics">, diff --git a/include/clang/Driver/Options.td b/include/clang/Driver/Options.td index 7ac04bec9d..f7af6dbe04 100644 --- a/include/clang/Driver/Options.td +++ b/include/clang/Driver/Options.td @@ -278,6 +278,7 @@ def filelist : Separate<"-filelist">, Flags<[LinkerInput]>; def findirect_virtual_calls : Flag<"-findirect-virtual-calls">, Group; def finline_functions : Flag<"-finline-functions">, Group; def finline : Flag<"-finline">, Group; +def finstrument_functions : Flag<"-finstrument-functions">, Group; def fkeep_inline_functions : Flag<"-fkeep-inline-functions">, Group; def flat__namespace : Flag<"-flat_namespace">; def flax_vector_conversions : Flag<"-flax-vector-conversions">, Group; diff --git a/include/clang/Frontend/CodeGenOptions.h b/include/clang/Frontend/CodeGenOptions.h index bfee72cba4..2d21f1d30b 100644 --- a/include/clang/Frontend/CodeGenOptions.h +++ b/include/clang/Frontend/CodeGenOptions.h @@ -47,6 +47,7 @@ public: /// done. unsigned DisableRedZone : 1; /// Set when -mno-red-zone is enabled. unsigned FunctionSections : 1; /// Set when -ffunction-sections is enabled + unsigned InstrumentFunctions : 1; /// Set when -finstrument-functions is enabled unsigned MergeAllConstants : 1; /// Merge identical constants. unsigned NoCommon : 1; /// Set when -fno-common or C++ is enabled. unsigned NoImplicitFloat : 1; /// Set when -mno-implicit-float is enabled. diff --git a/lib/AST/AttrImpl.cpp b/lib/AST/AttrImpl.cpp index 6db43c9565..b09ba895c0 100644 --- a/lib/AST/AttrImpl.cpp +++ b/lib/AST/AttrImpl.cpp @@ -93,6 +93,7 @@ DEF_SIMPLE_ATTR_CLONE(NSReturnsNotRetained) DEF_SIMPLE_ATTR_CLONE(NSReturnsRetained) DEF_SIMPLE_ATTR_CLONE(NoDebug) DEF_SIMPLE_ATTR_CLONE(NoInline) +DEF_SIMPLE_ATTR_CLONE(NoInstrumentFunction) DEF_SIMPLE_ATTR_CLONE(NoReturn) DEF_SIMPLE_ATTR_CLONE(NoThrow) DEF_SIMPLE_ATTR_CLONE(ObjCException) diff --git a/lib/CodeGen/CodeGenFunction.cpp b/lib/CodeGen/CodeGenFunction.cpp index af062356f6..8a0b41a7ca 100644 --- a/lib/CodeGen/CodeGenFunction.cpp +++ b/lib/CodeGen/CodeGenFunction.cpp @@ -20,7 +20,9 @@ #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/StmtCXX.h" +#include "clang/Frontend/CodeGenOptions.h" #include "llvm/Target/TargetData.h" +#include "llvm/Intrinsics.h" using namespace clang; using namespace CodeGen; @@ -127,6 +129,8 @@ void CodeGenFunction::FinishFunction(SourceLocation EndLoc) { // Emit function epilog (to return). EmitReturnBlock(); + EmitFunctionInstrumentation("__cyg_profile_func_exit"); + // Emit debug descriptor for function end. if (CGDebugInfo *DI = getDebugInfo()) { DI->setLocation(EndLoc); @@ -159,6 +163,41 @@ void CodeGenFunction::FinishFunction(SourceLocation EndLoc) { } } +/// ShouldInstrumentFunction - Return true if the current function should be +/// instrumented with __cyg_profile_func_* calls +bool CodeGenFunction::ShouldInstrumentFunction() { + if (!CGM.getCodeGenOpts().InstrumentFunctions) + return false; + if (CurFuncDecl->hasAttr()) + return false; + return true; +} + +/// EmitFunctionInstrumentation - Emit LLVM code to call the specified +/// instrumentation function with the current function and the call site, if +/// function instrumentation is enabled. +void CodeGenFunction::EmitFunctionInstrumentation(const char *Fn) { + if (!ShouldInstrumentFunction()) + return; + + const llvm::FunctionType *FunctionTy; + std::vector ProfileFuncArgs; + + ProfileFuncArgs.push_back(CurFn->getType()); + ProfileFuncArgs.push_back(llvm::Type::getInt8PtrTy(VMContext)); + FunctionTy = llvm::FunctionType::get( + llvm::Type::getVoidTy(VMContext), + ProfileFuncArgs, false); + + llvm::Constant *F = CGM.CreateRuntimeFunction(FunctionTy, Fn); + llvm::CallInst *CallSite = Builder.CreateCall( + CGM.getIntrinsic(llvm::Intrinsic::returnaddress, 0, 0), + llvm::ConstantInt::get(llvm::Type::getInt32Ty(VMContext), 0), + "callsite"); + + Builder.CreateCall2(F, CurFn, CallSite); +} + void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy, llvm::Function *Fn, const FunctionArgList &Args, @@ -208,6 +247,8 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy, DI->EmitFunctionStart(GD, FnType, CurFn, Builder); } + EmitFunctionInstrumentation("__cyg_profile_func_enter"); + // FIXME: Leaked. // CC info is ignored, hopefully? CurFnInfo = &CGM.getTypes().getFunctionInfo(FnRetTy, Args, diff --git a/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h index 0fdb60ccbb..f797c2c4ff 100644 --- a/lib/CodeGen/CodeGenFunction.h +++ b/lib/CodeGen/CodeGenFunction.h @@ -566,6 +566,15 @@ public: void EmitDtorEpilogue(const CXXDestructorDecl *Dtor, CXXDtorType Type); + /// ShouldInstrumentFunction - Return true if the current function should be + /// instrumented with __cyg_profile_func_* calls + bool ShouldInstrumentFunction(); + + /// EmitFunctionInstrumentation - Emit LLVM code to call the specified + /// instrumentation function with the current function and the call site, if + /// function instrumentation is enabled. + void EmitFunctionInstrumentation(const char *Fn); + /// EmitFunctionProlog - Emit the target specific LLVM code to load the /// arguments for the given function. This is also responsible for naming the /// LLVM function arguments. diff --git a/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp index ae197fbeba..cf866c3d47 100644 --- a/lib/Driver/Tools.cpp +++ b/lib/Driver/Tools.cpp @@ -1021,6 +1021,8 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, Args.AddAllArgs(CmdArgs, options::OPT_ffunction_sections); Args.AddAllArgs(CmdArgs, options::OPT_fdata_sections); + Args.AddAllArgs(CmdArgs, options::OPT_finstrument_functions); + Args.AddLastArg(CmdArgs, options::OPT_nostdinc); Args.AddLastArg(CmdArgs, options::OPT_nostdincxx); Args.AddLastArg(CmdArgs, options::OPT_nobuiltininc); diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index a925047d94..e7a75a619d 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -840,6 +840,8 @@ static void ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, Opts.MainFileName = Args.getLastArgValue(OPT_main_file_name); Opts.VerifyModule = !Args.hasArg(OPT_disable_llvm_verifier); + Opts.InstrumentFunctions = Args.hasArg(OPT_finstrument_functions); + if (Arg *A = Args.getLastArg(OPT_fobjc_dispatch_method_EQ)) { llvm::StringRef Name = A->getValue(Args); unsigned Method = llvm::StringSwitch(Name) diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index 5858de0669..dee10fb608 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -1698,6 +1698,23 @@ static void HandleNoInlineAttr(Decl *d, const AttributeList &Attr, Sema &S) { d->addAttr(::new (S.Context) NoInlineAttr()); } +static void HandleNoInstrumentFunctionAttr(Decl *d, const AttributeList &Attr, + Sema &S) { + // check the attribute arguments. + if (Attr.getNumArgs() != 0) { + S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 0; + return; + } + + if (!isa(d)) { + S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) + << Attr.getName() << 0 /*function*/; + return; + } + + d->addAttr(::new (S.Context) NoInstrumentFunctionAttr()); +} + static void HandleGNUInlineAttr(Decl *d, const AttributeList &Attr, Sema &S) { // check the attribute arguments. if (Attr.getNumArgs() != 0) { @@ -2030,9 +2047,11 @@ static void ProcessDeclAttribute(Scope *scope, Decl *D, case AttributeList::AT_noinline: HandleNoInlineAttr (D, Attr, S); break; case AttributeList::AT_regparm: HandleRegparmAttr (D, Attr, S); break; case AttributeList::IgnoredAttribute: - case AttributeList::AT_no_instrument_function: // Interacts with -pg. // Just ignore break; + case AttributeList::AT_no_instrument_function: // Interacts with -pg. + HandleNoInstrumentFunctionAttr(D, Attr, S); + break; case AttributeList::AT_stdcall: case AttributeList::AT_cdecl: case AttributeList::AT_fastcall: diff --git a/test/CodeGen/instrument-functions.c b/test/CodeGen/instrument-functions.c new file mode 100644 index 0000000000..d80385e223 --- /dev/null +++ b/test/CodeGen/instrument-functions.c @@ -0,0 +1,18 @@ +// RUN: %clang_cc1 -S -emit-llvm -o - %s -finstrument-functions | FileCheck %s + +// CHECK: @test1 +int test1(int x) { +// CHECK: __cyg_profile_func_enter +// CHECK: __cyg_profile_func_exit +// CHECK: ret + return x; +} + +// CHECK: @test2 +int test2(int) __attribute__((no_instrument_function)); +int test2(int x) { +// CHECK-NOT: __cyg_profile_func_enter +// CHECK-NOT: __cyg_profile_func_exit +// CHECK: ret + return x; +}