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 | |
30 | class Builder; |
31 | |
32 | enum 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 | |
53 | enum class VerifyKind : unsigned char { |
54 | SameShape, |
55 | SameType, |
56 | SameElementType, |
57 | TypeCheck, |
58 | NoVerify, |
59 | }; |
60 | |
61 | inline 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 | |
75 | inline const char *getOperandKindStr(OperandKind CC) { |
76 | const char *names[] = {"In" , "Out" , "InOut" , "Out" , nullptr}; |
77 | return names[(int)CC]; |
78 | } |
79 | |
80 | class 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>> ; |
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 &; |
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 &; |
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 | |
127 | public: |
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 &(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 | |
241 | private: |
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 | |
294 | class Builder { |
295 | std::ofstream &; |
296 | std::ofstream &cppStream; |
297 | std::ofstream &defStream; |
298 | std::ofstream &; |
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 | |
306 | public: |
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 (const std::string &filename) { |
388 | headerStream << "\n#include \"" << filename << "\"\n" ; |
389 | } |
390 | }; |
391 | |
392 | #endif // GLOW_TOOLS_NODEGEN_INSTRBUILDER_H |
393 | |