This is an archive of the discontinued LLVM Phabricator instance.

[Cloning] Move distinct GlobalVariable debug info metadata in CloneModule
ClosedPublic

Authored by EwanCrawford on Jul 31 2017, 4:04 AM.

Details

Summary

Duplicating the distinct Subprogram and CU metadata nodes seems like the incorrect thing to do in CloneModule for GlobalVariable debug info. As it results in the scope of the GlobalVariable DI no longer being consistent with the rest of the module, and the new CU is absent from llvm.dbg.cu.

Fixed by adding RF_MoveDistinctMDs to MapMetadata flags for GlobalVariables.

Current unit test IR after clone:

@gv = global i32 1, comdat($comdat), !dbg !0, !type !5

define private void @f() comdat($comdat) personality void ()* @persfn !dbg !14 {

!llvm.dbg.cu = !{!10}

!0 = !DIGlobalVariableExpression(var: !1)
!1 = distinct !DIGlobalVariable(name: "gv", linkageName: "gv", scope: !2, file: !3, line: 1, type: !9, isLocal: false, isDefinition: true)
!2 = distinct !DISubprogram(name: "f", linkageName: "f", scope: null, file: !3, line: 4, type: !4, isLocal: true, isDefinition: true, scopeLine: 3, isOptimized: false, unit: !6, variables: !5)
!3 = !DIFile(filename: "filename.c", directory: "/file/dir/")
!4 = !DISubroutineType(types: !5)
!5 = !{}
!6 = distinct !DICompileUnit(language: DW_LANG_C99, file: !7, producer: "CloneModule", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !5, globals: !8)
!7 = !DIFile(filename: "filename.c", directory: "/file/dir")
!8 = !{!0}
!9 = !DIBasicType(tag: DW_TAG_unspecified_type, name: "decltype(nullptr)")
!10 = distinct !DICompileUnit(language: DW_LANG_C99, file: !7, producer: "CloneModule", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !5, globals: !11)
!11 = !{!12}
!12 = !DIGlobalVariableExpression(var: !13)
!13 = distinct !DIGlobalVariable(name: "gv", linkageName: "gv", scope: !14, file: !3, line: 1, type: !9, isLocal: false, isDefinition: true)
!14 = distinct !DISubprogram(name: "f", linkageName: "f", scope: null, file: !3, line: 4, type: !4, isLocal: true, isDefinition: true, scopeLine: 3, isOptimized: false, unit: !10, variables: !5)

Patched IR after clone:

@gv = global i32 1, comdat($comdat), !dbg !0, !type !5

define private void @f() comdat($comdat) personality void ()* @persfn !dbg !2 {

!llvm.dbg.cu = !{!6}

!0 = !DIGlobalVariableExpression(var: !1)
!1 = distinct !DIGlobalVariable(name: "gv", linkageName: "gv", scope: !2, file: !3, line: 1, type: !9, isLocal: false, isDefinition: true)
!2 = distinct !DISubprogram(name: "f", linkageName: "f", scope: null, file: !3, line: 4, type: !4, isLocal: true, isDefinition: true, scopeLine: 3, isOptimized: false, unit: !6, variables: !5)
!3 = !DIFile(filename: "filename.c", directory: "/file/dir/")
!4 = !DISubroutineType(types: !5)
!5 = !{}
!6 = distinct !DICompileUnit(language: DW_LANG_C99, file: !7, producer: "CloneModule", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !5, globals: !8)
!7 = !DIFile(filename: "filename.c", directory: "/file/dir")
!8 = !{!0}
!9 = !DIBasicType(tag: DW_TAG_unspecified_type, name: "decltype(nullptr)")

Diff Detail

Repository
rL LLVM

Event Timeline

EwanCrawford created this revision.Jul 31 2017, 4:04 AM
aprantl edited edge metadata.Aug 1 2017, 9:46 AM

IIUC this is because the DIGlobalVariables are currently reachable both through the !dbg attachment on the Global and through the globals: field in the DICompileUnit?
This should be unaffected by you change, but could you, to prevent this from breaking whenever we make changes to the metadata graph in the future, also add a test for an constant global variable that is not attached to any symbol?

!1 = !DIGlobalVariableExpression(var: !3, expr: !2)
!2 = !DIExpression(DW_OP_constu, 42, DW_OP_stack_value)
!3 = !DIGlobalVariable(...)

to clarify: not attached to any Global, but listed in the globals: list of a DICompileUnit.

Thanks for taking a look Adrian, I think the DIGlobalVariables being accessible via two routes is indeed the reason for this.

Added that extra test.

aprantl accepted this revision.Aug 2 2017, 6:03 AM
This revision is now accepted and ready to land.Aug 2 2017, 6:03 AM
This revision was automatically updated to reflect the committed changes.
rtereshin added inline comments.
llvm/trunk/lib/Transforms/Utils/CloneModule.cpp
189

I'm hitting a case with an older version of LLVM I can't unfortunately expose as a test: this part of CloneModule while iterating over operands of !llvm.dbg.cu, which are DICompileUnits, sometimes duplicates a DICompileUnit. Not always, strangely enough. The duplicate itself is completely redundant, but it also explicitly breaks the debug info as that CU doesn't get included into llvm.dbg.cu list. Adding RF_MoveDistinctMDs flag to this MapMetadata call solves the problem.

Do you think you could take a look and see if we need RF_MoveDistinctMDs here as well as above?

RF_MoveDistinctMDs is not safe to use in llvm::CloneModule(), since this flag causes the IRLinker to mutate distinct nodes instead of cloning them. That's only sound if the original module is being discarded after linking (e.g., when you want to link some bitcode in, you read it into an ephemeral module, link in what you want, and discard what's left). In llvm::CloneModule(), you'll end up with the original module referencing global variables owned by the cloned module.

I suspect adding RF_MoveDistinctMDs here papered over an underlying bug related to cloning llvm.dbg.cu. See my longer comment in:
https://bugs.llvm.org/show_bug.cgi?id=48841

Herald added a project: Restricted Project. · View Herald TranscriptFeb 10 2021, 12:34 PM
dexonsmith added inline comments.Feb 10 2021, 12:45 PM
llvm/trunk/lib/Transforms/Utils/CloneModule.cpp
189

Adding RF_MoveDistinctMDs flag to this MapMetadata call solves the problem.

Doing so will only paper over the problem, and invalidate the source module being cloned.

Whatever is happening here may be the key to understanding both bugs. MapMetadata should clone metadata, and NewNMD should reference the clone instead of the original. It sounds like some caller is not going through VMap and is getting a different copy.