blob: abb7d4c25fb66edd12d1b0537f518a9d92797939 [file] [log] [blame]
Nicolas Capens2ae9d742016-11-24 14:43:05 -05001// 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
Nicolas Capens3ffbd622021-02-06 00:35:38 -050020#include <unordered_map>
Nicolas Capens2ae9d742016-11-24 14:43:05 -050021#include <vector>
22
Nicolas Capens157ba262019-12-10 17:49:14 -050023namespace {
24
25class Optimizer
Nicolas Capens2ae9d742016-11-24 14:43:05 -050026{
Nicolas Capens157ba262019-12-10 17:49:14 -050027public:
Nicolas Capens54313fb2021-02-19 14:26:27 -050028 Optimizer(rr::Nucleus::OptimizerReport *report)
29 : report(report)
30 {
31 }
32
Nicolas Capens157ba262019-12-10 17:49:14 -050033 void run(Ice::Cfg *function);
34
35private:
36 void analyzeUses(Ice::Cfg *function);
Nicolas Capens0b506e12021-02-04 17:00:08 -050037
Nicolas Capens157ba262019-12-10 17:49:14 -050038 void eliminateDeadCode();
Nicolas Capens0b506e12021-02-04 17:00:08 -050039 void propagateAlloca();
Nicolas Capens0cfc0432021-02-05 15:18:42 -050040 void performScalarReplacementOfAggregates();
Nicolas Capens3ffbd622021-02-06 00:35:38 -050041 void optimizeSingleBasicBlockLoadsStores();
Nicolas Capens157ba262019-12-10 17:49:14 -050042 void eliminateUnitializedLoads();
43 void eliminateLoadsFollowingSingleStore();
44 void optimizeStoresInSingleBasicBlock();
45
46 void replace(Ice::Inst *instruction, Ice::Operand *newValue);
47 void deleteInstruction(Ice::Inst *instruction);
48 bool isDead(Ice::Inst *instruction);
Nicolas Capens0cfc0432021-02-05 15:18:42 -050049 bool isStaticallyIndexedArray(Ice::Operand *allocaAddress);
Nicolas Capens3ffbd622021-02-06 00:35:38 -050050 Ice::InstAlloca *allocaOf(Ice::Operand *address);
Nicolas Capens157ba262019-12-10 17:49:14 -050051
Nicolas Capens33a77f72021-02-08 15:04:38 -050052 static const Ice::InstIntrinsic *asLoadSubVector(const Ice::Inst *instruction);
53 static const Ice::InstIntrinsic *asStoreSubVector(const Ice::Inst *instruction);
Nicolas Capens157ba262019-12-10 17:49:14 -050054 static bool isLoad(const Ice::Inst &instruction);
55 static bool isStore(const Ice::Inst &instruction);
Nicolas Capens157ba262019-12-10 17:49:14 -050056 static std::size_t storeSize(const Ice::Inst *instruction);
57 static bool loadTypeMatchesStore(const Ice::Inst *load, const Ice::Inst *store);
58
Nicolas Capens54313fb2021-02-19 14:26:27 -050059 void collectDiagnostics();
60
Nicolas Capens157ba262019-12-10 17:49:14 -050061 Ice::Cfg *function;
62 Ice::GlobalContext *context;
63
Ben Clayton713b8d32019-12-17 20:37:56 +000064 struct Uses : std::vector<Ice::Inst *>
Nicolas Capens2ae9d742016-11-24 14:43:05 -050065 {
Nicolas Capens157ba262019-12-10 17:49:14 -050066 bool areOnlyLoadStore() const;
67 void insert(Ice::Operand *value, Ice::Inst *instruction);
68 void erase(Ice::Inst *instruction);
Nicolas Capens2ae9d742016-11-24 14:43:05 -050069
Ben Clayton713b8d32019-12-17 20:37:56 +000070 std::vector<Ice::Inst *> loads;
71 std::vector<Ice::Inst *> stores;
Nicolas Capens2ae9d742016-11-24 14:43:05 -050072 };
73
Nicolas Capens157ba262019-12-10 17:49:14 -050074 struct LoadStoreInst
Nicolas Capens2ae9d742016-11-24 14:43:05 -050075 {
Ben Clayton713b8d32019-12-17 20:37:56 +000076 LoadStoreInst(Ice::Inst *inst, bool isStore)
77 : inst(inst)
Nicolas Capens673a7fe2021-02-05 15:03:22 -050078 , address(isStore ? inst->getStoreAddress() : inst->getLoadAddress())
Ben Clayton713b8d32019-12-17 20:37:56 +000079 , isStore(isStore)
Alexis Hetu932640b2018-06-20 15:35:53 -040080 {
Alexis Hetu932640b2018-06-20 15:35:53 -040081 }
Nicolas Capens2ae9d742016-11-24 14:43:05 -050082
Ben Clayton713b8d32019-12-17 20:37:56 +000083 Ice::Inst *inst;
Nicolas Capens157ba262019-12-10 17:49:14 -050084 Ice::Operand *address;
85 bool isStore;
86 };
87
Ben Clayton713b8d32019-12-17 20:37:56 +000088 Optimizer::Uses *getUses(Ice::Operand *);
89 void setUses(Ice::Operand *, Optimizer::Uses *);
90 bool hasUses(Ice::Operand *) const;
Nicolas Capens157ba262019-12-10 17:49:14 -050091
Ben Clayton713b8d32019-12-17 20:37:56 +000092 Ice::CfgNode *getNode(Ice::Inst *);
93 void setNode(Ice::Inst *, Ice::CfgNode *);
Nicolas Capens157ba262019-12-10 17:49:14 -050094
Ben Clayton713b8d32019-12-17 20:37:56 +000095 Ice::Inst *getDefinition(Ice::Variable *);
96 void setDefinition(Ice::Variable *, Ice::Inst *);
Nicolas Capens157ba262019-12-10 17:49:14 -050097
Ben Clayton713b8d32019-12-17 20:37:56 +000098 const std::vector<LoadStoreInst> &getLoadStoreInsts(Ice::CfgNode *);
99 void setLoadStoreInsts(Ice::CfgNode *, std::vector<LoadStoreInst> *);
100 bool hasLoadStoreInsts(Ice::CfgNode *node) const;
Nicolas Capens157ba262019-12-10 17:49:14 -0500101
Antonio Maiorano5ba2a5b2020-01-17 15:29:37 -0500102 std::vector<Ice::Operand *> operandsWithUses;
Nicolas Capens54313fb2021-02-19 14:26:27 -0500103
104 rr::Nucleus::OptimizerReport *report = nullptr;
Nicolas Capens157ba262019-12-10 17:49:14 -0500105};
106
107void Optimizer::run(Ice::Cfg *function)
108{
109 this->function = function;
110 this->context = function->getContext();
111
112 analyzeUses(function);
113
Nicolas Capens0b506e12021-02-04 17:00:08 -0500114 // Start by eliminating any dead code, to avoid redundant work for the
115 // subsequent optimization passes.
Nicolas Capens157ba262019-12-10 17:49:14 -0500116 eliminateDeadCode();
Nicolas Capens0b506e12021-02-04 17:00:08 -0500117
118 // Eliminate allocas which store the address of other allocas.
119 propagateAlloca();
120
Nicolas Capens0cfc0432021-02-05 15:18:42 -0500121 // Replace arrays with individual elements if only statically indexed.
122 performScalarReplacementOfAggregates();
123
Nicolas Capens3ffbd622021-02-06 00:35:38 -0500124 // Iterate through basic blocks to propagate loads following stores.
125 optimizeSingleBasicBlockLoadsStores();
126
Nicolas Capens157ba262019-12-10 17:49:14 -0500127 eliminateUnitializedLoads();
128 eliminateLoadsFollowingSingleStore();
129 optimizeStoresInSingleBasicBlock();
130 eliminateDeadCode();
131
Antonio Maiorano5ba2a5b2020-01-17 15:29:37 -0500132 for(auto operand : operandsWithUses)
Nicolas Capens2ae9d742016-11-24 14:43:05 -0500133 {
Antonio Maiorano614a4d42020-01-29 13:33:02 -0500134 // Deletes the Uses instance on the operand
135 setUses(operand, nullptr);
Nicolas Capens157ba262019-12-10 17:49:14 -0500136 }
Antonio Maiorano5ba2a5b2020-01-17 15:29:37 -0500137 operandsWithUses.clear();
Nicolas Capens54313fb2021-02-19 14:26:27 -0500138
139 collectDiagnostics();
Nicolas Capens157ba262019-12-10 17:49:14 -0500140}
141
Nicolas Capens0b506e12021-02-04 17:00:08 -0500142// Eliminates allocas which store the address of other allocas.
143void Optimizer::propagateAlloca()
144{
145 Ice::CfgNode *entryBlock = function->getEntryNode();
146 Ice::InstList &instList = entryBlock->getInsts();
147
148 for(Ice::Inst &inst : instList)
149 {
150 if(inst.isDeleted())
151 {
152 continue;
153 }
154
155 auto *alloca = llvm::dyn_cast<Ice::InstAlloca>(&inst);
156
157 if(!alloca)
158 {
159 break; // Allocas are all at the top
160 }
161
162 // Look for stores of this alloca's address.
163 Ice::Operand *address = alloca->getDest();
164 Uses uses = *getUses(address); // Hard copy
165
166 for(auto *store : uses)
167 {
168 if(isStore(*store) && store->getData() == address)
169 {
170 Ice::Operand *dest = store->getStoreAddress();
171 Ice::Variable *destVar = llvm::dyn_cast<Ice::Variable>(dest);
172 Ice::Inst *def = destVar ? getDefinition(destVar) : nullptr;
173
174 // If the address is stored into another stack variable, eliminate the latter.
175 if(def && def->getKind() == Ice::Inst::Alloca)
176 {
177 Uses destUses = *getUses(dest); // Hard copy
178
179 // Make sure the only store into the stack variable is this address, and that all of its other uses are loads.
180 // This prevents dynamic array loads/stores to be replaced by a scalar.
181 if((destUses.stores.size() == 1) && (destUses.loads.size() == destUses.size() - 1))
182 {
183 for(auto *load : destUses.loads)
184 {
185 replace(load, address);
186 }
187
188 // The address is now only stored, never loaded, so the store can be eliminated, together with its alloca.
189 assert(getUses(dest)->size() == 1);
190 deleteInstruction(store);
191 assert(def->isDeleted());
192 }
193 }
194 }
195 }
196 }
197}
198
Nicolas Capens0cfc0432021-02-05 15:18:42 -0500199Ice::Type pointerType()
200{
201 if(sizeof(void *) == 8)
202 {
203 return Ice::IceType_i64;
204 }
205 else
206 {
207 return Ice::IceType_i32;
208 }
209}
210
211// Replace arrays with individual elements if only statically indexed.
212void Optimizer::performScalarReplacementOfAggregates()
213{
214 std::vector<Ice::InstAlloca *> newAllocas;
215
216 Ice::CfgNode *entryBlock = function->getEntryNode();
217 Ice::InstList &instList = entryBlock->getInsts();
218
219 for(Ice::Inst &inst : instList)
220 {
221 if(inst.isDeleted())
222 {
223 continue;
224 }
225
226 auto *alloca = llvm::dyn_cast<Ice::InstAlloca>(&inst);
227
228 if(!alloca)
229 {
230 break; // Allocas are all at the top
231 }
232
233 uint32_t sizeInBytes = llvm::cast<Ice::ConstantInteger32>(alloca->getSizeInBytes())->getValue();
234 uint32_t alignInBytes = alloca->getAlignInBytes();
235
236 // This pass relies on array elements to be naturally aligned (i.e. matches the type size).
237 assert(sizeInBytes >= alignInBytes);
238 assert(sizeInBytes % alignInBytes == 0);
239 uint32_t elementCount = sizeInBytes / alignInBytes;
240
241 Ice::Operand *address = alloca->getDest();
242
243 if(isStaticallyIndexedArray(address))
244 {
245 // Delete the old array.
246 alloca->setDeleted();
247
248 // Allocate new stack slots for each element.
249 std::vector<Ice::Variable *> newAddress(elementCount);
250 auto *bytes = Ice::ConstantInteger32::create(context, Ice::IceType_i32, alignInBytes);
251
252 for(uint32_t i = 0; i < elementCount; i++)
253 {
254 newAddress[i] = function->makeVariable(pointerType());
255 auto *alloca = Ice::InstAlloca::create(function, newAddress[i], bytes, alignInBytes);
256 setDefinition(newAddress[i], alloca);
257
258 newAllocas.push_back(alloca);
259 }
260
261 Uses uses = *getUses(address); // Hard copy
262
263 for(auto *use : uses)
264 {
265 assert(!use->isDeleted());
266
267 if(isLoad(*use)) // Direct use of base address
268 {
269 use->replaceSource(asLoadSubVector(use) ? 1 : 0, newAddress[0]);
270 getUses(newAddress[0])->insert(newAddress[0], use);
271 }
272 else if(isStore(*use)) // Direct use of base address
273 {
274 use->replaceSource(asStoreSubVector(use) ? 2 : 1, newAddress[0]);
275 getUses(newAddress[0])->insert(newAddress[0], use);
276 }
277 else // Statically indexed use
278 {
279 auto *arithmetic = llvm::cast<Ice::InstArithmetic>(use);
280
281 if(arithmetic->getOp() == Ice::InstArithmetic::Add)
282 {
283 auto *rhs = arithmetic->getSrc(1);
284 int32_t offset = llvm::cast<Ice::ConstantInteger32>(rhs)->getValue();
285
286 assert(offset % alignInBytes == 0);
287 int32_t index = offset / alignInBytes;
288 assert(static_cast<uint32_t>(index) < elementCount);
289
290 replace(arithmetic, newAddress[index]);
291 }
292 else
293 assert(false && "Mismatch between isStaticallyIndexedArray() and scalarReplacementOfAggregates()");
294 }
295 }
296 }
297 }
298
299 // After looping over all the old allocas, add any new ones that replace them.
300 // They're added to the front in reverse order, to retain their original order.
301 for(size_t i = newAllocas.size(); i-- != 0;)
302 {
303 if(!isDead(newAllocas[i]))
304 {
305 instList.push_front(newAllocas[i]);
306 }
307 }
308}
309
Nicolas Capens157ba262019-12-10 17:49:14 -0500310void Optimizer::eliminateDeadCode()
311{
312 bool modified;
313 do
314 {
315 modified = false;
316 for(Ice::CfgNode *basicBlock : function->getNodes())
Nicolas Capens2ae9d742016-11-24 14:43:05 -0500317 {
Nicolas Capens157ba262019-12-10 17:49:14 -0500318 for(Ice::Inst &inst : Ice::reverse_range(basicBlock->getInsts()))
Nicolas Capens2ae9d742016-11-24 14:43:05 -0500319 {
Nicolas Capens157ba262019-12-10 17:49:14 -0500320 if(inst.isDeleted())
321 {
322 continue;
323 }
324
325 if(isDead(&inst))
326 {
327 deleteInstruction(&inst);
328 modified = true;
329 }
330 }
331 }
Ben Clayton713b8d32019-12-17 20:37:56 +0000332 } while(modified);
Nicolas Capens157ba262019-12-10 17:49:14 -0500333}
334
335void Optimizer::eliminateUnitializedLoads()
336{
337 Ice::CfgNode *entryBlock = function->getEntryNode();
338
339 for(Ice::Inst &alloca : entryBlock->getInsts())
340 {
341 if(alloca.isDeleted())
342 {
343 continue;
344 }
345
346 if(!llvm::isa<Ice::InstAlloca>(alloca))
347 {
Ben Clayton713b8d32019-12-17 20:37:56 +0000348 break; // Allocas are all at the top
Nicolas Capens157ba262019-12-10 17:49:14 -0500349 }
350
351 Ice::Operand *address = alloca.getDest();
352
353 if(!hasUses(address))
354 {
355 continue;
356 }
357
358 const auto &addressUses = *getUses(address);
359
360 if(!addressUses.areOnlyLoadStore())
361 {
362 continue;
363 }
364
365 if(addressUses.stores.empty())
366 {
367 for(Ice::Inst *load : addressUses.loads)
368 {
369 Ice::Variable *loadData = load->getDest();
370
371 if(hasUses(loadData))
372 {
373 for(Ice::Inst *use : *getUses(loadData))
374 {
375 for(Ice::SizeT i = 0; i < use->getSrcSize(); i++)
376 {
377 if(use->getSrc(i) == loadData)
378 {
379 auto *undef = context->getConstantUndef(loadData->getType());
380
381 use->replaceSource(i, undef);
382 }
383 }
384 }
385
386 setUses(loadData, nullptr);
387 }
388
389 load->setDeleted();
390 }
391
392 alloca.setDeleted();
393 setUses(address, nullptr);
394 }
395 }
396}
397
398void Optimizer::eliminateLoadsFollowingSingleStore()
399{
400 Ice::CfgNode *entryBlock = function->getEntryNode();
401
402 for(Ice::Inst &alloca : entryBlock->getInsts())
403 {
404 if(alloca.isDeleted())
405 {
406 continue;
407 }
408
409 if(!llvm::isa<Ice::InstAlloca>(alloca))
410 {
Ben Clayton713b8d32019-12-17 20:37:56 +0000411 break; // Allocas are all at the top
Nicolas Capens157ba262019-12-10 17:49:14 -0500412 }
413
414 Ice::Operand *address = alloca.getDest();
415
416 if(!hasUses(address))
417 {
418 continue;
419 }
420
421 auto &addressUses = *getUses(address);
422
423 if(!addressUses.areOnlyLoadStore())
424 {
425 continue;
426 }
427
428 if(addressUses.stores.size() == 1)
429 {
430 Ice::Inst *store = addressUses.stores[0];
Nicolas Capens673a7fe2021-02-05 15:03:22 -0500431 Ice::Operand *storeValue = store->getData();
Nicolas Capens157ba262019-12-10 17:49:14 -0500432
Nicolas Capens3ab17bd2021-02-10 16:41:31 -0500433 auto instIterator = store->getIterator();
434 auto basicBlockEnd = getNode(store)->getInsts().end();
435
436 while(++instIterator != basicBlockEnd)
Nicolas Capens157ba262019-12-10 17:49:14 -0500437 {
Nicolas Capens3ab17bd2021-02-10 16:41:31 -0500438 Ice::Inst *load = &*instIterator;
439
Nicolas Capens157ba262019-12-10 17:49:14 -0500440 if(load->isDeleted() || !isLoad(*load))
441 {
442 continue;
443 }
444
Nicolas Capens673a7fe2021-02-05 15:03:22 -0500445 if(load->getLoadAddress() != address)
Nicolas Capens157ba262019-12-10 17:49:14 -0500446 {
447 continue;
448 }
449
450 if(!loadTypeMatchesStore(load, store))
451 {
452 continue;
453 }
454
455 replace(load, storeValue);
456
457 for(size_t i = 0; i < addressUses.loads.size(); i++)
458 {
459 if(addressUses.loads[i] == load)
460 {
461 addressUses.loads[i] = addressUses.loads.back();
462 addressUses.loads.pop_back();
463 break;
464 }
465 }
466
467 for(size_t i = 0; i < addressUses.size(); i++)
468 {
469 if(addressUses[i] == load)
470 {
471 addressUses[i] = addressUses.back();
472 addressUses.pop_back();
473 break;
474 }
475 }
476
477 if(addressUses.size() == 1)
478 {
479 assert(addressUses[0] == store);
480
481 alloca.setDeleted();
482 store->setDeleted();
483 setUses(address, nullptr);
484
485 if(hasUses(storeValue))
486 {
487 auto &valueUses = *getUses(storeValue);
488
489 for(size_t i = 0; i < valueUses.size(); i++)
490 {
491 if(valueUses[i] == store)
492 {
493 valueUses[i] = valueUses.back();
494 valueUses.pop_back();
495 break;
496 }
497 }
498
499 if(valueUses.empty())
500 {
501 setUses(storeValue, nullptr);
502 }
503 }
504
505 break;
506 }
507 }
508 }
509 }
510}
511
512void Optimizer::optimizeStoresInSingleBasicBlock()
513{
514 Ice::CfgNode *entryBlock = function->getEntryNode();
515
Ben Clayton713b8d32019-12-17 20:37:56 +0000516 std::vector<std::vector<LoadStoreInst> *> allocatedVectors;
Nicolas Capens157ba262019-12-10 17:49:14 -0500517
518 for(Ice::Inst &alloca : entryBlock->getInsts())
519 {
520 if(alloca.isDeleted())
521 {
522 continue;
523 }
524
525 if(!llvm::isa<Ice::InstAlloca>(alloca))
526 {
Ben Clayton713b8d32019-12-17 20:37:56 +0000527 break; // Allocas are all at the top
Nicolas Capens157ba262019-12-10 17:49:14 -0500528 }
529
530 Ice::Operand *address = alloca.getDest();
531
532 if(!hasUses(address))
533 {
534 continue;
535 }
536
537 const auto &addressUses = *getUses(address);
538
539 if(!addressUses.areOnlyLoadStore())
540 {
541 continue;
542 }
543
544 Ice::CfgNode *singleBasicBlock = getNode(addressUses.stores[0]);
545
546 for(size_t i = 1; i < addressUses.stores.size(); i++)
547 {
548 Ice::Inst *store = addressUses.stores[i];
549 if(getNode(store) != singleBasicBlock)
550 {
551 singleBasicBlock = nullptr;
552 break;
553 }
554 }
555
556 if(singleBasicBlock)
557 {
558 if(!hasLoadStoreInsts(singleBasicBlock))
559 {
Ben Clayton713b8d32019-12-17 20:37:56 +0000560 std::vector<LoadStoreInst> *loadStoreInstVector = new std::vector<LoadStoreInst>();
Nicolas Capens157ba262019-12-10 17:49:14 -0500561 setLoadStoreInsts(singleBasicBlock, loadStoreInstVector);
562 allocatedVectors.push_back(loadStoreInstVector);
563 for(Ice::Inst &inst : singleBasicBlock->getInsts())
Nicolas Capensc9d70d52016-12-12 15:07:39 -0500564 {
565 if(inst.isDeleted())
566 {
567 continue;
568 }
Nicolas Capens2ae9d742016-11-24 14:43:05 -0500569
Nicolas Capens157ba262019-12-10 17:49:14 -0500570 bool isStoreInst = isStore(inst);
571 bool isLoadInst = isLoad(inst);
572
573 if(isStoreInst || isLoadInst)
Nicolas Capensc9d70d52016-12-12 15:07:39 -0500574 {
Nicolas Capens157ba262019-12-10 17:49:14 -0500575 loadStoreInstVector->push_back(LoadStoreInst(&inst, isStoreInst));
Nicolas Capensc9d70d52016-12-12 15:07:39 -0500576 }
577 }
Nicolas Capens2ae9d742016-11-24 14:43:05 -0500578 }
Nicolas Capens2ae9d742016-11-24 14:43:05 -0500579
Nicolas Capens157ba262019-12-10 17:49:14 -0500580 Ice::Inst *store = nullptr;
581 Ice::Operand *storeValue = nullptr;
582 bool unmatchedLoads = false;
Nicolas Capensf4452fc2016-12-12 13:08:06 -0500583
Ben Clayton713b8d32019-12-17 20:37:56 +0000584 for(auto &loadStoreInst : getLoadStoreInsts(singleBasicBlock))
Nicolas Capensf4452fc2016-12-12 13:08:06 -0500585 {
Ben Clayton713b8d32019-12-17 20:37:56 +0000586 Ice::Inst *inst = loadStoreInst.inst;
Nicolas Capensf4452fc2016-12-12 13:08:06 -0500587
Nicolas Capens157ba262019-12-10 17:49:14 -0500588 if((loadStoreInst.address != address) || inst->isDeleted())
Nicolas Capens2ae9d742016-11-24 14:43:05 -0500589 {
590 continue;
591 }
592
Nicolas Capens157ba262019-12-10 17:49:14 -0500593 if(loadStoreInst.isStore)
Alexis Hetu932640b2018-06-20 15:35:53 -0400594 {
Nicolas Capens157ba262019-12-10 17:49:14 -0500595 // New store found. If we had a previous one, try to eliminate it.
596 if(store && !unmatchedLoads)
Nicolas Capens2ae9d742016-11-24 14:43:05 -0500597 {
Nicolas Capens157ba262019-12-10 17:49:14 -0500598 // If the previous store is wider than the new one, we can't eliminate it
599 // because there could be a wide load reading its non-overwritten data.
600 if(storeSize(inst) >= storeSize(store))
Nicolas Capens2ae9d742016-11-24 14:43:05 -0500601 {
Nicolas Capens157ba262019-12-10 17:49:14 -0500602 deleteInstruction(store);
Nicolas Capens2ae9d742016-11-24 14:43:05 -0500603 }
604 }
605
Nicolas Capens157ba262019-12-10 17:49:14 -0500606 store = inst;
Nicolas Capens673a7fe2021-02-05 15:03:22 -0500607 storeValue = store->getData();
Nicolas Capens157ba262019-12-10 17:49:14 -0500608 unmatchedLoads = false;
609 }
610 else
611 {
612 if(!loadTypeMatchesStore(inst, store))
Nicolas Capens2ae9d742016-11-24 14:43:05 -0500613 {
Nicolas Capens157ba262019-12-10 17:49:14 -0500614 unmatchedLoads = true;
615 continue;
Nicolas Capens2ae9d742016-11-24 14:43:05 -0500616 }
Nicolas Capens157ba262019-12-10 17:49:14 -0500617
618 replace(inst, storeValue);
Nicolas Capens2ae9d742016-11-24 14:43:05 -0500619 }
620 }
621 }
622 }
Nicolas Capensf4452fc2016-12-12 13:08:06 -0500623
Nicolas Capens157ba262019-12-10 17:49:14 -0500624 for(auto loadStoreInstVector : allocatedVectors)
Nicolas Capense205c3d2016-11-30 15:14:28 -0500625 {
Nicolas Capens157ba262019-12-10 17:49:14 -0500626 delete loadStoreInstVector;
627 }
628}
Nicolas Capense205c3d2016-11-30 15:14:28 -0500629
Nicolas Capens3ffbd622021-02-06 00:35:38 -0500630// Iterate through basic blocks to propagate stores to subsequent loads.
631void Optimizer::optimizeSingleBasicBlockLoadsStores()
632{
633 for(Ice::CfgNode *block : function->getNodes())
634 {
635 // For each stack variable keep track of the last store instruction.
636 std::unordered_map<const Ice::InstAlloca *, const Ice::Inst *> lastStoreTo;
637
638 for(Ice::Inst &inst : block->getInsts())
639 {
640 if(inst.isDeleted())
641 {
642 continue;
643 }
644
645 if(isStore(inst))
646 {
647 Ice::Operand *address = inst.getStoreAddress();
648
649 if(Ice::InstAlloca *alloca = allocaOf(address))
650 {
651 // Only consider this store for propagation if its address is not used as
652 // a pointer which could be used for indirect stores.
653 if(getUses(address)->areOnlyLoadStore())
654 {
655 lastStoreTo[alloca] = &inst;
656 }
657 }
658 }
659 else if(isLoad(inst))
660 {
661 if(Ice::InstAlloca *alloca = allocaOf(inst.getLoadAddress()))
662 {
663 auto entry = lastStoreTo.find(alloca);
664 if(entry != lastStoreTo.end())
665 {
666 const Ice::Inst *store = entry->second;
667
668 // Conservatively check that the types match.
669 // TODO(b/179668593): Also valid to propagate if the load is smaller or equal in size to the store.
670 if(loadTypeMatchesStore(&inst, store))
671 {
672 replace(&inst, store->getData());
673 }
674 }
675 }
676 }
677 }
678 }
679
680 // This can leave some dead instructions. Specifically stores.
681 // TODO((b/179668593): Eliminate stores superseded by subsequent stores?
682 eliminateDeadCode();
683}
684
Nicolas Capens157ba262019-12-10 17:49:14 -0500685void Optimizer::analyzeUses(Ice::Cfg *function)
686{
687 for(Ice::CfgNode *basicBlock : function->getNodes())
688 {
689 for(Ice::Inst &instruction : basicBlock->getInsts())
Nicolas Capense205c3d2016-11-30 15:14:28 -0500690 {
Nicolas Capens157ba262019-12-10 17:49:14 -0500691 if(instruction.isDeleted())
Nicolas Capense205c3d2016-11-30 15:14:28 -0500692 {
Nicolas Capens157ba262019-12-10 17:49:14 -0500693 continue;
694 }
Alexis Hetu932640b2018-06-20 15:35:53 -0400695
Nicolas Capens157ba262019-12-10 17:49:14 -0500696 setNode(&instruction, basicBlock);
697 if(instruction.getDest())
698 {
699 setDefinition(instruction.getDest(), &instruction);
700 }
701
702 for(Ice::SizeT i = 0; i < instruction.getSrcSize(); i++)
703 {
704 Ice::SizeT unique = 0;
705 for(; unique < i; unique++)
Nicolas Capense205c3d2016-11-30 15:14:28 -0500706 {
Nicolas Capens157ba262019-12-10 17:49:14 -0500707 if(instruction.getSrc(i) == instruction.getSrc(unique))
Alexis Hetu932640b2018-06-20 15:35:53 -0400708 {
Nicolas Capensf4452fc2016-12-12 13:08:06 -0500709 break;
710 }
711 }
712
Nicolas Capens157ba262019-12-10 17:49:14 -0500713 if(i == unique)
Nicolas Capensf4452fc2016-12-12 13:08:06 -0500714 {
Nicolas Capens157ba262019-12-10 17:49:14 -0500715 Ice::Operand *src = instruction.getSrc(i);
716 getUses(src)->insert(src, &instruction);
Nicolas Capensf4452fc2016-12-12 13:08:06 -0500717 }
Nicolas Capensf4452fc2016-12-12 13:08:06 -0500718 }
719 }
720 }
Nicolas Capens2ae9d742016-11-24 14:43:05 -0500721}
722
Nicolas Capens157ba262019-12-10 17:49:14 -0500723void Optimizer::replace(Ice::Inst *instruction, Ice::Operand *newValue)
Nicolas Capens2ae9d742016-11-24 14:43:05 -0500724{
Nicolas Capens157ba262019-12-10 17:49:14 -0500725 Ice::Variable *oldValue = instruction->getDest();
Nicolas Capens2ae9d742016-11-24 14:43:05 -0500726
Nicolas Capens157ba262019-12-10 17:49:14 -0500727 if(!newValue)
728 {
729 newValue = context->getConstantUndef(oldValue->getType());
Nicolas Capens2ae9d742016-11-24 14:43:05 -0500730 }
Nicolas Capens157ba262019-12-10 17:49:14 -0500731
732 if(hasUses(oldValue))
733 {
734 for(Ice::Inst *use : *getUses(oldValue))
735 {
Ben Clayton713b8d32019-12-17 20:37:56 +0000736 assert(!use->isDeleted()); // Should have been removed from uses already
Nicolas Capens157ba262019-12-10 17:49:14 -0500737
738 for(Ice::SizeT i = 0; i < use->getSrcSize(); i++)
739 {
740 if(use->getSrc(i) == oldValue)
741 {
742 use->replaceSource(i, newValue);
743 }
744 }
745
746 getUses(newValue)->insert(newValue, use);
747 }
748
749 setUses(oldValue, nullptr);
750 }
751
752 deleteInstruction(instruction);
753}
754
755void Optimizer::deleteInstruction(Ice::Inst *instruction)
756{
757 if(!instruction || instruction->isDeleted())
758 {
759 return;
760 }
761
Nicolas Capens0cfc0432021-02-05 15:18:42 -0500762 assert(!instruction->getDest() || getUses(instruction->getDest())->empty());
Nicolas Capens157ba262019-12-10 17:49:14 -0500763 instruction->setDeleted();
764
765 for(Ice::SizeT i = 0; i < instruction->getSrcSize(); i++)
766 {
767 Ice::Operand *src = instruction->getSrc(i);
768
769 if(hasUses(src))
770 {
771 auto &srcUses = *getUses(src);
772
773 srcUses.erase(instruction);
774
775 if(srcUses.empty())
776 {
777 setUses(src, nullptr);
778
779 if(Ice::Variable *var = llvm::dyn_cast<Ice::Variable>(src))
780 {
781 deleteInstruction(getDefinition(var));
782 }
783 }
784 }
785 }
786}
787
788bool Optimizer::isDead(Ice::Inst *instruction)
789{
790 Ice::Variable *dest = instruction->getDest();
791
792 if(dest)
793 {
794 return (!hasUses(dest) || getUses(dest)->empty()) && !instruction->hasSideEffects();
795 }
796 else if(isStore(*instruction))
797 {
Nicolas Capens673a7fe2021-02-05 15:03:22 -0500798 if(Ice::Variable *address = llvm::dyn_cast<Ice::Variable>(instruction->getStoreAddress()))
Nicolas Capens157ba262019-12-10 17:49:14 -0500799 {
800 Ice::Inst *def = getDefinition(address);
801
802 if(def && llvm::isa<Ice::InstAlloca>(def))
803 {
804 if(hasUses(address))
805 {
Ben Clayton713b8d32019-12-17 20:37:56 +0000806 Optimizer::Uses *uses = getUses(address);
807 return uses->size() == uses->stores.size(); // Dead if all uses are stores
Nicolas Capens157ba262019-12-10 17:49:14 -0500808 }
809 else
810 {
Ben Clayton713b8d32019-12-17 20:37:56 +0000811 return true; // No uses
Nicolas Capens157ba262019-12-10 17:49:14 -0500812 }
813 }
814 }
815 }
816
817 return false;
818}
819
Nicolas Capens0cfc0432021-02-05 15:18:42 -0500820bool Optimizer::isStaticallyIndexedArray(Ice::Operand *allocaAddress)
821{
822 auto &uses = *getUses(allocaAddress);
823
824 for(auto *use : uses)
825 {
826 // Direct load from base address.
827 if(isLoad(*use) && use->getLoadAddress() == allocaAddress)
828 {
829 continue; // This is fine.
830 }
831
832 if(isStore(*use))
833 {
834 // Can either be the address we're storing to, or the data we're storing.
835 if(use->getStoreAddress() == allocaAddress)
836 {
837 continue;
838 }
839 else
840 {
841 // propagateAlloca() eliminates most of the stores of the address itself.
842 // For the cases it doesn't handle, assume SRoA is not feasible.
843 return false;
844 }
845 }
846
847 // Pointer arithmetic is fine if it only uses constants.
848 auto *arithmetic = llvm::dyn_cast<Ice::InstArithmetic>(use);
849 if(arithmetic && arithmetic->getOp() == Ice::InstArithmetic::Add)
850 {
851 auto *rhs = arithmetic->getSrc(1);
852
853 if(llvm::isa<Ice::Constant>(rhs))
854 {
855 continue;
856 }
857 }
858
859 // If there's any other type of use, bail out.
860 return false;
861 }
862
863 return true;
864}
865
Nicolas Capens3ffbd622021-02-06 00:35:38 -0500866Ice::InstAlloca *Optimizer::allocaOf(Ice::Operand *address)
867{
868 Ice::Variable *addressVar = llvm::dyn_cast<Ice::Variable>(address);
869 Ice::Inst *def = addressVar ? getDefinition(addressVar) : nullptr;
870 Ice::InstAlloca *alloca = def ? llvm::dyn_cast<Ice::InstAlloca>(def) : nullptr;
871
872 return alloca;
873}
874
Nicolas Capens33a77f72021-02-08 15:04:38 -0500875const Ice::InstIntrinsic *Optimizer::asLoadSubVector(const Ice::Inst *instruction)
Nicolas Capens157ba262019-12-10 17:49:14 -0500876{
Nicolas Capens33a77f72021-02-08 15:04:38 -0500877 if(auto *instrinsic = llvm::dyn_cast<Ice::InstIntrinsic>(instruction))
Nicolas Capens157ba262019-12-10 17:49:14 -0500878 {
Nicolas Capens673a7fe2021-02-05 15:03:22 -0500879 if(instrinsic->getIntrinsicID() == Ice::Intrinsics::LoadSubVector)
Nicolas Capens157ba262019-12-10 17:49:14 -0500880 {
881 return instrinsic;
882 }
883 }
884
885 return nullptr;
886}
887
Nicolas Capens33a77f72021-02-08 15:04:38 -0500888const Ice::InstIntrinsic *Optimizer::asStoreSubVector(const Ice::Inst *instruction)
Nicolas Capens157ba262019-12-10 17:49:14 -0500889{
Nicolas Capens33a77f72021-02-08 15:04:38 -0500890 if(auto *instrinsic = llvm::dyn_cast<Ice::InstIntrinsic>(instruction))
Nicolas Capens157ba262019-12-10 17:49:14 -0500891 {
Nicolas Capens673a7fe2021-02-05 15:03:22 -0500892 if(instrinsic->getIntrinsicID() == Ice::Intrinsics::StoreSubVector)
Nicolas Capens157ba262019-12-10 17:49:14 -0500893 {
894 return instrinsic;
895 }
896 }
897
898 return nullptr;
899}
900
901bool Optimizer::isLoad(const Ice::Inst &instruction)
902{
903 if(llvm::isa<Ice::InstLoad>(&instruction))
904 {
905 return true;
906 }
907
908 return asLoadSubVector(&instruction) != nullptr;
909}
910
911bool Optimizer::isStore(const Ice::Inst &instruction)
912{
913 if(llvm::isa<Ice::InstStore>(&instruction))
914 {
915 return true;
916 }
917
918 return asStoreSubVector(&instruction) != nullptr;
919}
920
Nicolas Capens157ba262019-12-10 17:49:14 -0500921std::size_t Optimizer::storeSize(const Ice::Inst *store)
922{
923 assert(isStore(*store));
924
925 if(auto *instStore = llvm::dyn_cast<Ice::InstStore>(store))
926 {
927 return Ice::typeWidthInBytes(instStore->getData()->getType());
928 }
929
930 if(auto *storeSubVector = asStoreSubVector(store))
931 {
932 return llvm::cast<Ice::ConstantInteger32>(storeSubVector->getSrc(3))->getValue();
933 }
934
935 return 0;
936}
937
938bool Optimizer::loadTypeMatchesStore(const Ice::Inst *load, const Ice::Inst *store)
939{
940 if(!load || !store)
941 {
942 return false;
943 }
944
945 assert(isLoad(*load) && isStore(*store));
Nicolas Capens673a7fe2021-02-05 15:03:22 -0500946 assert(load->getLoadAddress() == store->getStoreAddress());
Nicolas Capens157ba262019-12-10 17:49:14 -0500947
Nicolas Capens673a7fe2021-02-05 15:03:22 -0500948 if(store->getData()->getType() != load->getDest()->getType())
Nicolas Capens157ba262019-12-10 17:49:14 -0500949 {
Nicolas Capens673a7fe2021-02-05 15:03:22 -0500950 return false;
Nicolas Capens157ba262019-12-10 17:49:14 -0500951 }
952
953 if(auto *storeSubVector = asStoreSubVector(store))
954 {
955 if(auto *loadSubVector = asLoadSubVector(load))
956 {
Nicolas Capens673a7fe2021-02-05 15:03:22 -0500957 // Check for matching sub-vector width.
958 return llvm::cast<Ice::ConstantInteger32>(storeSubVector->getSrc(2))->getValue() ==
959 llvm::cast<Ice::ConstantInteger32>(loadSubVector->getSrc(1))->getValue();
Nicolas Capens157ba262019-12-10 17:49:14 -0500960 }
961 }
962
Nicolas Capens673a7fe2021-02-05 15:03:22 -0500963 return true;
Nicolas Capens157ba262019-12-10 17:49:14 -0500964}
965
Nicolas Capens54313fb2021-02-19 14:26:27 -0500966void Optimizer::collectDiagnostics()
967{
968 if(report)
969 {
970 *report = {};
971
972 for(auto *basicBlock : function->getNodes())
973 {
974 for(auto &inst : basicBlock->getInsts())
975 {
976 if(inst.isDeleted())
977 {
978 continue;
979 }
980
981 if(llvm::isa<Ice::InstAlloca>(inst))
982 {
983 report->allocas++;
984 }
985 else if(isLoad(inst))
986 {
987 report->loads++;
988 }
989 else if(isStore(inst))
990 {
991 report->stores++;
992 }
993 }
994 }
995 }
996}
997
Ben Clayton713b8d32019-12-17 20:37:56 +0000998Optimizer::Uses *Optimizer::getUses(Ice::Operand *operand)
Nicolas Capens157ba262019-12-10 17:49:14 -0500999{
Ben Clayton713b8d32019-12-17 20:37:56 +00001000 Optimizer::Uses *uses = (Optimizer::Uses *)operand->Ice::Operand::getExternalData();
Nicolas Capens157ba262019-12-10 17:49:14 -05001001 if(!uses)
1002 {
1003 uses = new Optimizer::Uses;
1004 setUses(operand, uses);
Antonio Maiorano5ba2a5b2020-01-17 15:29:37 -05001005 operandsWithUses.push_back(operand);
Nicolas Capens157ba262019-12-10 17:49:14 -05001006 }
1007 return uses;
1008}
1009
Ben Clayton713b8d32019-12-17 20:37:56 +00001010void Optimizer::setUses(Ice::Operand *operand, Optimizer::Uses *uses)
Nicolas Capens157ba262019-12-10 17:49:14 -05001011{
Antonio Maiorano614a4d42020-01-29 13:33:02 -05001012 if(auto *oldUses = reinterpret_cast<Optimizer::Uses *>(operand->Ice::Operand::getExternalData()))
1013 {
1014 delete oldUses;
1015 }
1016
Nicolas Capens157ba262019-12-10 17:49:14 -05001017 operand->Ice::Operand::setExternalData(uses);
1018}
1019
Ben Clayton713b8d32019-12-17 20:37:56 +00001020bool Optimizer::hasUses(Ice::Operand *operand) const
Nicolas Capens157ba262019-12-10 17:49:14 -05001021{
1022 return operand->Ice::Operand::getExternalData() != nullptr;
1023}
1024
Ben Clayton713b8d32019-12-17 20:37:56 +00001025Ice::CfgNode *Optimizer::getNode(Ice::Inst *inst)
Nicolas Capens157ba262019-12-10 17:49:14 -05001026{
Ben Clayton713b8d32019-12-17 20:37:56 +00001027 return (Ice::CfgNode *)inst->Ice::Inst::getExternalData();
Nicolas Capens157ba262019-12-10 17:49:14 -05001028}
1029
Ben Clayton713b8d32019-12-17 20:37:56 +00001030void Optimizer::setNode(Ice::Inst *inst, Ice::CfgNode *node)
Nicolas Capens157ba262019-12-10 17:49:14 -05001031{
1032 inst->Ice::Inst::setExternalData(node);
1033}
1034
Ben Clayton713b8d32019-12-17 20:37:56 +00001035Ice::Inst *Optimizer::getDefinition(Ice::Variable *var)
Nicolas Capens157ba262019-12-10 17:49:14 -05001036{
Ben Clayton713b8d32019-12-17 20:37:56 +00001037 return (Ice::Inst *)var->Ice::Variable::getExternalData();
Nicolas Capens157ba262019-12-10 17:49:14 -05001038}
1039
Ben Clayton713b8d32019-12-17 20:37:56 +00001040void Optimizer::setDefinition(Ice::Variable *var, Ice::Inst *inst)
Nicolas Capens157ba262019-12-10 17:49:14 -05001041{
1042 var->Ice::Variable::setExternalData(inst);
1043}
1044
Ben Clayton713b8d32019-12-17 20:37:56 +00001045const std::vector<Optimizer::LoadStoreInst> &Optimizer::getLoadStoreInsts(Ice::CfgNode *node)
Nicolas Capens157ba262019-12-10 17:49:14 -05001046{
Ben Clayton713b8d32019-12-17 20:37:56 +00001047 return *((const std::vector<LoadStoreInst> *)node->Ice::CfgNode::getExternalData());
Nicolas Capens157ba262019-12-10 17:49:14 -05001048}
1049
Ben Clayton713b8d32019-12-17 20:37:56 +00001050void Optimizer::setLoadStoreInsts(Ice::CfgNode *node, std::vector<LoadStoreInst> *insts)
Nicolas Capens157ba262019-12-10 17:49:14 -05001051{
1052 node->Ice::CfgNode::setExternalData(insts);
1053}
1054
Ben Clayton713b8d32019-12-17 20:37:56 +00001055bool Optimizer::hasLoadStoreInsts(Ice::CfgNode *node) const
Nicolas Capens157ba262019-12-10 17:49:14 -05001056{
1057 return node->Ice::CfgNode::getExternalData() != nullptr;
1058}
1059
1060bool Optimizer::Uses::areOnlyLoadStore() const
1061{
1062 return size() == (loads.size() + stores.size());
1063}
1064
1065void Optimizer::Uses::insert(Ice::Operand *value, Ice::Inst *instruction)
1066{
1067 push_back(instruction);
1068
1069 if(isLoad(*instruction))
1070 {
Nicolas Capens673a7fe2021-02-05 15:03:22 -05001071 if(value == instruction->getLoadAddress())
Nicolas Capens157ba262019-12-10 17:49:14 -05001072 {
1073 loads.push_back(instruction);
1074 }
1075 }
1076 else if(isStore(*instruction))
1077 {
Nicolas Capens673a7fe2021-02-05 15:03:22 -05001078 if(value == instruction->getStoreAddress())
Nicolas Capens157ba262019-12-10 17:49:14 -05001079 {
1080 stores.push_back(instruction);
1081 }
1082 }
1083}
1084
1085void Optimizer::Uses::erase(Ice::Inst *instruction)
1086{
1087 auto &uses = *this;
1088
1089 for(size_t i = 0; i < uses.size(); i++)
1090 {
1091 if(uses[i] == instruction)
1092 {
1093 uses[i] = back();
1094 pop_back();
1095
1096 for(size_t i = 0; i < loads.size(); i++)
1097 {
1098 if(loads[i] == instruction)
1099 {
1100 loads[i] = loads.back();
1101 loads.pop_back();
1102 break;
1103 }
1104 }
1105
1106 for(size_t i = 0; i < stores.size(); i++)
1107 {
1108 if(stores[i] == instruction)
1109 {
1110 stores[i] = stores.back();
1111 stores.pop_back();
1112 break;
1113 }
1114 }
1115
1116 break;
1117 }
1118 }
1119}
1120
Ben Clayton713b8d32019-12-17 20:37:56 +00001121} // anonymous namespace
Nicolas Capens157ba262019-12-10 17:49:14 -05001122
1123namespace rr {
1124
Nicolas Capens54313fb2021-02-19 14:26:27 -05001125void optimize(Ice::Cfg *function, Nucleus::OptimizerReport *report)
Nicolas Capens157ba262019-12-10 17:49:14 -05001126{
Nicolas Capens54313fb2021-02-19 14:26:27 -05001127 Optimizer optimizer(report);
Nicolas Capens157ba262019-12-10 17:49:14 -05001128
1129 optimizer.run(function);
1130}
1131
Antonio Maiorano614a4d42020-01-29 13:33:02 -05001132} // namespace rr