1 // Copyright 2016 The SwiftShader Authors. All Rights Reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "Optimizer.hpp"
16
17 #include "src/IceCfg.h"
18 #include "src/IceCfgNode.h"
19
20 #include <unordered_map>
21 #include <vector>
22
23 namespace {
24
25 class Optimizer
26 {
27 public:
Optimizer(rr::Nucleus::OptimizerReport * report)28 Optimizer(rr::Nucleus::OptimizerReport *report)
29 : report(report)
30 {
31 }
32
33 void run(Ice::Cfg *function);
34
35 private:
36 void analyzeUses(Ice::Cfg *function);
37
38 void eliminateDeadCode();
39 void eliminateUnitializedLoads();
40 void propagateAlloca();
41 void performScalarReplacementOfAggregates();
42 void optimizeSingleBasicBlockLoadsStores();
43
44 void replace(Ice::Inst *instruction, Ice::Operand *newValue);
45 void deleteInstruction(Ice::Inst *instruction);
46 bool isDead(Ice::Inst *instruction);
47 bool isStaticallyIndexedArray(Ice::Operand *allocaAddress);
48 Ice::InstAlloca *allocaOf(Ice::Operand *address);
49
50 static const Ice::InstIntrinsic *asLoadSubVector(const Ice::Inst *instruction);
51 static const Ice::InstIntrinsic *asStoreSubVector(const Ice::Inst *instruction);
52 static bool isLoad(const Ice::Inst &instruction);
53 static bool isStore(const Ice::Inst &instruction);
54 static bool loadTypeMatchesStore(const Ice::Inst *load, const Ice::Inst *store);
55 static bool storeTypeMatchesStore(const Ice::Inst *store1, const Ice::Inst *store2);
56
57 void collectDiagnostics();
58
59 Ice::Cfg *function;
60 Ice::GlobalContext *context;
61
62 struct Uses : std::vector<Ice::Inst *>
63 {
64 bool areOnlyLoadStore() const;
65 void insert(Ice::Operand *value, Ice::Inst *instruction);
66 void erase(Ice::Inst *instruction);
67
68 std::vector<Ice::Inst *> loads;
69 std::vector<Ice::Inst *> stores;
70 };
71
72 struct LoadStoreInst
73 {
LoadStoreInst__anonb40bc2440111::Optimizer::LoadStoreInst74 LoadStoreInst(Ice::Inst *inst, bool isStore)
75 : inst(inst)
76 , address(isStore ? inst->getStoreAddress() : inst->getLoadAddress())
77 , isStore(isStore)
78 {
79 }
80
81 Ice::Inst *inst;
82 Ice::Operand *address;
83 bool isStore;
84 };
85
86 Optimizer::Uses *getUses(Ice::Operand *);
87 void setUses(Ice::Operand *, Optimizer::Uses *);
88 bool hasUses(Ice::Operand *) const;
89
90 Ice::Inst *getDefinition(Ice::Variable *);
91 void setDefinition(Ice::Variable *, Ice::Inst *);
92
93 std::vector<Ice::Operand *> operandsWithUses;
94
95 rr::Nucleus::OptimizerReport *report = nullptr;
96 };
97
run(Ice::Cfg * function)98 void Optimizer::run(Ice::Cfg *function)
99 {
100 this->function = function;
101 this->context = function->getContext();
102
103 analyzeUses(function);
104
105 // Start by eliminating any dead code, to avoid redundant work for the
106 // subsequent optimization passes.
107 eliminateDeadCode();
108 eliminateUnitializedLoads();
109
110 // Eliminate allocas which store the address of other allocas.
111 propagateAlloca();
112
113 // Replace arrays with individual elements if only statically indexed.
114 performScalarReplacementOfAggregates();
115
116 // Iterate through basic blocks to propagate loads following stores.
117 optimizeSingleBasicBlockLoadsStores();
118
119 for(auto operand : operandsWithUses)
120 {
121 // Deletes the Uses instance on the operand
122 setUses(operand, nullptr);
123 }
124 operandsWithUses.clear();
125
126 collectDiagnostics();
127 }
128
129 // Eliminates allocas which store the address of other allocas.
propagateAlloca()130 void Optimizer::propagateAlloca()
131 {
132 Ice::CfgNode *entryBlock = function->getEntryNode();
133 Ice::InstList &instList = entryBlock->getInsts();
134
135 for(Ice::Inst &inst : instList)
136 {
137 if(inst.isDeleted())
138 {
139 continue;
140 }
141
142 auto *alloca = llvm::dyn_cast<Ice::InstAlloca>(&inst);
143
144 if(!alloca)
145 {
146 break; // Allocas are all at the top
147 }
148
149 // Look for stores of this alloca's address.
150 Ice::Operand *address = alloca->getDest();
151 Uses uses = *getUses(address); // Hard copy
152
153 for(auto *store : uses)
154 {
155 if(isStore(*store) && store->getData() == address)
156 {
157 Ice::Operand *dest = store->getStoreAddress();
158 Ice::Variable *destVar = llvm::dyn_cast<Ice::Variable>(dest);
159 Ice::Inst *def = destVar ? getDefinition(destVar) : nullptr;
160
161 // If the address is stored into another stack variable, eliminate the latter.
162 if(def && def->getKind() == Ice::Inst::Alloca)
163 {
164 Uses destUses = *getUses(dest); // Hard copy
165
166 // Make sure the only store into the stack variable is this address, and that all of its other uses are loads.
167 // This prevents dynamic array loads/stores to be replaced by a scalar.
168 if((destUses.stores.size() == 1) && (destUses.loads.size() == destUses.size() - 1))
169 {
170 for(auto *load : destUses.loads)
171 {
172 replace(load, address);
173 }
174
175 // The address is now only stored, never loaded, so the store can be eliminated, together with its alloca.
176 assert(getUses(dest)->size() == 1);
177 deleteInstruction(store);
178 assert(def->isDeleted());
179 }
180 }
181 }
182 }
183 }
184 }
185
pointerType()186 Ice::Type pointerType()
187 {
188 if(sizeof(void *) == 8)
189 {
190 return Ice::IceType_i64;
191 }
192 else
193 {
194 return Ice::IceType_i32;
195 }
196 }
197
198 // Replace arrays with individual elements if only statically indexed.
performScalarReplacementOfAggregates()199 void Optimizer::performScalarReplacementOfAggregates()
200 {
201 std::vector<Ice::InstAlloca *> newAllocas;
202
203 Ice::CfgNode *entryBlock = function->getEntryNode();
204 Ice::InstList &instList = entryBlock->getInsts();
205
206 for(Ice::Inst &inst : instList)
207 {
208 if(inst.isDeleted())
209 {
210 continue;
211 }
212
213 auto *alloca = llvm::dyn_cast<Ice::InstAlloca>(&inst);
214
215 if(!alloca)
216 {
217 break; // Allocas are all at the top
218 }
219
220 uint32_t sizeInBytes = llvm::cast<Ice::ConstantInteger32>(alloca->getSizeInBytes())->getValue();
221 uint32_t alignInBytes = alloca->getAlignInBytes();
222
223 // This pass relies on array elements to be naturally aligned (i.e. matches the type size).
224 assert(sizeInBytes >= alignInBytes);
225 assert(sizeInBytes % alignInBytes == 0);
226 uint32_t elementCount = sizeInBytes / alignInBytes;
227
228 Ice::Operand *address = alloca->getDest();
229
230 if(isStaticallyIndexedArray(address))
231 {
232 // Delete the old array.
233 alloca->setDeleted();
234
235 // Allocate new stack slots for each element.
236 std::vector<Ice::Variable *> newAddress(elementCount);
237 auto *bytes = Ice::ConstantInteger32::create(context, Ice::IceType_i32, alignInBytes);
238
239 for(uint32_t i = 0; i < elementCount; i++)
240 {
241 newAddress[i] = function->makeVariable(pointerType());
242 auto *alloca = Ice::InstAlloca::create(function, newAddress[i], bytes, alignInBytes);
243 setDefinition(newAddress[i], alloca);
244
245 newAllocas.push_back(alloca);
246 }
247
248 Uses uses = *getUses(address); // Hard copy
249
250 for(auto *use : uses)
251 {
252 assert(!use->isDeleted());
253
254 if(isLoad(*use)) // Direct use of base address
255 {
256 use->replaceSource(asLoadSubVector(use) ? 1 : 0, newAddress[0]);
257 getUses(newAddress[0])->insert(newAddress[0], use);
258 }
259 else if(isStore(*use)) // Direct use of base address
260 {
261 use->replaceSource(asStoreSubVector(use) ? 2 : 1, newAddress[0]);
262 getUses(newAddress[0])->insert(newAddress[0], use);
263 }
264 else // Statically indexed use
265 {
266 auto *arithmetic = llvm::cast<Ice::InstArithmetic>(use);
267
268 if(arithmetic->getOp() == Ice::InstArithmetic::Add)
269 {
270 auto *rhs = arithmetic->getSrc(1);
271 int32_t offset = llvm::cast<Ice::ConstantInteger32>(rhs)->getValue();
272
273 assert(offset % alignInBytes == 0);
274 int32_t index = offset / alignInBytes;
275 assert(static_cast<uint32_t>(index) < elementCount);
276
277 replace(arithmetic, newAddress[index]);
278 }
279 else
280 assert(false && "Mismatch between isStaticallyIndexedArray() and scalarReplacementOfAggregates()");
281 }
282 }
283 }
284 }
285
286 // After looping over all the old allocas, add any new ones that replace them.
287 // They're added to the front in reverse order, to retain their original order.
288 for(size_t i = newAllocas.size(); i-- != 0;)
289 {
290 if(!isDead(newAllocas[i]))
291 {
292 instList.push_front(newAllocas[i]);
293 }
294 }
295 }
296
eliminateDeadCode()297 void Optimizer::eliminateDeadCode()
298 {
299 bool modified;
300 do
301 {
302 modified = false;
303 for(Ice::CfgNode *basicBlock : function->getNodes())
304 {
305 for(Ice::Inst &inst : Ice::reverse_range(basicBlock->getInsts()))
306 {
307 if(inst.isDeleted())
308 {
309 continue;
310 }
311
312 if(isDead(&inst))
313 {
314 deleteInstruction(&inst);
315 modified = true;
316 }
317 }
318 }
319 } while(modified);
320 }
321
eliminateUnitializedLoads()322 void Optimizer::eliminateUnitializedLoads()
323 {
324 Ice::CfgNode *entryBlock = function->getEntryNode();
325
326 for(Ice::Inst &alloca : entryBlock->getInsts())
327 {
328 if(alloca.isDeleted())
329 {
330 continue;
331 }
332
333 if(!llvm::isa<Ice::InstAlloca>(alloca))
334 {
335 break; // Allocas are all at the top
336 }
337
338 Ice::Operand *address = alloca.getDest();
339
340 if(!hasUses(address))
341 {
342 continue;
343 }
344
345 const auto &addressUses = *getUses(address);
346
347 if(!addressUses.areOnlyLoadStore())
348 {
349 continue;
350 }
351
352 if(addressUses.stores.empty())
353 {
354 for(Ice::Inst *load : addressUses.loads)
355 {
356 Ice::Variable *loadData = load->getDest();
357
358 if(hasUses(loadData))
359 {
360 for(Ice::Inst *use : *getUses(loadData))
361 {
362 for(Ice::SizeT i = 0; i < use->getSrcSize(); i++)
363 {
364 if(use->getSrc(i) == loadData)
365 {
366 auto *undef = context->getConstantUndef(loadData->getType());
367
368 use->replaceSource(i, undef);
369 }
370 }
371 }
372
373 setUses(loadData, nullptr);
374 }
375
376 load->setDeleted();
377 }
378
379 alloca.setDeleted();
380 setUses(address, nullptr);
381 }
382 }
383 }
384
385 // Iterate through basic blocks to propagate stores to subsequent loads.
optimizeSingleBasicBlockLoadsStores()386 void Optimizer::optimizeSingleBasicBlockLoadsStores()
387 {
388 for(Ice::CfgNode *block : function->getNodes())
389 {
390 // For each stack variable keep track of the last store instruction.
391 // To eliminate a store followed by another store to the same alloca address
392 // we must also know whether all loads have been replaced by the store value.
393 struct LastStore
394 {
395 Ice::Inst *store;
396 bool allLoadsReplaced = true;
397 };
398
399 // Use the (unique) index of the alloca's destination argument (i.e. the address
400 // of the allocated variable), which is of type SizeT, as the key. Note we do not
401 // use the pointer to the alloca instruction or its resulting address, to avoid
402 // undeterministic unordered_map behavior.
403 std::unordered_map<Ice::SizeT, LastStore> lastStoreTo;
404
405 for(Ice::Inst &inst : block->getInsts())
406 {
407 if(inst.isDeleted())
408 {
409 continue;
410 }
411
412 if(isStore(inst))
413 {
414 Ice::Operand *address = inst.getStoreAddress();
415
416 if(Ice::InstAlloca *alloca = allocaOf(address))
417 {
418 // Only consider this store for propagation if its address is not used as
419 // a pointer which could be used for indirect stores.
420 if(getUses(address)->areOnlyLoadStore())
421 {
422 Ice::SizeT addressIdx = alloca->getDest()->getIndex();
423
424 // If there was a previous store to this address, and it was propagated
425 // to all subsequent loads, it can be eliminated.
426 if(auto entry = lastStoreTo.find(addressIdx); entry != lastStoreTo.end())
427 {
428 Ice::Inst *previousStore = entry->second.store;
429
430 if(storeTypeMatchesStore(&inst, previousStore) &&
431 entry->second.allLoadsReplaced)
432 {
433 deleteInstruction(previousStore);
434 }
435 }
436
437 lastStoreTo[addressIdx] = { &inst };
438 }
439 }
440 }
441 else if(isLoad(inst))
442 {
443 if(Ice::InstAlloca *alloca = allocaOf(inst.getLoadAddress()))
444 {
445 Ice::SizeT addressIdx = alloca->getDest()->getIndex();
446 auto entry = lastStoreTo.find(addressIdx);
447 if(entry != lastStoreTo.end())
448 {
449 const Ice::Inst *store = entry->second.store;
450
451 if(loadTypeMatchesStore(&inst, store))
452 {
453 replace(&inst, store->getData());
454 }
455 else
456 {
457 entry->second.allLoadsReplaced = false;
458 }
459 }
460 }
461 }
462 }
463 }
464
465 // This can leave some dead instructions. Specifically stores.
466 // TODO(b/179668593): Check just for dead stores by iterating over allocas?
467 eliminateDeadCode();
468 }
469
analyzeUses(Ice::Cfg * function)470 void Optimizer::analyzeUses(Ice::Cfg *function)
471 {
472 for(Ice::CfgNode *basicBlock : function->getNodes())
473 {
474 for(Ice::Inst &instruction : basicBlock->getInsts())
475 {
476 if(instruction.isDeleted())
477 {
478 continue;
479 }
480
481 if(instruction.getDest())
482 {
483 setDefinition(instruction.getDest(), &instruction);
484 }
485
486 for(Ice::SizeT i = 0; i < instruction.getSrcSize(); i++)
487 {
488 Ice::SizeT unique = 0;
489 for(; unique < i; unique++)
490 {
491 if(instruction.getSrc(i) == instruction.getSrc(unique))
492 {
493 break;
494 }
495 }
496
497 if(i == unique)
498 {
499 Ice::Operand *src = instruction.getSrc(i);
500 getUses(src)->insert(src, &instruction);
501 }
502 }
503 }
504 }
505 }
506
replace(Ice::Inst * instruction,Ice::Operand * newValue)507 void Optimizer::replace(Ice::Inst *instruction, Ice::Operand *newValue)
508 {
509 Ice::Variable *oldValue = instruction->getDest();
510
511 if(!newValue)
512 {
513 newValue = context->getConstantUndef(oldValue->getType());
514 }
515
516 if(hasUses(oldValue))
517 {
518 for(Ice::Inst *use : *getUses(oldValue))
519 {
520 assert(!use->isDeleted()); // Should have been removed from uses already
521
522 for(Ice::SizeT i = 0; i < use->getSrcSize(); i++)
523 {
524 if(use->getSrc(i) == oldValue)
525 {
526 use->replaceSource(i, newValue);
527 }
528 }
529
530 getUses(newValue)->insert(newValue, use);
531 }
532
533 setUses(oldValue, nullptr);
534 }
535
536 deleteInstruction(instruction);
537 }
538
deleteInstruction(Ice::Inst * instruction)539 void Optimizer::deleteInstruction(Ice::Inst *instruction)
540 {
541 if(!instruction || instruction->isDeleted())
542 {
543 return;
544 }
545
546 assert(!instruction->getDest() || getUses(instruction->getDest())->empty());
547 instruction->setDeleted();
548
549 for(Ice::SizeT i = 0; i < instruction->getSrcSize(); i++)
550 {
551 Ice::Operand *src = instruction->getSrc(i);
552
553 if(hasUses(src))
554 {
555 auto &srcUses = *getUses(src);
556
557 srcUses.erase(instruction);
558
559 if(srcUses.empty())
560 {
561 setUses(src, nullptr);
562
563 if(Ice::Variable *var = llvm::dyn_cast<Ice::Variable>(src))
564 {
565 deleteInstruction(getDefinition(var));
566 }
567 }
568 }
569 }
570 }
571
isDead(Ice::Inst * instruction)572 bool Optimizer::isDead(Ice::Inst *instruction)
573 {
574 Ice::Variable *dest = instruction->getDest();
575
576 if(dest)
577 {
578 return (!hasUses(dest) || getUses(dest)->empty()) && !instruction->hasSideEffects();
579 }
580 else if(isStore(*instruction))
581 {
582 if(Ice::Variable *address = llvm::dyn_cast<Ice::Variable>(instruction->getStoreAddress()))
583 {
584 Ice::Inst *def = getDefinition(address);
585
586 if(def && llvm::isa<Ice::InstAlloca>(def))
587 {
588 if(hasUses(address))
589 {
590 Optimizer::Uses *uses = getUses(address);
591 return uses->size() == uses->stores.size(); // Dead if all uses are stores
592 }
593 else
594 {
595 return true; // No uses
596 }
597 }
598 }
599 }
600
601 return false;
602 }
603
isStaticallyIndexedArray(Ice::Operand * allocaAddress)604 bool Optimizer::isStaticallyIndexedArray(Ice::Operand *allocaAddress)
605 {
606 auto &uses = *getUses(allocaAddress);
607
608 for(auto *use : uses)
609 {
610 // Direct load from base address.
611 if(isLoad(*use) && use->getLoadAddress() == allocaAddress)
612 {
613 continue; // This is fine.
614 }
615
616 if(isStore(*use))
617 {
618 // Can either be the address we're storing to, or the data we're storing.
619 if(use->getStoreAddress() == allocaAddress)
620 {
621 continue;
622 }
623 else
624 {
625 // propagateAlloca() eliminates most of the stores of the address itself.
626 // For the cases it doesn't handle, assume SRoA is not feasible.
627 return false;
628 }
629 }
630
631 // Pointer arithmetic is fine if it only uses constants.
632 auto *arithmetic = llvm::dyn_cast<Ice::InstArithmetic>(use);
633 if(arithmetic && arithmetic->getOp() == Ice::InstArithmetic::Add)
634 {
635 auto *rhs = arithmetic->getSrc(1);
636
637 if(llvm::isa<Ice::Constant>(rhs))
638 {
639 continue;
640 }
641 }
642
643 // If there's any other type of use, bail out.
644 return false;
645 }
646
647 return true;
648 }
649
allocaOf(Ice::Operand * address)650 Ice::InstAlloca *Optimizer::allocaOf(Ice::Operand *address)
651 {
652 Ice::Variable *addressVar = llvm::dyn_cast<Ice::Variable>(address);
653 Ice::Inst *def = addressVar ? getDefinition(addressVar) : nullptr;
654 Ice::InstAlloca *alloca = def ? llvm::dyn_cast<Ice::InstAlloca>(def) : nullptr;
655
656 return alloca;
657 }
658
asLoadSubVector(const Ice::Inst * instruction)659 const Ice::InstIntrinsic *Optimizer::asLoadSubVector(const Ice::Inst *instruction)
660 {
661 if(auto *instrinsic = llvm::dyn_cast<Ice::InstIntrinsic>(instruction))
662 {
663 if(instrinsic->getIntrinsicID() == Ice::Intrinsics::LoadSubVector)
664 {
665 return instrinsic;
666 }
667 }
668
669 return nullptr;
670 }
671
asStoreSubVector(const Ice::Inst * instruction)672 const Ice::InstIntrinsic *Optimizer::asStoreSubVector(const Ice::Inst *instruction)
673 {
674 if(auto *instrinsic = llvm::dyn_cast<Ice::InstIntrinsic>(instruction))
675 {
676 if(instrinsic->getIntrinsicID() == Ice::Intrinsics::StoreSubVector)
677 {
678 return instrinsic;
679 }
680 }
681
682 return nullptr;
683 }
684
isLoad(const Ice::Inst & instruction)685 bool Optimizer::isLoad(const Ice::Inst &instruction)
686 {
687 if(llvm::isa<Ice::InstLoad>(&instruction))
688 {
689 return true;
690 }
691
692 return asLoadSubVector(&instruction) != nullptr;
693 }
694
isStore(const Ice::Inst & instruction)695 bool Optimizer::isStore(const Ice::Inst &instruction)
696 {
697 if(llvm::isa<Ice::InstStore>(&instruction))
698 {
699 return true;
700 }
701
702 return asStoreSubVector(&instruction) != nullptr;
703 }
704
loadTypeMatchesStore(const Ice::Inst * load,const Ice::Inst * store)705 bool Optimizer::loadTypeMatchesStore(const Ice::Inst *load, const Ice::Inst *store)
706 {
707 if(!load || !store)
708 {
709 return false;
710 }
711
712 assert(isLoad(*load) && isStore(*store));
713 assert(load->getLoadAddress() == store->getStoreAddress());
714
715 if(store->getData()->getType() != load->getDest()->getType())
716 {
717 return false;
718 }
719
720 if(auto *storeSubVector = asStoreSubVector(store))
721 {
722 if(auto *loadSubVector = asLoadSubVector(load))
723 {
724 // Check for matching sub-vector width.
725 return llvm::cast<Ice::ConstantInteger32>(storeSubVector->getSrc(2))->getValue() ==
726 llvm::cast<Ice::ConstantInteger32>(loadSubVector->getSrc(1))->getValue();
727 }
728 }
729
730 return true;
731 }
732
storeTypeMatchesStore(const Ice::Inst * store1,const Ice::Inst * store2)733 bool Optimizer::storeTypeMatchesStore(const Ice::Inst *store1, const Ice::Inst *store2)
734 {
735 assert(isStore(*store1) && isStore(*store2));
736 assert(store1->getStoreAddress() == store2->getStoreAddress());
737
738 if(store1->getData()->getType() != store2->getData()->getType())
739 {
740 return false;
741 }
742
743 if(auto *storeSubVector1 = asStoreSubVector(store1))
744 {
745 if(auto *storeSubVector2 = asStoreSubVector(store2))
746 {
747 // Check for matching sub-vector width.
748 return llvm::cast<Ice::ConstantInteger32>(storeSubVector1->getSrc(2))->getValue() ==
749 llvm::cast<Ice::ConstantInteger32>(storeSubVector2->getSrc(2))->getValue();
750 }
751 }
752
753 return true;
754 }
755
collectDiagnostics()756 void Optimizer::collectDiagnostics()
757 {
758 if(report)
759 {
760 *report = {};
761
762 for(auto *basicBlock : function->getNodes())
763 {
764 for(auto &inst : basicBlock->getInsts())
765 {
766 if(inst.isDeleted())
767 {
768 continue;
769 }
770
771 if(llvm::isa<Ice::InstAlloca>(inst))
772 {
773 report->allocas++;
774 }
775 else if(isLoad(inst))
776 {
777 report->loads++;
778 }
779 else if(isStore(inst))
780 {
781 report->stores++;
782 }
783 }
784 }
785 }
786 }
787
getUses(Ice::Operand * operand)788 Optimizer::Uses *Optimizer::getUses(Ice::Operand *operand)
789 {
790 Optimizer::Uses *uses = (Optimizer::Uses *)operand->Ice::Operand::getExternalData();
791 if(!uses)
792 {
793 uses = new Optimizer::Uses;
794 setUses(operand, uses);
795 operandsWithUses.push_back(operand);
796 }
797 return uses;
798 }
799
setUses(Ice::Operand * operand,Optimizer::Uses * uses)800 void Optimizer::setUses(Ice::Operand *operand, Optimizer::Uses *uses)
801 {
802 if(auto *oldUses = reinterpret_cast<Optimizer::Uses *>(operand->Ice::Operand::getExternalData()))
803 {
804 delete oldUses;
805 }
806
807 operand->Ice::Operand::setExternalData(uses);
808 }
809
hasUses(Ice::Operand * operand) const810 bool Optimizer::hasUses(Ice::Operand *operand) const
811 {
812 return operand->Ice::Operand::getExternalData() != nullptr;
813 }
814
getDefinition(Ice::Variable * var)815 Ice::Inst *Optimizer::getDefinition(Ice::Variable *var)
816 {
817 return (Ice::Inst *)var->Ice::Variable::getExternalData();
818 }
819
setDefinition(Ice::Variable * var,Ice::Inst * inst)820 void Optimizer::setDefinition(Ice::Variable *var, Ice::Inst *inst)
821 {
822 var->Ice::Variable::setExternalData(inst);
823 }
824
areOnlyLoadStore() const825 bool Optimizer::Uses::areOnlyLoadStore() const
826 {
827 return size() == (loads.size() + stores.size());
828 }
829
insert(Ice::Operand * value,Ice::Inst * instruction)830 void Optimizer::Uses::insert(Ice::Operand *value, Ice::Inst *instruction)
831 {
832 push_back(instruction);
833
834 if(isLoad(*instruction))
835 {
836 if(value == instruction->getLoadAddress())
837 {
838 loads.push_back(instruction);
839 }
840 }
841 else if(isStore(*instruction))
842 {
843 if(value == instruction->getStoreAddress())
844 {
845 stores.push_back(instruction);
846 }
847 }
848 }
849
erase(Ice::Inst * instruction)850 void Optimizer::Uses::erase(Ice::Inst *instruction)
851 {
852 auto &uses = *this;
853
854 for(size_t i = 0; i < uses.size(); i++)
855 {
856 if(uses[i] == instruction)
857 {
858 uses[i] = back();
859 pop_back();
860
861 for(size_t i = 0; i < loads.size(); i++)
862 {
863 if(loads[i] == instruction)
864 {
865 loads[i] = loads.back();
866 loads.pop_back();
867 break;
868 }
869 }
870
871 for(size_t i = 0; i < stores.size(); i++)
872 {
873 if(stores[i] == instruction)
874 {
875 stores[i] = stores.back();
876 stores.pop_back();
877 break;
878 }
879 }
880
881 break;
882 }
883 }
884 }
885
886 } // anonymous namespace
887
888 namespace rr {
889
optimize(Ice::Cfg * function,Nucleus::OptimizerReport * report)890 void optimize(Ice::Cfg *function, Nucleus::OptimizerReport *report)
891 {
892 Optimizer optimizer(report);
893
894 optimizer.run(function);
895 }
896
897 } // namespace rr
898