Skip to content

Commit b84ea7a

Browse files
committed
[Attributor] Improve use of dominating writes during reasoning
This resolves a recent regression introduced by a bug fix and allows us to use dominating write information (formerly HasBeenWrittenTo information) to skip potential interfering accesses. Generally, there are two changes here: 1) If we have dominating writes they form a chain and we can look at the least one to minimize the distance between the write and the (read) access in question. 2) If such a least dominating write exists, we can ignore writes in other functions as long as they cannot be reached from code between this write and the (read) access in question. We have all the tools available to make such queries and the positive tests show the result. Note that the negative test from the bug fix is still in tree and not affected. As a side-effect, we can remove the (arbitrary) treshold now on the number of interfering accesses since we do not iterate over dominating ones anymore.
1 parent 1f0c237 commit b84ea7a

File tree

6 files changed

+242
-66
lines changed

6 files changed

+242
-66
lines changed

llvm/lib/Transforms/IPO/AttributorAttributes.cpp

Lines changed: 76 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -96,12 +96,6 @@ static cl::opt<int> MaxPotentialValuesIterations(
9696
"Maximum number of iterations we keep dismantling potential values."),
9797
cl::init(64));
9898

99-
static cl::opt<unsigned> MaxInterferingAccesses(
100-
"attributor-max-interfering-accesses", cl::Hidden,
101-
cl::desc("Maximum number of interfering accesses to "
102-
"check before assuming all might interfere."),
103-
cl::init(6));
104-
10599
STATISTIC(NumAAs, "Number of abstract attributes created");
106100

107101
// Some helper macros to deal with statistics tracking.
@@ -1081,7 +1075,6 @@ struct AAPointerInfoImpl
10811075
const bool FindInterferingReads = I.mayWriteToMemory();
10821076
const bool UseDominanceReasoning =
10831077
FindInterferingWrites && NoRecurseAA.isKnownNoRecurse();
1084-
const bool CanUseCFGResoning = CanIgnoreThreading(I);
10851078
const DominatorTree *DT =
10861079
InfoCache.getAnalysisResultForFunction<DominatorTreeAnalysis>(Scope);
10871080

@@ -1141,65 +1134,107 @@ struct AAPointerInfoImpl
11411134
(!FindInterferingReads || !Acc.isRead()))
11421135
return true;
11431136

1144-
bool Dominates = DT && Exact && Acc.isMustAccess() &&
1145-
(Acc.getLocalInst()->getFunction() == &Scope) &&
1137+
bool Dominates = FindInterferingWrites && DT && Exact &&
1138+
Acc.isMustAccess() &&
1139+
(Acc.getRemoteInst()->getFunction() == &Scope) &&
11461140
DT->dominates(Acc.getRemoteInst(), &I);
1147-
if (FindInterferingWrites && Dominates)
1148-
HasBeenWrittenTo = true;
1141+
if (Dominates)
1142+
DominatingWrites.insert(&Acc);
11491143

11501144
// Track if all interesting accesses are in the same `nosync` function as
11511145
// the given instruction.
11521146
AllInSameNoSyncFn &= Acc.getRemoteInst()->getFunction() == &Scope;
11531147

1154-
// For now we only filter accesses based on CFG reasoning which does not
1155-
// work yet if we have threading effects, or the access is complicated.
1156-
if (CanUseCFGResoning && Dominates && UseDominanceReasoning &&
1157-
IsSameThreadAsInst(Acc))
1158-
DominatingWrites.insert(&Acc);
1159-
11601148
InterferingAccesses.push_back({&Acc, Exact});
11611149
return true;
11621150
};
11631151
if (!State::forallInterferingAccesses(I, AccessCB, Range))
11641152
return false;
11651153

1166-
// Helper to determine if we can skip a specific write access. This is in
1167-
// the worst case quadratic as we are looking for another write that will
1168-
// hide the effect of this one.
1154+
HasBeenWrittenTo = !DominatingWrites.empty();
1155+
1156+
// Dominating writes form a chain, find the least/lowest member.
1157+
Instruction *LeastDominatingWriteInst = nullptr;
1158+
for (const Access *Acc : DominatingWrites) {
1159+
if (!LeastDominatingWriteInst) {
1160+
LeastDominatingWriteInst = Acc->getRemoteInst();
1161+
} else if (DT->dominates(LeastDominatingWriteInst,
1162+
Acc->getRemoteInst())) {
1163+
LeastDominatingWriteInst = Acc->getRemoteInst();
1164+
}
1165+
}
1166+
1167+
// Helper to determine if we can skip a specific write access.
11691168
auto CanSkipAccess = [&](const Access &Acc, bool Exact) {
11701169
if (!IsSameThreadAsInst(Acc))
11711170
return false;
1172-
if ((!Acc.isWriteOrAssumption() ||
1173-
!AA::isPotentiallyReachable(A, *Acc.getRemoteInst(), I, QueryingAA,
1174-
&ExclusionSet, IsLiveInCalleeCB)) &&
1175-
(!Acc.isRead() ||
1176-
!AA::isPotentiallyReachable(A, I, *Acc.getRemoteInst(), QueryingAA,
1177-
&ExclusionSet, IsLiveInCalleeCB)))
1171+
1172+
// Check read (RAW) dependences and write (WAR) dependences as necessary.
1173+
// If we successfully excluded all effects we are interested in, the
1174+
// access can be skipped.
1175+
bool ReadChecked = !FindInterferingReads;
1176+
bool WriteChecked = !FindInterferingWrites;
1177+
1178+
// If the instruction cannot reach the access, the former does not
1179+
// interfere with what the access reads.
1180+
if (!ReadChecked) {
1181+
if (!AA::isPotentiallyReachable(A, I, *Acc.getRemoteInst(), QueryingAA,
1182+
&ExclusionSet, IsLiveInCalleeCB))
1183+
ReadChecked = true;
1184+
}
1185+
// If the instruction cannot be reach from the access, the latter does not
1186+
// interfere with what the instruction reads.
1187+
if (!WriteChecked) {
1188+
if (!AA::isPotentiallyReachable(A, *Acc.getRemoteInst(), I, QueryingAA,
1189+
&ExclusionSet, IsLiveInCalleeCB))
1190+
WriteChecked = true;
1191+
}
1192+
1193+
// If we still might be affected by the write of the access but there are
1194+
// dominating writes in the function of the instruction
1195+
// (HasBeenWrittenTo), we can try to reason that the access is overwritten
1196+
// by them. This would have happend above if they are all in the same
1197+
// function, so we only check the inter-procedural case. Effectively, we
1198+
// want to show that there is no call after the dominting write that might
1199+
// reach the access, and when it returns reach the instruction with the
1200+
// updated value. To this end, we iterate all call sites, check if they
1201+
// might reach the instruction without going through another access
1202+
// (ExclusionSet) and at the same time might reach the access. However,
1203+
// that is all part of AAInterFnReachability.
1204+
if (!WriteChecked && HasBeenWrittenTo &&
1205+
Acc.getRemoteInst()->getFunction() != &Scope) {
1206+
1207+
const auto &FnReachabilityAA = A.getAAFor<AAInterFnReachability>(
1208+
QueryingAA, IRPosition::function(Scope), DepClassTy::OPTIONAL);
1209+
1210+
// Without going backwards in the call tree, can we reach the access
1211+
// from the least dominating write. Do not allow to pass the instruction
1212+
// itself either.
1213+
bool Inserted = ExclusionSet.insert(&I).second;
1214+
1215+
if (!FnReachabilityAA.instructionCanReach(
1216+
A, *LeastDominatingWriteInst,
1217+
*Acc.getRemoteInst()->getFunction(), &ExclusionSet))
1218+
WriteChecked = true;
1219+
1220+
if (Inserted)
1221+
ExclusionSet.erase(&I);
1222+
}
1223+
1224+
if (ReadChecked && WriteChecked)
11781225
return true;
11791226

11801227
if (!DT || !UseDominanceReasoning)
11811228
return false;
11821229
if (!DominatingWrites.count(&Acc))
11831230
return false;
1184-
for (const Access *DomAcc : DominatingWrites) {
1185-
assert(Acc.getLocalInst()->getFunction() ==
1186-
DomAcc->getLocalInst()->getFunction() &&
1187-
"Expected dominating writes to be in the same function!");
1188-
1189-
if (DomAcc != &Acc &&
1190-
DT->dominates(Acc.getLocalInst(), DomAcc->getLocalInst())) {
1191-
return true;
1192-
}
1193-
}
1194-
return false;
1231+
return LeastDominatingWriteInst != Acc.getLocalInst();
11951232
};
11961233

1197-
// Run the user callback on all accesses we cannot skip and return if that
1198-
// succeeded for all or not.
1199-
unsigned NumInterferingAccesses = InterferingAccesses.size();
1234+
// Run the user callback on all accesses we cannot skip and return if
1235+
// that succeeded for all or not.
12001236
for (auto &It : InterferingAccesses) {
12011237
if ((!AllInSameNoSyncFn && !IsThreadLocalObj) ||
1202-
NumInterferingAccesses > MaxInterferingAccesses ||
12031238
!CanSkipAccess(*It.first, It.second)) {
12041239
if (!UserCB(*It.first, It.second))
12051240
return false;

llvm/test/Transforms/Attributor/IPConstantProp/2009-09-24-byval-ptr.ll

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -77,15 +77,15 @@ define i32 @unions() nounwind {
7777
; TUNIT-LABEL: define {{[^@]+}}@unions
7878
; TUNIT-SAME: () #[[ATTR0]] {
7979
; TUNIT-NEXT: entry:
80-
; TUNIT-NEXT: [[MYSTR_CAST:%.*]] = bitcast %struct.MYstr* @mystr to i8*
81-
; TUNIT-NEXT: [[TMP0:%.*]] = load i8, i8* [[MYSTR_CAST]], align 8
82-
; TUNIT-NEXT: [[MYSTR_0_1:%.*]] = getelementptr [[STRUCT_MYSTR:%.*]], %struct.MYstr* @mystr, i64 0, i32 1
83-
; TUNIT-NEXT: [[TMP1:%.*]] = load i32, i32* [[MYSTR_0_1]], align 8
84-
; TUNIT-NEXT: call void @vfu1(i8 [[TMP0]], i32 [[TMP1]]) #[[ATTR0]]
8580
; TUNIT-NEXT: [[MYSTR_CAST1:%.*]] = bitcast %struct.MYstr* @mystr to i8*
86-
; TUNIT-NEXT: [[TMP2:%.*]] = load i8, i8* [[MYSTR_CAST1]], align 8
87-
; TUNIT-NEXT: [[MYSTR_0_12:%.*]] = getelementptr [[STRUCT_MYSTR]], %struct.MYstr* @mystr, i64 0, i32 1
88-
; TUNIT-NEXT: [[TMP3:%.*]] = load i32, i32* [[MYSTR_0_12]], align 8
81+
; TUNIT-NEXT: [[TMP0:%.*]] = load i8, i8* [[MYSTR_CAST1]], align 8
82+
; TUNIT-NEXT: [[MYSTR_0_12:%.*]] = getelementptr [[STRUCT_MYSTR:%.*]], %struct.MYstr* @mystr, i64 0, i32 1
83+
; TUNIT-NEXT: [[TMP1:%.*]] = load i32, i32* [[MYSTR_0_12]], align 8
84+
; TUNIT-NEXT: call void @vfu1(i8 [[TMP0]], i32 [[TMP1]]) #[[ATTR0]]
85+
; TUNIT-NEXT: [[MYSTR_CAST:%.*]] = bitcast %struct.MYstr* @mystr to i8*
86+
; TUNIT-NEXT: [[TMP2:%.*]] = load i8, i8* [[MYSTR_CAST]], align 8
87+
; TUNIT-NEXT: [[MYSTR_0_1:%.*]] = getelementptr [[STRUCT_MYSTR]], %struct.MYstr* @mystr, i64 0, i32 1
88+
; TUNIT-NEXT: [[TMP3:%.*]] = load i32, i32* [[MYSTR_0_1]], align 8
8989
; TUNIT-NEXT: [[RESULT:%.*]] = call i32 @vfu2(i8 [[TMP2]], i32 [[TMP3]]) #[[ATTR0]]
9090
; TUNIT-NEXT: ret i32 [[RESULT]]
9191
;

llvm/test/Transforms/Attributor/value-simplify-assume.ll

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -122,20 +122,18 @@ define i1 @keep_assume_4c_nr() norecurse {
122122
; TUNIT-SAME: () #[[ATTR2]] {
123123
; TUNIT-NEXT: [[STACK:%.*]] = alloca i1, align 1
124124
; TUNIT-NEXT: store i1 true, ptr [[STACK]], align 1
125-
; TUNIT-NEXT: [[L4:%.*]] = load i1, ptr [[STACK]], align 1
126-
; TUNIT-NEXT: call void @llvm.assume(i1 noundef [[L4]]) #[[ATTR6]]
125+
; TUNIT-NEXT: call void @llvm.assume(i1 noundef true) #[[ATTR6]]
127126
; TUNIT-NEXT: call void @useI1p(ptr noalias nocapture noundef nonnull dereferenceable(1) [[STACK]])
128-
; TUNIT-NEXT: ret i1 [[L4]]
127+
; TUNIT-NEXT: ret i1 true
129128
;
130129
; CGSCC: Function Attrs: norecurse
131130
; CGSCC-LABEL: define {{[^@]+}}@keep_assume_4c_nr
132131
; CGSCC-SAME: () #[[ATTR2]] {
133132
; CGSCC-NEXT: [[STACK:%.*]] = alloca i1, align 1
134133
; CGSCC-NEXT: store i1 true, ptr [[STACK]], align 1
135-
; CGSCC-NEXT: [[L4:%.*]] = load i1, ptr [[STACK]], align 1
136-
; CGSCC-NEXT: call void @llvm.assume(i1 noundef [[L4]]) #[[ATTR7]]
134+
; CGSCC-NEXT: call void @llvm.assume(i1 noundef true) #[[ATTR7]]
137135
; CGSCC-NEXT: call void @useI1p(ptr noalias nocapture noundef nonnull dereferenceable(1) [[STACK]])
138-
; CGSCC-NEXT: ret i1 [[L4]]
136+
; CGSCC-NEXT: ret i1 true
139137
;
140138
%stack = alloca i1
141139
store i1 true, ptr %stack
@@ -245,23 +243,21 @@ define i1 @keep_assume_4_nr(i1 %arg) norecurse {
245243
;
246244
; TUNIT: Function Attrs: norecurse
247245
; TUNIT-LABEL: define {{[^@]+}}@keep_assume_4_nr
248-
; TUNIT-SAME: (i1 [[ARG:%.*]]) #[[ATTR2]] {
246+
; TUNIT-SAME: (i1 returned [[ARG:%.*]]) #[[ATTR2]] {
249247
; TUNIT-NEXT: [[STACK:%.*]] = alloca i1, align 1
250248
; TUNIT-NEXT: store i1 [[ARG]], ptr [[STACK]], align 1
251-
; TUNIT-NEXT: [[L:%.*]] = load i1, ptr [[STACK]], align 1
252-
; TUNIT-NEXT: call void @llvm.assume(i1 noundef [[L]]) #[[ATTR6]]
249+
; TUNIT-NEXT: call void @llvm.assume(i1 noundef [[ARG]]) #[[ATTR6]]
253250
; TUNIT-NEXT: call void @useI1p(ptr noalias nocapture noundef nonnull dereferenceable(1) [[STACK]])
254-
; TUNIT-NEXT: ret i1 [[L]]
251+
; TUNIT-NEXT: ret i1 [[ARG]]
255252
;
256253
; CGSCC: Function Attrs: norecurse
257254
; CGSCC-LABEL: define {{[^@]+}}@keep_assume_4_nr
258-
; CGSCC-SAME: (i1 [[ARG:%.*]]) #[[ATTR2]] {
255+
; CGSCC-SAME: (i1 returned [[ARG:%.*]]) #[[ATTR2]] {
259256
; CGSCC-NEXT: [[STACK:%.*]] = alloca i1, align 1
260257
; CGSCC-NEXT: store i1 [[ARG]], ptr [[STACK]], align 1
261-
; CGSCC-NEXT: [[L:%.*]] = load i1, ptr [[STACK]], align 1
262-
; CGSCC-NEXT: call void @llvm.assume(i1 noundef [[L]]) #[[ATTR7]]
258+
; CGSCC-NEXT: call void @llvm.assume(i1 noundef [[ARG]]) #[[ATTR7]]
263259
; CGSCC-NEXT: call void @useI1p(ptr noalias nocapture noundef nonnull dereferenceable(1) [[STACK]])
264-
; CGSCC-NEXT: ret i1 [[L]]
260+
; CGSCC-NEXT: ret i1 [[ARG]]
265261
;
266262
%stack = alloca i1
267263
store i1 %arg, ptr %stack
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes --check-globals
2+
; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=3 -S < %s | FileCheck %s --check-prefixes=CHECK,TUNIT
3+
; RUN: opt -aa-pipeline=basic-aa -passes=attributor-cgscc -attributor-manifest-internal -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,CGSCC
4+
5+
declare void @unknown() nocallback
6+
7+
define i32 @many_writes_nosycn(i1 %c0, i1 %c1, i1 %c2) nosync {
8+
; CHECK: Function Attrs: norecurse nosync
9+
; CHECK-LABEL: define {{[^@]+}}@many_writes_nosycn
10+
; CHECK-SAME: (i1 [[C0:%.*]], i1 [[C1:%.*]], i1 [[C2:%.*]]) #[[ATTR1:[0-9]+]] {
11+
; CHECK-NEXT: [[P:%.*]] = alloca i32, align 4
12+
; CHECK-NEXT: call void @unknown()
13+
; CHECK-NEXT: br i1 [[C0]], label [[T0:%.*]], label [[F0:%.*]]
14+
; CHECK: t0:
15+
; CHECK-NEXT: call void @unknown()
16+
; CHECK-NEXT: br i1 [[C1]], label [[T1:%.*]], label [[M1:%.*]]
17+
; CHECK: f0:
18+
; CHECK-NEXT: call void @unknown()
19+
; CHECK-NEXT: br i1 [[C2]], label [[F1:%.*]], label [[M1]]
20+
; CHECK: t1:
21+
; CHECK-NEXT: call void @unknown()
22+
; CHECK-NEXT: store i32 7, ptr [[P]], align 4
23+
; CHECK-NEXT: br label [[M2:%.*]]
24+
; CHECK: f1:
25+
; CHECK-NEXT: call void @unknown()
26+
; CHECK-NEXT: store i32 9, ptr [[P]], align 4
27+
; CHECK-NEXT: br label [[M2]]
28+
; CHECK: m1:
29+
; CHECK-NEXT: call void @unknown()
30+
; CHECK-NEXT: store i32 11, ptr [[P]], align 4
31+
; CHECK-NEXT: br label [[M2]]
32+
; CHECK: m2:
33+
; CHECK-NEXT: call void @unknown()
34+
; CHECK-NEXT: [[L:%.*]] = load i32, ptr [[P]], align 4
35+
; CHECK-NEXT: ret i32 [[L]]
36+
;
37+
%p = alloca i32
38+
store i32 0, ptr %p
39+
call void @unknown()
40+
store i32 1, ptr %p
41+
br i1 %c0, label %t0, label %f0
42+
t0:
43+
store i32 2, ptr %p
44+
call void @unknown()
45+
store i32 3, ptr %p
46+
br i1 %c1, label %t1, label %m1
47+
f0:
48+
store i32 4, ptr %p
49+
call void @unknown()
50+
store i32 5, ptr %p
51+
br i1 %c2, label %f1, label %m1
52+
t1:
53+
store i32 6, ptr %p
54+
call void @unknown()
55+
store i32 7, ptr %p
56+
br label %m2
57+
f1:
58+
store i32 8, ptr %p
59+
call void @unknown()
60+
store i32 9, ptr %p
61+
br label %m2
62+
m1:
63+
store i32 10, ptr %p
64+
call void @unknown()
65+
store i32 11, ptr %p
66+
br label %m2
67+
m2:
68+
call void @unknown()
69+
%l = load i32, ptr %p
70+
ret i32 %l
71+
}
72+
73+
define i32 @many_writes(i1 %c0, i1 %c1, i1 %c2) {
74+
; CHECK: Function Attrs: norecurse
75+
; CHECK-LABEL: define {{[^@]+}}@many_writes
76+
; CHECK-SAME: (i1 [[C0:%.*]], i1 [[C1:%.*]], i1 [[C2:%.*]]) #[[ATTR2:[0-9]+]] {
77+
; CHECK-NEXT: [[P:%.*]] = alloca i32, align 4
78+
; CHECK-NEXT: call void @unknown()
79+
; CHECK-NEXT: br i1 [[C0]], label [[T0:%.*]], label [[F0:%.*]]
80+
; CHECK: t0:
81+
; CHECK-NEXT: call void @unknown()
82+
; CHECK-NEXT: br i1 [[C1]], label [[T1:%.*]], label [[M1:%.*]]
83+
; CHECK: f0:
84+
; CHECK-NEXT: call void @unknown()
85+
; CHECK-NEXT: br i1 [[C2]], label [[F1:%.*]], label [[M1]]
86+
; CHECK: t1:
87+
; CHECK-NEXT: call void @unknown()
88+
; CHECK-NEXT: store i32 7, ptr [[P]], align 4
89+
; CHECK-NEXT: br label [[M2:%.*]]
90+
; CHECK: f1:
91+
; CHECK-NEXT: call void @unknown()
92+
; CHECK-NEXT: store i32 9, ptr [[P]], align 4
93+
; CHECK-NEXT: br label [[M2]]
94+
; CHECK: m1:
95+
; CHECK-NEXT: call void @unknown()
96+
; CHECK-NEXT: store i32 11, ptr [[P]], align 4
97+
; CHECK-NEXT: br label [[M2]]
98+
; CHECK: m2:
99+
; CHECK-NEXT: call void @unknown()
100+
; CHECK-NEXT: [[L:%.*]] = load i32, ptr [[P]], align 4
101+
; CHECK-NEXT: ret i32 [[L]]
102+
;
103+
%p = alloca i32
104+
store i32 0, ptr %p
105+
call void @unknown()
106+
store i32 1, ptr %p
107+
br i1 %c0, label %t0, label %f0
108+
t0:
109+
store i32 2, ptr %p
110+
call void @unknown()
111+
store i32 3, ptr %p
112+
br i1 %c1, label %t1, label %m1
113+
f0:
114+
store i32 4, ptr %p
115+
call void @unknown()
116+
store i32 5, ptr %p
117+
br i1 %c2, label %f1, label %m1
118+
t1:
119+
store i32 6, ptr %p
120+
call void @unknown()
121+
store i32 7, ptr %p
122+
br label %m2
123+
f1:
124+
store i32 8, ptr %p
125+
call void @unknown()
126+
store i32 9, ptr %p
127+
br label %m2
128+
m1:
129+
store i32 10, ptr %p
130+
call void @unknown()
131+
store i32 11, ptr %p
132+
br label %m2
133+
m2:
134+
call void @unknown()
135+
%l = load i32, ptr %p
136+
ret i32 %l
137+
}
138+
;.
139+
; CHECK: attributes #[[ATTR0:[0-9]+]] = { nocallback }
140+
; CHECK: attributes #[[ATTR1]] = { norecurse nosync }
141+
; CHECK: attributes #[[ATTR2]] = { norecurse }
142+
;.
143+
;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
144+
; CGSCC: {{.*}}
145+
; TUNIT: {{.*}}

llvm/test/Transforms/Attributor/value-simplify-pointer-info-struct.ll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes --check-globals
2-
; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=9 -S < %s | FileCheck %s --check-prefixes=CHECK,TUNIT
2+
; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=13 -S < %s | FileCheck %s --check-prefixes=CHECK,TUNIT
33
; RUN: opt -aa-pipeline=basic-aa -passes=attributor-cgscc -attributor-manifest-internal -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,CGSCC
44
;
55
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"

0 commit comments

Comments
 (0)