1 | //===- llvm/unittest/IR/StructuralHashTest.cpp ----------------------------===// |
2 | // |
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 |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
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 "gtest/gtest.h" |
14 | |
15 | #include <memory> |
16 | |
17 | using namespace llvm; |
18 | |
19 | namespace { |
20 | |
21 | std::unique_ptr<Module> parseIR(LLVMContext &Context, const char *IR) { |
22 | SMDiagnostic Err; |
23 | std::unique_ptr<Module> M = parseAssemblyString(AsmString: IR, Err, Context); |
24 | if (!M) |
25 | Err.print(ProgName: "StructuralHashTest" , S&: errs()); |
26 | return M; |
27 | } |
28 | |
29 | TEST(StructuralHashTest, Empty) { |
30 | LLVMContext Ctx; |
31 | std::unique_ptr<Module> M1 = parseIR(Context&: Ctx, IR: "" ); |
32 | std::unique_ptr<Module> M2 = parseIR(Context&: Ctx, IR: "" ); |
33 | EXPECT_EQ(StructuralHash(*M1), StructuralHash(*M2)); |
34 | } |
35 | |
36 | TEST(StructuralHashTest, Basic) { |
37 | LLVMContext Ctx; |
38 | std::unique_ptr<Module> M0 = parseIR(Context&: Ctx, IR: "" ); |
39 | std::unique_ptr<Module> M1 = parseIR(Context&: Ctx, IR: "define void @f() { ret void }" ); |
40 | std::unique_ptr<Module> M2 = parseIR(Context&: Ctx, IR: "define void @f() { ret void }" ); |
41 | std::unique_ptr<Module> M3 = parseIR(Context&: Ctx, IR: "@g = global i32 2" ); |
42 | std::unique_ptr<Module> M4 = parseIR(Context&: Ctx, IR: "@g = global i32 2" ); |
43 | EXPECT_NE(StructuralHash(*M0), StructuralHash(*M1)); |
44 | EXPECT_NE(StructuralHash(*M0), StructuralHash(*M3)); |
45 | EXPECT_NE(StructuralHash(*M1), StructuralHash(*M3)); |
46 | EXPECT_EQ(StructuralHash(*M1), StructuralHash(*M2)); |
47 | EXPECT_EQ(StructuralHash(*M3), StructuralHash(*M4)); |
48 | } |
49 | |
50 | TEST(StructuralHashTest, BasicFunction) { |
51 | LLVMContext Ctx; |
52 | std::unique_ptr<Module> M = parseIR(Context&: Ctx, IR: "define void @f() {\n" |
53 | " ret void\n" |
54 | "}\n" |
55 | "define void @g() {\n" |
56 | " ret void\n" |
57 | "}\n" |
58 | "define i32 @h(i32 %i) {\n" |
59 | " ret i32 %i\n" |
60 | "}\n" ); |
61 | EXPECT_EQ(StructuralHash(*M->getFunction("f" )), |
62 | StructuralHash(*M->getFunction("g" ))); |
63 | EXPECT_NE(StructuralHash(*M->getFunction("f" )), |
64 | StructuralHash(*M->getFunction("h" ))); |
65 | } |
66 | |
67 | TEST(StructuralHashTest, Declaration) { |
68 | LLVMContext Ctx; |
69 | std::unique_ptr<Module> M0 = parseIR(Context&: Ctx, IR: "" ); |
70 | std::unique_ptr<Module> M1 = parseIR(Context&: Ctx, IR: "declare void @f()" ); |
71 | std::unique_ptr<Module> M2 = parseIR(Context&: Ctx, IR: "@g = external global i32" ); |
72 | EXPECT_EQ(StructuralHash(*M0), StructuralHash(*M1)); |
73 | EXPECT_EQ(StructuralHash(*M0), StructuralHash(*M2)); |
74 | } |
75 | |
76 | TEST(StructuralHashTest, GlobalType) { |
77 | LLVMContext Ctx; |
78 | std::unique_ptr<Module> M1 = parseIR(Context&: Ctx, IR: "@g = global i32 1" ); |
79 | std::unique_ptr<Module> M2 = parseIR(Context&: Ctx, IR: "@g = global float 1.0" ); |
80 | EXPECT_NE(StructuralHash(*M1), StructuralHash(*M2)); |
81 | } |
82 | |
83 | TEST(StructuralHashTest, Function) { |
84 | LLVMContext Ctx; |
85 | std::unique_ptr<Module> M1 = parseIR(Context&: Ctx, IR: "define void @f() { ret void }" ); |
86 | std::unique_ptr<Module> M2 = parseIR(Context&: Ctx, IR: "define void @f(i32) { ret void }" ); |
87 | EXPECT_NE(StructuralHash(*M1), StructuralHash(*M2)); |
88 | } |
89 | |
90 | TEST(StructuralHashTest, FunctionRetType) { |
91 | LLVMContext Ctx; |
92 | std::unique_ptr<Module> M1 = parseIR(Context&: Ctx, IR: "define void @f() { ret void }" ); |
93 | std::unique_ptr<Module> M2 = parseIR(Context&: Ctx, IR: "define i32 @f() { ret i32 0 }" ); |
94 | EXPECT_EQ(StructuralHash(*M1), StructuralHash(*M2)); |
95 | EXPECT_NE(StructuralHash(*M1, true), StructuralHash(*M2, true)); |
96 | } |
97 | |
98 | TEST(StructuralHashTest, InstructionOpCode) { |
99 | LLVMContext Ctx; |
100 | std::unique_ptr<Module> M1 = parseIR(Context&: Ctx, IR: "define void @f(ptr %p) {\n" |
101 | " %a = load i32, ptr %p\n" |
102 | " ret void\n" |
103 | "}\n" ); |
104 | std::unique_ptr<Module> M2 = |
105 | parseIR(Context&: Ctx, IR: "define void @f(ptr %p) {\n" |
106 | " %a = getelementptr i8, ptr %p, i32 1\n" |
107 | " ret void\n" |
108 | "}\n" ); |
109 | EXPECT_NE(StructuralHash(*M1), StructuralHash(*M2)); |
110 | } |
111 | |
112 | TEST(StructuralHashTest, InstructionSubType) { |
113 | LLVMContext Ctx; |
114 | std::unique_ptr<Module> M1 = parseIR(Context&: Ctx, IR: "define void @f(ptr %p) {\n" |
115 | " %a = load i32, ptr %p\n" |
116 | " ret void\n" |
117 | "}\n" ); |
118 | std::unique_ptr<Module> M2 = parseIR(Context&: Ctx, IR: "define void @f(ptr %p) {\n" |
119 | " %a = load i64, ptr %p\n" |
120 | " ret void\n" |
121 | "}\n" ); |
122 | EXPECT_EQ(StructuralHash(*M1), StructuralHash(*M2)); |
123 | EXPECT_NE(StructuralHash(*M1, true), StructuralHash(*M2, true)); |
124 | } |
125 | |
126 | TEST(StructuralHashTest, InstructionType) { |
127 | LLVMContext Ctx; |
128 | std::unique_ptr<Module> M1 = parseIR(Context&: Ctx, IR: "define void @f(ptr %p) {\n" |
129 | " %1 = load i32, ptr %p\n" |
130 | " ret void\n" |
131 | "}\n" ); |
132 | std::unique_ptr<Module> M2 = parseIR(Context&: Ctx, IR: "define void @f(ptr %p) {\n" |
133 | " %1 = load float, ptr %p\n" |
134 | " ret void\n" |
135 | "}\n" ); |
136 | EXPECT_EQ(StructuralHash(*M1), StructuralHash(*M2)); |
137 | EXPECT_NE(StructuralHash(*M1, true), StructuralHash(*M2, true)); |
138 | } |
139 | |
140 | TEST(StructuralHashTest, IgnoredMetadata) { |
141 | LLVMContext Ctx; |
142 | std::unique_ptr<Module> M1 = parseIR(Context&: Ctx, IR: "@a = global i32 1\n" ); |
143 | // clang-format off |
144 | std::unique_ptr<Module> M2 = parseIR( |
145 | Context&: Ctx, IR: R"( |
146 | @a = global i32 1 |
147 | @llvm.embedded.object = private constant [4 x i8] c"BC\C0\00", section ".llvm.lto", align 1, !exclude !0 |
148 | @llvm.compiler.used = appending global [1 x ptr] [ptr @llvm.embedded.object], section "llvm.metadata" |
149 | |
150 | !llvm.embedded.objects = !{!1} |
151 | |
152 | !0 = !{} |
153 | !1 = !{ptr @llvm.embedded.object, !".llvm.lto"} |
154 | )" ); |
155 | // clang-format on |
156 | EXPECT_EQ(StructuralHash(*M1), StructuralHash(*M2)); |
157 | } |
158 | |
159 | TEST(StructuralHashTest, ComparisonInstructionPredicate) { |
160 | LLVMContext Ctx; |
161 | std::unique_ptr<Module> M1 = parseIR(Context&: Ctx, IR: "define i1 @f(i64 %a, i64 %b) {\n" |
162 | " %1 = icmp eq i64 %a, %b\n" |
163 | " ret i1 %1\n" |
164 | "}\n" ); |
165 | std::unique_ptr<Module> M2 = parseIR(Context&: Ctx, IR: "define i1 @f(i64 %a, i64 %b) {\n" |
166 | " %1 = icmp ne i64 %a, %b\n" |
167 | " ret i1 %1\n" |
168 | "}\n" ); |
169 | EXPECT_EQ(StructuralHash(*M1), StructuralHash(*M2)); |
170 | EXPECT_NE(StructuralHash(*M1, true), StructuralHash(*M2, true)); |
171 | } |
172 | |
173 | TEST(StructuralHashTest, IntrinsicInstruction) { |
174 | LLVMContext Ctx; |
175 | std::unique_ptr<Module> M1 = |
176 | parseIR(Context&: Ctx, IR: "define float @f(float %a) {\n" |
177 | " %b = call float @llvm.sin.f32(float %a)\n" |
178 | " ret float %b\n" |
179 | "}\n" |
180 | "declare float @llvm.sin.f32(float)\n" ); |
181 | std::unique_ptr<Module> M2 = |
182 | parseIR(Context&: Ctx, IR: "define float @f(float %a) {\n" |
183 | " %b = call float @llvm.cos.f32(float %a)\n" |
184 | " ret float %b\n" |
185 | "}\n" |
186 | "declare float @llvm.cos.f32(float)\n" ); |
187 | EXPECT_EQ(StructuralHash(*M1), StructuralHash(*M2)); |
188 | EXPECT_NE(StructuralHash(*M1, true), StructuralHash(*M2, true)); |
189 | } |
190 | |
191 | TEST(StructuralHashTest, CallInstruction) { |
192 | LLVMContext Ctx; |
193 | std::unique_ptr<Module> M1 = parseIR(Context&: Ctx, IR: "define i64 @f(i64 %a) {\n" |
194 | " %b = call i64 @f1(i64 %a)\n" |
195 | " ret i64 %b\n" |
196 | "}\n" |
197 | "declare i64 @f1(i64)" ); |
198 | std::unique_ptr<Module> M2 = parseIR(Context&: Ctx, IR: "define i64 @f(i64 %a) {\n" |
199 | " %b = call i64 @f2(i64 %a)\n" |
200 | " ret i64 %b\n" |
201 | "}\n" |
202 | "declare i64 @f2(i64)" ); |
203 | EXPECT_EQ(StructuralHash(*M1), StructuralHash(*M2)); |
204 | EXPECT_NE(StructuralHash(*M1, true), StructuralHash(*M2, true)); |
205 | } |
206 | |
207 | TEST(StructuralHashTest, ConstantInteger) { |
208 | LLVMContext Ctx; |
209 | std::unique_ptr<Module> M1 = parseIR(Context&: Ctx, IR: "define i64 @f1() {\n" |
210 | " ret i64 1\n" |
211 | "}\n" ); |
212 | std::unique_ptr<Module> M2 = parseIR(Context&: Ctx, IR: "define i64 @f2() {\n" |
213 | " ret i64 2\n" |
214 | "}\n" ); |
215 | EXPECT_EQ(StructuralHash(*M1), StructuralHash(*M2)); |
216 | EXPECT_NE(StructuralHash(*M1, true), StructuralHash(*M2, true)); |
217 | } |
218 | |
219 | TEST(StructuralHashTest, BigConstantInteger) { |
220 | LLVMContext Ctx; |
221 | std::unique_ptr<Module> M1 = parseIR(Context&: Ctx, IR: "define i128 @f1() {\n" |
222 | " ret i128 18446744073709551616\n" |
223 | "}\n" ); |
224 | std::unique_ptr<Module> M2 = parseIR(Context&: Ctx, IR: "define i128 @f2() {\n" |
225 | " ret i128 18446744073709551617\n" |
226 | "}\n" ); |
227 | EXPECT_EQ(StructuralHash(*M1), StructuralHash(*M2)); |
228 | EXPECT_NE(StructuralHash(*M1, true), StructuralHash(*M2, true)); |
229 | } |
230 | |
231 | TEST(StructuralHashTest, ArgumentNumber) { |
232 | LLVMContext Ctx; |
233 | std::unique_ptr<Module> M1 = parseIR(Context&: Ctx, IR: "define i64 @f1(i64 %a, i64 %b) {\n" |
234 | " ret i64 %a\n" |
235 | "}\n" ); |
236 | std::unique_ptr<Module> M2 = parseIR(Context&: Ctx, IR: "define i64 @f2(i64 %a, i64 %b) {\n" |
237 | " ret i64 %b\n" |
238 | "}\n" ); |
239 | EXPECT_EQ(StructuralHash(*M1), StructuralHash(*M2)); |
240 | EXPECT_NE(StructuralHash(*M1, true), StructuralHash(*M2, true)); |
241 | } |
242 | } // end anonymous namespace |
243 | |