Skip to content

Commit f275ffa

Browse files
committed
[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.
1 parent ec5bc53 commit f275ffa

File tree

5 files changed

+116
-119
lines changed

5 files changed

+116
-119
lines changed

llvm/include/llvm/IR/Instruction.h

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -881,16 +881,19 @@ 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 isIdenticalToWhenDefined(const Instruction *I,
885+
bool IgnoreAttrs = false) const LLVM_READONLY;
885886

886887
/// When checking for operation equivalence (using isSameOperationAs) it is
887888
/// sometimes useful to ignore certain attributes.
888889
enum OperationEquivalenceFlags {
889890
/// Check for equivalence ignoring load/store alignment.
890-
CompareIgnoringAlignment = 1<<0,
891+
CompareIgnoringAlignment = 1 << 0,
891892
/// Check for equivalence treating a type and a vector of that type
892893
/// as equivalent.
893-
CompareUsingScalarTypes = 1<<1
894+
CompareUsingScalarTypes = 1 << 1,
895+
/// Check for equivalence ignoring callbase attrs.
896+
CompareIgnoringAttrs = 1 << 2,
894897
};
895898

896899
/// This function determines if the specified instruction executes the same
@@ -911,8 +914,8 @@ class Instruction : public User,
911914
/// @returns true if the specific instruction has the same opcde specific
912915
/// characteristics as the current one. Determine if one instruction has the
913916
/// same state as another.
914-
bool hasSameSpecialState(const Instruction *I2,
915-
bool IgnoreAlignment = false) const LLVM_READONLY;
917+
bool hasSameSpecialState(const Instruction *I2, bool IgnoreAlignment = false,
918+
bool IgnoreAttrs = false) const LLVM_READONLY;
916919

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

llvm/lib/IR/Instruction.cpp

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -785,7 +785,8 @@ 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 IgnoreAttrs) const {
789790
auto I1 = this;
790791
assert(I1->getOpcode() == I2->getOpcode() &&
791792
"Can not compare special state of different instructions");
@@ -811,15 +812,18 @@ bool Instruction::hasSameSpecialState(const Instruction *I2,
811812
if (const CallInst *CI = dyn_cast<CallInst>(I1))
812813
return CI->isTailCall() == cast<CallInst>(I2)->isTailCall() &&
813814
CI->getCallingConv() == cast<CallInst>(I2)->getCallingConv() &&
814-
CI->getAttributes() == cast<CallInst>(I2)->getAttributes() &&
815+
(IgnoreAttrs ||
816+
CI->getAttributes() == cast<CallInst>(I2)->getAttributes()) &&
815817
CI->hasIdenticalOperandBundleSchema(*cast<CallInst>(I2));
816818
if (const InvokeInst *CI = dyn_cast<InvokeInst>(I1))
817819
return CI->getCallingConv() == cast<InvokeInst>(I2)->getCallingConv() &&
818-
CI->getAttributes() == cast<InvokeInst>(I2)->getAttributes() &&
820+
(IgnoreAttrs ||
821+
CI->getAttributes() == cast<InvokeInst>(I2)->getAttributes()) &&
819822
CI->hasIdenticalOperandBundleSchema(*cast<InvokeInst>(I2));
820823
if (const CallBrInst *CI = dyn_cast<CallBrInst>(I1))
821824
return CI->getCallingConv() == cast<CallBrInst>(I2)->getCallingConv() &&
822-
CI->getAttributes() == cast<CallBrInst>(I2)->getAttributes() &&
825+
(IgnoreAttrs ||
826+
CI->getAttributes() == cast<CallBrInst>(I2)->getAttributes()) &&
823827
CI->hasIdenticalOperandBundleSchema(*cast<CallBrInst>(I2));
824828
if (const InsertValueInst *IVI = dyn_cast<InsertValueInst>(I1))
825829
return IVI->getIndices() == cast<InsertValueInst>(I2)->getIndices();
@@ -857,10 +861,10 @@ bool Instruction::isIdenticalTo(const Instruction *I) const {
857861
SubclassOptionalData == I->SubclassOptionalData;
858862
}
859863

860-
bool Instruction::isIdenticalToWhenDefined(const Instruction *I) const {
864+
bool Instruction::isIdenticalToWhenDefined(const Instruction *I,
865+
bool IgnoreAttrs) const {
861866
if (getOpcode() != I->getOpcode() ||
862-
getNumOperands() != I->getNumOperands() ||
863-
getType() != I->getType())
867+
getNumOperands() != I->getNumOperands() || getType() != I->getType())
864868
return false;
865869

866870
// If both instructions have no operands, they are identical.
@@ -879,15 +883,16 @@ bool Instruction::isIdenticalToWhenDefined(const Instruction *I) const {
879883
otherPHI->block_begin());
880884
}
881885

882-
return this->hasSameSpecialState(I);
886+
return this->hasSameSpecialState(I, /*IgnoreAlignment=*/false, IgnoreAttrs);
883887
}
884888

885889
// Keep this in sync with FunctionComparator::cmpOperations in
886890
// lib/Transforms/IPO/MergeFunctions.cpp.
887891
bool Instruction::isSameOperationAs(const Instruction *I,
888892
unsigned flags) const {
889893
bool IgnoreAlignment = flags & CompareIgnoringAlignment;
890-
bool UseScalarTypes = flags & CompareUsingScalarTypes;
894+
bool UseScalarTypes = flags & CompareUsingScalarTypes;
895+
bool IgnoreAttrs = flags & CompareIgnoringAttrs;
891896

892897
if (getOpcode() != I->getOpcode() ||
893898
getNumOperands() != I->getNumOperands() ||
@@ -905,7 +910,7 @@ bool Instruction::isSameOperationAs(const Instruction *I,
905910
getOperand(i)->getType() != I->getOperand(i)->getType())
906911
return false;
907912

908-
return this->hasSameSpecialState(I, IgnoreAlignment);
913+
return this->hasSameSpecialState(I, IgnoreAlignment, IgnoreAttrs);
909914
}
910915

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

llvm/lib/Transforms/Utils/SimplifyCFG.cpp

Lines changed: 51 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1592,26 +1592,6 @@ static void hoistLockstepIdenticalDbgVariableRecords(
15921592
}
15931593
}
15941594

1595-
static bool areIdenticalUpToCommutativity(const Instruction *I1,
1596-
const Instruction *I2) {
1597-
if (I1->isIdenticalToWhenDefined(I2))
1598-
return true;
1599-
1600-
if (auto *Cmp1 = dyn_cast<CmpInst>(I1))
1601-
if (auto *Cmp2 = dyn_cast<CmpInst>(I2))
1602-
return Cmp1->getPredicate() == Cmp2->getSwappedPredicate() &&
1603-
Cmp1->getOperand(0) == Cmp2->getOperand(1) &&
1604-
Cmp1->getOperand(1) == Cmp2->getOperand(0);
1605-
1606-
if (I1->isCommutative() && I1->isSameOperationAs(I2)) {
1607-
return I1->getOperand(0) == I2->getOperand(1) &&
1608-
I1->getOperand(1) == I2->getOperand(0) &&
1609-
equal(drop_begin(I1->operands(), 2), drop_begin(I2->operands(), 2));
1610-
}
1611-
1612-
return false;
1613-
}
1614-
16151595
/// If the target supports conditional faulting,
16161596
/// we look for the following pattern:
16171597
/// \code
@@ -1747,6 +1727,36 @@ static bool isSafeCheapLoadStore(const Instruction *I,
17471727
getLoadStoreAlignment(I) < Value::MaximumAlignment;
17481728
}
17491729

1730+
static std::optional<AttributeList> tryIntersectCBAttrs(const CallBase *CB0,
1731+
const CallBase *CB1) {
1732+
AttributeList AL0 = CB0->getAttributes();
1733+
if (CB0 == CB1)
1734+
return AL0;
1735+
1736+
return AL0.intersectWith(CB0->getContext(), CB1->getAttributes());
1737+
}
1738+
1739+
static bool areIdenticalUpToCommutativity(const Instruction *I1,
1740+
const Instruction *I2) {
1741+
if (I1->isIdenticalToWhenDefined(I2, /*IgnoreAttrs=*/true)) {
1742+
if (auto *CB1 = dyn_cast<CallBase>(I1))
1743+
return tryIntersectCBAttrs(CB1, cast<CallBase>(I2)).has_value();
1744+
return true;
1745+
}
1746+
1747+
if (auto *Cmp1 = dyn_cast<CmpInst>(I1))
1748+
if (auto *Cmp2 = dyn_cast<CmpInst>(I2))
1749+
return Cmp1->getPredicate() == Cmp2->getSwappedPredicate() &&
1750+
Cmp1->getOperand(0) == Cmp2->getOperand(1) &&
1751+
Cmp1->getOperand(1) == Cmp2->getOperand(0);
1752+
if (I1->isCommutative() && I1->isSameOperationAs(I2)) {
1753+
return I1->getOperand(0) == I2->getOperand(1) &&
1754+
I1->getOperand(1) == I2->getOperand(0) &&
1755+
equal(drop_begin(I1->operands(), 2), drop_begin(I2->operands(), 2));
1756+
}
1757+
return false;
1758+
}
1759+
17501760
/// Hoist any common code in the successor blocks up into the block. This
17511761
/// function guarantees that BB dominates all successors. If EqTermsOnly is
17521762
/// given, only perform hoisting in case both blocks only contain a terminator.
@@ -1910,6 +1920,14 @@ bool SimplifyCFGOpt::hoistCommonCodeFromSuccessors(Instruction *TI,
19101920
if (!I2->use_empty())
19111921
I2->replaceAllUsesWith(I1);
19121922
I1->andIRFlags(I2);
1923+
if (auto *CB = dyn_cast<CallBase>(I1)) {
1924+
auto IntersectedAttrs = tryIntersectCBAttrs(CB, cast<CallBase>(I2));
1925+
assert(IntersectedAttrs &&
1926+
"We should not be trying to hoist callbases "
1927+
"with non-intersectable attributes");
1928+
CB->setAttributes(*IntersectedAttrs);
1929+
}
1930+
19131931
combineMetadataForCSE(I1, I2, true);
19141932
// I1 and I2 are being combined into a single instruction. Its debug
19151933
// location is the merged locations of the original instructions.
@@ -2130,7 +2148,7 @@ static bool canSinkInstructions(
21302148
const Instruction *I0 = Insts.front();
21312149
const auto I0MMRA = MMRAMetadata(*I0);
21322150
for (auto *I : Insts) {
2133-
if (!I->isSameOperationAs(I0))
2151+
if (!I->isSameOperationAs(I0, Instruction::CompareIgnoringAttrs))
21342152
return false;
21352153

21362154
// swifterror pointers can only be used by a load or store; sinking a load
@@ -2164,7 +2182,7 @@ static bool canSinkInstructions(
21642182
// I.e. if we have two direct calls to different callees, we don't want to
21652183
// turn that into an indirect call. Likewise, if we have an indirect call,
21662184
// and a direct call, we don't actually want to have a single indirect call.
2167-
if (isa<CallBase>(I0)) {
2185+
if (auto *CB = dyn_cast<CallBase>(I0)) {
21682186
auto IsIndirectCall = [](const Instruction *I) {
21692187
return cast<CallBase>(I)->isIndirectCall();
21702188
};
@@ -2184,6 +2202,11 @@ static bool canSinkInstructions(
21842202
return false;
21852203
}
21862204
}
2205+
// Check that we can intersect the attributes if we sink.
2206+
for (const Instruction *I : Insts) {
2207+
if (I != I0 && !tryIntersectCBAttrs(CB, cast<CallBase>(I)))
2208+
return false;
2209+
}
21872210
}
21882211

21892212
for (unsigned OI = 0, OE = I0->getNumOperands(); OI != OE; ++OI) {
@@ -2287,6 +2310,12 @@ static void sinkLastInstruction(ArrayRef<BasicBlock*> Blocks) {
22872310
I0->applyMergedLocation(I0->getDebugLoc(), I->getDebugLoc());
22882311
combineMetadataForCSE(I0, I, true);
22892312
I0->andIRFlags(I);
2313+
if (auto *CB = dyn_cast<CallBase>(I0)) {
2314+
auto IntersectedAttrs = tryIntersectCBAttrs(CB, cast<CallBase>(I));
2315+
assert(IntersectedAttrs && "We should not be trying to sink callbases "
2316+
"with non-intersectable attributes");
2317+
CB->setAttributes(*IntersectedAttrs);
2318+
}
22902319
}
22912320

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

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

Lines changed: 21 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,11 @@ declare void @side.effect()
99
define ptr @test_hoist_int_attrs(i1 %c, ptr %p, i64 %x) {
1010
; CHECK-LABEL: define {{[^@]+}}@test_hoist_int_attrs
1111
; CHECK-SAME: (i1 [[C:%.*]], ptr [[P:%.*]], i64 [[X:%.*]]) {
12-
; CHECK-NEXT: br i1 [[C]], label [[IF:%.*]], label [[ELSE:%.*]]
12+
; CHECK-NEXT: [[R:%.*]] = call ptr @foo(ptr align 32 dereferenceable(50) dereferenceable_or_null(100) [[P]], i64 range(i64 10, 100000) [[X]]) #[[ATTR0:[0-9]+]]
13+
; CHECK-NEXT: br i1 [[C]], label [[COMMON_RET:%.*]], label [[ELSE:%.*]]
1314
; CHECK: common.ret:
14-
; CHECK-NEXT: [[COMMON_RET_OP:%.*]] = phi ptr [ [[R:%.*]], [[IF]] ], [ [[R2:%.*]], [[ELSE]] ]
15-
; CHECK-NEXT: ret ptr [[COMMON_RET_OP]]
16-
; CHECK: if:
17-
; CHECK-NEXT: [[R]] = call ptr @foo(ptr align 64 dereferenceable(50) dereferenceable_or_null(100) [[P]], i64 range(i64 10, 1000) [[X]]) #[[ATTR0:[0-9]+]]
18-
; CHECK-NEXT: br label [[COMMON_RET:%.*]]
15+
; CHECK-NEXT: ret ptr [[R]]
1916
; CHECK: else:
20-
; CHECK-NEXT: [[R2]] = call ptr @foo(ptr align 32 dereferenceable(100) dereferenceable_or_null(200) [[P]], i64 range(i64 10000, 100000) [[X]]) #[[ATTR1:[0-9]+]]
2117
; CHECK-NEXT: call void @side.effect()
2218
; CHECK-NEXT: br label [[COMMON_RET]]
2319
;
@@ -35,15 +31,11 @@ else:
3531
define ptr @test_hoist_int_attrs2(i1 %c, ptr %p, i64 %x) {
3632
; CHECK-LABEL: define {{[^@]+}}@test_hoist_int_attrs2
3733
; CHECK-SAME: (i1 [[C:%.*]], ptr [[P:%.*]], i64 [[X:%.*]]) {
38-
; CHECK-NEXT: br i1 [[C]], label [[IF:%.*]], label [[ELSE:%.*]]
34+
; CHECK-NEXT: [[R:%.*]] = call ptr @foo(ptr dereferenceable(50) [[P]], i64 range(i64 10, 1000) [[X]]) #[[ATTR1:[0-9]+]]
35+
; CHECK-NEXT: br i1 [[C]], label [[COMMON_RET:%.*]], label [[ELSE:%.*]]
3936
; CHECK: common.ret:
40-
; CHECK-NEXT: [[COMMON_RET_OP:%.*]] = phi ptr [ [[R:%.*]], [[IF]] ], [ [[R2:%.*]], [[ELSE]] ]
41-
; CHECK-NEXT: ret ptr [[COMMON_RET_OP]]
42-
; CHECK: if:
43-
; CHECK-NEXT: [[R]] = call ptr @foo(ptr dereferenceable(50) [[P]], i64 range(i64 10, 1000) [[X]]) #[[ATTR0]]
44-
; CHECK-NEXT: br label [[COMMON_RET:%.*]]
37+
; CHECK-NEXT: ret ptr [[R]]
4538
; CHECK: else:
46-
; 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]+]]
4739
; CHECK-NEXT: call void @side.effect()
4840
; CHECK-NEXT: br label [[COMMON_RET]]
4941
;
@@ -61,15 +53,11 @@ else:
6153
define ptr @test_hoist_bool_attrs2(i1 %c, ptr %p, i64 %x) {
6254
; CHECK-LABEL: define {{[^@]+}}@test_hoist_bool_attrs2
6355
; CHECK-SAME: (i1 [[C:%.*]], ptr [[P:%.*]], i64 [[X:%.*]]) {
64-
; CHECK-NEXT: br i1 [[C]], label [[IF:%.*]], label [[ELSE:%.*]]
56+
; CHECK-NEXT: [[R:%.*]] = call noundef ptr @foo(ptr nonnull [[P]], i64 noundef [[X]]) #[[ATTR2:[0-9]+]]
57+
; CHECK-NEXT: br i1 [[C]], label [[COMMON_RET:%.*]], label [[ELSE:%.*]]
6558
; CHECK: common.ret:
66-
; CHECK-NEXT: [[COMMON_RET_OP:%.*]] = phi ptr [ [[R:%.*]], [[IF]] ], [ [[R2:%.*]], [[ELSE]] ]
67-
; CHECK-NEXT: ret ptr [[COMMON_RET_OP]]
68-
; CHECK: if:
69-
; CHECK-NEXT: [[R]] = call noundef ptr @foo(ptr noundef nonnull readnone [[P]], i64 noundef [[X]]) #[[ATTR3:[0-9]+]]
70-
; CHECK-NEXT: br label [[COMMON_RET:%.*]]
59+
; CHECK-NEXT: ret ptr [[R]]
7160
; CHECK: else:
72-
; CHECK-NEXT: [[R2]] = call noundef nonnull ptr @foo(ptr nonnull readonly [[P]], i64 noundef [[X]]) #[[ATTR4:[0-9]+]]
7361
; CHECK-NEXT: call void @side.effect()
7462
; CHECK-NEXT: br label [[COMMON_RET]]
7563
;
@@ -87,15 +75,11 @@ else:
8775
define ptr @test_hoist_bool_attrs3(i1 %c, ptr %p, i64 %x) {
8876
; CHECK-LABEL: define {{[^@]+}}@test_hoist_bool_attrs3
8977
; CHECK-SAME: (i1 [[C:%.*]], ptr [[P:%.*]], i64 [[X:%.*]]) {
90-
; CHECK-NEXT: br i1 [[C]], label [[IF:%.*]], label [[ELSE:%.*]]
78+
; CHECK-NEXT: [[R:%.*]] = call nonnull ptr @foo(ptr [[P]], i64 noundef [[X]]) #[[ATTR3:[0-9]+]]
79+
; CHECK-NEXT: br i1 [[C]], label [[COMMON_RET:%.*]], label [[ELSE:%.*]]
9180
; CHECK: common.ret:
92-
; CHECK-NEXT: [[COMMON_RET_OP:%.*]] = phi ptr [ [[R:%.*]], [[IF]] ], [ [[R2:%.*]], [[ELSE]] ]
93-
; CHECK-NEXT: ret ptr [[COMMON_RET_OP]]
94-
; CHECK: if:
95-
; CHECK-NEXT: [[R]] = call nonnull ptr @foo(ptr noundef readonly [[P]], i64 noundef [[X]]) #[[ATTR5:[0-9]+]]
96-
; CHECK-NEXT: br label [[COMMON_RET:%.*]]
81+
; CHECK-NEXT: ret ptr [[R]]
9782
; CHECK: else:
98-
; CHECK-NEXT: [[R2]] = call noundef nonnull ptr @foo(ptr nonnull writeonly [[P]], i64 noundef [[X]]) #[[ATTR6:[0-9]+]]
9983
; CHECK-NEXT: call void @side.effect()
10084
; CHECK-NEXT: br label [[COMMON_RET]]
10185
;
@@ -118,10 +102,10 @@ define ptr @test_hoist_bool_attrs_fail_non_droppable(i1 %c, ptr %p, i64 %x) {
118102
; CHECK-NEXT: [[COMMON_RET_OP:%.*]] = phi ptr [ [[R:%.*]], [[IF]] ], [ [[R2:%.*]], [[ELSE]] ]
119103
; CHECK-NEXT: ret ptr [[COMMON_RET_OP]]
120104
; CHECK: if:
121-
; CHECK-NEXT: [[R]] = call nonnull ptr @foo(ptr noundef readonly [[P]], i64 noundef [[X]]) #[[ATTR5]]
105+
; CHECK-NEXT: [[R]] = call nonnull ptr @foo(ptr noundef readonly [[P]], i64 noundef [[X]]) #[[ATTR4:[0-9]+]]
122106
; CHECK-NEXT: br label [[COMMON_RET:%.*]]
123107
; CHECK: else:
124-
; CHECK-NEXT: [[R2]] = call noundef nonnull ptr @foo(ptr nonnull writeonly [[P]], i64 noundef [[X]]) #[[ATTR7:[0-9]+]]
108+
; CHECK-NEXT: [[R2]] = call noundef nonnull ptr @foo(ptr nonnull writeonly [[P]], i64 noundef [[X]]) #[[ATTR5:[0-9]+]]
125109
; CHECK-NEXT: call void @side.effect()
126110
; CHECK-NEXT: br label [[COMMON_RET]]
127111
;
@@ -144,10 +128,10 @@ define ptr @test_hoist_bool_attrs_fail_non_droppable2(i1 %c, ptr %p, i64 %x) {
144128
; CHECK-NEXT: [[COMMON_RET_OP:%.*]] = phi ptr [ [[R:%.*]], [[IF]] ], [ [[R2:%.*]], [[ELSE]] ]
145129
; CHECK-NEXT: ret ptr [[COMMON_RET_OP]]
146130
; CHECK: if:
147-
; CHECK-NEXT: [[R]] = call nonnull ptr @foo(ptr noundef readonly [[P]], i64 noundef [[X]]) #[[ATTR8:[0-9]+]]
131+
; CHECK-NEXT: [[R]] = call nonnull ptr @foo(ptr noundef readonly [[P]], i64 noundef [[X]]) #[[ATTR6:[0-9]+]]
148132
; CHECK-NEXT: br label [[COMMON_RET:%.*]]
149133
; CHECK: else:
150-
; CHECK-NEXT: [[R2]] = call noundef nonnull ptr @foo(ptr nonnull writeonly byval(i64) [[P]], i64 noundef [[X]]) #[[ATTR7]]
134+
; CHECK-NEXT: [[R2]] = call noundef nonnull ptr @foo(ptr nonnull writeonly byval(i64) [[P]], i64 noundef [[X]]) #[[ATTR5]]
151135
; CHECK-NEXT: call void @side.effect()
152136
; CHECK-NEXT: br label [[COMMON_RET]]
153137
;
@@ -170,10 +154,10 @@ define ptr @test_hoist_bool_attrs_fail_non_droppable3(i1 %c, ptr %p, i64 %x) {
170154
; CHECK-NEXT: [[COMMON_RET_OP:%.*]] = phi ptr [ [[R:%.*]], [[IF]] ], [ [[R2:%.*]], [[ELSE]] ]
171155
; CHECK-NEXT: ret ptr [[COMMON_RET_OP]]
172156
; CHECK: if:
173-
; CHECK-NEXT: [[R]] = call nonnull ptr @foo(ptr noundef readonly byval(i32) [[P]], i64 noundef [[X]]) #[[ATTR8]]
157+
; CHECK-NEXT: [[R]] = call nonnull ptr @foo(ptr noundef readonly byval(i32) [[P]], i64 noundef [[X]]) #[[ATTR6]]
174158
; CHECK-NEXT: br label [[COMMON_RET:%.*]]
175159
; CHECK: else:
176-
; CHECK-NEXT: [[R2]] = call noundef nonnull ptr @foo(ptr nonnull writeonly byval(i64) [[P]], i64 noundef [[X]]) #[[ATTR7]]
160+
; CHECK-NEXT: [[R2]] = call noundef nonnull ptr @foo(ptr nonnull writeonly byval(i64) [[P]], i64 noundef [[X]]) #[[ATTR5]]
177161
; CHECK-NEXT: call void @side.effect()
178162
; CHECK-NEXT: br label [[COMMON_RET]]
179163
;
@@ -191,15 +175,11 @@ else:
191175
define ptr @test_hoist_bool_attrs4(i1 %c, ptr %p, i64 %x) {
192176
; CHECK-LABEL: define {{[^@]+}}@test_hoist_bool_attrs4
193177
; CHECK-SAME: (i1 [[C:%.*]], ptr [[P:%.*]], i64 [[X:%.*]]) {
194-
; CHECK-NEXT: br i1 [[C]], label [[IF:%.*]], label [[ELSE:%.*]]
178+
; CHECK-NEXT: [[R:%.*]] = call nonnull ptr @foo(ptr byval(i64) [[P]], i64 noundef [[X]]) #[[ATTR5]]
179+
; CHECK-NEXT: br i1 [[C]], label [[COMMON_RET:%.*]], label [[ELSE:%.*]]
195180
; CHECK: common.ret:
196-
; CHECK-NEXT: [[COMMON_RET_OP:%.*]] = phi ptr [ [[R:%.*]], [[IF]] ], [ [[R2:%.*]], [[ELSE]] ]
197-
; CHECK-NEXT: ret ptr [[COMMON_RET_OP]]
198-
; CHECK: if:
199-
; CHECK-NEXT: [[R]] = call nonnull ptr @foo(ptr noundef readonly byval(i64) [[P]], i64 noundef [[X]]) #[[ATTR8]]
200-
; CHECK-NEXT: br label [[COMMON_RET:%.*]]
181+
; CHECK-NEXT: ret ptr [[R]]
201182
; CHECK: else:
202-
; CHECK-NEXT: [[R2]] = call noundef nonnull ptr @foo(ptr nonnull writeonly byval(i64) [[P]], i64 noundef [[X]]) #[[ATTR7]]
203183
; CHECK-NEXT: call void @side.effect()
204184
; CHECK-NEXT: br label [[COMMON_RET]]
205185
;

0 commit comments

Comments
 (0)