mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-29 10:26:06 +00:00
[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 #109472
This commit is contained in:
parent
c42659417f
commit
4d4beeb43c
@ -1461,6 +1461,22 @@ public:
|
||||
///
|
||||
void setAttributes(AttributeList A) { Attrs = A; }
|
||||
|
||||
/// Try to intersect the attributes from 'this' CallBase and the
|
||||
/// 'Other' CallBase. Sets the intersected attributes to 'this' and
|
||||
/// return true if successful. Doesn't modify 'this' and returns
|
||||
/// false if unsuccessful.
|
||||
bool tryIntersectAttributes(const CallBase *Other) {
|
||||
if (this == Other)
|
||||
return true;
|
||||
AttributeList AL = getAttributes();
|
||||
AttributeList ALOther = Other->getAttributes();
|
||||
auto Intersected = AL.intersectWith(getContext(), ALOther);
|
||||
if (!Intersected)
|
||||
return false;
|
||||
setAttributes(*Intersected);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Determine whether this call has the given attribute. If it does not
|
||||
/// then determine if the called function has the attribute, but only if
|
||||
/// the attribute is allowed for the call.
|
||||
|
@ -881,16 +881,20 @@ public:
|
||||
/// This is like isIdenticalTo, except that it ignores the
|
||||
/// SubclassOptionalData flags, which may specify conditions under which the
|
||||
/// instruction's result is undefined.
|
||||
bool isIdenticalToWhenDefined(const Instruction *I) const LLVM_READONLY;
|
||||
bool
|
||||
isIdenticalToWhenDefined(const Instruction *I,
|
||||
bool IntersectAttrs = false) const LLVM_READONLY;
|
||||
|
||||
/// When checking for operation equivalence (using isSameOperationAs) it is
|
||||
/// sometimes useful to ignore certain attributes.
|
||||
enum OperationEquivalenceFlags {
|
||||
/// Check for equivalence ignoring load/store alignment.
|
||||
CompareIgnoringAlignment = 1<<0,
|
||||
CompareIgnoringAlignment = 1 << 0,
|
||||
/// Check for equivalence treating a type and a vector of that type
|
||||
/// as equivalent.
|
||||
CompareUsingScalarTypes = 1<<1
|
||||
CompareUsingScalarTypes = 1 << 1,
|
||||
/// Check for equivalence with intersected callbase attrs.
|
||||
CompareUsingIntersectedAttrs = 1 << 2,
|
||||
};
|
||||
|
||||
/// This function determines if the specified instruction executes the same
|
||||
@ -911,8 +915,8 @@ public:
|
||||
/// @returns true if the specific instruction has the same opcde specific
|
||||
/// characteristics as the current one. Determine if one instruction has the
|
||||
/// same state as another.
|
||||
bool hasSameSpecialState(const Instruction *I2,
|
||||
bool IgnoreAlignment = false) const LLVM_READONLY;
|
||||
bool hasSameSpecialState(const Instruction *I2, bool IgnoreAlignment = false,
|
||||
bool IntersectAttrs = false) const LLVM_READONLY;
|
||||
|
||||
/// Return true if there are any uses of this instruction in blocks other than
|
||||
/// the specified block. Note that PHI nodes are considered to evaluate their
|
||||
|
@ -785,11 +785,21 @@ const char *Instruction::getOpcodeName(unsigned OpCode) {
|
||||
/// This must be kept in sync with FunctionComparator::cmpOperations in
|
||||
/// lib/Transforms/IPO/MergeFunctions.cpp.
|
||||
bool Instruction::hasSameSpecialState(const Instruction *I2,
|
||||
bool IgnoreAlignment) const {
|
||||
bool IgnoreAlignment,
|
||||
bool IntersectAttrs) const {
|
||||
auto I1 = this;
|
||||
assert(I1->getOpcode() == I2->getOpcode() &&
|
||||
"Can not compare special state of different instructions");
|
||||
|
||||
auto CheckAttrsSame = [IntersectAttrs](const CallBase *CB0,
|
||||
const CallBase *CB1) {
|
||||
return IntersectAttrs
|
||||
? CB0->getAttributes()
|
||||
.intersectWith(CB0->getContext(), CB1->getAttributes())
|
||||
.has_value()
|
||||
: CB0->getAttributes() == CB1->getAttributes();
|
||||
};
|
||||
|
||||
if (const AllocaInst *AI = dyn_cast<AllocaInst>(I1))
|
||||
return AI->getAllocatedType() == cast<AllocaInst>(I2)->getAllocatedType() &&
|
||||
(AI->getAlign() == cast<AllocaInst>(I2)->getAlign() ||
|
||||
@ -811,15 +821,15 @@ bool Instruction::hasSameSpecialState(const Instruction *I2,
|
||||
if (const CallInst *CI = dyn_cast<CallInst>(I1))
|
||||
return CI->isTailCall() == cast<CallInst>(I2)->isTailCall() &&
|
||||
CI->getCallingConv() == cast<CallInst>(I2)->getCallingConv() &&
|
||||
CI->getAttributes() == cast<CallInst>(I2)->getAttributes() &&
|
||||
CheckAttrsSame(CI, cast<CallInst>(I2)) &&
|
||||
CI->hasIdenticalOperandBundleSchema(*cast<CallInst>(I2));
|
||||
if (const InvokeInst *CI = dyn_cast<InvokeInst>(I1))
|
||||
return CI->getCallingConv() == cast<InvokeInst>(I2)->getCallingConv() &&
|
||||
CI->getAttributes() == cast<InvokeInst>(I2)->getAttributes() &&
|
||||
CheckAttrsSame(CI, cast<InvokeInst>(I2)) &&
|
||||
CI->hasIdenticalOperandBundleSchema(*cast<InvokeInst>(I2));
|
||||
if (const CallBrInst *CI = dyn_cast<CallBrInst>(I1))
|
||||
return CI->getCallingConv() == cast<CallBrInst>(I2)->getCallingConv() &&
|
||||
CI->getAttributes() == cast<CallBrInst>(I2)->getAttributes() &&
|
||||
CheckAttrsSame(CI, cast<CallBrInst>(I2)) &&
|
||||
CI->hasIdenticalOperandBundleSchema(*cast<CallBrInst>(I2));
|
||||
if (const InsertValueInst *IVI = dyn_cast<InsertValueInst>(I1))
|
||||
return IVI->getIndices() == cast<InsertValueInst>(I2)->getIndices();
|
||||
@ -857,10 +867,10 @@ bool Instruction::isIdenticalTo(const Instruction *I) const {
|
||||
SubclassOptionalData == I->SubclassOptionalData;
|
||||
}
|
||||
|
||||
bool Instruction::isIdenticalToWhenDefined(const Instruction *I) const {
|
||||
bool Instruction::isIdenticalToWhenDefined(const Instruction *I,
|
||||
bool IntersectAttrs) const {
|
||||
if (getOpcode() != I->getOpcode() ||
|
||||
getNumOperands() != I->getNumOperands() ||
|
||||
getType() != I->getType())
|
||||
getNumOperands() != I->getNumOperands() || getType() != I->getType())
|
||||
return false;
|
||||
|
||||
// If both instructions have no operands, they are identical.
|
||||
@ -879,7 +889,8 @@ bool Instruction::isIdenticalToWhenDefined(const Instruction *I) const {
|
||||
otherPHI->block_begin());
|
||||
}
|
||||
|
||||
return this->hasSameSpecialState(I);
|
||||
return this->hasSameSpecialState(I, /*IgnoreAlignment=*/false,
|
||||
IntersectAttrs);
|
||||
}
|
||||
|
||||
// Keep this in sync with FunctionComparator::cmpOperations in
|
||||
@ -887,7 +898,8 @@ bool Instruction::isIdenticalToWhenDefined(const Instruction *I) const {
|
||||
bool Instruction::isSameOperationAs(const Instruction *I,
|
||||
unsigned flags) const {
|
||||
bool IgnoreAlignment = flags & CompareIgnoringAlignment;
|
||||
bool UseScalarTypes = flags & CompareUsingScalarTypes;
|
||||
bool UseScalarTypes = flags & CompareUsingScalarTypes;
|
||||
bool IntersectAttrs = flags & CompareUsingIntersectedAttrs;
|
||||
|
||||
if (getOpcode() != I->getOpcode() ||
|
||||
getNumOperands() != I->getNumOperands() ||
|
||||
@ -905,7 +917,7 @@ bool Instruction::isSameOperationAs(const Instruction *I,
|
||||
getOperand(i)->getType() != I->getOperand(i)->getType())
|
||||
return false;
|
||||
|
||||
return this->hasSameSpecialState(I, IgnoreAlignment);
|
||||
return this->hasSameSpecialState(I, IgnoreAlignment, IntersectAttrs);
|
||||
}
|
||||
|
||||
bool Instruction::isUsedOutsideOfBlock(const BasicBlock *BB) const {
|
||||
|
@ -1594,7 +1594,7 @@ static void hoistLockstepIdenticalDbgVariableRecords(
|
||||
|
||||
static bool areIdenticalUpToCommutativity(const Instruction *I1,
|
||||
const Instruction *I2) {
|
||||
if (I1->isIdenticalToWhenDefined(I2))
|
||||
if (I1->isIdenticalToWhenDefined(I2, /*IntersectAttrs=*/true))
|
||||
return true;
|
||||
|
||||
if (auto *Cmp1 = dyn_cast<CmpInst>(I1))
|
||||
@ -1910,6 +1910,14 @@ bool SimplifyCFGOpt::hoistCommonCodeFromSuccessors(Instruction *TI,
|
||||
if (!I2->use_empty())
|
||||
I2->replaceAllUsesWith(I1);
|
||||
I1->andIRFlags(I2);
|
||||
if (auto *CB = dyn_cast<CallBase>(I1)) {
|
||||
bool Success = CB->tryIntersectAttributes(cast<CallBase>(I2));
|
||||
assert(Success && "We should not be trying to hoist callbases "
|
||||
"with non-intersectable attributes");
|
||||
// For NDEBUG Compile.
|
||||
(void)Success;
|
||||
}
|
||||
|
||||
combineMetadataForCSE(I1, I2, true);
|
||||
// I1 and I2 are being combined into a single instruction. Its debug
|
||||
// location is the merged locations of the original instructions.
|
||||
@ -2130,7 +2138,7 @@ static bool canSinkInstructions(
|
||||
const Instruction *I0 = Insts.front();
|
||||
const auto I0MMRA = MMRAMetadata(*I0);
|
||||
for (auto *I : Insts) {
|
||||
if (!I->isSameOperationAs(I0))
|
||||
if (!I->isSameOperationAs(I0, Instruction::CompareUsingIntersectedAttrs))
|
||||
return false;
|
||||
|
||||
// swifterror pointers can only be used by a load or store; sinking a load
|
||||
@ -2287,6 +2295,13 @@ static void sinkLastInstruction(ArrayRef<BasicBlock*> Blocks) {
|
||||
I0->applyMergedLocation(I0->getDebugLoc(), I->getDebugLoc());
|
||||
combineMetadataForCSE(I0, I, true);
|
||||
I0->andIRFlags(I);
|
||||
if (auto *CB = dyn_cast<CallBase>(I0)) {
|
||||
bool Success = CB->tryIntersectAttributes(cast<CallBase>(I));
|
||||
assert(Success && "We should not be trying to sink callbases "
|
||||
"with non-intersectable attributes");
|
||||
// For NDEBUG Compile.
|
||||
(void)Success;
|
||||
}
|
||||
}
|
||||
|
||||
for (User *U : make_early_inc_range(I0->users())) {
|
||||
|
@ -8,15 +8,11 @@ declare void @side.effect()
|
||||
define ptr @test_hoist_int_attrs(i1 %c, ptr %p, ptr %p2, i64 %x) {
|
||||
; CHECK-LABEL: define {{[^@]+}}@test_hoist_int_attrs
|
||||
; CHECK-SAME: (i1 [[C:%.*]], ptr [[P:%.*]], ptr [[P2:%.*]], i64 [[X:%.*]]) {
|
||||
; CHECK-NEXT: br i1 [[C]], label [[IF:%.*]], label [[ELSE:%.*]]
|
||||
; 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]+]]
|
||||
; CHECK-NEXT: br i1 [[C]], label [[COMMON_RET:%.*]], label [[ELSE:%.*]]
|
||||
; CHECK: common.ret:
|
||||
; CHECK-NEXT: [[COMMON_RET_OP:%.*]] = phi ptr [ [[R:%.*]], [[IF]] ], [ [[R2:%.*]], [[ELSE]] ]
|
||||
; CHECK-NEXT: ret ptr [[COMMON_RET_OP]]
|
||||
; CHECK: if:
|
||||
; 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]+]]
|
||||
; CHECK-NEXT: br label [[COMMON_RET:%.*]]
|
||||
; CHECK-NEXT: ret ptr [[R]]
|
||||
; CHECK: else:
|
||||
; 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]+]]
|
||||
; CHECK-NEXT: call void @side.effect()
|
||||
; CHECK-NEXT: br label [[COMMON_RET]]
|
||||
;
|
||||
@ -34,15 +30,11 @@ else:
|
||||
define ptr @test_hoist_int_attrs2(i1 %c, ptr %p, i64 %x) {
|
||||
; CHECK-LABEL: define {{[^@]+}}@test_hoist_int_attrs2
|
||||
; CHECK-SAME: (i1 [[C:%.*]], ptr [[P:%.*]], i64 [[X:%.*]]) {
|
||||
; CHECK-NEXT: br i1 [[C]], label [[IF:%.*]], label [[ELSE:%.*]]
|
||||
; CHECK-NEXT: [[R:%.*]] = call ptr @foo(ptr dereferenceable(50) [[P]], i64 range(i64 10, 1000) [[X]]) #[[ATTR1:[0-9]+]]
|
||||
; CHECK-NEXT: br i1 [[C]], label [[COMMON_RET:%.*]], label [[ELSE:%.*]]
|
||||
; CHECK: common.ret:
|
||||
; CHECK-NEXT: [[COMMON_RET_OP:%.*]] = phi ptr [ [[R:%.*]], [[IF]] ], [ [[R2:%.*]], [[ELSE]] ]
|
||||
; CHECK-NEXT: ret ptr [[COMMON_RET_OP]]
|
||||
; CHECK: if:
|
||||
; CHECK-NEXT: [[R]] = call ptr @foo(ptr dereferenceable(50) [[P]], i64 range(i64 10, 1000) [[X]]) #[[ATTR0]]
|
||||
; CHECK-NEXT: br label [[COMMON_RET:%.*]]
|
||||
; CHECK-NEXT: ret ptr [[R]]
|
||||
; CHECK: else:
|
||||
; 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]+]]
|
||||
; CHECK-NEXT: call void @side.effect()
|
||||
; CHECK-NEXT: br label [[COMMON_RET]]
|
||||
;
|
||||
@ -60,15 +52,11 @@ else:
|
||||
define ptr @test_hoist_bool_attrs2(i1 %c, ptr %p, i64 %x) {
|
||||
; CHECK-LABEL: define {{[^@]+}}@test_hoist_bool_attrs2
|
||||
; CHECK-SAME: (i1 [[C:%.*]], ptr [[P:%.*]], i64 [[X:%.*]]) {
|
||||
; CHECK-NEXT: br i1 [[C]], label [[IF:%.*]], label [[ELSE:%.*]]
|
||||
; CHECK-NEXT: [[R:%.*]] = call noundef ptr @foo(ptr nonnull [[P]], i64 noundef [[X]]) #[[ATTR2:[0-9]+]]
|
||||
; CHECK-NEXT: br i1 [[C]], label [[COMMON_RET:%.*]], label [[ELSE:%.*]]
|
||||
; CHECK: common.ret:
|
||||
; CHECK-NEXT: [[COMMON_RET_OP:%.*]] = phi ptr [ [[R:%.*]], [[IF]] ], [ [[R2:%.*]], [[ELSE]] ]
|
||||
; CHECK-NEXT: ret ptr [[COMMON_RET_OP]]
|
||||
; CHECK: if:
|
||||
; CHECK-NEXT: [[R]] = call noundef ptr @foo(ptr noundef nonnull readnone [[P]], i64 noundef [[X]]) #[[ATTR3:[0-9]+]]
|
||||
; CHECK-NEXT: br label [[COMMON_RET:%.*]]
|
||||
; CHECK-NEXT: ret ptr [[R]]
|
||||
; CHECK: else:
|
||||
; CHECK-NEXT: [[R2]] = call noundef nonnull ptr @foo(ptr nonnull readonly [[P]], i64 noundef [[X]]) #[[ATTR4:[0-9]+]]
|
||||
; CHECK-NEXT: call void @side.effect()
|
||||
; CHECK-NEXT: br label [[COMMON_RET]]
|
||||
;
|
||||
@ -86,15 +74,11 @@ else:
|
||||
define ptr @test_hoist_bool_attrs3(i1 %c, ptr %p, i64 %x) {
|
||||
; CHECK-LABEL: define {{[^@]+}}@test_hoist_bool_attrs3
|
||||
; CHECK-SAME: (i1 [[C:%.*]], ptr [[P:%.*]], i64 [[X:%.*]]) {
|
||||
; CHECK-NEXT: br i1 [[C]], label [[IF:%.*]], label [[ELSE:%.*]]
|
||||
; CHECK-NEXT: [[R:%.*]] = call nonnull ptr @foo(ptr [[P]], i64 noundef [[X]]) #[[ATTR3:[0-9]+]]
|
||||
; CHECK-NEXT: br i1 [[C]], label [[COMMON_RET:%.*]], label [[ELSE:%.*]]
|
||||
; CHECK: common.ret:
|
||||
; CHECK-NEXT: [[COMMON_RET_OP:%.*]] = phi ptr [ [[R:%.*]], [[IF]] ], [ [[R2:%.*]], [[ELSE]] ]
|
||||
; CHECK-NEXT: ret ptr [[COMMON_RET_OP]]
|
||||
; CHECK: if:
|
||||
; CHECK-NEXT: [[R]] = call nonnull ptr @foo(ptr noundef readonly [[P]], i64 noundef [[X]]) #[[ATTR5:[0-9]+]]
|
||||
; CHECK-NEXT: br label [[COMMON_RET:%.*]]
|
||||
; CHECK-NEXT: ret ptr [[R]]
|
||||
; CHECK: else:
|
||||
; CHECK-NEXT: [[R2]] = call noundef nonnull ptr @foo(ptr nonnull writeonly [[P]], i64 noundef [[X]]) #[[ATTR6:[0-9]+]]
|
||||
; CHECK-NEXT: call void @side.effect()
|
||||
; CHECK-NEXT: br label [[COMMON_RET]]
|
||||
;
|
||||
@ -117,10 +101,10 @@ define ptr @test_hoist_bool_attrs_fail_non_droppable(i1 %c, ptr %p, i64 %x) {
|
||||
; CHECK-NEXT: [[COMMON_RET_OP:%.*]] = phi ptr [ [[R:%.*]], [[IF]] ], [ [[R2:%.*]], [[ELSE]] ]
|
||||
; CHECK-NEXT: ret ptr [[COMMON_RET_OP]]
|
||||
; CHECK: if:
|
||||
; CHECK-NEXT: [[R]] = call nonnull ptr @foo(ptr noundef readonly [[P]], i64 noundef [[X]]) #[[ATTR5]]
|
||||
; CHECK-NEXT: [[R]] = call nonnull ptr @foo(ptr noundef readonly [[P]], i64 noundef [[X]]) #[[ATTR4:[0-9]+]]
|
||||
; CHECK-NEXT: br label [[COMMON_RET:%.*]]
|
||||
; CHECK: else:
|
||||
; CHECK-NEXT: [[R2]] = call noundef nonnull ptr @foo(ptr nonnull writeonly [[P]], i64 noundef [[X]]) #[[ATTR7:[0-9]+]]
|
||||
; CHECK-NEXT: [[R2]] = call noundef nonnull ptr @foo(ptr nonnull writeonly [[P]], i64 noundef [[X]]) #[[ATTR5:[0-9]+]]
|
||||
; CHECK-NEXT: call void @side.effect()
|
||||
; CHECK-NEXT: br label [[COMMON_RET]]
|
||||
;
|
||||
@ -143,10 +127,10 @@ define ptr @test_hoist_bool_attrs_fail_non_droppable2(i1 %c, ptr %p, i64 %x) {
|
||||
; CHECK-NEXT: [[COMMON_RET_OP:%.*]] = phi ptr [ [[R:%.*]], [[IF]] ], [ [[R2:%.*]], [[ELSE]] ]
|
||||
; CHECK-NEXT: ret ptr [[COMMON_RET_OP]]
|
||||
; CHECK: if:
|
||||
; CHECK-NEXT: [[R]] = call nonnull ptr @foo(ptr noundef readonly [[P]], i64 noundef [[X]]) #[[ATTR8:[0-9]+]]
|
||||
; CHECK-NEXT: [[R]] = call nonnull ptr @foo(ptr noundef readonly [[P]], i64 noundef [[X]]) #[[ATTR6:[0-9]+]]
|
||||
; CHECK-NEXT: br label [[COMMON_RET:%.*]]
|
||||
; CHECK: else:
|
||||
; CHECK-NEXT: [[R2]] = call noundef nonnull ptr @foo(ptr nonnull writeonly byval(i64) [[P]], i64 noundef [[X]]) #[[ATTR7]]
|
||||
; CHECK-NEXT: [[R2]] = call noundef nonnull ptr @foo(ptr nonnull writeonly byval(i64) [[P]], i64 noundef [[X]]) #[[ATTR5]]
|
||||
; CHECK-NEXT: call void @side.effect()
|
||||
; CHECK-NEXT: br label [[COMMON_RET]]
|
||||
;
|
||||
@ -169,10 +153,10 @@ define ptr @test_hoist_bool_attrs_fail_non_droppable3(i1 %c, ptr %p, i64 %x) {
|
||||
; CHECK-NEXT: [[COMMON_RET_OP:%.*]] = phi ptr [ [[R:%.*]], [[IF]] ], [ [[R2:%.*]], [[ELSE]] ]
|
||||
; CHECK-NEXT: ret ptr [[COMMON_RET_OP]]
|
||||
; CHECK: if:
|
||||
; CHECK-NEXT: [[R]] = call nonnull ptr @foo(ptr noundef readonly byval(i32) [[P]], i64 noundef [[X]]) #[[ATTR8]]
|
||||
; CHECK-NEXT: [[R]] = call nonnull ptr @foo(ptr noundef readonly byval(i32) [[P]], i64 noundef [[X]]) #[[ATTR6]]
|
||||
; CHECK-NEXT: br label [[COMMON_RET:%.*]]
|
||||
; CHECK: else:
|
||||
; CHECK-NEXT: [[R2]] = call noundef nonnull ptr @foo(ptr nonnull writeonly byval(i64) [[P]], i64 noundef [[X]]) #[[ATTR7]]
|
||||
; CHECK-NEXT: [[R2]] = call noundef nonnull ptr @foo(ptr nonnull writeonly byval(i64) [[P]], i64 noundef [[X]]) #[[ATTR5]]
|
||||
; CHECK-NEXT: call void @side.effect()
|
||||
; CHECK-NEXT: br label [[COMMON_RET]]
|
||||
;
|
||||
@ -190,15 +174,11 @@ else:
|
||||
define ptr @test_hoist_bool_attrs4(i1 %c, ptr %p, i64 %x) {
|
||||
; CHECK-LABEL: define {{[^@]+}}@test_hoist_bool_attrs4
|
||||
; CHECK-SAME: (i1 [[C:%.*]], ptr [[P:%.*]], i64 [[X:%.*]]) {
|
||||
; CHECK-NEXT: br i1 [[C]], label [[IF:%.*]], label [[ELSE:%.*]]
|
||||
; CHECK-NEXT: [[R:%.*]] = call nonnull ptr @foo(ptr byval(i64) [[P]], i64 noundef [[X]]) #[[ATTR5]]
|
||||
; CHECK-NEXT: br i1 [[C]], label [[COMMON_RET:%.*]], label [[ELSE:%.*]]
|
||||
; CHECK: common.ret:
|
||||
; CHECK-NEXT: [[COMMON_RET_OP:%.*]] = phi ptr [ [[R:%.*]], [[IF]] ], [ [[R2:%.*]], [[ELSE]] ]
|
||||
; CHECK-NEXT: ret ptr [[COMMON_RET_OP]]
|
||||
; CHECK: if:
|
||||
; CHECK-NEXT: [[R]] = call nonnull ptr @foo(ptr noundef readonly byval(i64) [[P]], i64 noundef [[X]]) #[[ATTR8]]
|
||||
; CHECK-NEXT: br label [[COMMON_RET:%.*]]
|
||||
; CHECK-NEXT: ret ptr [[R]]
|
||||
; CHECK: else:
|
||||
; CHECK-NEXT: [[R2]] = call noundef nonnull ptr @foo(ptr nonnull writeonly byval(i64) [[P]], i64 noundef [[X]]) #[[ATTR7]]
|
||||
; CHECK-NEXT: call void @side.effect()
|
||||
; CHECK-NEXT: br label [[COMMON_RET]]
|
||||
;
|
||||
@ -214,13 +194,11 @@ else:
|
||||
}
|
||||
|
||||
;.
|
||||
; CHECK: attributes #[[ATTR0]] = { memory(read) }
|
||||
; CHECK: attributes #[[ATTR1]] = { memory(write) }
|
||||
; CHECK: attributes #[[ATTR2]] = { memory(none) }
|
||||
; CHECK: attributes #[[ATTR3]] = { cold mustprogress nocallback nofree nosync willreturn }
|
||||
; CHECK: attributes #[[ATTR4]] = { mustprogress nocallback nofree willreturn }
|
||||
; CHECK: attributes #[[ATTR5]] = { alwaysinline cold nocallback nofree nosync willreturn }
|
||||
; CHECK: attributes #[[ATTR6]] = { alwaysinline nosync willreturn }
|
||||
; CHECK: attributes #[[ATTR7]] = { nosync willreturn }
|
||||
; CHECK: attributes #[[ATTR8]] = { cold nocallback nofree nosync willreturn }
|
||||
; CHECK: attributes #[[ATTR0]] = { memory(readwrite) }
|
||||
; CHECK: attributes #[[ATTR1]] = { memory(read) }
|
||||
; CHECK: attributes #[[ATTR2]] = { mustprogress nocallback nofree willreturn }
|
||||
; CHECK: attributes #[[ATTR3]] = { alwaysinline nosync willreturn }
|
||||
; CHECK: attributes #[[ATTR4]] = { alwaysinline cold nocallback nofree nosync willreturn }
|
||||
; CHECK: attributes #[[ATTR5]] = { nosync willreturn }
|
||||
; CHECK: attributes #[[ATTR6]] = { cold nocallback nofree nosync willreturn }
|
||||
;.
|
||||
|
@ -8,17 +8,13 @@ declare void @side.effect()
|
||||
define ptr @test_sink_int_attrs(i1 %c, ptr %p, ptr %p2, i64 %x) {
|
||||
; CHECK-LABEL: define {{[^@]+}}@test_sink_int_attrs
|
||||
; CHECK-SAME: (i1 [[C:%.*]], ptr [[P:%.*]], ptr [[P2:%.*]], i64 [[X:%.*]]) {
|
||||
; CHECK-NEXT: br i1 [[C]], label [[IF:%.*]], label [[ELSE:%.*]]
|
||||
; CHECK-NEXT: br i1 [[C]], label [[IF:%.*]], label [[END:%.*]]
|
||||
; CHECK: if:
|
||||
; CHECK-NEXT: call void @side.effect()
|
||||
; 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]+]]
|
||||
; CHECK-NEXT: br label [[END:%.*]]
|
||||
; CHECK: else:
|
||||
; 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]+]]
|
||||
; CHECK-NEXT: br label [[END]]
|
||||
; CHECK: end:
|
||||
; CHECK-NEXT: [[PR:%.*]] = phi ptr [ [[R]], [[IF]] ], [ [[R2]], [[ELSE]] ]
|
||||
; CHECK-NEXT: ret ptr [[PR]]
|
||||
; CHECK-NEXT: [[R2:%.*]] = 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]+]]
|
||||
; CHECK-NEXT: ret ptr [[R2]]
|
||||
;
|
||||
br i1 %c, label %if, label %else
|
||||
if:
|
||||
@ -37,17 +33,13 @@ end:
|
||||
define ptr @test_sink_int_attrs2(i1 %c, ptr %p, i64 %x) {
|
||||
; CHECK-LABEL: define {{[^@]+}}@test_sink_int_attrs2
|
||||
; CHECK-SAME: (i1 [[C:%.*]], ptr [[P:%.*]], i64 [[X:%.*]]) {
|
||||
; CHECK-NEXT: br i1 [[C]], label [[IF:%.*]], label [[ELSE:%.*]]
|
||||
; CHECK-NEXT: br i1 [[C]], label [[IF:%.*]], label [[END:%.*]]
|
||||
; CHECK: if:
|
||||
; CHECK-NEXT: call void @side.effect()
|
||||
; CHECK-NEXT: [[R:%.*]] = call ptr @foo(ptr dereferenceable(50) [[P]], i64 range(i64 10, 1000) [[X]]) #[[ATTR0]]
|
||||
; CHECK-NEXT: br label [[END:%.*]]
|
||||
; CHECK: else:
|
||||
; 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]+]]
|
||||
; CHECK-NEXT: br label [[END]]
|
||||
; CHECK: end:
|
||||
; CHECK-NEXT: [[PR:%.*]] = phi ptr [ [[R]], [[IF]] ], [ [[R2]], [[ELSE]] ]
|
||||
; CHECK-NEXT: ret ptr [[PR]]
|
||||
; CHECK-NEXT: [[R2:%.*]] = call ptr @foo(ptr dereferenceable(50) [[P]], i64 range(i64 10, 1000) [[X]]) #[[ATTR1:[0-9]+]]
|
||||
; CHECK-NEXT: ret ptr [[R2]]
|
||||
;
|
||||
br i1 %c, label %if, label %else
|
||||
if:
|
||||
@ -66,17 +58,13 @@ end:
|
||||
define ptr @test_sink_bool_attrs2(i1 %c, ptr %p, i64 %x) {
|
||||
; CHECK-LABEL: define {{[^@]+}}@test_sink_bool_attrs2
|
||||
; CHECK-SAME: (i1 [[C:%.*]], ptr [[P:%.*]], i64 [[X:%.*]]) {
|
||||
; CHECK-NEXT: br i1 [[C]], label [[IF:%.*]], label [[ELSE:%.*]]
|
||||
; CHECK-NEXT: br i1 [[C]], label [[IF:%.*]], label [[END:%.*]]
|
||||
; CHECK: if:
|
||||
; CHECK-NEXT: call void @side.effect()
|
||||
; CHECK-NEXT: [[R:%.*]] = call noundef ptr @foo(ptr noundef nonnull readnone [[P]], i64 noundef [[X]]) #[[ATTR3:[0-9]+]]
|
||||
; CHECK-NEXT: br label [[END:%.*]]
|
||||
; CHECK: else:
|
||||
; CHECK-NEXT: [[R2:%.*]] = call noundef nonnull ptr @foo(ptr nonnull readonly [[P]], i64 noundef [[X]]) #[[ATTR4:[0-9]+]]
|
||||
; CHECK-NEXT: br label [[END]]
|
||||
; CHECK: end:
|
||||
; CHECK-NEXT: [[PR:%.*]] = phi ptr [ [[R]], [[IF]] ], [ [[R2]], [[ELSE]] ]
|
||||
; CHECK-NEXT: ret ptr [[PR]]
|
||||
; CHECK-NEXT: [[R2:%.*]] = call noundef ptr @foo(ptr nonnull [[P]], i64 noundef [[X]]) #[[ATTR2:[0-9]+]]
|
||||
; CHECK-NEXT: ret ptr [[R2]]
|
||||
;
|
||||
br i1 %c, label %if, label %else
|
||||
if:
|
||||
@ -95,17 +83,13 @@ end:
|
||||
define ptr @test_sink_bool_attrs3(i1 %c, ptr %p, i64 %x) {
|
||||
; CHECK-LABEL: define {{[^@]+}}@test_sink_bool_attrs3
|
||||
; CHECK-SAME: (i1 [[C:%.*]], ptr [[P:%.*]], i64 [[X:%.*]]) {
|
||||
; CHECK-NEXT: br i1 [[C]], label [[IF:%.*]], label [[ELSE:%.*]]
|
||||
; CHECK-NEXT: br i1 [[C]], label [[IF:%.*]], label [[END:%.*]]
|
||||
; CHECK: if:
|
||||
; CHECK-NEXT: call void @side.effect()
|
||||
; CHECK-NEXT: [[R:%.*]] = call nonnull ptr @foo(ptr noundef readonly [[P]], i64 noundef [[X]]) #[[ATTR5:[0-9]+]]
|
||||
; CHECK-NEXT: br label [[END:%.*]]
|
||||
; CHECK: else:
|
||||
; CHECK-NEXT: [[R2:%.*]] = call noundef nonnull ptr @foo(ptr nonnull writeonly [[P]], i64 noundef [[X]]) #[[ATTR6:[0-9]+]]
|
||||
; CHECK-NEXT: br label [[END]]
|
||||
; CHECK: end:
|
||||
; CHECK-NEXT: [[PR:%.*]] = phi ptr [ [[R]], [[IF]] ], [ [[R2]], [[ELSE]] ]
|
||||
; CHECK-NEXT: ret ptr [[PR]]
|
||||
; CHECK-NEXT: [[R2:%.*]] = call nonnull ptr @foo(ptr [[P]], i64 noundef [[X]]) #[[ATTR3:[0-9]+]]
|
||||
; CHECK-NEXT: ret ptr [[R2]]
|
||||
;
|
||||
br i1 %c, label %if, label %else
|
||||
if:
|
||||
@ -127,10 +111,10 @@ define ptr @test_sink_bool_attrs_fail_non_droppable(i1 %c, ptr %p, i64 %x) {
|
||||
; CHECK-NEXT: br i1 [[C]], label [[IF:%.*]], label [[ELSE:%.*]]
|
||||
; CHECK: if:
|
||||
; CHECK-NEXT: call void @side.effect()
|
||||
; CHECK-NEXT: [[R:%.*]] = call nonnull ptr @foo(ptr noundef readonly [[P]], i64 noundef [[X]]) #[[ATTR5]]
|
||||
; CHECK-NEXT: [[R:%.*]] = call nonnull ptr @foo(ptr noundef readonly [[P]], i64 noundef [[X]]) #[[ATTR4:[0-9]+]]
|
||||
; CHECK-NEXT: br label [[END:%.*]]
|
||||
; CHECK: else:
|
||||
; CHECK-NEXT: [[R2:%.*]] = call noundef nonnull ptr @foo(ptr nonnull writeonly [[P]], i64 noundef [[X]]) #[[ATTR7:[0-9]+]]
|
||||
; CHECK-NEXT: [[R2:%.*]] = call noundef nonnull ptr @foo(ptr nonnull writeonly [[P]], i64 noundef [[X]]) #[[ATTR5:[0-9]+]]
|
||||
; CHECK-NEXT: br label [[END]]
|
||||
; CHECK: end:
|
||||
; CHECK-NEXT: [[PR:%.*]] = phi ptr [ [[R]], [[IF]] ], [ [[R2]], [[ELSE]] ]
|
||||
@ -156,10 +140,10 @@ define ptr @test_sink_bool_attrs_fail_non_droppable2(i1 %c, ptr %p, i64 %x) {
|
||||
; CHECK-NEXT: br i1 [[C]], label [[IF:%.*]], label [[ELSE:%.*]]
|
||||
; CHECK: if:
|
||||
; CHECK-NEXT: call void @side.effect()
|
||||
; CHECK-NEXT: [[R:%.*]] = call nonnull ptr @foo(ptr noundef readonly [[P]], i64 noundef [[X]]) #[[ATTR8:[0-9]+]]
|
||||
; CHECK-NEXT: [[R:%.*]] = call nonnull ptr @foo(ptr noundef readonly [[P]], i64 noundef [[X]]) #[[ATTR6:[0-9]+]]
|
||||
; CHECK-NEXT: br label [[END:%.*]]
|
||||
; CHECK: else:
|
||||
; CHECK-NEXT: [[R2:%.*]] = call noundef nonnull ptr @foo(ptr nonnull writeonly byval(i64) [[P]], i64 noundef [[X]]) #[[ATTR7]]
|
||||
; CHECK-NEXT: [[R2:%.*]] = call noundef nonnull ptr @foo(ptr nonnull writeonly byval(i64) [[P]], i64 noundef [[X]]) #[[ATTR5]]
|
||||
; CHECK-NEXT: br label [[END]]
|
||||
; CHECK: end:
|
||||
; CHECK-NEXT: [[PR:%.*]] = phi ptr [ [[R]], [[IF]] ], [ [[R2]], [[ELSE]] ]
|
||||
@ -185,10 +169,10 @@ define ptr @test_sink_bool_attrs_fail_non_droppable3(i1 %c, ptr %p, i64 %x) {
|
||||
; CHECK-NEXT: br i1 [[C]], label [[IF:%.*]], label [[ELSE:%.*]]
|
||||
; CHECK: if:
|
||||
; CHECK-NEXT: call void @side.effect()
|
||||
; CHECK-NEXT: [[R:%.*]] = call nonnull ptr @foo(ptr noundef readonly byval(i32) [[P]], i64 noundef [[X]]) #[[ATTR8]]
|
||||
; CHECK-NEXT: [[R:%.*]] = call nonnull ptr @foo(ptr noundef readonly byval(i32) [[P]], i64 noundef [[X]]) #[[ATTR6]]
|
||||
; CHECK-NEXT: br label [[END:%.*]]
|
||||
; CHECK: else:
|
||||
; CHECK-NEXT: [[R2:%.*]] = call noundef nonnull ptr @foo(ptr nonnull writeonly byval(i64) [[P]], i64 noundef [[X]]) #[[ATTR9:[0-9]+]]
|
||||
; CHECK-NEXT: [[R2:%.*]] = call noundef nonnull ptr @foo(ptr nonnull writeonly byval(i64) [[P]], i64 noundef [[X]]) #[[ATTR7:[0-9]+]]
|
||||
; CHECK-NEXT: br label [[END]]
|
||||
; CHECK: end:
|
||||
; CHECK-NEXT: [[PR:%.*]] = phi ptr [ [[R]], [[IF]] ], [ [[R2]], [[ELSE]] ]
|
||||
@ -211,17 +195,13 @@ end:
|
||||
define ptr @test_sink_bool_attrs4(i1 %c, ptr %p, i64 %x) {
|
||||
; CHECK-LABEL: define {{[^@]+}}@test_sink_bool_attrs4
|
||||
; CHECK-SAME: (i1 [[C:%.*]], ptr [[P:%.*]], i64 [[X:%.*]]) {
|
||||
; CHECK-NEXT: br i1 [[C]], label [[IF:%.*]], label [[ELSE:%.*]]
|
||||
; CHECK-NEXT: br i1 [[C]], label [[IF:%.*]], label [[END:%.*]]
|
||||
; CHECK: if:
|
||||
; CHECK-NEXT: call void @side.effect()
|
||||
; CHECK-NEXT: [[R:%.*]] = call nonnull ptr @foo(ptr noundef readonly byval(i64) [[P]], i64 noundef [[X]]) #[[ATTR8]]
|
||||
; CHECK-NEXT: br label [[END:%.*]]
|
||||
; CHECK: else:
|
||||
; CHECK-NEXT: [[R2:%.*]] = call noundef nonnull ptr @foo(ptr nonnull writeonly byval(i64) [[P]], i64 noundef [[X]]) #[[ATTR9]]
|
||||
; CHECK-NEXT: br label [[END]]
|
||||
; CHECK: end:
|
||||
; CHECK-NEXT: [[PR:%.*]] = phi ptr [ [[R]], [[IF]] ], [ [[R2]], [[ELSE]] ]
|
||||
; CHECK-NEXT: ret ptr [[PR]]
|
||||
; CHECK-NEXT: [[R2:%.*]] = call nonnull ptr @foo(ptr byval(i64) [[P]], i64 noundef [[X]]) #[[ATTR7]]
|
||||
; CHECK-NEXT: ret ptr [[R2]]
|
||||
;
|
||||
br i1 %c, label %if, label %else
|
||||
if:
|
||||
@ -238,14 +218,12 @@ end:
|
||||
}
|
||||
|
||||
;.
|
||||
; CHECK: attributes #[[ATTR0]] = { memory(read) }
|
||||
; CHECK: attributes #[[ATTR1]] = { memory(write) }
|
||||
; CHECK: attributes #[[ATTR2]] = { memory(none) }
|
||||
; CHECK: attributes #[[ATTR3]] = { cold mustprogress nocallback nofree nosync willreturn }
|
||||
; CHECK: attributes #[[ATTR4]] = { mustprogress nocallback nofree willreturn }
|
||||
; CHECK: attributes #[[ATTR5]] = { alwaysinline cold nocallback nofree nosync willreturn }
|
||||
; CHECK: attributes #[[ATTR6]] = { alwaysinline nosync willreturn }
|
||||
; CHECK: attributes #[[ATTR7]] = { nosync willreturn }
|
||||
; CHECK: attributes #[[ATTR8]] = { cold nocallback nofree nosync willreturn }
|
||||
; CHECK: attributes #[[ATTR9]] = { nosync }
|
||||
; CHECK: attributes #[[ATTR0]] = { memory(readwrite) }
|
||||
; CHECK: attributes #[[ATTR1]] = { memory(read) }
|
||||
; CHECK: attributes #[[ATTR2]] = { mustprogress nocallback nofree willreturn }
|
||||
; CHECK: attributes #[[ATTR3]] = { alwaysinline nosync willreturn }
|
||||
; CHECK: attributes #[[ATTR4]] = { alwaysinline cold nocallback nofree nosync willreturn }
|
||||
; CHECK: attributes #[[ATTR5]] = { nosync willreturn }
|
||||
; CHECK: attributes #[[ATTR6]] = { cold nocallback nofree nosync willreturn }
|
||||
; CHECK: attributes #[[ATTR7]] = { nosync }
|
||||
;.
|
||||
|
Loading…
x
Reference in New Issue
Block a user