Skip to content

Commit b9337b1

Browse files
committed
[ADCE] Preserve MemorySSA if only debug instructions are removed
As we've seen in llvm#58285 throwing away MemorySSA when only debug instructions are removed can lead to different code when debug info is present or not present. This is the second attempt at fixing the problem. The first one was https://reviews.llvm.org/D145051 which was reverted due to a 15% compile time regression for tramp3d-v4 in NewPM-ReleaseLTO-g. Differential Revision: https://reviews.llvm.org/D145478
1 parent 1db2fb9 commit b9337b1

File tree

2 files changed

+84
-1
lines changed

2 files changed

+84
-1
lines changed

llvm/lib/Transforms/Scalar/ADCE.cpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "llvm/Analysis/DomTreeUpdater.h"
2727
#include "llvm/Analysis/GlobalsModRef.h"
2828
#include "llvm/Analysis/IteratedDominanceFrontier.h"
29+
#include "llvm/Analysis/MemorySSA.h"
2930
#include "llvm/Analysis/PostDominators.h"
3031
#include "llvm/IR/BasicBlock.h"
3132
#include "llvm/IR/CFG.h"
@@ -115,6 +116,7 @@ struct BlockInfoType {
115116

116117
struct ADCEChanged {
117118
bool ChangedAnything = false;
119+
bool ChangedNonDebugInstr = false;
118120
bool ChangedControlFlow = false;
119121
};
120122

@@ -560,6 +562,8 @@ ADCEChanged AggressiveDeadCodeElimination::removeDeadInstructions() {
560562
continue;
561563

562564
// Fallthrough and drop the intrinsic.
565+
} else {
566+
Changed.ChangedNonDebugInstr = true;
563567
}
564568

565569
// Prepare to delete.
@@ -713,8 +717,17 @@ PreservedAnalyses ADCEPass::run(Function &F, FunctionAnalysisManager &FAM) {
713717
return PreservedAnalyses::all();
714718

715719
PreservedAnalyses PA;
716-
if (!Changed.ChangedControlFlow)
720+
if (!Changed.ChangedControlFlow) {
717721
PA.preserveSet<CFGAnalyses>();
722+
if (!Changed.ChangedNonDebugInstr) {
723+
// Only removing debug instructions does not affect MemorySSA.
724+
//
725+
// Therefore we preserve MemorySSA when only removing debug instructions
726+
// since otherwise later passes may behave differently which then makes
727+
// the presence of debug info affect code generation.
728+
PA.preserve<MemorySSAAnalysis>();
729+
}
730+
}
718731
PA.preserve<DominatorTreeAnalysis>();
719732
PA.preserve<PostDominatorTreeAnalysis>();
720733

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
; RUN: opt -passes='require<memoryssa>,adce' -o - -S -debug-pass-manager < %s 2>&1 | FileCheck %s
2+
3+
; ADCE should remove the dbg.declare in test1, but it should preserve
4+
; MemorySSA since it only removed a debug instruction.
5+
6+
; Invalidating MemorySSA when only removing debug instructions may lead
7+
; to different code with/without debug info present.
8+
; In
9+
; https://github.com/llvm/llvm-project/issues/58285
10+
; we saw how ADCE removed a dbg.declare, invalidated all analyses, and later
11+
; DSE behaved in some way. Without the dbg.declare in the input ADCE kept
12+
; analysis info, and DSE behaved differently.
13+
14+
; CHECK: Running analysis: MemorySSAAnalysis on test1
15+
; CHECK: Running pass: ADCEPass on test1 (1 instruction)
16+
; CHECK-NOT: Invalidating analysis: MemorySSAAnalysis on test1
17+
18+
; In test2 ADCE also removes an instruction, but since we remove a non-debug
19+
; instruction as well we invalidate several analyses, including MemorySSA.
20+
21+
; CHECK: Running analysis: MemorySSAAnalysis on test2
22+
; CHECK: Running pass: ADCEPass on test2 (2 instructions)
23+
; CHECK: Invalidating analysis: MemorySSAAnalysis on test2
24+
; CHECK: Running pass: PrintModulePass
25+
26+
; CHECK-LABEL: @test1(
27+
; CHECK-NEXT: entry:
28+
; CHECK-NEXT: ret i16 0
29+
30+
; CHECK-LABEL: @test2(
31+
; CHECK-NEXT: entry:
32+
; CHECK-NEXT: ret i16 0
33+
34+
define i16 @test1() {
35+
entry:
36+
call void @llvm.dbg.declare(metadata ptr undef, metadata !4, metadata !DIExpression()), !dbg !16
37+
ret i16 0
38+
}
39+
40+
define i16 @test2() {
41+
entry:
42+
%dead = add i16 1, 2
43+
ret i16 0
44+
}
45+
46+
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
47+
declare void @llvm.dbg.declare(metadata, metadata, metadata) #0
48+
49+
attributes #0 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
50+
51+
!llvm.dbg.cu = !{}
52+
!llvm.module.flags = !{!0, !1, !2, !3}
53+
54+
!0 = !{i32 7, !"Dwarf Version", i32 4}
55+
!1 = !{i32 2, !"Debug Info Version", i32 3}
56+
!2 = !{i32 1, !"wchar_size", i32 1}
57+
!3 = !{i32 7, !"frame-pointer", i32 2}
58+
!4 = !DILocalVariable(name: "w", scope: !5, file: !6, line: 18, type: !11)
59+
!5 = distinct !DILexicalBlock(scope: !7, file: !6, line: 18, column: 8)
60+
!6 = !DIFile(filename: "foo2.c", directory: "/llvm")
61+
!7 = distinct !DISubprogram(name: "test1", scope: !6, file: !6, line: 14, type: !8, scopeLine: 14, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !10)
62+
!8 = !DISubroutineType(types: !9)
63+
!9 = !{}
64+
!10 = distinct !DICompileUnit(language: DW_LANG_C99, file: !6, producer: "clang version 16.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, globals: !9, splitDebugInlining: false, nameTableKind: None)
65+
!11 = !DIDerivedType(tag: DW_TAG_typedef, name: "uint64_t", file: !12, line: 60, baseType: !13)
66+
!12 = !DIFile(filename: "/include/sys/_stdint.h", directory: "")
67+
!13 = !DIDerivedType(tag: DW_TAG_typedef, name: "__uint64_t", file: !14, line: 108, baseType: !15)
68+
!14 = !DIFile(filename: "/include/machine/_default_types.h", directory: "")
69+
!15 = !DIBasicType(name: "unsigned long long", size: 64, encoding: DW_ATE_unsigned)
70+
!16 = !DILocation(line: 18, column: 8, scope: !5)

0 commit comments

Comments
 (0)