diff --git a/llvm/include/llvm/FuzzMutate/OpDescriptor.h b/llvm/include/llvm/FuzzMutate/OpDescriptor.h --- a/llvm/include/llvm/FuzzMutate/OpDescriptor.h +++ b/llvm/include/llvm/FuzzMutate/OpDescriptor.h @@ -18,6 +18,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/IR/Constants.h" #include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/InstrTypes.h" #include "llvm/IR/Type.h" #include "llvm/IR/Value.h" #include @@ -117,6 +118,20 @@ return {Pred, Make}; } +static inline SourcePred anyIntOrVecIntType() { + auto Pred = [](ArrayRef, const Value *V) { + return V->getType()->isIntOrIntVectorTy(); + }; + return {Pred, std::nullopt}; +} + +static inline SourcePred boolOrVecBoolType() { + auto Pred = [](ArrayRef, const Value *V) { + return V->getType()->isIntOrIntVectorTy(1); + }; + return {Pred, std::nullopt}; +} + static inline SourcePred anyFloatType() { auto Pred = [](ArrayRef, const Value *V) { return V->getType()->isFloatingPointTy(); @@ -125,6 +140,13 @@ return {Pred, Make}; } +static inline SourcePred anyFloatOrVecFloatType() { + auto Pred = [](ArrayRef, const Value *V) { + return V->getType()->isFPOrFPVectorTy(); + }; + return {Pred, std::nullopt}; +} + static inline SourcePred anyPtrType() { auto Pred = [](ArrayRef, const Value *V) { return V->getType()->isPointerTy() && !V->isSwiftError(); @@ -161,6 +183,54 @@ return {Pred, Make}; } +static inline SourcePred matchFirstLengthWAnyType() { + auto Pred = [](ArrayRef Cur, const Value *V) { + assert(!Cur.empty() && "No first source yet"); + Type *This = V->getType(), *First = Cur[0]->getType(); + VectorType *ThisVec = dyn_cast(This); + VectorType *FirstVec = dyn_cast(First); + if (ThisVec && FirstVec) { + return ThisVec->getElementCount() == FirstVec->getElementCount(); + } + return (ThisVec == nullptr) && (FirstVec == nullptr) && (!This->isVoidTy()); + }; + auto Make = [](ArrayRef Cur, ArrayRef BaseTypes) { + assert(!Cur.empty() && "No first source yet"); + std::vector Result; + ElementCount EC; + bool isVec = false; + if (VectorType *VecTy = dyn_cast(Cur[0]->getType())) { + EC = VecTy->getElementCount(); + isVec = true; + } + for (Type *T : BaseTypes) { + if (VectorType::isValidElementType(T)) { + if (isVec) + // If the first pred is , make the result + makeConstantsWithType(VectorType::get(T, EC), Result); + else + makeConstantsWithType(T, Result); + } + } + assert(!Result.empty() && "No potential constants."); + return Result; + }; + return {Pred, Make}; +} + +/// Match values that have the same type as the first source. +static inline SourcePred matchSecondType() { + auto Pred = [](ArrayRef Cur, const Value *V) { + assert((Cur.size() > 1) && "No second source yet"); + return V->getType() == Cur[1]->getType(); + }; + auto Make = [](ArrayRef Cur, ArrayRef) { + assert((Cur.size() > 1) && "No second source yet"); + return makeConstantsWithType(Cur[1]->getType()); + }; + return {Pred, Make}; +} + static inline SourcePred anyAggregateType() { auto Pred = [](ArrayRef, const Value *V) { // We can't index zero sized arrays. diff --git a/llvm/include/llvm/FuzzMutate/Operations.h b/llvm/include/llvm/FuzzMutate/Operations.h --- a/llvm/include/llvm/FuzzMutate/Operations.h +++ b/llvm/include/llvm/FuzzMutate/Operations.h @@ -28,12 +28,16 @@ void describeFuzzerPointerOps(std::vector &Ops); void describeFuzzerAggregateOps(std::vector &Ops); void describeFuzzerVectorOps(std::vector &Ops); +void describeFuzzerUnaryOperations(std::vector &Ops); +void describeFuzzerOtherOps(std::vector &Ops); /// @} namespace fuzzerop { /// Descriptors for individual operations. /// @{ +OpDescriptor selectDescriptor(unsigned Weight); +OpDescriptor fnegDescriptor(unsigned Weight); OpDescriptor binOpDescriptor(unsigned Weight, Instruction::BinaryOps Op); OpDescriptor cmpOpDescriptor(unsigned Weight, Instruction::OtherOps CmpOp, CmpInst::Predicate Pred); @@ -44,6 +48,7 @@ OpDescriptor extractElementDescriptor(unsigned Weight); OpDescriptor insertElementDescriptor(unsigned Weight); OpDescriptor shuffleVectorDescriptor(unsigned Weight); + /// @} } // namespace fuzzerop diff --git a/llvm/lib/FuzzMutate/OpDescriptor.cpp b/llvm/lib/FuzzMutate/OpDescriptor.cpp --- a/llvm/lib/FuzzMutate/OpDescriptor.cpp +++ b/llvm/lib/FuzzMutate/OpDescriptor.cpp @@ -15,6 +15,9 @@ void fuzzerop::makeConstantsWithType(Type *T, std::vector &Cs) { if (auto *IntTy = dyn_cast(T)) { uint64_t W = IntTy->getBitWidth(); + Cs.push_back(ConstantInt::get(IntTy, 0)); + Cs.push_back(ConstantInt::get(IntTy, 1)); + Cs.push_back(ConstantInt::get(IntTy, 42)); Cs.push_back(ConstantInt::get(IntTy, APInt::getMaxValue(W))); Cs.push_back(ConstantInt::get(IntTy, APInt::getMinValue(W))); Cs.push_back(ConstantInt::get(IntTy, APInt::getSignedMaxValue(W))); @@ -24,10 +27,24 @@ auto &Ctx = T->getContext(); auto &Sem = T->getFltSemantics(); Cs.push_back(ConstantFP::get(Ctx, APFloat::getZero(Sem))); + Cs.push_back(ConstantFP::get(Ctx, APFloat(Sem, 1))); + Cs.push_back(ConstantFP::get(Ctx, APFloat(Sem, 42))); Cs.push_back(ConstantFP::get(Ctx, APFloat::getLargest(Sem))); Cs.push_back(ConstantFP::get(Ctx, APFloat::getSmallest(Sem))); - } else + Cs.push_back(ConstantFP::get(Ctx, APFloat::getInf(Sem))); + Cs.push_back(ConstantFP::get(Ctx, APFloat::getNaN(Sem))); + } else if (VectorType *VecTy = dyn_cast(T)) { + std::vector EleCs; + Type *EltTy = VecTy->getElementType(); + makeConstantsWithType(EltTy, EleCs); + ElementCount EC = VecTy->getElementCount(); + for (Constant *Elt : EleCs) { + Cs.push_back(ConstantVector::getSplat(EC, Elt)); + } + } else { Cs.push_back(UndefValue::get(T)); + Cs.push_back(PoisonValue::get(T)); + } } std::vector fuzzerop::makeConstantsWithType(Type *T) { diff --git a/llvm/lib/FuzzMutate/Operations.cpp b/llvm/lib/FuzzMutate/Operations.cpp --- a/llvm/lib/FuzzMutate/Operations.cpp +++ b/llvm/lib/FuzzMutate/Operations.cpp @@ -67,11 +67,20 @@ Ops.push_back(cmpOpDescriptor(1, Instruction::FCmp, CmpInst::FCMP_TRUE)); } +void llvm::describeFuzzerUnaryOperations( + std::vector &Ops) { + Ops.push_back(fnegDescriptor(1)); +} + void llvm::describeFuzzerControlFlowOps( std::vector &Ops) { Ops.push_back(splitBlockDescriptor(1)); } +void llvm::describeFuzzerOtherOps(std::vector &Ops) { + Ops.push_back(selectDescriptor(1)); +} + void llvm::describeFuzzerPointerOps(std::vector &Ops) { Ops.push_back(gepDescriptor(1)); } @@ -88,6 +97,22 @@ Ops.push_back(shuffleVectorDescriptor(1)); } +OpDescriptor llvm::fuzzerop::selectDescriptor(unsigned Weight) { + auto buildOp = [](ArrayRef Srcs, Instruction *Inst) { + return SelectInst::Create(Srcs[0], Srcs[1], Srcs[2], "S", Inst); + }; + return {Weight, + {boolOrVecBoolType(), matchFirstLengthWAnyType(), matchSecondType()}, + buildOp}; +} + +OpDescriptor llvm::fuzzerop::fnegDescriptor(unsigned Weight) { + auto buildOp = [](ArrayRef Srcs, Instruction *Inst) { + return UnaryOperator::Create(Instruction::FNeg, Srcs[0], "F", Inst); + }; + return {Weight, {anyFloatOrVecFloatType()}, buildOp}; +} + OpDescriptor llvm::fuzzerop::binOpDescriptor(unsigned Weight, Instruction::BinaryOps Op) { auto buildOp = [Op](ArrayRef Srcs, Instruction *Inst) { @@ -107,13 +132,13 @@ case Instruction::And: case Instruction::Or: case Instruction::Xor: - return {Weight, {anyIntType(), matchFirstType()}, buildOp}; + return {Weight, {anyIntOrVecIntType(), matchFirstType()}, buildOp}; case Instruction::FAdd: case Instruction::FSub: case Instruction::FMul: case Instruction::FDiv: case Instruction::FRem: - return {Weight, {anyFloatType(), matchFirstType()}, buildOp}; + return {Weight, {anyFloatOrVecFloatType(), matchFirstType()}, buildOp}; case Instruction::BinaryOpsEnd: llvm_unreachable("Value out of range of enum"); } @@ -129,9 +154,9 @@ switch (CmpOp) { case Instruction::ICmp: - return {Weight, {anyIntType(), matchFirstType()}, buildOp}; + return {Weight, {anyIntOrVecIntType(), matchFirstType()}, buildOp}; case Instruction::FCmp: - return {Weight, {anyFloatType(), matchFirstType()}, buildOp}; + return {Weight, {anyFloatOrVecFloatType(), matchFirstType()}, buildOp}; default: llvm_unreachable("CmpOp must be ICmp or FCmp"); } diff --git a/llvm/unittests/FuzzMutate/OperationsTest.cpp b/llvm/unittests/FuzzMutate/OperationsTest.cpp --- a/llvm/unittests/FuzzMutate/OperationsTest.cpp +++ b/llvm/unittests/FuzzMutate/OperationsTest.cpp @@ -91,10 +91,17 @@ Constant *s = ConstantStruct::get(StructType::create(Ctx, "OpaqueStruct")); Constant *a = ConstantArray::get(ArrayType::get(i32->getType(), 2), {i32, i32}); + Constant *v8i1 = ConstantVector::getSplat(ElementCount::getFixed(8), i1); Constant *v8i8 = ConstantVector::getSplat(ElementCount::getFixed(8), i8); Constant *v4f16 = ConstantVector::getSplat(ElementCount::getFixed(4), f16); Constant *p0i32 = ConstantPointerNull::get(PointerType::get(i32->getType(), 0)); + Constant *v8p0i32 = + ConstantVector::getSplat(ElementCount::getFixed(8), p0i32); + Constant *vni32 = ConstantVector::getSplat(ElementCount::getScalable(8), i32); + Constant *vnf64 = ConstantVector::getSplat(ElementCount::getScalable(8), f64); + Constant *vnp0i32 = + ConstantVector::getSplat(ElementCount::getScalable(8), p0i32); auto OnlyI32 = onlyType(i32->getType()); EXPECT_TRUE(OnlyI32.matches({}, i32)); @@ -126,6 +133,36 @@ AnyInt.generate({}, {i32->getType(), f16->getType(), v8i8->getType()}), AllOf(SizeIs(Ge(1u)), Each(TypesMatch(i32)))); + auto AnyIntOrVecInt = anyIntOrVecIntType(); + EXPECT_TRUE(AnyIntOrVecInt.matches({}, i1)); + EXPECT_TRUE(AnyIntOrVecInt.matches({}, i64)); + EXPECT_FALSE(AnyIntOrVecInt.matches({}, f32)); + EXPECT_FALSE(AnyIntOrVecInt.matches({}, v4f16)); + EXPECT_TRUE(AnyIntOrVecInt.matches({}, v8i8)); + EXPECT_FALSE(AnyIntOrVecInt.matches({}, v4f16)); + EXPECT_FALSE(AnyIntOrVecInt.matches({}, v8p0i32)); + EXPECT_TRUE(AnyIntOrVecInt.matches({}, vni32)); + EXPECT_FALSE(AnyIntOrVecInt.matches({}, vnf64)); + EXPECT_FALSE(AnyIntOrVecInt.matches({}, vnp0i32)); + + EXPECT_THAT(AnyIntOrVecInt.generate({}, {v8i8->getType()}), + AllOf(Each(TypesMatch(v8i8)))); + + auto BoolOrVecBool = boolOrVecBoolType(); + EXPECT_TRUE(BoolOrVecBool.matches({}, i1)); + EXPECT_FALSE(BoolOrVecBool.matches({}, i64)); + EXPECT_FALSE(BoolOrVecBool.matches({}, f32)); + EXPECT_FALSE(BoolOrVecBool.matches({}, v4f16)); + EXPECT_TRUE(BoolOrVecBool.matches({}, v8i1)); + EXPECT_FALSE(BoolOrVecBool.matches({}, v4f16)); + EXPECT_FALSE(BoolOrVecBool.matches({}, v8p0i32)); + EXPECT_FALSE(BoolOrVecBool.matches({}, vni32)); + EXPECT_FALSE(BoolOrVecBool.matches({}, vnf64)); + EXPECT_FALSE(BoolOrVecBool.matches({}, vnp0i32)); + + EXPECT_THAT(BoolOrVecBool.generate({}, {v8i8->getType(), v8i1->getType()}), + AllOf(Each(TypesMatch(v8i1)))); + auto AnyFP = anyFloatType(); EXPECT_TRUE(AnyFP.matches({}, f16)); EXPECT_TRUE(AnyFP.matches({}, f32)); @@ -137,11 +174,30 @@ AnyFP.generate({}, {i32->getType(), f16->getType(), v8i8->getType()}), AllOf(SizeIs(Ge(1u)), Each(TypesMatch(f16)))); + auto AnyFPOrVecFP = anyFloatOrVecFloatType(); + EXPECT_TRUE(AnyFPOrVecFP.matches({}, f16)); + EXPECT_TRUE(AnyFPOrVecFP.matches({}, f32)); + EXPECT_FALSE(AnyFPOrVecFP.matches({}, i16)); + EXPECT_FALSE(AnyFPOrVecFP.matches({}, p0i32)); + EXPECT_TRUE(AnyFPOrVecFP.matches({}, v4f16)); + EXPECT_FALSE(AnyFPOrVecFP.matches({}, v8p0i32)); + EXPECT_FALSE(AnyFPOrVecFP.matches({}, vni32)); + EXPECT_TRUE(AnyFPOrVecFP.matches({}, vnf64)); + EXPECT_FALSE(AnyFPOrVecFP.matches({}, vnp0i32)); + + EXPECT_THAT(AnyFPOrVecFP.generate( + {}, {i32->getType(), f16->getType(), v8i8->getType()}), + AllOf(SizeIs(Ge(1u)), Each(TypesMatch(f16)))); + EXPECT_THAT(AnyFPOrVecFP.generate({}, {v4f16->getType()}), + AllOf(SizeIs(Ge(1u)), Each(TypesMatch(v4f16)))); + auto AnyPtr = anyPtrType(); EXPECT_TRUE(AnyPtr.matches({}, p0i32)); EXPECT_FALSE(AnyPtr.matches({}, i8)); EXPECT_FALSE(AnyPtr.matches({}, a)); EXPECT_FALSE(AnyPtr.matches({}, v8i8)); + EXPECT_FALSE(AnyPtr.matches({}, v8p0i32)); + EXPECT_FALSE(AnyPtr.matches({}, vni32)); auto isPointer = [](Value *V) { return V->getType()->isPointerTy(); }; EXPECT_THAT( @@ -154,9 +210,12 @@ EXPECT_FALSE(AnyVec.matches({}, i8)); EXPECT_FALSE(AnyVec.matches({}, a)); EXPECT_FALSE(AnyVec.matches({}, s)); + EXPECT_TRUE(AnyVec.matches({}, v8p0i32)); + EXPECT_TRUE(AnyVec.matches({}, vni32)); + EXPECT_TRUE(AnyVec.matches({}, vnf64)); + EXPECT_TRUE(AnyVec.matches({}, vnp0i32)); - EXPECT_THAT(AnyVec.generate({}, {v8i8->getType()}), - ElementsAre(TypesMatch(v8i8))); + EXPECT_THAT(AnyVec.generate({}, {v8i8->getType()}), Each(TypesMatch(v8i8))); auto First = matchFirstType(); EXPECT_TRUE(First.matches({i8}, i8)); @@ -167,6 +226,28 @@ 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))); + + auto FirstLength = matchFirstLengthWAnyType(); + EXPECT_TRUE(FirstLength.matches({v8i8}, v8i1)); + + EXPECT_THAT(FirstLength.generate({v8i1}, {i8->getType()}), + Each(TypesMatch(v8i8))); + + auto Second = matchSecondType(); + EXPECT_TRUE(Second.matches({i32, i8}, i8)); + EXPECT_TRUE(Second.matches({i8, f16}, f16)); + + EXPECT_THAT(Second.generate({v8i8, i32}, {}), Each(TypesMatch(i32))); + EXPECT_THAT(Second.generate({f32, f16}, {f16->getType()}), + Each(TypesMatch(f16))); + + auto FirstScalar = matchScalarOfFirstType(); + EXPECT_TRUE(FirstScalar.matches({v8i8}, i8)); + EXPECT_TRUE(FirstScalar.matches({i8}, i8)); + EXPECT_TRUE(FirstScalar.matches({v4f16}, f16)); + + EXPECT_THAT(FirstScalar.generate({v8i8}, {i8->getType()}), + Each(TypesMatch(i8))); } TEST(OperationsTest, SplitBlock) { @@ -301,13 +382,12 @@ // Check that we only pick sized pointers for the GEP instructions LLVMContext Ctx; - const char *SourceCode = - "%opaque = type opaque\n" - "declare void @f()\n" - "define void @test(%opaque %o) {\n" - " %a = alloca i64, i32 10\n" - " ret void\n" - "}"; + const char *SourceCode = "%opaque = type opaque\n" + "declare void @f()\n" + "define void @test(%opaque %o) {\n" + " %a = alloca i64, i32 10\n" + " ret void\n" + "}"; auto M = parseAssembly(SourceCode, Ctx); fuzzerop::OpDescriptor Descr = fuzzerop::gepDescriptor(1); diff --git a/llvm/unittests/FuzzMutate/StrategiesTest.cpp b/llvm/unittests/FuzzMutate/StrategiesTest.cpp --- a/llvm/unittests/FuzzMutate/StrategiesTest.cpp +++ b/llvm/unittests/FuzzMutate/StrategiesTest.cpp @@ -34,6 +34,17 @@ Type::getInt1Ty, Type::getInt8Ty, Type::getInt16Ty, Type::getInt32Ty, Type::getInt64Ty, Type::getFloatTy, Type::getDoubleTy}; + // Add vector 1, 2, 3, 4, and 8. + int VectorLength[] = {1, 2, 3, 4, 8}; + std::vector BasicTypeGetters(Types); + for (auto typeGetter : BasicTypeGetters) { + for (int length : VectorLength) { + Types.push_back([typeGetter, length](LLVMContext &C) { + return VectorType::get(typeGetter(C), length, false); + }); + } + } + std::vector> Strategies; Strategies.push_back(std::make_unique( InjectorIRStrategy::getDefaultOps())); @@ -78,6 +89,25 @@ } } +static void mutateAndVerifyModule(StringRef Source, + std::unique_ptr &Mutator, + int repeat = 100) { + LLVMContext Ctx; + auto M = parseAssembly(Source.data(), Ctx); + std::mt19937 mt(Seed); + std::uniform_int_distribution RandInt(INT_MIN, INT_MAX); + for (int i = 0; i < repeat; i++) { + Mutator->mutateModule(*M, RandInt(mt), Source.size(), Source.size() + 1024); + ASSERT_FALSE(verifyModule(*M, &errs())); + } +} +template +static void mutateAndVerifyModule(StringRef Source, int repeat = 100) { + auto Mutator = createMutator(); + ASSERT_TRUE(Mutator); + mutateAndVerifyModule(Source, Mutator, repeat); +} + TEST(InjectorIRStrategyTest, EmptyModule) { // Test that we can inject into empty module @@ -92,6 +122,13 @@ EXPECT_TRUE(!verifyModule(*M, &errs())); } +TEST(InjectorIRStrategyTest, LargeInsertion) { + StringRef Source = ""; + auto Mutator = createInjectorMutator(); + ASSERT_TRUE(Mutator); + mutateAndVerifyModule(Source, Mutator, 100); +} + TEST(InstDeleterIRStrategyTest, EmptyFunction) { // Test that we don't crash even if we can't remove from one of the functions. @@ -385,21 +422,6 @@ ASSERT_TRUE(p.second); } -template -static void mutateAndVerifyModule(StringRef Source, int repeat = 100) { - LLVMContext Ctx; - auto Mutator = createMutator(); - ASSERT_TRUE(Mutator); - - auto M = parseAssembly(Source.data(), Ctx); - std::mt19937 mt(Seed); - std::uniform_int_distribution RandInt(INT_MIN, INT_MAX); - for (int i = 0; i < repeat; i++) { - Mutator->mutateModule(*M, RandInt(mt), Source.size(), Source.size() + 1024); - ASSERT_FALSE(verifyModule(*M, &errs())); - } -} - TEST(InsertCFGStrategy, CFG) { StringRef Source = "\n\ define i32 @test(i1 %C1, i1 %C2, i1 %C3, i16 %S1, i16 %S2, i32 %I1) { \n\