diff --git a/llvm/lib/IR/Attributes.cpp b/llvm/lib/IR/Attributes.cpp index c5874c58c0eca..0fe213e95e50a 100644 --- a/llvm/lib/IR/Attributes.cpp +++ b/llvm/lib/IR/Attributes.cpp @@ -18,6 +18,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallSet.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" @@ -1800,12 +1801,10 @@ AttributeList::intersectWith(LLVMContext &C, AttributeList Other) const { if (*this == Other) return *this; - // At least for now, only intersect lists with same number of params. - if (getNumAttrSets() != Other.getNumAttrSets()) - return std::nullopt; - SmallVector> IntersectedAttrs; - for (unsigned Idx : indexes()) { + auto IndexIt = + index_iterator(std::max(getNumAttrSets(), Other.getNumAttrSets())); + for (unsigned Idx : IndexIt) { auto IntersectedAS = getAttributes(Idx).intersectWith(C, Other.getAttributes(Idx)); // If any index fails to intersect, fail. diff --git a/llvm/lib/IR/Instruction.cpp b/llvm/lib/IR/Instruction.cpp index b84e62d2e43b7..eec751078698b 100644 --- a/llvm/lib/IR/Instruction.cpp +++ b/llvm/lib/IR/Instruction.cpp @@ -875,7 +875,8 @@ bool Instruction::isIdenticalToWhenDefined(const Instruction *I, // If both instructions have no operands, they are identical. if (getNumOperands() == 0 && I->getNumOperands() == 0) - return this->hasSameSpecialState(I); + return this->hasSameSpecialState(I, /*IgnoreAlignment=*/false, + IntersectAttrs); // We have two instructions of identical opcode and #operands. Check to see // if all operands are the same. diff --git a/llvm/test/Transforms/SimplifyCFG/sink-cb-diff-attrs.ll b/llvm/test/Transforms/SimplifyCFG/sink-cb-diff-attrs.ll index 1998055ca75f2..fe9d87080dd11 100644 --- a/llvm/test/Transforms/SimplifyCFG/sink-cb-diff-attrs.ll +++ b/llvm/test/Transforms/SimplifyCFG/sink-cb-diff-attrs.ll @@ -1,10 +1,65 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes --check-globals ; RUN: opt < %s -passes='simplifycfg' -simplifycfg-require-and-preserve-domtree=1 -S | FileCheck %s +declare ptr @foo0() declare ptr @foo(ptr %p, i64 %x) declare ptr @foo2(ptr %p, ptr %p2, i64 %x) declare void @side.effect() +define ptr @test_sink_no_args_oneside(i1 %c) { +; CHECK-LABEL: define {{[^@]+}}@test_sink_no_args_oneside +; CHECK-SAME: (i1 [[C:%.*]]) { +; CHECK-NEXT: br i1 [[C]], label [[IF:%.*]], label [[END:%.*]] +; CHECK: if: +; CHECK-NEXT: call void @side.effect() +; CHECK-NEXT: br label [[END]] +; CHECK: end: +; CHECK-NEXT: [[R2:%.*]] = call ptr @foo0() +; CHECK-NEXT: ret ptr [[R2]] +; + br i1 %c, label %if, label %else +if: + call void @side.effect() + %r = call ptr @foo0() + br label %end + +else: + %r2 = call ptr @foo0() readonly + br label %end +end: + %pr = phi ptr [ %r, %if], [%r2, %else] + ret ptr %pr +} + +define ptr @test_sink_no_args_oneside_fail(i1 %c) { +; CHECK-LABEL: define {{[^@]+}}@test_sink_no_args_oneside_fail +; CHECK-SAME: (i1 [[C:%.*]]) { +; CHECK-NEXT: br i1 [[C]], label [[IF:%.*]], label [[ELSE:%.*]] +; CHECK: if: +; CHECK-NEXT: call void @side.effect() +; CHECK-NEXT: [[R:%.*]] = call ptr @foo0() +; CHECK-NEXT: br label [[END:%.*]] +; CHECK: else: +; CHECK-NEXT: [[R2:%.*]] = call ptr @foo0() #[[ATTR0:[0-9]+]] +; CHECK-NEXT: br label [[END]] +; CHECK: end: +; CHECK-NEXT: [[PR:%.*]] = phi ptr [ [[R]], [[IF]] ], [ [[R2]], [[ELSE]] ] +; CHECK-NEXT: ret ptr [[PR]] +; + br i1 %c, label %if, label %else +if: + call void @side.effect() + %r = call ptr @foo0() + br label %end + +else: + %r2 = call ptr @foo0() readonly alwaysinline + br label %end +end: + %pr = phi ptr [ %r, %if], [%r2, %else] + ret ptr %pr +} + 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:%.*]]) { @@ -13,7 +68,7 @@ define ptr @test_sink_int_attrs(i1 %c, ptr %p, ptr %p2, i64 %x) { ; CHECK-NEXT: call void @side.effect() ; CHECK-NEXT: br label [[END]] ; CHECK: end: -; 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: [[R2:%.*]] = call ptr @foo2(ptr align 32 dereferenceable_or_null(100) [[P]], ptr align 32 dereferenceable(50) [[P2]], i64 range(i64 10, 100000) [[X]]) #[[ATTR1:[0-9]+]] ; CHECK-NEXT: ret ptr [[R2]] ; br i1 %c, label %if, label %else @@ -38,7 +93,7 @@ define ptr @test_sink_int_attrs2(i1 %c, ptr %p, i64 %x) { ; CHECK-NEXT: call void @side.effect() ; CHECK-NEXT: br label [[END]] ; CHECK: end: -; CHECK-NEXT: [[R2:%.*]] = call ptr @foo(ptr dereferenceable(50) [[P]], i64 range(i64 10, 1000) [[X]]) #[[ATTR1:[0-9]+]] +; CHECK-NEXT: [[R2:%.*]] = call ptr @foo(ptr dereferenceable(50) [[P]], i64 range(i64 10, 1000) [[X]]) #[[ATTR2:[0-9]+]] ; CHECK-NEXT: ret ptr [[R2]] ; br i1 %c, label %if, label %else @@ -63,7 +118,7 @@ define ptr @test_sink_bool_attrs2(i1 %c, ptr %p, i64 %x) { ; CHECK-NEXT: call void @side.effect() ; CHECK-NEXT: br label [[END]] ; CHECK: end: -; CHECK-NEXT: [[R2:%.*]] = call noundef ptr @foo(ptr nonnull [[P]], i64 noundef [[X]]) #[[ATTR2:[0-9]+]] +; CHECK-NEXT: [[R2:%.*]] = call noundef ptr @foo(ptr nonnull [[P]], i64 noundef [[X]]) #[[ATTR3:[0-9]+]] ; CHECK-NEXT: ret ptr [[R2]] ; br i1 %c, label %if, label %else @@ -88,7 +143,7 @@ define ptr @test_sink_bool_attrs3(i1 %c, ptr %p, i64 %x) { ; CHECK-NEXT: call void @side.effect() ; CHECK-NEXT: br label [[END]] ; CHECK: end: -; CHECK-NEXT: [[R2:%.*]] = call nonnull ptr @foo(ptr [[P]], i64 noundef [[X]]) #[[ATTR3:[0-9]+]] +; CHECK-NEXT: [[R2:%.*]] = call nonnull ptr @foo(ptr [[P]], i64 noundef [[X]]) #[[ATTR4:[0-9]+]] ; CHECK-NEXT: ret ptr [[R2]] ; br i1 %c, label %if, label %else @@ -111,10 +166,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]]) #[[ATTR4:[0-9]+]] +; 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]]) #[[ATTR5:[0-9]+]] +; 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]] ] @@ -140,10 +195,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]]) #[[ATTR6:[0-9]+]] +; CHECK-NEXT: [[R:%.*]] = call nonnull ptr @foo(ptr noundef readonly [[P]], i64 noundef [[X]]) #[[ATTR7:[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]]) #[[ATTR5]] +; CHECK-NEXT: [[R2:%.*]] = call noundef nonnull ptr @foo(ptr nonnull writeonly byval(i64) [[P]], i64 noundef [[X]]) #[[ATTR6]] ; CHECK-NEXT: br label [[END]] ; CHECK: end: ; CHECK-NEXT: [[PR:%.*]] = phi ptr [ [[R]], [[IF]] ], [ [[R2]], [[ELSE]] ] @@ -169,10 +224,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]]) #[[ATTR6]] +; CHECK-NEXT: [[R:%.*]] = call nonnull ptr @foo(ptr noundef readonly byval(i32) [[P]], i64 noundef [[X]]) #[[ATTR7]] ; 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:[0-9]+]] +; CHECK-NEXT: [[R2:%.*]] = call noundef nonnull ptr @foo(ptr nonnull writeonly byval(i64) [[P]], i64 noundef [[X]]) #[[ATTR8:[0-9]+]] ; CHECK-NEXT: br label [[END]] ; CHECK: end: ; CHECK-NEXT: [[PR:%.*]] = phi ptr [ [[R]], [[IF]] ], [ [[R2]], [[ELSE]] ] @@ -200,7 +255,7 @@ define ptr @test_sink_bool_attrs4(i1 %c, ptr %p, i64 %x) { ; CHECK-NEXT: call void @side.effect() ; CHECK-NEXT: br label [[END]] ; CHECK: end: -; CHECK-NEXT: [[R2:%.*]] = call nonnull ptr @foo(ptr byval(i64) [[P]], i64 noundef [[X]]) #[[ATTR7]] +; CHECK-NEXT: [[R2:%.*]] = call nonnull ptr @foo(ptr byval(i64) [[P]], i64 noundef [[X]]) #[[ATTR8]] ; CHECK-NEXT: ret ptr [[R2]] ; br i1 %c, label %if, label %else @@ -218,12 +273,13 @@ end: } ;. -; 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 } +; CHECK: attributes #[[ATTR0]] = { alwaysinline memory(read) } +; CHECK: attributes #[[ATTR1]] = { memory(readwrite) } +; CHECK: attributes #[[ATTR2]] = { memory(read) } +; CHECK: attributes #[[ATTR3]] = { mustprogress nocallback nofree willreturn } +; CHECK: attributes #[[ATTR4]] = { alwaysinline nosync willreturn } +; CHECK: attributes #[[ATTR5]] = { alwaysinline cold nocallback nofree nosync willreturn } +; CHECK: attributes #[[ATTR6]] = { nosync willreturn } +; CHECK: attributes #[[ATTR7]] = { cold nocallback nofree nosync willreturn } +; CHECK: attributes #[[ATTR8]] = { nosync } ;. diff --git a/llvm/unittests/IR/AttributesTest.cpp b/llvm/unittests/IR/AttributesTest.cpp index 0fdb5a9c5a688..f73f2b20e9fea 100644 --- a/llvm/unittests/IR/AttributesTest.cpp +++ b/llvm/unittests/IR/AttributesTest.cpp @@ -596,6 +596,38 @@ TEST(Attributes, SetIntersectByValAlign) { } } +TEST(Attributes, ListIntersectDifferingMustPreserve) { + LLVMContext C; + std::optional Res; + { + AttributeList AL0; + AttributeList AL1; + AL1 = AL1.addFnAttribute(C, Attribute::ReadOnly); + AL0 = AL0.addParamAttribute(C, 0, Attribute::SExt); + Res = AL0.intersectWith(C, AL1); + ASSERT_FALSE(Res.has_value()); + Res = AL1.intersectWith(C, AL0); + ASSERT_FALSE(Res.has_value()); + } + { + AttributeList AL0; + AttributeList AL1; + AL1 = AL1.addFnAttribute(C, Attribute::AlwaysInline); + AL0 = AL0.addParamAttribute(C, 0, Attribute::ReadOnly); + Res = AL0.intersectWith(C, AL1); + ASSERT_FALSE(Res.has_value()); + Res = AL1.intersectWith(C, AL0); + ASSERT_FALSE(Res.has_value()); + + AL0 = AL0.addFnAttribute(C, Attribute::AlwaysInline); + AL1 = AL1.addParamAttribute(C, 1, Attribute::SExt); + Res = AL0.intersectWith(C, AL1); + ASSERT_FALSE(Res.has_value()); + Res = AL1.intersectWith(C, AL0); + ASSERT_FALSE(Res.has_value()); + } +} + TEST(Attributes, ListIntersect) { LLVMContext C; AttributeList AL0; @@ -610,11 +642,16 @@ TEST(Attributes, ListIntersect) { AL0 = AL0.addParamAttribute(C, 1, Attribute::NoUndef); Res = AL0.intersectWith(C, AL1); - ASSERT_FALSE(Res.has_value()); + ASSERT_TRUE(Res.has_value()); + ASSERT_TRUE(Res->hasRetAttr(Attribute::NoUndef)); + ASSERT_FALSE(Res->hasParamAttr(1, Attribute::NoUndef)); AL1 = AL1.addParamAttribute(C, 2, Attribute::NoUndef); Res = AL0.intersectWith(C, AL1); - ASSERT_FALSE(Res.has_value()); + ASSERT_TRUE(Res.has_value()); + ASSERT_TRUE(Res->hasRetAttr(Attribute::NoUndef)); + ASSERT_FALSE(Res->hasParamAttr(1, Attribute::NoUndef)); + ASSERT_FALSE(Res->hasParamAttr(2, Attribute::NoUndef)); AL0 = AL0.addParamAttribute(C, 2, Attribute::NoUndef); AL1 = AL1.addParamAttribute(C, 1, Attribute::NoUndef); @@ -692,7 +729,17 @@ TEST(Attributes, ListIntersect) { AL1 = AL1.addParamAttribute(C, 3, Attribute::ReadNone); Res = AL0.intersectWith(C, AL1); - ASSERT_FALSE(Res.has_value()); + ASSERT_TRUE(Res.has_value()); + ASSERT_TRUE(Res.has_value()); + ASSERT_TRUE(Res->hasFnAttr(Attribute::AlwaysInline)); + ASSERT_TRUE(Res->hasFnAttr(Attribute::ReadOnly)); + ASSERT_TRUE(Res->hasRetAttr(Attribute::NoUndef)); + ASSERT_FALSE(Res->hasRetAttr(Attribute::NonNull)); + ASSERT_TRUE(Res->hasParamAttr(1, Attribute::NoUndef)); + ASSERT_TRUE(Res->hasParamAttr(2, Attribute::NoUndef)); + ASSERT_FALSE(Res->hasParamAttr(2, Attribute::NonNull)); + ASSERT_FALSE(Res->hasParamAttr(2, Attribute::ReadNone)); + ASSERT_FALSE(Res->hasParamAttr(3, Attribute::ReadNone)); AL0 = AL0.addParamAttribute(C, 3, Attribute::ReadNone); Res = AL0.intersectWith(C, AL1);