Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[clang][DebugInfo] Attach DW_AT_const_value to static data-member definitions if available #72730

Open
wants to merge 8 commits into
base: main
Choose a base branch
from

Conversation

Michael137
Copy link
Member

@Michael137 Michael137 commented Nov 18, 2023

In #71780 we started emitting definitions for all static data-members with constant initialisers, even if they were constants (i.e., didn't have a location). We also dropped the DW_AT_const_value from the declaration to help resolve inconsistencies during type merging in the DWARFParallelLinker. However, for static data members that do have locations, we wouldn't emit a DW_AT_const_value on it, assuming that the consumer knows how to read the value using the location. This broke some consumers that really wanted to find a DW_AT_const_value.

This patch makes sure we also attach the DW_AT_const_value to definitions that have a location.

Test changes

  • debug-info-static-member.cpp:

    • We added the check for const_b as part of the
      patch series in 638a8393615e911b729d5662096f60ef49f1c65e.
      The check for isUsableAsConstantExpression added in the current patch
      doesn't support constant inline floats (since they are neither constexpr nor
      integrals). This isn't a regression since before said patch series
      we wouldn't ever emit the definition for const_b anyway. Now
      we just don't do it for inline const floats. This is consistent with
      GCC's behaviour starting with C++11.
  • debug-info-static-inline-member:

    • This was just a bug which is now fixed. We shouldn't emit
      a DW_AT_const_value for a non-const static.

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:codegen debuginfo labels Nov 18, 2023
@llvmbot
Copy link
Member

llvmbot commented Nov 18, 2023

@llvm/pr-subscribers-clang

@llvm/pr-subscribers-debuginfo

Author: Michael Buch (Michael137)

Changes

In #71780 we started emitting definitions for all static data-members with constant initialisers, even if they were constants (i.e., didn't have a location). We also dropped the DW_AT_const_value from the declaration to help resolve inconsistencies during type merging in the DWARFParallelLinker. However, for static data members that do have locations, we wouldn't emit a DW_AT_const_value on it, assuming that the consumer knows how to read the value using the location. This broke some consumers that really wanted to find a DW_AT_const_value.

This patch makes sure we also attach the DW_AT_const_value to definitions that have a location.


Full diff: https://github.com/llvm/llvm-project/pull/72730.diff

3 Files Affected:

  • (modified) clang/lib/CodeGen/CGDebugInfo.cpp (+22-6)
  • (modified) clang/test/CodeGenCXX/debug-info-static-inline-member.cpp (+1-1)
  • (modified) clang/test/CodeGenCXX/inline-dllexport-member.cpp (+1-1)
diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index 0b52d99ad07f164..e01c57baef19931 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -69,6 +69,19 @@ static uint32_t getDeclAlignIfRequired(const Decl *D, const ASTContext &Ctx) {
   return D->hasAttr<AlignedAttr>() ? D->getMaxAlignment() : 0;
 }
 
+APValue const *evaluateConstantInitializer(clang::VarDecl const *VD) {
+  assert(VD != nullptr);
+
+  VD = VD->getCanonicalDecl();
+  if (!VD)
+    return nullptr;
+
+  if (!VD->hasConstantInitialization() || !VD->hasInit())
+    return nullptr;
+
+  return VD->evaluateValue();
+}
+
 CGDebugInfo::CGDebugInfo(CodeGenModule &CGM)
     : CGM(CGM), DebugKind(CGM.getCodeGenOpts().getDebugInfo()),
       DebugTypeExtRefs(CGM.getCodeGenOpts().DebugTypeExtRefs),
@@ -5503,11 +5516,17 @@ void CGDebugInfo::EmitGlobalVariable(llvm::GlobalVariable *Var,
     }
     AppendAddressSpaceXDeref(AddressSpace, Expr);
 
+    llvm::DIExpression *E = nullptr;
+    if (Expr.empty()) {
+      if (auto const *InitVal = evaluateConstantInitializer(D))
+        E = createConstantValueExpression(D, *InitVal);
+    } else
+      E = DBuilder.createExpression(Expr);
+
     llvm::DINodeArray Annotations = CollectBTFDeclTagAnnotations(D);
     GVE = DBuilder.createGlobalVariableExpression(
         DContext, DeclName, LinkageName, Unit, LineNo, getOrCreateType(T, Unit),
-        Var->hasLocalLinkage(), true,
-        Expr.empty() ? nullptr : DBuilder.createExpression(Expr),
+        Var->hasLocalLinkage(), true, E,
         getOrCreateStaticDataMemberDeclarationOrNull(D), TemplateParameters,
         Align, Annotations);
     Var->addDebugInfo(GVE);
@@ -5596,14 +5615,11 @@ void CGDebugInfo::EmitGlobalVariable(const VarDecl *VD) {
   if (VD->hasAttr<NoDebugAttr>())
     return;
 
-  if (!VD->hasInit())
-    return;
-
   const auto CacheIt = DeclCache.find(VD);
   if (CacheIt != DeclCache.end())
     return;
 
-  auto const *InitVal = VD->evaluateValue();
+  auto const *InitVal = evaluateConstantInitializer(VD);
   if (!InitVal)
     return;
 
diff --git a/clang/test/CodeGenCXX/debug-info-static-inline-member.cpp b/clang/test/CodeGenCXX/debug-info-static-inline-member.cpp
index f2d4d9408a8297a..950ea9b302b290c 100644
--- a/clang/test/CodeGenCXX/debug-info-static-inline-member.cpp
+++ b/clang/test/CodeGenCXX/debug-info-static-inline-member.cpp
@@ -43,7 +43,7 @@ int main() {
 // CHECK:      @{{.*}}cexpr_struct_with_addr{{.*}} = 
 // CHECK-SAME    !dbg ![[EMPTY_GLOBAL:[0-9]+]]
 
-// CHECK:      !DIGlobalVariableExpression(var: ![[INT_VAR:[0-9]+]], expr: !DIExpression())
+// CHECK:      !DIGlobalVariableExpression(var: ![[INT_VAR:[0-9]+]], expr: !DIExpression(DW_OP_constu, 25, DW_OP_stack_value))
 // CHECK:      ![[INT_VAR]] = distinct !DIGlobalVariable(name: "cexpr_int_with_addr", linkageName:
 // CHECK-SAME:                isLocal: false, isDefinition: true, declaration: ![[INT_DECL:[0-9]+]])
 
diff --git a/clang/test/CodeGenCXX/inline-dllexport-member.cpp b/clang/test/CodeGenCXX/inline-dllexport-member.cpp
index d6b004d66dc6cbd..6bc01599c466780 100644
--- a/clang/test/CodeGenCXX/inline-dllexport-member.cpp
+++ b/clang/test/CodeGenCXX/inline-dllexport-member.cpp
@@ -7,7 +7,7 @@ struct __declspec(dllexport) s {
   static const unsigned int ui = 0;
 };
 
-// CHECK: [[UI]] = !DIGlobalVariableExpression(var: [[UIV:.*]], expr: !DIExpression())
+// CHECK: [[UI]] = !DIGlobalVariableExpression(var: [[UIV:.*]], expr: !DIExpression(DW_OP_constu, 0, DW_OP_stack_value))
 // CHECK: [[UIV]] = distinct !DIGlobalVariable(name: "ui", linkageName: "?ui@s@@2IB", scope: ![[SCOPE:[0-9]+]],
 // CHECK: ![[SCOPE]] = distinct !DICompileUnit(
 

@llvmbot
Copy link
Member

llvmbot commented Nov 18, 2023

@llvm/pr-subscribers-clang-codegen

Author: Michael Buch (Michael137)

Changes

In #71780 we started emitting definitions for all static data-members with constant initialisers, even if they were constants (i.e., didn't have a location). We also dropped the DW_AT_const_value from the declaration to help resolve inconsistencies during type merging in the DWARFParallelLinker. However, for static data members that do have locations, we wouldn't emit a DW_AT_const_value on it, assuming that the consumer knows how to read the value using the location. This broke some consumers that really wanted to find a DW_AT_const_value.

This patch makes sure we also attach the DW_AT_const_value to definitions that have a location.


Full diff: https://github.com/llvm/llvm-project/pull/72730.diff

3 Files Affected:

  • (modified) clang/lib/CodeGen/CGDebugInfo.cpp (+22-6)
  • (modified) clang/test/CodeGenCXX/debug-info-static-inline-member.cpp (+1-1)
  • (modified) clang/test/CodeGenCXX/inline-dllexport-member.cpp (+1-1)
diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index 0b52d99ad07f164..e01c57baef19931 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -69,6 +69,19 @@ static uint32_t getDeclAlignIfRequired(const Decl *D, const ASTContext &Ctx) {
   return D->hasAttr<AlignedAttr>() ? D->getMaxAlignment() : 0;
 }
 
+APValue const *evaluateConstantInitializer(clang::VarDecl const *VD) {
+  assert(VD != nullptr);
+
+  VD = VD->getCanonicalDecl();
+  if (!VD)
+    return nullptr;
+
+  if (!VD->hasConstantInitialization() || !VD->hasInit())
+    return nullptr;
+
+  return VD->evaluateValue();
+}
+
 CGDebugInfo::CGDebugInfo(CodeGenModule &CGM)
     : CGM(CGM), DebugKind(CGM.getCodeGenOpts().getDebugInfo()),
       DebugTypeExtRefs(CGM.getCodeGenOpts().DebugTypeExtRefs),
@@ -5503,11 +5516,17 @@ void CGDebugInfo::EmitGlobalVariable(llvm::GlobalVariable *Var,
     }
     AppendAddressSpaceXDeref(AddressSpace, Expr);
 
+    llvm::DIExpression *E = nullptr;
+    if (Expr.empty()) {
+      if (auto const *InitVal = evaluateConstantInitializer(D))
+        E = createConstantValueExpression(D, *InitVal);
+    } else
+      E = DBuilder.createExpression(Expr);
+
     llvm::DINodeArray Annotations = CollectBTFDeclTagAnnotations(D);
     GVE = DBuilder.createGlobalVariableExpression(
         DContext, DeclName, LinkageName, Unit, LineNo, getOrCreateType(T, Unit),
-        Var->hasLocalLinkage(), true,
-        Expr.empty() ? nullptr : DBuilder.createExpression(Expr),
+        Var->hasLocalLinkage(), true, E,
         getOrCreateStaticDataMemberDeclarationOrNull(D), TemplateParameters,
         Align, Annotations);
     Var->addDebugInfo(GVE);
@@ -5596,14 +5615,11 @@ void CGDebugInfo::EmitGlobalVariable(const VarDecl *VD) {
   if (VD->hasAttr<NoDebugAttr>())
     return;
 
-  if (!VD->hasInit())
-    return;
-
   const auto CacheIt = DeclCache.find(VD);
   if (CacheIt != DeclCache.end())
     return;
 
-  auto const *InitVal = VD->evaluateValue();
+  auto const *InitVal = evaluateConstantInitializer(VD);
   if (!InitVal)
     return;
 
diff --git a/clang/test/CodeGenCXX/debug-info-static-inline-member.cpp b/clang/test/CodeGenCXX/debug-info-static-inline-member.cpp
index f2d4d9408a8297a..950ea9b302b290c 100644
--- a/clang/test/CodeGenCXX/debug-info-static-inline-member.cpp
+++ b/clang/test/CodeGenCXX/debug-info-static-inline-member.cpp
@@ -43,7 +43,7 @@ int main() {
 // CHECK:      @{{.*}}cexpr_struct_with_addr{{.*}} = 
 // CHECK-SAME    !dbg ![[EMPTY_GLOBAL:[0-9]+]]
 
-// CHECK:      !DIGlobalVariableExpression(var: ![[INT_VAR:[0-9]+]], expr: !DIExpression())
+// CHECK:      !DIGlobalVariableExpression(var: ![[INT_VAR:[0-9]+]], expr: !DIExpression(DW_OP_constu, 25, DW_OP_stack_value))
 // CHECK:      ![[INT_VAR]] = distinct !DIGlobalVariable(name: "cexpr_int_with_addr", linkageName:
 // CHECK-SAME:                isLocal: false, isDefinition: true, declaration: ![[INT_DECL:[0-9]+]])
 
diff --git a/clang/test/CodeGenCXX/inline-dllexport-member.cpp b/clang/test/CodeGenCXX/inline-dllexport-member.cpp
index d6b004d66dc6cbd..6bc01599c466780 100644
--- a/clang/test/CodeGenCXX/inline-dllexport-member.cpp
+++ b/clang/test/CodeGenCXX/inline-dllexport-member.cpp
@@ -7,7 +7,7 @@ struct __declspec(dllexport) s {
   static const unsigned int ui = 0;
 };
 
-// CHECK: [[UI]] = !DIGlobalVariableExpression(var: [[UIV:.*]], expr: !DIExpression())
+// CHECK: [[UI]] = !DIGlobalVariableExpression(var: [[UIV:.*]], expr: !DIExpression(DW_OP_constu, 0, DW_OP_stack_value))
 // CHECK: [[UIV]] = distinct !DIGlobalVariable(name: "ui", linkageName: "?ui@s@@2IB", scope: ![[SCOPE:[0-9]+]],
 // CHECK: ![[SCOPE]] = distinct !DICompileUnit(
 

@Michael137
Copy link
Member Author

Michael137 commented Nov 18, 2023

@ilovepi
Copy link
Contributor

ilovepi commented Nov 18, 2023

This looks great, thanks for keeping me in the loop. :)

Copy link
Member

@jryans jryans left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall this looks good, thanks!

I think we may want to add a test to check that the output DWARF has a const value in the expected place, since consumers are relying on it.

clang/lib/CodeGen/CGDebugInfo.cpp Outdated Show resolved Hide resolved
@Michael137
Copy link
Member Author

Overall this looks good, thanks!

I think we may want to add a test to check that the output DWARF has a const value in the expected place, since consumers are relying on it.

Any suggestions where to put such end-to-end test?

This patch makes sure we encode the constant in a DIExpression on definitions, which is tested. And we have tests for ensuring that DW_OP_constu expressions get turned into DW_AT_const_values (I know of at least llvm/test/DebugInfo/Generic/dwarf5-debug-info-static-member.ll)

Copy link
Member

@jryans jryans left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This patch makes sure we encode the constant in a DIExpression on definitions, which is tested. And we have tests for ensuring that DW_OP_constu expressions get turned into DW_AT_const_values (I know of at least llvm/test/DebugInfo/Generic/dwarf5-debug-info-static-member.ll)

Ah okay, then with that in mind, I think the test changes here are likely enough to merge. 🙂

* `debug-info-static-member.cpp`:
  * We added the check for `const_b` as part of the
    patch series in `638a8393615e911b729d5662096f60ef49f1c65e`.
    The new check `isUsableAsConstantExpression` doesn't support
    constant inline floats (since they are neither constexpr nor
    integrals). This isn't a regression since before said patch series
    we wouldn't ever emit the definition for `const_b` anyway. Now
    we just don't do it for `const float`s. This is consistent with
    GCC's behaviour starting with C++11.

* `debug-info-static-inline-member`:
  * This was just a bug which is now fixed. We shouldn't emit
    a `DW_AT_const_value` for a non-const static.
Copy link
Collaborator

@pogo59 pogo59 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One comment, otherwise LGTM.

/// Given a VarDecl corresponding to either the definition or
/// declaration of a C++ static data member, if it has a constant
/// initializer and is evaluatable, return the evaluated value.
/// Returns std::nullopt on failure.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe instead of "on failure" say "otherwise." It's not really a failure condition.

@Michael137
Copy link
Member Author

This will need a slight tweak in DwarfCompileUnit because it currently doesn't attach a DW_AT_location to global variable DIEs that already have a DW_AT_const_value

@dwblaikie
Copy link
Collaborator

Hmm, I can't quite tell from the test case updates in the patch, at least at a glance: How does this get encoded at the IR level? Do we end up with two DIGlobalVariableExpressions? One with the constant value expresison, and one that references the actual global variable? Or somehow encode both in one?

@Michael137
Copy link
Member Author

Hmm, I can't quite tell from the test case updates in the patch, at least at a glance: How does this get encoded at the IR level? Do we end up with two DIGlobalVariableExpressions? One with the constant value expresison, and one that references the actual global variable? Or somehow encode both in one?

My understanding was that the DIExpression parameter to DIGlobalVariableExpression was empty for global variables with locations. So the patch just encodes the constant into that expression if it's otherwise empty.

My plan was to make DwarfCompileUnit support global variables with locations and constants, which it currently assumes doesn't happen.

Let me know if I missed something here

@dwblaikie
Copy link
Collaborator

My understanding was that the DIExpression parameter to DIGlobalVariableExpression was empty for global variables with locations. So the patch just encodes the constant into that expression if it's otherwise empty.

I think in theory it can be non-empty (see what happens under a merge globals optimization... I'm not sure of an example of when this optimization fires, or what the debug info we emit looks like - in theory it should look like a global with a non-empty expression that describes offsetting the pointer forward to reach the global inside the merged global) & so then you'd have a hard time telling whether the expression is meant to be used in addition to the location, or as part of evaluating the location.

We don't really have a mechanism for encoding a variable in two locations (or a constant and a location) at the same time, I think. We could invent a special opcode to use in the expression to communicate this, or define some special handling if there's two separate expressions providing a location (or a location and a constant in this case) for the same variable (say that they're both valid, and emit them both if we can).

@adrian-prantl thoughts?

@dwblaikie
Copy link
Collaborator

// Copy metadata while adjusting any debug info metadata by the original
- this is around where I think we'd get a global with a location and a non-empty expression

@Michael137
Copy link
Member Author

I extracted the parts that are clean-ups/bugfixes into a new PR: #72974

@Michael137
Copy link
Member Author

Michael137 commented Nov 21, 2023

My understanding was that the DIExpression parameter to DIGlobalVariableExpression was empty for global variables with locations. So the patch just encodes the constant into that expression if it's otherwise empty.

I think in theory it can be non-empty (see what happens under a merge globals optimization... I'm not sure of an example of when this optimization fires, or what the debug info we emit looks like - in theory it should look like a global with a non-empty expression that describes offsetting the pointer forward to reach the global inside the merged global) & so then you'd have a hard time telling whether the expression is meant to be used in addition to the location, or as part of evaluating the location.

We don't really have a mechanism for encoding a variable in two locations (or a constant and a location) at the same time, I think. We could invent a special opcode to use in the expression to communicate this, or define some special handling if there's two separate expressions providing a location (or a location and a constant in this case) for the same variable (say that they're both valid, and emit them both if we can).

@adrian-prantl thoughts?

Thanks for the pointers. Managed to trigger the optimisation you described as follows:

// merge.cpp
namespace {
struct Foo {
    static const int a;
    static const int b;
};

const int Foo::a = 100;
const int Foo::b = 200;
}

int main() {
    const int* x = &Foo::a;
    const int* y = &Foo::b;
    return *x + *y;
}

That will generate internal constants in the IR (it's important that they don't have external linkage).

...
@_ZN12_GLOBAL__N_13Foo1aE = internal constant i32 100, align 4, !dbg !0
@_ZN12_GLOBAL__N_13Foo1bE = internal constant i32 200, align 4, !dbg !10
...
!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
!1 = distinct !DIGlobalVariable(name: "a", linkageName: "_ZN12_GLOBAL__N_13Foo1aE", scope: !2, file: !3, line: 7, type: !4, isLocal: true, isDefinition: true, declaration: !6)
...
!10 = !DIGlobalVariableExpression(var: !11, expr: !DIExpression())
!11 = distinct !DIGlobalVariable(name: "b", linkageName: "_ZN12_GLOBAL__N_13Foo1bE", scope: !2, file: !3, line: 8, type: !4, isLocal: true, isDefinition: true, declaration: !9)

Then run opt as:

./bin/opt  -global-merge -global-merge-max-offset=100 -global-merge-on-const -S merge.ll

That changes the IR into:

...
...
@_MergedGlobals = internal constant <{ i32, i32 }> <{ i32 100, i32 200 }>, align 4, !dbg !0, !dbg !10

; Function Attrs: mustprogress noinline norecurse nounwind optnone ssp uwtable(sync)
define noundef i32 @main() #0 !dbg !22 {
entry:
...
  call void @llvm.dbg.declare(metadata ptr %x, metadata !26, metadata !DIExpression()), !dbg !28
  store ptr @_MergedGlobals, ptr %x, align 8, !dbg !28
  call void @llvm.dbg.declare(metadata ptr %y, metadata !29, metadata !DIExpression()), !dbg !30
  store ptr getelementptr inbounds (<{ i32, i32 }>, ptr @_MergedGlobals, i32 0, i32 1), ptr %y, align 8, !dbg !30
...
}
...
!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
!1 = distinct !DIGlobalVariable(name: "a", linkageName: "_ZN12_GLOBAL__N_13Foo1aE", scope: !2, file: !3, line: 7, type: !4, isLocal: true, isDefinition: true, declaration: !6)
...
!10 = !DIGlobalVariableExpression(var: !11, expr: !DIExpression(DW_OP_plus_uconst, 4))
!11 = distinct !DIGlobalVariable(name: "b", linkageName: "_ZN12_GLOBAL__N_13Foo1bE", scope: !2, file: !3, line: 8, type: !4, isLocal: true, isDefinition: true, declaration: !9)
...

@Michael137
Copy link
Member Author

Do we end up with two DIGlobalVariableExpressions?

Just checked. With this patch, MergeGlobals would produce following IR:

...
!10 = !DIGlobalVariableExpression(var: !11, expr: !DIExpression(DW_OP_plus_uconst, 4, DW_OP_constu, 200, DW_OP_stack_value))
!11 = distinct !DIGlobalVariable(name: "b", linkageName: "_ZN12_GLOBAL__N_13Foo1bE", scope: !2, file: !3, line: 8, type: !4, isLocal: true, isDefinition: true, declaration: !9)
...
!14 = !DIGlobalVariableExpression(var: !11, expr: !DIExpression(DW_OP_constu, 200, DW_OP_stack_value))
...

I.e., two DIGlobalVariableExpressions for the same DIGlobalVariable, which AFAICT would confuse DwarfDebug at the moment, which expects there to be a 1-to-1 mapping.

@zeroomega
Copy link
Contributor

@Michael137 is this patch ready to land? We (I have took over the bug from @ilovepi ) have a few builders that are currently blocked by the behavior change introduced in PR #70639 . It would be great if this change can be landed timely. Thx.

@dwblaikie
Copy link
Collaborator

I.e., two DIGlobalVariableExpressions for the same DIGlobalVariable, which AFAICT would confuse DwarfDebug at the moment, which expects there to be a 1-to-1 mapping.

Would confuse DwarfDebug without your changes/this patch? Or only with this patch?
(& hopefully there's some way to reach the merge globals functionality/problem/interesting IR from clang itself - but I guess even if it isn't, this is still a demonstration of representational issues)

@Michael137
Copy link
Member Author

@Michael137 is this patch ready to land? We (I have took over the bug from @ilovepi ) have a few builders that are currently blocked by the behavior change introduced in PR #70639 . It would be great if this change can be landed timely. Thx.

Nope this isn't ready yet. We need to figure out how to make global variables with locations and constants work. I'm about to submit a separate PR that will unblock your builds

@Michael137
Copy link
Member Author

Michael137 commented Nov 28, 2023

Would confuse DwarfDebug without your changes/this patch? Or only with this patch? (& hopefully there's some way to reach the merge globals functionality/problem/interesting IR from clang itself - but I guess even if it isn't, this is still a demonstration of representational issues)

Would only confuse DwarfDebug with this patch. Though I do wonder what GlobalMerge does when AppendAddressSpaceXDeref ends up adding an expression to the global variable; haven't really dug into that part

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:codegen clang Clang issues not falling into any other category debuginfo
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants