1/**
2 * Copyright (c) Glow Contributors. See CONTRIBUTORS file.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "glow/Graph/Graph.h"
18
19#include "glow/IR/IR.h"
20#include "glow/IR/IRBuilder.h"
21#include "glow/IR/Instrs.h"
22
23#include "llvm/ADT/StringSet.h"
24#include "llvm/Support/Casting.h"
25
26#include "gtest/gtest.h"
27
28#include <algorithm>
29#include <cassert>
30#include <cstddef>
31#include <cstdint>
32#include <iostream>
33#include <string>
34
35using namespace glow;
36using llvm::cast;
37using llvm::dyn_cast;
38using llvm::isa;
39
40TEST(IR, uniqueTypes) {
41 Module mod;
42 Type T1(ElemKind::FloatTy, {320, 200});
43 Type T2(ElemKind::FloatTy, {320, 200});
44 Type T3(ElemKind::FloatTy, {1, 2});
45
46 auto *u1 = mod.uniqueType(T1);
47 auto *u2 = mod.uniqueType(T2);
48 auto *u3 = mod.uniqueType(T3);
49
50 EXPECT_EQ(u1, u2);
51 EXPECT_NE(u1, u3);
52
53 for (int i = 0; i < 10; i++) {
54 EXPECT_EQ(u1, mod.uniqueType(T1));
55 }
56
57 // Check the uniqueing of quantized tensors.
58 Type T4(ElemKind::Int8QTy, {1, 2}, 0.4f, 2);
59 auto *t4 = mod.uniqueType(T4);
60 auto *u4 = mod.uniqueTypeWithNewShape(&T4, {2, 1});
61 auto *q4 = mod.uniqueTypeWithNewShape(u4, {1, 2});
62
63 EXPECT_NE(t4, u4);
64 EXPECT_EQ(t4, q4);
65}
66
67#define TEST_QUANT_TYPE(kind, type, type_name, scale, offset) \
68 { \
69 Type T(ElemKind::kind, {2, 3}, (scale), (offset)); \
70 EXPECT_EQ(T.getElementType(), ElemKind::kind); \
71 EXPECT_EQ(T.getScale(), (scale)); \
72 EXPECT_EQ(T.getOffset(), (offset)); \
73 EXPECT_TRUE(T.isQuantizedType()); \
74 EXPECT_EQ(T.getElementSize(), sizeof(type)); \
75 EXPECT_TRUE(T.isType<type>()); \
76 auto range = T.getQuantizedValueRange(); \
77 EXPECT_EQ(range.first, ((int64_t)type_name##_MIN - (offset)) * (scale)); \
78 EXPECT_EQ(range.second, ((int64_t)type_name##_MAX - (offset)) * (scale)); \
79 }
80
81TEST(IR, basicQuantizedTypes) {
82 // Quantized types
83 TEST_QUANT_TYPE(Int8QTy, int8_t, INT8, 0.3f, -45);
84 TEST_QUANT_TYPE(UInt8QTy, uint8_t, UINT8, 0.3f, -45);
85 TEST_QUANT_TYPE(Int16QTy, int16_t, INT16, 0.3f, -45);
86 TEST_QUANT_TYPE(Int32QTy, int32_t, INT32, 0.3f, -45);
87
88 // Sanity check for non quantized types
89 Type TF(ElemKind::FloatTy, {2, 3});
90 EXPECT_FALSE(TF.isQuantizedType());
91 Type T64I(ElemKind::Int64ITy, {2, 3});
92 EXPECT_FALSE(T64I.isQuantizedType());
93 Type TU8I(ElemKind::UInt8ITy, {2, 3});
94 EXPECT_FALSE(TU8I.isQuantizedType());
95}
96
97TEST(IR, basicUseList) {
98 Module mod;
99 Function *F = mod.createFunction("main");
100 IRFunction M(F);
101 {
102 IRBuilder builder(&M);
103
104 auto *V1 = builder.createWeightVar(ElemKind::FloatTy, {320, 200});
105 auto *V2 = builder.createWeightVar(ElemKind::FloatTy, {320, 200});
106
107 // Check that we can construct a new instruction.
108 auto *CC = builder.createCopyInst("C", V1, V2);
109 InstructionNumbering IN(M);
110 CC->verifyUseList(IN);
111
112 // Check the getOperand and setOperand functions.
113 EXPECT_EQ(CC->getDest(), V1);
114 CC->setOperand(0, V2);
115 EXPECT_EQ(CC->getDest(), V2);
116 CC->verifyUseList(IN);
117 }
118
119 // Check that we can destroy the operands.
120 // ...
121}
122
123static IRFunction *createTestIRFunction(Module &mod) {
124 using MK = WeightVar::MutabilityKind;
125
126 Function *F = mod.createFunction("main");
127 IRFunction *M = new IRFunction(F);
128 auto T1 = mod.uniqueType(ElemKind::FloatTy, {1, 24, 24, 3});
129 auto T2 = mod.uniqueType(ElemKind::FloatTy, {64});
130 auto T4 = mod.uniqueType(ElemKind::Int64ITy, {1, 1});
131
132 {
133 IRBuilder builder(M);
134
135 auto *I0 = builder.createWeightVar(T1, "I0");
136 auto *I1 = builder.createWeightVar(T1, "I1");
137 auto *I2 = builder.createWeightVar(ElemKind::FloatTy, {1, 3, 24, 24}, "I2",
138 MK::Constant);
139
140 auto *I3 = builder.createWeightVar(ElemKind::FloatTy, {1, 12, 12, 64});
141 auto *I4 = builder.createWeightVar(ElemKind::FloatTy, {1, 12, 12, 3});
142 auto *I6 = builder.createWeightVar(ElemKind::FloatTy, {2, 12, 12, 64});
143 auto *I8 = builder.createWeightVar(ElemKind::FloatTy, {1, 24, 3, 24}, "I8");
144 auto *ComputationInfo =
145 builder.createWeightVar(ElemKind::FloatTy, {2}, "ComputationInfo");
146
147 auto *argmax = builder.createWeightVar(ElemKind::Int64ITy, {1, 12, 12, 3});
148 auto *B0 = builder.createWeightVar(T2, "B0");
149 auto *B1 =
150 builder.createWeightVar(ElemKind::FloatTy, {32}, "B1", MK::Mutable);
151 auto *F0 = builder.createWeightVar(ElemKind::FloatTy, {64, 7, 7, 3});
152 auto *F1 = builder.createWeightVar(ElemKind::FloatTy, {32, 1728});
153 auto *E0 = builder.createWeightVar(T4, "E0");
154
155 B0->setName("bias");
156 B1->setName("FC_bias");
157 F0->setName("filter");
158 F1->setName("FC_filter");
159 E0->setName("expected");
160 argmax->setName("argmax");
161
162 builder.createCopyInst("", I1, I0);
163 builder.createConvolutionInst("", I3, I1, F0, B0, {7, 7}, {2, 2},
164 {3, 3, 3, 3}, 1, {1, 1}, NHWC,
165 FusedActivation::NONE, {});
166 builder.createMaxPoolInst("", I4, I0, {7, 7}, {2, 2}, {3, 3, 3, 3}, NHWC);
167 builder.createSigmoidInst("", I1, I0);
168 builder.createTanhInst("", I1, I0);
169 builder.createSoftMaxInst("", I1, I0);
170 builder.createTransposeInst("", I8, I2, NHWC2NCHW);
171 builder.createTensorView(ElemKind::FloatTy, {1, 24, 3, 24}, I2, "I2_view");
172 builder.createInsertTensorInst("", I6, I3, {0, 0, 0, 0}, 1, 0);
173 builder.createElementMulInst("", I1, I0, I0);
174 builder.createDebugPrintInst("", I0, "console", "");
175 builder.createQuantizationProfileInst("", I0, B0, ComputationInfo);
176 }
177 return M;
178}
179
180TEST(IR, allInstrs) {
181 Module mod;
182 std::unique_ptr<IRFunction> M(createTestIRFunction(mod));
183 M->verify();
184}
185
186/// Check the IR Functions cloning functionality.
187TEST(IR, cloning) {
188 Module mod;
189 std::unique_ptr<IRFunction> M(createTestIRFunction(mod));
190 std::unique_ptr<IRFunction> clonedM(M->clone(M->getName()));
191 auto dumpedM = M->toString();
192 auto dumpedClonedM = clonedM->toString();
193 EXPECT_EQ(dumpedM, dumpedClonedM);
194}
195
196TEST(IR, casting) {
197 Module mod;
198 Function *F = mod.createFunction("main");
199 IRFunction M(F);
200 {
201 IRBuilder bb(&M);
202
203 auto *input = bb.createWeightVar(ElemKind::FloatTy, {1, 224, 224, 3});
204 auto *res = bb.createAllocActivationInst("sigmoid.res", input->getType());
205 auto *sig = bb.createSigmoidInst("sigmoid", res, input);
206 auto *pool =
207 bb.createAvgPoolOp(sig->getDest(), {7, 7}, {2, 2}, {3, 3, 3, 3}, NHWC,
208 /* countIncludePads */ true);
209
210 EXPECT_EQ(isa<AvgPoolInst>(pool), true);
211 EXPECT_EQ(isa<AvgPoolInst>(input), false);
212 EXPECT_EQ(isa<SigmoidInst>(sig), true);
213 EXPECT_EQ(isa<SigmoidInst>(pool), false);
214
215 EXPECT_NE(dyn_cast<AvgPoolInst>(pool), nullptr);
216 EXPECT_EQ(dyn_cast<AvgPoolInst>(pool), pool);
217
218 EXPECT_NE(dyn_cast<WeightVar>(input), nullptr);
219 EXPECT_EQ(dyn_cast<WeightVar>(input), input);
220 }
221}
222
223TEST(IR, predicateIR) {
224 Module mod;
225 Function *F = mod.createFunction("predicated");
226 IRFunction M(F);
227 {
228 IRBuilder builder(&M);
229 auto *V1 = builder.createWeightVar(ElemKind::FloatTy, {320, 200});
230 auto *V2 = builder.createWeightVar(ElemKind::FloatTy, {320, 200});
231 auto *P = builder.createWeightVar(ElemKind::Int64ITy, {320}, "p1");
232
233 // Check that we can construct a new instruction.
234 auto *CC = builder.createCopyInst("C", V1, V2);
235 // Set the predicate.
236 CC->setPredicate(P);
237 InstructionNumbering IN(M);
238 CC->verifyUseList(IN);
239 M.verify();
240 }
241}
242
243/// Note that IRFunction validation uses asserts, so these tests only die when
244/// asserts are turned on.
245#ifndef NDEBUG
246
247/// Check that the verify call dies when verifying an IRFunction with a
248/// non-memory/view Instruction with another non-memory/view Instruction as
249/// an input operand.
250TEST(IR, VerifyDiesOnInvalidInputOperand) {
251 Module mod;
252 Function *F = mod.createFunction("InvalidOperands");
253 IRFunction M(F);
254 {
255 IRBuilder builder(&M);
256 auto *LHS = builder.createWeightVar(ElemKind::FloatTy, {10, 10});
257 auto *RHS = builder.createWeightVar(ElemKind::FloatTy, {10, 10});
258 auto *O = builder.createWeightVar(ElemKind::FloatTy, {10, 10});
259 auto *EAI = builder.createElementAddInst("Add", O, LHS, RHS);
260
261 // Invalid to use a non-memory/view Instruction as input operand.
262 builder.createElementAddInst("Add", O, EAI, RHS);
263
264 EXPECT_DEATH(M.verify(), "");
265 }
266}
267
268/// Check that the verify call dies when verifying an IRFunction with a
269/// non-memory/view Instruction with another non-memory/view Instruction as
270/// an output operand.
271TEST(IR, VerifyDiesOnInvalidOutputOperand) {
272 Module mod;
273 Function *F = mod.createFunction("InvalidOperands");
274 IRFunction M(F);
275 {
276 IRBuilder builder(&M);
277 auto *LHS = builder.createWeightVar(ElemKind::FloatTy, {10, 10});
278 auto *RHS = builder.createWeightVar(ElemKind::FloatTy, {10, 10});
279 auto *O = builder.createWeightVar(ElemKind::FloatTy, {10, 10});
280 auto *EAI = builder.createElementAddInst("Add", O, LHS, RHS);
281
282 // Invalid to use a non-memory/view Instruction as output operand.
283 builder.createElementAddInst("Add", EAI, LHS, RHS);
284
285 EXPECT_DEATH(M.verify(), "");
286 }
287}
288
289#endif /* NDEBUG */
290
291/// Verify that names of Instructions and WeightVars are uniqued when given the
292/// same name.
293TEST(IR, InstUniqueNames) {
294 Module mod;
295 Function *F = mod.createFunction("main");
296 IRFunction M(F);
297 {
298 const std::string name = "name";
299
300 // Add all of the names of the Instructions/WeightVars created to this set
301 // to verify they are unique.
302 llvm::StringSet<> nameSet;
303
304 IRBuilder builder(&M);
305 WeightVar *V1 =
306 builder.createWeightVar(ElemKind::FloatTy, {1, 4, 4, 1}, name);
307 auto it = nameSet.insert(V1->getName());
308 EXPECT_TRUE(it.second);
309
310 WeightVar *V2 = builder.createWeightVar(ElemKind::FloatTy, {4}, name);
311 it = nameSet.insert(V2->getName());
312 EXPECT_TRUE(it.second);
313
314 MaxPoolWithArgmaxInst *MP1 = builder.createMaxPoolWithArgmaxOp(
315 name, V1, {2, 2}, {1, 1}, {0, 2, 1, 3}, NHWC, ElemKind::Int64ITy);
316 it = nameSet.insert(MP1->getName());
317 EXPECT_TRUE(it.second);
318
319 // IRBuilder::createMaxPoolWithArgmaxOp() creates alloc activation insts
320 // internally, so we dealloc them here to keep the instruction list
321 // well-formed.
322 DeallocActivationInst *DAI1 =
323 builder.createDeallocActivationInst(name, MP1->getArgmax());
324 it = nameSet.insert(DAI1->getName());
325 EXPECT_TRUE(it.second);
326
327 DeallocActivationInst *DAI2 =
328 builder.createDeallocActivationInst(name, MP1->getDest());
329 it = nameSet.insert(DAI2->getName());
330 EXPECT_TRUE(it.second);
331
332 // IRBuilder::createTopKOp() creates alloc activation insts internally, so
333 // we dealloc them here to keep the instruction list well-formed.
334 TopKInst *TK = builder.createTopKOp(name, V2, 2, ElemKind::Int64ITy);
335 it = nameSet.insert(TK->getName());
336 EXPECT_TRUE(it.second);
337
338 DeallocActivationInst *DAI3 =
339 builder.createDeallocActivationInst(name, TK->getScratch());
340 it = nameSet.insert(DAI3->getName());
341 EXPECT_TRUE(it.second);
342
343 DeallocActivationInst *DAI4 =
344 builder.createDeallocActivationInst(name, TK->getValues());
345 it = nameSet.insert(DAI4->getName());
346 EXPECT_TRUE(it.second);
347
348 DeallocActivationInst *DAI5 =
349 builder.createDeallocActivationInst(name, TK->getIndices());
350 it = nameSet.insert(DAI5->getName());
351 EXPECT_TRUE(it.second);
352
353 M.verify();
354 }
355}
356
357TEST(IR, getOperandName) {
358 Module mod;
359 Function *F = mod.createFunction("main");
360 IRFunction M(F);
361 {
362 IRBuilder bb(&M);
363
364 auto *input = bb.createWeightVar(ElemKind::FloatTy, {1, 224, 224, 3});
365 auto *res = bb.createAllocActivationInst("sigmoid.res", input->getType());
366 auto *sig = bb.createSigmoidInst("sigmoid", res, input);
367 auto *pool =
368 bb.createAvgPoolOp(sig->getDest(), {7, 7}, {2, 2}, {3, 3, 3, 3}, NHWC,
369 /* countIncludePads */ true);
370
371 EXPECT_EQ(pool->getNumOperands(), 2);
372 EXPECT_EQ(pool->getOperandName(0), "Dest");
373 EXPECT_EQ(pool->getOperandName(1), "Src");
374 }
375}
376
377/// Check that Scratch is allocated properly for instructions.
378TEST(IR, scratchAllocation) {
379 Module mod;
380 Function *F = mod.createFunction("main");
381 IRFunction M(F);
382 {
383 IRBuilder bb(&M);
384 auto *input = bb.createWeightVar(ElemKind::FloatTy, {10});
385 TopKInst *topk = bb.createTopKOp("topk", input, 3, ElemKind::Int64ITy);
386 // Verify scratch is allocated and has correct size.
387 auto *scratch = topk->getScratch();
388 EXPECT_TRUE(isa<AllocActivationInst>(scratch));
389 EXPECT_EQ(scratch->getType()->size(), topk->getScratchSize());
390 }
391}
392