1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-10-19 11:02:59 +02:00
llvm-mirror/unittests/Analysis/ValueTrackingTest.cpp
Simon Dardis 186f679a98 Infer lowest bits of an integer Multiply when the low bits of the operands are known
When the lowest bits of the operands to an integer multiply are known, the low bits of the result are deducible.
Code to deduce known-zero bottom bits already existed, but this change improves on that by deducing known-ones.

Patch by: Pedro Ferreira

Reviewers: craig.topper, sanjoy, efriedma

Differential Revision: https://reviews.llvm.org/D34029

llvm-svn: 320269
2017-12-09 23:25:57 +00:00

316 lines
9.8 KiB
C++

//===- ValueTrackingTest.cpp - ValueTracking tests ------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "llvm/Analysis/ValueTracking.h"
#include "llvm/AsmParser/Parser.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/InstIterator.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/KnownBits.h"
#include "gtest/gtest.h"
using namespace llvm;
namespace {
class MatchSelectPatternTest : public testing::Test {
protected:
void parseAssembly(const char *Assembly) {
SMDiagnostic Error;
M = parseAssemblyString(Assembly, Error, Context);
std::string errMsg;
raw_string_ostream os(errMsg);
Error.print("", os);
// A failure here means that the test itself is buggy.
if (!M)
report_fatal_error(os.str());
Function *F = M->getFunction("test");
if (F == nullptr)
report_fatal_error("Test must have a function named @test");
A = nullptr;
for (inst_iterator I = inst_begin(F), E = inst_end(F); I != E; ++I) {
if (I->hasName()) {
if (I->getName() == "A")
A = &*I;
}
}
if (A == nullptr)
report_fatal_error("@test must have an instruction %A");
}
void expectPattern(const SelectPatternResult &P) {
Value *LHS, *RHS;
Instruction::CastOps CastOp;
SelectPatternResult R = matchSelectPattern(A, LHS, RHS, &CastOp);
EXPECT_EQ(P.Flavor, R.Flavor);
EXPECT_EQ(P.NaNBehavior, R.NaNBehavior);
EXPECT_EQ(P.Ordered, R.Ordered);
}
LLVMContext Context;
std::unique_ptr<Module> M;
Instruction *A, *B;
};
}
TEST_F(MatchSelectPatternTest, SimpleFMin) {
parseAssembly(
"define float @test(float %a) {\n"
" %1 = fcmp ult float %a, 5.0\n"
" %A = select i1 %1, float %a, float 5.0\n"
" ret float %A\n"
"}\n");
expectPattern({SPF_FMINNUM, SPNB_RETURNS_NAN, false});
}
TEST_F(MatchSelectPatternTest, SimpleFMax) {
parseAssembly(
"define float @test(float %a) {\n"
" %1 = fcmp ogt float %a, 5.0\n"
" %A = select i1 %1, float %a, float 5.0\n"
" ret float %A\n"
"}\n");
expectPattern({SPF_FMAXNUM, SPNB_RETURNS_OTHER, true});
}
TEST_F(MatchSelectPatternTest, SwappedFMax) {
parseAssembly(
"define float @test(float %a) {\n"
" %1 = fcmp olt float 5.0, %a\n"
" %A = select i1 %1, float %a, float 5.0\n"
" ret float %A\n"
"}\n");
expectPattern({SPF_FMAXNUM, SPNB_RETURNS_OTHER, false});
}
TEST_F(MatchSelectPatternTest, SwappedFMax2) {
parseAssembly(
"define float @test(float %a) {\n"
" %1 = fcmp olt float %a, 5.0\n"
" %A = select i1 %1, float 5.0, float %a\n"
" ret float %A\n"
"}\n");
expectPattern({SPF_FMAXNUM, SPNB_RETURNS_NAN, false});
}
TEST_F(MatchSelectPatternTest, SwappedFMax3) {
parseAssembly(
"define float @test(float %a) {\n"
" %1 = fcmp ult float %a, 5.0\n"
" %A = select i1 %1, float 5.0, float %a\n"
" ret float %A\n"
"}\n");
expectPattern({SPF_FMAXNUM, SPNB_RETURNS_OTHER, true});
}
TEST_F(MatchSelectPatternTest, FastFMin) {
parseAssembly(
"define float @test(float %a) {\n"
" %1 = fcmp nnan olt float %a, 5.0\n"
" %A = select i1 %1, float %a, float 5.0\n"
" ret float %A\n"
"}\n");
expectPattern({SPF_FMINNUM, SPNB_RETURNS_ANY, false});
}
TEST_F(MatchSelectPatternTest, FMinConstantZero) {
parseAssembly(
"define float @test(float %a) {\n"
" %1 = fcmp ole float %a, 0.0\n"
" %A = select i1 %1, float %a, float 0.0\n"
" ret float %A\n"
"}\n");
// This shouldn't be matched, as %a could be -0.0.
expectPattern({SPF_UNKNOWN, SPNB_NA, false});
}
TEST_F(MatchSelectPatternTest, FMinConstantZeroNsz) {
parseAssembly(
"define float @test(float %a) {\n"
" %1 = fcmp nsz ole float %a, 0.0\n"
" %A = select i1 %1, float %a, float 0.0\n"
" ret float %A\n"
"}\n");
// But this should be, because we've ignored signed zeroes.
expectPattern({SPF_FMINNUM, SPNB_RETURNS_OTHER, true});
}
TEST_F(MatchSelectPatternTest, DoubleCastU) {
parseAssembly(
"define i32 @test(i8 %a, i8 %b) {\n"
" %1 = icmp ult i8 %a, %b\n"
" %2 = zext i8 %a to i32\n"
" %3 = zext i8 %b to i32\n"
" %A = select i1 %1, i32 %2, i32 %3\n"
" ret i32 %A\n"
"}\n");
// We should be able to look through the situation where we cast both operands
// to the select.
expectPattern({SPF_UMIN, SPNB_NA, false});
}
TEST_F(MatchSelectPatternTest, DoubleCastS) {
parseAssembly(
"define i32 @test(i8 %a, i8 %b) {\n"
" %1 = icmp slt i8 %a, %b\n"
" %2 = sext i8 %a to i32\n"
" %3 = sext i8 %b to i32\n"
" %A = select i1 %1, i32 %2, i32 %3\n"
" ret i32 %A\n"
"}\n");
// We should be able to look through the situation where we cast both operands
// to the select.
expectPattern({SPF_SMIN, SPNB_NA, false});
}
TEST_F(MatchSelectPatternTest, DoubleCastBad) {
parseAssembly(
"define i32 @test(i8 %a, i8 %b) {\n"
" %1 = icmp ult i8 %a, %b\n"
" %2 = zext i8 %a to i32\n"
" %3 = sext i8 %b to i32\n"
" %A = select i1 %1, i32 %2, i32 %3\n"
" ret i32 %A\n"
"}\n");
// The cast types here aren't the same, so we cannot match an UMIN.
expectPattern({SPF_UNKNOWN, SPNB_NA, false});
}
TEST(ValueTracking, GuaranteedToTransferExecutionToSuccessor) {
StringRef Assembly =
"declare void @nounwind_readonly(i32*) nounwind readonly "
"declare void @nounwind_argmemonly(i32*) nounwind argmemonly "
"declare void @throws_but_readonly(i32*) readonly "
"declare void @throws_but_argmemonly(i32*) argmemonly "
" "
"declare void @unknown(i32*) "
" "
"define void @f(i32* %p) { "
" call void @nounwind_readonly(i32* %p) "
" call void @nounwind_argmemonly(i32* %p) "
" call void @throws_but_readonly(i32* %p) "
" call void @throws_but_argmemonly(i32* %p) "
" call void @unknown(i32* %p) nounwind readonly "
" call void @unknown(i32* %p) nounwind argmemonly "
" call void @unknown(i32* %p) readonly "
" call void @unknown(i32* %p) argmemonly "
" ret void "
"} ";
LLVMContext Context;
SMDiagnostic Error;
auto M = parseAssemblyString(Assembly, Error, Context);
assert(M && "Bad assembly?");
auto *F = M->getFunction("f");
assert(F && "Bad assembly?");
auto &BB = F->getEntryBlock();
bool ExpectedAnswers[] = {
true, // call void @nounwind_readonly(i32* %p)
true, // call void @nounwind_argmemonly(i32* %p)
false, // call void @throws_but_readonly(i32* %p)
false, // call void @throws_but_argmemonly(i32* %p)
true, // call void @unknown(i32* %p) nounwind readonly
true, // call void @unknown(i32* %p) nounwind argmemonly
false, // call void @unknown(i32* %p) readonly
false, // call void @unknown(i32* %p) argmemonly
false, // ret void
};
int Index = 0;
for (auto &I : BB) {
EXPECT_EQ(isGuaranteedToTransferExecutionToSuccessor(&I),
ExpectedAnswers[Index])
<< "Incorrect answer at instruction " << Index << " = " << I;
Index++;
}
}
TEST(ValueTracking, ComputeNumSignBits_PR32045) {
StringRef Assembly = "define i32 @f(i32 %a) { "
" %val = ashr i32 %a, -1 "
" ret i32 %val "
"} ";
LLVMContext Context;
SMDiagnostic Error;
auto M = parseAssemblyString(Assembly, Error, Context);
assert(M && "Bad assembly?");
auto *F = M->getFunction("f");
assert(F && "Bad assembly?");
auto *RVal =
cast<ReturnInst>(F->getEntryBlock().getTerminator())->getOperand(0);
EXPECT_EQ(ComputeNumSignBits(RVal, M->getDataLayout()), 1u);
}
TEST(ValueTracking, ComputeKnownBits) {
StringRef Assembly = "define i32 @f(i32 %a, i32 %b) { "
" %ash = mul i32 %a, 8 "
" %aad = add i32 %ash, 7 "
" %aan = and i32 %aad, 4095 "
" %bsh = shl i32 %b, 4 "
" %bad = or i32 %bsh, 6 "
" %ban = and i32 %bad, 4095 "
" %mul = mul i32 %aan, %ban "
" ret i32 %mul "
"} ";
LLVMContext Context;
SMDiagnostic Error;
auto M = parseAssemblyString(Assembly, Error, Context);
assert(M && "Bad assembly?");
auto *F = M->getFunction("f");
assert(F && "Bad assembly?");
auto *RVal =
cast<ReturnInst>(F->getEntryBlock().getTerminator())->getOperand(0);
auto Known = computeKnownBits(RVal, M->getDataLayout());
ASSERT_FALSE(Known.hasConflict());
EXPECT_EQ(Known.One.getZExtValue(), 10u);
EXPECT_EQ(Known.Zero.getZExtValue(), 4278190085u);
}
TEST(ValueTracking, ComputeKnownMulBits) {
StringRef Assembly = "define i32 @f(i32 %a, i32 %b) { "
" %aa = shl i32 %a, 5 "
" %bb = shl i32 %b, 5 "
" %aaa = or i32 %aa, 24 "
" %bbb = or i32 %bb, 28 "
" %mul = mul i32 %aaa, %bbb "
" ret i32 %mul "
"} ";
LLVMContext Context;
SMDiagnostic Error;
auto M = parseAssemblyString(Assembly, Error, Context);
assert(M && "Bad assembly?");
auto *F = M->getFunction("f");
assert(F && "Bad assembly?");
auto *RVal =
cast<ReturnInst>(F->getEntryBlock().getTerminator())->getOperand(0);
auto Known = computeKnownBits(RVal, M->getDataLayout());
ASSERT_FALSE(Known.hasConflict());
EXPECT_EQ(Known.One.getZExtValue(), 32u);
EXPECT_EQ(Known.Zero.getZExtValue(), 95u);
}