diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -120,6 +120,12 @@ ///< XRay instrumentation. VALUE_CODEGENOPT(XRayInstructionThreshold , 32, 200) +///< Only instrument 1 in N functions, by dividing functions into N total groups and +///< instrumenting only the specified group at a time. Group numbers start at 0 +///< and end at N-1. +VALUE_CODEGENOPT(XRayTotalFunctionGroups, 32, 1) +VALUE_CODEGENOPT(XRaySelectedFunctionGroup, 32, 0) + VALUE_CODEGENOPT(PatchableFunctionEntryCount , 32, 0) ///< Number of NOPs at function entry VALUE_CODEGENOPT(PatchableFunctionEntryOffset , 32, 0) diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -1339,6 +1339,17 @@ Group, Flags<[CC1Option]>, HelpText<"Select which XRay instrumentation points to emit. Options: all, none, function-entry, function-exit, function, custom. Default is 'all'. 'function' includes both 'function-entry' and 'function-exit'.">; +def fxray_function_groups : + Joined<["-"], "fxray-function-groups=">, + Group, Flags<[CC1Option]>, + HelpText<"Only instrument 1 of N groups">; + +def fxray_selected_function_group : + Joined<["-"], "fxray-selected-function-group=">, + Group, Flags<[CC1Option]>, + HelpText<"When using -fxray-function-groups, select which group of functions to instrument. Valid range is 0 to fxray-function-groups - 1">; + + def ffine_grained_bitfield_accesses : Flag<["-"], "ffine-grained-bitfield-accesses">, Group, Flags<[CC1Option]>, HelpText<"Use separate accesses for consecutive bitfield runs with legal widths and alignments.">; diff --git a/clang/include/clang/Driver/XRayArgs.h b/clang/include/clang/Driver/XRayArgs.h --- a/clang/include/clang/Driver/XRayArgs.h +++ b/clang/include/clang/Driver/XRayArgs.h @@ -32,6 +32,8 @@ bool XRayRT = true; bool XRayIgnoreLoops = false; bool XRayFunctionIndex; + int XRayFunctionGroups = 1; + int XRaySelectedFunctionGroup; public: /// Parses the XRay arguments from an argument list. diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -32,6 +32,7 @@ #include "clang/Basic/TargetInfo.h" #include "clang/CodeGen/CGFunctionInfo.h" #include "clang/Frontend/FrontendDiagnostic.h" +#include "llvm/ADT/ArrayRef.h" #include "llvm/Frontend/OpenMP/OMPIRBuilder.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/Dominators.h" @@ -40,6 +41,7 @@ #include "llvm/IR/Intrinsics.h" #include "llvm/IR/MDBuilder.h" #include "llvm/IR/Operator.h" +#include "llvm/Support/CRC.h" #include "llvm/Transforms/Utils/PromoteMemToReg.h" using namespace clang; using namespace CodeGen; @@ -772,13 +774,16 @@ SanOpts.Mask &= ~SanitizerKind::Null; // Apply xray attributes to the function (as a string, for now) + bool AlwaysXRayAttr = false; if (const auto *XRayAttr = D ? D->getAttr() : nullptr) { if (CGM.getCodeGenOpts().XRayInstrumentationBundle.has( XRayInstrKind::FunctionEntry) || CGM.getCodeGenOpts().XRayInstrumentationBundle.has( XRayInstrKind::FunctionExit)) { - if (XRayAttr->alwaysXRayInstrument() && ShouldXRayInstrumentFunction()) + if (XRayAttr->alwaysXRayInstrument() && ShouldXRayInstrumentFunction()) { Fn->addFnAttr("function-instrument", "xray-always"); + AlwaysXRayAttr = true; + } if (XRayAttr->neverXRayInstrument()) Fn->addFnAttr("function-instrument", "xray-never"); if (const auto *LogArgs = D->getAttr()) @@ -804,6 +809,16 @@ if (!CGM.getCodeGenOpts().XRayInstrumentationBundle.has( XRayInstrKind::FunctionEntry)) Fn->addFnAttr("xray-skip-entry"); + + auto FuncGroups = CGM.getCodeGenOpts().XRayTotalFunctionGroups; + if (FuncGroups > 1) { + const ArrayRef FuncName(CurFn->getName().bytes_begin(), + CurFn->getName().bytes_end()); + auto Group = crc32(FuncName) % FuncGroups; + if (Group != CGM.getCodeGenOpts().XRaySelectedFunctionGroup && + !AlwaysXRayAttr) + Fn->addFnAttr("function-instrument", "xray-never"); + } } unsigned Count, Offset; diff --git a/clang/lib/Driver/XRayArgs.cpp b/clang/lib/Driver/XRayArgs.cpp --- a/clang/lib/Driver/XRayArgs.cpp +++ b/clang/lib/Driver/XRayArgs.cpp @@ -186,6 +186,21 @@ Modes.push_back(std::string(M)); } + if (const Arg *A = Args.getLastArg(options::OPT_fxray_function_groups)) { + StringRef S = A->getValue(); + if (S.getAsInteger(0, XRayFunctionGroups) || XRayFunctionGroups < 1) + D.Diag(clang::diag::err_drv_invalid_value) << A->getAsString(Args) << S; + } + + if (const Arg *A = + Args.getLastArg(options::OPT_fxray_selected_function_group)) { + StringRef S = A->getValue(); + if (S.getAsInteger(0, XRaySelectedFunctionGroup) || + XRaySelectedFunctionGroup < 0 || + XRaySelectedFunctionGroup >= XRayFunctionGroups) + D.Diag(clang::diag::err_drv_invalid_value) << A->getAsString(Args) << S; + } + // Then we want to sort and unique the modes we've collected. llvm::sort(Modes); Modes.erase(std::unique(Modes.begin(), Modes.end()), Modes.end()); @@ -210,6 +225,17 @@ if (!XRayFunctionIndex) CmdArgs.push_back("-fno-xray-function-index"); + if (XRayFunctionGroups > 1) { + CmdArgs.push_back(Args.MakeArgString(Twine("-fxray-function-groups=") + + Twine(XRayFunctionGroups))); + } + + if (XRaySelectedFunctionGroup != 0) { + CmdArgs.push_back( + Args.MakeArgString(Twine("-fxray-selected-function-group=") + + Twine(XRaySelectedFunctionGroup))); + } + CmdArgs.push_back(Args.MakeArgString(Twine(XRayInstructionThresholdOption) + Twine(InstructionThreshold))); diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -1130,6 +1130,10 @@ getLastArgIntValue(Args, OPT_fxray_instruction_threshold_EQ, 200, Diags); Opts.XRayIgnoreLoops = Args.hasArg(OPT_fxray_ignore_loops); Opts.XRayOmitFunctionIndex = Args.hasArg(OPT_fno_xray_function_index); + Opts.XRayTotalFunctionGroups = + getLastArgIntValue(Args, OPT_fxray_function_groups, 1, Diags); + Opts.XRaySelectedFunctionGroup = + getLastArgIntValue(Args, OPT_fxray_selected_function_group, 0, Diags); auto XRayInstrBundles = Args.getAllArgValues(OPT_fxray_instrumentation_bundle); diff --git a/clang/test/CodeGen/xray-function-groups.cpp b/clang/test/CodeGen/xray-function-groups.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/xray-function-groups.cpp @@ -0,0 +1,55 @@ +// RUN: %clang_cc1 -fxray-instrument -fxray-instruction-threshold=1 -fxray-function-groups=3 -fxray-selected-function-group=0 \ +// RUN: -emit-llvm -o - %s -triple x86_64-unknown-linux-gnu | FileCheck --check-prefix=GROUP0 %s + +// RUN: %clang_cc1 -fxray-instrument -fxray-instruction-threshold=1 -fxray-function-groups=3 -fxray-selected-function-group=1 \ +// RUN: -emit-llvm -o - %s -triple x86_64-unknown-linux-gnu | FileCheck --check-prefix=GROUP1 %s + +// RUN: %clang_cc1 -fxray-instrument -fxray-instruction-threshold=1 -fxray-function-groups=3 -fxray-selected-function-group=2 \ +// RUN: -emit-llvm -o - %s -triple x86_64-unknown-linux-gnu | FileCheck --check-prefix=GROUP2 %s + +int foo() { // part of group 0 + return 1; +} + +int bar() { // part of group 2 + return 1; +} + +int yarr() { // part of group 1 + return 1; +} + +[[clang::xray_always_instrument]] int always() { // part of group 0 + return 1; +} + +[[clang::xray_never_instrument]] int never() { // part of group 1 + return 1; +} + +// GROUP0: define{{.*}} i32 @_Z3foov() #[[ATTRS_FOO:[0-9]+]] { +// GROUP0: define{{.*}} i32 @_Z3barv() #[[ATTRS_BAR:[0-9]+]] { +// GROUP0: define{{.*}} i32 @_Z4yarrv() #[[ATTRS_BAR]] { +// GROUP0: define{{.*}} i32 @_Z6alwaysv() #[[ATTRS_ALWAYS:[0-9]+]] { +// GROUP0: define{{.*}} i32 @_Z5neverv() #[[ATTRS_NEVER:[0-9]+]] { +// GROUP0-DAG: attributes #[[ATTRS_BAR]] = {{.*}} "function-instrument"="xray-never" {{.*}} +// GROUP0-DAG: attributes #[[ATTRS_ALWAYS]] = {{.*}} "function-instrument"="xray-always" {{.*}} +// GROUP0-DAG: attributes #[[ATTRS_NEVER]] = {{.*}} "function-instrument"="xray-never" {{.*}} + +// GROUP1: define{{.*}} i32 @_Z3foov() #[[ATTRS_FOO:[0-9]+]] { +// GROUP1: define{{.*}} i32 @_Z3barv() #[[ATTRS_FOO]] { +// GROUP1: define{{.*}} i32 @_Z4yarrv() #[[ATTRS_YARR:[0-9]+]] { +// GROUP1: define{{.*}} i32 @_Z6alwaysv() #[[ATTRS_ALWAYS:[0-9]+]] { +// GROUP1: define{{.*}} i32 @_Z5neverv() #[[ATTRS_NEVER:[0-9]+]] { +// GROUP1-DAG: attributes #[[ATTRS_FOO]] = {{.*}} "function-instrument"="xray-never" {{.*}} +// GROUP1-DAG: attributes #[[ATTRS_ALWAYS]] = {{.*}} "function-instrument"="xray-always" {{.*}} +// GROUP1-DAG: attributes #[[ATTRS_NEVER]] = {{.*}} "function-instrument"="xray-never" {{.*}} + +// GROUP2: define{{.*}} i32 @_Z3foov() #[[ATTRS_FOO:[0-9]+]] { +// GROUP2: define{{.*}} i32 @_Z3barv() #[[ATTRS_BAR:[0-9]+]] { +// GROUP2: define{{.*}} i32 @_Z4yarrv() #[[ATTRS_FOO]] { +// GROUP2: define{{.*}} i32 @_Z6alwaysv() #[[ATTRS_ALWAYS:[0-9]+]] { +// GROUP2: define{{.*}} i32 @_Z5neverv() #[[ATTRS_NEVER:[0-9]+]] { +// GROUP2-DAG: attributes #[[ATTRS_FOO]] = {{.*}} "function-instrument"="xray-never" {{.*}} +// GROUP2-DAG: attributes #[[ATTRS_ALWAYS]] = {{.*}} "function-instrument"="xray-always" {{.*}} +// GROUP2-DAG: attributes #[[ATTRS_NEVER]] = {{.*}} "function-instrument"="xray-never" {{.*}}