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/Backend/Backend.h"
18#include "glow/ExecutionEngine/ExecutionEngine.h"
19#include "glow/Graph/Graph.h"
20#include "glow/Graph/Hook.h"
21#include "glow/Graph/Node.h"
22#include "glow/Graph/Nodes.h"
23#include "glow/Graph/Utils.h"
24
25#include "../../lib/Backends/CPU/CPUBackend.h"
26#include "../../lib/Backends/CPU/CPULLVMIRGen.h"
27#include "glow/IR/Instrs.h"
28
29#include "gtest/gtest.h"
30
31using namespace glow;
32
33//==============================================================
34// Test main classes
35//==============================================================
36
37/// We compile the standard library (libjit) to LLVM bitcode, and then convert
38/// that binary data to an include file using an external utility (include-bin).
39/// The resulting file is included here to compile the bitcode image into our
40/// library.
41static const unsigned char libjit_bc[] = {
42#include "glow/CPU/test_libjit_bc.inc"
43};
44static const size_t libjit_bc_size = sizeof(libjit_bc);
45
46class MockLLVMIRGen : public CPULLVMIRGen {
47
48 // JIT test specific codegen method.
49 bool generateJITTESTIRForInstr(llvm::IRBuilder<> &builder,
50 const glow::Instruction *I) {
51 if (I->getKind() == Kinded::Kind::ElementLogInstKind) {
52 auto *AN = llvm::cast<ElementLogInst>(I);
53 auto *src = AN->getSrc();
54 auto *dest = AN->getDest();
55 auto *srcPtr = emitValueAddress(builder, src);
56 auto *destPtr = emitValueAddress(builder, dest);
57 auto *F = getFunction("JITTestDispatch_" + AN->getName().str());
58 createCall(builder, F, {srcPtr, destPtr});
59 return true;
60 }
61
62 return false;
63 }
64
65 virtual void generateLLVMIRForInstr(llvm::IRBuilder<> &builder,
66 const glow::Instruction *I) override {
67 // Try to generate test version of instruction.
68 if (generateJITTESTIRForInstr(builder, I)) {
69 return;
70 }
71
72 // Fall back to LLVMIRGen.
73 LLVMIRGen::generateLLVMIRForInstr(builder, I);
74 }
75
76 bool
77 canBePartOfDataParallelKernel(const glow::Instruction *I) const override {
78 return false;
79 }
80
81public:
82 MockLLVMIRGen(const IRFunction *F, AllocationsInfo &allocationsInfo,
83 std::string mainEntryName, llvm::StringRef libjitBC)
84 : CPULLVMIRGen(F, allocationsInfo, mainEntryName, libjitBC) {}
85};
86
87class MockCPUBackend : public CPUBackend {
88 virtual std::unique_ptr<LLVMIRGen>
89 createIRGen(const IRFunction *IR,
90 AllocationsInfo &allocationsInfo) const override {
91 MockLLVMIRGen *irgen =
92 new MockLLVMIRGen(IR, allocationsInfo, "", getLibjitBitcode());
93 return std::unique_ptr<MockLLVMIRGen>(irgen);
94 }
95 virtual std::string getBackendName() const override {
96 return "MockCPUBackend";
97 }
98
99public:
100 virtual llvm::StringRef getLibjitBitcode() const override {
101 return llvm::StringRef(reinterpret_cast<const char *>(libjit_bc),
102 libjit_bc_size);
103 }
104 static std::string getName() { return "MockCPUBackend"; }
105};
106
107REGISTER_GLOW_BACKEND_FACTORY(MockCPUFactory, MockCPUBackend);
108
109//==============================================================
110// Actual tests
111//
112// For JIT tests, we take the following approach:
113// - override the code generation for a particular node (log)
114// - the log input value determines the index of libjit function
115// that must be called
116// - in case there are later change in the Glow that may result
117// in a IR/code generation change, we check that the expected
118// JIT function was called by checking that the log output
119// value is JIT_MAGIC_VALUE + <log input>
120//==============================================================
121
122#define JIT_MAGIC_VALUE 555
123#ifndef WIN32
124// Test 0: test that the libjit code can use global C++ objects.
125TEST(CPUJITTest, testCppConstructors) {
126 glow::ExecutionEngine EE("MockCPUBackend");
127 Module &M = EE.getModule();
128 Function *F = M.createFunction("F");
129
130 // Create a simple graph.
131 auto *inputPH = M.createPlaceholder(ElemKind::FloatTy, {1}, "input", false);
132 Node *addNode = F->createLog("testCppConstructors", inputPH);
133 auto *saveNode = F->createSave("output", addNode);
134
135 PlaceholderBindings bindings;
136 auto *inputT = bindings.allocate(inputPH);
137 inputT->getHandle().clear(0);
138 Tensor expectedT(inputT->getType());
139 expectedT.getHandle().clear(JIT_MAGIC_VALUE + 0);
140 auto *outputT = bindings.allocate(saveNode->getPlaceholder());
141 EE.compile(CompilationMode::Infer);
142 EE.run(bindings);
143 EXPECT_TRUE(outputT->isEqual(expectedT));
144}
145#endif // ! WIN32
146
147#ifndef WIN32
148// Test 0: test that the libjit code can use global C++ objects.
149TEST(CPUJITTest, testWeakSym) {
150 glow::ExecutionEngine EE("MockCPUBackend");
151 Module &M = EE.getModule();
152 Function *F = M.createFunction("F");
153
154 // Create a simple graph.
155 auto *inputPH = M.createPlaceholder(ElemKind::FloatTy, {1}, "input", false);
156 Node *addNode = F->createLog("testWeakSym", inputPH);
157 auto *saveNode = F->createSave("output", addNode);
158
159 const float testVal = 1.23;
160
161 PlaceholderBindings bindings;
162 auto *inputT = bindings.allocate(inputPH);
163 inputT->getHandle().clear(testVal);
164 Tensor expectedT(inputT->getType());
165 expectedT.getHandle().clear(-testVal);
166 auto *outputT = bindings.allocate(saveNode->getPlaceholder());
167 EE.compile(CompilationMode::Infer);
168 EE.run(bindings);
169 EXPECT_TRUE(outputT->isEqual(expectedT));
170}
171#endif // ! WIN32
172