Skip to content

Commit 833c127

Browse files
goldsteinnSterling-Augustine
authored andcommitted
[SimplifyCFG] Supporting hoisting/sinking callbases with differing attrs
Some (many) attributes can safely be dropped to enable sinking. For example removing `nonnull` on a return/param can't affect correctness. Closes llvm#109472
1 parent d41d886 commit 833c127

File tree

6 files changed

+121
-118
lines changed

6 files changed

+121
-118
lines changed

llvm/include/llvm/IR/InstrTypes.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1461,6 +1461,22 @@ class CallBase : public Instruction {
14611461
///
14621462
void setAttributes(AttributeList A) { Attrs = A; }
14631463

1464+
/// Try to intersect the attributes from 'this' CallBase and the
1465+
/// 'Other' CallBase. Sets the intersected attributes to 'this' and
1466+
/// return true if successful. Doesn't modify 'this' and returns
1467+
/// false if unsuccessful.
1468+
bool tryIntersectAttributes(const CallBase *Other) {
1469+
if (this == Other)
1470+
return true;
1471+
AttributeList AL = getAttributes();
1472+
AttributeList ALOther = Other->getAttributes();
1473+
auto Intersected = AL.intersectWith(getContext(), ALOther);
1474+
if (!Intersected)
1475+
return false;
1476+
setAttributes(*Intersected);
1477+
return true;
1478+
}
1479+
14641480
/// Determine whether this call has the given attribute. If it does not
14651481
/// then determine if the called function has the attribute, but only if
14661482
/// the attribute is allowed for the call.

llvm/include/llvm/IR/Instruction.h

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -881,16 +881,20 @@ class Instruction : public User,
881881
/// This is like isIdenticalTo, except that it ignores the
882882
/// SubclassOptionalData flags, which may specify conditions under which the
883883
/// instruction's result is undefined.
884-
bool isIdenticalToWhenDefined(const Instruction *I) const LLVM_READONLY;
884+
bool
885+
isIdenticalToWhenDefined(const Instruction *I,
886+
bool IntersectAttrs = false) const LLVM_READONLY;
885887

886888
/// When checking for operation equivalence (using isSameOperationAs) it is
887889
/// sometimes useful to ignore certain attributes.
888890
enum OperationEquivalenceFlags {
889891
/// Check for equivalence ignoring load/store alignment.
890-
CompareIgnoringAlignment = 1<<0,
892+
CompareIgnoringAlignment = 1 << 0,
891893
/// Check for equivalence treating a type and a vector of that type
892894
/// as equivalent.
893-
CompareUsingScalarTypes = 1<<1
895+
CompareUsingScalarTypes = 1 << 1,
896+
/// Check for equivalence with intersected callbase attrs.
897+
CompareUsingIntersectedAttrs = 1 << 2,
894898
};
895899

896900
/// This function determines if the specified instruction executes the same
@@ -911,8 +915,8 @@ class Instruction : public User,
911915
/// @returns true if the specific instruction has the same opcde specific
912916
/// characteristics as the current one. Determine if one instruction has the
913917
/// same state as another.
914-
bool hasSameSpecialState(const Instruction *I2,
915-
bool IgnoreAlignment = false) const LLVM_READONLY;
918+
bool hasSameSpecialState(const Instruction *I2, bool IgnoreAlignment = false,
919+
bool IntersectAttrs = false) const LLVM_READONLY;
916920

917921
/// Return true if there are any uses of this instruction in blocks other than
918922
/// the specified block. Note that PHI nodes are considered to evaluate their

llvm/lib/IR/Instruction.cpp

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -785,11 +785,21 @@ const char *Instruction::getOpcodeName(unsigned OpCode) {
785785
/// This must be kept in sync with FunctionComparator::cmpOperations in
786786
/// lib/Transforms/IPO/MergeFunctions.cpp.
787787
bool Instruction::hasSameSpecialState(const Instruction *I2,
788-
bool IgnoreAlignment) const {
788+
bool IgnoreAlignment,
789+
bool IntersectAttrs) const {
789790
auto I1 = this;
790791
assert(I1->getOpcode() == I2->getOpcode() &&
791792
"Can not compare special state of different instructions");
792793

794+
auto CheckAttrsSame = [IntersectAttrs](const CallBase *CB0,
795+
const CallBase *CB1) {
796+
return IntersectAttrs
797+
? CB0->getAttributes()
798+
.intersectWith(CB0->getContext(), CB1->getAttributes())
799+
.has_value()
800+
: CB0->getAttributes() == CB1->getAttributes();
801+
};
802+
793803
if (const AllocaInst *AI = dyn_cast<AllocaInst>(I1))
794804
return AI->getAllocatedType() == cast<AllocaInst>(I2)->getAllocatedType() &&
795805
(AI->getAlign() == cast<AllocaInst>(I2)->getAlign() ||
@@ -811,15 +821,15 @@ bool Instruction::hasSameSpecialState(const Instruction *I2,
811821
if (const CallInst *CI = dyn_cast<CallInst>(I1))
812822
return CI->isTailCall() == cast<CallInst>(I2)->isTailCall() &&
813823
CI->getCallingConv() == cast<CallInst>(I2)->getCallingConv() &&
814-
CI->getAttributes() == cast<CallInst>(I2)->getAttributes() &&
824+
CheckAttrsSame(CI, cast<CallInst>(I2)) &&
815825
CI->hasIdenticalOperandBundleSchema(*cast<CallInst>(I2));
816826
if (const InvokeInst *CI = dyn_cast<InvokeInst>(I1))
817827
return CI->getCallingConv() == cast<InvokeInst>(I2)->getCallingConv() &&
818-
CI->getAttributes() == cast<InvokeInst>(I2)->getAttributes() &&
828+
CheckAttrsSame(CI, cast<InvokeInst>(I2)) &&
819829
CI->hasIdenticalOperandBundleSchema(*cast<InvokeInst>(I2));
820830
if (const CallBrInst *CI = dyn_cast<CallBrInst>(I1))
821831
return CI->getCallingConv() == cast<CallBrInst>(I2)->getCallingConv() &&
822-
CI->getAttributes() == cast<CallBrInst>(I2)->getAttributes() &&
832+
CheckAttrsSame(CI, cast<CallBrInst>(I2)) &&
823833
CI->hasIdenticalOperandBundleSchema(*cast<CallBrInst>(I2));
824834
if (const InsertValueInst *IVI = dyn_cast<InsertValueInst>(I1))
825835
return IVI->getIndices() == cast<InsertValueInst>(I2)->getIndices();
@@ -857,10 +867,10 @@ bool Instruction::isIdenticalTo(const Instruction *I) const {
857867
SubclassOptionalData == I->SubclassOptionalData;
858868
}
859869

860-
bool Instruction::isIdenticalToWhenDefined(const Instruction *I) const {
870+
bool Instruction::isIdenticalToWhenDefined(const Instruction *I,
871+
bool IntersectAttrs) const {
861872
if (getOpcode() != I->getOpcode() ||
862-
getNumOperands() != I->getNumOperands() ||
863-
getType() != I->getType())
873+
getNumOperands() != I->getNumOperands() || getType() != I->getType())
864874
return false;
865875

866876
// If both instructions have no operands, they are identical.
@@ -879,15 +889,17 @@ bool Instruction::isIdenticalToWhenDefined(const Instruction *I) const {
879889
otherPHI->block_begin());
880890
}
881891

882-
return this->hasSameSpecialState(I);
892+
return this->hasSameSpecialState(I, /*IgnoreAlignment=*/false,
893+
IntersectAttrs);
883894
}
884895

885896
// Keep this in sync with FunctionComparator::cmpOperations in
886897
// lib/Transforms/IPO/MergeFunctions.cpp.
887898
bool Instruction::isSameOperationAs(const Instruction *I,
888899
unsigned flags) const {
889900
bool IgnoreAlignment = flags & CompareIgnoringAlignment;
890-
bool UseScalarTypes = flags & CompareUsingScalarTypes;
901+
bool UseScalarTypes = flags & CompareUsingScalarTypes;
902+
bool IntersectAttrs = flags & CompareUsingIntersectedAttrs;
891903

892904
if (getOpcode() != I->getOpcode() ||
893905
getNumOperands() != I->getNumOperands() ||
@@ -905,7 +917,7 @@ bool Instruction::isSameOperationAs(const Instruction *I,
905917
getOperand(i)->getType() != I->getOperand(i)->getType())
906918
return false;
907919

908-
return this->hasSameSpecialState(I, IgnoreAlignment);
920+
return this->hasSameSpecialState(I, IgnoreAlignment, IntersectAttrs);
909921
}
910922

911923
bool Instruction::isUsedOutsideOfBlock(const BasicBlock *BB) const {

llvm/lib/Transforms/Utils/SimplifyCFG.cpp

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1594,7 +1594,7 @@ static void hoistLockstepIdenticalDbgVariableRecords(
15941594

15951595
static bool areIdenticalUpToCommutativity(const Instruction *I1,
15961596
const Instruction *I2) {
1597-
if (I1->isIdenticalToWhenDefined(I2))
1597+
if (I1->isIdenticalToWhenDefined(I2, /*IntersectAttrs=*/true))
15981598
return true;
15991599

16001600
if (auto *Cmp1 = dyn_cast<CmpInst>(I1))
@@ -1910,6 +1910,14 @@ bool SimplifyCFGOpt::hoistCommonCodeFromSuccessors(Instruction *TI,
19101910
if (!I2->use_empty())
19111911
I2->replaceAllUsesWith(I1);
19121912
I1->andIRFlags(I2);
1913+
if (auto *CB = dyn_cast<CallBase>(I1)) {
1914+
bool Success = CB->tryIntersectAttributes(cast<CallBase>(I2));
1915+
assert(Success && "We should not be trying to hoist callbases "
1916+
"with non-intersectable attributes");
1917+
// For NDEBUG Compile.
1918+
(void)Success;
1919+
}
1920+
19131921
combineMetadataForCSE(I1, I2, true);
19141922
// I1 and I2 are being combined into a single instruction. Its debug
19151923
// location is the merged locations of the original instructions.
@@ -2130,7 +2138,7 @@ static bool canSinkInstructions(
21302138
const Instruction *I0 = Insts.front();
21312139
const auto I0MMRA = MMRAMetadata(*I0);
21322140
for (auto *I : Insts) {
2133-
if (!I->isSameOperationAs(I0))
2141+
if (!I->isSameOperationAs(I0, Instruction::CompareUsingIntersectedAttrs))
21342142
return false;
21352143

21362144
// swifterror pointers can only be used by a load or store; sinking a load
@@ -2287,6 +2295,13 @@ static void sinkLastInstruction(ArrayRef<BasicBlock*> Blocks) {
22872295
I0->applyMergedLocation(I0->getDebugLoc(), I->getDebugLoc());
22882296
combineMetadataForCSE(I0, I, true);
22892297
I0->andIRFlags(I);
2298+
if (auto *CB = dyn_cast<CallBase>(I0)) {
2299+
bool Success = CB->tryIntersectAttributes(cast<CallBase>(I));
2300+
assert(Success && "We should not be trying to sink callbases "
2301+
"with non-intersectable attributes");
2302+
// For NDEBUG Compile.
2303+
(void)Success;
2304+
}
22902305
}
22912306

22922307
for (User *U : make_early_inc_range(I0->users())) {

llvm/test/Transforms/SimplifyCFG/hoist-cb-diff-attrs.ll

Lines changed: 28 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,11 @@ declare void @side.effect()
88
define ptr @test_hoist_int_attrs(i1 %c, ptr %p, ptr %p2, i64 %x) {
99
; CHECK-LABEL: define {{[^@]+}}@test_hoist_int_attrs
1010
; CHECK-SAME: (i1 [[C:%.*]], ptr [[P:%.*]], ptr [[P2:%.*]], i64 [[X:%.*]]) {
11-
; CHECK-NEXT: br i1 [[C]], label [[IF:%.*]], label [[ELSE:%.*]]
11+
; CHECK-NEXT: [[R:%.*]] = call ptr @foo2(ptr align 32 dereferenceable_or_null(100) [[P]], ptr align 32 dereferenceable(50) [[P2]], i64 range(i64 10, 100000) [[X]]) #[[ATTR0:[0-9]+]]
12+
; CHECK-NEXT: br i1 [[C]], label [[COMMON_RET:%.*]], label [[ELSE:%.*]]
1213
; CHECK: common.ret:
13-
; CHECK-NEXT: [[COMMON_RET_OP:%.*]] = phi ptr [ [[R:%.*]], [[IF]] ], [ [[R2:%.*]], [[ELSE]] ]
14-
; CHECK-NEXT: ret ptr [[COMMON_RET_OP]]
15-
; CHECK: if:
16-
; CHECK-NEXT: [[R]] = call ptr @foo2(ptr align 64 dereferenceable_or_null(100) [[P]], ptr align 64 dereferenceable(50) [[P2]], i64 range(i64 10, 1000) [[X]]) #[[ATTR0:[0-9]+]]
17-
; CHECK-NEXT: br label [[COMMON_RET:%.*]]
14+
; CHECK-NEXT: ret ptr [[R]]
1815
; CHECK: else:
19-
; CHECK-NEXT: [[R2]] = call ptr @foo2(ptr align 32 dereferenceable_or_null(200) [[P]], ptr align 32 dereferenceable(100) [[P2]], i64 range(i64 10000, 100000) [[X]]) #[[ATTR1:[0-9]+]]
2016
; CHECK-NEXT: call void @side.effect()
2117
; CHECK-NEXT: br label [[COMMON_RET]]
2218
;
@@ -34,15 +30,11 @@ else:
3430
define ptr @test_hoist_int_attrs2(i1 %c, ptr %p, i64 %x) {
3531
; CHECK-LABEL: define {{[^@]+}}@test_hoist_int_attrs2
3632
; CHECK-SAME: (i1 [[C:%.*]], ptr [[P:%.*]], i64 [[X:%.*]]) {
37-
; CHECK-NEXT: br i1 [[C]], label [[IF:%.*]], label [[ELSE:%.*]]
33+
; CHECK-NEXT: [[R:%.*]] = call ptr @foo(ptr dereferenceable(50) [[P]], i64 range(i64 10, 1000) [[X]]) #[[ATTR1:[0-9]+]]
34+
; CHECK-NEXT: br i1 [[C]], label [[COMMON_RET:%.*]], label [[ELSE:%.*]]
3835
; CHECK: common.ret:
39-
; CHECK-NEXT: [[COMMON_RET_OP:%.*]] = phi ptr [ [[R:%.*]], [[IF]] ], [ [[R2:%.*]], [[ELSE]] ]
40-
; CHECK-NEXT: ret ptr [[COMMON_RET_OP]]
41-
; CHECK: if:
42-
; CHECK-NEXT: [[R]] = call ptr @foo(ptr dereferenceable(50) [[P]], i64 range(i64 10, 1000) [[X]]) #[[ATTR0]]
43-
; CHECK-NEXT: br label [[COMMON_RET:%.*]]
36+
; CHECK-NEXT: ret ptr [[R]]
4437
; CHECK: else:
45-
; CHECK-NEXT: [[R2]] = call ptr @foo(ptr align 32 dereferenceable(100) dereferenceable_or_null(200) [[P]], i64 range(i64 11, 100) [[X]]) #[[ATTR2:[0-9]+]]
4638
; CHECK-NEXT: call void @side.effect()
4739
; CHECK-NEXT: br label [[COMMON_RET]]
4840
;
@@ -60,15 +52,11 @@ else:
6052
define ptr @test_hoist_bool_attrs2(i1 %c, ptr %p, i64 %x) {
6153
; CHECK-LABEL: define {{[^@]+}}@test_hoist_bool_attrs2
6254
; CHECK-SAME: (i1 [[C:%.*]], ptr [[P:%.*]], i64 [[X:%.*]]) {
63-
; CHECK-NEXT: br i1 [[C]], label [[IF:%.*]], label [[ELSE:%.*]]
55+
; CHECK-NEXT: [[R:%.*]] = call noundef ptr @foo(ptr nonnull [[P]], i64 noundef [[X]]) #[[ATTR2:[0-9]+]]
56+
; CHECK-NEXT: br i1 [[C]], label [[COMMON_RET:%.*]], label [[ELSE:%.*]]
6457
; CHECK: common.ret:
65-
; CHECK-NEXT: [[COMMON_RET_OP:%.*]] = phi ptr [ [[R:%.*]], [[IF]] ], [ [[R2:%.*]], [[ELSE]] ]
66-
; CHECK-NEXT: ret ptr [[COMMON_RET_OP]]
67-
; CHECK: if:
68-
; CHECK-NEXT: [[R]] = call noundef ptr @foo(ptr noundef nonnull readnone [[P]], i64 noundef [[X]]) #[[ATTR3:[0-9]+]]
69-
; CHECK-NEXT: br label [[COMMON_RET:%.*]]
58+
; CHECK-NEXT: ret ptr [[R]]
7059
; CHECK: else:
71-
; CHECK-NEXT: [[R2]] = call noundef nonnull ptr @foo(ptr nonnull readonly [[P]], i64 noundef [[X]]) #[[ATTR4:[0-9]+]]
7260
; CHECK-NEXT: call void @side.effect()
7361
; CHECK-NEXT: br label [[COMMON_RET]]
7462
;
@@ -86,15 +74,11 @@ else:
8674
define ptr @test_hoist_bool_attrs3(i1 %c, ptr %p, i64 %x) {
8775
; CHECK-LABEL: define {{[^@]+}}@test_hoist_bool_attrs3
8876
; CHECK-SAME: (i1 [[C:%.*]], ptr [[P:%.*]], i64 [[X:%.*]]) {
89-
; CHECK-NEXT: br i1 [[C]], label [[IF:%.*]], label [[ELSE:%.*]]
77+
; CHECK-NEXT: [[R:%.*]] = call nonnull ptr @foo(ptr [[P]], i64 noundef [[X]]) #[[ATTR3:[0-9]+]]
78+
; CHECK-NEXT: br i1 [[C]], label [[COMMON_RET:%.*]], label [[ELSE:%.*]]
9079
; CHECK: common.ret:
91-
; CHECK-NEXT: [[COMMON_RET_OP:%.*]] = phi ptr [ [[R:%.*]], [[IF]] ], [ [[R2:%.*]], [[ELSE]] ]
92-
; CHECK-NEXT: ret ptr [[COMMON_RET_OP]]
93-
; CHECK: if:
94-
; CHECK-NEXT: [[R]] = call nonnull ptr @foo(ptr noundef readonly [[P]], i64 noundef [[X]]) #[[ATTR5:[0-9]+]]
95-
; CHECK-NEXT: br label [[COMMON_RET:%.*]]
80+
; CHECK-NEXT: ret ptr [[R]]
9681
; CHECK: else:
97-
; CHECK-NEXT: [[R2]] = call noundef nonnull ptr @foo(ptr nonnull writeonly [[P]], i64 noundef [[X]]) #[[ATTR6:[0-9]+]]
9882
; CHECK-NEXT: call void @side.effect()
9983
; CHECK-NEXT: br label [[COMMON_RET]]
10084
;
@@ -117,10 +101,10 @@ define ptr @test_hoist_bool_attrs_fail_non_droppable(i1 %c, ptr %p, i64 %x) {
117101
; CHECK-NEXT: [[COMMON_RET_OP:%.*]] = phi ptr [ [[R:%.*]], [[IF]] ], [ [[R2:%.*]], [[ELSE]] ]
118102
; CHECK-NEXT: ret ptr [[COMMON_RET_OP]]
119103
; CHECK: if:
120-
; CHECK-NEXT: [[R]] = call nonnull ptr @foo(ptr noundef readonly [[P]], i64 noundef [[X]]) #[[ATTR5]]
104+
; CHECK-NEXT: [[R]] = call nonnull ptr @foo(ptr noundef readonly [[P]], i64 noundef [[X]]) #[[ATTR4:[0-9]+]]
121105
; CHECK-NEXT: br label [[COMMON_RET:%.*]]
122106
; CHECK: else:
123-
; CHECK-NEXT: [[R2]] = call noundef nonnull ptr @foo(ptr nonnull writeonly [[P]], i64 noundef [[X]]) #[[ATTR7:[0-9]+]]
107+
; CHECK-NEXT: [[R2]] = call noundef nonnull ptr @foo(ptr nonnull writeonly [[P]], i64 noundef [[X]]) #[[ATTR5:[0-9]+]]
124108
; CHECK-NEXT: call void @side.effect()
125109
; CHECK-NEXT: br label [[COMMON_RET]]
126110
;
@@ -143,10 +127,10 @@ define ptr @test_hoist_bool_attrs_fail_non_droppable2(i1 %c, ptr %p, i64 %x) {
143127
; CHECK-NEXT: [[COMMON_RET_OP:%.*]] = phi ptr [ [[R:%.*]], [[IF]] ], [ [[R2:%.*]], [[ELSE]] ]
144128
; CHECK-NEXT: ret ptr [[COMMON_RET_OP]]
145129
; CHECK: if:
146-
; CHECK-NEXT: [[R]] = call nonnull ptr @foo(ptr noundef readonly [[P]], i64 noundef [[X]]) #[[ATTR8:[0-9]+]]
130+
; CHECK-NEXT: [[R]] = call nonnull ptr @foo(ptr noundef readonly [[P]], i64 noundef [[X]]) #[[ATTR6:[0-9]+]]
147131
; CHECK-NEXT: br label [[COMMON_RET:%.*]]
148132
; CHECK: else:
149-
; CHECK-NEXT: [[R2]] = call noundef nonnull ptr @foo(ptr nonnull writeonly byval(i64) [[P]], i64 noundef [[X]]) #[[ATTR7]]
133+
; CHECK-NEXT: [[R2]] = call noundef nonnull ptr @foo(ptr nonnull writeonly byval(i64) [[P]], i64 noundef [[X]]) #[[ATTR5]]
150134
; CHECK-NEXT: call void @side.effect()
151135
; CHECK-NEXT: br label [[COMMON_RET]]
152136
;
@@ -169,10 +153,10 @@ define ptr @test_hoist_bool_attrs_fail_non_droppable3(i1 %c, ptr %p, i64 %x) {
169153
; CHECK-NEXT: [[COMMON_RET_OP:%.*]] = phi ptr [ [[R:%.*]], [[IF]] ], [ [[R2:%.*]], [[ELSE]] ]
170154
; CHECK-NEXT: ret ptr [[COMMON_RET_OP]]
171155
; CHECK: if:
172-
; CHECK-NEXT: [[R]] = call nonnull ptr @foo(ptr noundef readonly byval(i32) [[P]], i64 noundef [[X]]) #[[ATTR8]]
156+
; CHECK-NEXT: [[R]] = call nonnull ptr @foo(ptr noundef readonly byval(i32) [[P]], i64 noundef [[X]]) #[[ATTR6]]
173157
; CHECK-NEXT: br label [[COMMON_RET:%.*]]
174158
; CHECK: else:
175-
; CHECK-NEXT: [[R2]] = call noundef nonnull ptr @foo(ptr nonnull writeonly byval(i64) [[P]], i64 noundef [[X]]) #[[ATTR7]]
159+
; CHECK-NEXT: [[R2]] = call noundef nonnull ptr @foo(ptr nonnull writeonly byval(i64) [[P]], i64 noundef [[X]]) #[[ATTR5]]
176160
; CHECK-NEXT: call void @side.effect()
177161
; CHECK-NEXT: br label [[COMMON_RET]]
178162
;
@@ -190,15 +174,11 @@ else:
190174
define ptr @test_hoist_bool_attrs4(i1 %c, ptr %p, i64 %x) {
191175
; CHECK-LABEL: define {{[^@]+}}@test_hoist_bool_attrs4
192176
; CHECK-SAME: (i1 [[C:%.*]], ptr [[P:%.*]], i64 [[X:%.*]]) {
193-
; CHECK-NEXT: br i1 [[C]], label [[IF:%.*]], label [[ELSE:%.*]]
177+
; CHECK-NEXT: [[R:%.*]] = call nonnull ptr @foo(ptr byval(i64) [[P]], i64 noundef [[X]]) #[[ATTR5]]
178+
; CHECK-NEXT: br i1 [[C]], label [[COMMON_RET:%.*]], label [[ELSE:%.*]]
194179
; CHECK: common.ret:
195-
; CHECK-NEXT: [[COMMON_RET_OP:%.*]] = phi ptr [ [[R:%.*]], [[IF]] ], [ [[R2:%.*]], [[ELSE]] ]
196-
; CHECK-NEXT: ret ptr [[COMMON_RET_OP]]
197-
; CHECK: if:
198-
; CHECK-NEXT: [[R]] = call nonnull ptr @foo(ptr noundef readonly byval(i64) [[P]], i64 noundef [[X]]) #[[ATTR8]]
199-
; CHECK-NEXT: br label [[COMMON_RET:%.*]]
180+
; CHECK-NEXT: ret ptr [[R]]
200181
; CHECK: else:
201-
; CHECK-NEXT: [[R2]] = call noundef nonnull ptr @foo(ptr nonnull writeonly byval(i64) [[P]], i64 noundef [[X]]) #[[ATTR7]]
202182
; CHECK-NEXT: call void @side.effect()
203183
; CHECK-NEXT: br label [[COMMON_RET]]
204184
;
@@ -214,13 +194,11 @@ else:
214194
}
215195

216196
;.
217-
; CHECK: attributes #[[ATTR0]] = { memory(read) }
218-
; CHECK: attributes #[[ATTR1]] = { memory(write) }
219-
; CHECK: attributes #[[ATTR2]] = { memory(none) }
220-
; CHECK: attributes #[[ATTR3]] = { cold mustprogress nocallback nofree nosync willreturn }
221-
; CHECK: attributes #[[ATTR4]] = { mustprogress nocallback nofree willreturn }
222-
; CHECK: attributes #[[ATTR5]] = { alwaysinline cold nocallback nofree nosync willreturn }
223-
; CHECK: attributes #[[ATTR6]] = { alwaysinline nosync willreturn }
224-
; CHECK: attributes #[[ATTR7]] = { nosync willreturn }
225-
; CHECK: attributes #[[ATTR8]] = { cold nocallback nofree nosync willreturn }
197+
; CHECK: attributes #[[ATTR0]] = { memory(readwrite) }
198+
; CHECK: attributes #[[ATTR1]] = { memory(read) }
199+
; CHECK: attributes #[[ATTR2]] = { mustprogress nocallback nofree willreturn }
200+
; CHECK: attributes #[[ATTR3]] = { alwaysinline nosync willreturn }
201+
; CHECK: attributes #[[ATTR4]] = { alwaysinline cold nocallback nofree nosync willreturn }
202+
; CHECK: attributes #[[ATTR5]] = { nosync willreturn }
203+
; CHECK: attributes #[[ATTR6]] = { cold nocallback nofree nosync willreturn }
226204
;.

0 commit comments

Comments
 (0)