• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
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 
16 #include <algorithm>
17 #include "optimizer/ir/analysis.h"
18 #include "optimizer/ir/basicblock.h"
19 #include "optimizer/optimizations/escape.h"
20 #include "optimizer/analysis/loop_analyzer.h"
21 #include "optimizer/analysis/dominators_tree.h"
22 #include "optimizer/ir/inst.h"
23 #include "compiler/compiler_logger.h"
24 
25 namespace ark::compiler {
26 namespace {
27 constexpr ZeroInst *ZERO_INST {nullptr};
28 }  // namespace
29 
30 class VirtualState {
31 public:
VirtualState(Inst * inst,StateId id,ArenaAllocator * alloc)32     VirtualState(Inst *inst, StateId id, ArenaAllocator *alloc)
33         : inst_(inst), id_(id), fields_(alloc->Adapter()), aliases_(alloc->Adapter())
34     {
35         AddAlias(inst);
36         if (IsNewArray()) {
37             arrayComponentType_ =
38                 inst->GetBasicBlock()->GetGraph()->GetRuntime()->GetArrayComponentType(GetArrayComponentClass());
39         }
40     }
41 
42     ~VirtualState() = default;
43     NO_COPY_SEMANTIC(VirtualState);
44     NO_MOVE_SEMANTIC(VirtualState);
45 
IsNewObject() const46     bool IsNewObject() const
47     {
48         ASSERT(inst_->GetOpcode() == Opcode::NewObject || inst_->GetOpcode() == Opcode::NewArray);
49         return inst_->GetOpcode() == Opcode::NewObject;
50     }
51 
IsNewArray() const52     bool IsNewArray() const
53     {
54         return !IsNewObject();
55     }
56 
GetArrayComponentClass() const57     ClassPtr GetArrayComponentClass() const
58     {
59         ASSERT(IsNewArray());
60         return GetClass(inst_->GetDataFlowInput(0));
61     }
62 
GetArrayComponentType() const63     DataType::Type GetArrayComponentType() const
64     {
65         ASSERT(IsNewArray());
66         ASSERT(arrayComponentType_ != DataType::NO_TYPE);
67         return arrayComponentType_;
68     }
69 
SetField(Field field,StateOwner inst)70     void SetField(Field field, StateOwner inst)
71     {
72         fields_[field] = inst;
73     }
74 
GetFieldOrDefault(Field field,StateOwner defaultValue) const75     StateOwner GetFieldOrDefault(Field field, StateOwner defaultValue) const
76     {
77         auto it = fields_.find(field);
78         if (it == fields_.end()) {
79             return defaultValue;
80         }
81         return it->second;
82     }
83 
GetFields() const84     const ArenaMap<Field, StateOwner, FieldComporator> &GetFields() const
85     {
86         return fields_;
87     }
88 
GetId() const89     StateId GetId() const
90     {
91         return id_;
92     }
93 
Copy(ArenaAllocator * alloc) const94     VirtualState *Copy(ArenaAllocator *alloc) const
95     {
96         auto copy = alloc->New<VirtualState>(inst_, id_, alloc);
97         if (copy == nullptr) {
98             UNREACHABLE();
99         }
100         for (auto [ptr, id] : fields_) {
101             copy->SetField(ptr, id);
102         }
103         copy->aliases_ = aliases_;
104         return copy;
105     }
106 
GetInst() const107     Inst *GetInst() const
108     {
109         return inst_;
110     }
111 
AddAlias(Inst * inst)112     void AddAlias(Inst *inst)
113     {
114         auto it = std::find(aliases_.begin(), aliases_.end(), inst);
115         if (it != aliases_.end()) {
116             return;
117         }
118         aliases_.push_back(inst);
119     }
120 
Equals(const VirtualState * other) const121     bool Equals(const VirtualState *other) const
122     {
123         if (other == nullptr) {
124             return false;
125         }
126         if (fields_ == other->fields_) {
127             ASSERT(aliases_ == other->aliases_);
128             return true;
129         }
130         return false;
131     }
132 
GetAliases() const133     const ArenaVector<Inst *> &GetAliases() const
134     {
135         return aliases_;
136     }
137 
Dump()138     void Dump()
139     {
140         if (inst_ == nullptr) {
141             return;
142         }
143         std::cerr << "  Ptr: " << this << "\n";
144         std::cerr << "    VS for " << (IsNewObject() ? "object " : "array ") << inst_->GetId() << "\n";
145         std::cerr << "      Aliases: ";
146         for (auto alias : aliases_) {
147             std::cerr << alias->GetId() << ", ";
148         }
149         std::cerr << "\n      Fields: ";
150         for (auto [field, stateOwner] : fields_) {
151             if (std::holds_alternative<FieldPtr>(field)) {
152                 std::cerr << std::get<FieldPtr>(field) << ",";
153             } else {
154                 std::cerr << std::get<Index>(field).index << ",";
155             }
156         }
157         std::cerr << "\n";
158     }
159 
160 private:
161     Inst *inst_;
162     StateId id_;
163     ArenaMap<Field, StateOwner, FieldComporator> fields_;
164     ArenaVector<Inst *> aliases_;
165     DataType::Type arrayComponentType_ {DataType::NO_TYPE};
166 };
167 
168 class PhiState {
169 public:
PhiState(ArenaAllocator * alloc,DataType::Type type)170     PhiState(ArenaAllocator *alloc, DataType::Type type) : inputs_(alloc->Adapter()), type_(type) {}
171 
172     ~PhiState() = default;
173     NO_COPY_SEMANTIC(PhiState);
174     NO_MOVE_SEMANTIC(PhiState);
175 
AddInput(StateOwner input)176     void AddInput(StateOwner input)
177     {
178         inputs_.push_back(input);
179     }
180 
SetInput(size_t idx,StateOwner input)181     void SetInput(size_t idx, StateOwner input)
182     {
183         ASSERT(idx < inputs_.size());
184         inputs_[idx] = input;
185     }
186 
GetInputs() const187     const ArenaVector<StateOwner> &GetInputs() const
188     {
189         return inputs_;
190     }
191 
IsReference() const192     bool IsReference() const
193     {
194         return DataType::IsReference(type_);
195     }
196 
GetType() const197     DataType::Type GetType() const
198     {
199         return type_;
200     }
201 
202 private:
203     ArenaVector<StateOwner> inputs_;
204     DataType::Type type_;
205 };
206 
207 class BasicBlockState {
208 public:
BasicBlockState(ArenaAllocator * alloc)209     explicit BasicBlockState(ArenaAllocator *alloc) : states_(alloc->Adapter()), stateValues_(alloc->Adapter()) {}
210 
211     ~BasicBlockState() = default;
212     NO_COPY_SEMANTIC(BasicBlockState);
213     NO_MOVE_SEMANTIC(BasicBlockState);
214 
GetStateId(StateOwner inst) const215     StateId GetStateId(StateOwner inst) const
216     {
217         if (!std::holds_alternative<Inst *>(inst)) {
218             return EscapeAnalysis::MATERIALIZED_ID;
219         }
220         auto it = states_.find(std::get<Inst *>(inst));
221         return it == states_.end() ? EscapeAnalysis::MATERIALIZED_ID : it->second;
222     }
223 
SetStateId(const StateOwner & inst,StateId state)224     void SetStateId(const StateOwner &inst, StateId state)
225     {
226         ASSERT(std::holds_alternative<Inst *>(inst));
227         auto instInst = std::get<Inst *>(inst);
228         if (state != EscapeAnalysis::MATERIALIZED_ID) {
229             auto vstate = stateValues_[state];
230             vstate->AddAlias(instInst);
231             states_[instInst] = state;
232         } else {
233             states_.erase(instInst);
234         }
235     }
236 
SetState(const StateOwner & inst,VirtualState * vstate)237     void SetState(const StateOwner &inst, VirtualState *vstate)
238     {
239         ASSERT(std::holds_alternative<Inst *>(inst));
240         auto instInst = std::get<Inst *>(inst);
241         states_[instInst] = vstate->GetId();
242         if (stateValues_.size() <= vstate->GetId()) {
243             stateValues_.resize(vstate->GetId() + 1);
244         }
245         vstate->AddAlias(instInst);
246         stateValues_[vstate->GetId()] = vstate;
247     }
248 
HasState(const StateOwner & inst) const249     bool HasState(const StateOwner &inst) const
250     {
251         if (std::holds_alternative<Inst *>(inst)) {
252             return states_.find(std::get<Inst *>(inst)) != states_.end();
253         }
254         return false;
255     }
256 
HasStateWithId(StateId inst) const257     bool HasStateWithId(StateId inst) const
258     {
259         return stateValues_.size() > inst && stateValues_[inst] != nullptr;
260     }
261 
GetStateById(StateId state) const262     VirtualState *GetStateById(StateId state) const
263     {
264         if (state == EscapeAnalysis::MATERIALIZED_ID) {
265             return nullptr;
266         }
267         ASSERT(state < stateValues_.size());
268         auto value = stateValues_[state];
269         ASSERT(value != nullptr);
270         return value;
271     }
272 
GetState(const StateOwner & inst) const273     VirtualState *GetState(const StateOwner &inst) const
274     {
275         return GetStateById(GetStateId(inst));
276     }
277 
GetStates() const278     const ArenaMap<Inst *, StateId> &GetStates() const
279     {
280         return states_;
281     }
282 
Materialize(const StateOwner & inst)283     void Materialize(const StateOwner &inst)
284     {
285         if (!std::holds_alternative<Inst *>(inst)) {
286             return;
287         }
288         auto instInst = std::get<Inst *>(inst);
289         auto it = states_.find(instInst);
290         if (it == states_.end()) {
291             return;
292         }
293         auto stateId = it->second;
294         if (auto vstate = stateValues_[stateId]) {
295             for (auto &alias : vstate->GetAliases()) {
296                 states_.erase(alias);
297             }
298             states_.erase(vstate->GetInst());
299         } else {
300             states_.erase(instInst);
301         }
302         stateValues_[stateId] = nullptr;
303     }
304 
Clear()305     void Clear()
306     {
307         states_.clear();
308         stateValues_.clear();
309     }
310 
Copy(ArenaAllocator * alloc)311     BasicBlockState *Copy(ArenaAllocator *alloc)
312     {
313         auto copy = alloc->New<BasicBlockState>(alloc);
314         if (copy == nullptr) {
315             UNREACHABLE();
316         }
317         for (auto &[k, v] : states_) {
318             copy->states_[k] = v;
319         }
320         copy->stateValues_.resize(stateValues_.size());
321         for (StateId id = 0; id < stateValues_.size(); ++id) {
322             if (auto v = stateValues_[id]) {
323                 copy->stateValues_[id] = v->Copy(alloc);
324             }
325         }
326         return copy;
327     }
328 
Equals(const BasicBlockState * other) const329     bool Equals(const BasicBlockState *other) const
330     {
331         if (states_ != other->states_) {
332             return false;
333         }
334 
335         StateId id;
336         for (id = 0; id < std::min<size_t>(stateValues_.size(), other->stateValues_.size()); ++id) {
337             auto v = stateValues_[id];
338             auto otherState = other->stateValues_[id];
339             if ((v == nullptr) != (otherState == nullptr)) {
340                 return false;
341             }
342             if (v == nullptr) {
343                 continue;
344             }
345             if (!v->Equals(otherState)) {
346                 return false;
347             }
348         }
349         for (; id < stateValues_.size(); ++id) {
350             if (stateValues_[id] != nullptr) {
351                 return false;
352             }
353         }
354         for (; id < other->stateValues_.size(); ++id) {
355             if (other->stateValues_[id] != nullptr) {
356                 return false;
357             }
358         }
359 
360         return true;
361     }
362 
Dump()363     void Dump()
364     {
365         for (auto vs : stateValues_) {
366             if (vs == nullptr) {
367                 continue;
368             }
369             vs->Dump();
370         }
371     }
372 
373 private:
374     ArenaMap<Inst *, StateId> states_;
375     ArenaVector<VirtualState *> stateValues_;
376 };
377 
DumpVStates()378 void EscapeAnalysis::DumpVStates()
379 {
380     std::cerr << "Dump virtual states\n";
381     for (size_t i = 0; i < blockStates_.size(); ++i) {
382         if (blockStates_[i] == nullptr) {
383             continue;
384         }
385         std::cerr << "  BB " << i << "\n";
386         blockStates_[i]->Dump();
387     }
388 }
389 
DumpMStates()390 void EscapeAnalysis::DumpMStates()
391 {
392     std::cerr << "Dump materialization Info\n";
393     for (auto [msite, instVstates] : materializationInfo_) {
394         if (std::holds_alternative<Inst *>(msite)) {
395             std::cerr << "  Inst: " << std::get<Inst *>(msite)->GetId() << "\n";
396         } else {
397             std::cerr << "  BB: " << std::get<Inst *>(msite)->GetId() << "\n";
398         }
399         for (auto [inst, vstate] : instVstates) {
400             std::cerr << "    Inst: " << inst->GetId() << "\n";
401             vstate->Dump();
402         }
403     }
404 }
405 
DumpAliases()406 void EscapeAnalysis::DumpAliases()
407 {
408     std::cerr << "Dump Aliases info\n";
409     for (auto &[inst, stateOwner] : aliases_) {
410         std::cerr << "  Inst: " << *inst << "\n";
411         std::cerr << "  Aliases: " << *std::get<Inst *>(stateOwner) << "\n";
412     }
413 }
414 
Dump()415 void EscapeAnalysis::Dump()
416 {
417     DumpVStates();
418     DumpMStates();
419     DumpAliases();
420 }
421 
ProcessBlock(BasicBlock * block)422 void EscapeAnalysis::LiveInAnalysis::ProcessBlock(BasicBlock *block)
423 {
424     auto &liveIns = liveIn_[block->GetId()];
425 
426     for (auto succ : block->GetSuccsBlocks()) {
427         liveIns |= liveIn_[succ->GetId()];
428         for (auto phiInst : succ->PhiInsts()) {
429             auto phiInput = phiInst->CastToPhi()->GetPhiInput(block);
430             if (phiInput->GetBasicBlock() != block && DataType::IsReference(phiInput->GetType())) {
431                 liveIns.SetBit(phiInput->GetId());
432                 AddInst(phiInput);
433             }
434         }
435     }
436 
437     for (auto inst : block->InstsReverse()) {
438         if (DataType::IsReference(inst->GetType())) {
439             liveIns.ClearBit(inst->GetId());
440         }
441         for (auto &input : inst->GetInputs()) {
442             auto inputInst = input.GetInst();
443             if (!DataType::IsReference(inputInst->GetType())) {
444                 continue;
445             }
446             liveIns.SetBit(inputInst->GetId());
447             AddInst(inputInst);
448             auto dfInput = inst->GetDataFlowInput(inputInst);
449             if (dfInput != inputInst) {
450                 liveIns.SetBit(dfInput->GetId());
451                 AddInst(dfInput);
452             }
453         }
454     }
455 
456     // Loop header's live-ins are alive throughout the whole loop.
457     // If current block is a header then propagate its live-ins
458     // to all current loop's blocks as well as to all inner loops.
459     auto loop = block->GetLoop();
460     if (loop->IsRoot() || loop->GetHeader() != block) {
461         return;
462     }
463     for (auto loopBlock : graph_->GetVectorBlocks()) {
464         if (loopBlock == nullptr || loopBlock == block) {
465             continue;
466         }
467         if (loopBlock->GetLoop() == loop || loopBlock->GetLoop()->IsInside(loop)) {
468             auto &loopBlockLiveIns = liveIn_[loopBlock->GetId()];
469             loopBlockLiveIns |= liveIns;
470         }
471     }
472 }
473 
Run()474 bool EscapeAnalysis::LiveInAnalysis::Run()
475 {
476     bool hasAllocs = false;
477     for (auto block : graph_->GetBlocksRPO()) {
478         if (hasAllocs) {
479             break;
480         }
481         for (auto inst : block->Insts()) {
482             hasAllocs = hasAllocs || EscapeAnalysis::IsAllocInst(inst);
483             if (hasAllocs) {
484                 break;
485             }
486         }
487     }
488     if (!hasAllocs) {
489         return false;
490     }
491 
492     liveIn_.clear();
493     for (size_t idx = 0; idx < graph_->GetVectorBlocks().size(); ++idx) {
494         liveIn_.emplace_back(graph_->GetLocalAllocator());
495     }
496 
497     auto &rpo = graph_->GetBlocksRPO();
498     for (auto it = rpo.rbegin(), end = rpo.rend(); it != end; ++it) {
499         ProcessBlock(*it);
500     }
501     return true;
502 }
503 
504 // Create state corresponding to the beginning of the basic block by merging
505 // states of its predecessors.
MergeStates(BasicBlock * block)506 void EscapeAnalysis::MergeProcessor::MergeStates(BasicBlock *block)
507 {
508     if (block->GetPredsBlocks().empty()) {
509         return;
510     }
511 
512     auto newState = parent_->GetLocalAllocator()->New<BasicBlockState>(parent_->GetLocalAllocator());
513     if (newState == nullptr) {
514         UNREACHABLE();
515     }
516 
517     // Materialization of some fields may require further materalialization of other objects.
518     // Repeat merging process until no new materialization will happen.
519     bool stateChanged = true;
520     while (stateChanged) {
521         stateChanged = false;
522         newState->Clear();
523         ProcessPhis(block, newState);
524 
525         statesMergeBuffer_.clear();
526         CollectInstructionsToMerge(block);
527 
528         while (!statesMergeBuffer_.empty()) {
529             pendingInsts_.clear();
530             for (auto &inst : statesMergeBuffer_) {
531                 stateChanged = stateChanged || MergeState(inst, block, newState);
532             }
533             statesMergeBuffer_.clear();
534             statesMergeBuffer_.swap(pendingInsts_);
535         }
536         parent_->blockStates_[block->GetId()] = newState;
537     }
538     MaterializeObjectsAtTheBeginningOfBlock(block);
539 }
540 
ProcessPhis(BasicBlock * block,BasicBlockState * newState)541 void EscapeAnalysis::MergeProcessor::ProcessPhis(BasicBlock *block, BasicBlockState *newState)
542 {
543     for (auto phi : block->PhiInsts()) {
544         if (!DataType::IsReference(phi->GetType())) {
545             continue;
546         }
547         for (size_t inputIdx = 0; inputIdx < phi->GetInputsCount(); ++inputIdx) {
548             auto bb = phi->CastToPhi()->GetPhiInputBb(inputIdx);
549             parent_->Materialize(phi->GetInput(inputIdx).GetInst(), bb);
550         }
551         newState->Materialize(phi);
552     }
553 }
554 
CheckStatesAndInsertIntoBuffer(StateOwner inst,BasicBlock * block)555 void EscapeAnalysis::MergeProcessor::CheckStatesAndInsertIntoBuffer(StateOwner inst, BasicBlock *block)
556 {
557     for (auto predBlock : block->GetPredsBlocks()) {
558         auto predState = parent_->GetState(predBlock);
559         if (!predState->HasState(inst)) {
560             return;
561         }
562     }
563     statesMergeBuffer_.push_back(inst);
564 }
565 
CollectInstructionsToMerge(BasicBlock * block)566 void EscapeAnalysis::MergeProcessor::CollectInstructionsToMerge(BasicBlock *block)
567 {
568     parent_->liveIns_.VisitAlive(block, [&buffer = statesMergeBuffer_](auto inst) { buffer.push_back(inst); });
569 }
570 
MergeState(StateOwner inst,BasicBlock * block,BasicBlockState * newState)571 bool EscapeAnalysis::MergeProcessor::MergeState(StateOwner inst, BasicBlock *block, BasicBlockState *newState)
572 {
573     auto &predsBlocks = block->GetPredsBlocks();
574     auto commonId = parent_->GetState(predsBlocks.front())->GetStateId(inst);
575     // Materialization is required if predecessors have different
576     // states for the same instruction or all predecessors have materialized state for it.
577     bool materialize = commonId == EscapeAnalysis::MATERIALIZED_ID;
578     bool newMaterialization = false;
579     for (auto it = std::next(predsBlocks.begin()), end = predsBlocks.end(); it != end; ++it) {
580         auto predBlock = *it;
581         auto predId = parent_->GetState(predBlock)->GetStateId(inst);
582         if (predId == EscapeAnalysis::MATERIALIZED_ID || predId != commonId) {
583             materialize = true;
584             break;
585         }
586     }
587 
588     if (materialize) {
589         newMaterialization =
590             parent_->GetState(block->GetDominator())->GetStateId(inst) != EscapeAnalysis::MATERIALIZED_ID;
591         parent_->Materialize(inst, block->GetDominator());
592         newState->Materialize(inst);
593         return newMaterialization;
594     }
595 
596     if (newState->HasStateWithId(commonId)) {
597         // we already handled that vstate so we should skip further processing
598         newState->SetStateId(inst, commonId);
599         return false;
600     }
601 
602     auto vstate = parent_->CreateVState(
603         parent_->GetState(block->GetPredsBlocks().front())->GetStateById(commonId)->GetInst(), commonId);
604     newMaterialization = MergeFields(block, newState, commonId, vstate, pendingInsts_);
605     newState->SetState(inst, vstate);
606     // if inst is an alias then we also need to process original inst
607     pendingInsts_.push_back(vstate->GetInst());
608     return newMaterialization;
609 }
610 
MergeFields(BasicBlock * block,BasicBlockState * blockState,StateId stateToMerge,VirtualState * vstate,ArenaVector<StateOwner> & mergeQueue)611 bool EscapeAnalysis::MergeProcessor::MergeFields(BasicBlock *block, BasicBlockState *blockState, StateId stateToMerge,
612                                                  VirtualState *vstate, ArenaVector<StateOwner> &mergeQueue)
613 {
614     allFields_.clear();
615     for (auto pred : block->GetPredsBlocks()) {
616         for (auto &field : parent_->GetState(pred)->GetStateById(stateToMerge)->GetFields()) {
617             allFields_.push_back(field.first);
618         }
619     }
620     std::sort(allFields_.begin(), allFields_.end());
621     auto end = std::unique(allFields_.begin(), allFields_.end());
622 
623     for (auto it = allFields_.begin(); it != end; ++it) {
624         auto field = *it;
625         fieldsMergeBuffer_.clear();
626         // Use ZERO_INST as a placeholder for a field that was not set.
627         // When it'll come to scalar replacement this placeholder will be
628         // replaced with actual zero or nullptr const.
629         auto mergeState = parent_->GetState(block->GetPredsBlocks().front())->GetStateById(stateToMerge);
630         StateOwner commonId = mergeState->GetFieldOrDefault(field, ZERO_INST);
631         bool needMerge = false;
632         for (auto predBlock : block->GetPredsBlocks()) {
633             auto predState = parent_->GetState(predBlock)->GetStateById(stateToMerge);
634             auto predFieldValue = predState->GetFieldOrDefault(field, ZERO_INST);
635             if (commonId != predFieldValue) {
636                 needMerge = true;
637             }
638             fieldsMergeBuffer_.push_back(predFieldValue);
639         }
640         if (!needMerge) {
641             vstate->SetField(field, commonId);
642             // enqueue inst id for state copying
643             if (!blockState->HasState(commonId)) {
644                 mergeQueue.push_back(commonId);
645             }
646             continue;
647         }
648         auto [phi, newMaterialization] = parent_->CreatePhi(block, blockState, field, fieldsMergeBuffer_, vstate);
649         vstate->SetField(field, phi);
650         if (newMaterialization) {
651             return true;
652         }
653     }
654     return false;
655 }
656 
MaterializeObjectsAtTheBeginningOfBlock(BasicBlock * block)657 void EscapeAnalysis::MergeProcessor::MaterializeObjectsAtTheBeginningOfBlock(BasicBlock *block)
658 {
659     // Find all objects that should be materialized at the beginning of given block.
660     auto mIt = parent_->materializationInfo_.find(block);
661     auto blockState = parent_->GetState(block);
662     if (mIt == parent_->materializationInfo_.end()) {
663         return;
664     }
665     auto &matStates = mIt->second;
666     for (auto statesIt = matStates.begin(); statesIt != matStates.end();) {
667         auto inst = statesIt->first;
668         // If the instruction has virtual state then materialize it and save
669         // copy of a virtual state (it will be used to initialize fields after allocation).
670         // Otherwise remove the instruction from list of instruction requiring materialization
671         // at this block.
672         if (blockState->GetStateId(inst) != EscapeAnalysis::MATERIALIZED_ID) {
673             statesIt->second = blockState->GetState(inst)->Copy(parent_->GetLocalAllocator());
674             blockState->Materialize(inst);
675             ++statesIt;
676         } else {
677             statesIt = matStates.erase(statesIt);
678         }
679     }
680 }
681 
Initialize()682 void EscapeAnalysis::Initialize()
683 {
684     auto &blocks = GetGraph()->GetVectorBlocks();
685     blockStates_.resize(blocks.size());
686     phis_.reserve(blocks.size());
687     for (auto block : blocks) {
688         if (block != nullptr) {
689             if (auto newBlock = GetLocalAllocator()->New<BasicBlockState>(GetLocalAllocator())) {
690                 blockStates_[block->GetId()] = newBlock;
691             } else {
692                 UNREACHABLE();
693             }
694         }
695         phis_.emplace_back(GetLocalAllocator()->Adapter());
696     }
697 
698     PropagateLoadObjectsUpwards();
699     if (!GetGraph()->IsAnalysisValid<LoopAnalyzer>()) {
700         GetGraph()->RunPass<LoopAnalyzer>();
701     }
702     if (!GetGraph()->IsAnalysisValid<DominatorsTree>()) {
703         GetGraph()->RunPass<DominatorsTree>();
704     }
705 }
706 
707 /*
708  * Propagate upwards the LoadObjects that are users of a ref Phi
709  *
710  * Requirements:
711  * - the phi has NewObject instructions as it's inputs
712  * - all the phi users are LoadObject instructions
713 
714  considering the above we can remove the original phi after propagating
715  all the load instructions upwards and adding corresponding phi instructions
716  for them in the original phi's basic block
717 
718  for each user in users(phi) do
719    create new phi of type(user)
720    for each input in inputs(phi) do
721      create load instuction of type(user)
722      insert load before bb(input).last
723      set load input to input(phi, bb(input))
724      add load to new phi inputs
725    end
726    add users(user) to the new phi instruction
727    remove user
728  end
729  remove phi
730 
731  BB 1            BB 2
732  1.NewObject     2.NewObject
733       |               |
734       ----Phi(1, 2)----
735                |
736       t1.LoadObject(Phi, f1)
737       t2.LoadObject(Phi, f2)
738 
739 
740  BB 1                     BB 2
741  1.NewObject              2.NewObject
742  10.t1.LoadObject(1, f1)  20.t1.LoadObject(2, f1)
743  11.t2.LoadObject(1, f2)  21.t2.LoadObject(2, f2)
744       |                            |
745       ----------t1.Phi(10,20)-------
746                 t2.Phi(11,21)
747 */
748 
PhiHasLoadObjToPropagate(Inst * phi)749 static bool PhiHasLoadObjToPropagate(Inst *phi)
750 {
751     if (!DataType::IsReference(phi->GetType())) {
752         return false;
753     }
754 
755     ObjectTypeInfo typeinfo = {};
756     for (auto input : phi->GetInputs()) {
757         auto inst = input.GetInst();
758         if (inst->GetOpcode() != Opcode::NewObject) {
759             return false;
760         }
761         if (!typeinfo) {
762             typeinfo = inst->GetObjectTypeInfo();
763             continue;
764         }
765         if (inst->GetObjectTypeInfo() != typeinfo) {
766             return false;
767         }
768     }
769 
770     if (!typeinfo) {
771         return false;
772     }
773 
774     for (auto &user : phi->GetUsers()) {
775         if (user.GetInst()->GetOpcode() != Opcode::LoadObject) {
776             return false;
777         }
778     }
779     return true;
780 }
781 
782 #ifndef NDEBUG
EnsureLoadsRemoval()783 bool EscapeAnalysis::EnsureLoadsRemoval()
784 {
785     for (auto bb : GetGraph()->GetBlocksRPO()) {
786         for (auto inst : bb->Insts()) {
787             if (inst->GetOpcode() == Opcode::LoadObject && inst->IsMarked(removableLoads_)) {
788                 return false;
789             }
790         }
791     }
792     return true;
793 }
794 #endif
795 
CreateTemporaryLoads(Inst * phi)796 void EscapeAnalysis::CreateTemporaryLoads(Inst *phi)
797 {
798     for (auto &user : phi->GetUsers()) {
799         auto ld = user.GetInst();
800         auto type = ld->GetType();
801         auto newPhi = GetGraph()->CreateInstPhi(type, phi->GetPc());
802         for (size_t i = 0; i < phi->GetInputsCount(); ++i) {
803             auto ldClone = ld->Clone(GetGraph());
804             ldClone->SetInput(0, phi->GetInput(i).GetInst());
805             auto insertAt = phi->CastToPhi()->GetPhiInputBb(i)->GetLastInst();
806             insertAt = insertAt->IsControlFlow() ? insertAt->GetPrev() : insertAt;
807             insertAt->InsertAfter(ldClone);
808             newPhi->AppendInput(ldClone);
809             newPhi->SetPhiInputBbNum(i, phi->CastToPhi()->GetPhiInputBbNum(i));
810         }
811         ld->ReplaceUsers(newPhi);
812         phi->InsertAfter(newPhi);
813         phi->RemoveUser(&user);
814         ld->GetBasicBlock()->RemoveInst(ld);
815 #ifndef NDEBUG
816         ld->SetMarker(removableLoads_);
817 #endif
818     }
819 }
820 
PropagateLoadObjectsUpwards()821 void EscapeAnalysis::PropagateLoadObjectsUpwards()
822 {
823     for (auto bb : GetGraph()->GetBlocksRPO()) {
824         for (auto phi : bb->PhiInstsSafe()) {
825             if (!PhiHasLoadObjToPropagate(phi)) {
826                 continue;
827             }
828             CreateTemporaryLoads(phi);
829             phi->GetBasicBlock()->RemoveInst(phi);
830         }
831     }
832 }
833 
RunImpl()834 bool EscapeAnalysis::RunImpl()
835 {
836     if (!GetGraph()->HasEndBlock()) {
837         return false;
838     }
839 #ifndef NDEBUG
840     MarkerHolder mh {GetGraph()};
841     removableLoads_ = mh.GetMarker();
842 #endif
843     Initialize();
844 
845     if (!liveIns_.Run()) {
846         return false;
847     }
848 
849     if (!FindVirtualizableAllocations()) {
850         return false;
851     }
852 
853     if (virtualizableAllocations_.empty()) {
854         COMPILER_LOG(DEBUG, PEA) << "No allocations to virtualize";
855         return false;
856     }
857 
858     ScalarReplacement sr {GetGraph(), aliases_, phis_, materializationInfo_, saveStateInfo_};
859     sr.Apply(virtualizableAllocations_);
860 #ifndef NDEBUG
861     ASSERT(EnsureLoadsRemoval());
862 #endif
863     return true;
864 }
865 
AllPredecessorsVisited(const BasicBlock * block)866 bool EscapeAnalysis::AllPredecessorsVisited(const BasicBlock *block)
867 {
868     for (auto pred : block->GetPredsBlocks()) {
869         if (!pred->IsMarked(visited_)) {
870             return false;
871         }
872     }
873     return true;
874 }
875 
FindVirtualizableAllocations()876 bool EscapeAnalysis::FindVirtualizableAllocations()
877 {
878     virtualizationChanged_ = true;
879     while (virtualizationChanged_) {
880         stateId_ = 1U;
881         virtualizationChanged_ = false;
882         MarkerHolder mh {GetGraph()};
883         visited_ = mh.GetMarker();
884 
885         for (auto block : GetGraph()->GetBlocksRPO()) {
886             if (!ProcessBlock(block)) {
887                 return false;
888             }
889         }
890     }
891     return true;
892 }
893 
MaterializeDeoptSaveState(Inst * inst)894 void EscapeAnalysis::MaterializeDeoptSaveState(Inst *inst)
895 {
896     auto ss = inst->GetSaveState();
897     if (ss == nullptr) {
898         return;
899     }
900     // If deoptimization is happening inside inlined call then
901     // all objects captured in inlined call's save state should be materialized
902     // to correctly restore values of virtual registers in interpreted frame during deoptimization.
903     if (auto caller = ss->GetCallerInst()) {
904         MaterializeDeoptSaveState(caller);
905     }
906     for (auto input : ss->GetInputs()) {
907         auto blockState = GetState(ss->GetBasicBlock());
908         // We're trying to materialize an object at the save state predecessing current instruction,
909         // as a result the object might be alredy materialized in this basic block. If it actually is
910         // then register its materialization at current save state to ensure that on the next iteration
911         // it will be materialized here. Otherwise try to find a state owner and materialize it instead
912         // of an alias.
913         if (auto state = blockState->GetState(input.GetInst())) {
914             RegisterMaterialization(ss, state->GetInst());
915             blockState->Materialize(input.GetInst());
916         } else {
917             RegisterMaterialization(ss, input.GetInst());
918         }
919     }
920     Materialize(inst);
921 }
922 
CreatePhi(BasicBlock * targetBlock,BasicBlockState * blockState,Field field,ArenaVector<StateOwner> & inputs,VirtualState * state)923 std::pair<PhiState *, bool> EscapeAnalysis::CreatePhi(BasicBlock *targetBlock, BasicBlockState *blockState, Field field,
924                                                       ArenaVector<StateOwner> &inputs, VirtualState *state)
925 {
926     // try to reuse existing phi
927     auto it = phis_.at(targetBlock->GetId()).find(field);
928     if (it != phis_.at(targetBlock->GetId()).end()) {
929         auto phi = it->second;
930         for (size_t idx = 0; idx < inputs.size(); ++idx) {
931             ASSERT(GetState(targetBlock->GetPredsBlocks()[idx])->GetStateId(inputs[idx]) == MATERIALIZED_ID);
932             phi->SetInput(idx, inputs[idx]);
933         }
934         blockState->Materialize(phi);
935         return std::make_pair(phi, false);
936     }
937     auto fieldType = DataType::INT32;
938     if (std::holds_alternative<FieldPtr>(field)) {
939         fieldType = GetGraph()->GetRuntime()->GetFieldType(std::get<FieldPtr>(field));
940     } else {
941         ASSERT(std::holds_alternative<Index>(field));
942         fieldType = state->GetArrayComponentType();
943     }
944     auto phiState = GetLocalAllocator()->New<PhiState>(GetLocalAllocator(), fieldType);
945     if (phiState == nullptr) {
946         UNREACHABLE();
947     }
948     auto &preds = targetBlock->GetPredsBlocks();
949     ASSERT(inputs.size() == preds.size());
950     bool newMaterialization = false;
951     for (size_t idx = 0; idx < inputs.size(); ++idx) {
952         phiState->AddInput(inputs[idx]);
953         newMaterialization = newMaterialization || GetState(preds[idx])->GetStateId(inputs[idx]) != MATERIALIZED_ID;
954         Materialize(inputs[idx], preds[idx]);
955     }
956     if (phiState->IsReference()) {
957         // phi is always materialized
958         blockState->Materialize(phiState);
959     }
960     phis_.at(targetBlock->GetId())[field] = phiState;
961     return std::make_pair(phiState, newMaterialization);
962 }
963 
MaterializeInBlock(StateOwner inst,BasicBlock * block)964 void EscapeAnalysis::MaterializeInBlock(StateOwner inst, BasicBlock *block)
965 {
966     auto blockState = GetState(block);
967     inst = blockState->GetState(inst)->GetInst();
968     if (blockState->GetStateId(inst) == EscapeAnalysis::MATERIALIZED_ID) {
969         return;
970     }
971     RegisterMaterialization(block, std::get<Inst *>(inst));
972     auto instState = blockState->GetState(inst);
973     blockState->Materialize(inst);
974     for (auto &t : instState->GetFields()) {
975         auto &fieldInst = t.second;
976         if (blockState->GetStateId(fieldInst) == MATERIALIZED_ID) {
977             continue;
978         }
979         MaterializeInBlock(fieldInst, block);
980     }
981 }
982 
Materialize(StateOwner inst,BasicBlock * block)983 void EscapeAnalysis::Materialize(StateOwner inst, BasicBlock *block)
984 {
985     if (GetState(block)->GetStateId(inst) == EscapeAnalysis::MATERIALIZED_ID) {
986         return;
987     }
988     if (block->IsEmpty()) {
989         MaterializeInBlock(inst, block);
990     } else {
991         Materialize(inst, block->GetLastInst());
992     }
993 }
994 
Materialize(StateOwner inst,Inst * before)995 void EscapeAnalysis::Materialize(StateOwner inst, Inst *before)
996 {
997     ASSERT(before != nullptr);
998     auto blockState = GetState(before->GetBasicBlock());
999     if (blockState->GetStateId(inst) == EscapeAnalysis::MATERIALIZED_ID) {
1000         return;
1001     }
1002     if (!std::holds_alternative<Inst *>(inst)) {
1003         return;
1004     }
1005 
1006     COMPILER_LOG(DEBUG, PEA) << "Materialized " << *std::get<Inst *>(inst) << " before " << *before;
1007     auto instState = blockState->GetState(inst);
1008     // If an inst is an alias (like NullCheck inst) then get original NewObject inst from the state;
1009     inst = instState->GetInst();
1010     auto targetInst = std::get<Inst *>(inst);
1011     // Mark object non-virtualizable if it should be materialized in the same block it was allocated.
1012     Inst *res = before;
1013     bool canMaterializeFields = true;
1014     if (targetInst->GetBasicBlock() == before->GetBasicBlock()) {
1015         res = targetInst;
1016         canMaterializeFields = false;
1017     } else if (auto ss = before->GetSaveState()) {
1018         // In some cases (for example, when before is ReturnInlined) SaveState may be
1019         // placed in some other basic block and materialized object may not be defined at it yet.
1020         if (ss->GetBasicBlock() == before->GetBasicBlock()) {
1021             res = ss;
1022         }
1023     } else if (before->IsControlFlow() && before->GetPrev() != nullptr) {
1024         // Typical case is when block ends with If or IfImm and previous
1025         // instruction is Compare. To avoid materialization in between (which may prevent other optimizations)
1026         // use previous instruction as materialization point.
1027         auto prev = before->GetPrev();
1028         if ((before->GetOpcode() == Opcode::If || before->GetOpcode() == Opcode::IfImm) &&
1029             prev->GetOpcode() == Opcode::Compare) {
1030             res = before->GetPrev();
1031         }
1032     }
1033     auto prevState = blockState->GetState(inst);
1034     blockState->Materialize(inst);
1035     RegisterMaterialization(res, targetInst);
1036     for (auto &t : prevState->GetFields()) {
1037         auto &fieldInst = t.second;
1038         if (blockState->GetStateId(fieldInst) == MATERIALIZED_ID) {
1039             continue;
1040         }
1041         if (canMaterializeFields) {
1042             Materialize(fieldInst, res);
1043         } else {
1044             blockState->Materialize(fieldInst);
1045         }
1046     }
1047 }
1048 
Materialize(Inst * inst)1049 void EscapeAnalysis::Materialize(Inst *inst)
1050 {
1051     for (auto &input : inst->GetInputs()) {
1052         if (DataType::IsReference(input.GetInst()->GetType())) {
1053             Materialize(input.GetInst(), inst);
1054         }
1055     }
1056     if (inst->NoDest() && !inst->HasPseudoDestination()) {
1057         return;
1058     }
1059     if (DataType::IsReference(inst->GetType())) {
1060         GetState(inst->GetBasicBlock())->SetStateId(inst, EscapeAnalysis::MATERIALIZED_ID);
1061     }
1062 }
1063 
RegisterMaterialization(MaterializationSite site,Inst * inst)1064 void EscapeAnalysis::RegisterMaterialization(MaterializationSite site, Inst *inst)
1065 {
1066     auto alloc = GetLocalAllocator();
1067     auto &matInfo = materializationInfo_.try_emplace(site, alloc->Adapter()).first->second;
1068     if (matInfo.find(inst) != matInfo.end()) {
1069         return;
1070     }
1071     virtualizationChanged_ = true;
1072     matInfo[inst] = nullptr;
1073 }
1074 
1075 // Register all states' virtual fields as "should be materialized" at given site.
RegisterFieldsMaterialization(Inst * site,VirtualState * state,BasicBlockState * blockState,const ArenaMap<Inst *,VirtualState * > & states)1076 bool EscapeAnalysis::RegisterFieldsMaterialization(Inst *site, VirtualState *state, BasicBlockState *blockState,
1077                                                    const ArenaMap<Inst *, VirtualState *> &states)
1078 {
1079     bool materializedFields = false;
1080     for (auto &fields : state->GetFields()) {
1081         auto fieldStateId = blockState->GetStateId(fields.second);
1082         // Skip already materialized objects and objects already registered for materialization at given site.
1083         if (fieldStateId == MATERIALIZED_ID || (states.count(std::get<Inst *>(fields.second)) != 0)) {
1084             continue;
1085         }
1086         materializedFields = true;
1087         ASSERT(std::holds_alternative<Inst *>(fields.second));
1088         RegisterMaterialization(site, std::get<Inst *>(fields.second));
1089     }
1090     return materializedFields;
1091 }
1092 
RegisterVirtualObjectFieldsForMaterialization(Inst * ss)1093 void EscapeAnalysis::RegisterVirtualObjectFieldsForMaterialization(Inst *ss)
1094 {
1095     auto states = materializationInfo_.find(ss);
1096     ASSERT(states != materializationInfo_.end());
1097 
1098     auto blockState = GetState(ss->GetBasicBlock());
1099     bool materializedFields = true;
1100     while (materializedFields) {
1101         materializedFields = false;
1102         for (auto &t : states->second) {
1103             auto state = blockState->GetState(t.first);
1104             // Object has materialized state
1105             if (state == nullptr) {
1106                 continue;
1107             }
1108             materializedFields =
1109                 materializedFields || RegisterFieldsMaterialization(ss, state, blockState, states->second);
1110         }
1111     }
1112 }
1113 
CreateVState(Inst * inst)1114 VirtualState *EscapeAnalysis::CreateVState(Inst *inst)
1115 {
1116     auto vstate = GetLocalAllocator()->New<VirtualState>(inst, stateId_++, GetLocalAllocator());
1117     if (vstate == nullptr) {
1118         UNREACHABLE();
1119     }
1120     return vstate;
1121 }
1122 
CreateVState(Inst * inst,StateId id)1123 VirtualState *EscapeAnalysis::CreateVState(Inst *inst, StateId id)
1124 {
1125     auto vstate = GetLocalAllocator()->New<VirtualState>(inst, id, GetLocalAllocator());
1126     if (vstate == nullptr) {
1127         UNREACHABLE();
1128     }
1129     return vstate;
1130 }
1131 
VisitCmpRef(Inst * inst,ConditionCode cc)1132 void EscapeAnalysis::VisitCmpRef(Inst *inst, ConditionCode cc)
1133 {
1134     if (!DataType::IsReference(inst->GetInputType(0U))) {
1135         return;
1136     }
1137     auto blockState = GetState(inst->GetBasicBlock());
1138     auto lhs = inst->GetInput(0).GetInst();
1139     auto rhs = inst->GetInput(1).GetInst();
1140     auto lhsStateId = blockState->GetStateId(lhs);
1141     auto rhsStateId = blockState->GetStateId(rhs);
1142     if (rhsStateId == MATERIALIZED_ID && lhsStateId == MATERIALIZED_ID) {
1143         aliases_.erase(inst);
1144         return;
1145     }
1146     auto sameState = lhsStateId == rhsStateId;
1147     bool cmpResult = sameState == (cc == ConditionCode::CC_EQ);
1148     aliases_[inst] = GetGraph()->FindOrCreateConstant(cmpResult);
1149 }
1150 
VisitAllocation(Inst * inst)1151 void EscapeAnalysis::VisitAllocation(Inst *inst)
1152 {
1153     ASSERT(IsAllocInst(inst));
1154     VisitSaveStateUser(inst);
1155 
1156     // There are several reasons to materialize an object right at the allocation site:
1157     // (1) the object is an input for some instruction inside a catch block
1158     // (2) we already marked the object as one requiring materialization
1159     bool materialize =
1160         inst->GetFlag(inst_flags::Flags::CATCH_INPUT) || materializationInfo_.find(inst) != materializationInfo_.end();
1161 
1162     if (!relaxClassRestrictions_) {
1163         auto klassInput = inst->GetInput(0).GetInst();
1164         if (!materialize) {
1165             auto opc = klassInput->GetOpcode();
1166             // (3) object's class originated from some instruction we can't handle
1167             materialize = opc != Opcode::LoadImmediate && opc != Opcode::LoadAndInitClass && opc != Opcode::LoadClass;
1168         }
1169         if (!materialize) {
1170             RuntimeInterface::ClassPtr klassPtr;
1171             if (klassInput->GetOpcode() == Opcode::LoadImmediate) {
1172                 klassPtr = klassInput->CastToLoadImmediate()->GetObject();
1173             } else {
1174                 klassPtr = static_cast<ClassInst *>(klassInput)->GetClass();
1175             }
1176             // (4) object's class is not instantiable (for example, it's an interface or an abstract class)
1177             // (5) we can't apply scalar replacement for this particular class (for example, a class declares a function
1178             //     to invoke before GC (like LUA's finalizable objects))
1179             materialize = !GetGraph()->GetRuntime()->IsInstantiable(klassPtr) ||
1180                           !GetGraph()->GetRuntime()->CanScalarReplaceObject(klassPtr);
1181         }
1182     }
1183 
1184     if (materialize) {
1185         GetState(inst->GetBasicBlock())->Materialize(inst);
1186         RemoveVirtualizableAllocation(inst);
1187         return;
1188     }
1189     auto vstate = CreateVState(inst);
1190     GetState(inst->GetBasicBlock())->SetState(inst, vstate);
1191     AddVirtualizableAllocation(inst);
1192 }
1193 
VisitNewObject(Inst * inst)1194 void EscapeAnalysis::VisitNewObject(Inst *inst)
1195 {
1196     VisitAllocation(inst);
1197 }
1198 
VisitNewArray(Inst * inst)1199 void EscapeAnalysis::VisitNewArray(Inst *inst)
1200 {
1201     VisitAllocation(inst);
1202 }
1203 
VisitNullCheck(Inst * inst)1204 void EscapeAnalysis::VisitNullCheck(Inst *inst)
1205 {
1206     auto blockState = GetState(inst->GetBasicBlock());
1207 
1208     if (inst->GetFlag(inst_flags::Flags::CATCH_INPUT)) {
1209         Materialize(inst->GetDataFlowInput(0), inst);
1210         VisitSaveStateUser(inst);
1211         return;
1212     }
1213 
1214     VisitSaveStateUser(inst);
1215 
1216     auto aliasedInst = inst->GetDataFlowInput(0);
1217     auto aliasedStateId = blockState->GetStateId(aliasedInst);
1218     blockState->SetStateId(inst, aliasedStateId);
1219     if (aliasedStateId != MATERIALIZED_ID) {
1220         aliases_[inst] = blockState->GetState(aliasedInst)->GetInst();
1221     } else {
1222         aliases_.erase(inst);
1223     }
1224 }
1225 
VisitBlockInternal(BasicBlock * block)1226 void EscapeAnalysis::VisitBlockInternal(BasicBlock *block)
1227 {
1228     for (auto inst : block->AllInsts()) {
1229         HandleMaterializationSite(inst);
1230         TABLE[static_cast<unsigned>(inst->GetOpcode())](this, inst);
1231     }
1232 }
1233 
HandleMaterializationSite(Inst * inst)1234 void EscapeAnalysis::HandleMaterializationSite(Inst *inst)
1235 {
1236     auto it = materializationInfo_.find(inst);
1237     if (it == materializationInfo_.end()) {
1238         return;
1239     }
1240     auto blockState = GetState(inst->GetBasicBlock());
1241     // Ensure that for every object registered for materialization at the save state
1242     // all fields will be also registered for materialization here.
1243     RegisterVirtualObjectFieldsForMaterialization(inst);
1244 
1245     ArenaMap<Inst *, VirtualState *> &instsMap = it->second;
1246     ArenaVector<Inst *> pendingInsts {GetGraph()->GetLocalAllocator()->Adapter()};
1247     // If an alias was registered for materialization then try to find original instruction
1248     // and register it too.
1249     for (auto &t : instsMap) {
1250         // Allocation marked as non-virtualizable
1251         if (t.first == inst) {
1252             continue;
1253         }
1254         if (auto vstate = blockState->GetState(t.first)) {
1255             if (vstate->GetInst() != t.first && instsMap.count(vstate->GetInst()) == 0) {
1256                 pendingInsts.push_back(vstate->GetInst());
1257             }
1258         }
1259     }
1260     for (auto newInst : pendingInsts) {
1261         // insert only a key, value will be populated later
1262         instsMap[newInst] = nullptr;
1263     }
1264 
1265     for (auto &t : instsMap) {
1266         // skip aliases
1267         if (!EscapeAnalysis::IsAllocInst(t.first) || t.first == inst) {
1268             continue;
1269         }
1270         auto candidateInst = t.first;
1271         if (auto vstate = blockState->GetState(candidateInst)) {
1272             // make a snapshot of virtual object's fields to use it during
1273             // scalar replacement to correctly initialize materialized object
1274             t.second = vstate->Copy(GetLocalAllocator());
1275             blockState->Materialize(candidateInst);
1276         } else {
1277             // instruction is already materialized, clear it's state snapshot
1278             t.second = nullptr;
1279         }
1280     }
1281 }
1282 
VisitSaveState(Inst * inst)1283 void EscapeAnalysis::VisitSaveState(Inst *inst)
1284 {
1285     FillVirtualInputs(inst);
1286 }
1287 
VisitSaveStateDeoptimize(Inst * inst)1288 void EscapeAnalysis::VisitSaveStateDeoptimize(Inst *inst)
1289 {
1290     FillVirtualInputs(inst);
1291 }
1292 
VisitSafePoint(Inst * inst)1293 void EscapeAnalysis::VisitSafePoint(Inst *inst)
1294 {
1295     FillVirtualInputs(inst);
1296 }
1297 
VisitGetInstanceClass(Inst * inst)1298 void EscapeAnalysis::VisitGetInstanceClass(Inst *inst)
1299 {
1300     auto blockState = GetState(inst->GetBasicBlock());
1301     auto input = inst->GetInput(0).GetInst();
1302     if (auto vstate = blockState->GetState(input)) {
1303         auto newObj = vstate->GetInst();
1304         auto loadClass = newObj->GetInput(0).GetInst();
1305         if (!loadClass->IsClassInst() && loadClass->GetOpcode() != Opcode::GetInstanceClass) {
1306             return;
1307         }
1308         aliases_[inst] = loadClass;
1309     }
1310 }
1311 
FillVirtualInputs(Inst * inst)1312 void EscapeAnalysis::FillVirtualInputs(Inst *inst)
1313 {
1314     auto blockState = GetState(inst->GetBasicBlock());
1315     auto &states = saveStateInfo_.try_emplace(inst, GetLocalAllocator()).first->second;
1316     states.clear();
1317     // We can iterate over set bits only if it is not the first time we call this method
1318     // for the inst.
1319     for (size_t inputIdx = 0; inputIdx < inst->GetInputsCount(); ++inputIdx) {
1320         auto inputInst = inst->GetDataFlowInput(inputIdx);
1321         if (!DataType::IsReference(inputInst->GetType())) {
1322             continue;
1323         }
1324         if (blockState->GetStateId(inputInst) != MATERIALIZED_ID) {
1325             states.SetBit(inputIdx);
1326         }
1327     }
1328 }
1329 
VisitCall(CallInst * inst)1330 void EscapeAnalysis::VisitCall(CallInst *inst)
1331 {
1332     if (inst->IsInlined()) {
1333         FillVirtualInputs(inst);
1334     } else {
1335         Materialize(inst);
1336     }
1337     VisitSaveStateUser(inst);
1338 }
1339 
VisitLoadObject(Inst * inst)1340 void EscapeAnalysis::VisitLoadObject(Inst *inst)
1341 {
1342     auto vstate = GetState(inst->GetBasicBlock())->GetState(inst->GetDataFlowInput(0));
1343     auto field = inst->CastToLoadObject()->GetObjField();
1344 
1345     if (vstate != nullptr) {
1346         aliases_[inst] = vstate->GetFieldOrDefault(field, ZERO_INST);
1347     } else {
1348         aliases_.erase(inst);
1349     }
1350 
1351     if (DataType::IsReference(inst->GetType())) {
1352         if (vstate == nullptr) {
1353             GetState(inst->GetBasicBlock())->SetStateId(inst, MATERIALIZED_ID);
1354             return;
1355         }
1356         auto fieldInstId = vstate->GetFieldOrDefault(field, ZERO_INST);
1357         GetState(inst->GetBasicBlock())->SetStateId(inst, GetState(inst->GetBasicBlock())->GetStateId(fieldInstId));
1358     }
1359 }
1360 
VisitLoadArray(Inst * inst)1361 void EscapeAnalysis::VisitLoadArray(Inst *inst)
1362 {
1363     auto array = inst->GetDataFlowInput(0);
1364     auto vstate = GetState(inst->GetBasicBlock())->GetState(array);
1365     auto index = inst->GetDataFlowInput(1U);
1366     if (!index->IsConst()) {
1367         Materialize(array, inst);
1368         return;
1369     }
1370 
1371     if (vstate != nullptr) {
1372         aliases_[inst] = vstate->GetFieldOrDefault(
1373             Index {vstate->GetArrayComponentClass(), index->CastToConstant()->GetInt64Value()}, ZERO_INST);
1374     } else {
1375         aliases_.erase(inst);
1376     }
1377 
1378     if (!DataType::IsReference(inst->GetType())) {
1379         return;
1380     }
1381     if (vstate == nullptr) {
1382         GetState(inst->GetBasicBlock())->SetStateId(inst, MATERIALIZED_ID);
1383         return;
1384     }
1385     auto fieldInstId = vstate->GetFieldOrDefault(
1386         Index {vstate->GetArrayComponentClass(), index->CastToConstant()->GetInt64Value()}, ZERO_INST);
1387     GetState(inst->GetBasicBlock())->SetStateId(inst, GetState(inst->GetBasicBlock())->GetStateId(fieldInstId));
1388 }
1389 
VisitStoreObject(Inst * inst)1390 void EscapeAnalysis::VisitStoreObject(Inst *inst)
1391 {
1392     auto vstate = GetState(inst->GetBasicBlock())->GetState(inst->GetDataFlowInput(0));
1393     auto field = inst->CastToStoreObject()->GetObjField();
1394 
1395     if (vstate != nullptr) {
1396         vstate->SetField(field, inst->GetDataFlowInput(1U));
1397         // mark inst for removal
1398         aliases_[inst] = inst;
1399     } else {
1400         if (DataType::IsReference(inst->GetType())) {
1401             Materialize(inst->GetDataFlowInput(1U), inst);
1402         }
1403         aliases_.erase(inst);
1404     }
1405 }
1406 
VisitStoreArray(Inst * inst)1407 void EscapeAnalysis::VisitStoreArray(Inst *inst)
1408 {
1409     auto array = inst->GetDataFlowInput(0);
1410     auto vstate = GetState(inst->GetBasicBlock())->GetState(array);
1411     auto index = inst->GetDataFlowInput(1U);
1412     if (!index->IsConst()) {
1413         Materialize(array, inst);
1414         if (DataType::IsReference(inst->GetType())) {
1415             Materialize(inst->GetDataFlowInput(2U), inst);
1416         }
1417         return;
1418     }
1419     if (vstate != nullptr) {
1420         auto indexCnst = index->CastToConstant()->GetInt64Value();
1421         vstate->SetField(Index {vstate->GetArrayComponentClass(), indexCnst}, inst->GetDataFlowInput(2U));
1422         // mark inst for removal
1423         aliases_[inst] = inst;
1424     } else {
1425         if (DataType::IsReference(inst->GetType())) {
1426             Materialize(inst->GetDataFlowInput(2U), inst);
1427         }
1428         aliases_.erase(inst);
1429     }
1430 }
1431 
VisitSaveStateUser(Inst * inst)1432 void EscapeAnalysis::VisitSaveStateUser(Inst *inst)
1433 {
1434     auto ss = inst->GetSaveState();
1435     if (ss == nullptr || ss->GetOpcode() != Opcode::SaveState) {
1436         return;
1437     }
1438 
1439     auto blockState = GetState(inst->GetBasicBlock());
1440 
1441     // If an instruction is materialized at save state user
1442     // then it should be materialized before the save state too.
1443     auto virtualInputs = saveStateInfo_.at(ss);
1444     for (auto inputIdx : virtualInputs.GetSetBitsIndices()) {
1445         auto inputInst = ss->GetInput(inputIdx).GetInst();
1446         if (blockState->GetStateId(inputInst) == MATERIALIZED_ID) {
1447             Materialize(inputInst, ss);
1448         }
1449     }
1450 }
1451 
ProcessBlock(BasicBlock * block,size_t depth)1452 bool EscapeAnalysis::ProcessBlock(BasicBlock *block, size_t depth)
1453 {
1454     if (block->IsMarked(visited_)) {
1455         return true;
1456     }
1457     if (AllPredecessorsVisited(block)) {
1458         mergeProcessor_.MergeStates(block);
1459         VisitBlockInternal(block);
1460         block->SetMarker(visited_);
1461     } else if (!block->GetLoop()->IsRoot()) {
1462         ASSERT(block->GetLoop()->GetHeader() == block);
1463         if (!ProcessLoop(block, depth + 1)) {
1464             return false;
1465         }
1466     } else {
1467         return false;
1468     }
1469     return true;
1470 }
1471 
ProcessLoop(BasicBlock * header,size_t depth)1472 bool EscapeAnalysis::ProcessLoop(BasicBlock *header, size_t depth)
1473 {
1474     if (depth >= MAX_NESTNESS) {
1475         return false;
1476     }
1477     auto &rpo = GetGraph()->GetBlocksRPO();
1478     auto startIt = find(rpo.begin(), rpo.end(), header);
1479     // There should be only one visited predecessor.
1480     // Use its state as initial loop header's state (instead of executing MergeStates).
1481     auto headerPred = *std::find_if(header->GetPredsBlocks().begin(), header->GetPredsBlocks().end(),
1482                                     [marker = visited_](auto b) { return b->IsMarked(marker); });
1483     auto headerState = GetState(headerPred)->Copy(GetGraph()->GetLocalAllocator());
1484     // Set materialized states for all phis
1485     for (auto phi : header->PhiInsts()) {
1486         if (DataType::IsReference(phi->GetType())) {
1487             headerState->Materialize(phi);
1488         }
1489     }
1490     blockStates_[header->GetId()] = headerState;
1491     // Copy header's initial state to compare it after loop processing
1492     headerState = headerState->Copy(GetGraph()->GetLocalAllocator());
1493     VisitBlockInternal(header);
1494 
1495     bool stateChanged = true;
1496     while (stateChanged) {
1497         header->SetMarker(visited_);
1498         // Reset visited marker for the loop and all its nested loops
1499         for (auto it = std::next(startIt), end = rpo.end(); it != end; ++it) {
1500             if ((*it)->GetLoop() == header->GetLoop() || (*it)->GetLoop()->IsInside(header->GetLoop())) {
1501                 (*it)->ResetMarker(visited_);
1502             }
1503         }
1504 
1505         // Process loop and its nested loops only
1506         for (auto it = std::next(startIt), end = rpo.end(); it != end; ++it) {
1507             auto block = *it;
1508             if (block->GetLoop() != header->GetLoop() && !(*it)->GetLoop()->IsInside(header->GetLoop())) {
1509                 continue;
1510             }
1511             if (!ProcessBlock(block, depth)) {
1512                 return false;
1513             }
1514         }
1515         // Now all loop header's predecessors should be visited and we can merge its states
1516         ASSERT(AllPredecessorsVisited(header));
1517         mergeProcessor_.MergeStates(header);
1518         // Check if merged state differs from previous loop header's state
1519         stateChanged = !headerState->Equals(GetState(header));
1520         if (stateChanged) {
1521             headerState = GetState(header)->Copy(GetLocalAllocator());
1522         }
1523         VisitBlockInternal(header);
1524     }
1525     return true;
1526 }
1527 
CopySaveState(Inst * inst,VirtualState * except)1528 SaveStateInst *ScalarReplacement::CopySaveState(Inst *inst, VirtualState *except)
1529 {
1530     auto ss = inst->CastToSaveState();
1531     auto copy = static_cast<SaveStateInst *>(ss->Clone(graph_));
1532     auto &virtInsts = saveStateLiveness_.try_emplace(copy, saveStateLiveness_.at(inst)).first->second;
1533     const auto &aliases = except->GetAliases();
1534     for (size_t inputIdx = 0, copyIdx = 0; inputIdx < ss->GetInputsCount(); inputIdx++) {
1535         copy->AppendInput(inst->GetInput(inputIdx));
1536         copy->SetVirtualRegister(copyIdx++, ss->GetVirtualRegister(inputIdx));
1537 
1538         if (std::find(aliases.begin(), aliases.end(), inst->GetDataFlowInput(inputIdx)) != aliases.end()) {
1539             // Mark SaveState to fix later.
1540             virtInsts.SetBit(inputIdx);
1541         }
1542     }
1543 
1544     ss->GetBasicBlock()->InsertBefore(copy, ss);
1545     return copy;
1546 }
1547 
CreatePhis()1548 void ScalarReplacement::CreatePhis()
1549 {
1550     for (auto bb : graph_->GetVectorBlocks()) {
1551         if (bb == nullptr) {
1552             continue;
1553         }
1554         for (auto &t : phis_.at(bb->GetId())) {
1555             auto state = t.second;
1556             auto phiInst = graph_->CreateInstPhi(state->GetType(), bb->GetGuestPc());
1557             allocatedPhis_[state] = phiInst;
1558             bb->AppendPhi(phiInst);
1559         }
1560     }
1561 }
1562 
MaterializeObjects()1563 void ScalarReplacement::MaterializeObjects()
1564 {
1565     for (auto &[site, state] : materializationSites_) {
1566         if (std::holds_alternative<BasicBlock *>(site)) {
1567             MaterializeInEmptyBlock(std::get<BasicBlock *>(site), state);
1568             continue;
1569         }
1570         auto siteInst = std::get<Inst *>(site);
1571         if (siteInst->GetOpcode() == Opcode::SaveState) {
1572             MaterializeAtExistingSaveState(siteInst->CastToSaveState(), state);
1573         } else {
1574             MaterializeAtNewSaveState(siteInst, state);
1575         }
1576     }
1577 }
1578 
MaterializeAtExistingSaveState(SaveStateInst * saveState,ArenaMap<Inst *,VirtualState * > & state)1579 void ScalarReplacement::MaterializeAtExistingSaveState(SaveStateInst *saveState,
1580                                                        ArenaMap<Inst *, VirtualState *> &state)
1581 {
1582     auto previousSaveState = saveState;
1583     for (auto t : state) {
1584         if (t.second == nullptr || !EscapeAnalysis::IsAllocInst(t.first)) {
1585             continue;
1586         }
1587         auto origAlloc = t.first;
1588         ASSERT(origAlloc == t.second->GetInst());
1589         auto currSs = CopySaveState(previousSaveState, t.second);
1590         previousSaveState = currSs;
1591 
1592         Materialize(origAlloc, currSs, saveState, t.second);
1593     }
1594 }
1595 
MaterializeAtNewSaveState(Inst * site,ArenaMap<Inst *,VirtualState * > & state)1596 void ScalarReplacement::MaterializeAtNewSaveState(Inst *site, ArenaMap<Inst *, VirtualState *> &state)
1597 {
1598     auto block = site->GetBasicBlock();
1599     auto ssInsertionPoint = site;
1600     for (auto t : state) {
1601         if (t.second == nullptr || !EscapeAnalysis::IsAllocInst(t.first) || t.first == site) {
1602             continue;
1603         }
1604         auto origAlloc = t.first;
1605         auto currSs = graph_->CreateInstSaveState();
1606         if (auto callerInst = FindCallerInst(block, site)) {
1607             currSs->SetMethod(callerInst->GetCallMethod());
1608             currSs->SetCallerInst(callerInst);
1609             currSs->SetInliningDepth(callerInst->GetSaveState()->GetInliningDepth() + 1);
1610         } else {
1611             currSs->SetMethod(graph_->GetMethod());
1612         }
1613         block->InsertBefore(currSs, ssInsertionPoint);
1614         ssInsertionPoint = currSs;
1615 
1616         Materialize(origAlloc, currSs, site, t.second);
1617     }
1618 }
1619 
MaterializeInEmptyBlock(BasicBlock * block,ArenaMap<Inst *,VirtualState * > & state)1620 void ScalarReplacement::MaterializeInEmptyBlock(BasicBlock *block, ArenaMap<Inst *, VirtualState *> &state)
1621 {
1622     ASSERT(block->IsEmpty());
1623     for (auto t : state) {
1624         if (t.second == nullptr || !EscapeAnalysis::IsAllocInst(t.first)) {
1625             continue;
1626         }
1627         auto origAlloc = t.first;
1628         auto currSs = graph_->CreateInstSaveState();
1629         if (auto callerInst = FindCallerInst(block)) {
1630             currSs->SetMethod(callerInst->GetCallMethod());
1631             currSs->SetCallerInst(callerInst);
1632             currSs->SetInliningDepth(callerInst->GetSaveState()->GetInliningDepth() + 1);
1633         } else {
1634             currSs->SetMethod(graph_->GetMethod());
1635         }
1636         block->PrependInst(currSs);
1637 
1638         Materialize(origAlloc, currSs, nullptr, t.second);
1639     }
1640 }
1641 
Materialize(Inst * originalInst,Inst * ssAlloc,Inst * ssInit,VirtualState * state)1642 void ScalarReplacement::Materialize(Inst *originalInst, Inst *ssAlloc, Inst *ssInit, VirtualState *state)
1643 {
1644     Inst *newAlloc {nullptr};
1645     auto &allocs = materializedObjects_.try_emplace(originalInst, graph_->GetLocalAllocator()->Adapter()).first->second;
1646     if (originalInst->GetOpcode() == Opcode::NewObject) {
1647         newAlloc = CreateNewObject(originalInst, ssAlloc);
1648     } else {
1649         ASSERT(originalInst->GetOpcode() == Opcode::NewArray);
1650         newAlloc = CreateNewArray(originalInst, ssAlloc);
1651     }
1652     InitializeObject(newAlloc, ssInit, state);
1653     allocs.push_back(newAlloc);
1654 }
1655 
CreateNewObject(Inst * originalInst,Inst * saveState)1656 Inst *ScalarReplacement::CreateNewObject(Inst *originalInst, Inst *saveState)
1657 {
1658     ASSERT(originalInst->GetOpcode() == Opcode::NewObject);
1659     auto newAlloc = graph_->CreateInstNewObject(
1660         originalInst->GetType(), originalInst->GetPc(), originalInst->GetInput(0).GetInst(), saveState,
1661         TypeIdMixin {originalInst->CastToNewObject()->GetTypeId(), originalInst->CastToNewObject()->GetMethod()});
1662     saveState->GetBasicBlock()->InsertAfter(newAlloc, saveState);
1663     COMPILER_LOG(DEBUG, PEA) << "Materialized " << originalInst->GetId() << " at SavePoint " << saveState->GetId()
1664                              << " as " << *newAlloc;
1665     return newAlloc;
1666 }
1667 
CreateNewArray(Inst * originalInst,Inst * saveState)1668 Inst *ScalarReplacement::CreateNewArray(Inst *originalInst, Inst *saveState)
1669 {
1670     ASSERT(originalInst->GetOpcode() == Opcode::NewArray);
1671     auto newAlloc = graph_->CreateInstNewArray(
1672         originalInst->GetType(), originalInst->GetPc(), originalInst->GetInput(0).GetInst(),
1673         originalInst->GetInput(1).GetInst(), saveState,
1674         TypeIdMixin {originalInst->CastToNewArray()->GetTypeId(), originalInst->CastToNewArray()->GetMethod()});
1675     saveState->InsertAfter(newAlloc);
1676     COMPILER_LOG(DEBUG, PEA) << "Materialized " << originalInst->GetId() << " at SavePoint " << saveState->GetId()
1677                              << " as " << *newAlloc;
1678     return newAlloc;
1679 }
1680 
FindCallerInst(BasicBlock * target,Inst * start)1681 CallInst *ScalarReplacement::FindCallerInst(BasicBlock *target, Inst *start)
1682 {
1683     auto block = start == nullptr ? target->GetDominator() : target;
1684     size_t depth = 0;
1685     while (block != nullptr) {
1686         auto iter = InstBackwardIterator<IterationType::INST>(*block, start);
1687         for (auto inst : iter) {
1688             if (inst == start) {
1689                 continue;
1690             }
1691             if (inst->GetOpcode() == Opcode::ReturnInlined) {
1692                 depth++;
1693             }
1694             if (!inst->IsCall()) {
1695                 continue;
1696             }
1697             auto callInst = static_cast<CallInst *>(inst);
1698             auto isInlined = callInst->IsInlined();
1699             if (isInlined && depth == 0) {
1700                 return callInst;
1701             }
1702             if (isInlined) {
1703                 depth--;
1704             }
1705         }
1706         block = block->GetDominator();
1707         start = nullptr;
1708     }
1709     return nullptr;
1710 }
1711 
InitializeObject(Inst * alloc,Inst * instBefore,VirtualState * state)1712 void ScalarReplacement::InitializeObject(Inst *alloc, Inst *instBefore, VirtualState *state)
1713 {
1714     for (auto &[fieldVariant, fieldSource] : state->GetFields()) {
1715         Inst *fieldSourceInst {nullptr};
1716         if (std::holds_alternative<Inst *>(fieldSource)) {
1717             fieldSourceInst = std::get<Inst *>(fieldSource);
1718         } else if (std::holds_alternative<PhiState *>(fieldSource)) {
1719             auto phisState = std::get<PhiState *>(fieldSource);
1720             fieldSourceInst = allocatedPhis_[phisState];
1721             ASSERT(fieldSourceInst != nullptr);
1722         } else {
1723             // Field initialized with zero constant / nullptr, it's a default value,
1724             // so there is no need to insert explicit store instruction.
1725             continue;
1726         }
1727         Inst *store {nullptr};
1728         if (std::holds_alternative<FieldPtr>(fieldVariant)) {
1729             auto field = std::get<FieldPtr>(fieldVariant);
1730             auto fieldType = graph_->GetRuntime()->GetFieldType(field);
1731             store = graph_->CreateInstStoreObject(
1732                 fieldType, alloc->GetPc(), alloc, fieldSourceInst,
1733                 TypeIdMixin {graph_->GetRuntime()->GetFieldId(field), graph_->GetMethod()}, field,
1734                 graph_->GetRuntime()->IsFieldVolatile(field), DataType::IsReference(fieldType));
1735         } else {
1736             ASSERT(std::holds_alternative<Index>(fieldVariant));
1737             auto index = std::get<Index>(fieldVariant).index;
1738             auto type = state->GetArrayComponentType();
1739             store = graph_->CreateInstStoreArray(type, alloc->GetPc(), alloc, graph_->FindOrCreateConstant(index),
1740                                                  fieldSourceInst, DataType::IsReference(type));
1741         }
1742         if (instBefore != nullptr) {
1743             instBefore->GetBasicBlock()->InsertBefore(store, instBefore);
1744         } else {
1745             alloc->GetBasicBlock()->AppendInst(store);
1746         }
1747     }
1748 }
1749 
ResolveAlias(const StateOwner & alias,const Inst * inst)1750 Inst *ScalarReplacement::ResolveAlias(const StateOwner &alias, const Inst *inst)
1751 {
1752     if (std::holds_alternative<PhiState *>(alias)) {
1753         auto phiInst = allocatedPhis_[std::get<PhiState *>(alias)];
1754         ASSERT(phiInst != nullptr);
1755         return phiInst;
1756     }
1757     if (std::holds_alternative<Inst *>(alias)) {
1758         auto target = std::get<Inst *>(alias);
1759         // try to unwind aliasing chain
1760         auto it = aliases_.find(target);
1761         if (it == aliases_.end()) {
1762             return target;
1763         }
1764         if (std::holds_alternative<Inst *>(it->second) && std::get<Inst *>(it->second) == target) {
1765             return target;
1766         }
1767         return ResolveAlias(it->second, inst);
1768     }
1769     // It's neither PhiState, nor Inst, so it should be ZeroInst.
1770     if (DataType::IsReference(inst->GetType())) {
1771         return graph_->GetOrCreateNullPtr();
1772     }
1773     if (inst->GetType() == DataType::FLOAT32) {
1774         return graph_->FindOrCreateConstant(0.0F);
1775     }
1776     if (inst->GetType() == DataType::FLOAT64) {
1777         return graph_->FindOrCreateConstant(0.0);
1778     }
1779     return graph_->FindOrCreateConstant(0U);
1780 }
1781 
ReplaceAliases()1782 void ScalarReplacement::ReplaceAliases()
1783 {
1784     ArenaVector<Inst *> replaceInputs {graph_->GetLocalAllocator()->Adapter()};
1785     for (auto &[inst, alias] : aliases_) {
1786         Inst *replacement = ResolveAlias(alias, inst);
1787         if (replacement != inst && replacement != nullptr) {
1788             for (auto &user : inst->GetUsers()) {
1789                 replaceInputs.push_back(user.GetInst());
1790             }
1791         }
1792 
1793         bool replaced = false;
1794         if (replacement != nullptr && inst->GetOpcode() == Opcode::LoadObject &&
1795             DataType::NeedCastForTypes(graph_->GetArch(), replacement->GetType(), inst->GetType())) {
1796             // In case of loads/stores explicit casts could be eliminated before scalar replacement.
1797             // To use correct values after load's replacement with a value stored into a field we
1798             // need to insert some casts back.
1799             replaced = true;
1800             for (auto user : replaceInputs) {
1801                 auto cast = graph_->CreateInstCast(inst->GetType(), inst->GetPc(), replacement, replacement->GetType());
1802                 inst->InsertBefore(cast);
1803                 replacement = cast;
1804                 user->ReplaceInput(inst, replacement);
1805             }
1806         } else if (replacement != nullptr && replacement->IsClassInst()) {
1807             auto classImm = graph_->CreateInstLoadImmediate(DataType::REFERENCE, replacement->GetPc(),
1808                                                             static_cast<ClassInst *>(replacement)->GetClass());
1809             replacement->InsertAfter(classImm);
1810             replacement = classImm;
1811         }
1812 
1813         if (replacement != nullptr) {
1814             COMPILER_LOG(DEBUG, PEA) << "Replacing " << *inst << " with " << *replacement;
1815         }
1816         if (!replaced) {
1817             for (auto user : replaceInputs) {
1818                 user->ReplaceInput(inst, replacement);
1819             }
1820         }
1821         replaceInputs.clear();
1822         EnqueueForRemoval(inst);
1823     }
1824 }
1825 
ResolvePhiInputs()1826 void ScalarReplacement::ResolvePhiInputs()
1827 {
1828     for (auto [state, inst] : allocatedPhis_) {
1829         auto preds = inst->GetBasicBlock()->GetPredsBlocks();
1830         auto inputs = state->GetInputs();
1831         ASSERT(preds.size() == inputs.size());
1832         for (size_t idx = 0; idx < inputs.size(); idx++) {
1833             auto inputInst = ResolveAlias(inputs[idx], inst);
1834             ASSERT(inputInst != nullptr);
1835             if (EscapeAnalysis::IsAllocInst(inputInst)) {
1836                 inputInst = ResolveAllocation(inputInst, preds[idx]);
1837             }
1838             inst->AppendInput(inputInst);
1839         }
1840     }
1841 }
1842 
ResolveAllocation(Inst * inst,BasicBlock * block)1843 Inst *ScalarReplacement::ResolveAllocation(Inst *inst, BasicBlock *block)
1844 {
1845     ASSERT(inst != nullptr);
1846     auto it = materializedObjects_.find(inst);
1847     if (it == materializedObjects_.end()) {
1848         return inst;
1849     }
1850     auto &allocs = it->second;
1851     for (auto alloc : allocs) {
1852         if (alloc->GetBasicBlock()->IsDominate(block)) {
1853             return alloc;
1854         }
1855     }
1856     UNREACHABLE();
1857     return inst;
1858 }
1859 
UpdateSaveStates()1860 void ScalarReplacement::UpdateSaveStates()
1861 {
1862     ArenaVector<Inst *> queue {graph_->GetLocalAllocator()->Adapter()};
1863     for (auto &[site, virtualObjects] : saveStateLiveness_) {
1864         bool isCall = site->IsCall();
1865         ASSERT(!isCall || static_cast<CallInst *>(site)->IsInlined());
1866         // Remove virtual inputs (i.e. objects that are not alive)
1867         for (ssize_t inputIdx = static_cast<ssize_t>(site->GetInputsCount()) - 1; inputIdx >= 0; --inputIdx) {
1868             if (!virtualObjects.GetBit(inputIdx)) {
1869                 continue;
1870             }
1871             if (isCall) {
1872                 site->SetInput(inputIdx, graph_->GetOrCreateNullPtr());
1873                 continue;
1874             }
1875             site->RemoveInput(inputIdx);
1876         }
1877     }
1878 }
1879 
UpdateAllocationUsers()1880 void ScalarReplacement::UpdateAllocationUsers()
1881 {
1882     ArenaVector<std::tuple<Inst *, size_t, Inst *>> queue {graph_->GetLocalAllocator()->Adapter()};
1883     for (auto &[oldAlloc, newAllocs] : materializedObjects_) {
1884         // At these point:
1885         // - all aliases should be already processed and corresponding users will be enqueued for removal
1886         // - all save states and inlined calls will be already processed
1887         // - inputs for newly allocated phis will be processed as well
1888         for (auto &user : oldAlloc->GetUsers()) {
1889             auto userInst = user.GetInst();
1890             if (IsEnqueuedForRemoval(userInst)) {
1891                 continue;
1892             }
1893             // There might be multiple materializations of the same object so we need to find
1894             // the one dominating current user.
1895             auto userBlock =
1896                 userInst->IsPhi() ? userInst->CastToPhi()->GetPhiInputBb(user.GetIndex()) : userInst->GetBasicBlock();
1897             COMPILER_LOG(DEBUG, PEA) << "User block = " << userBlock->GetId();
1898             auto replacementIt = std::find_if(newAllocs.begin(), newAllocs.end(), [userBlock](auto newAlloc) {
1899                 return newAlloc->GetBasicBlock()->IsDominate(userBlock);
1900             });
1901             ASSERT(replacementIt != newAllocs.end());
1902             queue.emplace_back(userInst, user.GetIndex(), *replacementIt);
1903         }
1904         for (auto &[user, idx, replacement] : queue) {
1905             user->SetInput(idx, replacement);
1906         }
1907         queue.clear();
1908 
1909         EnqueueForRemoval(oldAlloc);
1910     }
1911 }
1912 
EnqueueForRemoval(Inst * inst)1913 void ScalarReplacement::EnqueueForRemoval(Inst *inst)
1914 {
1915     if (IsEnqueuedForRemoval(inst)) {
1916         return;
1917     }
1918     inst->SetMarker(removeInstMarker_);
1919     removalQueue_.push_back(inst);
1920 }
1921 
IsEnqueuedForRemoval(Inst * inst) const1922 bool ScalarReplacement::IsEnqueuedForRemoval(Inst *inst) const
1923 {
1924     return inst->IsMarked(removeInstMarker_);
1925 }
1926 
InputSizeGtSize(Inst * inst)1927 static bool InputSizeGtSize(Inst *inst)
1928 {
1929     uint32_t size = 0;
1930     auto arch = inst->GetBasicBlock()->GetGraph()->GetArch();
1931 
1932     for (auto input : inst->GetInputs()) {
1933         auto inputSize = DataType::GetTypeSize(input.GetInst()->GetType(), arch);
1934         size = size < inputSize ? inputSize : size;
1935     }
1936     return size > DataType::GetTypeSize(inst->GetType(), arch);
1937 }
1938 
1939 /* Phi instruction can have inputs that are wider than
1940  * the phi type, we have to insert casts, so that, the
1941  * phi takes the types matching it's own type */
FixPhiInputTypes()1942 void ScalarReplacement::FixPhiInputTypes()
1943 {
1944     auto arch = graph_->GetArch();
1945     for (auto alphi : allocatedPhis_) {
1946         auto phi = alphi.second;
1947         if (LIKELY(!InputSizeGtSize(phi) || !phi->HasUsers())) {
1948             continue;
1949         }
1950         auto phiSize = DataType::GetTypeSize(phi->GetType(), arch);
1951         for (auto &i : phi->GetInputs()) {
1952             auto input = i.GetInst();
1953             /* constants are taken care of by constprop and existing
1954              * phis have to be properly casted already */
1955             if (LIKELY(phiSize == DataType::GetTypeSize(input->GetType(), arch) ||
1956                        input->GetOpcode() == Opcode::Constant || input->GetOpcode() == Opcode::Phi)) {
1957                 continue;
1958             }
1959             /* replace the wider-than-phi-type input with the cast */
1960             auto cast = graph_->CreateInstCast(phi->GetType(), input->GetPc(), input, input->GetType());
1961             phi->ReplaceInput(input, cast);
1962             input->InsertAfter(cast);
1963         }
1964     }
1965 }
1966 
ProcessRemovalQueue()1967 void ScalarReplacement::ProcessRemovalQueue()
1968 {
1969     for (auto inst : removalQueue_) {
1970         COMPILER_LOG(DEBUG, PEA) << "Removing inst " << inst->GetId();
1971         inst->GetBasicBlock()->RemoveInst(inst);
1972     }
1973     FixPhiInputTypes();
1974 }
1975 
Apply(ArenaSet<Inst * > & candidates)1976 void ScalarReplacement::Apply(ArenaSet<Inst *> &candidates)
1977 {
1978     removeInstMarker_ = graph_->NewMarker();
1979     CreatePhis();
1980     MaterializeObjects();
1981     ReplaceAliases();
1982     ResolvePhiInputs();
1983     UpdateSaveStates();
1984     UpdateAllocationUsers();
1985     PatchSaveStates();
1986     for (auto candidate : candidates) {
1987         EnqueueForRemoval(candidate);
1988     }
1989     ProcessRemovalQueue();
1990     graph_->EraseMarker(removeInstMarker_);
1991 }
1992 
PatchSaveStates()1993 void ScalarReplacement::PatchSaveStates()
1994 {
1995     ArenaVector<ArenaSet<Inst *>> liveness {graph_->GetLocalAllocator()->Adapter()};
1996     for (size_t idx = 0; idx < graph_->GetVectorBlocks().size(); ++idx) {
1997         liveness.emplace_back(graph_->GetLocalAllocator()->Adapter());
1998     }
1999 
2000     auto &rpo = graph_->GetBlocksRPO();
2001     for (auto it = rpo.rbegin(), end = rpo.rend(); it != end; ++it) {
2002         auto block = *it;
2003         PatchSaveStatesInBlock(block, liveness);
2004     }
2005 }
2006 
FillLiveInsts(BasicBlock * block,ArenaSet<Inst * > & liveIns,ArenaVector<ArenaSet<Inst * >> & liveness)2007 void ScalarReplacement::FillLiveInsts(BasicBlock *block, ArenaSet<Inst *> &liveIns,
2008                                       ArenaVector<ArenaSet<Inst *>> &liveness)
2009 {
2010     for (auto succ : block->GetSuccsBlocks()) {
2011         liveIns.insert(liveness[succ->GetId()].begin(), liveness[succ->GetId()].end());
2012         for (auto phiInst : succ->PhiInsts()) {
2013             auto phiInput = phiInst->CastToPhi()->GetPhiInput(block);
2014             if (DataType::IsReference(phiInput->GetType())) {
2015                 liveIns.insert(phiInput);
2016             }
2017         }
2018     }
2019 }
2020 
HasUsageOutsideBlock(Inst * inst,BasicBlock * initialBlock)2021 bool ScalarReplacement::HasUsageOutsideBlock(Inst *inst, BasicBlock *initialBlock)
2022 {
2023     for (auto &user : inst->GetUsers()) {
2024         if (user.GetInst()->GetBasicBlock() != initialBlock) {
2025             return true;
2026         }
2027     }
2028     return false;
2029 }
2030 
2031 // Compute live ref-valued insturctions at each save state and insert any missing live instruction into a save state.
PatchSaveStatesInBlock(BasicBlock * block,ArenaVector<ArenaSet<Inst * >> & liveness)2032 void ScalarReplacement::PatchSaveStatesInBlock(BasicBlock *block, ArenaVector<ArenaSet<Inst *>> &liveness)
2033 {
2034     auto &liveIns = liveness[block->GetId()];
2035     FillLiveInsts(block, liveIns, liveness);
2036 
2037     auto loop = block->GetLoop();
2038     bool loopIsHeader = !loop->IsRoot() && loop->GetHeader() == block;
2039     if (loopIsHeader) {
2040         for (auto inst : block->InstsReverse()) {
2041             if (IsEnqueuedForRemoval(inst) || !HasUsageOutsideBlock(inst, block)) {
2042                 continue;
2043             }
2044             // That part is neccessary only when some instructions is used outside block with definition ref
2045             AddLiveInputs(inst, liveIns);
2046         }
2047     }
2048     for (auto inst : block->InstsReverse()) {
2049         if (inst->IsSaveState()) {
2050             PatchSaveState(static_cast<SaveStateInst *>(inst), liveIns);
2051         }
2052         if (DataType::IsReference(inst->GetType())) {
2053             liveIns.erase(inst);
2054         }
2055         if (IsEnqueuedForRemoval(inst)) {
2056             continue;
2057         }
2058         AddLiveInputs(inst, liveIns);
2059     }
2060 
2061     if (!loop->IsRoot() && loop->GetHeader() == block) {
2062         for (auto phiInst : block->PhiInsts()) {
2063             bool propagateThroughLoop = false;
2064             for (auto &user : phiInst->GetUsers()) {
2065                 propagateThroughLoop =
2066                     propagateThroughLoop ||
2067                     (user.GetInst()->IsPhi() && user.GetInst()->GetBasicBlock() == phiInst->GetBasicBlock());
2068             }
2069             if (!propagateThroughLoop) {
2070                 liveIns.erase(phiInst);
2071             }
2072         }
2073         PatchSaveStatesInLoop(loop, liveIns, liveness);
2074     }
2075     for (auto phiInst : block->PhiInsts()) {
2076         liveIns.erase(phiInst);
2077     }
2078 }
2079 
AddLiveInputs(Inst * inst,ArenaSet<Inst * > & liveIns)2080 void ScalarReplacement::AddLiveInputs(Inst *inst, ArenaSet<Inst *> &liveIns)
2081 {
2082     for (auto &input : inst->GetInputs()) {
2083         auto inputInst = inst->GetDataFlowInput(input.GetInst());
2084         if (!DataType::IsReference(inputInst->GetType()) || inputInst->IsStore() || !inputInst->IsMovableObject()) {
2085             continue;
2086         }
2087         liveIns.insert(inputInst);
2088     }
2089 }
2090 
PatchSaveStatesInLoop(Loop * loop,ArenaSet<Inst * > & loopLiveIns,ArenaVector<ArenaSet<Inst * >> & liveness)2091 void ScalarReplacement::PatchSaveStatesInLoop(Loop *loop, ArenaSet<Inst *> &loopLiveIns,
2092                                               ArenaVector<ArenaSet<Inst *>> &liveness)
2093 {
2094     for (auto loopBlock : graph_->GetVectorBlocks()) {
2095         if (loopBlock == nullptr) {
2096             continue;
2097         }
2098         if (loopBlock->GetLoop() != loop && !loopBlock->GetLoop()->IsInside(loop)) {
2099             continue;
2100         }
2101         if (loopBlock != loop->GetHeader()) {
2102             auto &loopBlockLiveIns = liveness[loopBlock->GetId()];
2103             loopBlockLiveIns.insert(loopLiveIns.begin(), loopLiveIns.end());
2104         }
2105         for (auto inst : loopBlock->Insts()) {
2106             if (inst->IsSaveState()) {
2107                 PatchSaveState(static_cast<SaveStateInst *>(inst), loopLiveIns);
2108             }
2109         }
2110     }
2111 }
2112 
PatchSaveState(SaveStateInst * saveState,ArenaSet<Inst * > & liveInstructions)2113 void ScalarReplacement::PatchSaveState(SaveStateInst *saveState, ArenaSet<Inst *> &liveInstructions)
2114 {
2115     for (auto inst : liveInstructions) {
2116         inst = Inst::GetDataFlowInput(inst);
2117         if (!inst->IsMovableObject()) {
2118             continue;
2119         }
2120         auto inputs = saveState->GetInputs();
2121         auto it = std::find_if(inputs.begin(), inputs.end(), [inst](auto &in) { return in.GetInst() == inst; });
2122         if (it != inputs.end()) {
2123             continue;
2124         }
2125         saveState->AppendBridge(inst);
2126     }
2127 }
2128 }  // namespace ark::compiler
2129