1 // Copyright 2016 The SwiftShader Authors. All Rights Reserved.
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
7 // http://www.apache.org/licenses/LICENSE-2.0
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.
15 #include "Optimizer.hpp"
17 #include "src/IceCfg.h"
18 #include "src/IceCfgNode.h"
28 void run(Ice::Cfg *function);
31 void analyzeUses(Ice::Cfg *function);
32 void eliminateDeadCode();
33 void eliminateUnitializedLoads();
34 void eliminateLoadsFollowingSingleStore();
35 void optimizeStoresInSingleBasicBlock();
37 void replace(Ice::Inst *instruction, Ice::Operand *newValue);
38 void deleteInstruction(Ice::Inst *instruction);
39 bool isDead(Ice::Inst *instruction);
41 static const Ice::InstIntrinsicCall *asLoadSubVector(const Ice::Inst *instruction);
42 static const Ice::InstIntrinsicCall *asStoreSubVector(const Ice::Inst *instruction);
43 static bool isLoad(const Ice::Inst &instruction);
44 static bool isStore(const Ice::Inst &instruction);
45 static Ice::Operand *storeAddress(const Ice::Inst *instruction);
46 static Ice::Operand *loadAddress(const Ice::Inst *instruction);
47 static Ice::Operand *storeData(const Ice::Inst *instruction);
48 static std::size_t storeSize(const Ice::Inst *instruction);
49 static bool loadTypeMatchesStore(const Ice::Inst *load, const Ice::Inst *store);
52 Ice::GlobalContext *context;
54 struct Uses : std::vector<Ice::Inst*>
56 bool areOnlyLoadStore() const;
57 void insert(Ice::Operand *value, Ice::Inst *instruction);
58 void erase(Ice::Inst *instruction);
60 std::vector<Ice::Inst*> loads;
61 std::vector<Ice::Inst*> stores;
64 std::map<Ice::Operand*, Uses> uses;
65 std::map<Ice::Inst*, Ice::CfgNode*> node;
66 std::map<Ice::Variable*, Ice::Inst*> definition;
69 void Optimizer::run(Ice::Cfg *function)
71 this->function = function;
72 this->context = function->getContext();
74 analyzeUses(function);
77 eliminateUnitializedLoads();
78 eliminateLoadsFollowingSingleStore();
79 optimizeStoresInSingleBasicBlock();
83 void Optimizer::eliminateDeadCode()
89 for(Ice::CfgNode *basicBlock : function->getNodes())
91 for(Ice::Inst &inst : Ice::reverse_range(basicBlock->getInsts()))
100 deleteInstruction(&inst);
109 void Optimizer::eliminateUnitializedLoads()
111 Ice::CfgNode *entryBlock = function->getEntryNode();
113 for(Ice::Inst &alloca : entryBlock->getInsts())
115 if(alloca.isDeleted())
120 if(!llvm::isa<Ice::InstAlloca>(alloca))
122 return; // Allocas are all at the top
125 Ice::Operand *address = alloca.getDest();
126 const auto &addressEntry = uses.find(address);
127 const auto &addressUses = addressEntry->second;
129 if(!addressUses.areOnlyLoadStore())
134 if(addressUses.stores.empty())
136 for(Ice::Inst *load : addressUses.loads)
138 Ice::Variable *loadData = load->getDest();
140 for(Ice::Inst *use : uses[loadData])
142 for(Ice::SizeT i = 0; i < use->getSrcSize(); i++)
144 if(use->getSrc(i) == loadData)
146 auto *undef = context->getConstantUndef(loadData->getType());
148 use->replaceSource(i, undef);
153 uses.erase(loadData);
159 uses.erase(addressEntry);
164 void Optimizer::eliminateLoadsFollowingSingleStore()
166 Ice::CfgNode *entryBlock = function->getEntryNode();
168 for(Ice::Inst &alloca : entryBlock->getInsts())
170 if(alloca.isDeleted())
175 if(!llvm::isa<Ice::InstAlloca>(alloca))
177 return; // Allocas are all at the top
180 Ice::Operand *address = alloca.getDest();
181 const auto &addressEntry = uses.find(address);
182 auto &addressUses = addressEntry->second;
184 if(!addressUses.areOnlyLoadStore())
189 if(addressUses.stores.size() == 1)
191 Ice::Inst *store = addressUses.stores[0];
192 Ice::Operand *storeValue = storeData(store);
194 for(Ice::Inst *load = &*++store->getIterator(), *next = nullptr; load != next; next = load, load = &*++store->getIterator())
196 if(load->isDeleted() || !isLoad(*load))
201 if(loadAddress(load) != address)
206 if(!loadTypeMatchesStore(load, store))
211 replace(load, storeValue);
213 for(size_t i = 0; i < addressUses.loads.size(); i++)
215 if(addressUses.loads[i] == load)
217 addressUses.loads[i] = addressUses.loads.back();
218 addressUses.loads.pop_back();
223 for(size_t i = 0; i < addressUses.size(); i++)
225 if(addressUses[i] == load)
227 addressUses[i] = addressUses.back();
228 addressUses.pop_back();
233 if(addressUses.size() == 1)
235 assert(addressUses[0] == store);
241 auto &valueUses = uses[storeValue];
243 for(size_t i = 0; i < valueUses.size(); i++)
245 if(valueUses[i] == store)
247 valueUses[i] = valueUses.back();
248 valueUses.pop_back();
253 if(valueUses.empty())
255 uses.erase(storeValue);
265 void Optimizer::optimizeStoresInSingleBasicBlock()
267 Ice::CfgNode *entryBlock = function->getEntryNode();
269 for(Ice::Inst &alloca : entryBlock->getInsts())
271 if(alloca.isDeleted())
276 if(!llvm::isa<Ice::InstAlloca>(alloca))
278 return; // Allocas are all at the top
281 Ice::Operand *address = alloca.getDest();
282 const auto &addressEntry = uses.find(address);
283 const auto &addressUses = addressEntry->second;
285 if(!addressUses.areOnlyLoadStore())
290 Ice::CfgNode *singleBasicBlock = node[addressUses.stores[0]];
292 for(size_t i = 1; i < addressUses.stores.size(); i++)
294 Ice::Inst *store = addressUses.stores[i];
295 if(node[store] != singleBasicBlock)
297 singleBasicBlock = nullptr;
304 auto &insts = singleBasicBlock->getInsts();
305 Ice::Inst *store = nullptr;
306 Ice::Operand *storeValue = nullptr;
307 bool unmatchedLoads = false;
309 for(Ice::Inst &inst : insts)
318 if(storeAddress(&inst) != address)
323 // New store found. If we had a previous one, try to eliminate it.
324 if(store && !unmatchedLoads)
326 // If the previous store is wider than the new one, we can't eliminate it
327 // because there could be a wide load reading its non-overwritten data.
328 if(storeSize(&inst) >= storeSize(store))
330 deleteInstruction(store);
335 storeValue = storeData(store);
336 unmatchedLoads = false;
338 else if(isLoad(inst))
340 Ice::Inst *load = &inst;
342 if(loadAddress(load) != address)
347 if(!loadTypeMatchesStore(load, store))
349 unmatchedLoads = true;
353 replace(load, storeValue);
360 void Optimizer::analyzeUses(Ice::Cfg *function)
366 for(Ice::CfgNode *basicBlock : function->getNodes())
368 for(Ice::Inst &instruction : basicBlock->getInsts())
370 if(instruction.isDeleted())
375 node[&instruction] = basicBlock;
376 definition[instruction.getDest()] = &instruction;
378 for(Ice::SizeT i = 0; i < instruction.getSrcSize(); i++)
380 Ice::SizeT unique = 0;
381 for(; unique < i; unique++)
383 if(instruction.getSrc(i) == instruction.getSrc(unique))
391 Ice::Operand *src = instruction.getSrc(i);
392 uses[src].insert(src, &instruction);
399 void Optimizer::replace(Ice::Inst *instruction, Ice::Operand *newValue)
401 Ice::Variable *oldValue = instruction->getDest();
405 newValue = context->getConstantUndef(oldValue->getType());
408 for(Ice::Inst *use : uses[oldValue])
410 assert(!use->isDeleted()); // Should have been removed from uses already
412 for(Ice::SizeT i = 0; i < use->getSrcSize(); i++)
414 if(use->getSrc(i) == oldValue)
416 use->replaceSource(i, newValue);
420 uses[newValue].insert(newValue, use);
423 uses.erase(oldValue);
425 deleteInstruction(instruction);
428 void Optimizer::deleteInstruction(Ice::Inst *instruction)
430 if(!instruction || instruction->isDeleted())
435 instruction->setDeleted();
437 for(Ice::SizeT i = 0; i < instruction->getSrcSize(); i++)
439 Ice::Operand *src = instruction->getSrc(i);
441 const auto &srcEntry = uses.find(src);
443 if(srcEntry != uses.end())
445 auto &srcUses = srcEntry->second;
447 srcUses.erase(instruction);
451 uses.erase(srcEntry);
453 if(Ice::Variable *var = llvm::dyn_cast<Ice::Variable>(src))
455 deleteInstruction(definition[var]);
462 bool Optimizer::isDead(Ice::Inst *instruction)
464 Ice::Variable *dest = instruction->getDest();
468 return uses[dest].empty() && !instruction->hasSideEffects();
470 else if(isStore(*instruction))
472 if(Ice::Variable *address = llvm::dyn_cast<Ice::Variable>(storeAddress(instruction)))
474 Ice::Inst *def = definition[address];
476 if(def && llvm::isa<Ice::InstAlloca>(def))
478 return uses[address].size() == uses[address].stores.size(); // Dead if all uses are stores
486 const Ice::InstIntrinsicCall *Optimizer::asLoadSubVector(const Ice::Inst *instruction)
488 if(auto *instrinsic = llvm::dyn_cast<Ice::InstIntrinsicCall>(instruction))
490 if(instrinsic->getIntrinsicInfo().ID == Ice::Intrinsics::LoadSubVector)
499 const Ice::InstIntrinsicCall *Optimizer::asStoreSubVector(const Ice::Inst *instruction)
501 if(auto *instrinsic = llvm::dyn_cast<Ice::InstIntrinsicCall>(instruction))
503 if(instrinsic->getIntrinsicInfo().ID == Ice::Intrinsics::StoreSubVector)
512 bool Optimizer::isLoad(const Ice::Inst &instruction)
514 if(llvm::isa<Ice::InstLoad>(&instruction))
519 return asLoadSubVector(&instruction) != nullptr;
522 bool Optimizer::isStore(const Ice::Inst &instruction)
524 if(llvm::isa<Ice::InstStore>(&instruction))
529 return asStoreSubVector(&instruction) != nullptr;
532 Ice::Operand *Optimizer::storeAddress(const Ice::Inst *instruction)
534 assert(isStore(*instruction));
536 if(auto *store = llvm::dyn_cast<Ice::InstStore>(instruction))
538 return store->getAddr();
541 if(auto *storeSubVector = asStoreSubVector(instruction))
543 return storeSubVector->getSrc(2);
549 Ice::Operand *Optimizer::loadAddress(const Ice::Inst *instruction)
551 assert(isLoad(*instruction));
553 if(auto *load = llvm::dyn_cast<Ice::InstLoad>(instruction))
555 return load->getSourceAddress();
558 if(auto *loadSubVector = asLoadSubVector(instruction))
560 return loadSubVector->getSrc(1);
566 Ice::Operand *Optimizer::storeData(const Ice::Inst *instruction)
568 assert(isStore(*instruction));
570 if(auto *store = llvm::dyn_cast<Ice::InstStore>(instruction))
572 return store->getData();
575 if(auto *storeSubVector = asStoreSubVector(instruction))
577 return storeSubVector->getSrc(1);
583 std::size_t Optimizer::storeSize(const Ice::Inst *store)
585 assert(isStore(*store));
587 if(auto *instStore = llvm::dyn_cast<Ice::InstStore>(store))
589 return Ice::typeWidthInBytes(instStore->getData()->getType());
592 if(auto *storeSubVector = asStoreSubVector(store))
594 return llvm::cast<Ice::ConstantInteger32>(storeSubVector->getSrc(3))->getValue();
600 bool Optimizer::loadTypeMatchesStore(const Ice::Inst *load, const Ice::Inst *store)
607 assert(isLoad(*load) && isStore(*store));
608 assert(loadAddress(load) == storeAddress(store));
610 if(auto *instStore = llvm::dyn_cast<Ice::InstStore>(store))
612 if(auto *instLoad = llvm::dyn_cast<Ice::InstLoad>(load))
614 return instStore->getData()->getType() == instLoad->getDest()->getType();
618 if(auto *storeSubVector = asStoreSubVector(store))
620 if(auto *loadSubVector = asLoadSubVector(load))
622 // Check for matching type and sub-vector width.
623 return storeSubVector->getSrc(1)->getType() == loadSubVector->getDest()->getType() &&
624 llvm::cast<Ice::ConstantInteger32>(storeSubVector->getSrc(3))->getValue() ==
625 llvm::cast<Ice::ConstantInteger32>(loadSubVector->getSrc(2))->getValue();
632 bool Optimizer::Uses::areOnlyLoadStore() const
634 return size() == (loads.size() + stores.size());
637 void Optimizer::Uses::insert(Ice::Operand *value, Ice::Inst *instruction)
639 push_back(instruction);
641 if(isLoad(*instruction))
643 if(value == loadAddress(instruction))
645 loads.push_back(instruction);
648 else if(isStore(*instruction))
650 if(value == storeAddress(instruction))
652 stores.push_back(instruction);
657 void Optimizer::Uses::erase(Ice::Inst *instruction)
661 for(size_t i = 0; i < uses.size(); i++)
663 if(uses[i] == instruction)
668 for(size_t i = 0; i < loads.size(); i++)
670 if(loads[i] == instruction)
672 loads[i] = loads.back();
678 for(size_t i = 0; i < stores.size(); i++)
680 if(stores[i] == instruction)
682 stores[i] = stores.back();
696 void optimize(Ice::Cfg *function)
700 optimizer.run(function);