mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-23 11:13:28 +01:00
c5917e5476
Same as r311392 with some fixes for library dependencies. Thanks to Chapuni for helping work those out! Original commit message: This introduces the FuzzMutate library, which provides structured fuzzing for LLVM IR, as described in my EuroLLVM 2017 talk. Most of the basic mutators to inject and delete IR are provided, with support for most basic operations. llvm-svn: 311402
324 lines
12 KiB
C++
324 lines
12 KiB
C++
//===- OperationsTest.cpp - Tests for fuzzer operations -------------------===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/FuzzMutate/Operations.h"
|
|
#include "llvm/FuzzMutate/OpDescriptor.h"
|
|
#include "llvm/IR/Constants.h"
|
|
#include "llvm/IR/Instructions.h"
|
|
#include "llvm/IR/Module.h"
|
|
#include "llvm/IR/Verifier.h"
|
|
#include "gmock/gmock.h"
|
|
#include "gtest/gtest.h"
|
|
#include <iostream>
|
|
|
|
// Define some pretty printers to help with debugging failures.
|
|
namespace llvm {
|
|
void PrintTo(Type *T, ::std::ostream *OS) {
|
|
raw_os_ostream ROS(*OS);
|
|
T->print(ROS);
|
|
}
|
|
|
|
void PrintTo(BasicBlock *BB, ::std::ostream *OS) {
|
|
raw_os_ostream ROS(*OS);
|
|
ROS << BB << " (" << BB->getName() << ")";
|
|
}
|
|
|
|
void PrintTo(Value *V, ::std::ostream *OS) {
|
|
raw_os_ostream ROS(*OS);
|
|
ROS << V << " (";
|
|
V->print(ROS);
|
|
ROS << ")";
|
|
}
|
|
void PrintTo(Constant *C, ::std::ostream *OS) { PrintTo(cast<Value>(C), OS); }
|
|
|
|
} // namespace llvm
|
|
|
|
using namespace llvm;
|
|
|
|
using testing::AllOf;
|
|
using testing::AnyOf;
|
|
using testing::ElementsAre;
|
|
using testing::Eq;
|
|
using testing::Ge;
|
|
using testing::Each;
|
|
using testing::Truly;
|
|
using testing::NotNull;
|
|
using testing::PrintToString;
|
|
using testing::SizeIs;
|
|
|
|
MATCHER_P(TypesMatch, V, "has type " + PrintToString(V->getType())) {
|
|
return arg->getType() == V->getType();
|
|
}
|
|
MATCHER_P(HasType, T, "") { return arg->getType() == T; }
|
|
|
|
TEST(OperationsTest, SourcePreds) {
|
|
using namespace llvm::fuzzerop;
|
|
|
|
LLVMContext Ctx;
|
|
|
|
Constant *i1 = ConstantInt::getFalse(Ctx);
|
|
Constant *i8 = ConstantInt::get(Type::getInt8Ty(Ctx), 3);
|
|
Constant *i16 = ConstantInt::get(Type::getInt16Ty(Ctx), 1 << 15);
|
|
Constant *i32 = ConstantInt::get(Type::getInt32Ty(Ctx), 0);
|
|
Constant *i64 = ConstantInt::get(Type::getInt64Ty(Ctx),
|
|
std::numeric_limits<uint64_t>::max());
|
|
Constant *f16 = ConstantFP::getInfinity(Type::getHalfTy(Ctx));
|
|
Constant *f32 = ConstantFP::get(Type::getFloatTy(Ctx), 0.0);
|
|
Constant *f64 = ConstantFP::get(Type::getDoubleTy(Ctx), 123.45);
|
|
Constant *s =
|
|
ConstantStruct::get(StructType::create(Ctx, "OpaqueStruct"));
|
|
Constant *a =
|
|
ConstantArray::get(ArrayType::get(i32->getType(), 2), {i32, i32});
|
|
Constant *v8i8 = ConstantVector::getSplat(8, i8);
|
|
Constant *v4f16 = ConstantVector::getSplat(4, f16);
|
|
Constant *p0i32 =
|
|
ConstantPointerNull::get(PointerType::get(i32->getType(), 0));
|
|
|
|
auto OnlyI32 = onlyType(i32->getType());
|
|
EXPECT_TRUE(OnlyI32.matches({}, i32));
|
|
EXPECT_FALSE(OnlyI32.matches({}, i64));
|
|
EXPECT_FALSE(OnlyI32.matches({}, p0i32));
|
|
EXPECT_FALSE(OnlyI32.matches({}, a));
|
|
|
|
EXPECT_THAT(OnlyI32.generate({}, {}),
|
|
AllOf(SizeIs(Ge(1u)), Each(TypesMatch(i32))));
|
|
|
|
auto AnyType = anyType();
|
|
EXPECT_TRUE(AnyType.matches({}, i1));
|
|
EXPECT_TRUE(AnyType.matches({}, f64));
|
|
EXPECT_TRUE(AnyType.matches({}, s));
|
|
EXPECT_TRUE(AnyType.matches({}, v8i8));
|
|
EXPECT_TRUE(AnyType.matches({}, p0i32));
|
|
|
|
EXPECT_THAT(
|
|
AnyType.generate({}, {i32->getType(), f16->getType(), v8i8->getType()}),
|
|
Each(AnyOf(TypesMatch(i32), TypesMatch(f16), TypesMatch(v8i8))));
|
|
|
|
auto AnyInt = anyIntType();
|
|
EXPECT_TRUE(AnyInt.matches({}, i1));
|
|
EXPECT_TRUE(AnyInt.matches({}, i64));
|
|
EXPECT_FALSE(AnyInt.matches({}, f32));
|
|
EXPECT_FALSE(AnyInt.matches({}, v4f16));
|
|
|
|
EXPECT_THAT(
|
|
AnyInt.generate({}, {i32->getType(), f16->getType(), v8i8->getType()}),
|
|
AllOf(SizeIs(Ge(1u)), Each(TypesMatch(i32))));
|
|
|
|
auto AnyFP = anyFloatType();
|
|
EXPECT_TRUE(AnyFP.matches({}, f16));
|
|
EXPECT_TRUE(AnyFP.matches({}, f32));
|
|
EXPECT_FALSE(AnyFP.matches({}, i16));
|
|
EXPECT_FALSE(AnyFP.matches({}, p0i32));
|
|
EXPECT_FALSE(AnyFP.matches({}, v4f16));
|
|
|
|
EXPECT_THAT(
|
|
AnyFP.generate({}, {i32->getType(), f16->getType(), v8i8->getType()}),
|
|
AllOf(SizeIs(Ge(1u)), Each(TypesMatch(f16))));
|
|
|
|
auto AnyPtr = anyPtrType();
|
|
EXPECT_TRUE(AnyPtr.matches({}, p0i32));
|
|
EXPECT_FALSE(AnyPtr.matches({}, i8));
|
|
EXPECT_FALSE(AnyPtr.matches({}, a));
|
|
EXPECT_FALSE(AnyPtr.matches({}, v8i8));
|
|
|
|
auto isPointer = [](Value *V) { return V->getType()->isPointerTy(); };
|
|
EXPECT_THAT(
|
|
AnyPtr.generate({}, {i32->getType(), f16->getType(), v8i8->getType()}),
|
|
AllOf(SizeIs(Ge(3u)), Each(Truly(isPointer))));
|
|
|
|
auto AnyVec = anyVectorType();
|
|
EXPECT_TRUE(AnyVec.matches({}, v8i8));
|
|
EXPECT_TRUE(AnyVec.matches({}, v4f16));
|
|
EXPECT_FALSE(AnyVec.matches({}, i8));
|
|
EXPECT_FALSE(AnyVec.matches({}, a));
|
|
EXPECT_FALSE(AnyVec.matches({}, s));
|
|
|
|
EXPECT_THAT(AnyVec.generate({}, {v8i8->getType()}),
|
|
ElementsAre(TypesMatch(v8i8)));
|
|
|
|
auto First = matchFirstType();
|
|
EXPECT_TRUE(First.matches({i8}, i8));
|
|
EXPECT_TRUE(First.matches({s, a}, s));
|
|
EXPECT_FALSE(First.matches({f16}, f32));
|
|
EXPECT_FALSE(First.matches({v4f16, f64}, f64));
|
|
|
|
EXPECT_THAT(First.generate({i8}, {}), Each(TypesMatch(i8)));
|
|
EXPECT_THAT(First.generate({f16}, {i8->getType()}),
|
|
Each(TypesMatch(f16)));
|
|
EXPECT_THAT(First.generate({v8i8, i32}, {}), Each(TypesMatch(v8i8)));
|
|
}
|
|
|
|
TEST(OperationsTest, SplitBlock) {
|
|
LLVMContext Ctx;
|
|
|
|
Module M("M", Ctx);
|
|
Function *F = Function::Create(FunctionType::get(Type::getVoidTy(Ctx), {},
|
|
/*isVarArg=*/false),
|
|
GlobalValue::ExternalLinkage, "f", &M);
|
|
auto SBOp = fuzzerop::splitBlockDescriptor(1);
|
|
|
|
// Create a block with only a return and split it on the return.
|
|
auto *BB = BasicBlock::Create(Ctx, "BB", F);
|
|
auto *RI = ReturnInst::Create(Ctx, BB);
|
|
SBOp.BuilderFunc({UndefValue::get(Type::getInt1Ty(Ctx))}, RI);
|
|
|
|
// We should end up with an unconditional branch from BB to BB1, and the
|
|
// return ends up in BB1.
|
|
auto *UncondBr = cast<BranchInst>(BB->getTerminator());
|
|
ASSERT_TRUE(UncondBr->isUnconditional());
|
|
auto *BB1 = UncondBr->getSuccessor(0);
|
|
ASSERT_THAT(RI->getParent(), Eq(BB1));
|
|
|
|
// Now add an instruction to BB1 and split on that.
|
|
auto *AI = new AllocaInst(Type::getInt8Ty(Ctx), 0, "a", RI);
|
|
Value *Cond = ConstantInt::getFalse(Ctx);
|
|
SBOp.BuilderFunc({Cond}, AI);
|
|
|
|
// We should end up with a loop back on BB1 and the instruction we split on
|
|
// moves to BB2.
|
|
auto *CondBr = cast<BranchInst>(BB1->getTerminator());
|
|
EXPECT_THAT(CondBr->getCondition(), Eq(Cond));
|
|
ASSERT_THAT(CondBr->getNumSuccessors(), Eq(2u));
|
|
ASSERT_THAT(CondBr->getSuccessor(0), Eq(BB1));
|
|
auto *BB2 = CondBr->getSuccessor(1);
|
|
EXPECT_THAT(AI->getParent(), Eq(BB2));
|
|
EXPECT_THAT(RI->getParent(), Eq(BB2));
|
|
|
|
EXPECT_FALSE(verifyModule(M, &errs()));
|
|
}
|
|
|
|
TEST(OperationsTest, SplitBlockWithPhis) {
|
|
LLVMContext Ctx;
|
|
|
|
Type *Int8Ty = Type::getInt8Ty(Ctx);
|
|
|
|
Module M("M", Ctx);
|
|
Function *F = Function::Create(FunctionType::get(Type::getVoidTy(Ctx), {},
|
|
/*isVarArg=*/false),
|
|
GlobalValue::ExternalLinkage, "f", &M);
|
|
auto SBOp = fuzzerop::splitBlockDescriptor(1);
|
|
|
|
// Create 3 blocks with an if-then branch.
|
|
auto *BB1 = BasicBlock::Create(Ctx, "BB1", F);
|
|
auto *BB2 = BasicBlock::Create(Ctx, "BB2", F);
|
|
auto *BB3 = BasicBlock::Create(Ctx, "BB3", F);
|
|
BranchInst::Create(BB2, BB3, ConstantInt::getFalse(Ctx), BB1);
|
|
BranchInst::Create(BB3, BB2);
|
|
|
|
// Set up phi nodes selecting values for the incoming edges.
|
|
auto *PHI1 = PHINode::Create(Int8Ty, /*NumReservedValues=*/2, "p1", BB3);
|
|
PHI1->addIncoming(ConstantInt::get(Int8Ty, 0), BB1);
|
|
PHI1->addIncoming(ConstantInt::get(Int8Ty, 1), BB2);
|
|
auto *PHI2 = PHINode::Create(Int8Ty, /*NumReservedValues=*/2, "p2", BB3);
|
|
PHI2->addIncoming(ConstantInt::get(Int8Ty, 1), BB1);
|
|
PHI2->addIncoming(ConstantInt::get(Int8Ty, 0), BB2);
|
|
auto *RI = ReturnInst::Create(Ctx, BB3);
|
|
|
|
// Now we split the block with PHI nodes, making sure they're all updated.
|
|
Value *Cond = ConstantInt::getFalse(Ctx);
|
|
SBOp.BuilderFunc({Cond}, RI);
|
|
|
|
// Make sure the PHIs are updated with a value for the third incoming edge.
|
|
EXPECT_THAT(PHI1->getNumIncomingValues(), Eq(3u));
|
|
EXPECT_THAT(PHI2->getNumIncomingValues(), Eq(3u));
|
|
EXPECT_FALSE(verifyModule(M, &errs()));
|
|
}
|
|
|
|
TEST(OperationsTest, GEP) {
|
|
LLVMContext Ctx;
|
|
|
|
Type *Int8PtrTy = Type::getInt8PtrTy(Ctx);
|
|
Type *Int32Ty = Type::getInt32Ty(Ctx);
|
|
|
|
Module M("M", Ctx);
|
|
Function *F = Function::Create(FunctionType::get(Type::getVoidTy(Ctx), {},
|
|
/*isVarArg=*/false),
|
|
GlobalValue::ExternalLinkage, "f", &M);
|
|
auto *BB = BasicBlock::Create(Ctx, "BB", F);
|
|
auto *RI = ReturnInst::Create(Ctx, BB);
|
|
|
|
auto GEPOp = fuzzerop::gepDescriptor(1);
|
|
EXPECT_TRUE(GEPOp.SourcePreds[0].matches({}, UndefValue::get(Int8PtrTy)));
|
|
EXPECT_TRUE(GEPOp.SourcePreds[1].matches({UndefValue::get(Int8PtrTy)},
|
|
ConstantInt::get(Int32Ty, 0)));
|
|
|
|
GEPOp.BuilderFunc({UndefValue::get(Int8PtrTy), ConstantInt::get(Int32Ty, 0)},
|
|
RI);
|
|
EXPECT_FALSE(verifyModule(M, &errs()));
|
|
}
|
|
|
|
TEST(OperationsTest, ExtractAndInsertValue) {
|
|
LLVMContext Ctx;
|
|
|
|
Type *Int8PtrTy = Type::getInt8PtrTy(Ctx);
|
|
Type *Int32Ty = Type::getInt32Ty(Ctx);
|
|
Type *Int64Ty = Type::getInt64Ty(Ctx);
|
|
|
|
Type *StructTy = StructType::create(Ctx, {Int8PtrTy, Int32Ty});
|
|
Type *OpaqueTy = StructType::create(Ctx, "OpaqueStruct");
|
|
Type *ArrayTy = ArrayType::get(Int64Ty, 4);
|
|
Type *VectorTy = VectorType::get(Int32Ty, 2);
|
|
|
|
auto EVOp = fuzzerop::extractValueDescriptor(1);
|
|
auto IVOp = fuzzerop::insertValueDescriptor(1);
|
|
|
|
// Sanity check the source preds.
|
|
Constant *SVal = UndefValue::get(StructTy);
|
|
Constant *OVal = UndefValue::get(OpaqueTy);
|
|
Constant *AVal = UndefValue::get(ArrayTy);
|
|
Constant *VVal = UndefValue::get(VectorTy);
|
|
|
|
EXPECT_TRUE(EVOp.SourcePreds[0].matches({}, SVal));
|
|
EXPECT_TRUE(EVOp.SourcePreds[0].matches({}, OVal));
|
|
EXPECT_TRUE(EVOp.SourcePreds[0].matches({}, AVal));
|
|
EXPECT_FALSE(EVOp.SourcePreds[0].matches({}, VVal));
|
|
EXPECT_TRUE(IVOp.SourcePreds[0].matches({}, SVal));
|
|
EXPECT_TRUE(IVOp.SourcePreds[0].matches({}, OVal));
|
|
EXPECT_TRUE(IVOp.SourcePreds[0].matches({}, AVal));
|
|
EXPECT_FALSE(IVOp.SourcePreds[0].matches({}, VVal));
|
|
|
|
// Make sure we're range checking appropriately.
|
|
EXPECT_TRUE(
|
|
EVOp.SourcePreds[1].matches({SVal}, ConstantInt::get(Int32Ty, 0)));
|
|
EXPECT_TRUE(
|
|
EVOp.SourcePreds[1].matches({SVal}, ConstantInt::get(Int32Ty, 1)));
|
|
EXPECT_FALSE(
|
|
EVOp.SourcePreds[1].matches({SVal}, ConstantInt::get(Int32Ty, 2)));
|
|
EXPECT_FALSE(
|
|
EVOp.SourcePreds[1].matches({OVal}, ConstantInt::get(Int32Ty, 0)));
|
|
EXPECT_FALSE(
|
|
EVOp.SourcePreds[1].matches({OVal}, ConstantInt::get(Int32Ty, 65536)));
|
|
EXPECT_TRUE(
|
|
EVOp.SourcePreds[1].matches({AVal}, ConstantInt::get(Int32Ty, 0)));
|
|
EXPECT_TRUE(
|
|
EVOp.SourcePreds[1].matches({AVal}, ConstantInt::get(Int32Ty, 3)));
|
|
EXPECT_FALSE(
|
|
EVOp.SourcePreds[1].matches({AVal}, ConstantInt::get(Int32Ty, 4)));
|
|
|
|
EXPECT_THAT(
|
|
EVOp.SourcePreds[1].generate({SVal}, {}),
|
|
ElementsAre(ConstantInt::get(Int32Ty, 0), ConstantInt::get(Int32Ty, 1)));
|
|
|
|
// InsertValue should accept any type in the struct, but only in positions
|
|
// where it makes sense.
|
|
EXPECT_TRUE(IVOp.SourcePreds[1].matches({SVal}, UndefValue::get(Int8PtrTy)));
|
|
EXPECT_TRUE(IVOp.SourcePreds[1].matches({SVal}, UndefValue::get(Int32Ty)));
|
|
EXPECT_FALSE(IVOp.SourcePreds[1].matches({SVal}, UndefValue::get(Int64Ty)));
|
|
EXPECT_FALSE(IVOp.SourcePreds[2].matches({SVal, UndefValue::get(Int32Ty)},
|
|
ConstantInt::get(Int32Ty, 0)));
|
|
EXPECT_TRUE(IVOp.SourcePreds[2].matches({SVal, UndefValue::get(Int32Ty)},
|
|
ConstantInt::get(Int32Ty, 1)));
|
|
|
|
EXPECT_THAT(IVOp.SourcePreds[1].generate({SVal}, {}),
|
|
Each(AnyOf(HasType(Int32Ty), HasType(Int8PtrTy))));
|
|
EXPECT_THAT(
|
|
IVOp.SourcePreds[2].generate({SVal, ConstantInt::get(Int32Ty, 0)}, {}),
|
|
ElementsAre(ConstantInt::get(Int32Ty, 1)));
|
|
}
|