261 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			261 lines
		
	
	
		
			7.9 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 "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);
 | |
| }
 |