From 389dc94d4be7a75c243528981a25260c1c7a6103 Mon Sep 17 00:00:00 2001 From: Petr Hosek Date: Tue, 10 Aug 2021 11:03:07 -0700 Subject: [PATCH] [InstrProfiling] Generate runtime hook for Fuchsia When none of the translation units in the binary have been instrumented we shouldn't need to link the profile runtime. However, because we pass -u__llvm_profile_runtime on Linux and Fuchsia, the runtime would still be pulled in and incur some overhead. On Fuchsia which uses runtime counter relocation, it also means that we cannot reference the bias variable unconditionally. This change modifies the InstrProfiling pass to pull in the profile runtime only when needed by declaring the __llvm_profile_runtime symbol in the translation unit only when needed. For now we restrict this only for Fuchsia, but this can be later expanded to other platforms. This approach was already used prior to 9a041a75221ca, but we changed it to always generate the __llvm_profile_runtime due to a TAPI limitation, but that limitation may no longer apply, and it certainly doesn't apply on platforms like Fuchsia. Differential Revision: https://reviews.llvm.org/D98061 --- clang/docs/UsersManual.rst | 8 +++ clang/lib/Driver/ToolChains/Fuchsia.cpp | 10 ---- clang/lib/Driver/ToolChains/Fuchsia.h | 3 -- clang/test/Driver/fuchsia.c | 2 - .../Transforms/Instrumentation/InstrProfiling.cpp | 59 ++++++++++++++-------- .../Instrumentation/InstrProfiling/profiling.ll | 3 +- 6 files changed, 48 insertions(+), 37 deletions(-) diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst index 980d0ab..f8a2f39 100644 --- a/clang/docs/UsersManual.rst +++ b/clang/docs/UsersManual.rst @@ -2345,6 +2345,14 @@ In these cases, you can use the flag ``-fno-profile-instr-generate`` (or Note that these flags should appear after the corresponding profile flags to have an effect. +.. note:: + + When none of the translation units inside a binary is instrumented, in the + case of Fuchsia the profile runtime will not be linked into the binary and + no profile will be produced, while on other platforms the profile runtime + will be linked and profile will be produced but there will not be any + counters. + Instrumenting only selected files or functions ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/lib/Driver/ToolChains/Fuchsia.cpp b/clang/lib/Driver/ToolChains/Fuchsia.cpp index fd9804a..144443d 100644 --- a/clang/lib/Driver/ToolChains/Fuchsia.cpp +++ b/clang/lib/Driver/ToolChains/Fuchsia.cpp @@ -437,13 +437,3 @@ SanitizerMask Fuchsia::getDefaultSanitizers() const { } return Res; } - -void Fuchsia::addProfileRTLibs(const llvm::opt::ArgList &Args, - llvm::opt::ArgStringList &CmdArgs) const { - // Add linker option -u__llvm_profile_runtime to cause runtime - // initialization module to be linked in. - if (needsProfileRT(Args)) - CmdArgs.push_back(Args.MakeArgString( - Twine("-u", llvm::getInstrProfRuntimeHookVarName()))); - ToolChain::addProfileRTLibs(Args, CmdArgs); -} diff --git a/clang/lib/Driver/ToolChains/Fuchsia.h b/clang/lib/Driver/ToolChains/Fuchsia.h index 07adf9b..1a0263c 100644 --- a/clang/lib/Driver/ToolChains/Fuchsia.h +++ b/clang/lib/Driver/ToolChains/Fuchsia.h @@ -71,9 +71,6 @@ public: SanitizerMask getSupportedSanitizers() const override; SanitizerMask getDefaultSanitizers() const override; - void addProfileRTLibs(const llvm::opt::ArgList &Args, - llvm::opt::ArgStringList &CmdArgs) const override; - RuntimeLibType GetRuntimeLibType(const llvm::opt::ArgList &Args) const override; CXXStdlibType diff --git a/clang/test/Driver/fuchsia.c b/clang/test/Driver/fuchsia.c index 899bb6c..e9b3969 100644 --- a/clang/test/Driver/fuchsia.c +++ b/clang/test/Driver/fuchsia.c @@ -249,7 +249,6 @@ // RUN: -fuse-ld=lld 2>&1 \ // RUN: | FileCheck %s -check-prefix=CHECK-PROFRT-AARCH64 // CHECK-PROFRT-AARCH64: "-resource-dir" "[[RESOURCE_DIR:[^"]+]]" -// CHECK-PROFRT-AARCH64: "-u__llvm_profile_runtime" // CHECK-PROFRT-AARCH64: "[[RESOURCE_DIR]]{{/|\\\\}}lib{{/|\\\\}}aarch64-unknown-fuchsia{{/|\\\\}}libclang_rt.profile.a" // RUN: %clang %s -### --target=x86_64-unknown-fuchsia \ @@ -258,5 +257,4 @@ // RUN: -fuse-ld=lld 2>&1 \ // RUN: | FileCheck %s -check-prefix=CHECK-PROFRT-X86_64 // CHECK-PROFRT-X86_64: "-resource-dir" "[[RESOURCE_DIR:[^"]+]]" -// CHECK-PROFRT-X86_64: "-u__llvm_profile_runtime" // CHECK-PROFRT-X86_64: "[[RESOURCE_DIR]]{{/|\\\\}}lib{{/|\\\\}}x86_64-unknown-fuchsia{{/|\\\\}}libclang_rt.profile.a" diff --git a/llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp b/llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp index 9c931de..36762d1 100644 --- a/llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp +++ b/llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp @@ -520,6 +520,14 @@ void InstrProfiling::promoteCounterLoadStores(Function *F) { } } +static bool needsRuntimeHookUnconditionally(const Triple &TT) { + // On Fuchsia, we only need runtime hook if any counters are present. + if (TT.isOSFuchsia()) + return false; + + return true; +} + /// Check if the module contains uses of any profiling intrinsics. static bool containsProfilingIntrinsics(Module &M) { if (auto *F = M.getFunction( @@ -548,8 +556,11 @@ bool InstrProfiling::run( UsedVars.clear(); TT = Triple(M.getTargetTriple()); + bool MadeChange; + // Emit the runtime hook even if no counters are present. - bool MadeChange = emitRuntimeHook(); + if (needsRuntimeHookUnconditionally(TT)) + MadeChange = emitRuntimeHook(); // Improve compile time by avoiding linear scans when there is no work. GlobalVariable *CoverageNamesVar = @@ -588,6 +599,7 @@ bool InstrProfiling::run( emitVNodes(); emitNameData(); + emitRuntimeHook(); emitRegistration(); emitUses(); emitInitialization(); @@ -1109,9 +1121,9 @@ void InstrProfiling::emitRegistration() { } bool InstrProfiling::emitRuntimeHook() { - // We expect the linker to be invoked with -u flag for Linux or - // Fuchsia, in which case there is no need to emit the user function. - if (TT.isOSLinux() || TT.isOSFuchsia()) + // We expect the linker to be invoked with -u flag for Linux + // in which case there is no need to emit the external variable. + if (TT.isOSLinux()) return false; // If the module's provided its own runtime, we don't need to do anything. @@ -1124,23 +1136,28 @@ bool InstrProfiling::emitRuntimeHook() { new GlobalVariable(*M, Int32Ty, false, GlobalValue::ExternalLinkage, nullptr, getInstrProfRuntimeHookVarName()); - // Make a function that uses it. - auto *User = Function::Create(FunctionType::get(Int32Ty, false), - GlobalValue::LinkOnceODRLinkage, - getInstrProfRuntimeHookVarUseFuncName(), M); - User->addFnAttr(Attribute::NoInline); - if (Options.NoRedZone) - User->addFnAttr(Attribute::NoRedZone); - User->setVisibility(GlobalValue::HiddenVisibility); - if (TT.supportsCOMDAT()) - User->setComdat(M->getOrInsertComdat(User->getName())); - - IRBuilder<> IRB(BasicBlock::Create(M->getContext(), "", User)); - auto *Load = IRB.CreateLoad(Int32Ty, Var); - IRB.CreateRet(Load); - - // Mark the user variable as used so that it isn't stripped out. - CompilerUsedVars.push_back(User); + if (TT.isOSBinFormatELF()) { + // Mark the user variable as used so that it isn't stripped out. + CompilerUsedVars.push_back(Var); + } else { + // Make a function that uses it. + auto *User = Function::Create(FunctionType::get(Int32Ty, false), + GlobalValue::LinkOnceODRLinkage, + getInstrProfRuntimeHookVarUseFuncName(), M); + User->addFnAttr(Attribute::NoInline); + if (Options.NoRedZone) + User->addFnAttr(Attribute::NoRedZone); + User->setVisibility(GlobalValue::HiddenVisibility); + if (TT.supportsCOMDAT()) + User->setComdat(M->getOrInsertComdat(User->getName())); + + IRBuilder<> IRB(BasicBlock::Create(M->getContext(), "", User)); + auto *Load = IRB.CreateLoad(Int32Ty, Var); + IRB.CreateRet(Load); + + // Mark the function as used so that it isn't stripped out. + CompilerUsedVars.push_back(User); + } return true; } diff --git a/llvm/test/Instrumentation/InstrProfiling/profiling.ll b/llvm/test/Instrumentation/InstrProfiling/profiling.ll index f449fa0..1775aff 100644 --- a/llvm/test/Instrumentation/InstrProfiling/profiling.ll +++ b/llvm/test/Instrumentation/InstrProfiling/profiling.ll @@ -54,11 +54,12 @@ define void @baz() { declare void @llvm.instrprof.increment(i8*, i64, i32, i32) -; ELF: @llvm.compiler.used = appending global {{.*}} @__llvm_profile_runtime_user {{.*}} @__profd_foo {{.*}} @__profd_bar {{.*}} @__profd_baz +; ELF: @llvm.compiler.used = appending global {{.*}} @__llvm_profile_runtime {{.*}} @__profd_foo {{.*}} @__profd_bar {{.*}} @__profd_baz ; MACHO: @llvm.used = appending global {{.*}} @__llvm_profile_runtime_user {{.*}} @__profd_foo {{.*}} @__profd_bar {{.*}} @__profd_baz ; WIN: @llvm.compiler.used = appending global {{.*}} @__llvm_profile_runtime_user {{.*}} @__profd_foo {{.*}} @__profd_bar {{.*}} @__profd_baz ; ELF_GENERIC: define internal void @__llvm_profile_register_functions() unnamed_addr { +; ELF_GENERIC-NEXT: call void @__llvm_profile_register_function(i8* bitcast (i32* @__llvm_profile_runtime to i8*)) ; ELF_GENERIC-NEXT: call void @__llvm_profile_register_function(i8* bitcast ({ i64, i64, i64, i8*, i8*, i32, [2 x i16] }* @__profd_foo to i8*)) ; ELF_GENERIC-NEXT: call void @__llvm_profile_register_function(i8* bitcast ({ i64, i64, i64, i8*, i8*, i32, [2 x i16] }* @__profd_bar to i8*)) ; ELF_GENERIC-NEXT: call void @__llvm_profile_register_function(i8* bitcast ({ i64, i64, i64, i8*, i8*, i32, [2 x i16] }* @__profd_baz to i8*)) -- 2.7.4