blob: b4872be32b4e42d1941ce7dbf74fee1c6622b29b [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 Capens6ddeb6b2021-02-22 09:41:13 -050039 void eliminateUnitializedLoads();
Nicolas Capens0b506e12021-02-04 17:00:08 -050040 void propagateAlloca();
Nicolas Capens0cfc0432021-02-05 15:18:42 -050041 void performScalarReplacementOfAggregates();
Nicolas Capens3ffbd622021-02-06 00:35:38 -050042 void optimizeSingleBasicBlockLoadsStores();
Nicolas Capens157ba262019-12-10 17:49:14 -050043
44 void replace(Ice::Inst *instruction, Ice::Operand *newValue);
45 void deleteInstruction(Ice::Inst *instruction);
46 bool isDead(Ice::Inst *instruction);
Nicolas Capens0cfc0432021-02-05 15:18:42 -050047 bool isStaticallyIndexedArray(Ice::Operand *allocaAddress);
Nicolas Capens3ffbd622021-02-06 00:35:38 -050048 Ice::InstAlloca *allocaOf(Ice::Operand *address);
Nicolas Capens157ba262019-12-10 17:49:14 -050049
Nicolas Capens33a77f72021-02-08 15:04:38 -050050 static const Ice::InstIntrinsic *asLoadSubVector(const Ice::Inst *instruction);
51 static const Ice::InstIntrinsic *asStoreSubVector(const Ice::Inst *instruction);
Nicolas Capens157ba262019-12-10 17:49:14 -050052 static bool isLoad(const Ice::Inst &instruction);
53 static bool isStore(const Ice::Inst &instruction);
Nicolas Capens157ba262019-12-10 17:49:14 -050054 static bool loadTypeMatchesStore(const Ice::Inst *load, const Ice::Inst *store);
Nicolas Capens008247a2021-02-07 07:27:10 -050055 static bool storeTypeMatchesStore(const Ice::Inst *store1, const Ice::Inst *store2);
Nicolas Capens157ba262019-12-10 17:49:14 -050056
Nicolas Capens54313fb2021-02-19 14:26:27 -050057 void collectDiagnostics();
58
Nicolas Capens157ba262019-12-10 17:49:14 -050059 Ice::Cfg *function;
60 Ice::GlobalContext *context;
61
Ben Clayton713b8d32019-12-17 20:37:56 +000062 struct Uses : std::vector<Ice::Inst *>
Nicolas Capens2ae9d742016-11-24 14:43:05 -050063 {
Nicolas Capens157ba262019-12-10 17:49:14 -050064 bool areOnlyLoadStore() const;
65 void insert(Ice::Operand *value, Ice::Inst *instruction);
66 void erase(Ice::Inst *instruction);
Nicolas Capens2ae9d742016-11-24 14:43:05 -050067
Ben Clayton713b8d32019-12-17 20:37:56 +000068 std::vector<Ice::Inst *> loads;
69 std::vector<Ice::Inst *> stores;
Nicolas Capens2ae9d742016-11-24 14:43:05 -050070 };
71
Nicolas Capens157ba262019-12-10 17:49:14 -050072 struct LoadStoreInst
Nicolas Capens2ae9d742016-11-24 14:43:05 -050073 {
Ben Clayton713b8d32019-12-17 20:37:56 +000074 LoadStoreInst(Ice::Inst *inst, bool isStore)
75 : inst(inst)
Nicolas Capens673a7fe2021-02-05 15:03:22 -050076 , address(isStore ? inst->getStoreAddress() : inst->getLoadAddress())
Ben Clayton713b8d32019-12-17 20:37:56 +000077 , isStore(isStore)
Alexis Hetu932640b2018-06-20 15:35:53 -040078 {
Alexis Hetu932640b2018-06-20 15:35:53 -040079 }
Nicolas Capens2ae9d742016-11-24 14:43:05 -050080
Ben Clayton713b8d32019-12-17 20:37:56 +000081 Ice::Inst *inst;
Nicolas Capens157ba262019-12-10 17:49:14 -050082 Ice::Operand *address;
83 bool isStore;
84 };
85
Ben Clayton713b8d32019-12-17 20:37:56 +000086 Optimizer::Uses *getUses(Ice::Operand *);
87 void setUses(Ice::Operand *, Optimizer::Uses *);
88 bool hasUses(Ice::Operand *) const;
Nicolas Capens157ba262019-12-10 17:49:14 -050089
Ben Clayton713b8d32019-12-17 20:37:56 +000090 Ice::Inst *getDefinition(Ice::Variable *);
91 void setDefinition(Ice::Variable *, Ice::Inst *);
Nicolas Capens157ba262019-12-10 17:49:14 -050092
Antonio Maiorano5ba2a5b2020-01-17 15:29:37 -050093 std::vector<Ice::Operand *> operandsWithUses;
Nicolas Capens54313fb2021-02-19 14:26:27 -050094
95 rr::Nucleus::OptimizerReport *report = nullptr;
Nicolas Capens157ba262019-12-10 17:49:14 -050096};
97
98void Optimizer::run(Ice::Cfg *function)
99{
100 this->function = function;
101 this->context = function->getContext();
102
103 analyzeUses(function);
104
Nicolas Capens0b506e12021-02-04 17:00:08 -0500105 // Start by eliminating any dead code, to avoid redundant work for the
106 // subsequent optimization passes.
Nicolas Capens157ba262019-12-10 17:49:14 -0500107 eliminateDeadCode();
Nicolas Capens6ddeb6b2021-02-22 09:41:13 -0500108 eliminateUnitializedLoads();
Nicolas Capens0b506e12021-02-04 17:00:08 -0500109
110 // Eliminate allocas which store the address of other allocas.
111 propagateAlloca();
112
Nicolas Capens0cfc0432021-02-05 15:18:42 -0500113 // Replace arrays with individual elements if only statically indexed.
114 performScalarReplacementOfAggregates();
115
Nicolas Capens3ffbd622021-02-06 00:35:38 -0500116 // Iterate through basic blocks to propagate loads following stores.
117 optimizeSingleBasicBlockLoadsStores();
118
Antonio Maiorano5ba2a5b2020-01-17 15:29:37 -0500119 for(auto operand : operandsWithUses)
Nicolas Capens2ae9d742016-11-24 14:43:05 -0500120 {
Antonio Maiorano614a4d42020-01-29 13:33:02 -0500121 // Deletes the Uses instance on the operand
122 setUses(operand, nullptr);
Nicolas Capens157ba262019-12-10 17:49:14 -0500123 }
Antonio Maiorano5ba2a5b2020-01-17 15:29:37 -0500124 operandsWithUses.clear();
Nicolas Capens54313fb2021-02-19 14:26:27 -0500125
126 collectDiagnostics();
Nicolas Capens157ba262019-12-10 17:49:14 -0500127}
128
Nicolas Capens0b506e12021-02-04 17:00:08 -0500129// Eliminates allocas which store the address of other allocas.
130void 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
Nicolas Capens0cfc0432021-02-05 15:18:42 -0500186Ice::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.
199void 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
Nicolas Capens157ba262019-12-10 17:49:14 -0500297void Optimizer::eliminateDeadCode()
298{
299 bool modified;
300 do
301 {
302 modified = false;
303 for(Ice::CfgNode *basicBlock : function->getNodes())
Nicolas Capens2ae9d742016-11-24 14:43:05 -0500304 {
Nicolas Capens157ba262019-12-10 17:49:14 -0500305 for(Ice::Inst &inst : Ice::reverse_range(basicBlock->getInsts()))
Nicolas Capens2ae9d742016-11-24 14:43:05 -0500306 {
Nicolas Capens157ba262019-12-10 17:49:14 -0500307 if(inst.isDeleted())
308 {
309 continue;
310 }
311
312 if(isDead(&inst))
313 {
314 deleteInstruction(&inst);
315 modified = true;
316 }
317 }
318 }
Ben Clayton713b8d32019-12-17 20:37:56 +0000319 } while(modified);
Nicolas Capens157ba262019-12-10 17:49:14 -0500320}
321
322void 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 {
Ben Clayton713b8d32019-12-17 20:37:56 +0000335 break; // Allocas are all at the top
Nicolas Capens157ba262019-12-10 17:49:14 -0500336 }
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
Nicolas Capens3ffbd622021-02-06 00:35:38 -0500385// Iterate through basic blocks to propagate stores to subsequent loads.
386void Optimizer::optimizeSingleBasicBlockLoadsStores()
387{
388 for(Ice::CfgNode *block : function->getNodes())
389 {
390 // For each stack variable keep track of the last store instruction.
Nicolas Capens008247a2021-02-07 07:27:10 -0500391 // 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
Nicolas Capense32890c2021-08-21 02:27:44 -0400399 // 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;
Nicolas Capens3ffbd622021-02-06 00:35:38 -0500404
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 {
Nicolas Capense32890c2021-08-21 02:27:44 -0400422 Ice::SizeT addressIdx = alloca->getDest()->getIndex();
423
Nicolas Capens008247a2021-02-07 07:27:10 -0500424 // If there was a previous store to this address, and it was propagated
425 // to all subsequent loads, it can be eliminated.
Nicolas Capense32890c2021-08-21 02:27:44 -0400426 if(auto entry = lastStoreTo.find(addressIdx); entry != lastStoreTo.end())
Nicolas Capens008247a2021-02-07 07:27:10 -0500427 {
428 Ice::Inst *previousStore = entry->second.store;
429
Nicolas Capens008247a2021-02-07 07:27:10 -0500430 if(storeTypeMatchesStore(&inst, previousStore) &&
431 entry->second.allLoadsReplaced)
432 {
433 deleteInstruction(previousStore);
434 }
435 }
436
Nicolas Capense32890c2021-08-21 02:27:44 -0400437 lastStoreTo[addressIdx] = { &inst };
Nicolas Capens3ffbd622021-02-06 00:35:38 -0500438 }
439 }
440 }
441 else if(isLoad(inst))
442 {
443 if(Ice::InstAlloca *alloca = allocaOf(inst.getLoadAddress()))
444 {
Nicolas Capense32890c2021-08-21 02:27:44 -0400445 Ice::SizeT addressIdx = alloca->getDest()->getIndex();
446 auto entry = lastStoreTo.find(addressIdx);
Nicolas Capens3ffbd622021-02-06 00:35:38 -0500447 if(entry != lastStoreTo.end())
448 {
Nicolas Capens008247a2021-02-07 07:27:10 -0500449 const Ice::Inst *store = entry->second.store;
Nicolas Capens3ffbd622021-02-06 00:35:38 -0500450
Nicolas Capens3ffbd622021-02-06 00:35:38 -0500451 if(loadTypeMatchesStore(&inst, store))
452 {
453 replace(&inst, store->getData());
454 }
Nicolas Capens008247a2021-02-07 07:27:10 -0500455 else
456 {
457 entry->second.allLoadsReplaced = false;
458 }
Nicolas Capens3ffbd622021-02-06 00:35:38 -0500459 }
460 }
461 }
462 }
463 }
464
465 // This can leave some dead instructions. Specifically stores.
Nicolas Capens6ddeb6b2021-02-22 09:41:13 -0500466 // TODO(b/179668593): Check just for dead stores by iterating over allocas?
Nicolas Capens3ffbd622021-02-06 00:35:38 -0500467 eliminateDeadCode();
468}
469
Nicolas Capens157ba262019-12-10 17:49:14 -0500470void Optimizer::analyzeUses(Ice::Cfg *function)
471{
472 for(Ice::CfgNode *basicBlock : function->getNodes())
473 {
474 for(Ice::Inst &instruction : basicBlock->getInsts())
Nicolas Capense205c3d2016-11-30 15:14:28 -0500475 {
Nicolas Capens157ba262019-12-10 17:49:14 -0500476 if(instruction.isDeleted())
Nicolas Capense205c3d2016-11-30 15:14:28 -0500477 {
Nicolas Capens157ba262019-12-10 17:49:14 -0500478 continue;
479 }
Alexis Hetu932640b2018-06-20 15:35:53 -0400480
Nicolas Capens157ba262019-12-10 17:49:14 -0500481 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++)
Nicolas Capense205c3d2016-11-30 15:14:28 -0500490 {
Nicolas Capens157ba262019-12-10 17:49:14 -0500491 if(instruction.getSrc(i) == instruction.getSrc(unique))
Alexis Hetu932640b2018-06-20 15:35:53 -0400492 {
Nicolas Capensf4452fc2016-12-12 13:08:06 -0500493 break;
494 }
495 }
496
Nicolas Capens157ba262019-12-10 17:49:14 -0500497 if(i == unique)
Nicolas Capensf4452fc2016-12-12 13:08:06 -0500498 {
Nicolas Capens157ba262019-12-10 17:49:14 -0500499 Ice::Operand *src = instruction.getSrc(i);
500 getUses(src)->insert(src, &instruction);
Nicolas Capensf4452fc2016-12-12 13:08:06 -0500501 }
Nicolas Capensf4452fc2016-12-12 13:08:06 -0500502 }
503 }
504 }
Nicolas Capens2ae9d742016-11-24 14:43:05 -0500505}
506
Nicolas Capens157ba262019-12-10 17:49:14 -0500507void Optimizer::replace(Ice::Inst *instruction, Ice::Operand *newValue)
Nicolas Capens2ae9d742016-11-24 14:43:05 -0500508{
Nicolas Capens157ba262019-12-10 17:49:14 -0500509 Ice::Variable *oldValue = instruction->getDest();
Nicolas Capens2ae9d742016-11-24 14:43:05 -0500510
Nicolas Capens157ba262019-12-10 17:49:14 -0500511 if(!newValue)
512 {
513 newValue = context->getConstantUndef(oldValue->getType());
Nicolas Capens2ae9d742016-11-24 14:43:05 -0500514 }
Nicolas Capens157ba262019-12-10 17:49:14 -0500515
516 if(hasUses(oldValue))
517 {
518 for(Ice::Inst *use : *getUses(oldValue))
519 {
Ben Clayton713b8d32019-12-17 20:37:56 +0000520 assert(!use->isDeleted()); // Should have been removed from uses already
Nicolas Capens157ba262019-12-10 17:49:14 -0500521
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
539void Optimizer::deleteInstruction(Ice::Inst *instruction)
540{
541 if(!instruction || instruction->isDeleted())
542 {
543 return;
544 }
545
Nicolas Capens0cfc0432021-02-05 15:18:42 -0500546 assert(!instruction->getDest() || getUses(instruction->getDest())->empty());
Nicolas Capens157ba262019-12-10 17:49:14 -0500547 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
572bool 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 {
Nicolas Capens673a7fe2021-02-05 15:03:22 -0500582 if(Ice::Variable *address = llvm::dyn_cast<Ice::Variable>(instruction->getStoreAddress()))
Nicolas Capens157ba262019-12-10 17:49:14 -0500583 {
584 Ice::Inst *def = getDefinition(address);
585
586 if(def && llvm::isa<Ice::InstAlloca>(def))
587 {
588 if(hasUses(address))
589 {
Ben Clayton713b8d32019-12-17 20:37:56 +0000590 Optimizer::Uses *uses = getUses(address);
591 return uses->size() == uses->stores.size(); // Dead if all uses are stores
Nicolas Capens157ba262019-12-10 17:49:14 -0500592 }
593 else
594 {
Ben Clayton713b8d32019-12-17 20:37:56 +0000595 return true; // No uses
Nicolas Capens157ba262019-12-10 17:49:14 -0500596 }
597 }
598 }
599 }
600
601 return false;
602}
603
Nicolas Capens0cfc0432021-02-05 15:18:42 -0500604bool 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
Nicolas Capens3ffbd622021-02-06 00:35:38 -0500650Ice::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
Nicolas Capens33a77f72021-02-08 15:04:38 -0500659const Ice::InstIntrinsic *Optimizer::asLoadSubVector(const Ice::Inst *instruction)
Nicolas Capens157ba262019-12-10 17:49:14 -0500660{
Nicolas Capens33a77f72021-02-08 15:04:38 -0500661 if(auto *instrinsic = llvm::dyn_cast<Ice::InstIntrinsic>(instruction))
Nicolas Capens157ba262019-12-10 17:49:14 -0500662 {
Nicolas Capens673a7fe2021-02-05 15:03:22 -0500663 if(instrinsic->getIntrinsicID() == Ice::Intrinsics::LoadSubVector)
Nicolas Capens157ba262019-12-10 17:49:14 -0500664 {
665 return instrinsic;
666 }
667 }
668
669 return nullptr;
670}
671
Nicolas Capens33a77f72021-02-08 15:04:38 -0500672const Ice::InstIntrinsic *Optimizer::asStoreSubVector(const Ice::Inst *instruction)
Nicolas Capens157ba262019-12-10 17:49:14 -0500673{
Nicolas Capens33a77f72021-02-08 15:04:38 -0500674 if(auto *instrinsic = llvm::dyn_cast<Ice::InstIntrinsic>(instruction))
Nicolas Capens157ba262019-12-10 17:49:14 -0500675 {
Nicolas Capens673a7fe2021-02-05 15:03:22 -0500676 if(instrinsic->getIntrinsicID() == Ice::Intrinsics::StoreSubVector)
Nicolas Capens157ba262019-12-10 17:49:14 -0500677 {
678 return instrinsic;
679 }
680 }
681
682 return nullptr;
683}
684
685bool 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
695bool 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
Nicolas Capens157ba262019-12-10 17:49:14 -0500705bool 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));
Nicolas Capens673a7fe2021-02-05 15:03:22 -0500713 assert(load->getLoadAddress() == store->getStoreAddress());
Nicolas Capens157ba262019-12-10 17:49:14 -0500714
Nicolas Capens673a7fe2021-02-05 15:03:22 -0500715 if(store->getData()->getType() != load->getDest()->getType())
Nicolas Capens157ba262019-12-10 17:49:14 -0500716 {
Nicolas Capens673a7fe2021-02-05 15:03:22 -0500717 return false;
Nicolas Capens157ba262019-12-10 17:49:14 -0500718 }
719
720 if(auto *storeSubVector = asStoreSubVector(store))
721 {
722 if(auto *loadSubVector = asLoadSubVector(load))
723 {
Nicolas Capens673a7fe2021-02-05 15:03:22 -0500724 // 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();
Nicolas Capens157ba262019-12-10 17:49:14 -0500727 }
728 }
729
Nicolas Capens673a7fe2021-02-05 15:03:22 -0500730 return true;
Nicolas Capens157ba262019-12-10 17:49:14 -0500731}
732
Nicolas Capens008247a2021-02-07 07:27:10 -0500733bool 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
Nicolas Capens54313fb2021-02-19 14:26:27 -0500756void 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
Ben Clayton713b8d32019-12-17 20:37:56 +0000788Optimizer::Uses *Optimizer::getUses(Ice::Operand *operand)
Nicolas Capens157ba262019-12-10 17:49:14 -0500789{
Ben Clayton713b8d32019-12-17 20:37:56 +0000790 Optimizer::Uses *uses = (Optimizer::Uses *)operand->Ice::Operand::getExternalData();
Nicolas Capens157ba262019-12-10 17:49:14 -0500791 if(!uses)
792 {
793 uses = new Optimizer::Uses;
794 setUses(operand, uses);
Antonio Maiorano5ba2a5b2020-01-17 15:29:37 -0500795 operandsWithUses.push_back(operand);
Nicolas Capens157ba262019-12-10 17:49:14 -0500796 }
797 return uses;
798}
799
Ben Clayton713b8d32019-12-17 20:37:56 +0000800void Optimizer::setUses(Ice::Operand *operand, Optimizer::Uses *uses)
Nicolas Capens157ba262019-12-10 17:49:14 -0500801{
Antonio Maiorano614a4d42020-01-29 13:33:02 -0500802 if(auto *oldUses = reinterpret_cast<Optimizer::Uses *>(operand->Ice::Operand::getExternalData()))
803 {
804 delete oldUses;
805 }
806
Nicolas Capens157ba262019-12-10 17:49:14 -0500807 operand->Ice::Operand::setExternalData(uses);
808}
809
Ben Clayton713b8d32019-12-17 20:37:56 +0000810bool Optimizer::hasUses(Ice::Operand *operand) const
Nicolas Capens157ba262019-12-10 17:49:14 -0500811{
812 return operand->Ice::Operand::getExternalData() != nullptr;
813}
814
Ben Clayton713b8d32019-12-17 20:37:56 +0000815Ice::Inst *Optimizer::getDefinition(Ice::Variable *var)
Nicolas Capens157ba262019-12-10 17:49:14 -0500816{
Ben Clayton713b8d32019-12-17 20:37:56 +0000817 return (Ice::Inst *)var->Ice::Variable::getExternalData();
Nicolas Capens157ba262019-12-10 17:49:14 -0500818}
819
Ben Clayton713b8d32019-12-17 20:37:56 +0000820void Optimizer::setDefinition(Ice::Variable *var, Ice::Inst *inst)
Nicolas Capens157ba262019-12-10 17:49:14 -0500821{
822 var->Ice::Variable::setExternalData(inst);
823}
824
Nicolas Capens157ba262019-12-10 17:49:14 -0500825bool Optimizer::Uses::areOnlyLoadStore() const
826{
827 return size() == (loads.size() + stores.size());
828}
829
830void Optimizer::Uses::insert(Ice::Operand *value, Ice::Inst *instruction)
831{
832 push_back(instruction);
833
834 if(isLoad(*instruction))
835 {
Nicolas Capens673a7fe2021-02-05 15:03:22 -0500836 if(value == instruction->getLoadAddress())
Nicolas Capens157ba262019-12-10 17:49:14 -0500837 {
838 loads.push_back(instruction);
839 }
840 }
841 else if(isStore(*instruction))
842 {
Nicolas Capens673a7fe2021-02-05 15:03:22 -0500843 if(value == instruction->getStoreAddress())
Nicolas Capens157ba262019-12-10 17:49:14 -0500844 {
845 stores.push_back(instruction);
846 }
847 }
848}
849
850void 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
Ben Clayton713b8d32019-12-17 20:37:56 +0000886} // anonymous namespace
Nicolas Capens157ba262019-12-10 17:49:14 -0500887
888namespace rr {
889
Nicolas Capens54313fb2021-02-19 14:26:27 -0500890void optimize(Ice::Cfg *function, Nucleus::OptimizerReport *report)
Nicolas Capens157ba262019-12-10 17:49:14 -0500891{
Nicolas Capens54313fb2021-02-19 14:26:27 -0500892 Optimizer optimizer(report);
Nicolas Capens157ba262019-12-10 17:49:14 -0500893
894 optimizer.run(function);
895}
896
Antonio Maiorano614a4d42020-01-29 13:33:02 -0500897} // namespace rr