blob: 01721e52a786a9211b4181e0dddc7815222cf34b [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);
Nicolas Capens008247a2021-02-07 07:27:10 -050058 static bool storeTypeMatchesStore(const Ice::Inst *store1, const Ice::Inst *store2);
Nicolas Capens157ba262019-12-10 17:49:14 -050059
Nicolas Capens54313fb2021-02-19 14:26:27 -050060 void collectDiagnostics();
61
Nicolas Capens157ba262019-12-10 17:49:14 -050062 Ice::Cfg *function;
63 Ice::GlobalContext *context;
64
Ben Clayton713b8d32019-12-17 20:37:56 +000065 struct Uses : std::vector<Ice::Inst *>
Nicolas Capens2ae9d742016-11-24 14:43:05 -050066 {
Nicolas Capens157ba262019-12-10 17:49:14 -050067 bool areOnlyLoadStore() const;
68 void insert(Ice::Operand *value, Ice::Inst *instruction);
69 void erase(Ice::Inst *instruction);
Nicolas Capens2ae9d742016-11-24 14:43:05 -050070
Ben Clayton713b8d32019-12-17 20:37:56 +000071 std::vector<Ice::Inst *> loads;
72 std::vector<Ice::Inst *> stores;
Nicolas Capens2ae9d742016-11-24 14:43:05 -050073 };
74
Nicolas Capens157ba262019-12-10 17:49:14 -050075 struct LoadStoreInst
Nicolas Capens2ae9d742016-11-24 14:43:05 -050076 {
Ben Clayton713b8d32019-12-17 20:37:56 +000077 LoadStoreInst(Ice::Inst *inst, bool isStore)
78 : inst(inst)
Nicolas Capens673a7fe2021-02-05 15:03:22 -050079 , address(isStore ? inst->getStoreAddress() : inst->getLoadAddress())
Ben Clayton713b8d32019-12-17 20:37:56 +000080 , isStore(isStore)
Alexis Hetu932640b2018-06-20 15:35:53 -040081 {
Alexis Hetu932640b2018-06-20 15:35:53 -040082 }
Nicolas Capens2ae9d742016-11-24 14:43:05 -050083
Ben Clayton713b8d32019-12-17 20:37:56 +000084 Ice::Inst *inst;
Nicolas Capens157ba262019-12-10 17:49:14 -050085 Ice::Operand *address;
86 bool isStore;
87 };
88
Ben Clayton713b8d32019-12-17 20:37:56 +000089 Optimizer::Uses *getUses(Ice::Operand *);
90 void setUses(Ice::Operand *, Optimizer::Uses *);
91 bool hasUses(Ice::Operand *) const;
Nicolas Capens157ba262019-12-10 17:49:14 -050092
Ben Clayton713b8d32019-12-17 20:37:56 +000093 Ice::CfgNode *getNode(Ice::Inst *);
94 void setNode(Ice::Inst *, Ice::CfgNode *);
Nicolas Capens157ba262019-12-10 17:49:14 -050095
Ben Clayton713b8d32019-12-17 20:37:56 +000096 Ice::Inst *getDefinition(Ice::Variable *);
97 void setDefinition(Ice::Variable *, Ice::Inst *);
Nicolas Capens157ba262019-12-10 17:49:14 -050098
Ben Clayton713b8d32019-12-17 20:37:56 +000099 const std::vector<LoadStoreInst> &getLoadStoreInsts(Ice::CfgNode *);
100 void setLoadStoreInsts(Ice::CfgNode *, std::vector<LoadStoreInst> *);
101 bool hasLoadStoreInsts(Ice::CfgNode *node) const;
Nicolas Capens157ba262019-12-10 17:49:14 -0500102
Antonio Maiorano5ba2a5b2020-01-17 15:29:37 -0500103 std::vector<Ice::Operand *> operandsWithUses;
Nicolas Capens54313fb2021-02-19 14:26:27 -0500104
105 rr::Nucleus::OptimizerReport *report = nullptr;
Nicolas Capens157ba262019-12-10 17:49:14 -0500106};
107
108void Optimizer::run(Ice::Cfg *function)
109{
110 this->function = function;
111 this->context = function->getContext();
112
113 analyzeUses(function);
114
Nicolas Capens0b506e12021-02-04 17:00:08 -0500115 // Start by eliminating any dead code, to avoid redundant work for the
116 // subsequent optimization passes.
Nicolas Capens157ba262019-12-10 17:49:14 -0500117 eliminateDeadCode();
Nicolas Capens0b506e12021-02-04 17:00:08 -0500118
119 // Eliminate allocas which store the address of other allocas.
120 propagateAlloca();
121
Nicolas Capens0cfc0432021-02-05 15:18:42 -0500122 // Replace arrays with individual elements if only statically indexed.
123 performScalarReplacementOfAggregates();
124
Nicolas Capens3ffbd622021-02-06 00:35:38 -0500125 // Iterate through basic blocks to propagate loads following stores.
126 optimizeSingleBasicBlockLoadsStores();
127
Nicolas Capens157ba262019-12-10 17:49:14 -0500128 eliminateUnitializedLoads();
129 eliminateLoadsFollowingSingleStore();
130 optimizeStoresInSingleBasicBlock();
131 eliminateDeadCode();
132
Antonio Maiorano5ba2a5b2020-01-17 15:29:37 -0500133 for(auto operand : operandsWithUses)
Nicolas Capens2ae9d742016-11-24 14:43:05 -0500134 {
Antonio Maiorano614a4d42020-01-29 13:33:02 -0500135 // Deletes the Uses instance on the operand
136 setUses(operand, nullptr);
Nicolas Capens157ba262019-12-10 17:49:14 -0500137 }
Antonio Maiorano5ba2a5b2020-01-17 15:29:37 -0500138 operandsWithUses.clear();
Nicolas Capens54313fb2021-02-19 14:26:27 -0500139
140 collectDiagnostics();
Nicolas Capens157ba262019-12-10 17:49:14 -0500141}
142
Nicolas Capens0b506e12021-02-04 17:00:08 -0500143// Eliminates allocas which store the address of other allocas.
144void Optimizer::propagateAlloca()
145{
146 Ice::CfgNode *entryBlock = function->getEntryNode();
147 Ice::InstList &instList = entryBlock->getInsts();
148
149 for(Ice::Inst &inst : instList)
150 {
151 if(inst.isDeleted())
152 {
153 continue;
154 }
155
156 auto *alloca = llvm::dyn_cast<Ice::InstAlloca>(&inst);
157
158 if(!alloca)
159 {
160 break; // Allocas are all at the top
161 }
162
163 // Look for stores of this alloca's address.
164 Ice::Operand *address = alloca->getDest();
165 Uses uses = *getUses(address); // Hard copy
166
167 for(auto *store : uses)
168 {
169 if(isStore(*store) && store->getData() == address)
170 {
171 Ice::Operand *dest = store->getStoreAddress();
172 Ice::Variable *destVar = llvm::dyn_cast<Ice::Variable>(dest);
173 Ice::Inst *def = destVar ? getDefinition(destVar) : nullptr;
174
175 // If the address is stored into another stack variable, eliminate the latter.
176 if(def && def->getKind() == Ice::Inst::Alloca)
177 {
178 Uses destUses = *getUses(dest); // Hard copy
179
180 // Make sure the only store into the stack variable is this address, and that all of its other uses are loads.
181 // This prevents dynamic array loads/stores to be replaced by a scalar.
182 if((destUses.stores.size() == 1) && (destUses.loads.size() == destUses.size() - 1))
183 {
184 for(auto *load : destUses.loads)
185 {
186 replace(load, address);
187 }
188
189 // The address is now only stored, never loaded, so the store can be eliminated, together with its alloca.
190 assert(getUses(dest)->size() == 1);
191 deleteInstruction(store);
192 assert(def->isDeleted());
193 }
194 }
195 }
196 }
197 }
198}
199
Nicolas Capens0cfc0432021-02-05 15:18:42 -0500200Ice::Type pointerType()
201{
202 if(sizeof(void *) == 8)
203 {
204 return Ice::IceType_i64;
205 }
206 else
207 {
208 return Ice::IceType_i32;
209 }
210}
211
212// Replace arrays with individual elements if only statically indexed.
213void Optimizer::performScalarReplacementOfAggregates()
214{
215 std::vector<Ice::InstAlloca *> newAllocas;
216
217 Ice::CfgNode *entryBlock = function->getEntryNode();
218 Ice::InstList &instList = entryBlock->getInsts();
219
220 for(Ice::Inst &inst : instList)
221 {
222 if(inst.isDeleted())
223 {
224 continue;
225 }
226
227 auto *alloca = llvm::dyn_cast<Ice::InstAlloca>(&inst);
228
229 if(!alloca)
230 {
231 break; // Allocas are all at the top
232 }
233
234 uint32_t sizeInBytes = llvm::cast<Ice::ConstantInteger32>(alloca->getSizeInBytes())->getValue();
235 uint32_t alignInBytes = alloca->getAlignInBytes();
236
237 // This pass relies on array elements to be naturally aligned (i.e. matches the type size).
238 assert(sizeInBytes >= alignInBytes);
239 assert(sizeInBytes % alignInBytes == 0);
240 uint32_t elementCount = sizeInBytes / alignInBytes;
241
242 Ice::Operand *address = alloca->getDest();
243
244 if(isStaticallyIndexedArray(address))
245 {
246 // Delete the old array.
247 alloca->setDeleted();
248
249 // Allocate new stack slots for each element.
250 std::vector<Ice::Variable *> newAddress(elementCount);
251 auto *bytes = Ice::ConstantInteger32::create(context, Ice::IceType_i32, alignInBytes);
252
253 for(uint32_t i = 0; i < elementCount; i++)
254 {
255 newAddress[i] = function->makeVariable(pointerType());
256 auto *alloca = Ice::InstAlloca::create(function, newAddress[i], bytes, alignInBytes);
257 setDefinition(newAddress[i], alloca);
258
259 newAllocas.push_back(alloca);
260 }
261
262 Uses uses = *getUses(address); // Hard copy
263
264 for(auto *use : uses)
265 {
266 assert(!use->isDeleted());
267
268 if(isLoad(*use)) // Direct use of base address
269 {
270 use->replaceSource(asLoadSubVector(use) ? 1 : 0, newAddress[0]);
271 getUses(newAddress[0])->insert(newAddress[0], use);
272 }
273 else if(isStore(*use)) // Direct use of base address
274 {
275 use->replaceSource(asStoreSubVector(use) ? 2 : 1, newAddress[0]);
276 getUses(newAddress[0])->insert(newAddress[0], use);
277 }
278 else // Statically indexed use
279 {
280 auto *arithmetic = llvm::cast<Ice::InstArithmetic>(use);
281
282 if(arithmetic->getOp() == Ice::InstArithmetic::Add)
283 {
284 auto *rhs = arithmetic->getSrc(1);
285 int32_t offset = llvm::cast<Ice::ConstantInteger32>(rhs)->getValue();
286
287 assert(offset % alignInBytes == 0);
288 int32_t index = offset / alignInBytes;
289 assert(static_cast<uint32_t>(index) < elementCount);
290
291 replace(arithmetic, newAddress[index]);
292 }
293 else
294 assert(false && "Mismatch between isStaticallyIndexedArray() and scalarReplacementOfAggregates()");
295 }
296 }
297 }
298 }
299
300 // After looping over all the old allocas, add any new ones that replace them.
301 // They're added to the front in reverse order, to retain their original order.
302 for(size_t i = newAllocas.size(); i-- != 0;)
303 {
304 if(!isDead(newAllocas[i]))
305 {
306 instList.push_front(newAllocas[i]);
307 }
308 }
309}
310
Nicolas Capens157ba262019-12-10 17:49:14 -0500311void Optimizer::eliminateDeadCode()
312{
313 bool modified;
314 do
315 {
316 modified = false;
317 for(Ice::CfgNode *basicBlock : function->getNodes())
Nicolas Capens2ae9d742016-11-24 14:43:05 -0500318 {
Nicolas Capens157ba262019-12-10 17:49:14 -0500319 for(Ice::Inst &inst : Ice::reverse_range(basicBlock->getInsts()))
Nicolas Capens2ae9d742016-11-24 14:43:05 -0500320 {
Nicolas Capens157ba262019-12-10 17:49:14 -0500321 if(inst.isDeleted())
322 {
323 continue;
324 }
325
326 if(isDead(&inst))
327 {
328 deleteInstruction(&inst);
329 modified = true;
330 }
331 }
332 }
Ben Clayton713b8d32019-12-17 20:37:56 +0000333 } while(modified);
Nicolas Capens157ba262019-12-10 17:49:14 -0500334}
335
336void Optimizer::eliminateUnitializedLoads()
337{
338 Ice::CfgNode *entryBlock = function->getEntryNode();
339
340 for(Ice::Inst &alloca : entryBlock->getInsts())
341 {
342 if(alloca.isDeleted())
343 {
344 continue;
345 }
346
347 if(!llvm::isa<Ice::InstAlloca>(alloca))
348 {
Ben Clayton713b8d32019-12-17 20:37:56 +0000349 break; // Allocas are all at the top
Nicolas Capens157ba262019-12-10 17:49:14 -0500350 }
351
352 Ice::Operand *address = alloca.getDest();
353
354 if(!hasUses(address))
355 {
356 continue;
357 }
358
359 const auto &addressUses = *getUses(address);
360
361 if(!addressUses.areOnlyLoadStore())
362 {
363 continue;
364 }
365
366 if(addressUses.stores.empty())
367 {
368 for(Ice::Inst *load : addressUses.loads)
369 {
370 Ice::Variable *loadData = load->getDest();
371
372 if(hasUses(loadData))
373 {
374 for(Ice::Inst *use : *getUses(loadData))
375 {
376 for(Ice::SizeT i = 0; i < use->getSrcSize(); i++)
377 {
378 if(use->getSrc(i) == loadData)
379 {
380 auto *undef = context->getConstantUndef(loadData->getType());
381
382 use->replaceSource(i, undef);
383 }
384 }
385 }
386
387 setUses(loadData, nullptr);
388 }
389
390 load->setDeleted();
391 }
392
393 alloca.setDeleted();
394 setUses(address, nullptr);
395 }
396 }
397}
398
399void Optimizer::eliminateLoadsFollowingSingleStore()
400{
401 Ice::CfgNode *entryBlock = function->getEntryNode();
402
403 for(Ice::Inst &alloca : entryBlock->getInsts())
404 {
405 if(alloca.isDeleted())
406 {
407 continue;
408 }
409
410 if(!llvm::isa<Ice::InstAlloca>(alloca))
411 {
Ben Clayton713b8d32019-12-17 20:37:56 +0000412 break; // Allocas are all at the top
Nicolas Capens157ba262019-12-10 17:49:14 -0500413 }
414
415 Ice::Operand *address = alloca.getDest();
416
417 if(!hasUses(address))
418 {
419 continue;
420 }
421
422 auto &addressUses = *getUses(address);
423
424 if(!addressUses.areOnlyLoadStore())
425 {
426 continue;
427 }
428
429 if(addressUses.stores.size() == 1)
430 {
431 Ice::Inst *store = addressUses.stores[0];
Nicolas Capens673a7fe2021-02-05 15:03:22 -0500432 Ice::Operand *storeValue = store->getData();
Nicolas Capens157ba262019-12-10 17:49:14 -0500433
Nicolas Capens3ab17bd2021-02-10 16:41:31 -0500434 auto instIterator = store->getIterator();
435 auto basicBlockEnd = getNode(store)->getInsts().end();
436
437 while(++instIterator != basicBlockEnd)
Nicolas Capens157ba262019-12-10 17:49:14 -0500438 {
Nicolas Capens3ab17bd2021-02-10 16:41:31 -0500439 Ice::Inst *load = &*instIterator;
440
Nicolas Capens157ba262019-12-10 17:49:14 -0500441 if(load->isDeleted() || !isLoad(*load))
442 {
443 continue;
444 }
445
Nicolas Capens673a7fe2021-02-05 15:03:22 -0500446 if(load->getLoadAddress() != address)
Nicolas Capens157ba262019-12-10 17:49:14 -0500447 {
448 continue;
449 }
450
451 if(!loadTypeMatchesStore(load, store))
452 {
453 continue;
454 }
455
456 replace(load, storeValue);
457
458 for(size_t i = 0; i < addressUses.loads.size(); i++)
459 {
460 if(addressUses.loads[i] == load)
461 {
462 addressUses.loads[i] = addressUses.loads.back();
463 addressUses.loads.pop_back();
464 break;
465 }
466 }
467
468 for(size_t i = 0; i < addressUses.size(); i++)
469 {
470 if(addressUses[i] == load)
471 {
472 addressUses[i] = addressUses.back();
473 addressUses.pop_back();
474 break;
475 }
476 }
477
478 if(addressUses.size() == 1)
479 {
480 assert(addressUses[0] == store);
481
482 alloca.setDeleted();
483 store->setDeleted();
484 setUses(address, nullptr);
485
486 if(hasUses(storeValue))
487 {
488 auto &valueUses = *getUses(storeValue);
489
490 for(size_t i = 0; i < valueUses.size(); i++)
491 {
492 if(valueUses[i] == store)
493 {
494 valueUses[i] = valueUses.back();
495 valueUses.pop_back();
496 break;
497 }
498 }
499
500 if(valueUses.empty())
501 {
502 setUses(storeValue, nullptr);
503 }
504 }
505
506 break;
507 }
508 }
509 }
510 }
511}
512
513void Optimizer::optimizeStoresInSingleBasicBlock()
514{
515 Ice::CfgNode *entryBlock = function->getEntryNode();
516
Ben Clayton713b8d32019-12-17 20:37:56 +0000517 std::vector<std::vector<LoadStoreInst> *> allocatedVectors;
Nicolas Capens157ba262019-12-10 17:49:14 -0500518
519 for(Ice::Inst &alloca : entryBlock->getInsts())
520 {
521 if(alloca.isDeleted())
522 {
523 continue;
524 }
525
526 if(!llvm::isa<Ice::InstAlloca>(alloca))
527 {
Ben Clayton713b8d32019-12-17 20:37:56 +0000528 break; // Allocas are all at the top
Nicolas Capens157ba262019-12-10 17:49:14 -0500529 }
530
531 Ice::Operand *address = alloca.getDest();
532
533 if(!hasUses(address))
534 {
535 continue;
536 }
537
538 const auto &addressUses = *getUses(address);
539
540 if(!addressUses.areOnlyLoadStore())
541 {
542 continue;
543 }
544
545 Ice::CfgNode *singleBasicBlock = getNode(addressUses.stores[0]);
546
547 for(size_t i = 1; i < addressUses.stores.size(); i++)
548 {
549 Ice::Inst *store = addressUses.stores[i];
550 if(getNode(store) != singleBasicBlock)
551 {
552 singleBasicBlock = nullptr;
553 break;
554 }
555 }
556
557 if(singleBasicBlock)
558 {
559 if(!hasLoadStoreInsts(singleBasicBlock))
560 {
Ben Clayton713b8d32019-12-17 20:37:56 +0000561 std::vector<LoadStoreInst> *loadStoreInstVector = new std::vector<LoadStoreInst>();
Nicolas Capens157ba262019-12-10 17:49:14 -0500562 setLoadStoreInsts(singleBasicBlock, loadStoreInstVector);
563 allocatedVectors.push_back(loadStoreInstVector);
564 for(Ice::Inst &inst : singleBasicBlock->getInsts())
Nicolas Capensc9d70d52016-12-12 15:07:39 -0500565 {
566 if(inst.isDeleted())
567 {
568 continue;
569 }
Nicolas Capens2ae9d742016-11-24 14:43:05 -0500570
Nicolas Capens157ba262019-12-10 17:49:14 -0500571 bool isStoreInst = isStore(inst);
572 bool isLoadInst = isLoad(inst);
573
574 if(isStoreInst || isLoadInst)
Nicolas Capensc9d70d52016-12-12 15:07:39 -0500575 {
Nicolas Capens157ba262019-12-10 17:49:14 -0500576 loadStoreInstVector->push_back(LoadStoreInst(&inst, isStoreInst));
Nicolas Capensc9d70d52016-12-12 15:07:39 -0500577 }
578 }
Nicolas Capens2ae9d742016-11-24 14:43:05 -0500579 }
Nicolas Capens2ae9d742016-11-24 14:43:05 -0500580
Nicolas Capens157ba262019-12-10 17:49:14 -0500581 Ice::Inst *store = nullptr;
582 Ice::Operand *storeValue = nullptr;
583 bool unmatchedLoads = false;
Nicolas Capensf4452fc2016-12-12 13:08:06 -0500584
Ben Clayton713b8d32019-12-17 20:37:56 +0000585 for(auto &loadStoreInst : getLoadStoreInsts(singleBasicBlock))
Nicolas Capensf4452fc2016-12-12 13:08:06 -0500586 {
Ben Clayton713b8d32019-12-17 20:37:56 +0000587 Ice::Inst *inst = loadStoreInst.inst;
Nicolas Capensf4452fc2016-12-12 13:08:06 -0500588
Nicolas Capens157ba262019-12-10 17:49:14 -0500589 if((loadStoreInst.address != address) || inst->isDeleted())
Nicolas Capens2ae9d742016-11-24 14:43:05 -0500590 {
591 continue;
592 }
593
Nicolas Capens157ba262019-12-10 17:49:14 -0500594 if(loadStoreInst.isStore)
Alexis Hetu932640b2018-06-20 15:35:53 -0400595 {
Nicolas Capens157ba262019-12-10 17:49:14 -0500596 // New store found. If we had a previous one, try to eliminate it.
597 if(store && !unmatchedLoads)
Nicolas Capens2ae9d742016-11-24 14:43:05 -0500598 {
Nicolas Capens157ba262019-12-10 17:49:14 -0500599 // If the previous store is wider than the new one, we can't eliminate it
600 // because there could be a wide load reading its non-overwritten data.
601 if(storeSize(inst) >= storeSize(store))
Nicolas Capens2ae9d742016-11-24 14:43:05 -0500602 {
Nicolas Capens157ba262019-12-10 17:49:14 -0500603 deleteInstruction(store);
Nicolas Capens2ae9d742016-11-24 14:43:05 -0500604 }
605 }
606
Nicolas Capens157ba262019-12-10 17:49:14 -0500607 store = inst;
Nicolas Capens673a7fe2021-02-05 15:03:22 -0500608 storeValue = store->getData();
Nicolas Capens157ba262019-12-10 17:49:14 -0500609 unmatchedLoads = false;
610 }
611 else
612 {
613 if(!loadTypeMatchesStore(inst, store))
Nicolas Capens2ae9d742016-11-24 14:43:05 -0500614 {
Nicolas Capens157ba262019-12-10 17:49:14 -0500615 unmatchedLoads = true;
616 continue;
Nicolas Capens2ae9d742016-11-24 14:43:05 -0500617 }
Nicolas Capens157ba262019-12-10 17:49:14 -0500618
619 replace(inst, storeValue);
Nicolas Capens2ae9d742016-11-24 14:43:05 -0500620 }
621 }
622 }
623 }
Nicolas Capensf4452fc2016-12-12 13:08:06 -0500624
Nicolas Capens157ba262019-12-10 17:49:14 -0500625 for(auto loadStoreInstVector : allocatedVectors)
Nicolas Capense205c3d2016-11-30 15:14:28 -0500626 {
Nicolas Capens157ba262019-12-10 17:49:14 -0500627 delete loadStoreInstVector;
628 }
629}
Nicolas Capense205c3d2016-11-30 15:14:28 -0500630
Nicolas Capens3ffbd622021-02-06 00:35:38 -0500631// Iterate through basic blocks to propagate stores to subsequent loads.
632void Optimizer::optimizeSingleBasicBlockLoadsStores()
633{
634 for(Ice::CfgNode *block : function->getNodes())
635 {
636 // For each stack variable keep track of the last store instruction.
Nicolas Capens008247a2021-02-07 07:27:10 -0500637 // To eliminate a store followed by another store to the same alloca address
638 // we must also know whether all loads have been replaced by the store value.
639 struct LastStore
640 {
641 Ice::Inst *store;
642 bool allLoadsReplaced = true;
643 };
644
645 std::unordered_map<const Ice::InstAlloca *, LastStore> lastStoreTo;
Nicolas Capens3ffbd622021-02-06 00:35:38 -0500646
647 for(Ice::Inst &inst : block->getInsts())
648 {
649 if(inst.isDeleted())
650 {
651 continue;
652 }
653
654 if(isStore(inst))
655 {
656 Ice::Operand *address = inst.getStoreAddress();
657
658 if(Ice::InstAlloca *alloca = allocaOf(address))
659 {
660 // Only consider this store for propagation if its address is not used as
661 // a pointer which could be used for indirect stores.
662 if(getUses(address)->areOnlyLoadStore())
663 {
Nicolas Capens008247a2021-02-07 07:27:10 -0500664 // If there was a previous store to this address, and it was propagated
665 // to all subsequent loads, it can be eliminated.
666 if(auto entry = lastStoreTo.find(alloca); entry != lastStoreTo.end())
667 {
668 Ice::Inst *previousStore = entry->second.store;
669
670 // TODO(b/179668593): Also eliminate it if the next store is larger in size.
671 if(storeTypeMatchesStore(&inst, previousStore) &&
672 entry->second.allLoadsReplaced)
673 {
674 deleteInstruction(previousStore);
675 }
676 }
677
678 lastStoreTo[alloca] = { &inst };
Nicolas Capens3ffbd622021-02-06 00:35:38 -0500679 }
680 }
681 }
682 else if(isLoad(inst))
683 {
684 if(Ice::InstAlloca *alloca = allocaOf(inst.getLoadAddress()))
685 {
686 auto entry = lastStoreTo.find(alloca);
687 if(entry != lastStoreTo.end())
688 {
Nicolas Capens008247a2021-02-07 07:27:10 -0500689 const Ice::Inst *store = entry->second.store;
Nicolas Capens3ffbd622021-02-06 00:35:38 -0500690
691 // Conservatively check that the types match.
692 // TODO(b/179668593): Also valid to propagate if the load is smaller or equal in size to the store.
693 if(loadTypeMatchesStore(&inst, store))
694 {
695 replace(&inst, store->getData());
696 }
Nicolas Capens008247a2021-02-07 07:27:10 -0500697 else
698 {
699 entry->second.allLoadsReplaced = false;
700 }
Nicolas Capens3ffbd622021-02-06 00:35:38 -0500701 }
702 }
703 }
704 }
705 }
706
707 // This can leave some dead instructions. Specifically stores.
708 // TODO((b/179668593): Eliminate stores superseded by subsequent stores?
709 eliminateDeadCode();
710}
711
Nicolas Capens157ba262019-12-10 17:49:14 -0500712void Optimizer::analyzeUses(Ice::Cfg *function)
713{
714 for(Ice::CfgNode *basicBlock : function->getNodes())
715 {
716 for(Ice::Inst &instruction : basicBlock->getInsts())
Nicolas Capense205c3d2016-11-30 15:14:28 -0500717 {
Nicolas Capens157ba262019-12-10 17:49:14 -0500718 if(instruction.isDeleted())
Nicolas Capense205c3d2016-11-30 15:14:28 -0500719 {
Nicolas Capens157ba262019-12-10 17:49:14 -0500720 continue;
721 }
Alexis Hetu932640b2018-06-20 15:35:53 -0400722
Nicolas Capens157ba262019-12-10 17:49:14 -0500723 setNode(&instruction, basicBlock);
724 if(instruction.getDest())
725 {
726 setDefinition(instruction.getDest(), &instruction);
727 }
728
729 for(Ice::SizeT i = 0; i < instruction.getSrcSize(); i++)
730 {
731 Ice::SizeT unique = 0;
732 for(; unique < i; unique++)
Nicolas Capense205c3d2016-11-30 15:14:28 -0500733 {
Nicolas Capens157ba262019-12-10 17:49:14 -0500734 if(instruction.getSrc(i) == instruction.getSrc(unique))
Alexis Hetu932640b2018-06-20 15:35:53 -0400735 {
Nicolas Capensf4452fc2016-12-12 13:08:06 -0500736 break;
737 }
738 }
739
Nicolas Capens157ba262019-12-10 17:49:14 -0500740 if(i == unique)
Nicolas Capensf4452fc2016-12-12 13:08:06 -0500741 {
Nicolas Capens157ba262019-12-10 17:49:14 -0500742 Ice::Operand *src = instruction.getSrc(i);
743 getUses(src)->insert(src, &instruction);
Nicolas Capensf4452fc2016-12-12 13:08:06 -0500744 }
Nicolas Capensf4452fc2016-12-12 13:08:06 -0500745 }
746 }
747 }
Nicolas Capens2ae9d742016-11-24 14:43:05 -0500748}
749
Nicolas Capens157ba262019-12-10 17:49:14 -0500750void Optimizer::replace(Ice::Inst *instruction, Ice::Operand *newValue)
Nicolas Capens2ae9d742016-11-24 14:43:05 -0500751{
Nicolas Capens157ba262019-12-10 17:49:14 -0500752 Ice::Variable *oldValue = instruction->getDest();
Nicolas Capens2ae9d742016-11-24 14:43:05 -0500753
Nicolas Capens157ba262019-12-10 17:49:14 -0500754 if(!newValue)
755 {
756 newValue = context->getConstantUndef(oldValue->getType());
Nicolas Capens2ae9d742016-11-24 14:43:05 -0500757 }
Nicolas Capens157ba262019-12-10 17:49:14 -0500758
759 if(hasUses(oldValue))
760 {
761 for(Ice::Inst *use : *getUses(oldValue))
762 {
Ben Clayton713b8d32019-12-17 20:37:56 +0000763 assert(!use->isDeleted()); // Should have been removed from uses already
Nicolas Capens157ba262019-12-10 17:49:14 -0500764
765 for(Ice::SizeT i = 0; i < use->getSrcSize(); i++)
766 {
767 if(use->getSrc(i) == oldValue)
768 {
769 use->replaceSource(i, newValue);
770 }
771 }
772
773 getUses(newValue)->insert(newValue, use);
774 }
775
776 setUses(oldValue, nullptr);
777 }
778
779 deleteInstruction(instruction);
780}
781
782void Optimizer::deleteInstruction(Ice::Inst *instruction)
783{
784 if(!instruction || instruction->isDeleted())
785 {
786 return;
787 }
788
Nicolas Capens0cfc0432021-02-05 15:18:42 -0500789 assert(!instruction->getDest() || getUses(instruction->getDest())->empty());
Nicolas Capens157ba262019-12-10 17:49:14 -0500790 instruction->setDeleted();
791
792 for(Ice::SizeT i = 0; i < instruction->getSrcSize(); i++)
793 {
794 Ice::Operand *src = instruction->getSrc(i);
795
796 if(hasUses(src))
797 {
798 auto &srcUses = *getUses(src);
799
800 srcUses.erase(instruction);
801
802 if(srcUses.empty())
803 {
804 setUses(src, nullptr);
805
806 if(Ice::Variable *var = llvm::dyn_cast<Ice::Variable>(src))
807 {
808 deleteInstruction(getDefinition(var));
809 }
810 }
811 }
812 }
813}
814
815bool Optimizer::isDead(Ice::Inst *instruction)
816{
817 Ice::Variable *dest = instruction->getDest();
818
819 if(dest)
820 {
821 return (!hasUses(dest) || getUses(dest)->empty()) && !instruction->hasSideEffects();
822 }
823 else if(isStore(*instruction))
824 {
Nicolas Capens673a7fe2021-02-05 15:03:22 -0500825 if(Ice::Variable *address = llvm::dyn_cast<Ice::Variable>(instruction->getStoreAddress()))
Nicolas Capens157ba262019-12-10 17:49:14 -0500826 {
827 Ice::Inst *def = getDefinition(address);
828
829 if(def && llvm::isa<Ice::InstAlloca>(def))
830 {
831 if(hasUses(address))
832 {
Ben Clayton713b8d32019-12-17 20:37:56 +0000833 Optimizer::Uses *uses = getUses(address);
834 return uses->size() == uses->stores.size(); // Dead if all uses are stores
Nicolas Capens157ba262019-12-10 17:49:14 -0500835 }
836 else
837 {
Ben Clayton713b8d32019-12-17 20:37:56 +0000838 return true; // No uses
Nicolas Capens157ba262019-12-10 17:49:14 -0500839 }
840 }
841 }
842 }
843
844 return false;
845}
846
Nicolas Capens0cfc0432021-02-05 15:18:42 -0500847bool Optimizer::isStaticallyIndexedArray(Ice::Operand *allocaAddress)
848{
849 auto &uses = *getUses(allocaAddress);
850
851 for(auto *use : uses)
852 {
853 // Direct load from base address.
854 if(isLoad(*use) && use->getLoadAddress() == allocaAddress)
855 {
856 continue; // This is fine.
857 }
858
859 if(isStore(*use))
860 {
861 // Can either be the address we're storing to, or the data we're storing.
862 if(use->getStoreAddress() == allocaAddress)
863 {
864 continue;
865 }
866 else
867 {
868 // propagateAlloca() eliminates most of the stores of the address itself.
869 // For the cases it doesn't handle, assume SRoA is not feasible.
870 return false;
871 }
872 }
873
874 // Pointer arithmetic is fine if it only uses constants.
875 auto *arithmetic = llvm::dyn_cast<Ice::InstArithmetic>(use);
876 if(arithmetic && arithmetic->getOp() == Ice::InstArithmetic::Add)
877 {
878 auto *rhs = arithmetic->getSrc(1);
879
880 if(llvm::isa<Ice::Constant>(rhs))
881 {
882 continue;
883 }
884 }
885
886 // If there's any other type of use, bail out.
887 return false;
888 }
889
890 return true;
891}
892
Nicolas Capens3ffbd622021-02-06 00:35:38 -0500893Ice::InstAlloca *Optimizer::allocaOf(Ice::Operand *address)
894{
895 Ice::Variable *addressVar = llvm::dyn_cast<Ice::Variable>(address);
896 Ice::Inst *def = addressVar ? getDefinition(addressVar) : nullptr;
897 Ice::InstAlloca *alloca = def ? llvm::dyn_cast<Ice::InstAlloca>(def) : nullptr;
898
899 return alloca;
900}
901
Nicolas Capens33a77f72021-02-08 15:04:38 -0500902const Ice::InstIntrinsic *Optimizer::asLoadSubVector(const Ice::Inst *instruction)
Nicolas Capens157ba262019-12-10 17:49:14 -0500903{
Nicolas Capens33a77f72021-02-08 15:04:38 -0500904 if(auto *instrinsic = llvm::dyn_cast<Ice::InstIntrinsic>(instruction))
Nicolas Capens157ba262019-12-10 17:49:14 -0500905 {
Nicolas Capens673a7fe2021-02-05 15:03:22 -0500906 if(instrinsic->getIntrinsicID() == Ice::Intrinsics::LoadSubVector)
Nicolas Capens157ba262019-12-10 17:49:14 -0500907 {
908 return instrinsic;
909 }
910 }
911
912 return nullptr;
913}
914
Nicolas Capens33a77f72021-02-08 15:04:38 -0500915const Ice::InstIntrinsic *Optimizer::asStoreSubVector(const Ice::Inst *instruction)
Nicolas Capens157ba262019-12-10 17:49:14 -0500916{
Nicolas Capens33a77f72021-02-08 15:04:38 -0500917 if(auto *instrinsic = llvm::dyn_cast<Ice::InstIntrinsic>(instruction))
Nicolas Capens157ba262019-12-10 17:49:14 -0500918 {
Nicolas Capens673a7fe2021-02-05 15:03:22 -0500919 if(instrinsic->getIntrinsicID() == Ice::Intrinsics::StoreSubVector)
Nicolas Capens157ba262019-12-10 17:49:14 -0500920 {
921 return instrinsic;
922 }
923 }
924
925 return nullptr;
926}
927
928bool Optimizer::isLoad(const Ice::Inst &instruction)
929{
930 if(llvm::isa<Ice::InstLoad>(&instruction))
931 {
932 return true;
933 }
934
935 return asLoadSubVector(&instruction) != nullptr;
936}
937
938bool Optimizer::isStore(const Ice::Inst &instruction)
939{
940 if(llvm::isa<Ice::InstStore>(&instruction))
941 {
942 return true;
943 }
944
945 return asStoreSubVector(&instruction) != nullptr;
946}
947
Nicolas Capens157ba262019-12-10 17:49:14 -0500948std::size_t Optimizer::storeSize(const Ice::Inst *store)
949{
950 assert(isStore(*store));
951
952 if(auto *instStore = llvm::dyn_cast<Ice::InstStore>(store))
953 {
954 return Ice::typeWidthInBytes(instStore->getData()->getType());
955 }
956
957 if(auto *storeSubVector = asStoreSubVector(store))
958 {
959 return llvm::cast<Ice::ConstantInteger32>(storeSubVector->getSrc(3))->getValue();
960 }
961
962 return 0;
963}
964
965bool Optimizer::loadTypeMatchesStore(const Ice::Inst *load, const Ice::Inst *store)
966{
967 if(!load || !store)
968 {
969 return false;
970 }
971
972 assert(isLoad(*load) && isStore(*store));
Nicolas Capens673a7fe2021-02-05 15:03:22 -0500973 assert(load->getLoadAddress() == store->getStoreAddress());
Nicolas Capens157ba262019-12-10 17:49:14 -0500974
Nicolas Capens673a7fe2021-02-05 15:03:22 -0500975 if(store->getData()->getType() != load->getDest()->getType())
Nicolas Capens157ba262019-12-10 17:49:14 -0500976 {
Nicolas Capens673a7fe2021-02-05 15:03:22 -0500977 return false;
Nicolas Capens157ba262019-12-10 17:49:14 -0500978 }
979
980 if(auto *storeSubVector = asStoreSubVector(store))
981 {
982 if(auto *loadSubVector = asLoadSubVector(load))
983 {
Nicolas Capens673a7fe2021-02-05 15:03:22 -0500984 // Check for matching sub-vector width.
985 return llvm::cast<Ice::ConstantInteger32>(storeSubVector->getSrc(2))->getValue() ==
986 llvm::cast<Ice::ConstantInteger32>(loadSubVector->getSrc(1))->getValue();
Nicolas Capens157ba262019-12-10 17:49:14 -0500987 }
988 }
989
Nicolas Capens673a7fe2021-02-05 15:03:22 -0500990 return true;
Nicolas Capens157ba262019-12-10 17:49:14 -0500991}
992
Nicolas Capens008247a2021-02-07 07:27:10 -0500993bool Optimizer::storeTypeMatchesStore(const Ice::Inst *store1, const Ice::Inst *store2)
994{
995 assert(isStore(*store1) && isStore(*store2));
996 assert(store1->getStoreAddress() == store2->getStoreAddress());
997
998 if(store1->getData()->getType() != store2->getData()->getType())
999 {
1000 return false;
1001 }
1002
1003 if(auto *storeSubVector1 = asStoreSubVector(store1))
1004 {
1005 if(auto *storeSubVector2 = asStoreSubVector(store2))
1006 {
1007 // Check for matching sub-vector width.
1008 return llvm::cast<Ice::ConstantInteger32>(storeSubVector1->getSrc(2))->getValue() ==
1009 llvm::cast<Ice::ConstantInteger32>(storeSubVector2->getSrc(2))->getValue();
1010 }
1011 }
1012
1013 return true;
1014}
1015
Nicolas Capens54313fb2021-02-19 14:26:27 -05001016void Optimizer::collectDiagnostics()
1017{
1018 if(report)
1019 {
1020 *report = {};
1021
1022 for(auto *basicBlock : function->getNodes())
1023 {
1024 for(auto &inst : basicBlock->getInsts())
1025 {
1026 if(inst.isDeleted())
1027 {
1028 continue;
1029 }
1030
1031 if(llvm::isa<Ice::InstAlloca>(inst))
1032 {
1033 report->allocas++;
1034 }
1035 else if(isLoad(inst))
1036 {
1037 report->loads++;
1038 }
1039 else if(isStore(inst))
1040 {
1041 report->stores++;
1042 }
1043 }
1044 }
1045 }
1046}
1047
Ben Clayton713b8d32019-12-17 20:37:56 +00001048Optimizer::Uses *Optimizer::getUses(Ice::Operand *operand)
Nicolas Capens157ba262019-12-10 17:49:14 -05001049{
Ben Clayton713b8d32019-12-17 20:37:56 +00001050 Optimizer::Uses *uses = (Optimizer::Uses *)operand->Ice::Operand::getExternalData();
Nicolas Capens157ba262019-12-10 17:49:14 -05001051 if(!uses)
1052 {
1053 uses = new Optimizer::Uses;
1054 setUses(operand, uses);
Antonio Maiorano5ba2a5b2020-01-17 15:29:37 -05001055 operandsWithUses.push_back(operand);
Nicolas Capens157ba262019-12-10 17:49:14 -05001056 }
1057 return uses;
1058}
1059
Ben Clayton713b8d32019-12-17 20:37:56 +00001060void Optimizer::setUses(Ice::Operand *operand, Optimizer::Uses *uses)
Nicolas Capens157ba262019-12-10 17:49:14 -05001061{
Antonio Maiorano614a4d42020-01-29 13:33:02 -05001062 if(auto *oldUses = reinterpret_cast<Optimizer::Uses *>(operand->Ice::Operand::getExternalData()))
1063 {
1064 delete oldUses;
1065 }
1066
Nicolas Capens157ba262019-12-10 17:49:14 -05001067 operand->Ice::Operand::setExternalData(uses);
1068}
1069
Ben Clayton713b8d32019-12-17 20:37:56 +00001070bool Optimizer::hasUses(Ice::Operand *operand) const
Nicolas Capens157ba262019-12-10 17:49:14 -05001071{
1072 return operand->Ice::Operand::getExternalData() != nullptr;
1073}
1074
Ben Clayton713b8d32019-12-17 20:37:56 +00001075Ice::CfgNode *Optimizer::getNode(Ice::Inst *inst)
Nicolas Capens157ba262019-12-10 17:49:14 -05001076{
Ben Clayton713b8d32019-12-17 20:37:56 +00001077 return (Ice::CfgNode *)inst->Ice::Inst::getExternalData();
Nicolas Capens157ba262019-12-10 17:49:14 -05001078}
1079
Ben Clayton713b8d32019-12-17 20:37:56 +00001080void Optimizer::setNode(Ice::Inst *inst, Ice::CfgNode *node)
Nicolas Capens157ba262019-12-10 17:49:14 -05001081{
1082 inst->Ice::Inst::setExternalData(node);
1083}
1084
Ben Clayton713b8d32019-12-17 20:37:56 +00001085Ice::Inst *Optimizer::getDefinition(Ice::Variable *var)
Nicolas Capens157ba262019-12-10 17:49:14 -05001086{
Ben Clayton713b8d32019-12-17 20:37:56 +00001087 return (Ice::Inst *)var->Ice::Variable::getExternalData();
Nicolas Capens157ba262019-12-10 17:49:14 -05001088}
1089
Ben Clayton713b8d32019-12-17 20:37:56 +00001090void Optimizer::setDefinition(Ice::Variable *var, Ice::Inst *inst)
Nicolas Capens157ba262019-12-10 17:49:14 -05001091{
1092 var->Ice::Variable::setExternalData(inst);
1093}
1094
Ben Clayton713b8d32019-12-17 20:37:56 +00001095const std::vector<Optimizer::LoadStoreInst> &Optimizer::getLoadStoreInsts(Ice::CfgNode *node)
Nicolas Capens157ba262019-12-10 17:49:14 -05001096{
Ben Clayton713b8d32019-12-17 20:37:56 +00001097 return *((const std::vector<LoadStoreInst> *)node->Ice::CfgNode::getExternalData());
Nicolas Capens157ba262019-12-10 17:49:14 -05001098}
1099
Ben Clayton713b8d32019-12-17 20:37:56 +00001100void Optimizer::setLoadStoreInsts(Ice::CfgNode *node, std::vector<LoadStoreInst> *insts)
Nicolas Capens157ba262019-12-10 17:49:14 -05001101{
1102 node->Ice::CfgNode::setExternalData(insts);
1103}
1104
Ben Clayton713b8d32019-12-17 20:37:56 +00001105bool Optimizer::hasLoadStoreInsts(Ice::CfgNode *node) const
Nicolas Capens157ba262019-12-10 17:49:14 -05001106{
1107 return node->Ice::CfgNode::getExternalData() != nullptr;
1108}
1109
1110bool Optimizer::Uses::areOnlyLoadStore() const
1111{
1112 return size() == (loads.size() + stores.size());
1113}
1114
1115void Optimizer::Uses::insert(Ice::Operand *value, Ice::Inst *instruction)
1116{
1117 push_back(instruction);
1118
1119 if(isLoad(*instruction))
1120 {
Nicolas Capens673a7fe2021-02-05 15:03:22 -05001121 if(value == instruction->getLoadAddress())
Nicolas Capens157ba262019-12-10 17:49:14 -05001122 {
1123 loads.push_back(instruction);
1124 }
1125 }
1126 else if(isStore(*instruction))
1127 {
Nicolas Capens673a7fe2021-02-05 15:03:22 -05001128 if(value == instruction->getStoreAddress())
Nicolas Capens157ba262019-12-10 17:49:14 -05001129 {
1130 stores.push_back(instruction);
1131 }
1132 }
1133}
1134
1135void Optimizer::Uses::erase(Ice::Inst *instruction)
1136{
1137 auto &uses = *this;
1138
1139 for(size_t i = 0; i < uses.size(); i++)
1140 {
1141 if(uses[i] == instruction)
1142 {
1143 uses[i] = back();
1144 pop_back();
1145
1146 for(size_t i = 0; i < loads.size(); i++)
1147 {
1148 if(loads[i] == instruction)
1149 {
1150 loads[i] = loads.back();
1151 loads.pop_back();
1152 break;
1153 }
1154 }
1155
1156 for(size_t i = 0; i < stores.size(); i++)
1157 {
1158 if(stores[i] == instruction)
1159 {
1160 stores[i] = stores.back();
1161 stores.pop_back();
1162 break;
1163 }
1164 }
1165
1166 break;
1167 }
1168 }
1169}
1170
Ben Clayton713b8d32019-12-17 20:37:56 +00001171} // anonymous namespace
Nicolas Capens157ba262019-12-10 17:49:14 -05001172
1173namespace rr {
1174
Nicolas Capens54313fb2021-02-19 14:26:27 -05001175void optimize(Ice::Cfg *function, Nucleus::OptimizerReport *report)
Nicolas Capens157ba262019-12-10 17:49:14 -05001176{
Nicolas Capens54313fb2021-02-19 14:26:27 -05001177 Optimizer optimizer(report);
Nicolas Capens157ba262019-12-10 17:49:14 -05001178
1179 optimizer.run(function);
1180}
1181
Antonio Maiorano614a4d42020-01-29 13:33:02 -05001182} // namespace rr