Author: Orlando Cazalet-Hyams Date: 2026-08-14T10:20:20+01:00 New Revision: c024bb531b2158fe13c9678c78e134710ec56510
URL: https://github.com/llvm/llvm-project/commit/c024bb531b2158fe13c9678c78e134710ec56510 DIFF: https://github.com/llvm/llvm-project/commit/c024bb531b2158fe13c9678c78e134710ec56510.diff LOG: [dyndbg][Clang] Implement nested-ELF dynamic debugging support (#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 6e9bde32e0655..82c2cb942c5ed 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 -fcuda-include-gpubinary option to forward to /// CUDA runtime back-end for incorporating them into host-side object file. std::string CudaGpuBinaryFileName; 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 ba820778b80c0..c147e0f58b246 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -2021,3 +2021,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 6af0f578b6d63..c78484933b7e3 100644 --- a/clang/include/clang/Options/Options.td +++ b/clang/include/clang/Options/Options.td @@ -5368,6 +5368,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..866e25f7b180c 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; + // Disable dyndbg if the target isn't available as we're compiling to the + // inner module to object regardless of other options. (This may change). + if (EnableDynamicDebugging) { + std::string Error; + const llvm::Target *TheTarget = + TargetRegistry::lookupTarget(M->getTargetTriple(), Error); + if (!TheTarget) { + 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 19d37b2ed7017..d2ddea2551418 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..4b6c339504d62 --- /dev/null +++ b/clang/test/DebugInfo/DynamicDebugging/attr-outer-tail-pad.c @@ -0,0 +1,10 @@ +// 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..a896ea57a8b26 --- /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 %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( _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
