diff --git a/CMakeLists.txt b/CMakeLists.txt index 8eb419ec0b..ac1f811183 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -767,9 +767,13 @@ if (LDC_INSTALL_LLVM_RUNTIME_LIBS) copy_compilerrt_lib("darwin/libclang_rt.asan_osx_dynamic.dylib" "libldc_rt.asan_osx_dynamic.dylib" TRUE) if(NOT (LDC_LLVM_VER LESS 600)) copy_compilerrt_lib("darwin/libclang_rt.fuzzer_osx.a" "libldc_rt.fuzzer_osx.a" FALSE) + copy_compilerrt_lib("darwin/libclang_rt.xray_osx.a" "libldc_rt.xray_osx.a" FALSE) endif() elseif(UNIX) copy_compilerrt_lib("linux/libclang_rt.asan-x86_64.a" "libldc_rt.asan-x86_64.a" FALSE) + if(NOT (LDC_LLVM_VER LESS 500)) + copy_compilerrt_lib("linux/libclang_rt.xray-x86_64.a" "libldc_rt.xray-x86_64.a" FALSE) + endif() if(NOT (LDC_LLVM_VER LESS 600)) copy_compilerrt_lib("linux/libclang_rt.fuzzer-x86_64.a" "libldc_rt.fuzzer-x86_64.a" FALSE) endif() diff --git a/driver/cl_options_instrumentation.cpp b/driver/cl_options_instrumentation.cpp index e4c89c9431..54058371ae 100644 --- a/driver/cl_options_instrumentation.cpp +++ b/driver/cl_options_instrumentation.cpp @@ -17,6 +17,7 @@ #include "errors.h" #include "globals.h" +#include "gen/to_string.h" #include "llvm/ADT/Triple.h" namespace { @@ -52,6 +53,15 @@ cl::opt ASTPGOInstrUseFile( cl::desc("Use instrumentation data for profile-guided optimization"), cl::ValueRequired); +#if LDC_LLVM_VER >= 500 +cl::opt fXRayInstructionThreshold( + "fxray-instruction-threshold", cl::value_desc("value"), + cl::desc("Sets the minimum function size to instrument with XRay"), + cl::init(200), cl::ZeroOrMore, cl::ValueRequired); +#else +constexpr int fXRayInstructionThreshold = 200; +#endif + } // anonymous namespace namespace opts { @@ -68,6 +78,20 @@ static cl::opt dmdFunctionTrace( "fdmd-trace-functions", cl::ZeroOrMore, cl::desc("DMD-style runtime performance profiling of generated code")); +#if LDC_LLVM_VER >= 500 +cl::opt fXRayInstrument( + "fxray-instrument", cl::ZeroOrMore, + cl::desc("Generate XRay instrumentation sleds on function entry and exit")); +#endif + +llvm::StringRef getXRayInstructionThresholdString() { + // The instruction threshold is constant during one compiler invoke, so we + // can cache the int->string conversion result. + static std::string thresholdString = + ldc::to_string(fXRayInstructionThreshold); + return thresholdString; +} + void initializeInstrumentationOptionsFromCmdline(const llvm::Triple &triple) { if (ASTPGOInstrGenFile.getNumOccurrences() > 0) { pgoMode = PGO_ASTBasedInstr; diff --git a/driver/cl_options_instrumentation.h b/driver/cl_options_instrumentation.h index 749a395db7..a9122001ee 100644 --- a/driver/cl_options_instrumentation.h +++ b/driver/cl_options_instrumentation.h @@ -27,6 +27,13 @@ namespace cl = llvm::cl; extern cl::opt instrumentFunctions; +#if LDC_LLVM_VER >= 500 +extern cl::opt fXRayInstrument; +#else +constexpr bool fXRayInstrument = false; +#endif +llvm::StringRef getXRayInstructionThresholdString(); + /// This initializes the instrumentation options, and checks the validity of the /// commandline flags. targetTriple should be initialized before calling this. /// It should be called only once. diff --git a/driver/linker-gcc.cpp b/driver/linker-gcc.cpp index ae374adcf5..a564c54115 100644 --- a/driver/linker-gcc.cpp +++ b/driver/linker-gcc.cpp @@ -53,6 +53,7 @@ private: virtual void addASanLinkFlags(const llvm::Triple &triple); virtual void addFuzzLinkFlags(const llvm::Triple &triple); virtual void addCppStdlibLinkFlags(const llvm::Triple &triple); + virtual void addXRayLinkFlags(const llvm::Triple &triple); virtual void addLinker(); virtual void addUserSwitches(); @@ -312,6 +313,32 @@ void ArgsBuilder::addFuzzLinkFlags(const llvm::Triple &triple) { } } +// Adds all required link flags for -fxray-instrument when the xray library is +// found. +void ArgsBuilder::addXRayLinkFlags(const llvm::Triple &triple) { + std::string searchPaths[] = { + getFullCompilerRTLibPath(triple, "libldc_rt.xray"), + getFullCompilerRTLibPath(triple, "libclang_rt.xray"), + getFullClangCompilerRTLibPath(triple, "libclang_rt.xray"), + }; + + bool linkerDarwin = triple.isOSDarwin(); + if (!triple.isOSLinux()) + warning(Loc(), "XRay is not (fully) supported on non-Linux target OS."); + + for (const auto &filepath : searchPaths) { + if (!linkerDarwin) + addLdFlag("--whole-archive"); + + args.push_back(filepath); + + if (!linkerDarwin) + addLdFlag("--no-whole-archive"); + + return; + } +} + void ArgsBuilder::addCppStdlibLinkFlags(const llvm::Triple &triple) { if (linkNoCpp) return; @@ -410,6 +437,10 @@ void ArgsBuilder::build(llvm::StringRef outputPath, addSanitizers(*global.params.targetTriple); + if (opts::fXRayInstrument) { + addXRayLinkFlags(*global.params.targetTriple); + } + #if LDC_LLVM_VER >= 309 // Add LTO link flags before adding the user link switches, such that the user // can pass additional options to the LTO plugin. diff --git a/gen/functions.cpp b/gen/functions.cpp index 9f4374795e..be326938af 100644 --- a/gen/functions.cpp +++ b/gen/functions.cpp @@ -19,6 +19,7 @@ #include "statement.h" #include "template.h" #include "driver/cl_options.h" +#include "driver/cl_options_instrumentation.h" #include "driver/cl_options_sanitizers.h" #include "gen/abi.h" #include "gen/arrays.h" @@ -463,6 +464,18 @@ void applyTargetMachineAttributes(llvm::Function &func, willEliminateFramePointer() ? "false" : "true"); } +void applyXRayAttributes(FuncDeclaration &fdecl, llvm::Function &func) { + if (!opts::fXRayInstrument) + return; + + if (!fdecl.emitInstrumentation) { + func.addFnAttr("function-instrument", "xray-never"); + } else { + func.addFnAttr("xray-instruction-threshold", + opts::getXRayInstructionThresholdString()); + } +} + } // anonymous namespace //////////////////////////////////////////////////////////////////////////////// @@ -1010,6 +1023,7 @@ void DtoDefineFunction(FuncDeclaration *fd, bool linkageAvailableExternally) { func->addFnAttr(LLAttribute::SanitizeThread); } } + applyXRayAttributes(*fd, *func); llvm::BasicBlock *beginbb = llvm::BasicBlock::Create(gIR->context(), "", func); diff --git a/tests/instrument/lit.local.cfg b/tests/instrument/lit.local.cfg new file mode 100644 index 0000000000..a2a3b58a90 --- /dev/null +++ b/tests/instrument/lit.local.cfg @@ -0,0 +1,10 @@ +import os +import platform +import re + +# Add "XRay_RT" feature if the runtime library is available +for file in os.listdir(config.ldc2_lib_dir): + m = re.match('.*xray.*', file) + if m is not None: + config.available_features.add('XRay') + continue diff --git a/tests/instrument/xray_check_pipeline.d b/tests/instrument/xray_check_pipeline.d new file mode 100644 index 0000000000..985f1dd9dc --- /dev/null +++ b/tests/instrument/xray_check_pipeline.d @@ -0,0 +1,11 @@ +// Check that the LLVM pipeline is set up correctly to generate XRay sleds. + +// If we have the XRay runtime lib for this platform, then we can also do machinecodegen: +// REQUIRES: XRay_RT + +// RUN: %ldc -c -output-s -betterC -fxray-instrument -fxray-instruction-threshold=1 -of=%t.s %s && FileCheck %s < %t.s + +// CHECK: xray_sled +void instrument() +{ +} diff --git a/tests/instrument/xray_instrument.d b/tests/instrument/xray_instrument.d new file mode 100644 index 0000000000..8b769f18b6 --- /dev/null +++ b/tests/instrument/xray_instrument.d @@ -0,0 +1,21 @@ +// REQUIRES: atleast_llvm500 + +// RUN: %ldc -c -output-ll -fxray-instrument -of=%t.ll %s && FileCheck %s < %t.ll + +import ldc.attributes; + +// CHECK-LABEL: define{{.*}} @{{.*}}10instrument +// CHECK-SAME: #[[INSTR:[0-9]+]] +void instrument() +{ +} + +// CHECK-LABEL: define{{.*}} @{{.*}}15dont_instrument +// CHECK-SAME: #[[DONT_INSTR:[0-9]+]] +void dont_instrument() +{ + pragma(LDC_profile_instr, false); +} + +// CHECK-DAG: attributes #[[INSTR]] ={{.*}} "xray-instruction-threshold"= +// CHECK-DAG: attributes #[[DONT_INSTR]] ={{.*}} "function-instrument"="xray-never" diff --git a/tests/instrument/xray_instrument_threshold.d b/tests/instrument/xray_instrument_threshold.d new file mode 100644 index 0000000000..07ad048050 --- /dev/null +++ b/tests/instrument/xray_instrument_threshold.d @@ -0,0 +1,11 @@ +// REQUIRES: atleast_llvm500 + +// RUN: %ldc -c -output-ll -fxray-instrument -fxray-instruction-threshold=543 -of=%t.ll %s && FileCheck %s < %t.ll + +// CHECK-LABEL: define{{.*}} @{{.*}}10instrument +// CHECK-SAME: #[[INSTR:[0-9]+]] +void instrument() +{ +} + +// CHECK-DAG: attributes #[[INSTR]] ={{.*}} "xray-instruction-threshold"="543" diff --git a/tests/instrument/xray_link.d b/tests/instrument/xray_link.d new file mode 100644 index 0000000000..3f2079c52c --- /dev/null +++ b/tests/instrument/xray_link.d @@ -0,0 +1,15 @@ +// REQUIRES: XRay_RT + +// RUN: %ldc -fxray-instrument -fxray-instruction-threshold=1 -of=%t%exe %s -vv | FileCheck %s + +void foo() +{ +} + +void main() +{ + foo(); +} + +// CHECK: Linking with: +// CHECK-NEXT: rt.xray