| //===- llvm/unittest/Support/KnownBitsTest.cpp - KnownBits tests ----------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file implements unit tests for KnownBits functions. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "llvm/ADT/ArrayRef.h" |
| #include "llvm/Support/KnownBits.h" |
| #include "KnownBitsTest.h" |
| #include "gtest/gtest.h" |
| |
| using namespace llvm; |
| |
| using UnaryBitsFn = llvm::function_ref<KnownBits(const KnownBits &)>; |
| using UnaryIntFn = llvm::function_ref<std::optional<APInt>(const APInt &)>; |
| using UnaryCheckFn = llvm::function_ref<bool(const KnownBits &)>; |
| |
| using BinaryBitsFn = |
| llvm::function_ref<KnownBits(const KnownBits &, const KnownBits &)>; |
| using BinaryIntFn = |
| llvm::function_ref<std::optional<APInt>(const APInt &, const APInt &)>; |
| using BinaryCheckFn = |
| llvm::function_ref<bool(const KnownBits &, const KnownBits &)>; |
| |
| static bool checkOptimalityUnary(const KnownBits &) { return true; } |
| static bool checkCorrectnessOnlyUnary(const KnownBits &) { return false; } |
| static bool checkOptimalityBinary(const KnownBits &, const KnownBits &) { |
| return true; |
| } |
| static bool checkCorrectnessOnlyBinary(const KnownBits &, const KnownBits &) { |
| return false; |
| } |
| |
| static testing::AssertionResult isCorrect(const KnownBits &Exact, |
| const KnownBits &Computed, |
| ArrayRef<KnownBits> Inputs) { |
| if (Computed.Zero.isSubsetOf(Exact.Zero) && |
| Computed.One.isSubsetOf(Exact.One)) |
| return testing::AssertionSuccess(); |
| |
| testing::AssertionResult Result = testing::AssertionFailure(); |
| Result << "Inputs = "; |
| for (const KnownBits &Input : Inputs) |
| Result << Input << ", "; |
| Result << "Computed = " << Computed << ", Exact = " << Exact; |
| return Result; |
| } |
| |
| static testing::AssertionResult isOptimal(const KnownBits &Exact, |
| const KnownBits &Computed, |
| ArrayRef<KnownBits> Inputs) { |
| if (Computed == Exact) |
| return testing::AssertionSuccess(); |
| |
| testing::AssertionResult Result = testing::AssertionFailure(); |
| Result << "Inputs = "; |
| for (const KnownBits &Input : Inputs) |
| Result << Input << ", "; |
| Result << "Computed = " << Computed << ", Exact = " << Exact; |
| return Result; |
| } |
| |
| static void |
| testUnaryOpExhaustive(UnaryBitsFn BitsFn, UnaryIntFn IntFn, |
| UnaryCheckFn CheckOptimalityFn = checkOptimalityUnary) { |
| for (unsigned Bits : {1, 4}) { |
| ForeachKnownBits(Bits, [&](const KnownBits &Known) { |
| KnownBits Computed = BitsFn(Known); |
| KnownBits Exact(Bits); |
| Exact.Zero.setAllBits(); |
| Exact.One.setAllBits(); |
| |
| ForeachNumInKnownBits(Known, [&](const APInt &N) { |
| if (std::optional<APInt> Res = IntFn(N)) { |
| Exact.One &= *Res; |
| Exact.Zero &= ~*Res; |
| } |
| }); |
| |
| EXPECT_TRUE(!Computed.hasConflict()); |
| EXPECT_TRUE(isCorrect(Exact, Computed, Known)); |
| // We generally don't want to return conflicting known bits, even if it is |
| // legal for always poison results. |
| if (CheckOptimalityFn(Known) && !Exact.hasConflict()) { |
| EXPECT_TRUE(isOptimal(Exact, Computed, Known)); |
| } |
| }); |
| } |
| } |
| |
| static void |
| testBinaryOpExhaustive(BinaryBitsFn BitsFn, BinaryIntFn IntFn, |
| BinaryCheckFn CheckOptimalityFn = checkOptimalityBinary, |
| bool RefinePoisonToZero = false) { |
| for (unsigned Bits : {1, 4}) { |
| ForeachKnownBits(Bits, [&](const KnownBits &Known1) { |
| ForeachKnownBits(Bits, [&](const KnownBits &Known2) { |
| KnownBits Computed = BitsFn(Known1, Known2); |
| KnownBits Exact(Bits); |
| Exact.Zero.setAllBits(); |
| Exact.One.setAllBits(); |
| |
| ForeachNumInKnownBits(Known1, [&](const APInt &N1) { |
| ForeachNumInKnownBits(Known2, [&](const APInt &N2) { |
| if (std::optional<APInt> Res = IntFn(N1, N2)) { |
| Exact.One &= *Res; |
| Exact.Zero &= ~*Res; |
| } |
| }); |
| }); |
| |
| EXPECT_TRUE(!Computed.hasConflict()); |
| EXPECT_TRUE(isCorrect(Exact, Computed, {Known1, Known2})); |
| // We generally don't want to return conflicting known bits, even if it |
| // is legal for always poison results. |
| if (CheckOptimalityFn(Known1, Known2) && !Exact.hasConflict()) { |
| EXPECT_TRUE(isOptimal(Exact, Computed, {Known1, Known2})); |
| } |
| // In some cases we choose to return zero if the result is always |
| // poison. |
| if (RefinePoisonToZero && Exact.hasConflict()) { |
| EXPECT_TRUE(Computed.isZero()); |
| } |
| }); |
| }); |
| } |
| } |
| |
| namespace { |
| |
| TEST(KnownBitsTest, AddCarryExhaustive) { |
| unsigned Bits = 4; |
| ForeachKnownBits(Bits, [&](const KnownBits &Known1) { |
| ForeachKnownBits(Bits, [&](const KnownBits &Known2) { |
| ForeachKnownBits(1, [&](const KnownBits &KnownCarry) { |
| // Explicitly compute known bits of the addition by trying all |
| // possibilities. |
| KnownBits Known(Bits); |
| Known.Zero.setAllBits(); |
| Known.One.setAllBits(); |
| ForeachNumInKnownBits(Known1, [&](const APInt &N1) { |
| ForeachNumInKnownBits(Known2, [&](const APInt &N2) { |
| ForeachNumInKnownBits(KnownCarry, [&](const APInt &Carry) { |
| APInt Add = N1 + N2; |
| if (Carry.getBoolValue()) |
| ++Add; |
| |
| Known.One &= Add; |
| Known.Zero &= ~Add; |
| }); |
| }); |
| }); |
| |
| KnownBits KnownComputed = |
| KnownBits::computeForAddCarry(Known1, Known2, KnownCarry); |
| EXPECT_EQ(Known, KnownComputed); |
| }); |
| }); |
| }); |
| } |
| |
| static void TestAddSubExhaustive(bool IsAdd) { |
| unsigned Bits = 4; |
| ForeachKnownBits(Bits, [&](const KnownBits &Known1) { |
| ForeachKnownBits(Bits, [&](const KnownBits &Known2) { |
| KnownBits Known(Bits), KnownNSW(Bits); |
| Known.Zero.setAllBits(); |
| Known.One.setAllBits(); |
| KnownNSW.Zero.setAllBits(); |
| KnownNSW.One.setAllBits(); |
| |
| ForeachNumInKnownBits(Known1, [&](const APInt &N1) { |
| ForeachNumInKnownBits(Known2, [&](const APInt &N2) { |
| bool Overflow; |
| APInt Res; |
| if (IsAdd) |
| Res = N1.sadd_ov(N2, Overflow); |
| else |
| Res = N1.ssub_ov(N2, Overflow); |
| |
| Known.One &= Res; |
| Known.Zero &= ~Res; |
| |
| if (!Overflow) { |
| KnownNSW.One &= Res; |
| KnownNSW.Zero &= ~Res; |
| } |
| }); |
| }); |
| |
| KnownBits KnownComputed = |
| KnownBits::computeForAddSub(IsAdd, /*NSW*/ false, Known1, Known2); |
| EXPECT_EQ(Known, KnownComputed); |
| |
| // The NSW calculation is not precise, only check that it's |
| // conservatively correct. |
| KnownBits KnownNSWComputed = KnownBits::computeForAddSub( |
| IsAdd, /*NSW*/true, Known1, Known2); |
| EXPECT_TRUE(KnownNSWComputed.Zero.isSubsetOf(KnownNSW.Zero)); |
| EXPECT_TRUE(KnownNSWComputed.One.isSubsetOf(KnownNSW.One)); |
| }); |
| }); |
| } |
| |
| TEST(KnownBitsTest, AddSubExhaustive) { |
| TestAddSubExhaustive(true); |
| TestAddSubExhaustive(false); |
| } |
| |
| TEST(KnownBitsTest, BinaryExhaustive) { |
| testBinaryOpExhaustive( |
| [](const KnownBits &Known1, const KnownBits &Known2) { |
| return Known1 & Known2; |
| }, |
| [](const APInt &N1, const APInt &N2) { return N1 & N2; }); |
| testBinaryOpExhaustive( |
| [](const KnownBits &Known1, const KnownBits &Known2) { |
| return Known1 | Known2; |
| }, |
| [](const APInt &N1, const APInt &N2) { return N1 | N2; }); |
| testBinaryOpExhaustive( |
| [](const KnownBits &Known1, const KnownBits &Known2) { |
| return Known1 ^ Known2; |
| }, |
| [](const APInt &N1, const APInt &N2) { return N1 ^ N2; }); |
| |
| testBinaryOpExhaustive( |
| [](const KnownBits &Known1, const KnownBits &Known2) { |
| return KnownBits::umax(Known1, Known2); |
| }, |
| [](const APInt &N1, const APInt &N2) { return APIntOps::umax(N1, N2); }); |
| testBinaryOpExhaustive( |
| [](const KnownBits &Known1, const KnownBits &Known2) { |
| return KnownBits::umin(Known1, Known2); |
| }, |
| [](const APInt &N1, const APInt &N2) { return APIntOps::umin(N1, N2); }); |
| testBinaryOpExhaustive( |
| [](const KnownBits &Known1, const KnownBits &Known2) { |
| return KnownBits::smax(Known1, Known2); |
| }, |
| [](const APInt &N1, const APInt &N2) { return APIntOps::smax(N1, N2); }); |
| testBinaryOpExhaustive( |
| [](const KnownBits &Known1, const KnownBits &Known2) { |
| return KnownBits::smin(Known1, Known2); |
| }, |
| [](const APInt &N1, const APInt &N2) { return APIntOps::smin(N1, N2); }); |
| |
| testBinaryOpExhaustive( |
| [](const KnownBits &Known1, const KnownBits &Known2) { |
| return KnownBits::udiv(Known1, Known2); |
| }, |
| [](const APInt &N1, const APInt &N2) -> std::optional<APInt> { |
| if (N2.isZero()) |
| return std::nullopt; |
| return N1.udiv(N2); |
| }, |
| checkCorrectnessOnlyBinary); |
| testBinaryOpExhaustive( |
| [](const KnownBits &Known1, const KnownBits &Known2) { |
| return KnownBits::udiv(Known1, Known2, /*Exact*/ true); |
| }, |
| [](const APInt &N1, const APInt &N2) -> std::optional<APInt> { |
| if (N2.isZero() || !N1.urem(N2).isZero()) |
| return std::nullopt; |
| return N1.udiv(N2); |
| }, |
| checkCorrectnessOnlyBinary); |
| testBinaryOpExhaustive( |
| [](const KnownBits &Known1, const KnownBits &Known2) { |
| return KnownBits::sdiv(Known1, Known2); |
| }, |
| [](const APInt &N1, const APInt &N2) -> std::optional<APInt> { |
| if (N2.isZero() || (N1.isMinSignedValue() && N2.isAllOnes())) |
| return std::nullopt; |
| return N1.sdiv(N2); |
| }, |
| checkCorrectnessOnlyBinary); |
| testBinaryOpExhaustive( |
| [](const KnownBits &Known1, const KnownBits &Known2) { |
| return KnownBits::sdiv(Known1, Known2, /*Exact*/ true); |
| }, |
| [](const APInt &N1, const APInt &N2) -> std::optional<APInt> { |
| if (N2.isZero() || (N1.isMinSignedValue() && N2.isAllOnes()) || |
| !N1.srem(N2).isZero()) |
| return std::nullopt; |
| return N1.sdiv(N2); |
| }, |
| checkCorrectnessOnlyBinary); |
| testBinaryOpExhaustive( |
| [](const KnownBits &Known1, const KnownBits &Known2) { |
| return KnownBits::urem(Known1, Known2); |
| }, |
| [](const APInt &N1, const APInt &N2) -> std::optional<APInt> { |
| if (N2.isZero()) |
| return std::nullopt; |
| return N1.urem(N2); |
| }, |
| checkCorrectnessOnlyBinary); |
| testBinaryOpExhaustive( |
| [](const KnownBits &Known1, const KnownBits &Known2) { |
| return KnownBits::srem(Known1, Known2); |
| }, |
| [](const APInt &N1, const APInt &N2) -> std::optional<APInt> { |
| if (N2.isZero()) |
| return std::nullopt; |
| return N1.srem(N2); |
| }, |
| checkCorrectnessOnlyBinary); |
| testBinaryOpExhaustive( |
| [](const KnownBits &Known1, const KnownBits &Known2) { |
| return KnownBits::sadd_sat(Known1, Known2); |
| }, |
| [](const APInt &N1, const APInt &N2) -> std::optional<APInt> { |
| return N1.sadd_sat(N2); |
| }, |
| checkCorrectnessOnlyBinary); |
| testBinaryOpExhaustive( |
| [](const KnownBits &Known1, const KnownBits &Known2) { |
| return KnownBits::uadd_sat(Known1, Known2); |
| }, |
| [](const APInt &N1, const APInt &N2) -> std::optional<APInt> { |
| return N1.uadd_sat(N2); |
| }, |
| checkCorrectnessOnlyBinary); |
| testBinaryOpExhaustive( |
| [](const KnownBits &Known1, const KnownBits &Known2) { |
| return KnownBits::ssub_sat(Known1, Known2); |
| }, |
| [](const APInt &N1, const APInt &N2) -> std::optional<APInt> { |
| return N1.ssub_sat(N2); |
| }, |
| checkCorrectnessOnlyBinary); |
| testBinaryOpExhaustive( |
| [](const KnownBits &Known1, const KnownBits &Known2) { |
| return KnownBits::usub_sat(Known1, Known2); |
| }, |
| [](const APInt &N1, const APInt &N2) -> std::optional<APInt> { |
| return N1.usub_sat(N2); |
| }, |
| checkCorrectnessOnlyBinary); |
| testBinaryOpExhaustive( |
| [](const KnownBits &Known1, const KnownBits &Known2) { |
| return KnownBits::shl(Known1, Known2); |
| }, |
| [](const APInt &N1, const APInt &N2) -> std::optional<APInt> { |
| if (N2.uge(N2.getBitWidth())) |
| return std::nullopt; |
| return N1.shl(N2); |
| }, |
| checkOptimalityBinary, /* RefinePoisonToZero */ true); |
| testBinaryOpExhaustive( |
| [](const KnownBits &Known1, const KnownBits &Known2) { |
| return KnownBits::shl(Known1, Known2, /* NUW */ true); |
| }, |
| [](const APInt &N1, const APInt &N2) -> std::optional<APInt> { |
| bool Overflow; |
| APInt Res = N1.ushl_ov(N2, Overflow); |
| if (Overflow) |
| return std::nullopt; |
| return Res; |
| }, |
| checkOptimalityBinary, /* RefinePoisonToZero */ true); |
| testBinaryOpExhaustive( |
| [](const KnownBits &Known1, const KnownBits &Known2) { |
| return KnownBits::shl(Known1, Known2, /* NUW */ false, /* NSW */ true); |
| }, |
| [](const APInt &N1, const APInt &N2) -> std::optional<APInt> { |
| bool Overflow; |
| APInt Res = N1.sshl_ov(N2, Overflow); |
| if (Overflow) |
| return std::nullopt; |
| return Res; |
| }, |
| checkOptimalityBinary, /* RefinePoisonToZero */ true); |
| testBinaryOpExhaustive( |
| [](const KnownBits &Known1, const KnownBits &Known2) { |
| return KnownBits::shl(Known1, Known2, /* NUW */ true, /* NSW */ true); |
| }, |
| [](const APInt &N1, const APInt &N2) -> std::optional<APInt> { |
| bool OverflowUnsigned, OverflowSigned; |
| APInt Res = N1.ushl_ov(N2, OverflowUnsigned); |
| (void)N1.sshl_ov(N2, OverflowSigned); |
| if (OverflowUnsigned || OverflowSigned) |
| return std::nullopt; |
| return Res; |
| }, |
| checkOptimalityBinary, /* RefinePoisonToZero */ true); |
| |
| testBinaryOpExhaustive( |
| [](const KnownBits &Known1, const KnownBits &Known2) { |
| return KnownBits::lshr(Known1, Known2); |
| }, |
| [](const APInt &N1, const APInt &N2) -> std::optional<APInt> { |
| if (N2.uge(N2.getBitWidth())) |
| return std::nullopt; |
| return N1.lshr(N2); |
| }, |
| checkOptimalityBinary, /* RefinePoisonToZero */ true); |
| testBinaryOpExhaustive( |
| [](const KnownBits &Known1, const KnownBits &Known2) { |
| return KnownBits::ashr(Known1, Known2); |
| }, |
| [](const APInt &N1, const APInt &N2) -> std::optional<APInt> { |
| if (N2.uge(N2.getBitWidth())) |
| return std::nullopt; |
| return N1.ashr(N2); |
| }, |
| checkOptimalityBinary, /* RefinePoisonToZero */ true); |
| |
| testBinaryOpExhaustive( |
| [](const KnownBits &Known1, const KnownBits &Known2) { |
| return KnownBits::mul(Known1, Known2); |
| }, |
| [](const APInt &N1, const APInt &N2) { return N1 * N2; }, |
| checkCorrectnessOnlyBinary); |
| testBinaryOpExhaustive( |
| [](const KnownBits &Known1, const KnownBits &Known2) { |
| return KnownBits::mulhs(Known1, Known2); |
| }, |
| [](const APInt &N1, const APInt &N2) { |
| unsigned Bits = N1.getBitWidth(); |
| return (N1.sext(2 * Bits) * N2.sext(2 * Bits)).extractBits(Bits, Bits); |
| }, |
| checkCorrectnessOnlyBinary); |
| testBinaryOpExhaustive( |
| [](const KnownBits &Known1, const KnownBits &Known2) { |
| return KnownBits::mulhu(Known1, Known2); |
| }, |
| [](const APInt &N1, const APInt &N2) { |
| unsigned Bits = N1.getBitWidth(); |
| return (N1.zext(2 * Bits) * N2.zext(2 * Bits)).extractBits(Bits, Bits); |
| }, |
| checkCorrectnessOnlyBinary); |
| } |
| |
| TEST(KnownBitsTest, UnaryExhaustive) { |
| testUnaryOpExhaustive([](const KnownBits &Known) { return Known.abs(); }, |
| [](const APInt &N) { return N.abs(); }); |
| |
| testUnaryOpExhaustive([](const KnownBits &Known) { return Known.abs(true); }, |
| [](const APInt &N) -> std::optional<APInt> { |
| if (N.isMinSignedValue()) |
| return std::nullopt; |
| return N.abs(); |
| }); |
| |
| testUnaryOpExhaustive([](const KnownBits &Known) { return Known.blsi(); }, |
| [](const APInt &N) { return N & -N; }); |
| testUnaryOpExhaustive([](const KnownBits &Known) { return Known.blsmsk(); }, |
| [](const APInt &N) { return N ^ (N - 1); }); |
| |
| testUnaryOpExhaustive( |
| [](const KnownBits &Known) { |
| return KnownBits::mul(Known, Known, /*SelfMultiply*/ true); |
| }, |
| [](const APInt &N) { return N * N; }, checkCorrectnessOnlyUnary); |
| } |
| |
| TEST(KnownBitsTest, WideShifts) { |
| unsigned BitWidth = 128; |
| KnownBits Unknown(BitWidth); |
| KnownBits AllOnes = KnownBits::makeConstant(APInt::getAllOnes(BitWidth)); |
| |
| KnownBits ShlResult(BitWidth); |
| ShlResult.makeNegative(); |
| EXPECT_EQ(KnownBits::shl(AllOnes, Unknown), ShlResult); |
| KnownBits LShrResult(BitWidth); |
| LShrResult.One.setBit(0); |
| EXPECT_EQ(KnownBits::lshr(AllOnes, Unknown), LShrResult); |
| EXPECT_EQ(KnownBits::ashr(AllOnes, Unknown), AllOnes); |
| } |
| |
| TEST(KnownBitsTest, ICmpExhaustive) { |
| unsigned Bits = 4; |
| ForeachKnownBits(Bits, [&](const KnownBits &Known1) { |
| ForeachKnownBits(Bits, [&](const KnownBits &Known2) { |
| bool AllEQ = true, NoneEQ = true; |
| bool AllNE = true, NoneNE = true; |
| bool AllUGT = true, NoneUGT = true; |
| bool AllUGE = true, NoneUGE = true; |
| bool AllULT = true, NoneULT = true; |
| bool AllULE = true, NoneULE = true; |
| bool AllSGT = true, NoneSGT = true; |
| bool AllSGE = true, NoneSGE = true; |
| bool AllSLT = true, NoneSLT = true; |
| bool AllSLE = true, NoneSLE = true; |
| |
| ForeachNumInKnownBits(Known1, [&](const APInt &N1) { |
| ForeachNumInKnownBits(Known2, [&](const APInt &N2) { |
| AllEQ &= N1.eq(N2); |
| AllNE &= N1.ne(N2); |
| AllUGT &= N1.ugt(N2); |
| AllUGE &= N1.uge(N2); |
| AllULT &= N1.ult(N2); |
| AllULE &= N1.ule(N2); |
| AllSGT &= N1.sgt(N2); |
| AllSGE &= N1.sge(N2); |
| AllSLT &= N1.slt(N2); |
| AllSLE &= N1.sle(N2); |
| NoneEQ &= !N1.eq(N2); |
| NoneNE &= !N1.ne(N2); |
| NoneUGT &= !N1.ugt(N2); |
| NoneUGE &= !N1.uge(N2); |
| NoneULT &= !N1.ult(N2); |
| NoneULE &= !N1.ule(N2); |
| NoneSGT &= !N1.sgt(N2); |
| NoneSGE &= !N1.sge(N2); |
| NoneSLT &= !N1.slt(N2); |
| NoneSLE &= !N1.sle(N2); |
| }); |
| }); |
| |
| std::optional<bool> KnownEQ = KnownBits::eq(Known1, Known2); |
| std::optional<bool> KnownNE = KnownBits::ne(Known1, Known2); |
| std::optional<bool> KnownUGT = KnownBits::ugt(Known1, Known2); |
| std::optional<bool> KnownUGE = KnownBits::uge(Known1, Known2); |
| std::optional<bool> KnownULT = KnownBits::ult(Known1, Known2); |
| std::optional<bool> KnownULE = KnownBits::ule(Known1, Known2); |
| std::optional<bool> KnownSGT = KnownBits::sgt(Known1, Known2); |
| std::optional<bool> KnownSGE = KnownBits::sge(Known1, Known2); |
| std::optional<bool> KnownSLT = KnownBits::slt(Known1, Known2); |
| std::optional<bool> KnownSLE = KnownBits::sle(Known1, Known2); |
| |
| EXPECT_EQ(AllEQ || NoneEQ, KnownEQ.has_value()); |
| EXPECT_EQ(AllNE || NoneNE, KnownNE.has_value()); |
| EXPECT_EQ(AllUGT || NoneUGT, KnownUGT.has_value()); |
| EXPECT_EQ(AllUGE || NoneUGE, KnownUGE.has_value()); |
| EXPECT_EQ(AllULT || NoneULT, KnownULT.has_value()); |
| EXPECT_EQ(AllULE || NoneULE, KnownULE.has_value()); |
| EXPECT_EQ(AllSGT || NoneSGT, KnownSGT.has_value()); |
| EXPECT_EQ(AllSGE || NoneSGE, KnownSGE.has_value()); |
| EXPECT_EQ(AllSLT || NoneSLT, KnownSLT.has_value()); |
| EXPECT_EQ(AllSLE || NoneSLE, KnownSLE.has_value()); |
| |
| EXPECT_EQ(AllEQ, KnownEQ.has_value() && *KnownEQ); |
| EXPECT_EQ(AllNE, KnownNE.has_value() && *KnownNE); |
| EXPECT_EQ(AllUGT, KnownUGT.has_value() && *KnownUGT); |
| EXPECT_EQ(AllUGE, KnownUGE.has_value() && *KnownUGE); |
| EXPECT_EQ(AllULT, KnownULT.has_value() && *KnownULT); |
| EXPECT_EQ(AllULE, KnownULE.has_value() && *KnownULE); |
| EXPECT_EQ(AllSGT, KnownSGT.has_value() && *KnownSGT); |
| EXPECT_EQ(AllSGE, KnownSGE.has_value() && *KnownSGE); |
| EXPECT_EQ(AllSLT, KnownSLT.has_value() && *KnownSLT); |
| EXPECT_EQ(AllSLE, KnownSLE.has_value() && *KnownSLE); |
| |
| EXPECT_EQ(NoneEQ, KnownEQ.has_value() && !*KnownEQ); |
| EXPECT_EQ(NoneNE, KnownNE.has_value() && !*KnownNE); |
| EXPECT_EQ(NoneUGT, KnownUGT.has_value() && !*KnownUGT); |
| EXPECT_EQ(NoneUGE, KnownUGE.has_value() && !*KnownUGE); |
| EXPECT_EQ(NoneULT, KnownULT.has_value() && !*KnownULT); |
| EXPECT_EQ(NoneULE, KnownULE.has_value() && !*KnownULE); |
| EXPECT_EQ(NoneSGT, KnownSGT.has_value() && !*KnownSGT); |
| EXPECT_EQ(NoneSGE, KnownSGE.has_value() && !*KnownSGE); |
| EXPECT_EQ(NoneSLT, KnownSLT.has_value() && !*KnownSLT); |
| EXPECT_EQ(NoneSLE, KnownSLE.has_value() && !*KnownSLE); |
| }); |
| }); |
| } |
| |
| TEST(KnownBitsTest, GetMinMaxVal) { |
| unsigned Bits = 4; |
| ForeachKnownBits(Bits, [&](const KnownBits &Known) { |
| APInt Min = APInt::getMaxValue(Bits); |
| APInt Max = APInt::getMinValue(Bits); |
| ForeachNumInKnownBits(Known, [&](const APInt &N) { |
| Min = APIntOps::umin(Min, N); |
| Max = APIntOps::umax(Max, N); |
| }); |
| EXPECT_EQ(Min, Known.getMinValue()); |
| EXPECT_EQ(Max, Known.getMaxValue()); |
| }); |
| } |
| |
| TEST(KnownBitsTest, GetSignedMinMaxVal) { |
| unsigned Bits = 4; |
| ForeachKnownBits(Bits, [&](const KnownBits &Known) { |
| APInt Min = APInt::getSignedMaxValue(Bits); |
| APInt Max = APInt::getSignedMinValue(Bits); |
| ForeachNumInKnownBits(Known, [&](const APInt &N) { |
| Min = APIntOps::smin(Min, N); |
| Max = APIntOps::smax(Max, N); |
| }); |
| EXPECT_EQ(Min, Known.getSignedMinValue()); |
| EXPECT_EQ(Max, Known.getSignedMaxValue()); |
| }); |
| } |
| |
| TEST(KnownBitsTest, CountMaxActiveBits) { |
| unsigned Bits = 4; |
| ForeachKnownBits(Bits, [&](const KnownBits &Known) { |
| unsigned Expected = 0; |
| ForeachNumInKnownBits(Known, [&](const APInt &N) { |
| Expected = std::max(Expected, N.getActiveBits()); |
| }); |
| EXPECT_EQ(Expected, Known.countMaxActiveBits()); |
| }); |
| } |
| |
| TEST(KnownBitsTest, CountMaxSignificantBits) { |
| unsigned Bits = 4; |
| ForeachKnownBits(Bits, [&](const KnownBits &Known) { |
| unsigned Expected = 0; |
| ForeachNumInKnownBits(Known, [&](const APInt &N) { |
| Expected = std::max(Expected, N.getSignificantBits()); |
| }); |
| EXPECT_EQ(Expected, Known.countMaxSignificantBits()); |
| }); |
| } |
| |
| TEST(KnownBitsTest, SExtOrTrunc) { |
| const unsigned NarrowerSize = 4; |
| const unsigned BaseSize = 6; |
| const unsigned WiderSize = 8; |
| APInt NegativeFitsNarrower(BaseSize, -4, /*isSigned*/ true); |
| APInt NegativeDoesntFitNarrower(BaseSize, -28, /*isSigned*/ true); |
| APInt PositiveFitsNarrower(BaseSize, 14); |
| APInt PositiveDoesntFitNarrower(BaseSize, 36); |
| auto InitKnownBits = [&](KnownBits &Res, const APInt &Input) { |
| Res = KnownBits(Input.getBitWidth()); |
| Res.One = Input; |
| Res.Zero = ~Input; |
| }; |
| |
| for (unsigned Size : {NarrowerSize, BaseSize, WiderSize}) { |
| for (const APInt &Input : |
| {NegativeFitsNarrower, NegativeDoesntFitNarrower, PositiveFitsNarrower, |
| PositiveDoesntFitNarrower}) { |
| KnownBits Test; |
| InitKnownBits(Test, Input); |
| KnownBits Baseline; |
| InitKnownBits(Baseline, Input.sextOrTrunc(Size)); |
| Test = Test.sextOrTrunc(Size); |
| EXPECT_EQ(Test, Baseline); |
| } |
| } |
| } |
| |
| TEST(KnownBitsTest, SExtInReg) { |
| unsigned Bits = 4; |
| for (unsigned FromBits = 1; FromBits <= Bits; ++FromBits) { |
| ForeachKnownBits(Bits, [&](const KnownBits &Known) { |
| APInt CommonOne = APInt::getAllOnes(Bits); |
| APInt CommonZero = APInt::getAllOnes(Bits); |
| unsigned ExtBits = Bits - FromBits; |
| ForeachNumInKnownBits(Known, [&](const APInt &N) { |
| APInt Ext = N << ExtBits; |
| Ext.ashrInPlace(ExtBits); |
| CommonOne &= Ext; |
| CommonZero &= ~Ext; |
| }); |
| KnownBits KnownSExtInReg = Known.sextInReg(FromBits); |
| EXPECT_EQ(CommonOne, KnownSExtInReg.One); |
| EXPECT_EQ(CommonZero, KnownSExtInReg.Zero); |
| }); |
| } |
| } |
| |
| TEST(KnownBitsTest, CommonBitsSet) { |
| unsigned Bits = 4; |
| ForeachKnownBits(Bits, [&](const KnownBits &Known1) { |
| ForeachKnownBits(Bits, [&](const KnownBits &Known2) { |
| bool HasCommonBitsSet = false; |
| ForeachNumInKnownBits(Known1, [&](const APInt &N1) { |
| ForeachNumInKnownBits(Known2, [&](const APInt &N2) { |
| HasCommonBitsSet |= N1.intersects(N2); |
| }); |
| }); |
| EXPECT_EQ(!HasCommonBitsSet, |
| KnownBits::haveNoCommonBitsSet(Known1, Known2)); |
| }); |
| }); |
| } |
| |
| TEST(KnownBitsTest, ConcatBits) { |
| unsigned Bits = 4; |
| for (unsigned LoBits = 1; LoBits < Bits; ++LoBits) { |
| unsigned HiBits = Bits - LoBits; |
| ForeachKnownBits(LoBits, [&](const KnownBits &KnownLo) { |
| ForeachKnownBits(HiBits, [&](const KnownBits &KnownHi) { |
| KnownBits KnownAll = KnownHi.concat(KnownLo); |
| |
| EXPECT_EQ(KnownLo.countMinPopulation() + KnownHi.countMinPopulation(), |
| KnownAll.countMinPopulation()); |
| EXPECT_EQ(KnownLo.countMaxPopulation() + KnownHi.countMaxPopulation(), |
| KnownAll.countMaxPopulation()); |
| |
| KnownBits ExtractLo = KnownAll.extractBits(LoBits, 0); |
| KnownBits ExtractHi = KnownAll.extractBits(HiBits, LoBits); |
| |
| EXPECT_EQ(KnownLo.One.getZExtValue(), ExtractLo.One.getZExtValue()); |
| EXPECT_EQ(KnownHi.One.getZExtValue(), ExtractHi.One.getZExtValue()); |
| EXPECT_EQ(KnownLo.Zero.getZExtValue(), ExtractLo.Zero.getZExtValue()); |
| EXPECT_EQ(KnownHi.Zero.getZExtValue(), ExtractHi.Zero.getZExtValue()); |
| }); |
| }); |
| } |
| } |
| |
| } // end anonymous namespace |