[llvm] [dyndbg][LLVM] Implement dynamic debugging support (PR #194854)
Stephen Tozer via llvm-commits
llvm-commits at lists.llvm.org
Mon Aug 10 05:32:02 PDT 2026
================
@@ -0,0 +1,569 @@
+//===- DynamicDebuggingTest.cpp - Unit tests for dynamic debugging utils -===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Transforms/Utils/DynamicDebugging.h"
+#include "llvm/AsmParser/Parser.h"
+#include "llvm/IR/Attributes.h"
+#include "llvm/IR/GlobalVariable.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/Verifier.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Transforms/IPO/Attributor.h"
+#include "llvm/Transforms/Utils/Cloning.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+
+namespace {
+
+TEST(DynamicDebugging, UnoptimizedModuleAttributes) {
+ // Test that `prepareForDynamicDebugging` removes alwaysinline and minsize
+ // attributes from cloned functions in the unoptimized module, adding
+ // noinline and optnone.
+ StringRef IR = R"(
+ define dso_local void @f() #0 !dbg !4 {
+ ret void
+ }
+
+ attributes #0 = { alwaysinline minsize }
+
+ !llvm.dbg.cu = !{!0}
+ !llvm.module.flags = !{!2, !3}
+
+ !0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang version 23.0.0git", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+ !1 = !DIFile(filename: "test.c", directory: "/")
+ !2 = !{i32 7, !"Dwarf Version", i32 5}
+ !3 = !{i32 2, !"Debug Info Version", i32 3}
+ !4 = distinct !DISubprogram(name: "f", scope: !5, file: !5, line: 1, type: !6, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0)
+ !5 = !DIFile(filename: "test.c", directory: "/")
+ !6 = !DISubroutineType(types: !7)
+ !7 = !{null}
+ )";
+
+ LLVMContext Context;
+ SMDiagnostic Error;
+
+ std::unique_ptr<Module> OptModule = parseAssemblyString(IR, Error, Context);
+ ASSERT_TRUE(OptModule != nullptr) << "Failed to parse IR\n";
+ ASSERT_TRUE(OptModule->getFunction("f"));
+ ASSERT_TRUE(
+ OptModule->getFunction("f")->hasFnAttribute(Attribute::AlwaysInline));
+ ASSERT_TRUE(OptModule->getFunction("f")->hasFnAttribute(Attribute::MinSize));
+
+ std::unique_ptr<Module> UnoptModule =
+ prepareForDynamicDebugging(OptModule.get(), "unused");
+
+ // Check that optnone and noinline are added to the unoptimized function
+ // clone and that the clashing attributes minsize and alwaysinline are
+ // removed.
+ Function *F = UnoptModule->getFunction("__dyndbg.f");
+ ASSERT_TRUE(F && !F->isDeclaration());
+ EXPECT_FALSE(F->hasFnAttribute(Attribute::MinSize));
+ EXPECT_FALSE(F->hasFnAttribute(Attribute::AlwaysInline));
+ EXPECT_TRUE(F->hasFnAttribute(Attribute::OptimizeNone));
+ EXPECT_TRUE(F->hasFnAttribute(Attribute::NoInline));
+
+ // Check the declarations (references to OptModule's functions) are unchanged.
+ F = UnoptModule->getFunction("f");
+ ASSERT_TRUE(F && F->isDeclaration());
+ EXPECT_TRUE(F->hasFnAttribute(Attribute::MinSize));
+ EXPECT_TRUE(F->hasFnAttribute(Attribute::AlwaysInline));
+ EXPECT_FALSE(F->hasFnAttribute(Attribute::OptimizeNone));
+ EXPECT_FALSE(F->hasFnAttribute(Attribute::NoInline));
+
+ // The originl function in OptModule should be unchanged.
+ F = OptModule->getFunction("f");
+ ASSERT_TRUE(F && !F->isDeclaration());
+ EXPECT_TRUE(F->hasFnAttribute(Attribute::MinSize));
+ EXPECT_TRUE(F->hasFnAttribute(Attribute::AlwaysInline));
+ EXPECT_FALSE(F->hasFnAttribute(Attribute::OptimizeNone));
+ EXPECT_FALSE(F->hasFnAttribute(Attribute::NoInline));
+}
+
+TEST(DynamicDebugging, OptimizedModuleAttributes) {
+ // Test that `prepareForDynamicDebugging` adds necessary attributes to
+ // functions in the (to be) optimized module.
+ // Note: The test requires a triple as support for targets other than x86_64
+ // hasn't been added yet for the tail padding attributes.
+ StringRef IR = R"(
+ target triple = "x86_64-unknown-linux"
+
+ define dso_local void @f() !dbg !4 {
+ ret void
+ }
+
+ !llvm.dbg.cu = !{!0}
+ !llvm.module.flags = !{!2, !3}
+
+ !0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang version 23.0.0git", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+ !1 = !DIFile(filename: "test.c", directory: "/")
+ !2 = !{i32 7, !"Dwarf Version", i32 5}
+ !3 = !{i32 2, !"Debug Info Version", i32 3}
+ !4 = distinct !DISubprogram(name: "f", scope: !5, file: !5, line: 1, type: !6, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0)
+ !5 = !DIFile(filename: "test.c", directory: "/")
+ !6 = !DISubroutineType(types: !7)
+ !7 = !{null}
+ )";
+
+ LLVMContext Context;
+ SMDiagnostic Error;
+
+ std::unique_ptr<Module> M = parseAssemblyString(IR, Error, Context);
+ ASSERT_TRUE(M != nullptr) << "Failed to parse IR\n";
+
+ // We're only interested in M (drop returned unoptimzed module).
+ prepareForDynamicDebugging(M.get(), "unused");
+ Function *F = M->getFunction("f");
+ ASSERT_TRUE(F);
+
+ EXPECT_TRUE(F->hasFnAttribute(Attribute::NoOutline));
+ EXPECT_TRUE(F->hasFnAttribute(Attribute::NoIPA));
+ EXPECT_EQ(F->getFnAttributeAsParsedInteger("tail-pad-to-size"), 5u);
+ EXPECT_EQ(F->getFnAttributeAsParsedInteger("tail-pad-value"), 144u);
+}
+
+TEST(DynamicDebugging, FunctionLinkage) {
+ // Test functions get expected linkage and names in the unoptimized and
+ // optimized modules. Each optimized module function should have a
+ // corresponding unoptimized version, and ones with internal linkage should be
+ // given external aliases.
+ StringRef IR = R"(
+ ; Mark as used - unused internal or weak symbols aren't promoted.
+ @llvm.compiler.used = appending global [1 x ptr] [ptr @f], section "llvm.metadata"
+
+ define dso_local void @f() !dbg !4 {
+ ret void
+ }
+
+ !llvm.dbg.cu = !{!0}
+ !llvm.module.flags = !{!2, !3}
+
+ !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 23.0.0git", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+ !1 = !DIFile(filename: "test.cpp", directory: "/")
+ !2 = !{i32 7, !"Dwarf Version", i32 5}
+ !3 = !{i32 2, !"Debug Info Version", i32 3}
+ !4 = distinct !DISubprogram(name: "f", scope: !1, file: !1, line: 1, type: !5, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0)
+ !5 = !DISubroutineType(types: !6)
+ !6 = !{null}
+ )";
+
+ LLVMContext Context;
+ SMDiagnostic Error;
+
+ std::unique_ptr<Module> M = parseAssemblyString(IR, Error, Context);
+ ASSERT_TRUE(M != nullptr) << "Failed to parse IR\n";
+
+ // Input: define f
+ // Opt module: define f, alias f.promoted
+ // Unopt module: declare f.promoted, define __dyndbg.f.promoted
+ auto ExpectPromoted = [&M](GlobalValue::LinkageTypes Linkage,
+ GlobalValue::VisibilityTypes Visibility,
+ int Line) {
+#define FAIL_MSG "ExpectPromoted called from " << Line;
+ std::unique_ptr<Module> OptModule = CloneModule(*M);
+ Function *F = OptModule->getFunction("f");
+ ASSERT_TRUE(F && !F->isDeclaration()) << FAIL_MSG;
+ F->setLinkage(Linkage);
+ F->setVisibility(Visibility);
+
+ std::unique_ptr<Module> UnoptModule =
+ prepareForDynamicDebugging(OptModule.get(), ".promoted");
+
+ // Optimized module function linkage and visibility should not change.
+ EXPECT_EQ(F->getLinkage(), Linkage) << FAIL_MSG;
+ EXPECT_EQ(F->getVisibility(), Visibility) << FAIL_MSG;
+
+ // External alias added to optimized module.
+ GlobalAlias *A = OptModule->getNamedAlias("f.promoted");
+ ASSERT_TRUE(A) << FAIL_MSG;
+ EXPECT_EQ(A->getAliasee(), F) << FAIL_MSG;
+ EXPECT_EQ(A->getLinkage(), GlobalValue::ExternalLinkage) << FAIL_MSG;
+ EXPECT_EQ(A->getVisibility(), GlobalValue::HiddenVisibility) << FAIL_MSG;
+
+ // Unoptimized module's reference to the optimzed module's function.
+ F = UnoptModule->getFunction("f.promoted");
+ ASSERT_TRUE(F) << FAIL_MSG;
+ EXPECT_TRUE(F->isDeclaration()) << FAIL_MSG;
+ EXPECT_EQ(F->getLinkage(), GlobalValue::ExternalLinkage) << FAIL_MSG;
+ EXPECT_EQ(F->getVisibility(), GlobalValue::HiddenVisibility) << FAIL_MSG;
+
+ // Unoptimized module's copy of the function.
+ F = UnoptModule->getFunction("__dyndbg.f.promoted");
+ ASSERT_TRUE(F) << FAIL_MSG;
+ EXPECT_FALSE(F->isDeclaration()) << FAIL_MSG;
+ EXPECT_EQ(F->getLinkage(), GlobalValue::ExternalLinkage) << FAIL_MSG;
+ EXPECT_EQ(F->getVisibility(), GlobalValue::HiddenVisibility) << FAIL_MSG;
+#undef FAIL_MSG
+ };
+
+ // Input: define f
+ // Opt module: define f
+ // Unopt module: declare f, define __dyndbg.f
+ auto ExpectNotPromoted = [&M](GlobalValue::LinkageTypes Linkage,
+ GlobalValue::VisibilityTypes Visibility,
+ int Line) {
+#define FAIL_MSG "ExpectNotPromoted called from " << Line;
+ std::unique_ptr<Module> OptModule = CloneModule(*M);
+ Function *F = OptModule->getFunction("f");
+ ASSERT_TRUE(F && !F->isDeclaration()) << FAIL_MSG;
+ F->setLinkage(Linkage);
+ F->setVisibility(Visibility);
+
+ std::unique_ptr<Module> UnoptModule =
+ prepareForDynamicDebugging(OptModule.get(), ".promoted");
+
+ // Optimized module function linkage and visibility should not change.
+ EXPECT_EQ(F->getLinkage(), Linkage) << FAIL_MSG;
+ EXPECT_EQ(F->getVisibility(), Visibility) << FAIL_MSG;
+
+ // External alias not added to optimized module.
+ EXPECT_EQ(OptModule->alias_size(), 0u) << FAIL_MSG;
+
+ // Unoptimized module's reference to the optimzed module's function.
+ F = UnoptModule->getFunction("f");
+ ASSERT_TRUE(F) << FAIL_MSG;
+ EXPECT_TRUE(F->isDeclaration()) << FAIL_MSG;
+ EXPECT_EQ(F->getLinkage(), GlobalValue::ExternalLinkage) << FAIL_MSG;
+ EXPECT_EQ(F->getVisibility(), Visibility) << FAIL_MSG;
+
+ // Unoptimized module's copy of the function.
+ F = UnoptModule->getFunction("__dyndbg.f");
+ ASSERT_TRUE(F) << FAIL_MSG;
+ EXPECT_FALSE(F->isDeclaration()) << FAIL_MSG;
+ EXPECT_EQ(F->getLinkage(), Linkage) << FAIL_MSG;
+ EXPECT_EQ(F->getVisibility(), Visibility) << FAIL_MSG;
+#undef FAIL_MSG
+ };
+
+ ExpectPromoted(GlobalValue::InternalLinkage, GlobalValue::DefaultVisibility,
+ __LINE__);
+ ExpectPromoted(GlobalValue::PrivateLinkage, GlobalValue::DefaultVisibility,
+ __LINE__);
+
+ ExpectNotPromoted(GlobalValue::ExternalLinkage,
+ GlobalValue::DefaultVisibility, __LINE__);
+ ExpectNotPromoted(GlobalValue::ExternalLinkage, GlobalValue::HiddenVisibility,
+ __LINE__);
+ ExpectNotPromoted(GlobalValue::ExternalLinkage,
+ GlobalValue::ProtectedVisibility, __LINE__);
+
+ ExpectNotPromoted(GlobalValue::AvailableExternallyLinkage,
+ GlobalValue::DefaultVisibility, __LINE__);
+ ExpectNotPromoted(GlobalValue::AvailableExternallyLinkage,
+ GlobalValue::HiddenVisibility, __LINE__);
+ ExpectNotPromoted(GlobalValue::AvailableExternallyLinkage,
+ GlobalValue::ProtectedVisibility, __LINE__);
+
+ ExpectNotPromoted(GlobalValue::LinkOnceAnyLinkage,
+ GlobalValue::DefaultVisibility, __LINE__);
+ ExpectNotPromoted(GlobalValue::LinkOnceAnyLinkage,
+ GlobalValue::HiddenVisibility, __LINE__);
+ ExpectNotPromoted(GlobalValue::LinkOnceAnyLinkage,
+ GlobalValue::ProtectedVisibility, __LINE__);
+
+ ExpectNotPromoted(GlobalValue::WeakAnyLinkage, GlobalValue::DefaultVisibility,
+ __LINE__);
+ ExpectNotPromoted(GlobalValue::WeakAnyLinkage, GlobalValue::HiddenVisibility,
+ __LINE__);
+ ExpectNotPromoted(GlobalValue::WeakAnyLinkage,
+ GlobalValue::ProtectedVisibility, __LINE__);
+
+ ExpectNotPromoted(GlobalValue::WeakODRLinkage, GlobalValue::DefaultVisibility,
+ __LINE__);
+ ExpectNotPromoted(GlobalValue::WeakODRLinkage, GlobalValue::HiddenVisibility,
+ __LINE__);
+ ExpectNotPromoted(GlobalValue::WeakODRLinkage,
+ GlobalValue::ProtectedVisibility, __LINE__);
+
+ ExpectNotPromoted(GlobalValue::ExternalWeakLinkage,
+ GlobalValue::DefaultVisibility, __LINE__);
+ ExpectNotPromoted(GlobalValue::ExternalWeakLinkage,
+ GlobalValue::HiddenVisibility, __LINE__);
+ ExpectNotPromoted(GlobalValue::ExternalWeakLinkage,
+ GlobalValue::ProtectedVisibility, __LINE__);
+
+ // Add a comadt group.
----------------
SLTozer wrote:
```suggestion
// Add a comdat group.
```
https://github.com/llvm/llvm-project/pull/194854
More information about the llvm-commits
mailing list