1 //===- llvm/unittest/IR/StructuralHashTest.cpp ----------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "llvm/IR/StructuralHash.h"
10 #include "llvm/AsmParser/Parser.h"
11 #include "llvm/IR/Module.h"
12 #include "llvm/Support/SourceMgr.h"
13 #include "gmock/gmock-matchers.h"
14 #include "gtest/gtest.h"
22 using testing::Contains
;
25 using testing::SizeIs
;
27 std::unique_ptr
<Module
> parseIR(LLVMContext
&Context
, const char *IR
) {
29 std::unique_ptr
<Module
> M
= parseAssemblyString(IR
, Err
, Context
);
31 Err
.print("StructuralHashTest", errs());
35 TEST(StructuralHashTest
, Empty
) {
37 std::unique_ptr
<Module
> M1
= parseIR(Ctx
, "");
38 std::unique_ptr
<Module
> M2
= parseIR(Ctx
, "");
39 EXPECT_EQ(StructuralHash(*M1
), StructuralHash(*M2
));
42 TEST(StructuralHashTest
, Basic
) {
44 std::unique_ptr
<Module
> M0
= parseIR(Ctx
, "");
45 std::unique_ptr
<Module
> M1
= parseIR(Ctx
, "define void @f() { ret void }");
46 std::unique_ptr
<Module
> M2
= parseIR(Ctx
, "define void @f() { ret void }");
47 std::unique_ptr
<Module
> M3
= parseIR(Ctx
, "@g = global i32 2");
48 std::unique_ptr
<Module
> M4
= parseIR(Ctx
, "@g = global i32 2");
49 EXPECT_NE(StructuralHash(*M0
), StructuralHash(*M1
));
50 EXPECT_NE(StructuralHash(*M0
), StructuralHash(*M3
));
51 EXPECT_NE(StructuralHash(*M1
), StructuralHash(*M3
));
52 EXPECT_EQ(StructuralHash(*M1
), StructuralHash(*M2
));
53 EXPECT_EQ(StructuralHash(*M3
), StructuralHash(*M4
));
56 TEST(StructuralHashTest
, BasicFunction
) {
58 std::unique_ptr
<Module
> M
= parseIR(Ctx
, "define void @f() {\n"
61 "define void @g() {\n"
64 "define i32 @h(i32 %i) {\n"
67 EXPECT_EQ(StructuralHash(*M
->getFunction("f")),
68 StructuralHash(*M
->getFunction("g")));
69 EXPECT_NE(StructuralHash(*M
->getFunction("f")),
70 StructuralHash(*M
->getFunction("h")));
73 TEST(StructuralHashTest
, Declaration
) {
75 std::unique_ptr
<Module
> M0
= parseIR(Ctx
, "");
76 std::unique_ptr
<Module
> M1
= parseIR(Ctx
, "declare void @f()");
77 std::unique_ptr
<Module
> M2
= parseIR(Ctx
, "@g = external global i32");
78 EXPECT_EQ(StructuralHash(*M0
), StructuralHash(*M1
));
79 EXPECT_EQ(StructuralHash(*M0
), StructuralHash(*M2
));
82 TEST(StructuralHashTest
, GlobalType
) {
84 std::unique_ptr
<Module
> M1
= parseIR(Ctx
, "@g = global i32 1");
85 std::unique_ptr
<Module
> M2
= parseIR(Ctx
, "@g = global float 1.0");
86 EXPECT_NE(StructuralHash(*M1
), StructuralHash(*M2
));
89 TEST(StructuralHashTest
, Function
) {
91 std::unique_ptr
<Module
> M1
= parseIR(Ctx
, "define void @f() { ret void }");
92 std::unique_ptr
<Module
> M2
= parseIR(Ctx
, "define void @f(i32) { ret void }");
93 EXPECT_NE(StructuralHash(*M1
), StructuralHash(*M2
));
96 TEST(StructuralHashTest
, FunctionRetType
) {
98 std::unique_ptr
<Module
> M1
= parseIR(Ctx
, "define void @f() { ret void }");
99 std::unique_ptr
<Module
> M2
= parseIR(Ctx
, "define i32 @f() { ret i32 0 }");
100 EXPECT_EQ(StructuralHash(*M1
), StructuralHash(*M2
));
101 EXPECT_NE(StructuralHash(*M1
, true), StructuralHash(*M2
, true));
104 TEST(StructuralHashTest
, InstructionOpCode
) {
106 std::unique_ptr
<Module
> M1
= parseIR(Ctx
, "define void @f(ptr %p) {\n"
107 " %a = load i32, ptr %p\n"
110 std::unique_ptr
<Module
> M2
=
111 parseIR(Ctx
, "define void @f(ptr %p) {\n"
112 " %a = getelementptr i8, ptr %p, i32 1\n"
115 EXPECT_NE(StructuralHash(*M1
), StructuralHash(*M2
));
118 TEST(StructuralHashTest
, InstructionSubType
) {
120 std::unique_ptr
<Module
> M1
= parseIR(Ctx
, "define void @f(ptr %p) {\n"
121 " %a = load i32, ptr %p\n"
124 std::unique_ptr
<Module
> M2
= parseIR(Ctx
, "define void @f(ptr %p) {\n"
125 " %a = load i64, ptr %p\n"
128 EXPECT_EQ(StructuralHash(*M1
), StructuralHash(*M2
));
129 EXPECT_NE(StructuralHash(*M1
, true), StructuralHash(*M2
, true));
132 TEST(StructuralHashTest
, InstructionType
) {
134 std::unique_ptr
<Module
> M1
= parseIR(Ctx
, "define void @f(ptr %p) {\n"
135 " %1 = load i32, ptr %p\n"
138 std::unique_ptr
<Module
> M2
= parseIR(Ctx
, "define void @f(ptr %p) {\n"
139 " %1 = load float, ptr %p\n"
142 EXPECT_EQ(StructuralHash(*M1
), StructuralHash(*M2
));
143 EXPECT_NE(StructuralHash(*M1
, true), StructuralHash(*M2
, true));
146 TEST(StructuralHashTest
, IgnoredMetadata
) {
148 std::unique_ptr
<Module
> M1
= parseIR(Ctx
, "@a = global i32 1\n");
150 std::unique_ptr
<Module
> M2
= parseIR(
153 @llvm.embedded.object = private constant [4 x i8] c"BC\C0\
00", section ".llvm
.lto
", align 1, !exclude !0
154 @llvm.compiler.used = appending global [1 x ptr] [ptr @llvm.embedded.object], section "llvm
.metadata
"
156 !llvm.embedded.objects = !{!1}
159 !1 = !{ptr @llvm.embedded.object, !".llvm
.lto
"}
162 EXPECT_EQ(StructuralHash(*M1
), StructuralHash(*M2
));
165 TEST(StructuralHashTest
, ComparisonInstructionPredicate
) {
167 std::unique_ptr
<Module
> M1
= parseIR(Ctx
, "define i1 @f(i64 %a, i64 %b) {\n"
168 " %1 = icmp eq i64 %a, %b\n"
171 std::unique_ptr
<Module
> M2
= parseIR(Ctx
, "define i1 @f(i64 %a, i64 %b) {\n"
172 " %1 = icmp ne i64 %a, %b\n"
175 EXPECT_EQ(StructuralHash(*M1
), StructuralHash(*M2
));
176 EXPECT_NE(StructuralHash(*M1
, true), StructuralHash(*M2
, true));
179 TEST(StructuralHashTest
, IntrinsicInstruction
) {
181 std::unique_ptr
<Module
> M1
=
182 parseIR(Ctx
, "define float @f(float %a) {\n"
183 " %b = call float @llvm.sin.f32(float %a)\n"
186 "declare float @llvm.sin.f32(float)\n");
187 std::unique_ptr
<Module
> M2
=
188 parseIR(Ctx
, "define float @f(float %a) {\n"
189 " %b = call float @llvm.cos.f32(float %a)\n"
192 "declare float @llvm.cos.f32(float)\n");
193 EXPECT_EQ(StructuralHash(*M1
), StructuralHash(*M2
));
194 EXPECT_NE(StructuralHash(*M1
, true), StructuralHash(*M2
, true));
197 TEST(StructuralHashTest
, CallInstruction
) {
199 std::unique_ptr
<Module
> M1
= parseIR(Ctx
, "define i64 @f(i64 %a) {\n"
200 " %b = call i64 @f1(i64 %a)\n"
203 "declare i64 @f1(i64)");
204 std::unique_ptr
<Module
> M2
= parseIR(Ctx
, "define i64 @f(i64 %a) {\n"
205 " %b = call i64 @f2(i64 %a)\n"
208 "declare i64 @f2(i64)");
209 EXPECT_EQ(StructuralHash(*M1
), StructuralHash(*M2
));
210 EXPECT_NE(StructuralHash(*M1
, true), StructuralHash(*M2
, true));
213 TEST(StructuralHashTest
, ConstantInteger
) {
215 std::unique_ptr
<Module
> M1
= parseIR(Ctx
, "define i64 @f1() {\n"
218 std::unique_ptr
<Module
> M2
= parseIR(Ctx
, "define i64 @f2() {\n"
221 EXPECT_EQ(StructuralHash(*M1
), StructuralHash(*M2
));
222 EXPECT_NE(StructuralHash(*M1
, true), StructuralHash(*M2
, true));
225 TEST(StructuralHashTest
, BigConstantInteger
) {
227 std::unique_ptr
<Module
> M1
= parseIR(Ctx
, "define i128 @f1() {\n"
228 " ret i128 18446744073709551616\n"
230 std::unique_ptr
<Module
> M2
= parseIR(Ctx
, "define i128 @f2() {\n"
231 " ret i128 18446744073709551617\n"
233 EXPECT_EQ(StructuralHash(*M1
), StructuralHash(*M2
));
234 EXPECT_NE(StructuralHash(*M1
, true), StructuralHash(*M2
, true));
237 TEST(StructuralHashTest
, ArgumentNumber
) {
239 std::unique_ptr
<Module
> M1
= parseIR(Ctx
, "define i64 @f1(i64 %a, i64 %b) {\n"
242 std::unique_ptr
<Module
> M2
= parseIR(Ctx
, "define i64 @f2(i64 %a, i64 %b) {\n"
245 EXPECT_EQ(StructuralHash(*M1
), StructuralHash(*M2
));
246 EXPECT_NE(StructuralHash(*M1
, true), StructuralHash(*M2
, true));
249 TEST(StructuralHashTest
, Differences
) {
251 std::unique_ptr
<Module
> M1
= parseIR(Ctx
, "define i64 @f(i64 %a) {\n"
252 " %c = add i64 %a, 1\n"
253 " %b = call i64 @f1(i64 %c)\n"
256 "declare i64 @f1(i64)");
257 auto *F1
= M1
->getFunction("f");
258 std::unique_ptr
<Module
> M2
= parseIR(Ctx
, "define i64 @g(i64 %a) {\n"
259 " %c = add i64 %a, 1\n"
260 " %b = call i64 @f2(i64 %c)\n"
263 "declare i64 @f2(i64)");
264 auto *F2
= M2
->getFunction("g");
266 // They are originally different when not ignoring any operand.
267 EXPECT_NE(StructuralHash(*F1
, true), StructuralHash(*F2
, true));
268 EXPECT_NE(StructuralHashWithDifferences(*F1
, nullptr).FunctionHash
,
269 StructuralHashWithDifferences(*F2
, nullptr).FunctionHash
);
271 // When we ignore the call target f1 vs f2, they have the same hash.
272 auto IgnoreOp
= [&](const Instruction
*I
, unsigned OpndIdx
) {
273 return I
->getOpcode() == Instruction::Call
&& OpndIdx
== 1;
275 auto FuncHashInfo1
= StructuralHashWithDifferences(*F1
, IgnoreOp
);
276 auto FuncHashInfo2
= StructuralHashWithDifferences(*F2
, IgnoreOp
);
277 EXPECT_EQ(FuncHashInfo1
.FunctionHash
, FuncHashInfo2
.FunctionHash
);
279 // There are total 3 instructions.
280 EXPECT_THAT(*FuncHashInfo1
.IndexInstruction
, SizeIs(3));
281 EXPECT_THAT(*FuncHashInfo2
.IndexInstruction
, SizeIs(3));
283 // The only 1 operand (the call target) has been ignored.
284 EXPECT_THAT(*FuncHashInfo1
.IndexOperandHashMap
, SizeIs(1u));
285 EXPECT_THAT(*FuncHashInfo2
.IndexOperandHashMap
, SizeIs(1u));
287 // The index pair of instruction and operand (1, 1) is a key in the map.
288 ASSERT_THAT(*FuncHashInfo1
.IndexOperandHashMap
, Contains(Key(Pair(1, 1))));
289 ASSERT_THAT(*FuncHashInfo2
.IndexOperandHashMap
, Contains(Key(Pair(1, 1))));
291 // The indexed instruciton must be the call instruction as shown in the
293 EXPECT_EQ(FuncHashInfo1
.IndexInstruction
->lookup(1)->getOpcode(),
295 EXPECT_EQ(FuncHashInfo2
.IndexInstruction
->lookup(1)->getOpcode(),
298 // The ignored operand hashes (for f1 vs. f2) are different.
299 EXPECT_NE(FuncHashInfo1
.IndexOperandHashMap
->lookup({1, 1}),
300 FuncHashInfo2
.IndexOperandHashMap
->lookup({1, 1}));
303 } // end anonymous namespace