llvm.org GIT mirror llvm / 2a478d4
[ThinLTO] Enable ThinLTO WholeProgramDevirt and LowerTypeTests in new PM Summary: Enable these passes for CFI and WPD in ThinLTO and LTO with the new pass manager. Add a couple of tests for both PMs based on the clang tests tools/clang/test/CodeGen/thinlto-distributed-cfi*.ll, but just test through llvm-lto2 and not with distributed ThinLTO. Reviewers: pcc Subscribers: mehdi_amini, inglorion, eraman, steven_wu, dexonsmith, llvm-commits Differential Revision: https://reviews.llvm.org/D49429 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@337461 91177308-0d34-0410-b5e6-96231b3b80d8 Teresa Johnson 1 year, 1 month ago
11 changed file(s) with 227 addition(s) and 32 deletion(s). Raw diff Collapse all Expand all
2626 class StringRef;
2727 class AAManager;
2828 class TargetMachine;
29 class ModuleSummaryIndex;
2930
3031 /// A struct capturing PGO tunables.
3132 struct PGOOptions {
309310 /// only intended for use when attempting to optimize code. If frontends
310311 /// require some transformations for semantic reasons, they should explicitly
311312 /// build them.
312 ModulePassManager buildThinLTODefaultPipeline(OptimizationLevel Level,
313 bool DebugLogging = false);
313 ModulePassManager
314 buildThinLTODefaultPipeline(OptimizationLevel Level, bool DebugLogging,
315 const ModuleSummaryIndex *ImportSummary);
314316
315317 /// Build a pre-link, LTO-targeting default optimization pipeline to a pass
316318 /// manager.
339341 /// require some transformations for semantic reasons, they should explicitly
340342 /// build them.
341343 ModulePassManager buildLTODefaultPipeline(OptimizationLevel Level,
342 bool DebugLogging = false);
344 bool DebugLogging,
345 ModuleSummaryIndex *ExportSummary);
343346
344347 /// Build the default `AAManager` with the default alias analysis pipeline
345348 /// registered.
2525 namespace llvm {
2626
2727 class Module;
28 class ModuleSummaryIndex;
2829 class raw_ostream;
2930
3031 namespace lowertypetests {
196197
197198 class LowerTypeTestsPass : public PassInfoMixin {
198199 public:
200 ModuleSummaryIndex *ExportSummary;
201 const ModuleSummaryIndex *ImportSummary;
202 LowerTypeTestsPass(ModuleSummaryIndex *ExportSummary,
203 const ModuleSummaryIndex *ImportSummary)
204 : ExportSummary(ExportSummary), ImportSummary(ImportSummary) {}
199205 PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM);
200206 };
201207
2727 template class MutableArrayRef;
2828 class Function;
2929 class GlobalVariable;
30 class ModuleSummaryIndex;
3031
3132 namespace wholeprogramdevirt {
3233
217218 } // end namespace wholeprogramdevirt
218219
219220 struct WholeProgramDevirtPass : public PassInfoMixin {
221 ModuleSummaryIndex *ExportSummary;
222 const ModuleSummaryIndex *ImportSummary;
223 WholeProgramDevirtPass(ModuleSummaryIndex *ExportSummary,
224 const ModuleSummaryIndex *ImportSummary)
225 : ExportSummary(ExportSummary), ImportSummary(ImportSummary) {
226 assert(!(ExportSummary && ImportSummary));
227 }
220228 PreservedAnalyses run(Module &M, ModuleAnalysisManager &);
221229 };
222230
143143 }
144144
145145 static void runNewPMPasses(Config &Conf, Module &Mod, TargetMachine *TM,
146 unsigned OptLevel, bool IsThinLTO) {
146 unsigned OptLevel, bool IsThinLTO,
147 ModuleSummaryIndex *ExportSummary,
148 const ModuleSummaryIndex *ImportSummary) {
147149 Optional PGOOpt;
148150 if (!Conf.SampleProfile.empty())
149151 PGOOpt = PGOOptions("", "", Conf.SampleProfile, false, true);
193195 }
194196
195197 if (IsThinLTO)
196 MPM = PB.buildThinLTODefaultPipeline(OL, Conf.DebugPassManager);
198 MPM = PB.buildThinLTODefaultPipeline(OL, Conf.DebugPassManager,
199 ImportSummary);
197200 else
198 MPM = PB.buildLTODefaultPipeline(OL, Conf.DebugPassManager);
201 MPM = PB.buildLTODefaultPipeline(OL, Conf.DebugPassManager, ExportSummary);
199202 MPM.run(Mod, MAM);
200203
201204 // FIXME (davide): verify the output.
278281 runNewPMCustomPasses(Mod, TM, Conf.OptPipeline, Conf.AAPipeline,
279282 Conf.DisableVerify);
280283 else if (Conf.UseNewPM)
281 runNewPMPasses(Conf, Mod, TM, Conf.OptLevel, IsThinLTO);
284 runNewPMPasses(Conf, Mod, TM, Conf.OptLevel, IsThinLTO, ExportSummary,
285 ImportSummary);
282286 else
283287 runOldPMPasses(Conf, Mod, TM, IsThinLTO, ExportSummary, ImportSummary);
284288 return !Conf.PostOptModuleHook || Conf.PostOptModuleHook(Task, Mod);
921921 return MPM;
922922 }
923923
924 ModulePassManager
925 PassBuilder::buildThinLTODefaultPipeline(OptimizationLevel Level,
926 bool DebugLogging) {
927 // FIXME: The summary index is not hooked in the new pass manager yet.
928 // When it's going to be hooked, enable WholeProgramDevirt and LowerTypeTest
929 // here.
930
924 ModulePassManager PassBuilder::buildThinLTODefaultPipeline(
925 OptimizationLevel Level, bool DebugLogging,
926 const ModuleSummaryIndex *ImportSummary) {
931927 ModulePassManager MPM(DebugLogging);
928
929 if (ImportSummary) {
930 // These passes import type identifier resolutions for whole-program
931 // devirtualization and CFI. They must run early because other passes may
932 // disturb the specific instruction patterns that these passes look for,
933 // creating dependencies on resolutions that may not appear in the summary.
934 //
935 // For example, GVN may transform the pattern assume(type.test) appearing in
936 // two basic blocks into assume(phi(type.test, type.test)), which would
937 // transform a dependency on a WPD resolution into a dependency on a type
938 // identifier resolution for CFI.
939 //
940 // Also, WPD has access to more precise information than ICP and can
941 // devirtualize more effectively, so it should operate on the IR first.
942 MPM.addPass(WholeProgramDevirtPass(nullptr, ImportSummary));
943 MPM.addPass(LowerTypeTestsPass(nullptr, ImportSummary));
944 }
932945
933946 // Force any function attributes we want the rest of the pipeline to observe.
934947 MPM.addPass(ForceFunctionAttrsPass());
960973 return buildPerModuleDefaultPipeline(Level, DebugLogging);
961974 }
962975
963 ModulePassManager PassBuilder::buildLTODefaultPipeline(OptimizationLevel Level,
964 bool DebugLogging) {
976 ModulePassManager
977 PassBuilder::buildLTODefaultPipeline(OptimizationLevel Level, bool DebugLogging,
978 ModuleSummaryIndex *ExportSummary) {
965979 assert(Level != O0 && "Must request optimizations for the default pipeline!");
966980 ModulePassManager MPM(DebugLogging);
967981
10111025
10121026 // Run whole program optimization of virtual call when the list of callees
10131027 // is fixed.
1014 MPM.addPass(WholeProgramDevirtPass());
1028 MPM.addPass(WholeProgramDevirtPass(ExportSummary, nullptr));
10151029
10161030 // Stop here at -O1.
1017 if (Level == 1)
1031 if (Level == 1) {
1032 // The LowerTypeTestsPass needs to run to lower type metadata and the
1033 // type.test intrinsics. The pass does nothing if CFI is disabled.
1034 MPM.addPass(LowerTypeTestsPass(ExportSummary, nullptr));
10181035 return MPM;
1036 }
10191037
10201038 // Optimize globals to try and fold them into constants.
10211039 MPM.addPass(GlobalOptPass());
11241142 // clang's control flow integrity mechanisms (-fsanitize=cfi*) and needs
11251143 // to be run at link time if CFI is enabled. This pass does nothing if
11261144 // CFI is disabled.
1127 // Enable once we add support for the summary in the new PM.
1128 #if 0
1129 MPM.addPass(LowerTypeTestsPass(Summary ? PassSummaryAction::Export :
1130 PassSummaryAction::None,
1131 Summary));
1132 #endif
1145 MPM.addPass(LowerTypeTestsPass(ExportSummary, nullptr));
11331146
11341147 // Add late LTO optimization passes.
11351148 // Delete basic blocks, which optimization passes may have killed.
14411454 } else if (Matches[1] == "thinlto-pre-link") {
14421455 MPM.addPass(buildThinLTOPreLinkDefaultPipeline(L, DebugLogging));
14431456 } else if (Matches[1] == "thinlto") {
1444 MPM.addPass(buildThinLTODefaultPipeline(L, DebugLogging));
1457 MPM.addPass(buildThinLTODefaultPipeline(L, DebugLogging, nullptr));
14451458 } else if (Matches[1] == "lto-pre-link") {
14461459 MPM.addPass(buildLTOPreLinkDefaultPipeline(L, DebugLogging));
14471460 } else {
14481461 assert(Matches[1] == "lto" && "Not one of the matched options!");
1449 MPM.addPass(buildLTODefaultPipeline(L, DebugLogging));
1462 MPM.addPass(buildLTODefaultPipeline(L, DebugLogging, nullptr));
14501463 }
14511464 return true;
14521465 }
5555 MODULE_PASS("internalize", InternalizePass())
5656 MODULE_PASS("invalidate", InvalidateAllAnalysesPass())
5757 MODULE_PASS("ipsccp", IPSCCPPass())
58 MODULE_PASS("lowertypetests", LowerTypeTestsPass())
58 MODULE_PASS("lowertypetests", LowerTypeTestsPass(nullptr, nullptr))
5959 MODULE_PASS("name-anon-globals", NameAnonGlobalPass())
6060 MODULE_PASS("no-op-module", NoOpModulePass())
6161 MODULE_PASS("partial-inliner", PartialInlinerPass())
7474 MODULE_PASS("sample-profile", SampleProfileLoaderPass())
7575 MODULE_PASS("strip-dead-prototypes", StripDeadPrototypesPass())
7676 MODULE_PASS("synthetic-counts-propagation", SyntheticCountsPropagation())
77 MODULE_PASS("wholeprogramdevirt", WholeProgramDevirtPass())
77 MODULE_PASS("wholeprogramdevirt", WholeProgramDevirtPass(nullptr, nullptr))
7878 MODULE_PASS("verify", VerifierPass())
7979 #undef MODULE_PASS
8080
21012101
21022102 PreservedAnalyses LowerTypeTestsPass::run(Module &M,
21032103 ModuleAnalysisManager &AM) {
2104 bool Changed = LowerTypeTestsModule(M, /*ExportSummary=*/nullptr,
2105 /*ImportSummary=*/nullptr)
2106 .lower();
2104 bool Changed = LowerTypeTestsModule(M, ExportSummary, ImportSummary).lower();
21072105 if (!Changed)
21082106 return PreservedAnalyses::all();
21092107 return PreservedAnalyses::none();
610610 auto OREGetter = [&](Function *F) -> OptimizationRemarkEmitter & {
611611 return FAM.getResult(*F);
612612 };
613 if (!DevirtModule(M, AARGetter, OREGetter, nullptr, nullptr).run())
613 if (!DevirtModule(M, AARGetter, OREGetter, ExportSummary, ImportSummary)
614 .run())
614615 return PreservedAnalyses::all();
615616 return PreservedAnalyses::none();
616617 }
5353 ; CHECK-O-NEXT: Running analysis: CallGraphAnalysis
5454 ; CHECK-O-NEXT: Running pass: GlobalSplitPass
5555 ; CHECK-O-NEXT: Running pass: WholeProgramDevirtPass
56 ; CHECK-O1-NEXT: Running pass: LowerTypeTestsPass
5657 ; CHECK-O2-NEXT: Running pass: GlobalOptPass
5758 ; CHECK-O2-NEXT: Running pass: ModuleToFunctionPassAdaptor<{{.*}}PromotePass>
5859 ; CHECK-O2-NEXT: Running analysis: DominatorTreeAnalysis
8182 ; CHECK-O2-NEXT: Running analysis: MemoryDependenceAnalysis
8283 ; CHECK-O2-NEXT: Running analysis: DemandedBitsAnalysis
8384 ; CHECK-O2-NEXT: Running pass: CrossDSOCFIPass
85 ; CHECK-O2-NEXT: Running pass: LowerTypeTestsPass
8486 ; CHECK-O2-NEXT: Running pass: ModuleToFunctionPassAdaptor<{{.*}}SimplifyCFGPass>
8587 ; CHECK-O2-NEXT: Running pass: EliminateAvailableExternallyPass
8688 ; CHECK-O2-NEXT: Running pass: GlobalDCEPass
0 ; REQUIRES: x86-registered-target
1
2 ; Test CFI devirtualization through the thin link and backend.
3
4 ; RUN: opt -thinlto-bc -o %t.o %s
5
6 ; Legacy PM
7 ; RUN: llvm-lto2 run %t.o -save-temps \
8 ; RUN: -o %t3 \
9 ; RUN: -r=%t.o,test,px \
10 ; RUN: -r=%t.o,_ZN1A1nEi,p \
11 ; RUN: -r=%t.o,_ZN1B1fEi,p \
12 ; RUN: -r=%t.o,_ZN1C1fEi,p \
13 ; RUN: -r=%t.o,_ZTV1B, \
14 ; RUN: -r=%t.o,_ZTV1C, \
15 ; RUN: -r=%t.o,_ZN1A1nEi, \
16 ; RUN: -r=%t.o,_ZN1B1fEi, \
17 ; RUN: -r=%t.o,_ZN1C1fEi, \
18 ; RUN: -r=%t.o,_ZTV1B,px \
19 ; RUN: -r=%t.o,_ZTV1C,px
20 ; RUN: llvm-dis %t3.1.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-IR
21
22 ; New PM
23 ; RUN: llvm-lto2 run %t.o -save-temps -use-new-pm \
24 ; RUN: -o %t3 \
25 ; RUN: -r=%t.o,test,px \
26 ; RUN: -r=%t.o,_ZN1A1nEi,p \
27 ; RUN: -r=%t.o,_ZN1B1fEi,p \
28 ; RUN: -r=%t.o,_ZN1C1fEi,p \
29 ; RUN: -r=%t.o,_ZTV1B, \
30 ; RUN: -r=%t.o,_ZTV1C, \
31 ; RUN: -r=%t.o,_ZN1A1nEi, \
32 ; RUN: -r=%t.o,_ZN1B1fEi, \
33 ; RUN: -r=%t.o,_ZN1C1fEi, \
34 ; RUN: -r=%t.o,_ZTV1B,px \
35 ; RUN: -r=%t.o,_ZTV1C,px
36 ; RUN: llvm-dis %t3.1.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-IR
37
38 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
39 target triple = "x86_64-grtev4-linux-gnu"
40
41 %struct.A = type { i32 (...)** }
42 %struct.B = type { %struct.A }
43 %struct.C = type { %struct.A }
44
45 @_ZTV1B = constant { [4 x i8*] } { [4 x i8*] [i8* null, i8* undef, i8* bitcast (i32 (%struct.B*, i32)* @_ZN1B1fEi to i8*), i8* bitcast (i32 (%struct.A*, i32)* @_ZN1A1nEi to i8*)] }, !type !0, !type !1
46 @_ZTV1C = constant { [4 x i8*] } { [4 x i8*] [i8* null, i8* undef, i8* bitcast (i32 (%struct.C*, i32)* @_ZN1C1fEi to i8*), i8* bitcast (i32 (%struct.A*, i32)* @_ZN1A1nEi to i8*)] }, !type !0, !type !2
47
48 ; CHECK-IR-LABEL: define i32 @test
49 define i32 @test(%struct.A* %obj, i32 %a) {
50 entry:
51 %0 = bitcast %struct.A* %obj to i8**
52 %vtable5 = load i8*, i8** %0
53
54 %1 = tail call { i8*, i1 } @llvm.type.checked.load(i8* %vtable5, i32 8, metadata !"_ZTS1A")
55 %2 = extractvalue { i8*, i1 } %1, 1
56 br i1 %2, label %cont, label %trap
57
58 trap:
59 tail call void @llvm.trap()
60 unreachable
61
62 cont:
63 %3 = extractvalue { i8*, i1 } %1, 0
64 %4 = bitcast i8* %3 to i32 (%struct.A*, i32)*
65
66 ; Check that the call was devirtualized.
67 ; CHECK-IR: %call = tail call i32 @_ZN1A1nEi
68 %call = tail call i32 %4(%struct.A* nonnull %obj, i32 %a)
69 %vtable16 = load i8*, i8** %0
70 %5 = tail call { i8*, i1 } @llvm.type.checked.load(i8* %vtable16, i32 0, metadata !"_ZTS1A")
71 %6 = extractvalue { i8*, i1 } %5, 1
72 br i1 %6, label %cont2, label %trap
73
74 cont2:
75 %7 = extractvalue { i8*, i1 } %5, 0
76 %8 = bitcast i8* %7 to i32 (%struct.A*, i32)*
77
78 ; Check that traps are conditional. Invalid TYPE_ID can cause
79 ; unconditional traps.
80 ; CHECK-IR: br i1 {{.*}}, label %trap
81
82 ; We still have to call it as virtual.
83 ; CHECK-IR: %call3 = tail call i32 %8
84 %call3 = tail call i32 %8(%struct.A* nonnull %obj, i32 %call)
85 ret i32 %call3
86 }
87 ; CHECK-IR-LABEL: ret i32
88 ; CHECK-IR-LABEL: }
89
90 declare { i8*, i1 } @llvm.type.checked.load(i8*, i32, metadata)
91 declare void @llvm.trap()
92
93 declare i32 @_ZN1B1fEi(%struct.B* %this, i32 %a)
94 declare i32 @_ZN1A1nEi(%struct.A* %this, i32 %a)
95 declare i32 @_ZN1C1fEi(%struct.C* %this, i32 %a)
96
97 !0 = !{i64 16, !"_ZTS1A"}
98 !1 = !{i64 16, !"_ZTS1B"}
99 !2 = !{i64 16, !"_ZTS1C"}
0 ; REQUIRES: x86-registered-target
1
2 ; Test CFI through the thin link and backend.
3
4 ; RUN: opt -thinlto-bc -o %t.o %s
5
6 ; Legacy PM
7 ; RUN: llvm-lto2 run -save-temps %t.o \
8 ; RUN: -o %t3 \
9 ; RUN: -r=%t.o,test,px \
10 ; RUN: -r=%t.o,_ZTV1B, \
11 ; RUN: -r=%t.o,_ZN1B1fEi, \
12 ; RUN: -r=%t.o,_ZTV1B,px
13 ; RUN: llvm-dis %t3.1.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-IR
14
15 ; New PM
16 ; RUN: llvm-lto2 run -save-temps %t.o -use-new-pm \
17 ; RUN: -o %t3 \
18 ; RUN: -r=%t.o,test,px \
19 ; RUN: -r=%t.o,_ZTV1B, \
20 ; RUN: -r=%t.o,_ZN1B1fEi, \
21 ; RUN: -r=%t.o,_ZTV1B,px
22 ; RUN: llvm-dis %t3.1.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-IR
23
24 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
25 target triple = "x86_64-grtev4-linux-gnu"
26
27 %struct.B = type { %struct.A }
28 %struct.A = type { i32 (...)** }
29
30 @_ZTV1B = constant { [3 x i8*] } { [3 x i8*] [i8* undef, i8* undef, i8* undef] }, !type !0
31
32 ; CHECK-IR-LABEL: define void @test
33 define void @test(i8* %b) {
34 entry:
35 ; Ensure that traps are conditional. Invalid TYPE_ID can cause
36 ; unconditional traps.
37 ; CHECK-IR: br i1 {{.*}}, label %trap
38 %0 = bitcast i8* %b to i8**
39 %vtable2 = load i8*, i8** %0
40 %1 = tail call i1 @llvm.type.test(i8* %vtable2, metadata !"_ZTS1A")
41 br i1 %1, label %cont, label %trap
42
43 trap:
44 tail call void @llvm.trap()
45 unreachable
46
47 cont:
48 ; CHECK-IR-LABEL: ret void
49 ret void
50 }
51 ; CHECK-IR-LABEL: }
52
53 declare i1 @llvm.type.test(i8*, metadata)
54 declare void @llvm.trap()
55
56 declare i32 @_ZN1B1fEi(%struct.B* %this, i32 %a)
57
58 !0 = !{i64 16, !"_ZTS1A"}
59 !1 = !{i64 16, !"_ZTS1B"}