From 9398caf399ae04d3f331c7d766ee9884f794d4f2 Mon Sep 17 00:00:00 2001 From: Alexander Shaposhnikov Date: Fri, 20 May 2022 18:39:21 +0000 Subject: [PATCH] Recommit "[ConstantRange] Improve the implementation of binaryOr" This recommits https://reviews.llvm.org/rG6990e7477d24ff585ae86549f5280f0be65422a6 as the problematic test has been updated updated in https://reviews.llvm.org/rG3bd112c720dc614a59e3f34ebf9b45075037bfa0. --- llvm/lib/IR/ConstantRange.cpp | 15 ++++++------- llvm/unittests/IR/ConstantRangeTest.cpp | 38 +++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 8 deletions(-) diff --git a/llvm/lib/IR/ConstantRange.cpp b/llvm/lib/IR/ConstantRange.cpp index c3915ce..be6386c 100644 --- a/llvm/lib/IR/ConstantRange.cpp +++ b/llvm/lib/IR/ConstantRange.cpp @@ -1410,14 +1410,13 @@ ConstantRange ConstantRange::binaryOr(const ConstantRange &Other) const { if (isEmptySet() || Other.isEmptySet()) return getEmpty(); - // Use APInt's implementation of OR for single element ranges. - if (isSingleElement() && Other.isSingleElement()) - return {*getSingleElement() | *Other.getSingleElement()}; - - // TODO: replace this with something less conservative - - APInt umax = APIntOps::umax(getUnsignedMin(), Other.getUnsignedMin()); - return getNonEmpty(std::move(umax), APInt::getZero(getBitWidth())); + ConstantRange KnownBitsRange = + fromKnownBits(toKnownBits() | Other.toKnownBits(), false); + // Upper wrapped range. + ConstantRange UMaxUMinRange = + getNonEmpty(APIntOps::umax(getUnsignedMin(), Other.getUnsignedMin()), + APInt::getZero(getBitWidth())); + return KnownBitsRange.intersectWith(UMaxUMinRange); } ConstantRange ConstantRange::binaryXor(const ConstantRange &Other) const { diff --git a/llvm/unittests/IR/ConstantRangeTest.cpp b/llvm/unittests/IR/ConstantRangeTest.cpp index ffd0f00..8a543b1 100644 --- a/llvm/unittests/IR/ConstantRangeTest.cpp +++ b/llvm/unittests/IR/ConstantRangeTest.cpp @@ -2560,6 +2560,44 @@ TEST_F(ConstantRangeTest, binaryAnd) { CheckSingleElementsOnly); } +TEST_F(ConstantRangeTest, binaryOr) { + // Single element ranges. + ConstantRange R16(APInt(8, 16)); + ConstantRange R20(APInt(8, 20)); + EXPECT_EQ(*R16.binaryOr(R16).getSingleElement(), APInt(8, 16)); + EXPECT_EQ(*R16.binaryOr(R20).getSingleElement(), APInt(8, 16 | 20)); + + ConstantRange R16_32(APInt(8, 16), APInt(8, 32)); + // 'Or' with a high bits mask. + // KnownBits estimate is important, otherwise the maximum included element + // would be 2^8 - 1. + ConstantRange R32(APInt(8, 32)); + ConstantRange R48_64(APInt(8, 48), APInt(8, 64)); + EXPECT_EQ(R16_32.binaryOr(R32), R48_64); + EXPECT_EQ(R32.binaryOr(R16_32), R48_64); + // 'Or' with a low bits mask. + ConstantRange R4(APInt(8, 4)); + ConstantRange R0_16(APInt(8, 0), APInt(8, 16)); + ConstantRange R4_16(APInt(8, 4), APInt(8, 16)); + EXPECT_EQ(R0_16.binaryOr(R4), R4_16); + EXPECT_EQ(R4.binaryOr(R0_16), R4_16); + + // Ranges with more than one element. Handled conservatively for now. + // UMaxUMin estimate is important, otherwise the lower bound would be zero. + ConstantRange R0_64(APInt(8, 0), APInt(8, 64)); + ConstantRange R5_32(APInt(8, 5), APInt(8, 32)); + ConstantRange R5_64(APInt(8, 5), APInt(8, 64)); + EXPECT_EQ(R0_64.binaryOr(R5_32), R5_64); + EXPECT_EQ(R5_32.binaryOr(R0_64), R5_64); + + TestBinaryOpExhaustive( + [](const ConstantRange &CR1, const ConstantRange &CR2) { + return CR1.binaryOr(CR2); + }, + [](const APInt &N1, const APInt &N2) { return N1 | N2; }, PreferSmallest, + CheckSingleElementsOnly); +} + TEST_F(ConstantRangeTest, binaryXor) { // Single element ranges. ConstantRange R16(APInt(8, 16)); -- 2.7.4