| //===- IRSimilarityIdentifierTest.cpp - IRSimilarityIdentifier unit 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 |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // Tests for components for finding similarity such as the instruction mapper, |
| // suffix tree usage, and structural analysis. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "llvm/Analysis/IRSimilarityIdentifier.h" |
| #include "llvm/AsmParser/Parser.h" |
| #include "llvm/IR/LLVMContext.h" |
| #include "llvm/IR/Module.h" |
| #include "llvm/Support/Allocator.h" |
| #include "llvm/Support/SourceMgr.h" |
| #include "gtest/gtest.h" |
| |
| using namespace llvm; |
| using namespace IRSimilarity; |
| |
| static std::unique_ptr<Module> makeLLVMModule(LLVMContext &Context, |
| StringRef ModuleStr) { |
| SMDiagnostic Err; |
| std::unique_ptr<Module> M = parseAssemblyString(ModuleStr, Err, Context); |
| assert(M && "Bad LLVM IR?"); |
| return M; |
| } |
| |
| void getVectors(Module &M, IRInstructionMapper &Mapper, |
| std::vector<IRInstructionData *> &InstrList, |
| std::vector<unsigned> &UnsignedVec) { |
| for (Function &F : M) |
| for (BasicBlock &BB : F) |
| Mapper.convertToUnsignedVec(BB, InstrList, UnsignedVec); |
| } |
| |
| void getSimilarities( |
| Module &M, |
| std::vector<std::vector<IRSimilarityCandidate>> &SimilarityCandidates) { |
| IRSimilarityIdentifier Identifier; |
| SimilarityCandidates = Identifier.findSimilarity(M); |
| } |
| |
| // Checks that different opcodes are mapped to different values |
| TEST(IRInstructionMapper, OpcodeDifferentiation) { |
| StringRef ModuleString = R"( |
| define i32 @f(i32 %a, i32 %b) { |
| bb0: |
| %0 = add i32 %a, %b |
| %1 = mul i32 %a, %b |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| // Check that the size of the unsigned vector and the instruction list are the |
| // same as a safety check. |
| ASSERT_TRUE(InstrList.size() == UnsignedVec.size()); |
| |
| // Make sure that the unsigned vector is the expected size. |
| ASSERT_TRUE(UnsignedVec.size() == 3); |
| |
| // Check whether the instructions are not mapped to the same value. |
| ASSERT_TRUE(UnsignedVec[0] != UnsignedVec[1]); |
| } |
| |
| // Checks that the same opcodes and types are mapped to the same values. |
| TEST(IRInstructionMapper, OpcodeTypeSimilarity) { |
| StringRef ModuleString = R"( |
| define i32 @f(i32 %a, i32 %b) { |
| bb0: |
| %0 = add i32 %a, %b |
| %1 = add i32 %b, %a |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| ASSERT_TRUE(InstrList.size() == UnsignedVec.size()); |
| ASSERT_TRUE(UnsignedVec.size() == 3); |
| |
| // Check whether the instructions are mapped to the same value. |
| ASSERT_TRUE(UnsignedVec[0] == UnsignedVec[1]); |
| } |
| |
| // Checks that the same opcode and different types are mapped to different |
| // values. |
| TEST(IRInstructionMapper, TypeDifferentiation) { |
| StringRef ModuleString = R"( |
| define i32 @f(i32 %a, i32 %b, i64 %c, i64 %d) { |
| bb0: |
| %0 = add i32 %a, %b |
| %1 = add i64 %c, %d |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| ASSERT_TRUE(InstrList.size() == UnsignedVec.size()); |
| ASSERT_TRUE(UnsignedVec.size() == 3); |
| ASSERT_TRUE(UnsignedVec[0] != UnsignedVec[1]); |
| } |
| |
| // Checks that different predicates map to different values. |
| TEST(IRInstructionMapper, PredicateDifferentiation) { |
| StringRef ModuleString = R"( |
| define i32 @f(i32 %a, i32 %b) { |
| bb0: |
| %0 = icmp sge i32 %b, %a |
| %1 = icmp slt i32 %a, %b |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| ASSERT_TRUE(InstrList.size() == UnsignedVec.size()); |
| ASSERT_TRUE(UnsignedVec.size() == 3); |
| ASSERT_TRUE(UnsignedVec[0] != UnsignedVec[1]); |
| } |
| |
| // Checks that predicates where that can be considered the same when the |
| // operands are swapped, i.e. greater than to less than are mapped to the same |
| // unsigned integer. |
| TEST(IRInstructionMapper, PredicateIsomorphism) { |
| StringRef ModuleString = R"( |
| define i32 @f(i32 %a, i32 %b) { |
| bb0: |
| %0 = icmp sgt i32 %a, %b |
| %1 = icmp slt i32 %b, %a |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| ASSERT_TRUE(InstrList.size() == UnsignedVec.size()); |
| ASSERT_TRUE(UnsignedVec.size() == 3); |
| ASSERT_TRUE(UnsignedVec[0] == UnsignedVec[1]); |
| } |
| |
| // Checks that the same predicate maps to the same value. |
| TEST(IRInstructionMapper, PredicateSimilarity) { |
| StringRef ModuleString = R"( |
| define i32 @f(i32 %a, i32 %b) { |
| bb0: |
| %0 = icmp slt i32 %a, %b |
| %1 = icmp slt i32 %b, %a |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| ASSERT_TRUE(InstrList.size() == UnsignedVec.size()); |
| ASSERT_TRUE(UnsignedVec.size() == 3); |
| ASSERT_TRUE(UnsignedVec[0] == UnsignedVec[1]); |
| } |
| |
| // Checks that the same predicate maps to the same value for floating point |
| // CmpInsts. |
| TEST(IRInstructionMapper, FPPredicateSimilarity) { |
| StringRef ModuleString = R"( |
| define i32 @f(double %a, double %b) { |
| bb0: |
| %0 = fcmp olt double %a, %b |
| %1 = fcmp olt double %b, %a |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| ASSERT_TRUE(InstrList.size() == UnsignedVec.size()); |
| ASSERT_TRUE(UnsignedVec.size() == 3); |
| ASSERT_TRUE(UnsignedVec[0] == UnsignedVec[1]); |
| } |
| |
| // Checks that the different predicate maps to a different value for floating |
| // point CmpInsts. |
| TEST(IRInstructionMapper, FPPredicatDifference) { |
| StringRef ModuleString = R"( |
| define i32 @f(double %a, double %b) { |
| bb0: |
| %0 = fcmp olt double %a, %b |
| %1 = fcmp oge double %b, %a |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| ASSERT_TRUE(InstrList.size() == UnsignedVec.size()); |
| ASSERT_TRUE(UnsignedVec.size() == 3); |
| ASSERT_TRUE(UnsignedVec[0] != UnsignedVec[1]); |
| } |
| |
| // Checks that the zexts that have the same type parameters map to the same |
| // unsigned integer. |
| TEST(IRInstructionMapper, ZextTypeSimilarity) { |
| StringRef ModuleString = R"( |
| define i32 @f(i32 %a) { |
| bb0: |
| %0 = zext i32 %a to i64 |
| %1 = zext i32 %a to i64 |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| ASSERT_TRUE(InstrList.size() == UnsignedVec.size()); |
| ASSERT_TRUE(UnsignedVec.size() == 3); |
| ASSERT_TRUE(UnsignedVec[0] == UnsignedVec[1]); |
| } |
| |
| // Checks that the sexts that have the same type parameters map to the same |
| // unsigned integer. |
| TEST(IRInstructionMapper, SextTypeSimilarity) { |
| StringRef ModuleString = R"( |
| define i32 @f(i32 %a) { |
| bb0: |
| %0 = sext i32 %a to i64 |
| %1 = sext i32 %a to i64 |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| ASSERT_TRUE(InstrList.size() == UnsignedVec.size()); |
| ASSERT_TRUE(UnsignedVec.size() == 3); |
| ASSERT_TRUE(UnsignedVec[0] == UnsignedVec[1]); |
| } |
| |
| // Checks that the zexts that have the different type parameters map to the |
| // different unsigned integers. |
| TEST(IRInstructionMapper, ZextTypeDifference) { |
| StringRef ModuleString = R"( |
| define i32 @f(i32 %a, i8 %b) { |
| bb0: |
| %0 = zext i32 %a to i64 |
| %1 = zext i8 %b to i32 |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| ASSERT_TRUE(InstrList.size() == UnsignedVec.size()); |
| ASSERT_TRUE(UnsignedVec.size() == 3); |
| ASSERT_TRUE(UnsignedVec[0] != UnsignedVec[1]); |
| } |
| |
| // Checks that the sexts that have the different type parameters map to the |
| // different unsigned integers. |
| TEST(IRInstructionMapper, SextTypeDifference) { |
| StringRef ModuleString = R"( |
| define i32 @f(i32 %a, i8 %b) { |
| bb0: |
| %0 = sext i32 %a to i64 |
| %1 = sext i8 %b to i32 |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| ASSERT_TRUE(InstrList.size() == UnsignedVec.size()); |
| ASSERT_TRUE(UnsignedVec.size() == 3); |
| ASSERT_TRUE(UnsignedVec[0] != UnsignedVec[1]); |
| } |
| |
| // Checks that loads that have the same type are mapped to the same unsigned |
| // integer. |
| TEST(IRInstructionMapper, LoadSimilarType) { |
| StringRef ModuleString = R"( |
| define i32 @f(i32* %a, i32* %b) { |
| bb0: |
| %0 = load i32, i32* %a |
| %1 = load i32, i32* %b |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| ASSERT_TRUE(InstrList.size() == UnsignedVec.size()); |
| ASSERT_TRUE(UnsignedVec.size() == 3); |
| ASSERT_TRUE(UnsignedVec[0] == UnsignedVec[1]); |
| } |
| |
| // Checks that loads that have the different types are mapped to |
| // different unsigned integers. |
| TEST(IRInstructionMapper, LoadDifferentType) { |
| StringRef ModuleString = R"( |
| define i32 @f(i32* %a, i64* %b) { |
| bb0: |
| %0 = load i32, i32* %a |
| %1 = load i64, i64* %b |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| ASSERT_TRUE(InstrList.size() == UnsignedVec.size()); |
| ASSERT_TRUE(UnsignedVec.size() == 3); |
| ASSERT_TRUE(UnsignedVec[0] != UnsignedVec[1]); |
| } |
| |
| // Checks that loads that have the different aligns are mapped to different |
| // unsigned integers. |
| TEST(IRInstructionMapper, LoadDifferentAlign) { |
| StringRef ModuleString = R"( |
| define i32 @f(i32* %a, i32* %b) { |
| bb0: |
| %0 = load i32, i32* %a, align 4 |
| %1 = load i32, i32* %b, align 8 |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| ASSERT_TRUE(InstrList.size() == UnsignedVec.size()); |
| ASSERT_TRUE(UnsignedVec.size() == 3); |
| ASSERT_TRUE(UnsignedVec[0] != UnsignedVec[1]); |
| } |
| |
| // Checks that loads that have the different volatile settings are mapped to |
| // different unsigned integers. |
| TEST(IRInstructionMapper, LoadDifferentVolatile) { |
| StringRef ModuleString = R"( |
| define i32 @f(i32* %a, i32* %b) { |
| bb0: |
| %0 = load volatile i32, i32* %a |
| %1 = load i32, i32* %b |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| ASSERT_TRUE(InstrList.size() == UnsignedVec.size()); |
| ASSERT_TRUE(UnsignedVec.size() == 3); |
| ASSERT_TRUE(UnsignedVec[0] != UnsignedVec[1]); |
| } |
| |
| // Checks that loads that have the same volatile settings are mapped to |
| // different unsigned integers. |
| TEST(IRInstructionMapper, LoadSameVolatile) { |
| StringRef ModuleString = R"( |
| define i32 @f(i32* %a, i32* %b) { |
| bb0: |
| %0 = load volatile i32, i32* %a |
| %1 = load volatile i32, i32* %b |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| ASSERT_TRUE(InstrList.size() == UnsignedVec.size()); |
| ASSERT_TRUE(UnsignedVec.size() == 3); |
| ASSERT_TRUE(UnsignedVec[0] == UnsignedVec[1]); |
| } |
| |
| // Checks that loads that have the different atomicity settings are mapped to |
| // different unsigned integers. |
| TEST(IRInstructionMapper, LoadDifferentAtomic) { |
| StringRef ModuleString = R"( |
| define i32 @f(i32* %a, i32* %b) { |
| bb0: |
| %0 = load atomic i32, i32* %a unordered, align 4 |
| %1 = load atomic i32, i32* %b monotonic, align 4 |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| ASSERT_TRUE(InstrList.size() == UnsignedVec.size()); |
| ASSERT_TRUE(UnsignedVec.size() == 3); |
| ASSERT_TRUE(UnsignedVec[0] != UnsignedVec[1]); |
| } |
| |
| // Checks that loads that have the same atomicity settings are mapped to |
| // different unsigned integers. |
| TEST(IRInstructionMapper, LoadSameAtomic) { |
| StringRef ModuleString = R"( |
| define i32 @f(i32* %a, i32* %b) { |
| bb0: |
| %0 = load atomic i32, i32* %a unordered, align 4 |
| %1 = load atomic i32, i32* %b unordered, align 4 |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| ASSERT_TRUE(InstrList.size() == UnsignedVec.size()); |
| ASSERT_TRUE(UnsignedVec.size() == 3); |
| ASSERT_TRUE(UnsignedVec[0] == UnsignedVec[1]); |
| } |
| |
| // Checks that stores that have the same type are mapped to the same unsigned |
| // integer. |
| TEST(IRInstructionMapper, StoreSimilarType) { |
| StringRef ModuleString = R"( |
| define i32 @f(i32* %a, i32* %b) { |
| bb0: |
| store i32 1, i32* %a |
| store i32 2, i32* %a |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| ASSERT_TRUE(InstrList.size() == UnsignedVec.size()); |
| ASSERT_TRUE(UnsignedVec.size() == 3); |
| ASSERT_TRUE(UnsignedVec[0] == UnsignedVec[1]); |
| } |
| |
| // Checks that stores that have the different types are mapped to |
| // different unsigned integers. |
| TEST(IRInstructionMapper, StoreDifferentType) { |
| StringRef ModuleString = R"( |
| define i32 @f(i32* %a, i64* %b) { |
| bb0: |
| store i32 1, i32* %a |
| store i64 1, i64* %b |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| ASSERT_TRUE(InstrList.size() == UnsignedVec.size()); |
| ASSERT_TRUE(UnsignedVec.size() == 3); |
| ASSERT_TRUE(UnsignedVec[0] != UnsignedVec[1]); |
| } |
| |
| // Checks that stores that have the different aligns are mapped to different |
| // unsigned integers. |
| TEST(IRInstructionMapper, StoreDifferentAlign) { |
| StringRef ModuleString = R"( |
| define i32 @f(i32* %a, i32* %b) { |
| bb0: |
| store i32 1, i32* %a, align 4 |
| store i32 1, i32* %b, align 8 |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| ASSERT_TRUE(InstrList.size() == UnsignedVec.size()); |
| ASSERT_TRUE(UnsignedVec.size() == 3); |
| ASSERT_TRUE(UnsignedVec[0] != UnsignedVec[1]); |
| } |
| |
| // Checks that stores that have the different volatile settings are mapped to |
| // different unsigned integers. |
| TEST(IRInstructionMapper, StoreDifferentVolatile) { |
| StringRef ModuleString = R"( |
| define i32 @f(i32* %a, i32* %b) { |
| bb0: |
| store volatile i32 1, i32* %a |
| store i32 1, i32* %b |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| ASSERT_TRUE(InstrList.size() == UnsignedVec.size()); |
| ASSERT_TRUE(UnsignedVec.size() == 3); |
| ASSERT_TRUE(UnsignedVec[0] != UnsignedVec[1]); |
| } |
| |
| // Checks that stores that have the same volatile settings are mapped to |
| // different unsigned integers. |
| TEST(IRInstructionMapper, StoreSameVolatile) { |
| StringRef ModuleString = R"( |
| define i32 @f(i32* %a, i32* %b) { |
| bb0: |
| store volatile i32 1, i32* %a |
| store volatile i32 1, i32* %b |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| ASSERT_TRUE(InstrList.size() == UnsignedVec.size()); |
| ASSERT_TRUE(UnsignedVec.size() == 3); |
| ASSERT_TRUE(UnsignedVec[0] == UnsignedVec[1]); |
| } |
| |
| // Checks that loads that have the same atomicity settings are mapped to |
| // different unsigned integers. |
| TEST(IRInstructionMapper, StoreSameAtomic) { |
| StringRef ModuleString = R"( |
| define i32 @f(i32* %a, i32* %b) { |
| bb0: |
| store atomic i32 1, i32* %a unordered, align 4 |
| store atomic i32 1, i32* %b unordered, align 4 |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| ASSERT_TRUE(InstrList.size() == UnsignedVec.size()); |
| ASSERT_TRUE(UnsignedVec.size() == 3); |
| ASSERT_TRUE(UnsignedVec[0] == UnsignedVec[1]); |
| } |
| |
| // Checks that loads that have the different atomicity settings are mapped to |
| // different unsigned integers. |
| TEST(IRInstructionMapper, StoreDifferentAtomic) { |
| StringRef ModuleString = R"( |
| define i32 @f(i32* %a, i32* %b) { |
| bb0: |
| store atomic i32 1, i32* %a unordered, align 4 |
| store atomic i32 1, i32* %b monotonic, align 4 |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| ASSERT_TRUE(InstrList.size() == UnsignedVec.size()); |
| ASSERT_TRUE(UnsignedVec.size() == 3); |
| ASSERT_TRUE(UnsignedVec[0] != UnsignedVec[1]); |
| } |
| |
| // In most cases, the illegal instructions we are collecting don't require any |
| // sort of setup. In these cases, we can just only have illegal instructions, |
| // and the mapper will create 0 length vectors, and we can check that. |
| |
| // In cases where we have legal instructions needed to set up the illegal |
| // instruction, to check illegal instructions are assigned unsigned integers |
| // from the maximum value decreasing to 0, it will be greater than a legal |
| // instruction that comes after. So to check that we have an illegal |
| // instruction, we place a legal instruction after an illegal instruction, and |
| // check that the illegal unsigned integer is greater than the unsigned integer |
| // of the legal instruction. |
| |
| // Checks that the branch is mapped to be illegal since there is extra checking |
| // needed to ensure that a branch in one region is branching to an isomorphic |
| // location in a different region. |
| TEST(IRInstructionMapper, BranchIllegal) { |
| StringRef ModuleString = R"( |
| define i32 @f(i32 %a, i32 %b) { |
| bb0: |
| %0 = icmp slt i32 %a, %b |
| br i1 %0, label %bb0, label %bb1 |
| bb1: |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| ASSERT_EQ(InstrList.size(), UnsignedVec.size()); |
| ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(0)); |
| } |
| |
| // Checks that a PHINode is mapped to be illegal since there is extra checking |
| // needed to ensure that a branch in one region is bin an isomorphic |
| // location in a different region. |
| TEST(IRInstructionMapper, PhiIllegal) { |
| StringRef ModuleString = R"( |
| define i32 @f(i32 %a, i32 %b) { |
| bb0: |
| %0 = phi i1 [ 0, %bb0 ], [ %0, %bb1 ] |
| ret i32 0 |
| bb1: |
| ret i32 1 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| ASSERT_EQ(InstrList.size(), UnsignedVec.size()); |
| ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(0)); |
| } |
| |
| // Checks that an alloca instruction is mapped to be illegal. |
| TEST(IRInstructionMapper, AllocaIllegal) { |
| StringRef ModuleString = R"( |
| define i32 @f(i32 %a, i32 %b) { |
| bb0: |
| %0 = alloca i32 |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| ASSERT_EQ(InstrList.size(), UnsignedVec.size()); |
| ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(0)); |
| } |
| |
| // Checks that an getelementptr instruction is mapped to be legal. And that |
| // the operands in getelementpointer instructions are the exact same after the |
| // first element operand, which only requires the same type. |
| TEST(IRInstructionMapper, GetElementPtrSameEndOperands) { |
| StringRef ModuleString = R"( |
| %struct.RT = type { i8, [10 x [20 x i32]], i8 } |
| %struct.ST = type { i32, double, %struct.RT } |
| define i32 @f(%struct.ST* %s, i64 %a, i64 %b) { |
| bb0: |
| %0 = getelementptr inbounds %struct.ST, %struct.ST* %s, i64 %a, i32 0 |
| %1 = getelementptr inbounds %struct.ST, %struct.ST* %s, i64 %b, i32 0 |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| ASSERT_EQ(InstrList.size(), UnsignedVec.size()); |
| ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(3)); |
| ASSERT_EQ(UnsignedVec[0], UnsignedVec[1]); |
| } |
| |
| // Check that when the operands in getelementpointer instructions are not the |
| // exact same after the first element operand, the instructions are mapped to |
| // different values. |
| TEST(IRInstructionMapper, GetElementPtrDifferentEndOperands) { |
| StringRef ModuleString = R"( |
| %struct.RT = type { i8, [10 x [20 x i32]], i8 } |
| %struct.ST = type { i32, double, %struct.RT } |
| define i32 @f(%struct.ST* %s, i64 %a, i64 %b) { |
| bb0: |
| %0 = getelementptr inbounds %struct.ST, %struct.ST* %s, i64 %a, i32 0 |
| %1 = getelementptr inbounds %struct.ST, %struct.ST* %s, i64 %b, i32 2 |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| ASSERT_EQ(InstrList.size(), UnsignedVec.size()); |
| ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(3)); |
| ASSERT_NE(UnsignedVec[0], UnsignedVec[1]); |
| } |
| |
| // Check that when the operands in getelementpointer instructions are not the |
| // same initial base type, each instruction is mapped to a different value. |
| TEST(IRInstructionMapper, GetElementPtrDifferentBaseType) { |
| StringRef ModuleString = R"( |
| %struct.RT = type { i8, [10 x [20 x i32]], i8 } |
| %struct.ST = type { i32, double, %struct.RT } |
| define i32 @f(%struct.ST* %s, %struct.RT* %r, i64 %a, i64 %b) { |
| bb0: |
| %0 = getelementptr inbounds %struct.ST, %struct.ST* %s, i64 %a |
| %1 = getelementptr inbounds %struct.RT, %struct.RT* %r, i64 %b |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| ASSERT_EQ(InstrList.size(), UnsignedVec.size()); |
| ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(3)); |
| ASSERT_NE(UnsignedVec[0], UnsignedVec[1]); |
| } |
| |
| // Check that when the operands in getelementpointer instructions do not have |
| // the same inbounds modifier, they are not counted as the same. |
| TEST(IRInstructionMapper, GetElementPtrDifferentInBounds) { |
| StringRef ModuleString = R"( |
| %struct.RT = type { i8, [10 x [20 x i32]], i8 } |
| %struct.ST = type { i32, double, %struct.RT } |
| define i32 @f(%struct.ST* %s, %struct.RT* %r, i64 %a, i64 %b) { |
| bb0: |
| %0 = getelementptr inbounds %struct.ST, %struct.ST* %s, i64 %a, i32 0 |
| %1 = getelementptr %struct.ST, %struct.ST* %s, i64 %b, i32 0 |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| ASSERT_EQ(InstrList.size(), UnsignedVec.size()); |
| ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(3)); |
| ASSERT_NE(UnsignedVec[0], UnsignedVec[1]); |
| } |
| |
| // Checks that indirect call instructions are mapped to be illegal since we |
| // cannot guarantee the same function in two different cases. |
| TEST(IRInstructionMapper, CallsIllegalIndirect) { |
| StringRef ModuleString = R"( |
| define i32 @f(void()* %func) { |
| bb0: |
| call void %func() |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| ASSERT_EQ(InstrList.size(), UnsignedVec.size()); |
| ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(0)); |
| } |
| |
| // Checks that a call instruction is mapped to be legal. Here we check that |
| // a call with the same name, and same types are mapped to the same |
| // value. |
| TEST(IRInstructionMapper, CallsSameTypeSameName) { |
| StringRef ModuleString = R"( |
| declare i32 @f1(i32, i32) |
| define i32 @f(i32 %a, i32 %b) { |
| bb0: |
| %0 = call i32 @f1(i32 %a, i32 %b) |
| %1 = call i32 @f1(i32 %a, i32 %b) |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| ASSERT_EQ(InstrList.size(), UnsignedVec.size()); |
| ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(3)); |
| ASSERT_EQ(UnsignedVec[0], UnsignedVec[1]); |
| } |
| |
| // Here we check that a calls with different names, but the same arguments types |
| // are mapped to different value. |
| TEST(IRInstructionMapper, CallsSameArgTypeDifferentName) { |
| StringRef ModuleString = R"( |
| declare i32 @f1(i32, i32) |
| declare i32 @f2(i32, i32) |
| define i32 @f(i32 %a, i32 %b) { |
| bb0: |
| %0 = call i32 @f1(i32 %a, i32 %b) |
| %1 = call i32 @f2(i32 %a, i32 %b) |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| ASSERT_EQ(InstrList.size(), UnsignedVec.size()); |
| ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(3)); |
| ASSERT_NE(UnsignedVec[0], UnsignedVec[1]); |
| } |
| |
| // Here we check that a calls with different names, and different arguments |
| // types are mapped to different value. |
| TEST(IRInstructionMapper, CallsDifferentArgTypeDifferentName) { |
| StringRef ModuleString = R"( |
| declare i32 @f1(i32, i32) |
| declare i32 @f2(i32) |
| define i32 @f(i32 %a, i32 %b) { |
| bb0: |
| %0 = call i32 @f1(i32 %a, i32 %b) |
| %1 = call i32 @f2(i32 %a) |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| ASSERT_EQ(InstrList.size(), UnsignedVec.size()); |
| ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(3)); |
| ASSERT_NE(UnsignedVec[0], UnsignedVec[1]); |
| } |
| |
| // Here we check that calls with different names, and different return |
| // types are mapped to different value. |
| TEST(IRInstructionMapper, CallsDifferentReturnTypeDifferentName) { |
| StringRef ModuleString = R"( |
| declare i64 @f1(i32, i32) |
| declare i32 @f2(i32, i32) |
| define i32 @f(i32 %a, i32 %b) { |
| bb0: |
| %0 = call i64 @f1(i32 %a, i32 %b) |
| %1 = call i32 @f2(i32 %a, i32 %b) |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| ASSERT_EQ(InstrList.size(), UnsignedVec.size()); |
| ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(3)); |
| ASSERT_NE(UnsignedVec[0], UnsignedVec[1]); |
| } |
| |
| // Here we check that calls with the same name, types, and parameters map to the |
| // same unsigned integer. |
| TEST(IRInstructionMapper, CallsSameParameters) { |
| StringRef ModuleString = R"( |
| declare i32 @f1(i32, i32) |
| define i32 @f(i32 %a, i32 %b) { |
| bb0: |
| %0 = tail call fastcc i32 @f1(i32 %a, i32 %b) |
| %1 = tail call fastcc i32 @f1(i32 %a, i32 %b) |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| ASSERT_EQ(InstrList.size(), UnsignedVec.size()); |
| ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(3)); |
| ASSERT_EQ(UnsignedVec[0], UnsignedVec[1]); |
| } |
| |
| // Here we check that calls with different tail call settings are mapped to |
| // different values. |
| TEST(IRInstructionMapper, CallsDifferentTails) { |
| StringRef ModuleString = R"( |
| declare i32 @f1(i32, i32) |
| define i32 @f(i32 %a, i32 %b) { |
| bb0: |
| %0 = tail call i32 @f1(i32 %a, i32 %b) |
| %1 = call i32 @f1(i32 %a, i32 %b) |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| ASSERT_EQ(InstrList.size(), UnsignedVec.size()); |
| ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(3)); |
| ASSERT_NE(UnsignedVec[0], UnsignedVec[1]); |
| } |
| |
| // Here we check that calls with different calling convention settings are |
| // mapped to different values. |
| TEST(IRInstructionMapper, CallsDifferentCallingConventions) { |
| StringRef ModuleString = R"( |
| declare i32 @f1(i32, i32) |
| define i32 @f(i32 %a, i32 %b) { |
| bb0: |
| %0 = call fastcc i32 @f1(i32 %a, i32 %b) |
| %1 = call i32 @f1(i32 %a, i32 %b) |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| ASSERT_EQ(InstrList.size(), UnsignedVec.size()); |
| ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(3)); |
| ASSERT_NE(UnsignedVec[0], UnsignedVec[1]); |
| } |
| |
| // Checks that an invoke instruction is mapped to be illegal. Invoke |
| // instructions are considered to be illegal because of the change in the |
| // control flow that is currently not recognized. |
| TEST(IRInstructionMapper, InvokeIllegal) { |
| StringRef ModuleString = R"( |
| define i32 @f(i8 *%gep1, i32 %b) { |
| then: |
| invoke i32 undef(i8* undef) |
| to label %invoke unwind label %lpad |
| |
| invoke: |
| unreachable |
| |
| lpad: |
| landingpad { i8*, i32 } |
| catch i8* null |
| unreachable |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| ASSERT_EQ(InstrList.size(), UnsignedVec.size()); |
| ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(0)); |
| } |
| |
| // Checks that an callbr instructions are considered to be illegal. Callbr |
| // instructions are considered to be illegal because of the change in the |
| // control flow that is currently not recognized. |
| TEST(IRInstructionMapper, CallBrInstIllegal) { |
| StringRef ModuleString = R"( |
| define void @test() { |
| fail: |
| ret void |
| } |
| |
| define i32 @f(i32 %a, i32 %b) { |
| bb0: |
| callbr void asm "xorl $0, $0; jmp ${1:l}", "r,X,~{dirflag},~{fpsr},~{flags}"(i32 %a, i8* blockaddress(@test, %fail)) to label %normal [label %fail] |
| fail: |
| ret i32 0 |
| normal: |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| ASSERT_EQ(InstrList.size(), UnsignedVec.size()); |
| ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(0)); |
| } |
| |
| // Checks that an debuginfo intrinsics are mapped to be invisible. Since they |
| // do not semantically change the program, they can be recognized as similar. |
| TEST(IRInstructionMapper, DebugInfoInvisible) { |
| StringRef ModuleString = R"( |
| define i32 @f(i32 %a, i32 %b) { |
| then: |
| %0 = add i32 %a, %b |
| call void @llvm.dbg.value(metadata !0) |
| %1 = add i32 %a, %b |
| ret i32 0 |
| } |
| |
| declare void @llvm.dbg.value(metadata) |
| !0 = distinct !{!"test\00", i32 10})"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| ASSERT_EQ(InstrList.size(), UnsignedVec.size()); |
| ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(3)); |
| } |
| |
| // The following are all exception handling intrinsics. We do not currently |
| // handle these instruction because they are very context dependent. |
| |
| // Checks that an eh.typeid.for intrinsic is mapped to be illegal. |
| TEST(IRInstructionMapper, ExceptionHandlingTypeIdIllegal) { |
| StringRef ModuleString = R"( |
| @_ZTIi = external constant i8* |
| define i32 @f() { |
| then: |
| %0 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*)) |
| ret i32 0 |
| } |
| |
| declare i32 @llvm.eh.typeid.for(i8*))"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| ASSERT_EQ(InstrList.size(), UnsignedVec.size()); |
| ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(0)); |
| } |
| |
| // Checks that an eh.exceptioncode intrinsic is mapped to be illegal. |
| TEST(IRInstructionMapper, ExceptionHandlingExceptionCodeIllegal) { |
| StringRef ModuleString = R"( |
| define i32 @f(i32 %a, i32 %b) { |
| entry: |
| %0 = catchswitch within none [label %__except] unwind to caller |
| |
| __except: |
| %1 = catchpad within %0 [i8* null] |
| catchret from %1 to label %__except |
| |
| then: |
| %2 = call i32 @llvm.eh.exceptioncode(token %1) |
| ret i32 0 |
| } |
| |
| declare i32 @llvm.eh.exceptioncode(token))"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| ASSERT_EQ(InstrList.size(), UnsignedVec.size()); |
| ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(0)); |
| } |
| |
| // Checks that an eh.unwind intrinsic is mapped to be illegal. |
| TEST(IRInstructionMapper, ExceptionHandlingUnwindIllegal) { |
| StringRef ModuleString = R"( |
| define i32 @f(i32 %a, i32 %b) { |
| entry: |
| call void @llvm.eh.unwind.init() |
| ret i32 0 |
| } |
| |
| declare void @llvm.eh.unwind.init())"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| ASSERT_EQ(InstrList.size(), UnsignedVec.size()); |
| ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(0)); |
| } |
| |
| // Checks that an eh.exceptionpointer intrinsic is mapped to be illegal. |
| TEST(IRInstructionMapper, ExceptionHandlingExceptionPointerIllegal) { |
| StringRef ModuleString = R"( |
| define i32 @f(i32 %a, i32 %b) { |
| entry: |
| %0 = call i8* @llvm.eh.exceptionpointer.p0i8(i32 0) |
| ret i32 0 |
| } |
| |
| declare i8* @llvm.eh.exceptionpointer.p0i8(i32))"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| ASSERT_EQ(InstrList.size(), UnsignedVec.size()); |
| ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(0)); |
| } |
| |
| // Checks that a catchpad instruction is mapped to an illegal value. |
| TEST(IRInstructionMapper, CatchpadIllegal) { |
| StringRef ModuleString = R"( |
| declare void @llvm.donothing() nounwind readnone |
| |
| define void @function() personality i8 3 { |
| entry: |
| invoke void @llvm.donothing() to label %normal unwind label %exception |
| exception: |
| %cs1 = catchswitch within none [label %catchpad1] unwind to caller |
| catchpad1: |
| catchpad within %cs1 [] |
| br label %normal |
| normal: |
| ret void |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| ASSERT_EQ(InstrList.size(), UnsignedVec.size()); |
| ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(0)); |
| } |
| |
| // Checks that a cleanuppad instruction is mapped to an illegal value. |
| TEST(IRInstructionMapper, CleanuppadIllegal) { |
| StringRef ModuleString = R"( |
| declare void @llvm.donothing() nounwind readnone |
| |
| define void @function() personality i8 3 { |
| entry: |
| invoke void @llvm.donothing() to label %normal unwind label %exception |
| exception: |
| %cs1 = catchswitch within none [label %catchpad1] unwind to caller |
| catchpad1: |
| %clean = cleanuppad within none [] |
| br label %normal |
| normal: |
| ret void |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| ASSERT_EQ(InstrList.size(), UnsignedVec.size()); |
| ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(0)); |
| } |
| |
| // The following three instructions are memory transfer and setting based, which |
| // are considered illegal since is extra checking needed to handle the address |
| // space checking. |
| |
| // Checks that a memset instruction is mapped to an illegal value. |
| TEST(IRInstructionMapper, MemSetIllegal) { |
| StringRef ModuleString = R"( |
| declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i32, i1) |
| |
| define i64 @function(i64 %x, i64 %z, i64 %n) { |
| entry: |
| %pool = alloca [59 x i64], align 4 |
| %tmp = bitcast [59 x i64]* %pool to i8* |
| call void @llvm.memset.p0i8.i64(i8* nonnull %tmp, i8 0, i64 236, i32 4, i1 false) |
| %cmp3 = icmp eq i64 %n, 0 |
| %a = add i64 %x, %z |
| %c = add i64 %x, %z |
| ret i64 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| ASSERT_EQ(InstrList.size(), UnsignedVec.size()); |
| ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(6)); |
| ASSERT_TRUE(UnsignedVec[2] < UnsignedVec[1]); |
| } |
| |
| // Checks that a memcpy instruction is mapped to an illegal value. |
| TEST(IRInstructionMapper, MemCpyIllegal) { |
| StringRef ModuleString = R"( |
| declare void @llvm.memcpy.p0i8.i64(i8* nocapture writeonly, i8, i64, i32, i1) |
| |
| define i64 @function(i64 %x, i64 %z, i64 %n) { |
| entry: |
| %pool = alloca [59 x i64], align 4 |
| %tmp = bitcast [59 x i64]* %pool to i8* |
| call void @llvm.memcpy.p0i8.i64(i8* nonnull %tmp, i8 0, i64 236, i32 4, i1 false) |
| %cmp3 = icmp eq i64 %n, 0 |
| %a = add i64 %x, %z |
| %c = add i64 %x, %z |
| ret i64 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| ASSERT_EQ(InstrList.size(), UnsignedVec.size()); |
| ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(6)); |
| ASSERT_TRUE(UnsignedVec[2] < UnsignedVec[1]); |
| } |
| |
| // Checks that a memmove instruction is mapped to an illegal value. |
| TEST(IRInstructionMapper, MemMoveIllegal) { |
| StringRef ModuleString = R"( |
| declare void @llvm.memmove.p0i8.i64(i8* nocapture writeonly, i8, i64, i32, i1) |
| |
| define i64 @function(i64 %x, i64 %z, i64 %n) { |
| entry: |
| %pool = alloca [59 x i64], align 4 |
| %tmp = bitcast [59 x i64]* %pool to i8* |
| call void @llvm.memmove.p0i8.i64(i8* nonnull %tmp, i8 0, i64 236, i32 4, i1 false) |
| %cmp3 = icmp eq i64 %n, 0 |
| %a = add i64 %x, %z |
| %c = add i64 %x, %z |
| ret i64 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| ASSERT_EQ(InstrList.size(), UnsignedVec.size()); |
| ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(6)); |
| ASSERT_TRUE(UnsignedVec[2] < UnsignedVec[1]); |
| } |
| |
| // Checks that a variable argument instructions are mapped to an illegal value. |
| // We exclude variable argument instructions since variable arguments |
| // requires extra checking of the argument list. |
| TEST(IRInstructionMapper, VarArgsIllegal) { |
| StringRef ModuleString = R"( |
| declare void @llvm.va_start(i8*) |
| declare void @llvm.va_copy(i8*, i8*) |
| declare void @llvm.va_end(i8*) |
| |
| define i32 @func1(i32 %a, double %b, i8* %v, ...) nounwind { |
| entry: |
| %a.addr = alloca i32, align 4 |
| %b.addr = alloca double, align 8 |
| %ap = alloca i8*, align 4 |
| %c = alloca i32, align 4 |
| store i32 %a, i32* %a.addr, align 4 |
| store double %b, double* %b.addr, align 8 |
| %ap1 = bitcast i8** %ap to i8* |
| call void @llvm.va_start(i8* %ap1) |
| store double %b, double* %b.addr, align 8 |
| store double %b, double* %b.addr, align 8 |
| %0 = va_arg i8** %ap, i32 |
| store double %b, double* %b.addr, align 8 |
| store double %b, double* %b.addr, align 8 |
| call void @llvm.va_copy(i8* %v, i8* %ap1) |
| store double %b, double* %b.addr, align 8 |
| store double %b, double* %b.addr, align 8 |
| call void @llvm.va_end(i8* %ap1) |
| store i32 %0, i32* %c, align 4 |
| %tmp = load i32, i32* %c, align 4 |
| ret i32 %tmp |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| ASSERT_EQ(InstrList.size(), UnsignedVec.size()); |
| ASSERT_EQ(UnsignedVec.size(), static_cast<unsigned>(16)); |
| ASSERT_TRUE(UnsignedVec[4] < UnsignedVec[3]); |
| ASSERT_TRUE(UnsignedVec[7] < UnsignedVec[6]); |
| ASSERT_TRUE(UnsignedVec[10] < UnsignedVec[9]); |
| ASSERT_TRUE(UnsignedVec[13] < UnsignedVec[12]); |
| } |
| |
| // Check the length of adding two illegal instructions one after th other. We |
| // should find that only one element is added for each illegal range. |
| TEST(IRInstructionMapper, RepeatedIllegalLength) { |
| StringRef ModuleString = R"( |
| define i32 @f(i32 %a, i32 %b) { |
| bb0: |
| %0 = add i32 %a, %b |
| %1 = mul i32 %a, %b |
| %2 = alloca i32 |
| %3 = alloca i32 |
| %4 = add i32 %a, %b |
| %5 = mul i32 %a, %b |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| // Check that the size of the unsigned vector and the instruction list are the |
| // same as a safety check. |
| ASSERT_TRUE(InstrList.size() == UnsignedVec.size()); |
| |
| // Make sure that the unsigned vector is the expected size. |
| ASSERT_TRUE(UnsignedVec.size() == 6); |
| } |
| |
| // A helper function that accepts an instruction list from a module made up of |
| // two blocks of two legal instructions and terminator, and checks them for |
| // instruction similarity. |
| static bool longSimCandCompare(std::vector<IRInstructionData *> &InstrList, |
| bool Structure = false) { |
| std::vector<IRInstructionData *>::iterator Start, End; |
| |
| Start = InstrList.begin(); |
| End = InstrList.begin(); |
| |
| std::advance(End, 1); |
| IRSimilarityCandidate Cand1(0, 2, *Start, *End); |
| |
| Start = InstrList.begin(); |
| End = InstrList.begin(); |
| |
| std::advance(Start, 3); |
| std::advance(End, 4); |
| IRSimilarityCandidate Cand2(3, 2, *Start, *End); |
| if (Structure) |
| return IRSimilarityCandidate::compareStructure(Cand1, Cand2); |
| return IRSimilarityCandidate::isSimilar(Cand1, Cand2); |
| } |
| |
| // Checks that two adds with commuted operands are considered to be the same |
| // instructions. |
| TEST(IRSimilarityCandidate, CheckIdenticalInstructions) { |
| StringRef ModuleString = R"( |
| define i32 @f(i32 %a, i32 %b) { |
| bb0: |
| %0 = add i32 %a, %b |
| %1 = add i32 %b, %a |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| // Check to make sure that we have a long enough region. |
| ASSERT_EQ(InstrList.size(), static_cast<unsigned>(3)); |
| // Check that the instructions were added correctly to both vectors. |
| ASSERT_TRUE(InstrList.size() == UnsignedVec.size()); |
| |
| std::vector<IRInstructionData *>::iterator Start, End; |
| Start = InstrList.begin(); |
| End = InstrList.begin(); |
| std::advance(End, 1); |
| IRSimilarityCandidate Cand1(0, 2, *Start, *End); |
| IRSimilarityCandidate Cand2(0, 2, *Start, *End); |
| |
| ASSERT_TRUE(IRSimilarityCandidate::isSimilar(Cand1, Cand2)); |
| } |
| |
| // Checks that comparison instructions are found to be similar instructions |
| // when the operands are flipped and the predicate is also swapped. |
| TEST(IRSimilarityCandidate, PredicateIsomorphism) { |
| StringRef ModuleString = R"( |
| define i32 @f(i32 %a, i32 %b) { |
| bb0: |
| %0 = icmp sgt i32 %a, %b |
| %1 = add i32 %b, %a |
| br label %bb1 |
| bb1: |
| %2 = icmp slt i32 %a, %b |
| %3 = add i32 %a, %b |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| ASSERT_TRUE(InstrList.size() > 5); |
| ASSERT_TRUE(InstrList.size() == UnsignedVec.size()); |
| |
| std::vector<IRInstructionData *>::iterator Start, End; |
| Start = InstrList.begin(); |
| End = InstrList.begin(); |
| |
| std::advance(End, 1); |
| IRSimilarityCandidate Cand1(0, 2, *Start, *End); |
| |
| Start = InstrList.begin(); |
| End = InstrList.begin(); |
| |
| std::advance(Start, 3); |
| std::advance(End, 4); |
| IRSimilarityCandidate Cand2(3, 2, *Start, *End); |
| |
| ASSERT_TRUE(IRSimilarityCandidate::isSimilar(Cand1, Cand2)); |
| } |
| |
| // Checks that IRSimilarityCandidates wrapping these two regions of instructions |
| // are able to differentiate between instructions that have different opcodes. |
| TEST(IRSimilarityCandidate, CheckRegionsDifferentInstruction) { |
| StringRef ModuleString = R"( |
| define i32 @f(i32 %a, i32 %b) { |
| bb0: |
| %0 = add i32 %a, %b |
| %1 = add i32 %b, %a |
| ret i32 0 |
| bb1: |
| %2 = sub i32 %a, %b |
| %3 = add i32 %b, %a |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| // Check to make sure that we have a long enough region. |
| ASSERT_EQ(InstrList.size(), static_cast<unsigned>(6)); |
| // Check that the instructions were added correctly to both vectors. |
| ASSERT_TRUE(InstrList.size() == UnsignedVec.size()); |
| |
| ASSERT_FALSE(longSimCandCompare(InstrList)); |
| } |
| |
| // Checks that IRSimilarityCandidates wrapping these two regions of instructions |
| // are able to differentiate between instructions that have different types. |
| TEST(IRSimilarityCandidate, CheckRegionsDifferentTypes) { |
| StringRef ModuleString = R"( |
| define i32 @f(i32 %a, i32 %b, i64 %c, i64 %d) { |
| bb0: |
| %0 = add i32 %a, %b |
| %1 = add i32 %b, %a |
| ret i32 0 |
| bb1: |
| %2 = add i64 %c, %d |
| %3 = add i64 %d, %c |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| // Check to make sure that we have a long enough region. |
| ASSERT_EQ(InstrList.size(), static_cast<unsigned>(6)); |
| // Check that the instructions were added correctly to both vectors. |
| ASSERT_TRUE(InstrList.size() == UnsignedVec.size()); |
| |
| ASSERT_FALSE(longSimCandCompare(InstrList)); |
| } |
| |
| // Check that debug instructions do not impact similarity. They are marked as |
| // invisible. |
| TEST(IRSimilarityCandidate, IdenticalWithDebug) { |
| StringRef ModuleString = R"( |
| define i32 @f(i32 %a, i32 %b) { |
| bb0: |
| %0 = add i32 %a, %b |
| call void @llvm.dbg.value(metadata !0) |
| %1 = add i32 %b, %a |
| ret i32 0 |
| bb1: |
| %2 = add i32 %a, %b |
| call void @llvm.dbg.value(metadata !1) |
| %3 = add i32 %b, %a |
| ret i32 0 |
| bb2: |
| %4 = add i32 %a, %b |
| %5 = add i32 %b, %a |
| ret i32 0 |
| } |
| |
| declare void @llvm.dbg.value(metadata) |
| !0 = distinct !{!"test\00", i32 10} |
| !1 = distinct !{!"test\00", i32 11})"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| // Check to make sure that we have a long enough region. |
| ASSERT_EQ(InstrList.size(), static_cast<unsigned>(9)); |
| // Check that the instructions were added correctly to both vectors. |
| ASSERT_TRUE(InstrList.size() == UnsignedVec.size()); |
| |
| ASSERT_TRUE(longSimCandCompare(InstrList)); |
| } |
| |
| // Checks that IRSimilarityCandidates that include illegal instructions, are not |
| // considered to be the same set of instructions. In these sets of instructions |
| // the allocas are illegal. |
| TEST(IRSimilarityCandidate, IllegalInCandidate) { |
| StringRef ModuleString = R"( |
| define i32 @f(i32 %a, i32 %b) { |
| bb0: |
| %0 = add i32 %a, %b |
| %1 = add i32 %a, %b |
| %2 = alloca i32 |
| ret i32 0 |
| bb1: |
| %3 = add i32 %a, %b |
| %4 = add i32 %a, %b |
| %5 = alloca i32 |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| // Check to make sure that we have a long enough region. |
| ASSERT_EQ(InstrList.size(), static_cast<unsigned>(6)); |
| // Check that the instructions were added correctly to both vectors. |
| ASSERT_TRUE(InstrList.size() == UnsignedVec.size()); |
| |
| std::vector<IRInstructionData *>::iterator Start, End; |
| |
| Start = InstrList.begin(); |
| End = InstrList.begin(); |
| |
| std::advance(End, 2); |
| IRSimilarityCandidate Cand1(0, 3, *Start, *End); |
| |
| Start = InstrList.begin(); |
| End = InstrList.begin(); |
| |
| std::advance(Start, 3); |
| std::advance(End, 5); |
| IRSimilarityCandidate Cand2(3, 3, *Start, *End); |
| ASSERT_FALSE(IRSimilarityCandidate::isSimilar(Cand1, Cand2)); |
| } |
| |
| // Checks that different structure, in this case, where we introduce a new |
| // needed input in one region, is recognized as different. |
| TEST(IRSimilarityCandidate, DifferentStructure) { |
| StringRef ModuleString = R"( |
| define i32 @f(i32 %a, i32 %b) { |
| bb0: |
| %0 = add i32 %a, %b |
| %1 = add i32 %b, %a |
| ret i32 0 |
| bb1: |
| %2 = add i32 %a, %b |
| %3 = add i32 %b, %0 |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| // Check to make sure that we have a long enough region. |
| ASSERT_EQ(InstrList.size(), static_cast<unsigned>(6)); |
| // Check that the instructions were added correctly to both vectors. |
| ASSERT_TRUE(InstrList.size() == UnsignedVec.size()); |
| |
| ASSERT_FALSE(longSimCandCompare(InstrList, true)); |
| } |
| |
| // Checks that comparison instructions are found to have the same structure |
| // when the operands are flipped and the predicate is also swapped. |
| TEST(IRSimilarityCandidate, PredicateIsomorphismStructure) { |
| StringRef ModuleString = R"( |
| define i32 @f(i32 %a, i32 %b) { |
| bb0: |
| %0 = icmp sgt i32 %a, %b |
| %1 = add i32 %a, %b |
| br label %bb1 |
| bb1: |
| %2 = icmp slt i32 %b, %a |
| %3 = add i32 %a, %b |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| ASSERT_TRUE(InstrList.size() > 5); |
| ASSERT_TRUE(InstrList.size() == UnsignedVec.size()); |
| |
| ASSERT_TRUE(longSimCandCompare(InstrList, true)); |
| } |
| |
| // Checks that different predicates are counted as diferent. |
| TEST(IRSimilarityCandidate, PredicateDifference) { |
| StringRef ModuleString = R"( |
| define i32 @f(i32 %a, i32 %b) { |
| bb0: |
| %0 = icmp sge i32 %a, %b |
| %1 = add i32 %b, %a |
| br label %bb1 |
| bb1: |
| %2 = icmp slt i32 %b, %a |
| %3 = add i32 %a, %b |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| ASSERT_TRUE(InstrList.size() > 5); |
| ASSERT_TRUE(InstrList.size() == UnsignedVec.size()); |
| |
| ASSERT_FALSE(longSimCandCompare(InstrList)); |
| } |
| |
| // Checks that the same structure is recognized between two candidates. The |
| // items %a and %b are used in the same way in both sets of instructions. |
| TEST(IRSimilarityCandidate, SameStructure) { |
| StringRef ModuleString = R"( |
| define i32 @f(i32 %a, i32 %b) { |
| bb0: |
| %0 = add i32 %a, %b |
| %1 = sub i32 %b, %a |
| ret i32 0 |
| bb1: |
| %2 = add i32 %a, %b |
| %3 = sub i32 %b, %a |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| // Check to make sure that we have a long enough region. |
| ASSERT_EQ(InstrList.size(), static_cast<unsigned>(6)); |
| // Check that the instructions were added correctly to both vectors. |
| ASSERT_TRUE(InstrList.size() == UnsignedVec.size()); |
| |
| ASSERT_TRUE(longSimCandCompare(InstrList, true)); |
| } |
| |
| // Checks that the same structure is recognized between two candidates. While |
| // the input names are reversed, they still perform the same overall operation. |
| TEST(IRSimilarityCandidate, DifferentNameSameStructure) { |
| StringRef ModuleString = R"( |
| define i32 @f(i32 %a, i32 %b) { |
| bb0: |
| %0 = add i32 %a, %b |
| %1 = add i32 %b, %a |
| ret i32 0 |
| bb1: |
| %2 = add i32 %b, %a |
| %3 = add i32 %a, %b |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<IRInstructionData *> InstrList; |
| std::vector<unsigned> UnsignedVec; |
| |
| SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator; |
| SpecificBumpPtrAllocator<IRInstructionDataList> IDLAllocator; |
| IRInstructionMapper Mapper(&InstDataAllocator, &IDLAllocator); |
| getVectors(*M, Mapper, InstrList, UnsignedVec); |
| |
| // Check to make sure that we have a long enough region. |
| ASSERT_EQ(InstrList.size(), static_cast<unsigned>(6)); |
| // Check that the instructions were added correctly to both vectors. |
| ASSERT_TRUE(InstrList.size() == UnsignedVec.size()); |
| |
| ASSERT_TRUE(longSimCandCompare(InstrList, true)); |
| } |
| |
| // Checks that two sets of identical instructions are found to be the same. |
| // Both sequences of adds have the same operand ordering, and the same |
| // instructions, making them strcturally equivalent. |
| TEST(IRSimilarityIdentifier, IdentitySimilarity) { |
| StringRef ModuleString = R"( |
| define i32 @f(i32 %a, i32 %b) { |
| bb0: |
| %0 = add i32 %a, %b |
| %1 = sub i32 %b, %a |
| br label %bb1 |
| bb1: |
| %2 = add i32 %a, %b |
| %3 = sub i32 %b, %a |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<std::vector<IRSimilarityCandidate>> SimilarityCandidates; |
| getSimilarities(*M, SimilarityCandidates); |
| |
| ASSERT_TRUE(SimilarityCandidates.size() == 1); |
| for (std::vector<IRSimilarityCandidate> &Cands : SimilarityCandidates) { |
| ASSERT_TRUE(Cands.size() == 2); |
| unsigned InstIdx = 0; |
| for (IRSimilarityCandidate &Cand : Cands) { |
| ASSERT_TRUE(Cand.getStartIdx() == InstIdx); |
| InstIdx += 3; |
| } |
| } |
| } |
| |
| // Checks that incorrect sequences are not found as similar. In this case, |
| // we have different sequences of instructions. |
| TEST(IRSimilarityIdentifier, InstructionDifference) { |
| StringRef ModuleString = R"( |
| define i32 @f(i32 %a, i32 %b, i32 %c, i32 %d) { |
| bb0: |
| %0 = sub i32 %a, %b |
| %1 = add i32 %b, %a |
| br label %bb1 |
| bb1: |
| %2 = add i32 %c, %d |
| %3 = sub i32 %d, %c |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<std::vector<IRSimilarityCandidate>> SimilarityCandidates; |
| getSimilarities(*M, SimilarityCandidates); |
| |
| ASSERT_TRUE(SimilarityCandidates.empty()); |
| } |
| |
| // This test checks to see whether we can detect similarity for commutative |
| // instructions where the operands have been reversed. |
| TEST(IRSimilarityIdentifier, CommutativeSimilarity) { |
| StringRef ModuleString = R"( |
| define i32 @f(i32 %a, i32 %b) { |
| bb0: |
| %0 = add i32 %a, %b |
| %1 = add i32 %b, %a |
| br label %bb1 |
| bb1: |
| %2 = add i32 %a, %b |
| %3 = add i32 %a, %b |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<std::vector<IRSimilarityCandidate>> SimilarityCandidates; |
| getSimilarities(*M, SimilarityCandidates); |
| |
| ASSERT_TRUE(SimilarityCandidates.size() == 1); |
| for (std::vector<IRSimilarityCandidate> &Cands : SimilarityCandidates) { |
| ASSERT_TRUE(Cands.size() == 2); |
| unsigned InstIdx = 0; |
| for (IRSimilarityCandidate &Cand : Cands) { |
| ASSERT_TRUE(Cand.getStartIdx() == InstIdx); |
| InstIdx += 3; |
| } |
| } |
| } |
| |
| // This test checks to see whether we can detect different structure in |
| // commutative instructions. In this case, the second operand in the second |
| // add is different. |
| TEST(IRSimilarityIdentifier, NoCommutativeSimilarity) { |
| StringRef ModuleString = R"( |
| define i32 @f(i32 %a, i32 %b) { |
| bb0: |
| %0 = add i32 %a, %b |
| %1 = add i32 %1, %b |
| br label %bb1 |
| bb1: |
| %2 = add i32 %a, %b |
| %3 = add i32 %2, %a |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<std::vector<IRSimilarityCandidate>> SimilarityCandidates; |
| getSimilarities(*M, SimilarityCandidates); |
| |
| ASSERT_TRUE(SimilarityCandidates.size() == 0); |
| } |
| |
| // Check that we are not finding similarity in non commutative |
| // instructions. That is, while the instruction and operands used are the same |
| // in the two subtraction sequences, they are in a different order, and cannot |
| // be counted as the same since a subtraction is not commutative. |
| TEST(IRSimilarityIdentifier, NonCommutativeDifference) { |
| StringRef ModuleString = R"( |
| define i32 @f(i32 %a, i32 %b) { |
| bb0: |
| %0 = sub i32 %a, %b |
| %1 = sub i32 %b, %a |
| br label %bb1 |
| bb1: |
| %2 = sub i32 %a, %b |
| %3 = sub i32 %a, %b |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<std::vector<IRSimilarityCandidate>> SimilarityCandidates; |
| getSimilarities(*M, SimilarityCandidates); |
| |
| ASSERT_TRUE(SimilarityCandidates.empty()); |
| } |
| |
| // Check that we find similarity despite changing the register names. |
| TEST(IRSimilarityIdentifier, MappingSimilarity) { |
| StringRef ModuleString = R"( |
| define i32 @f(i32 %a, i32 %b, i32 %c, i32 %d) { |
| bb0: |
| %0 = add i32 %a, %b |
| %1 = sub i32 %b, %a |
| br label %bb1 |
| bb1: |
| %2 = add i32 %c, %d |
| %3 = sub i32 %d, %c |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<std::vector<IRSimilarityCandidate>> SimilarityCandidates; |
| getSimilarities(*M, SimilarityCandidates); |
| |
| ASSERT_TRUE(SimilarityCandidates.size() == 1); |
| for (std::vector<IRSimilarityCandidate> &Cands : SimilarityCandidates) { |
| ASSERT_TRUE(Cands.size() == 2); |
| unsigned InstIdx = 0; |
| for (IRSimilarityCandidate &Cand : Cands) { |
| ASSERT_TRUE(Cand.getStartIdx() == InstIdx); |
| InstIdx += 3; |
| } |
| } |
| } |
| |
| // Check that we find instances of swapped predicate isomorphism. That is, |
| // for predicates that can be flipped, e.g. greater than to less than, |
| // we can identify that instances of these different literal predicates, but are |
| // the same within a single swap can be found. |
| TEST(IRSimilarityIdentifier, PredicateIsomorphism) { |
| StringRef ModuleString = R"( |
| define i32 @f(i32 %a, i32 %b) { |
| bb0: |
| %0 = add i32 %a, %b |
| %1 = icmp sgt i32 %b, %a |
| br label %bb1 |
| bb1: |
| %2 = add i32 %a, %b |
| %3 = icmp slt i32 %a, %b |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<std::vector<IRSimilarityCandidate>> SimilarityCandidates; |
| getSimilarities(*M, SimilarityCandidates); |
| |
| ASSERT_TRUE(SimilarityCandidates.size() == 1); |
| for (std::vector<IRSimilarityCandidate> &Cands : SimilarityCandidates) { |
| ASSERT_TRUE(Cands.size() == 2); |
| unsigned InstIdx = 0; |
| for (IRSimilarityCandidate &Cand : Cands) { |
| ASSERT_TRUE(Cand.getStartIdx() == InstIdx); |
| InstIdx += 3; |
| } |
| } |
| } |
| |
| // Checks that constants are detected as the same operand in each use in the |
| // sequences of instructions. Also checks that we can find structural |
| // equivalence using constants. In this case the 1 has the same use pattern as |
| // %a. |
| TEST(IRSimilarityIdentifier, ConstantMappingSimilarity) { |
| StringRef ModuleString = R"( |
| define i32 @f(i32 %a, i32 %b) { |
| bb0: |
| %0 = add i32 1, %b |
| %1 = icmp sgt i32 %b, 1 |
| br label %bb1 |
| bb1: |
| %2 = add i32 %a, %b |
| %3 = icmp sgt i32 %b, %a |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<std::vector<IRSimilarityCandidate>> SimilarityCandidates; |
| getSimilarities(*M, SimilarityCandidates); |
| |
| ASSERT_TRUE(SimilarityCandidates.size() == 1); |
| for (std::vector<IRSimilarityCandidate> &Cands : SimilarityCandidates) { |
| ASSERT_TRUE(Cands.size() == 2); |
| unsigned InstIdx = 0; |
| for (IRSimilarityCandidate &Cand : Cands) { |
| ASSERT_TRUE(Cand.getStartIdx() == InstIdx); |
| InstIdx += 3; |
| } |
| } |
| } |
| |
| // Check that constants are uniquely identified. i.e. two different constants |
| // are not considered the same. This means that this should not find any |
| // structural similarity. |
| TEST(IRSimilarityIdentifier, ConstantMappingDifference) { |
| StringRef ModuleString = R"( |
| define i32 @f(i32 %a, i32 %b) { |
| bb0: |
| %0 = add i32 1, %b |
| %1 = icmp sgt i32 %b, 2 |
| br label %bb1 |
| bb1: |
| %2 = add i32 %a, %b |
| %3 = icmp slt i32 %a, %b |
| ret i32 0 |
| })"; |
| LLVMContext Context; |
| std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString); |
| |
| std::vector<std::vector<IRSimilarityCandidate>> SimilarityCandidates; |
| getSimilarities(*M, SimilarityCandidates); |
| |
| ASSERT_TRUE(SimilarityCandidates.empty()); |
| } |