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#ifndef GLOW_TOOLS_NODEGEN_INSTRBUILDER_H
17#define GLOW_TOOLS_NODEGEN_INSTRBUILDER_H
18
19#include "MemberType.h"
20#include "glow/Support/Support.h"
21
22#include <cassert>
23#include <fstream>
24#include <iostream>
25#include <list>
26#include <string>
27#include <unordered_map>
28#include <vector>
29
30class Builder;
31
32enum class OperandKind : unsigned char {
33 In,
34 Out,
35 InOut,
36 /// The 'Scratch' operand kind is similar to the 'Out' operand kind with the
37 /// following distinctions:
38 /// - is intended to be used as a temporary memory buffer for Instructions
39 /// to write some temporary data before writing the final results using
40 /// their 'Out' operands.
41 /// - it is NOT intended to be consumed by other Instructions and hence it is
42 /// deallocated immediately after the Instruction execution.
43 /// - it is only exposed in the Instruction constructor and NOT in the IR
44 /// builder method 'createXInst'. The intention is to have the IR builder
45 /// method manage the scratch allocation/deallocation automatically.
46 /// - when defining a 'Scratch' operand named 'X', the instruction builder
47 /// automatically declares and uses a method 'getXSize()' as part of the
48 /// respective instruction which must be implemented by the developer in
49 /// order for the scratch size requirements (in bytes) to be provided.
50 Scratch,
51};
52
53enum class VerifyKind : unsigned char {
54 SameShape,
55 SameType,
56 SameElementType,
57 TypeCheck,
58 NoVerify,
59};
60
61inline OperandKind negateOperandKind(OperandKind CC) {
62 switch (CC) {
63 case OperandKind::In:
64 return OperandKind::Out;
65 case OperandKind::Out:
66 return OperandKind::In;
67 case OperandKind::InOut:
68 return OperandKind::InOut;
69 case OperandKind::Scratch:
70 return OperandKind::Scratch;
71 }
72 llvm_unreachable("Invalid operand kind.");
73}
74
75inline const char *getOperandKindStr(OperandKind CC) {
76 const char *names[] = {"In", "Out", "InOut", "Out", nullptr};
77 return names[(int)CC];
78}
79
80class InstrBuilder {
81 /// The type-initialization expression.
82 std::string ty_{"nullptr"};
83 /// The instruction name.
84 std::string name_;
85 /// The instruction operands.
86 std::vector<std::pair<std::string, OperandKind>> operands_;
87 /// A list of instruction members. Format: (type, name).
88 std::vector<std::pair<MemberTypeInfo, std::string>> members_;
89 /// Stores the decl and body of a new public method that will be added to the
90 /// class.
91 std::vector<std::pair<std::string, std::string>> extraMethods_;
92 /// A list of list of operands that are declared as 'inplace' operands.
93 /// Each list depicts the 'inplace' operands for one output.
94 /// The output is the first element of the related list.
95 std::list<std::vector<std::string>> inplaceOperands_;
96 /// A list of (VerifyKind, {op1, op2, ...}) pairs. Each pair represents a
97 /// specific kind of verification to apply on the list of operands.
98 std::vector<std::pair<VerifyKind, std::vector<std::string>>>
99 autoVerificationPairs_;
100 /// If autoIRGen is used on this Instr, this is the name of the Node that
101 /// generates to this Instr. If left empty then autoIRGen is not used.
102 std::string autoIRGenNodeName;
103
104 /// Header file stream.
105 std::ofstream &headerStream;
106 /// CPP file stream.
107 std::ofstream &cppStream;
108 /// Def-enum file stream.
109 std::ofstream &defStream;
110 /// The IRBuilder header stream.
111 std::ofstream &builderHeaderStream;
112 /// The IRBuilder CPP stream.
113 std::ofstream &builderCppStream;
114 /// The IRGen stream.
115 std::ofstream &irGenStream;
116
117 /// Specifies if this Instr is backend specific.
118 bool isBackendSpecific_{false};
119
120 /// Specifies if this Instr is data parallel.
121 bool isDataParallel_{false};
122
123 /// \returns the index of the operand with the name \p name. Aborts if no such
124 /// name.
125 unsigned getOperandIndexByName(llvm::StringRef name) const;
126
127public:
128 InstrBuilder(std::ofstream &H, std::ofstream &C, std::ofstream &D,
129 std::ofstream &BH, std::ofstream &BC, std::ofstream &I,
130 const std::string &name, bool isBackendSpecific)
131 : name_(name), headerStream(H), cppStream(C), defStream(D),
132 builderHeaderStream(BH), builderCppStream(BC), irGenStream(I),
133 isBackendSpecific_(isBackendSpecific) {
134 defStream << (isBackendSpecific_ ? "DEF_BACKEND_SPECIFIC_INSTR("
135 : "DEF_INSTR(")
136 << name << "Inst, " << glow::tolower(name) << ")\n";
137 }
138
139 /// Add an operand to the instruction. The name should start with a capital
140 /// letter. For example: "Input".
141 InstrBuilder &addOperand(const std::string &op, OperandKind k) {
142 operands_.push_back({op, k});
143 return *this;
144 }
145
146 /// Add a member to the instruction. Format: type, name.
147 /// The name should start with a capital letter.
148 /// For example: "Filter".
149 InstrBuilder &addMember(const MemberType type, const std::string &name);
150
151 /// Add a member to the node. Format type, name.
152 /// The name should start with a capital letter.
153 /// For example: "Filter".
154 /// If MemberTypeInfo refers to an external user-defined type, this type T
155 /// should satisfy the following requirements:
156 /// * There should be a hash function with a signature like `llvm::hash_code
157 /// hash_value(const T)` which takes T by value, by reference or as a
158 /// pointer, depending on the intended use.
159 /// * There should be a stream output operator with a signature like
160 /// `llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const T);`, which
161 /// takes T by value, by reference or as a pointer, depending on the
162 /// intended use.
163 /// * There should be a comparison operator `bool operator==(const T LHS,
164 /// const T RHS)` (or a custom comparator function mentioned in
165 /// MemberTypeInfo::cmpFn), which takes T by reference or by value depending
166 /// on the intended use.
167 InstrBuilder &addMember(MemberTypeInfo typeInfo, const std::string &name) {
168 members_.push_back({typeInfo, name});
169 return *this;
170 }
171
172 /// Adds the body of a new public method to the class. \p decl is the
173 /// decleration that goes in the header file. \p body is the implementation
174 /// that goes in the cpp file.
175 InstrBuilder &addExtraMethod(const std::string &decl,
176 const std::string &body) {
177 extraMethods_.push_back(std::make_pair(decl, body));
178 return *this;
179 }
180
181 /// Set the expression that initializes the type of the instruction.
182 /// Example: 'LHS->getType()'.
183 InstrBuilder &setType(const std::string &ty) {
184 ty_ = ty;
185 return *this;
186 }
187
188 /// Adds a list of inplace operands. The instruction may use the memory
189 /// read by any of the operands in \p lst[1 .. n] for writing the result of
190 /// the operand \p lst[0].
191 InstrBuilder &inplaceOperand(llvm::ArrayRef<llvm::StringRef> lst) {
192 assert(lst.size() > 1 && "Not enough operands");
193 inplaceOperands_.emplace_back(lst.begin(), lst.end());
194 // Check that the output parameter is described at most once.
195 for (auto it = inplaceOperands_.begin(), end = inplaceOperands_.end();
196 it != end; ++it) {
197 for (auto secondIt = std::next(it); secondIt != end; ++secondIt) {
198 assert(getOperandIndexByName((*it)[0]) !=
199 getOperandIndexByName((*secondIt)[0]) &&
200 "Inplace operands for output appears more than once");
201 }
202 }
203 return *this;
204 }
205
206 /// Constructs a new gradient instruction that is based on the current
207 /// instruction that we are building.
208 void addGradientInstr(llvm::ArrayRef<llvm::StringRef> originalFields,
209 llvm::ArrayRef<llvm::StringRef> gradFields);
210
211 /// Turns on automatic IRGen generation for this instruction given the Node \p
212 /// name (if empty, defaults to same name as Instr).
213 InstrBuilder &autoIRGen(const std::string &name = "") {
214 autoIRGenNodeName = (name.empty() ? name_ : name);
215 return *this;
216 }
217
218 /// Automatically generates verification of type \p verif
219 InstrBuilder &autoVerify(VerifyKind verif,
220 llvm::ArrayRef<std::string> operands = {""}) {
221 if (verif != VerifyKind::NoVerify) {
222 assert(operands.size() > 1 && "Must list 2 or more operands.");
223 }
224 auto newPair = std::make_pair(verif, std::vector<std::string>());
225 newPair.second.insert(newPair.second.begin(), operands.begin(),
226 operands.end());
227 autoVerificationPairs_.push_back(newPair);
228 return *this;
229 }
230
231 InstrBuilder &dataParallel() {
232 isDataParallel_ = true;
233 return *this;
234 }
235
236 /// Helper to add a FusedActivation Member to this instruction.
237 InstrBuilder &addFusedActivation();
238
239 ~InstrBuilder();
240
241private:
242 /// Emit the class constructor.
243 void emitCtor(std::ostream &os) const;
244
245 /// Emit the methods that make the IRBuilder.
246 void emitIRBuilderMethods(std::ostream &osH, std::ostream &osB) const;
247
248 /// Emits the class members (the fields of the class).
249 void emitClassMembers(std::ostream &os) const;
250
251 /// Emits the method that calculates the inplace property.
252 void emitInplaceMethod(std::ostream &os) const;
253
254 /// Emits the property that returns true if the instruction is canonical.
255 void emitCanonicalProperty(std::ostream &os) const;
256
257 /// Emits the property that returns true if the instruction is data parallel.
258 void emitDataParallelProperty(std::ostream &os) const;
259
260 /// Emits the methods that are properties of the instructions.
261 void emitProperties(std::ostream &os) const;
262
263 /// Emit the getter for an operand.
264 void emitOperandGetter(std::ostream &os, const std::string &name,
265 int index) const;
266
267 /// Emit the getter for a accessible class member.
268 void emitMemberGetter(std::ostream &os, const MemberTypeInfo *type,
269 const std::string &name) const;
270
271 /// Emit setters/getters for each accessible class member.
272 void emitSettersGetters(std::ostream &os) const;
273
274 /// Emit the methods that print a textual summary.
275 void emitPrettyPrinter(std::ostream &os) const;
276
277 /// Emit the class definition.
278 void emitClass(std::ostream &os) const;
279
280 /// Emit the clone() method.
281 void emitCloner(std::ostream &os) const;
282
283 /// Emit the getOperandName() method.
284 void emitGetOperandName(std::ostream &os) const;
285
286 /// Emit the methods that go into the CPP file and implement the methods that
287 /// were declared in the header file.
288 void emitCppMethods(std::ostream &os) const;
289
290 /// Adds a case to AutoIRGen for generating this Instr from a Node.
291 void emitAutoIRGen(std::ostream &os) const;
292};
293
294class Builder {
295 std::ofstream &headerStream;
296 std::ofstream &cppStream;
297 std::ofstream &defStream;
298 std::ofstream &builderHeaderStream;
299 std::ofstream &builderCppStream;
300 std::ofstream &irGenStream;
301 // First defined instruction.
302 std::string firstInstr;
303 // Last defined instruction.
304 std::string lastInstr;
305
306public:
307 /// Create a new top-level builder that holds the output streams that
308 /// point to the header file, cpp file and enum definition file, as well as
309 /// the builder and IR Gen files.
310 Builder(std::ofstream &H, std::ofstream &C, std::ofstream &D,
311 std::ofstream &BH, std::ofstream &BC, std::ofstream &I)
312 : headerStream(H), cppStream(C), defStream(D), builderHeaderStream(BH),
313 builderCppStream(BC), irGenStream(I) {
314 cppStream << "#include \"glow/IR/IR.h\"\n"
315 "#include \"glow/IR/Instrs.h\"\n"
316 "#include \"glow/Base/Type.h\"\n"
317 "#include \"glow/Support/Support.h\"\n\n"
318 "using namespace glow;\n";
319 defStream
320 << "#ifndef DEF_INSTR\n#error The macro DEF_INSTR was not declared.\n"
321 "#endif\n"
322 "#ifndef DEF_VALUE\n#error The macro DEF_VALUE was not declared.\n"
323 "#endif\n"
324 "#ifndef DEF_BACKEND_SPECIFIC_INSTR\n#error The macro "
325 "DEF_BACKEND_SPECIFIC_INSTR "
326 "was not declared.\n"
327 "#endif\n"
328 "#ifndef DEF_INSTR_RANGE\n"
329 "#define DEF_INSTR_RANGE(ID, FIRST, LAST)\n"
330 "#endif\n";
331
332 builderCppStream << "#include \"glow/IR/IRBuilder.h\"\n"
333 << "#include \"glow/IR/IR.h\"\n"
334 << "#include \"glow/IR/Instrs.h\"\n\n"
335 << "using namespace glow;\n";
336 }
337
338 ~Builder() {
339 defStream << "DEF_INSTR_RANGE(Instruction, " << firstInstr << "Inst"
340 << ", " << lastInstr << "Inst"
341 << ")\n";
342
343 defStream << "#undef DEF_INSTR_RANGE\n"
344 "#undef DEF_INSTR\n"
345 "#undef DEF_BACKEND_SPECIFIC_INSTR\n"
346 "#undef DEF_VALUE";
347 }
348
349 /// Declare a new instruction and generate code for it.
350 InstrBuilder newInstr(const std::string &name) {
351 if (firstInstr.empty())
352 firstInstr = name;
353 lastInstr = name;
354 const bool isBackendSpecific = false;
355 return InstrBuilder(headerStream, cppStream, defStream, builderHeaderStream,
356 builderCppStream, irGenStream, name, isBackendSpecific);
357 }
358
359 /// Declare a new backend-specific instruction and generate code for it.
360 InstrBuilder newBackendSpecificInstr(const std::string &name) {
361 if (firstInstr.empty())
362 firstInstr = name;
363 lastInstr = name;
364 const bool isBackendSpecific = true;
365 return InstrBuilder(headerStream, cppStream, defStream, builderHeaderStream,
366 builderCppStream, irGenStream, name, isBackendSpecific);
367 }
368
369 /// Declare the instruction in the def file but don't generate code for it.
370 void declareInstr(const std::string &name) {
371 defStream << "DEF_INSTR(" << name << "Inst, " << glow::tolower(name)
372 << ")\n";
373 }
374
375 /// Declare the value in the def file but don't generate code for it.
376 void declareValue(const std::string &name) {
377 defStream << "DEF_VALUE(" << name << ", " << glow::tolower(name) << ")\n";
378 }
379
380 /// Include backend-specific verification at the end of the auto-generated
381 /// Instrs cpp file.
382 void includeBackendSpecificVerification(const std::string &filename) {
383 cppStream << "\n#include \"" << filename << "\"\n";
384 }
385
386 /// Include header into the auto-generated Instrs include file.
387 void includeHeader(const std::string &filename) {
388 headerStream << "\n#include \"" << filename << "\"\n";
389 }
390};
391
392#endif // GLOW_TOOLS_NODEGEN_INSTRBUILDER_H
393