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/IR/IR.h" |
18 | #include "glow/Graph/FXIRWrapper.h" |
19 | #include "glow/Graph/Graph.h" |
20 | #include "glow/IR/IRUtils.h" |
21 | #include "glow/IR/Instrs.h" |
22 | #include "glow/Support/Support.h" |
23 | |
24 | #include "llvm/ADT/StringSet.h" |
25 | #include "llvm/Support/Casting.h" |
26 | #include "llvm/Support/FileSystem.h" |
27 | #include "llvm/Support/raw_ostream.h" |
28 | |
29 | #include <fstream> |
30 | #include <iostream> |
31 | #include <sstream> |
32 | #include <unordered_set> |
33 | |
34 | using namespace glow; |
35 | using llvm::cast; |
36 | using llvm::dyn_cast; |
37 | using llvm::isa; |
38 | |
39 | //===----------------------------------------------------------------------===// |
40 | // General IR operations |
41 | //===----------------------------------------------------------------------===// |
42 | |
43 | bool Instruction::classof(const Value *V) { |
44 | #define DEF_VALUE(CLASS, NAME) |
45 | #define DEF_INSTR(CLASS, NAME) |
46 | #define DEF_BACKEND_SPECIFIC_INSTR(CLASS, NAME) |
47 | #define DEF_INSTR_RANGE(CLASS, FIRST, LAST) \ |
48 | constexpr auto First_##CLASS = Kinded::Kind::FIRST##Kind; \ |
49 | constexpr auto Last_##CLASS = Kinded::Kind::LAST##Kind; |
50 | #include "glow/AutoGenInstr.def" |
51 | return V->getKind() >= First_Instruction && V->getKind() <= Last_Instruction; |
52 | } |
53 | |
54 | void Use::setOperand(Value *other) { use_->setOperand(idx_, other); } |
55 | |
56 | InstructionOperand Use::getOperand() { return use_->getOperand(idx_); } |
57 | |
58 | ConstInstructionOperand Use::getOperand() const { |
59 | return use_->getOperand(idx_); |
60 | } |
61 | |
62 | Value *Instruction::getPredicate() const { |
63 | assert(hasPredicate() && "No predicate is set" ); |
64 | return getOperand(predicateIndex_).first; |
65 | } |
66 | |
67 | void Instruction::setPredicate(Value *p) { |
68 | // Push a new predicate. |
69 | if (!hasPredicate()) { |
70 | predicateIndex_ = getNumOperands(); |
71 | pushOperand({p, OperandKind::In}); |
72 | } |
73 | |
74 | setOperand(predicateIndex_, p); |
75 | } |
76 | |
77 | bool Instruction::hasPredicate() const { return predicateIndex_ > 0; } |
78 | |
79 | void Instruction::pushOperand(Operand op) { |
80 | ops_.emplace_back(nullptr, op.second); |
81 | setOperand(ops_.size() - 1, op.first); |
82 | } |
83 | |
84 | void Instruction::setOperand(unsigned idx, Value *v) { |
85 | auto *currVal = ops_[idx].first; |
86 | |
87 | if (currVal == v) { |
88 | return; |
89 | } |
90 | |
91 | if (currVal) { |
92 | currVal->removeUse(Use(idx, this)); |
93 | } |
94 | |
95 | if (v) { |
96 | ops_[idx].first = v; |
97 | v->addUse(Use(idx, this)); |
98 | } |
99 | } |
100 | |
101 | Instruction::Operand Instruction::getOperand(unsigned idx) const { |
102 | assert(ops_.size() > idx && "Invalid operand" ); |
103 | return ops_[idx]; |
104 | } |
105 | |
106 | unsigned Instruction::getNumInputs() const { |
107 | unsigned numInputs = 0; |
108 | for (const auto &op : ops_) { |
109 | if (op.second != OperandKind::Out) { |
110 | numInputs++; |
111 | } |
112 | } |
113 | return numInputs; |
114 | } |
115 | |
116 | unsigned Instruction::getNumOutputs() const { |
117 | unsigned numOutputs = 0; |
118 | for (const auto &op : ops_) { |
119 | if (op.second != OperandKind::In) { |
120 | numOutputs++; |
121 | } |
122 | } |
123 | return numOutputs; |
124 | } |
125 | |
126 | llvm::StringRef Instruction::getOperandName(unsigned idx) const { |
127 | switch (getKind()) { |
128 | #define DEF_INSTR(CLASS, NAME) \ |
129 | case glow::Kinded::Kind::CLASS##Kind: \ |
130 | return static_cast<const CLASS *>(this)->getOperandName(idx); |
131 | #define DEF_BACKEND_SPECIFIC_INSTR(CLASS, NAME) DEF_INSTR(CLASS, NAME) |
132 | #define DEF_VALUE(CLASS, NAME) |
133 | #include "glow/AutoGenInstr.def" |
134 | default: |
135 | llvm_unreachable("Unhandled instruction" ); |
136 | } |
137 | } |
138 | |
139 | void Instruction::eraseFromParent() { getParent()->eraseInstruction(this); } |
140 | |
141 | bool Instruction::verifyUseList( |
142 | const InstructionNumbering &InstrNumbering) const { |
143 | for (const auto &op : ops_) { |
144 | auto *v = op.first; |
145 | (void)v; |
146 | assert(v && "Instruction operand must be a real value" ); |
147 | assert(v->hasUser(this) && "Invalid use-list" ); |
148 | v->verifyUseList(InstrNumbering); |
149 | } |
150 | return true; |
151 | } |
152 | |
153 | bool Instruction::verify() const { |
154 | // All operands of instructions must be either memory (AllocActivation or |
155 | // WeightVar), or a TensorView of such. |
156 | for (const auto &opPair : getOperands()) { |
157 | const Value *op = opPair.first; |
158 | (void)op; |
159 | assert((isa<AllocActivationInst>(op) || isa<WeightVar>(op) || |
160 | isa<TensorViewInst>(op)) && |
161 | "Operands must be one of {AllocActivation, WeightVar, TensorView}" ); |
162 | } |
163 | |
164 | // Perform Instruction-specific verification. |
165 | #define DEF_INSTR(CLASS, NAME) \ |
166 | if (auto *X = dyn_cast<const CLASS>(this)) \ |
167 | X->verify(); |
168 | #define DEF_BACKEND_SPECIFIC_INSTR(CLASS, NAME) DEF_INSTR(CLASS, NAME) |
169 | #define DEF_VALUE(CLASS, NAME) |
170 | #include "glow/AutoGenInstr.def" |
171 | return true; |
172 | } |
173 | |
174 | bool Value::verify(const IRFunction &M) const { return true; } |
175 | |
176 | bool Value::verifyUseList(const InstructionNumbering &InstrNumbering) const { |
177 | auto users = getUsers(); |
178 | for (const auto &use : users) { |
179 | auto *I = use.get(); |
180 | (void)I; |
181 | // Every instruction using this value should be in the instruction list. |
182 | assert(InstrNumbering.getInstrNumber(I) != -1); |
183 | // All uses must come after defs. |
184 | assert(!isa<Instruction>(this) || |
185 | InstrNumbering.getInstrNumber(I) > |
186 | InstrNumbering.getInstrNumber(cast<Instruction>(this))); |
187 | } |
188 | return true; |
189 | } |
190 | |
191 | void Instruction::destroyInstruction(Instruction *I) { |
192 | switch (I->getKind()) { |
193 | default: |
194 | llvm_unreachable("Unknown value kind" ); |
195 | break; |
196 | #define DEF_INSTR(CLASS, NAME) \ |
197 | case Kinded::Kind::CLASS##Kind: { \ |
198 | delete llvm::cast<CLASS>(I); \ |
199 | break; \ |
200 | } |
201 | #define DEF_BACKEND_SPECIFIC_INSTR(CLASS, NAME) DEF_INSTR(CLASS, NAME) |
202 | #define DEF_VALUE(CLASS, NAME) |
203 | #include "glow/AutoGenInstr.def" |
204 | } |
205 | } |
206 | |
207 | void IRFunction::eraseInstruction(glow::Instruction *I) { |
208 | assert(I->getParent() == this && |
209 | "Cannot erase an instruction not belonging to a function" ); |
210 | instrs_.erase(I); |
211 | } |
212 | |
213 | InstrIterator IRFunction::removeInstruction(glow::Instruction *I) { |
214 | assert(I->getParent() == this && |
215 | "Cannot erase an instruction not beloning to a function" ); |
216 | auto result = I->getIterator(); |
217 | ++result; |
218 | instrs_.remove(I); |
219 | return result; |
220 | } |
221 | |
222 | void IRFunction::insertInstruction(glow::Instruction *I) { |
223 | instrs_.push_back(I); |
224 | } |
225 | |
226 | InstrIterator IRFunction::insertInstruction(glow::Instruction *where, |
227 | glow::Instruction *I) { |
228 | return instrs_.insert(where->getIterator(), I); |
229 | } |
230 | |
231 | InstrIterator IRFunction::moveInstruction(Instruction *where, |
232 | glow::Instruction *I) { |
233 | I->getParent()->removeInstruction(I); |
234 | return insertInstruction(where, I); |
235 | } |
236 | |
237 | IRFunction::~IRFunction() { clear(); } |
238 | |
239 | void IRFunction::clear() { |
240 | // Remove the mapping between the graph nodes and the IR that we are deleting. |
241 | variableMap_.clear(); |
242 | |
243 | // Delete all of the instructions, in reverse order, to make sure that |
244 | // we delete the users before the instructions. |
245 | for (auto it = instrs_.rbegin(), e = instrs_.rend(); it != e;) { |
246 | auto *curI = &*it; |
247 | ++it; |
248 | Instruction::destroyInstruction(curI); |
249 | } |
250 | |
251 | // Delete all of the weights. |
252 | for (auto &I : weights_) { |
253 | delete I; |
254 | } |
255 | // TaggedList's destructor is going to destroy the InstList. |
256 | instrs_.clearAndLeakNodesUnsafely(); |
257 | weights_.clear(); |
258 | |
259 | G_ = nullptr; |
260 | } |
261 | |
262 | static void LLVM_ATTRIBUTE_UNUSED verifyOperandsAccess(const Instruction *I) { |
263 | if (llvm::isa<CopyInst>(I)) |
264 | return; |
265 | for (size_t opIdx = 0, e = I->getNumOperands(); opIdx < e; ++opIdx) { |
266 | auto op = I->getOperand(opIdx); |
267 | auto opKind = op.second; |
268 | auto opValue = op.first; |
269 | // Check that an instruction never tries to update a constant argument. |
270 | if (opKind != OperandKind::In) { |
271 | if (auto *W = llvm::dyn_cast<WeightVar>(opValue)) { |
272 | assert(W->getMutability() != WeightVar::MutabilityKind::Constant && |
273 | "Constant weights cannot be updated" ); |
274 | (void)W; |
275 | } |
276 | } |
277 | // If the same operand is used multiple times by an instruction, |
278 | // check that it is a valid access pattern. |
279 | for (size_t nextOpIdx = opIdx + 1; nextOpIdx < e; ++nextOpIdx) { |
280 | auto nextOp = I->getOperand(nextOpIdx); |
281 | auto nextOpKind = nextOp.second; |
282 | auto nextOpValue = nextOp.first; |
283 | // Bail if it is a different value. |
284 | if (opValue != nextOpValue) |
285 | continue; |
286 | // It is OK to write into the same buffer if the instruction permits such |
287 | // an inplace update. |
288 | if (opKind == OperandKind::In && nextOpKind != OperandKind::In && |
289 | Instruction::isInplaceOp(I, nextOpIdx, opIdx)) |
290 | continue; |
291 | if (opKind != OperandKind::In && nextOpKind == OperandKind::In && |
292 | Instruction::isInplaceOp(I, opIdx, nextOpIdx)) |
293 | continue; |
294 | // If an operand is used as @out or @inout it cannot be used |
295 | // for anything else. |
296 | // It is OK to use the same operand as input multiple times. |
297 | assert(opKind == OperandKind::In && nextOpKind == OperandKind::In && |
298 | "Conflicting uses of the same operand by the same instruction" ); |
299 | } |
300 | } |
301 | } |
302 | |
303 | /// Verify that liveness constraints are satisfied. |
304 | /// There should be no uses of an allocation after |
305 | /// it was deallocated or before it is allocated. |
306 | static void verifyLiveness(const IRFunction &M) { |
307 | // The live set stores allocations that are known to be live. |
308 | std::unordered_map<const Value *, bool> liveBuffers; |
309 | for (const auto &I : M.getInstrs()) { |
310 | if (auto *AI = dyn_cast<AllocActivationInst>(&I)) { |
311 | assert(liveBuffers.find(AI) == liveBuffers.end() && |
312 | "Redefinition of an existing allocation" ); |
313 | liveBuffers.insert({AI, false}); |
314 | continue; |
315 | } |
316 | if (auto *DI = dyn_cast<DeallocActivationInst>(&I)) { |
317 | assert(llvm::isa<AllocActivationInst>(DI->getSrc()) && |
318 | "Only allocations can be deallocated" ); |
319 | assert(liveBuffers.find(DI->getSrc()) != liveBuffers.end() && |
320 | "Deallocation of an allocation that is not alive" ); |
321 | liveBuffers.erase(DI->getSrc()); |
322 | continue; |
323 | } |
324 | // Do not consider tensorview definitions to be real uses of any |
325 | // allocations. |
326 | if (llvm::isa<TensorViewInst>(&I)) |
327 | continue; |
328 | |
329 | for (const auto &Op : I.getOperands()) { |
330 | if (auto *AI = dyn_cast<AllocActivationInst>(getOrigin(Op.first))) { |
331 | auto entry = liveBuffers.find(AI); |
332 | assert(entry != liveBuffers.end() && |
333 | "Allocation should be alive when it is used" ); |
334 | assert((Op.second == OperandKind::Out || entry->second) && |
335 | "@in and @inout operands should be initialized before their " |
336 | "first use" ); |
337 | // Remember that an allocation was initialized. |
338 | if (Op.second != OperandKind::In) |
339 | entry->second = true; |
340 | } |
341 | } |
342 | } |
343 | } |
344 | |
345 | bool IRFunction::verify() const { |
346 | InstructionNumbering InstrNumbering(*this); |
347 | assert(!instrs_.empty() && "Instruction list is empty!" ); |
348 | llvm::StringSet<> nameSet; |
349 | for (const auto &I : instrs_) { |
350 | I.verifyUseList(InstrNumbering); |
351 | verifyOperandsAccess(&I); |
352 | I.verify(); |
353 | auto it = nameSet.insert(I.getName()); |
354 | (void)it; |
355 | assert(it.second && "All Instruction and WeightVar names must be unique." ); |
356 | } |
357 | |
358 | verifyLiveness(*this); |
359 | |
360 | for (auto p : variableMap_) { |
361 | (void)p; |
362 | assert(p.first->getType() == p.second->getType() && |
363 | "Weight and variable must have the same type" ); |
364 | p.second->verify(*this); |
365 | p.second->verifyUseList(InstrNumbering); |
366 | auto it = nameSet.insert(p.second->getName()); |
367 | (void)it; |
368 | assert(it.second && "All Instruction and WeightVar names must be unique." ); |
369 | } |
370 | return true; |
371 | } |
372 | |
373 | Value *IRFunction::getWeightForNode(const Storage *V) const { |
374 | auto it = variableMap_.find(V); |
375 | if (it == variableMap_.end()) { |
376 | return nullptr; |
377 | } |
378 | |
379 | return it->second; |
380 | } |
381 | |
382 | bool Instruction::isCanonical() const { |
383 | switch (getKind()) { |
384 | default: |
385 | llvm_unreachable("Unknown value kind" ); |
386 | break; |
387 | #define DEF_INSTR(CLASS, NAME) \ |
388 | case Kinded::Kind::CLASS##Kind: { \ |
389 | auto *X = llvm::cast<const CLASS>(this); \ |
390 | return X->isCanonical(); \ |
391 | break; \ |
392 | } |
393 | #define DEF_BACKEND_SPECIFIC_INSTR(CLASS, NAME) DEF_INSTR(CLASS, NAME) |
394 | #define DEF_VALUE(CLASS, NAME) |
395 | #include "glow/AutoGenInstr.def" |
396 | } |
397 | return false; |
398 | } |
399 | |
400 | bool Instruction::isDataParallel() const { |
401 | switch (getKind()) { |
402 | default: |
403 | llvm_unreachable("Unknown value kind" ); |
404 | break; |
405 | #define DEF_INSTR(CLASS, NAME) \ |
406 | case Kinded::Kind::CLASS##Kind: { \ |
407 | auto *X = llvm::cast<const CLASS>(this); \ |
408 | return X->isDataParallel(); \ |
409 | break; \ |
410 | } |
411 | #define DEF_BACKEND_SPECIFIC_INSTR(CLASS, NAME) DEF_INSTR(CLASS, NAME) |
412 | #define DEF_VALUE(CLASS, NAME) |
413 | #include "glow/AutoGenInstr.def" |
414 | } |
415 | return false; |
416 | } |
417 | |
418 | Instruction *Instruction::clone() const { |
419 | switch (getKind()) { |
420 | default: |
421 | llvm_unreachable("Unknown value kind" ); |
422 | break; |
423 | #define DEF_INSTR(CLASS, NAME) \ |
424 | case Kinded::Kind::CLASS##Kind: { \ |
425 | auto *X = llvm::cast<const CLASS>(this); \ |
426 | return X->clone(); \ |
427 | break; \ |
428 | } |
429 | #define DEF_BACKEND_SPECIFIC_INSTR(CLASS, NAME) DEF_INSTR(CLASS, NAME) |
430 | #define DEF_VALUE(CLASS, NAME) |
431 | #include "glow/AutoGenInstr.def" |
432 | } |
433 | return nullptr; |
434 | } |
435 | |
436 | //===----------------------------------------------------------------------===// |
437 | // Instruction numbering |
438 | //===----------------------------------------------------------------------===// |
439 | |
440 | InstructionNumbering::InstructionNumbering(const IRFunction &M) { |
441 | auto &instrs = M.getInstrs(); |
442 | size_t instIdx = 0; |
443 | for (const auto &I : instrs) { |
444 | numToInstr_.push_back(&I); |
445 | instrToNum_[&I] = instIdx; |
446 | ++instIdx; |
447 | } |
448 | } |
449 | |
450 | int64_t InstructionNumbering::getInstrNumber(const Instruction *I) const { |
451 | auto Result = instrToNum_.find(I); |
452 | if (Result == instrToNum_.end()) |
453 | return -1; |
454 | return (int64_t)Result->second; |
455 | } |
456 | |
457 | const Instruction *InstructionNumbering::getInstr(size_t instrNumber) const { |
458 | assert(instrNumber < numToInstr_.size()); |
459 | return numToInstr_[instrNumber]; |
460 | } |
461 | |
462 | //===----------------------------------------------------------------------===// |
463 | // IR printing and visualizing |
464 | //===----------------------------------------------------------------------===// |
465 | |
466 | static void dumpIR(const Value *V, llvm::raw_ostream &out) { |
467 | switch (V->getKind()) { |
468 | default: |
469 | llvm_unreachable("Unknown value kind" ); |
470 | break; |
471 | #define DEF_INSTR(CLASS, NAME) \ |
472 | case Kinded::Kind::CLASS##Kind: { \ |
473 | auto *X = llvm::cast<const CLASS>(V); \ |
474 | X->dump(out); \ |
475 | break; \ |
476 | } |
477 | #define DEF_BACKEND_SPECIFIC_INSTR(CLASS, NAME) DEF_INSTR(CLASS, NAME) |
478 | #define DEF_VALUE(CLASS, NAME) DEF_INSTR(CLASS, NAME) |
479 | #include "glow/AutoGenInstr.def" |
480 | } |
481 | } |
482 | |
483 | static void dumpIRInContext(const Value *V, llvm::raw_ostream &out) { |
484 | // Dump all operands. |
485 | if (const auto *I = dyn_cast<const Instruction>(V)) { |
486 | if (I->getNumOperands() > 0) |
487 | out << "Operands:\n" ; |
488 | for (const auto &Op : I->getOperands()) { |
489 | out << "\t" ; |
490 | Op.first->dump(out); |
491 | out << "\n" ; |
492 | } |
493 | } |
494 | out << "-> " ; |
495 | |
496 | dumpIR(V, out); |
497 | |
498 | // Dump all uses. |
499 | out << "\n" ; |
500 | if (V->getNumUsers() > 0) |
501 | out << "Users:\n" ; |
502 | for (const Use &U : V->getUsers()) { |
503 | out << "\t" ; |
504 | U.get()->dump(out); |
505 | out << "\n" ; |
506 | } |
507 | } |
508 | |
509 | std::vector<const Constant *> IRFunction::findConstants() const { |
510 | std::vector<const Constant *> constants; |
511 | for (auto &node : variableMap_) { |
512 | if (const Constant *constant = dyn_cast<const Constant>(node.first)) { |
513 | constants.push_back(constant); |
514 | } |
515 | } |
516 | return constants; |
517 | } |
518 | |
519 | std::vector<const Placeholder *> IRFunction::findPlaceholders() const { |
520 | std::vector<const Placeholder *> placeholders; |
521 | for (auto &node : variableMap_) { |
522 | if (const Placeholder *PH = dyn_cast<const Placeholder>(node.first)) { |
523 | placeholders.push_back(PH); |
524 | } |
525 | } |
526 | return placeholders; |
527 | } |
528 | |
529 | /// Dump the instruction numbers of all users of \p V. |
530 | static void dumpUsers(const Value *V, llvm::raw_ostream &out, |
531 | InstructionNumbering &InstrNumbering) { |
532 | if (V->getNumUsers() == 0) |
533 | return; |
534 | out << " // Users: " ; |
535 | bool isFirst = true; |
536 | for (auto U = V->getUsers().rbegin(), E = V->getUsers().rend(); U != E; ++U) { |
537 | auto *I = U->get(); |
538 | if (!isFirst) { |
539 | out << ", " ; |
540 | } |
541 | |
542 | out << getOperandKindStr(U->getOperand().second) << " " ; |
543 | |
544 | auto instrNum = InstrNumbering.getInstrNumber(I); |
545 | assert(instrNum >= 0); |
546 | out << instrNum; |
547 | isFirst = false; |
548 | } |
549 | } |
550 | |
551 | void Value::dump(llvm::raw_ostream &out) const { dumpIR(this, out); } |
552 | |
553 | void Value::dump() const { dumpIR(this, llvm::outs()); } |
554 | |
555 | std::string Value::toString() const { |
556 | std::string storage; |
557 | llvm::raw_string_ostream os(storage); |
558 | dump(os); |
559 | return os.str(); |
560 | } |
561 | |
562 | void Value::dumpInContext(llvm::raw_ostream &out) const { |
563 | dumpIRInContext(this, out); |
564 | } |
565 | |
566 | void Value::dumpInContext() const { dumpInContext(llvm::outs()); } |
567 | |
568 | bool Instruction::isInplaceOp(const Instruction *I, unsigned dstIdx, |
569 | unsigned srcIdx) { |
570 | #define DEF_INSTR(CLASS, NAME) \ |
571 | if (const auto *X = dyn_cast<const CLASS>(I)) \ |
572 | return X->isInplaceOp(dstIdx, srcIdx); |
573 | #define DEF_BACKEND_SPECIFIC_INSTR(CLASS, NAME) DEF_INSTR(CLASS, NAME) |
574 | #define DEF_VALUE(CLASS, NAME) |
575 | #include "glow/AutoGenInstr.def" |
576 | |
577 | llvm_unreachable("Invalid instruction kind." ); |
578 | } |
579 | |
580 | void Instruction::dumpOperands(llvm::raw_ostream &os) const { |
581 | // Dump the predicate of the instruction: |
582 | if (hasPredicate()) { |
583 | Value *pred = getPredicate(); |
584 | os << "[ pred: " << pred->getName() << " ] " ; |
585 | } |
586 | |
587 | // Dump the operands of the instruction: |
588 | for (size_t i = 0, e = getNumOperands(); i < e; i++) { |
589 | auto op = getOperand(i); |
590 | auto cc = getOperandKindStr(op.second); |
591 | if (i) { |
592 | os << ", " ; |
593 | } |
594 | os << cc << " %" << op.first->getName().str(); |
595 | } |
596 | } |
597 | |
598 | IRFunction::IRFunction(IRContainer *G) |
599 | : IRContainer(G ? G->getName() : llvm::StringRef{}), G_(G) {} |
600 | |
601 | #if FACEBOOK_INTERNAL |
602 | /// \returns a reference to the glow FX graph. |
603 | FXIRWrapper *IRFunction::getFXGraph() { |
604 | assert(llvm::isa<FXIRWrapper>(G_)); |
605 | return llvm::cast<FXIRWrapper>(G_); |
606 | } |
607 | |
608 | /// \returns a reference to the glow FX graph. |
609 | const FXIRWrapper *IRFunction::getFXGraph() const { |
610 | assert(llvm::isa<FXIRWrapper>(G_)); |
611 | return llvm::cast<FXIRWrapper>(G_); |
612 | } |
613 | #endif |
614 | |
615 | static bool hasResultValue(const Instruction *I) { |
616 | return I->getKind() == Instruction::Kind::AllocActivationInstKind || |
617 | I->getKind() == Instruction::Kind::TensorViewInstKind; |
618 | } |
619 | |
620 | void IRFunction::dump() const { dump(llvm::outs()); } |
621 | |
622 | void IRFunction::dump(llvm::raw_ostream &OS) const { |
623 | InstructionNumbering InstrNumbering(*this); |
624 | // Print all of the variables: |
625 | std::string s; |
626 | llvm::raw_string_ostream sb{s}; |
627 | sb << "function " << getName().str() << "\n" ; |
628 | |
629 | size_t sizeInBytes = 0; |
630 | sb << "declare {\n" ; |
631 | for (auto it : weights_) { |
632 | Value *V = it; |
633 | sb << " " ; |
634 | dumpIR(V, sb); |
635 | sb << " // size: " << V->getSizeInBytes(); |
636 | dumpUsers(V, sb, InstrNumbering); |
637 | sb << "\n" ; |
638 | |
639 | auto *T = V->getType(); |
640 | sizeInBytes += T->getElementSize() * T->size(); |
641 | } |
642 | |
643 | sb << "\n ; size = " << sizeInBytes << " bytes\n" ; |
644 | |
645 | sb << "}\n\n" ; |
646 | sb << "code {\n" ; |
647 | |
648 | // Print all of the instructions: |
649 | for (const auto &I : instrs_) { |
650 | sb << " " ; |
651 | auto InstrNum = InstrNumbering.getInstrNumber(&I); |
652 | assert(InstrNum >= 0); |
653 | sb << InstrNum << " " ; |
654 | dumpIR(&I, sb); |
655 | if (isa<AllocActivationInst>(&I)) |
656 | sb << " // size: " << I.getSizeInBytes(); |
657 | if (isa<DeallocActivationInst>(&I)) { |
658 | sb << " // size: " |
659 | << cast<DeallocActivationInst>(&I) |
660 | ->getSrc() |
661 | ->getType() |
662 | ->getSizeInBytes(); |
663 | } |
664 | if (hasResultValue(&I)) |
665 | dumpUsers(&I, sb, InstrNumbering); |
666 | sb << "\n" ; |
667 | } |
668 | |
669 | sb << "}\n" ; |
670 | |
671 | OS << sb.str(); |
672 | OS.flush(); |
673 | } |
674 | |
675 | std::string IRFunction::toString() const { |
676 | std::string storage; |
677 | llvm::raw_string_ostream os(storage); |
678 | dump(os); |
679 | return os.str(); |
680 | } |
681 | |
682 | IRFunction * |
683 | IRFunction::clone(llvm::StringRef newName, |
684 | llvm::DenseMap<const Value *, Value *> *map, |
685 | llvm::DenseMap<const Value *, Value *> *currToNewMap) { |
686 | // Create a new function. |
687 | auto *newF = new IRFunction(getRawGraph()); |
688 | newF->setName(newName); |
689 | return clone(newF, map, currToNewMap); |
690 | } |
691 | |
692 | IRFunction * |
693 | IRFunction::clone(IRFunction *newF, llvm::DenseMap<const Value *, Value *> *map, |
694 | llvm::DenseMap<const Value *, Value *> *currToNewMap) const { |
695 | |
696 | llvm::DenseMap<const Value *, Value *> currToNew; |
697 | // Initialize the map from a user-provided map. |
698 | if (currToNewMap) { |
699 | currToNew.insert(currToNewMap->begin(), currToNewMap->end()); |
700 | } |
701 | // Clone weights. |
702 | for (auto &w : getWeights()) { |
703 | auto cloneWeight = |
704 | new WeightVar(w->getName(), w->getType(), w->getMutability()); |
705 | newF->getWeights().emplace_back(cloneWeight); |
706 | currToNew[w] = cloneWeight; |
707 | } |
708 | // Clone the variable map. |
709 | for (auto &v : getVariableMap()) { |
710 | assert(v.second && "The value should have been cloned already" ); |
711 | newF->getVariableMap()[v.first] = currToNew[v.second]; |
712 | } |
713 | // Clone instructions. |
714 | for (const auto &I : getInstrs()) { |
715 | auto *cloneI = I.clone(); |
716 | currToNew[&I] = cloneI; |
717 | // Remap all operands. |
718 | for (unsigned idx = 0, e = cloneI->getNumOperands(); idx < e; ++idx) { |
719 | cloneI->setOperand(idx, currToNew[cloneI->getOperand(idx).first]); |
720 | } |
721 | newF->insertInstruction(cloneI); |
722 | } |
723 | // Record the node mapping into the external map. |
724 | if (map) { |
725 | assert(map->empty() && "The external map must be empty" ); |
726 | for (auto it : currToNew) { |
727 | map->insert(it); |
728 | } |
729 | } |
730 | assert(newF->getInstrs().size() == getInstrs().size() && "Invalid func size" ); |
731 | assert(newF->getWeights().size() == getWeights().size() && |
732 | "Invalid func size" ); |
733 | newF->verify(); |
734 | return newF; |
735 | } |
736 | |
737 | //===----------------------------------------------------------------------===// |
738 | // InstructionTraits<Instruction> Implementation |
739 | //===----------------------------------------------------------------------===// |
740 | |
741 | // The trait object is embedded into a IRFunction. Use dirty hacks to |
742 | // reconstruct the IRFunction from the 'self' pointer of the trait. |
743 | IRFunction *InstructionTraits::getContainingFunction() { |
744 | size_t Offset( |
745 | size_t(&((IRFunction *)nullptr->*IRFunction::getInstrsMemberPtr()))); |
746 | IRFunction::InstListTy *Anchor(static_cast<IRFunction::InstListTy *>(this)); |
747 | return reinterpret_cast<IRFunction *>(reinterpret_cast<char *>(Anchor) - |
748 | Offset); |
749 | } |
750 | |
751 | void InstructionTraits::addNodeToList(Instruction *I) { |
752 | assert(I->getParent() == nullptr && "Already in a list!" ); |
753 | I->setParent(getContainingFunction()); |
754 | } |
755 | |
756 | void InstructionTraits::removeNodeFromList(Instruction *I) { |
757 | // When an instruction is removed from a function, clear the parent pointer. |
758 | assert(I->getParent() && "Not in a list!" ); |
759 | I->setParent(nullptr); |
760 | } |
761 | |
762 | namespace glow { |
763 | |
764 | llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const Value &V) { |
765 | V.dump(os); |
766 | return os; |
767 | } |
768 | |
769 | llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const Value *V) { |
770 | assert(V != nullptr && "Null Pointer." ); |
771 | V->dump(os); |
772 | return os; |
773 | } |
774 | |
775 | llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const IRFunction &irf) { |
776 | irf.dump(os); |
777 | return os; |
778 | } |
779 | |
780 | llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const IRFunction *irf) { |
781 | assert(irf != nullptr && "Null Pointer." ); |
782 | irf->dump(os); |
783 | return os; |
784 | } |
785 | |
786 | llvm::raw_ostream &operator<<(llvm::raw_ostream &os, InstrumentKind kind) { |
787 | switch (kind) { |
788 | case InstrumentKind::Before: |
789 | os << "Before" ; |
790 | break; |
791 | case InstrumentKind::After: |
792 | os << "After" ; |
793 | break; |
794 | default: |
795 | llvm_unreachable("Unknown instrumentation kind" ); |
796 | } |
797 | return os; |
798 | } |
799 | } // namespace glow |
800 | |