[llvm] 65e0fe6 - [dyndbg][Clang] Implement nested-ELF dynamic debugging support (#216307)
via llvm-commits
llvm-commits at lists.llvm.org
Sun Aug 16 09:36:57 PDT 2026
Author: Orlando Cazalet-Hyams
Date: 2026-08-16T17:36:50+01:00
New Revision: 65e0fe689bb7c1a6516644c27c25c3fe3c8b1ac7
URL: https://github.com/llvm/llvm-project/commit/65e0fe689bb7c1a6516644c27c25c3fe3c8b1ac7
DIFF: https://github.com/llvm/llvm-project/commit/65e0fe689bb7c1a6516644c27c25c3fe3c8b1ac7.diff
LOG: [dyndbg][Clang] Implement nested-ELF dynamic debugging support (#216307)
Re-land https://github.com/llvm/llvm-project/pull/194860
A clone of the module is compiled without optimisations and embedded into the
to-be-optimised module using `embedBufferInModule`, similarly to how
`-ffat-lto-objects` and `-fembed-offload-object` work.
That unoptimised-code object is embedded in the optimised-code object in a
section called `.debug_llvm_dyndbg`.
The optimised ELF/module may be referred to as the "outer" ELF/module, and the
unoptimized one the "inner" ELF/module.
The outer module holds global data referred to by both modules and all calls in
the inner module are to outer module functions. To facilitate this the outer
module is modified, adding external-linkage aliases for local symbols.
For more detail see RFC https://discourse.llvm.org/t/90113 and documentation at
llvm/docs/DynamicDebugging.md.
Added:
clang/test/DebugInfo/DynamicDebugging/EndToEnd/X86/globalopt.c
clang/test/DebugInfo/DynamicDebugging/EndToEnd/X86/lit.local.cfg
clang/test/DebugInfo/DynamicDebugging/EndToEnd/X86/obj-emission-target/lit.local.cfg
clang/test/DebugInfo/DynamicDebugging/EndToEnd/X86/obj-emission-target/no-target.c
clang/test/DebugInfo/DynamicDebugging/EndToEnd/X86/section.c
clang/test/DebugInfo/DynamicDebugging/attr-outer-tail-pad.c
clang/test/DebugInfo/DynamicDebugging/attr-outer.c
clang/test/DebugInfo/DynamicDebugging/compiler-used.cpp
clang/test/DebugInfo/DynamicDebugging/embed.c
clang/test/DebugInfo/DynamicDebugging/inner-attrs.c
clang/test/DebugInfo/DynamicDebugging/profile-coverage.c
clang/test/DebugInfo/DynamicDebugging/symbols-functions.cpp
clang/test/DebugInfo/DynamicDebugging/symbols-globals.cpp
clang/test/DebugInfo/DynamicDebugging/symbols-internal-comdat.cpp
clang/test/Driver/dynamic-debugging-flags.c
Modified:
clang/include/clang/Basic/CodeGenOptions.h
clang/include/clang/Basic/DebugOptions.def
clang/include/clang/Basic/DiagnosticDriverKinds.td
clang/include/clang/Basic/DiagnosticFrontendKinds.td
clang/include/clang/Basic/DiagnosticGroups.td
clang/include/clang/Options/Options.td
clang/lib/CodeGen/BackendUtil.cpp
clang/lib/Driver/ToolChains/Clang.cpp
clang/lib/Frontend/CompilerInvocation.cpp
llvm/include/llvm/Transforms/Utils/ModuleUtils.h
llvm/lib/Transforms/Utils/ModuleUtils.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h
index 4eec3522b1255..babc5cf76d44a 100644
--- a/clang/include/clang/Basic/CodeGenOptions.h
+++ b/clang/include/clang/Basic/CodeGenOptions.h
@@ -397,6 +397,9 @@ class CodeGenOptions : public CodeGenOptionsBase {
/// Prefix to use for -save-temps output.
std::string SaveTempsFilePrefix;
+ /// Prefix to use for -save-dynamic-debugging-temps output.
+ std::string SaveDynDbgTempsFilePrefix;
+
/// Name of file passed with -foffload-include-binary option to forward to
/// offloading runtime back-end for incorporating them into host-side object
/// file.
diff --git a/clang/include/clang/Basic/DebugOptions.def b/clang/include/clang/Basic/DebugOptions.def
index c9dd3f726e799..e496825d51274 100644
--- a/clang/include/clang/Basic/DebugOptions.def
+++ b/clang/include/clang/Basic/DebugOptions.def
@@ -62,6 +62,13 @@ ENUM_DEBUGOPT(AssignmentTrackingMode, AssignmentTrackingOpts, 2,
/// Whether or not to use Key Instructions to determine breakpoint locations.
DEBUGOPT(DebugKeyInstructions, 1, 0, Benign)
+/// Whether or not to use the Dynamic Debugging feature.
+DEBUGOPT(DynamicDebugging, 1, 0, Benign)
+
+/// Flag for testing: discard the dynamic debugging debug module before codegen
+/// if true.
+DEBUGOPT(DiscardDynamicDebuggingDebugModule, 1, 0, Benign)
+
DEBUGOPT(DebugColumnInfo, 1, 0, Compatible) ///< Whether or not to use column information
///< in debug info.
diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td
index 91895d4957cf7..929421f915a4e 100644
--- a/clang/include/clang/Basic/DiagnosticDriverKinds.td
+++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td
@@ -409,6 +409,11 @@ def err_drv_optimization_remark_format : Error<
def err_drv_no_neon_modifier : Error<"[no]neon is not accepted as modifier, please use [no]simd instead">;
def err_drv_invalid_omp_target : Error<"OpenMP target is invalid: '%0'">;
def err_drv_incompatible_omp_arch : Error<"OpenMP target architecture '%0' pointer size is incompatible with host '%1'">;
+def err_drv_dyndbg_lto : Error<"'-fdynamic-debugging' incompatible with '-flto'">;
+def err_drv_dyndbg_incompatible : Error<"'-fdynamic-debugging' incompatible with '%0'">;
+def err_drv_dyndbg_ir : Error<"'-fdynamic-debugging' incompatible with IR input">;
+def warn_drv_dyndbg_req_debug : Warning<"'-fdynamic-debugging' ignored: requires debug info">,
+ InGroup<DynamicDebugging>;
def err_drv_omp_host_target_not_supported : Error<
"target '%0' is not a supported OpenMP host target">;
def err_drv_expecting_fopenmp_with_fopenmp_targets : Error<
diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
index 151de132433d1..051a50406812a 100644
--- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td
+++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
@@ -550,4 +550,10 @@ def err_member_ptr_requires_complete_type
: Error<"member pointer representation requires a complete class type for "
"%0 to perform this expression">;
+def warn_dyndbg_unable_to_create_target : Warning<
+ "ignoring -fdynamic-debugging: unable to create target: '%0'">,
+ InGroup<DynamicDebugging>;
+
+def err_dyndbg_no_instrumentation : Error<
+ "'-fdynamic-debugging' unsupported with instrumentation (PGO/code coverage)">;
}
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index d44f69a815784..e55cb3ac23a3a 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -2023,3 +2023,5 @@ def ExperimentalOption : DiagGroup<"experimental-option">;
def UnguardedBuiltinUsageAMDGPU : DiagGroup<"amdgpu-unguarded-builtin-usage">;
def AvailableVisibleAMDGPU : DiagGroup<"amdgpu-av">;
+
+def DynamicDebugging : DiagGroup<"dynamic-debugging">;
diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td
index 3abb04a5f36c7..adc4224dd561c 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -5372,6 +5372,24 @@ defm key_instructions : BoolGOption<"key-instructions",
" in some debuggers. DWARF only.">,
BothFlags<[], [ClangOption, CLOption, CC1Option]>>,
Group<g_flags_Group>;
+defm dynamic_debugging : BoolOption<"f", "dynamic-debugging",
+ CodeGenOpts<"DynamicDebugging">, DefaultFalse,
+ NegFlag<SetFalse>, PosFlag<SetTrue, [], [],
+ "Enable Dynamic Debugging, which allows runtime switching between"
+ " optimized and unoptimized code in debuggers that support it."
+ " Specified optimization level affects only the optimized code."
+ " This flag inhibits interprocedural optimizations.">,
+ BothFlags<[], [ClangOption, CLOption, CC1Option]>>;
+def save_dynamic_debugging_temps : Flag<["--"], "save-dynamic-debugging-temps">,
+ Visibility<[ClangOption, CLOption, CC1Option]>,
+ HelpText<"Compiler-debugging/testing option to save the intermediate"
+ " states of dynamic debugging in the same directory as the final"
+ " output file">;
+def discard_dynamic_debugging_debug_module : Flag<["--"], "discard-dynamic-debugging-debug-module">,
+ MarshallingInfoFlag<CodeGenOpts<"DiscardDynamicDebuggingDebugModule">>,
+ Visibility<[CC1Option]>,
+ HelpText<"Compiler-debugging/testing option to discard the dynamic"
+ " debugging debug module before Clang codegen">;
def headerpad__max__install__names : Joined<["-"], "headerpad_max_install_names">;
def help : Flag<["-", "--"], "help">,
Visibility<[ClangOption, CC1Option, CC1AsOption,
diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp
index 0b1a4221d3351..75c3d16e455f1 100644
--- a/clang/lib/CodeGen/BackendUtil.cpp
+++ b/clang/lib/CodeGen/BackendUtil.cpp
@@ -22,6 +22,7 @@
#include "llvm/Analysis/RuntimeLibcallInfo.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/TargetTransformInfo.h"
+#include "llvm/BinaryFormat/ELF.h"
#include "llvm/Bitcode/BitcodeReader.h"
#include "llvm/Bitcode/BitcodeWriter.h"
#include "llvm/Bitcode/BitcodeWriterPass.h"
@@ -93,6 +94,7 @@
#include "llvm/Transforms/Scalar/JumpThreading.h"
#include "llvm/Transforms/Utils/AssignGUID.h"
#include "llvm/Transforms/Utils/Debugify.h"
+#include "llvm/Transforms/Utils/DynamicDebugging.h"
#include "llvm/Transforms/Utils/ModuleUtils.h"
#include <limits>
#include <memory>
@@ -1495,6 +1497,89 @@ runThinLTOBackend(CompilerInstance &CI, ModuleSummaryIndex *CombinedIndex,
}
}
+static void createAndEmbedModuleForDynamicDebugging(
+ CompilerInstance &CI, CodeGenOptions &CGOpts, llvm::Module *M,
+ IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS, BackendConsumer *BC) {
+ /// Helper for saving the module(s) at various dyndbg stages.
+ auto SaveModule = [&](StringRef Name, llvm::Module &M) {
+ if (CGOpts.SaveDynDbgTempsFilePrefix == "")
+ return;
+ std::error_code EC;
+ std::string Path =
+ Twine(CGOpts.SaveDynDbgTempsFilePrefix + "." + Name + ".ll").str();
+ raw_fd_ostream OS(Path, EC, sys::fs::OpenFlags::OF_None);
+ if (EC) {
+ // Copy -save-temps behaviour: this is a debugging option so we simply
+ // exit if there's an issue.
+ errs() << "failed to open " << Path << ": " << EC.message() << '\n';
+ errs().flush();
+ exit(1);
+ }
+ M.print(OS, nullptr);
+ };
+
+ // Compute a hash suffix for promoting static globals (once per TU).
+ std::string PromotionSuffix;
+ {
+ // LLVM's hash/hash_combine is not guaranteed to be stable.
+ MD5 Hash;
+ // Include args in the hash else preprocessor definitions used to alter
+ // the same source file compiled twice won't generate unique hashes.
+ Hash.update(CGOpts.CmdArgs);
+ for (auto *CU : M->debug_compile_units()) {
+ Hash.update(CU->getDirectory());
+ Hash.update(CU->getFilename());
+ }
+
+ MD5::MD5Result Result;
+ Hash.final(Result);
+ PromotionSuffix = ".dyndbg." + utohexstr(Result.low());
+ }
+
+ SaveModule("dyndbg.0.input", *M);
+ // Modify M as needed and create an "unoptimized" clone.
+ auto UnoptM = prepareForDynamicDebugging(M, PromotionSuffix);
+ SaveModule("dyndbg.1.inner", *UnoptM);
+
+ if (!CGOpts.DiscardDynamicDebuggingDebugModule) {
+ CodeGenOptions UnoptOpts = CGOpts;
+ UnoptOpts.OptimizationLevel = 0;
+ UnoptOpts.OptimizeSize = 0;
+ EmitAssemblyHelper AsmHelper(CI, UnoptOpts, UnoptM.get(), VFS);
+
+ // Create a buffer and ostream for the inner ELF.
+ SmallVector<char, 0> UnoptBuf;
+ std::unique_ptr<llvm::raw_pwrite_stream> UnoptOS =
+ std::make_unique<llvm::raw_svector_ostream>(UnoptBuf);
+
+ // Always run the full codegen pipeline (Backend_EmitObj). This causes
+ // assertion failures if there's no registered backend which is why we
+ // disable the feature if that's the case (see
+ // warn_dyndbg_unable_to_create_target above).
+ AsmHelper.emitAssembly(Backend_EmitObj, std::move(UnoptOS), BC);
+ assert(!UnoptBuf.empty() && "Expected emitAssembly to fill UnoptBuf");
+
+ // Inject the inner ELF into the outer module.
+ StringRef SR(UnoptBuf.data(), UnoptBuf.size());
+ std::unique_ptr<MemoryBuffer> Buf =
+ MemoryBuffer::getMemBuffer(SR, "", false);
+
+ GlobalVariable *EmbeddedGV =
+ llvm::embedBufferInModule(*M, *Buf, ".debug_llvm_dyndbg", Align(8),
+ /*SectionExclude*/ false);
+ // Add ELF section properties metadata.
+ auto &C = M->getContext();
+ auto getU32Metadata = [&C](unsigned Val) {
+ return ConstantAsMetadata::get(ConstantInt::get(C, APInt(32, Val)));
+ };
+ EmbeddedGV->addMetadata(
+ LLVMContext::MD_elf_section_properties,
+ *MDTuple::get(C, {/*sh_type*/ getU32Metadata(ELF::SHT_LLVM_DYNDBG_ELF),
+ /*sh_entsize*/ getU32Metadata(0)}));
+ }
+ SaveModule("dyndbg.2.outer", *M);
+}
+
void clang::emitBackendOutput(CompilerInstance &CI, CodeGenOptions &CGOpts,
StringRef TDesc, llvm::Module *M,
BackendAction Action,
@@ -1518,7 +1603,7 @@ void clang::emitBackendOutput(CompilerInstance &CI, CodeGenOptions &CGOpts,
.moveInto(CombinedIndex)) {
logAllUnhandledErrors(std::move(E), errs(),
"Error loading index file '" +
- CGOpts.ThinLTOIndexFile + "': ");
+ CGOpts.ThinLTOIndexFile + "': ");
return;
}
@@ -1544,6 +1629,30 @@ void clang::emitBackendOutput(CompilerInstance &CI, CodeGenOptions &CGOpts,
}
}
+ bool EnableDynamicDebugging = CGOpts.DynamicDebugging;
+ if (EnableDynamicDebugging) {
+ // Disable dyndbg if the target isn't available as we're compiling to the
+ // inner module (unless we're discarding it for debugging/testing).
+ std::string Error;
+ const llvm::Target *TheTarget =
+ TargetRegistry::lookupTarget(M->getTargetTriple(), Error);
+ if (!TheTarget && !CGOpts.DiscardDynamicDebuggingDebugModule) {
+ Diags.Report(diag::warn_dyndbg_unable_to_create_target) << Error;
+ EnableDynamicDebugging = false;
+ }
+
+ // Instrumentation causes issues (parts of LLVM expect certain globals to
+ // have initializers). Intrinsics may already have been added to IR by now,
+ // so we can't just turn it off for the inner module (we'd have to strip
+ // them out / not clone them). TODO: Support instrumentation.
+ if (CGOpts.getProfileInstr() != driver::ProfileInstrKind::ProfileNone) {
+ Diags.Report(diag::err_dyndbg_no_instrumentation);
+ EnableDynamicDebugging = false;
+ }
+ }
+ if (EnableDynamicDebugging)
+ createAndEmbedModuleForDynamicDebugging(CI, CGOpts, M, VFS, BC);
+
EmitAssemblyHelper AsmHelper(CI, CGOpts, M, VFS);
AsmHelper.emitAssembly(Action, std::move(OS), BC);
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 91ad5be238f5d..63eaa130d0969 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -4661,7 +4661,7 @@ renderDebugOptions(const ToolChain &TC, const Driver &D, const llvm::Triple &T,
const ArgList &Args, types::ID InputType,
ArgStringList &CmdArgs, const InputInfo &Output,
llvm::codegenoptions::DebugInfoKind &DebugInfoKind,
- DwarfFissionKind &DwarfFission) {
+ DwarfFissionKind &DwarfFission, bool IsUsingLTO) {
bool IRInput = isLLVMIR(InputType);
bool PlainCOrCXX = isDerivedFromC(InputType) && !isCuda(InputType) &&
!isHIP(InputType) && !isObjC(InputType) &&
@@ -4886,6 +4886,36 @@ renderDebugOptions(const ToolChain &TC, const Driver &D, const llvm::Triple &T,
options::OPT_gno_structor_decl_linkage_names, true))
CmdArgs.push_back("-gno-structor-decl-linkage-names");
+ if (Args.hasFlag(options::OPT_fdynamic_debugging,
+ options::OPT_fno_dynamic_debugging, false)) {
+ // As this is an experimental feature we can afford to be strict about
+ // supported configurations.
+ // NOTE on adding target support, consider adding "tail-pad-to-size"
+ // support in `llvm::prepareForDynamicDebugging`.
+ if (!TC.getTriple().isX86())
+ D.Diag(diag::err_drv_unsupported_opt_for_target)
+ << Args.getLastArg(options::OPT_fdynamic_debugging)->getAsString(Args)
+ << T.getTriple();
+ if (IsUsingLTO)
+ D.Diag(diag::err_drv_dyndbg_lto);
+ if (DwarfFission != DwarfFissionKind::None)
+ D.Diag(diag::err_drv_dyndbg_incompatible)
+ << Args.getLastArg(options::OPT_gsplit_dwarf)->getAsString(Args);
+ // There's no fundamental reason why IR input should be incompatible, but
+ // it would add some complexity, and reducing the test matrix is valuable.
+ if (IRInput)
+ D.Diag(diag::err_drv_dyndbg_ir);
+
+ // Disable composition with sanitizers for now.
+ if (auto *San = Args.getLastArg(options::OPT_fsanitize_EQ))
+ D.Diag(diag::err_drv_dyndbg_incompatible) << San->getAsString(Args);
+
+ if (!EmitDwarf)
+ D.Diag(diag::warn_drv_dyndbg_req_debug);
+ else
+ CmdArgs.push_back("-fdynamic-debugging");
+ }
+
if (EmitCodeView) {
CmdArgs.push_back("-gcodeview");
@@ -5605,6 +5635,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
if (Args.getLastArg(options::OPT_save_temps_EQ))
Args.AddLastArg(CmdArgs, options::OPT_save_temps_EQ);
+ if (Args.getLastArg(options::OPT_save_dynamic_debugging_temps))
+ Args.AddLastArg(CmdArgs, options::OPT_save_dynamic_debugging_temps);
+
auto *MemProfArg = Args.getLastArg(options::OPT_fmemory_profile,
options::OPT_fmemory_profile_EQ,
options::OPT_fno_memory_profile);
@@ -6442,7 +6475,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
llvm::codegenoptions::NoDebugInfo;
DwarfFissionKind DwarfFission = DwarfFissionKind::None;
renderDebugOptions(TC, D, RawTriple, Args, InputType, CmdArgs, Output,
- DebugInfoKind, DwarfFission);
+ DebugInfoKind, DwarfFission, IsUsingLTO);
// Add the split debug info name to the command lines here so we
// can propagate it to the backend.
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index 256ae636ca9e6..70fc346d85920 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -1679,6 +1679,9 @@ void CompilerInvocationBase::GenerateCodeGenArgs(const CodeGenOptions &Opts,
if (Opts.SaveTempsFilePrefix == OutputFile)
GenerateArg(Consumer, OPT_save_temps_EQ, "obj");
+ if (!Opts.SaveDynDbgTempsFilePrefix.empty())
+ GenerateArg(Consumer, OPT_save_dynamic_debugging_temps);
+
StringRef MemProfileBasename("memprof.profraw");
if (!Opts.MemoryProfileOutput.empty()) {
if (Opts.MemoryProfileOutput == MemProfileBasename) {
@@ -2013,6 +2016,9 @@ bool CompilerInvocation::ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args,
.Case("obj", OutputFile)
.Default(llvm::sys::path::filename(OutputFile).str());
+ if (Args.getLastArg(OPT_save_dynamic_debugging_temps))
+ Opts.SaveDynDbgTempsFilePrefix = OutputFile;
+
// The memory profile runtime appends the pid to make this name more unique.
const char *MemProfileBasename = "memprof.profraw";
if (Args.hasArg(OPT_fmemory_profile_EQ)) {
diff --git a/clang/test/DebugInfo/DynamicDebugging/EndToEnd/X86/globalopt.c b/clang/test/DebugInfo/DynamicDebugging/EndToEnd/X86/globalopt.c
new file mode 100644
index 0000000000000..3cd4c630fb861
--- /dev/null
+++ b/clang/test/DebugInfo/DynamicDebugging/EndToEnd/X86/globalopt.c
@@ -0,0 +1,24 @@
+// Check `a` and `d` have aliases while keeping their original symbols.
+// GlobalOpt would usually replace these internal-linkage functions with
+// their external-linkage aliases. That's currently prevented as a side effect
+// of adding discardable functions to the compiler-used global.
+
+// RUN: %clang -cc1 %s -emit-obj -O3 -debug-info-kind=limited -fdynamic-debugging -o - -triple x86_64-unknown-unknown | llvm-nm - | FileCheck %s
+// CHECK: t a
+// CHECK: T a.dyndbg.[[hash:[A-F0-9]+]]
+// CHECK: T b
+// CHECK: U c
+// CHECK: t d
+// CHECK: T d.dyndbg.[[hash]]
+// CHECK: B g
+
+int g;
+int c();
+
+__attribute__((always_inline))
+static inline int d() { return c(); }
+
+__attribute__((always_inline))
+static int a() { return g; }
+
+int b() { return a() + d() ;}
diff --git a/clang/test/DebugInfo/DynamicDebugging/EndToEnd/X86/lit.local.cfg b/clang/test/DebugInfo/DynamicDebugging/EndToEnd/X86/lit.local.cfg
new file mode 100644
index 0000000000000..bf8bd32700a9d
--- /dev/null
+++ b/clang/test/DebugInfo/DynamicDebugging/EndToEnd/X86/lit.local.cfg
@@ -0,0 +1,4 @@
+# These tests require x86 in order to inspect the inner object.
+# FIXME: These tests are quite broad.
+if "x86-registered-target" not in config.available_features:
+ config.unsupported = True
diff --git a/clang/test/DebugInfo/DynamicDebugging/EndToEnd/X86/obj-emission-target/lit.local.cfg b/clang/test/DebugInfo/DynamicDebugging/EndToEnd/X86/obj-emission-target/lit.local.cfg
new file mode 100644
index 0000000000000..d36760917851d
--- /dev/null
+++ b/clang/test/DebugInfo/DynamicDebugging/EndToEnd/X86/obj-emission-target/lit.local.cfg
@@ -0,0 +1,3 @@
+# This test wants X86 support without aarch64 support.
+if "x86-registered-target" not in config.available_features or "aarch64-registered-target" in config.available_features:
+ config.unsupported = True
diff --git a/clang/test/DebugInfo/DynamicDebugging/EndToEnd/X86/obj-emission-target/no-target.c b/clang/test/DebugInfo/DynamicDebugging/EndToEnd/X86/obj-emission-target/no-target.c
new file mode 100644
index 0000000000000..b5af3a3b7ac9c
--- /dev/null
+++ b/clang/test/DebugInfo/DynamicDebugging/EndToEnd/X86/obj-emission-target/no-target.c
@@ -0,0 +1,14 @@
+// Check we get a warning if -fdynamic-debugging is specified for an
+// unsupported target. Dynamic debugging currently emits the inner module as
+// an object regardless of output flags (e.g. -emit-llvm).
+
+// RUN: %clang -cc1 %s -emit-llvm -debug-info-kind=limited -fdynamic-debugging -o - -triple aarch64-unknown-unknown 2>&1 | FileCheck %s --check-prefix=WITHOUT_TARGET
+// WITHOUT_TARGET: warning: ignoring -fdynamic-debugging: unable to create target: 'No available targets are compatible with triple "aarch64-unknown-unknown"'
+// WITHOUT_TARGET-NOT: .debug_llvm_dyndbg
+
+// Prevent rotten green test by checking we do see .debug_llvm_dyndbg otherwise.
+// RUN: %clang -cc1 %s -emit-obj -debug-info-kind=limited -fdynamic-debugging -o - -triple x86_64-unknown-unknown 2>&1 | FileCheck %s --check-prefix=WITH_TARGET
+// WITH_TARGET-NOT: ignoring -fdynamic-debugging:
+// WITH_TARGET: .debug_llvm_dyndbg
+
+int g;
diff --git a/clang/test/DebugInfo/DynamicDebugging/EndToEnd/X86/section.c b/clang/test/DebugInfo/DynamicDebugging/EndToEnd/X86/section.c
new file mode 100644
index 0000000000000..10e1a6ef05da9
--- /dev/null
+++ b/clang/test/DebugInfo/DynamicDebugging/EndToEnd/X86/section.c
@@ -0,0 +1,15 @@
+// By default LLVM gives this section SHF_EXCLUDE, which we don't want. We
+// expect a section named .debug_llvm_dyndbg of type LLVM_DYNDBG_ELF with
+// no flags and alignment of 8 bytes.
+
+// RUN: %clang -cc1 %s -emit-obj -debug-info-kind=limited -fdynamic-debugging -o - -triple x86_64-unknown-unknown | llvm-readelf --section-details - \
+// RUN: | FileCheck %s
+// [Nr] Name
+// CHECK: .debug_llvm_dyndbg
+// Type Address Off Size ES Lk Inf Al
+// CHECK-NEXT: LLVM_DYNDBG_ELF 0000000000000000 {{[0-9a-f]+}} {{[0-9a-f]+}} 00 0 0 8
+// Flags
+// CHECK-NEXT: [0000000000000000]: {{$}}
+
+int g;
+int b() { return g; }
diff --git a/clang/test/DebugInfo/DynamicDebugging/attr-outer-tail-pad.c b/clang/test/DebugInfo/DynamicDebugging/attr-outer-tail-pad.c
new file mode 100644
index 0000000000000..d327a01c23f02
--- /dev/null
+++ b/clang/test/DebugInfo/DynamicDebugging/attr-outer-tail-pad.c
@@ -0,0 +1,11 @@
+// REQUIRES: x86-registered-target
+// RUN: %clang -cc1 %s -triple x86_64-unknown-unknown -debug-info-kind=constructor -fdynamic-debugging -o - \
+// RUN: -emit-llvm --discard-dynamic-debugging-debug-module \
+// RUN: | FileCheck %s --check-prefix=X86
+
+/// Pad functions to minimum of 5 bytes for insertion of 32 rel jump.
+// X86: define dso_local i32 @f() #0
+// X86: attributes #0 =
+// X86-SAME: "tail-pad-to-size"="5"
+// X86-SAME: "tail-pad-value"="144"
+int f() { return 0; }
diff --git a/clang/test/DebugInfo/DynamicDebugging/attr-outer.c b/clang/test/DebugInfo/DynamicDebugging/attr-outer.c
new file mode 100644
index 0000000000000..ec3f078bb067a
--- /dev/null
+++ b/clang/test/DebugInfo/DynamicDebugging/attr-outer.c
@@ -0,0 +1,9 @@
+// RUN: %clang -cc1 -triple x86_64-unknown-unknown %s -debug-info-kind=limited -fdynamic-debugging -o - \
+// RUN: -emit-llvm --discard-dynamic-debugging-debug-module \
+// RUN: | FileCheck %s
+
+// CHECK: define dso_local i32 @f() #0
+// CHECK: attributes #0 =
+// CHECK-SAME: noipa
+// CHECK-SAME: nooutline
+int f() { return 0; }
diff --git a/clang/test/DebugInfo/DynamicDebugging/compiler-used.cpp b/clang/test/DebugInfo/DynamicDebugging/compiler-used.cpp
new file mode 100644
index 0000000000000..b77e23ce69032
--- /dev/null
+++ b/clang/test/DebugInfo/DynamicDebugging/compiler-used.cpp
@@ -0,0 +1,37 @@
+// RUN: %clang -cc1 -triple x86_64-unknown-unknown %s -debug-info-kind=limited -fdynamic-debugging -o %t \
+// RUN: -emit-llvm --save-dynamic-debugging-temps --discard-dynamic-debugging-debug-module
+// RUN: FileCheck %s --check-prefix=OUTER < %t.dyndbg.2.outer.ll
+
+// Test discardable symbols are added to the @llvm.compiler.used global,
+// which prevents them being discarded (including by globalopt replacing) them
+// with their external linkage aliases.
+
+// OUTER: @llvm.compiler.used = appending global [[[#]] x ptr]
+// OUTER-SAME: [
+// OUTER-SAME: ptr @__cxx_global_var_init,
+// OUTER-SAME: ptr @_ZL12internal_funv,
+// OUTER-SAME: ptr @_Z14internal_fun_2v,
+// OUTER-SAME: ptr @_GLOBAL__sub_I_compiler_used.cpp,
+// OUTER-SAME: ptr @_ZL12used_by_init,
+// OUTER-SAME: ptr @odrweak,
+// OUTER-SAME: ptr @_ZL8internal,
+// OUTER-SAME: ptr @_ZZ14internal_fun_2vE8internal
+// OUTER-SAME: ],
+// OUTER-SAME: section "llvm.metadata"
+
+// 'external' has external linkage; it's not discardable if unused, so it
+// doesn't need to be added to compiler-used.
+int external = 1;
+// 'odrweak' and 'internal' are both discardable and may be only referenced
+// from the inner module, so we must keep them.
+inline int odrweak = 2;
+static int internal = 1;
+// 'unused_internal' has internal linkage and is unused so we don't need to
+// preserve it (it isn't referenced by the outer or inner module).
+static int internal_fun() { static int unused_internal = 0; return 0; }
+inline int internal_fun_2() { static int internal = 0; return internal; }
+
+// Use those globals so they're not omitted by Clang. Don't use [[gnu::used]]
+// because that populates the compiler-used global. This global itself is
+// unused in user code but it _is_ used in __cxx_global_var_init.
+static int used_by_init = external + odrweak + internal + internal_fun() + internal_fun_2();
diff --git a/clang/test/DebugInfo/DynamicDebugging/embed.c b/clang/test/DebugInfo/DynamicDebugging/embed.c
new file mode 100644
index 0000000000000..280993ef49d17
--- /dev/null
+++ b/clang/test/DebugInfo/DynamicDebugging/embed.c
@@ -0,0 +1,14 @@
+// REQUIRES: x86-registered-target
+// Requires X86 as this test runs the codegen pipeline for the debug module.
+// RUN: %clang -cc1 -emit-llvm -triple %itanium_abi_triple %s -debug-info-kind=limited -fdynamic-debugging -o - \
+// RUN: | FileCheck %s
+
+// Test that a dynamic debugging section is embedded in the outer module. Note
+// that !exclude is ignored by LLVM as this section's flags are chosen based
+// on its name. FIXME: We could introduce new metadata like !exclude to avoid
+// the special casing in LLVM.
+int e() { return 0; }
+
+// CHECK: @llvm.embedded.object = private constant {{.*}}, section ".debug_llvm_dyndbg", align 8,
+// CHECK-SAME: !elf_section_properties ![[elf_props:[0-9]+]], !metadata_section_kind
+// CHECK: ![[elf_props:[0-9]+]] = !{i32 1879002128, i32 0}
diff --git a/clang/test/DebugInfo/DynamicDebugging/inner-attrs.c b/clang/test/DebugInfo/DynamicDebugging/inner-attrs.c
new file mode 100644
index 0000000000000..fbdaedebcca69
--- /dev/null
+++ b/clang/test/DebugInfo/DynamicDebugging/inner-attrs.c
@@ -0,0 +1,27 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown %s -Os -debug-info-kind=constructor -fdynamic-debugging -o %t \
+// RUN: -emit-llvm --save-dynamic-debugging-temps --discard-dynamic-debugging-debug-module
+// RUN: FileCheck %s --check-prefix=INPUT < %t.dyndbg.0.input.ll
+// RUN: FileCheck %s --check-prefix=INNER < %t.dyndbg.1.inner.ll
+
+/// always_inline should be removed from the inner module.
+__attribute__((always_inline)) void a() { }
+__attribute__((minsize)) void b() { }
+
+/// Confirm the input module has alwaysinline, minsize, optsize.
+// INPUT: define dso_local void @a() #0
+// INPUT: define dso_local void @b() #1
+// INPUT: attributes #0 = { alwaysinline nounwind optsize "
+// INPUT: attributes #1 = { minsize nounwind optsize "
+
+/// Check the inner module has noinline and optnone added to its copies of the
+/// outer functions, removing alwaysinline, minsize, optsize.
+// INNER: define dso_local void @__dyndbg.a() #0
+// INNER: define dso_local void @__dyndbg.b() #0
+// INNER: declare dso_local void @a() #1
+// INNER: declare dso_local void @b() #2
+// INNER: attributes #0 = { noinline nounwind optnone "
+/// Inner's references to outer's functions keep their original attributes.
+/// Note that noipa and nooutline are added to the outer definition before
+/// copying the decl to Inner.
+// INNER: attributes #1 = { alwaysinline noipa nooutline nounwind optsize "
+// INNER: attributes #2 = { minsize noipa nooutline nounwind optsize "
diff --git a/clang/test/DebugInfo/DynamicDebugging/profile-coverage.c b/clang/test/DebugInfo/DynamicDebugging/profile-coverage.c
new file mode 100644
index 0000000000000..30f78af33c883
--- /dev/null
+++ b/clang/test/DebugInfo/DynamicDebugging/profile-coverage.c
@@ -0,0 +1,7 @@
+// Instrumentation from PGO/code coverage isn't supported yet for dyndbg.
+// RUN: not %clang_cc1 -triple x86_64-unknown-unknown %s -fprofile-instrument=clang -fdynamic-debugging -emit-llvm \
+// RUN: --discard-dynamic-debugging-debug-module 2>&1 \
+// RUN: | FileCheck %s
+// CHECK: error: '-fdynamic-debugging' unsupported with instrumentation (PGO/code coverage)
+
+int b() { return 0; }
diff --git a/clang/test/DebugInfo/DynamicDebugging/symbols-functions.cpp b/clang/test/DebugInfo/DynamicDebugging/symbols-functions.cpp
new file mode 100644
index 0000000000000..4edb887238da3
--- /dev/null
+++ b/clang/test/DebugInfo/DynamicDebugging/symbols-functions.cpp
@@ -0,0 +1,33 @@
+// RUN: %clang -cc1 -triple x86_64-unknown-unknown %s -debug-info-kind=limited -fdynamic-debugging -o %t \
+// RUN: -emit-llvm --save-dynamic-debugging-temps --discard-dynamic-debugging-debug-module
+// RUN: FileCheck %s --check-prefix=INNER < %t.dyndbg.1.inner.ll
+// RUN: FileCheck %s --check-prefix=OUTER < %t.dyndbg.2.outer.ll
+
+/// Test functions get expected linkage and names in the dyndbg inner
+/// and outer modules. Each outer (to-be-optimized) function should have a
+/// corresponding inner (unoptimized) version.
+///
+/// The internal functions get external aliases in outer.
+
+// OUTER: $_Z7odrweakv = comdat any
+// INNER: $__dyndbg._Z7odrweakv = comdat any
+
+/// Outer: as input. Inner: external reference, __dyndbg copy.
+void external() {}
+// OUTER-DAG: define dso_local void @_Z8externalv()
+// INNER-DAG: declare dso_local void @_Z8externalv()
+// INNER-DAG: define dso_local void @__dyndbg._Z8externalv()
+
+/// Outer: add external linkage alias. Inner: external reference to alias,
+/// __dyndbg copy.
+[[gnu::used]] static void internal() {}
+// OUTER-DAG: @_ZL8internalv.dyndbg.[[hash:[0-9A-F]+]] = hidden alias void (), ptr @_ZL8internalv
+// OUTER-DAG: define internal void @_ZL8internalv()
+// INNER-DAG: declare hidden void @_ZL8internalv.dyndbg.[[hash:[0-9A-F]+]]()
+// INNER-DAG: define hidden void @__dyndbg._ZL8internalv.dyndbg.[[hash]]
+
+/// Outer: as input. Inner: external reference, __dyndbg copy.
+[[gnu::used]] inline void odrweak() {}
+// OUTER-DAG: define linkonce_odr void @_Z7odrweakv()
+// INNER-DAG: declare void @_Z7odrweakv()
+// INNER-DAG: define linkonce_odr void @__dyndbg._Z7odrweakv()
diff --git a/clang/test/DebugInfo/DynamicDebugging/symbols-globals.cpp b/clang/test/DebugInfo/DynamicDebugging/symbols-globals.cpp
new file mode 100644
index 0000000000000..94b431050e615
--- /dev/null
+++ b/clang/test/DebugInfo/DynamicDebugging/symbols-globals.cpp
@@ -0,0 +1,72 @@
+// RUN: %clang -cc1 -triple x86_64-unknown-unknown %s -debug-info-kind=limited -fdynamic-debugging -o %t \
+// RUN: -emit-llvm --save-dynamic-debugging-temps --discard-dynamic-debugging-debug-module
+// RUN: FileCheck %s --check-prefix=INNER < %t.dyndbg.1.inner.ll
+// RUN: FileCheck %s --check-prefix=OUTER < %t.dyndbg.2.outer.ll
+
+// Test global variables get expected linkage and names in the dyndbg inner
+// and outer modules. Global data is stored in the outer module and referenced
+// from the inner module.
+//
+// The external global variables are simply referenced by inner.
+//
+// The internal (static) global variables get external aliases in outer
+// and those are referenced from inner.
+//
+// The internal functions _ZTW1d (thread-local wrapper routine for d) and
+// _GLOBAL__sub_I_symbols_globals, __cxx_global_var_init are promoted too (get
+// global aliases).
+
+int a = 1;
+// OUTER-DAG: @a = global i32 1, align 4
+// INNER-DAG: @a = external global i32, align 4
+
+extern int b;
+// OUTER-DAG: @b = external global i32, align 4
+// INNER-DAG: @b = external global i32, align 4
+
+inline int c = 2;
+// OUTER-DAG: $c = comdat any
+// OUTER-DAG: @c = linkonce_odr global i32 2, comdat, align 4
+// INNER-DAG: @c = external global i32, align 4
+
+thread_local int d = 3;
+// OUTER-DAG: @d = thread_local global i32 3, align 4
+// INNER-DAG: @d = external thread_local global i32, align 4
+//
+// OUTER-DAG: $_ZTW1d = comdat any
+// OUTER-DAG: define weak_odr hidden noundef ptr @_ZTW1d() #[[#]] comdat
+// INNER-DAG: $__dyndbg._ZTW1d = comdat any
+// INNER-DAG: declare hidden noundef ptr @_ZTW1d()
+// INNER-DAG: define weak_odr hidden noundef ptr @__dyndbg._ZTW1d() #[[#]] comdat
+
+struct S { int a, b; float c, d; } e {0, 100, 4.f, 5.f};
+// OUTER-DAG: @e = global %struct.S { i32 0, i32 100, float 4.000000e+00, float 5.000000e+00 }, align 4
+// INNER-DAG: @e = external global %struct.S, align 4
+
+inline S f {0, 100, 4.f, 5.f};
+// OUTER-DAG: $f = comdat any
+// OUTER-DAG: @f = linkonce_odr global %struct.S { i32 0, i32 100, float 4.000000e+00, float 5.000000e+00 }, comdat, align 4
+// INNER-DAG: @f = external global %struct.S, align 4
+
+int fun() {
+ static int g = 0;
+ return g;
+}
+// OUTER-DAG: @_ZZ3funvE1g = internal global i32 0, align 4
+// OUTER-DAG: @_ZZ3funvE1g.dyndbg.[[hash:[0-9A-F]+]] = hidden alias i32, ptr @_ZZ3funvE1g
+// INNER-DAG: @_ZZ3funvE1g.dyndbg.[[hash:[0-9A-F]+]] = external hidden global i32, align 4
+
+static int h = 1;
+// OUTER-DAG: @_ZL1h = internal global i32 1, align 4
+// OUTER-DAG: @_ZL1h.dyndbg.[[hash]] = hidden alias i32, ptr @_ZL1h
+// INNER-DAG: @_ZL1h.dyndbg.[[hash]] = external hidden global i32, align 4
+
+__attribute__((nodebug)) int use = a + b + c + d + e.a + f.a + h;
+
+// OUTER-DAG: define internal void @__cxx_global_var_init()
+// OUTER-DAG: @__cxx_global_var_init.dyndbg.[[hash]] = hidden alias void (), ptr @__cxx_global_var_init
+// INNER-DAG: declare hidden void @__cxx_global_var_init.dyndbg.[[hash]]()
+
+// OUTER-DAG: define internal void @_GLOBAL__sub_I_symbols_globals.cpp()
+// OUTER-DAG: @_GLOBAL__sub_I_symbols_globals.cpp.dyndbg.[[hash]] = hidden alias void (), ptr @_GLOBAL__sub_I_symbols_globals.cpp
+// INNER-DAG: define hidden void @__dyndbg._GLOBAL__sub_I_symbols_globals.cpp.dyndbg.[[hash]]() #[[#]]
diff --git a/clang/test/DebugInfo/DynamicDebugging/symbols-internal-comdat.cpp b/clang/test/DebugInfo/DynamicDebugging/symbols-internal-comdat.cpp
new file mode 100644
index 0000000000000..8e30329d35258
--- /dev/null
+++ b/clang/test/DebugInfo/DynamicDebugging/symbols-internal-comdat.cpp
@@ -0,0 +1,27 @@
+// RUN: %clang -cc1 -triple x86_64-unknown-unknown %s -debug-info-kind=limited -fdynamic-debugging -o %t \
+// RUN: -emit-llvm --save-dynamic-debugging-temps --discard-dynamic-debugging-debug-module
+// RUN: FileCheck %s --check-prefix=INNER < %t.dyndbg.1.inner.ll --implicit-check-not=@__dyndbg._ZN1X5weirdE
+// RUN: FileCheck %s --check-prefix=OUTER < %t.dyndbg.2.outer.ll
+
+/// Check that internal symbols in comdats, __cxx_global_var_init in this case,
+/// are not promoted (no external alias is produced).
+///
+/// --implicit-check-not=@__dyndbg._ZN1X5weirdE:
+/// The innner cxx_global_var_init gets a comdat, '$__dyndbg._ZN1X5weirdE', but
+/// unlike the one in the outer module, this one has no associated global data.
+
+// OUTER: @_ZN1X5weirdE = linkonce_odr global %struct.X zeroinitializer, comdat, align 4
+// INNER: @_ZN1X5weirdE = external global %struct.X, align 4
+
+// OUTER: define internal void @__cxx_global_var_init() {{.*}} comdat($_ZN1X5weirdE)
+// INNER: define internal void @__dyndbg.__cxx_global_var_init() {{.*}} comdat($__dyndbg._ZN1X5weirdE)
+// INNER: declare dso_local void @__cxx_global_var_init()
+
+struct X {
+public:
+ X(int a) : a(a) {}
+ int a;
+ static const X weird;
+};
+
+inline const X X::weird = X(5);
diff --git a/clang/test/Driver/dynamic-debugging-flags.c b/clang/test/Driver/dynamic-debugging-flags.c
new file mode 100644
index 0000000000000..dedc9509eeb3d
--- /dev/null
+++ b/clang/test/Driver/dynamic-debugging-flags.c
@@ -0,0 +1,32 @@
+// Only support x86_64 targets initially.
+// RUN: not %clang -c -target aarch64-unknown-unknown -g -fdynamic-debugging -### -o /dev/null %s 2>&1 | FileCheck %s -check-prefix=CHECK-TARGET-ERR
+// RUN: %clang -c -target x86_64-unknown-unknown -g -fdynamic-debugging -### -o /dev/null %s 2>&1 | FileCheck %s -check-prefix=CHECK-OK
+// CHECK-TARGET-ERR: error: unsupported option '-fdynamic-debugging' for target 'aarch64-unknown-unknown'
+
+// Do not support LTO initially.
+// RUN: not %clang -c -target x86_64-unknown-unknown -g -fdynamic-debugging -flto -### -o /dev/null %s 2>&1 | FileCheck %s -check-prefix=CHECK-LTO-ERR
+// RUN: not %clang -c -target x86_64-unknown-unknown -g -fdynamic-debugging -flto=full -### -o /dev/null %s 2>&1 | FileCheck %s -check-prefix=CHECK-LTO-ERR
+// RUN: not %clang -c -target x86_64-unknown-unknown -g -fdynamic-debugging -flto=thin -### -o /dev/null %s 2>&1 | FileCheck %s -check-prefix=CHECK-LTO-ERR
+// RUN: %clang -c -target x86_64-unknown-unknown -g -fdynamic-debugging -flto -fno-lto -### -o /dev/null %s 2>&1 | FileCheck %s -check-prefix=CHECK-OK
+// CHECK-LTO-ERR: clang: error: '-fdynamic-debugging' incompatible with '-flto'
+
+// Do not support split dwarf.
+// RUN: not %clang -c -target x86_64-unknown-unknown -fdynamic-debugging -g -gsplit-dwarf -### -o /dev/null %s 2>&1 | FileCheck %s -check-prefix=CHECK-DWO-ERR
+// RUN: %clang -c -target x86_64-unknown-unknown -fdynamic-debugging -g -gsplit-dwarf -gno-split-dwarf -### -o /dev/null %s 2>&1 | FileCheck %s -check-prefix=CHECK-OK
+// CHECK-DWO-ERR: clang: error: '-fdynamic-debugging' incompatible with '-gsplit-dwarf'
+
+// Do not support llvm IR input.
+// RUN: not %clang -c -target x86_64-unknown-unknown -fdynamic-debugging -gsplit-dwarf -### -o /dev/null -x ir %s 2>&1 | FileCheck %s -check-prefix=CHECK-LL-ERR
+// CHECK-LL-ERR: clang: error: '-fdynamic-debugging' incompatible with IR input
+
+// Warning - requires debug info.
+// RUN: %clang -c -target x86_64-unknown-unknown -fdynamic-debugging -gsplit-dwarf -### -o /dev/null %s 2>&1 | FileCheck %s -check-prefix=CHECK-DBG-WARN
+// CHECK-DBG-WARN: clang: warning: '-fdynamic-debugging' ignored: requires debug info
+// CHECK-DBG-WARN-NOT: -fdynamic-debugging
+
+// Do not support sanitizers initially.
+// RUN: not %clang -fsanitize=undefined -c -target x86_64-unknown-unknown -fdynamic-debugging -### -o /dev/null -x ir %s 2>&1 | FileCheck %s -check-prefix=CHECK-SAN-ERR
+// CHECK-SAN-ERR: clang: error: '-fdynamic-debugging' incompatible with '-fsanitize=undefined'
+
+// CHECK-OK-NOT: error:
+// CHECK-OK-NOT: warning:
diff --git a/llvm/include/llvm/Transforms/Utils/ModuleUtils.h b/llvm/include/llvm/Transforms/Utils/ModuleUtils.h
index 4036c4e947c75..baafc078039ec 100644
--- a/llvm/include/llvm/Transforms/Utils/ModuleUtils.h
+++ b/llvm/include/llvm/Transforms/Utils/ModuleUtils.h
@@ -138,10 +138,14 @@ LLVM_ABI std::string getUniqueModuleId(Module *M);
/// Embed the memory buffer \p Buf into the module \p M as a global using the
/// specified section name. Also provide a metadata entry to identify it in the
-/// module using the same section name.
-LLVM_ABI void embedBufferInModule(Module &M, MemoryBufferRef Buf,
- StringRef SectionName,
- Align Alignment = Align(1));
+/// module using the same section name. If \p SectionExclude is true !exclude
+/// is applied to the global in order to apply necessary linkage flags to
+/// exclude the section from a link. If false, apply !metadata_section_kind
+/// which results in no additional section linkage flags.
+LLVM_ABI GlobalVariable *embedBufferInModule(Module &M, MemoryBufferRef Buf,
+ StringRef SectionName,
+ Align Alignment = Align(1),
+ bool SectionExclude = true);
/// Lower all calls to ifuncs by replacing uses with indirect calls loaded out
/// of a global table initialized in a global constructor. This will introduce
diff --git a/llvm/lib/Transforms/Utils/ModuleUtils.cpp b/llvm/lib/Transforms/Utils/ModuleUtils.cpp
index 2976ebf46c9b7..71e9c70cbf9a5 100644
--- a/llvm/lib/Transforms/Utils/ModuleUtils.cpp
+++ b/llvm/lib/Transforms/Utils/ModuleUtils.cpp
@@ -381,8 +381,10 @@ std::string llvm::getUniqueModuleId(Module *M) {
return ("." + Str).str();
}
-void llvm::embedBufferInModule(Module &M, MemoryBufferRef Buf,
- StringRef SectionName, Align Alignment) {
+GlobalVariable *llvm::embedBufferInModule(Module &M, MemoryBufferRef Buf,
+ StringRef SectionName,
+ Align Alignment,
+ bool SectionExclude) {
// Embed the memory buffer into the module.
Constant *ModuleConstant = ConstantDataArray::get(
M.getContext(), ArrayRef(Buf.getBufferStart(), Buf.getBufferSize()));
@@ -396,11 +398,16 @@ void llvm::embedBufferInModule(Module &M, MemoryBufferRef Buf,
NamedMDNode *MD = M.getOrInsertNamedMetadata("llvm.embedded.objects");
Metadata *MDVals[] = {ConstantAsMetadata::get(GV),
MDString::get(Ctx, SectionName)};
-
MD->addOperand(llvm::MDNode::get(Ctx, MDVals));
- GV->setMetadata(LLVMContext::MD_exclude, llvm::MDNode::get(Ctx, {}));
+
+ if (SectionExclude)
+ GV->setMetadata(LLVMContext::MD_exclude, llvm::MDNode::get(Ctx, {}));
+ else
+ GV->setMetadata(LLVMContext::MD_metadata_section_kind,
+ llvm::MDNode::get(Ctx, {}));
appendToCompilerUsed(M, GV);
+ return GV;
}
bool llvm::lowerGlobalIFuncUsersAsGlobalCtor(
More information about the llvm-commits
mailing list