Index: llvm/include/llvm/Analysis/IRSimilarityIdentifier.h =================================================================== --- llvm/include/llvm/Analysis/IRSimilarityIdentifier.h +++ llvm/include/llvm/Analysis/IRSimilarityIdentifier.h @@ -156,6 +156,12 @@ /// \param [in] Value - The IRInstructionData instance to be hashed. /// \returns A hash_value of the IRInstructionData. friend hash_code hash_value(const IRInstructionData &Value) { + if (CallInst *CI = dyn_cast(Value.Inst)) + return hash_combine( + hash_value(Value.Inst->getOpcode()), + hash_value(Value.Inst->getType()), + hash_value(CI->getCalledFunction()->getName().str()), + hash_combine_range(Value.OperTypes.begin(), Value.OperTypes.end())); return hash_combine( hash_value(Value.Inst->getOpcode()), hash_value(Value.Inst->getType()), hash_combine_range(Value.OperTypes.begin(), Value.OperTypes.end())); @@ -351,8 +357,14 @@ } // TODO: Handle specific intrinsics. InstrType visitIntrinsicInst(IntrinsicInst &II) { return Illegal; } - // TODO: Handle CallInsts. - InstrType visitCallInst(CallInst &CI) { return Illegal; } + // We only allow call instructions where the function has a name and + // is not an indirect call. + InstrType visitCallInst(CallInst &CI) { + Function *F = CI.getCalledFunction(); + if (!F || CI.isIndirectCall() || !F->hasName()) + return Illegal; + return Legal; + } // TODO: We do not current handle similarity that changes the control flow. InstrType visitInvokeInst(InvokeInst &II) { return Illegal; } // TODO: We do not current handle similarity that changes the control flow. Index: llvm/lib/Analysis/IRSimilarityIdentifier.cpp =================================================================== --- llvm/lib/Analysis/IRSimilarityIdentifier.cpp +++ llvm/lib/Analysis/IRSimilarityIdentifier.cpp @@ -33,6 +33,10 @@ } } +std::string getCalledFunctionName(CallInst &CI) { + return CI.getCalledFunction()->getName().str(); +} + bool IRSimilarity::isClose(const IRInstructionData &A, const IRInstructionData &B) { @@ -44,6 +48,20 @@ if (!A.Inst->isSameOperationAs(B.Inst)) return false; + // If the instructions are functions, we make sure that the function name is + // the same. We already know that the types are since is isSameOperationAs is + // true. + if (isa(A.Inst) && isa(B.Inst)) { + CallInst *CIA = cast(A.Inst); + CallInst *CIB = cast(B.Inst); + assert(CIA->getCalledFunction() != nullptr && + "Called Function is nullptr?"); + assert(CIB->getCalledFunction() != nullptr && + "Called Function is nullptr?"); + if (getCalledFunctionName(*CIA) != getCalledFunctionName(*CIB)) + return false; + } + return true; } Index: llvm/test/Transforms/IROutliner/illegal-calls.ll =================================================================== --- llvm/test/Transforms/IROutliner/illegal-calls.ll +++ llvm/test/Transforms/IROutliner/illegal-calls.ll @@ -13,7 +13,9 @@ ; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 ; CHECK-NEXT: [[B:%.*]] = alloca i32, align 4 ; CHECK-NEXT: [[C:%.*]] = alloca i32, align 4 -; CHECK-NEXT: call void @outlined_ir_func_1(i32* [[A]], i32* [[B]], i32* [[C]]) +; CHECK-NEXT: store i32 2, i32* [[A]], align 4 +; CHECK-NEXT: store i32 3, i32* [[B]], align 4 +; CHECK-NEXT: store i32 4, i32* [[C]], align 4 ; CHECK-NEXT: call void @f1(i32* [[A]], i32* [[B]]) ; CHECK-NEXT: call void @outlined_ir_func_0(i32* [[A]], i32* [[B]], i32* [[C]]) ; CHECK-NEXT: ret void @@ -38,7 +40,9 @@ ; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 ; CHECK-NEXT: [[B:%.*]] = alloca i32, align 4 ; CHECK-NEXT: [[C:%.*]] = alloca i32, align 4 -; CHECK-NEXT: call void @outlined_ir_func_1(i32* [[A]], i32* [[B]], i32* [[C]]) +; CHECK-NEXT: store i32 2, i32* [[A]], align 4 +; CHECK-NEXT: store i32 3, i32* [[B]], align 4 +; CHECK-NEXT: store i32 4, i32* [[C]], align 4 ; CHECK-NEXT: call void @f1(i32* [[A]], i32* [[B]]) ; CHECK-NEXT: call void @outlined_ir_func_0(i32* [[A]], i32* [[B]], i32* [[C]]) ; CHECK-NEXT: ret void Index: llvm/unittests/Analysis/IRSimilarityIdentifierTest.cpp =================================================================== --- llvm/unittests/Analysis/IRSimilarityIdentifierTest.cpp +++ llvm/unittests/Analysis/IRSimilarityIdentifierTest.cpp @@ -753,14 +753,37 @@ ASSERT_EQ(UnsignedVec.size(), static_cast(0)); } -// Checks that a call instruction is mapped to be illegal. We have to perform -// extra checks to ensure that both the name and function type are the same. -TEST(IRInstructionMapper, CallIllegal) { +// Checks that indirect call instructions are mapped to be illegal since we +// cannot guarantee the same of the function. +TEST(IRInstructionMapper, CallsIllegalIndirect) { + StringRef ModuleString = R"( + define i32 @f(void()* %func) { + bb0: + call void %func() + ret i32 0 + })"; + LLVMContext Context; + std::unique_ptr M = makeLLVMModule(Context, ModuleString); + + std::vector InstrList; + std::vector UnsignedVec; + + getVectors(*M, InstrList, UnsignedVec); + + ASSERT_EQ(InstrList.size(), UnsignedVec.size()); + ASSERT_EQ(UnsignedVec.size(), static_cast(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; @@ -772,7 +795,155 @@ getVectors(*M, InstrList, UnsignedVec); ASSERT_EQ(InstrList.size(), UnsignedVec.size()); - ASSERT_EQ(UnsignedVec.size(), static_cast(0)); + ASSERT_EQ(UnsignedVec.size(), static_cast(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 M = makeLLVMModule(Context, ModuleString); + + std::vector InstrList; + std::vector UnsignedVec; + + getVectors(*M, InstrList, UnsignedVec); + + ASSERT_EQ(InstrList.size(), UnsignedVec.size()); + ASSERT_EQ(UnsignedVec.size(), static_cast(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 M = makeLLVMModule(Context, ModuleString); + + std::vector InstrList; + std::vector UnsignedVec; + + getVectors(*M, InstrList, UnsignedVec); + + ASSERT_EQ(InstrList.size(), UnsignedVec.size()); + ASSERT_EQ(UnsignedVec.size(), static_cast(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 M = makeLLVMModule(Context, ModuleString); + + std::vector InstrList; + std::vector UnsignedVec; + + getVectors(*M, InstrList, UnsignedVec); + + ASSERT_EQ(InstrList.size(), UnsignedVec.size()); + ASSERT_EQ(UnsignedVec.size(), static_cast(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 M = makeLLVMModule(Context, ModuleString); + + std::vector InstrList; + std::vector UnsignedVec; + + getVectors(*M, InstrList, UnsignedVec); + + ASSERT_EQ(InstrList.size(), UnsignedVec.size()); + ASSERT_EQ(UnsignedVec.size(), static_cast(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 M = makeLLVMModule(Context, ModuleString); + + std::vector InstrList; + std::vector UnsignedVec; + + getVectors(*M, InstrList, UnsignedVec); + + ASSERT_EQ(InstrList.size(), UnsignedVec.size()); + ASSERT_EQ(UnsignedVec.size(), static_cast(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 M = makeLLVMModule(Context, ModuleString); + + std::vector InstrList; + std::vector UnsignedVec; + + getVectors(*M, InstrList, UnsignedVec); + + ASSERT_EQ(InstrList.size(), UnsignedVec.size()); + ASSERT_EQ(UnsignedVec.size(), static_cast(3)); + ASSERT_NE(UnsignedVec[0], UnsignedVec[1]); } // Checks that an invoke instruction is mapped to be illegal. Invoke @@ -1160,8 +1331,8 @@ bb0: %0 = add i32 %a, %b %1 = mul i32 %a, %b - %2 = call i32 @f(i32 %a, i32 %b) - %3 = call i32 @f(i32 %a, i32 %b) + %2 = alloca i32 + %3 = alloca i32 %4 = add i32 %a, %b %5 = mul i32 %a, %b ret i32 0