xref: /aosp_15_r20/external/swiftshader/src/Reactor/Optimizer.cpp (revision 03ce13f70fcc45d86ee91b7ee4cab1936a95046e)
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