• 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         return;
1353     }
1354     if (vstate == nullptr) {
1355         GetState(inst->GetBasicBlock())->SetStateId(inst, MATERIALIZED_ID);
1356         return;
1357     }
1358     auto fieldInstId = vstate->GetFieldOrDefault(field, ZERO_INST);
1359     GetState(inst->GetBasicBlock())->SetStateId(inst, GetState(inst->GetBasicBlock())->GetStateId(fieldInstId));
1360 }
1361 
VisitLoadArray(Inst * inst)1362 void EscapeAnalysis::VisitLoadArray(Inst *inst)
1363 {
1364     auto array = inst->GetDataFlowInput(0);
1365     auto vstate = GetState(inst->GetBasicBlock())->GetState(array);
1366     auto index = inst->GetDataFlowInput(1U);
1367     if (!index->IsConst()) {
1368         Materialize(array, inst);
1369         return;
1370     }
1371 
1372     if (vstate != nullptr) {
1373         aliases_[inst] = vstate->GetFieldOrDefault(
1374             Index {vstate->GetArrayComponentClass(), index->CastToConstant()->GetInt64Value()}, ZERO_INST);
1375     } else {
1376         aliases_.erase(inst);
1377     }
1378 
1379     if (!DataType::IsReference(inst->GetType())) {
1380         return;
1381     }
1382     if (vstate == nullptr) {
1383         GetState(inst->GetBasicBlock())->SetStateId(inst, MATERIALIZED_ID);
1384         return;
1385     }
1386     auto fieldInstId = vstate->GetFieldOrDefault(
1387         Index {vstate->GetArrayComponentClass(), index->CastToConstant()->GetInt64Value()}, ZERO_INST);
1388     GetState(inst->GetBasicBlock())->SetStateId(inst, GetState(inst->GetBasicBlock())->GetStateId(fieldInstId));
1389 }
1390 
VisitStoreObject(Inst * inst)1391 void EscapeAnalysis::VisitStoreObject(Inst *inst)
1392 {
1393     auto vstate = GetState(inst->GetBasicBlock())->GetState(inst->GetDataFlowInput(0));
1394     auto field = inst->CastToStoreObject()->GetObjField();
1395 
1396     if (vstate != nullptr) {
1397         vstate->SetField(field, inst->GetDataFlowInput(1U));
1398         // mark inst for removal
1399         aliases_[inst] = inst;
1400     } else {
1401         if (DataType::IsReference(inst->GetType())) {
1402             Materialize(inst->GetDataFlowInput(1U), inst);
1403         }
1404         aliases_.erase(inst);
1405     }
1406 }
1407 
VisitStoreArray(Inst * inst)1408 void EscapeAnalysis::VisitStoreArray(Inst *inst)
1409 {
1410     auto array = inst->GetDataFlowInput(0);
1411     auto vstate = GetState(inst->GetBasicBlock())->GetState(array);
1412     auto index = inst->GetDataFlowInput(1U);
1413     if (!index->IsConst()) {
1414         Materialize(array, inst);
1415         if (DataType::IsReference(inst->GetType())) {
1416             Materialize(inst->GetDataFlowInput(2U), inst);
1417         }
1418         return;
1419     }
1420     if (vstate != nullptr) {
1421         auto indexCnst = index->CastToConstant()->GetInt64Value();
1422         vstate->SetField(Index {vstate->GetArrayComponentClass(), indexCnst}, inst->GetDataFlowInput(2U));
1423         // mark inst for removal
1424         aliases_[inst] = inst;
1425     } else {
1426         if (DataType::IsReference(inst->GetType())) {
1427             Materialize(inst->GetDataFlowInput(2U), inst);
1428         }
1429         aliases_.erase(inst);
1430     }
1431 }
1432 
VisitSaveStateUser(Inst * inst)1433 void EscapeAnalysis::VisitSaveStateUser(Inst *inst)
1434 {
1435     auto ss = inst->GetSaveState();
1436     if (ss == nullptr || ss->GetOpcode() != Opcode::SaveState) {
1437         return;
1438     }
1439 
1440     auto blockState = GetState(inst->GetBasicBlock());
1441 
1442     // If an instruction is materialized at save state user
1443     // then it should be materialized before the save state too.
1444     auto virtualInputs = saveStateInfo_.at(ss);
1445     for (auto inputIdx : virtualInputs.GetSetBitsIndices()) {
1446         auto inputInst = ss->GetInput(inputIdx).GetInst();
1447         if (blockState->GetStateId(inputInst) == MATERIALIZED_ID) {
1448             Materialize(inputInst, ss);
1449         }
1450     }
1451 }
1452 
ProcessBlock(BasicBlock * block,size_t depth)1453 bool EscapeAnalysis::ProcessBlock(BasicBlock *block, size_t depth)
1454 {
1455     if (block->IsMarked(visited_)) {
1456         return true;
1457     }
1458     if (AllPredecessorsVisited(block)) {
1459         mergeProcessor_.MergeStates(block);
1460         VisitBlockInternal(block);
1461         block->SetMarker(visited_);
1462     } else if (!block->GetLoop()->IsRoot()) {
1463         ASSERT(block->GetLoop()->GetHeader() == block);
1464         if (!ProcessLoop(block, depth + 1)) {
1465             return false;
1466         }
1467     } else {
1468         return false;
1469     }
1470     return true;
1471 }
1472 
ProcessLoop(BasicBlock * header,size_t depth)1473 bool EscapeAnalysis::ProcessLoop(BasicBlock *header, size_t depth)
1474 {
1475     if (depth >= MAX_NESTNESS) {
1476         return false;
1477     }
1478     auto &rpo = GetGraph()->GetBlocksRPO();
1479     auto startIt = find(rpo.begin(), rpo.end(), header);
1480     // There should be only one visited predecessor.
1481     // Use its state as initial loop header's state (instead of executing MergeStates).
1482     auto headerPred = *std::find_if(header->GetPredsBlocks().begin(), header->GetPredsBlocks().end(),
1483                                     [marker = visited_](auto b) { return b->IsMarked(marker); });
1484     auto headerState = GetState(headerPred)->Copy(GetGraph()->GetLocalAllocator());
1485     // Set materialized states for all phis
1486     for (auto phi : header->PhiInsts()) {
1487         if (DataType::IsReference(phi->GetType())) {
1488             headerState->Materialize(phi);
1489         }
1490     }
1491     blockStates_[header->GetId()] = headerState;
1492     // Copy header's initial state to compare it after loop processing
1493     headerState = headerState->Copy(GetGraph()->GetLocalAllocator());
1494     VisitBlockInternal(header);
1495 
1496     bool stateChanged = true;
1497     while (stateChanged) {
1498         header->SetMarker(visited_);
1499         // Reset visited marker for the loop and all its nested loops
1500         for (auto it = std::next(startIt), end = rpo.end(); it != end; ++it) {
1501             if ((*it)->GetLoop() == header->GetLoop() || (*it)->GetLoop()->IsInside(header->GetLoop())) {
1502                 (*it)->ResetMarker(visited_);
1503             }
1504         }
1505 
1506         // Process loop and its nested loops only
1507         for (auto it = std::next(startIt), end = rpo.end(); it != end; ++it) {
1508             auto block = *it;
1509             if (block->GetLoop() != header->GetLoop() && !(*it)->GetLoop()->IsInside(header->GetLoop())) {
1510                 continue;
1511             }
1512             if (!ProcessBlock(block, depth)) {
1513                 return false;
1514             }
1515         }
1516         // Now all loop header's predecessors should be visited and we can merge its states
1517         ASSERT(AllPredecessorsVisited(header));
1518         mergeProcessor_.MergeStates(header);
1519         // Check if merged state differs from previous loop header's state
1520         stateChanged = !headerState->Equals(GetState(header));
1521         if (stateChanged) {
1522             headerState = GetState(header)->Copy(GetLocalAllocator());
1523         }
1524         VisitBlockInternal(header);
1525     }
1526     return true;
1527 }
1528 
CopySaveState(Inst * inst,VirtualState * except)1529 SaveStateInst *ScalarReplacement::CopySaveState(Inst *inst, VirtualState *except)
1530 {
1531     auto ss = inst->CastToSaveState();
1532     auto copy = static_cast<SaveStateInst *>(ss->Clone(graph_));
1533     auto &virtInsts = saveStateLiveness_.try_emplace(copy, saveStateLiveness_.at(inst)).first->second;
1534     const auto &aliases = except->GetAliases();
1535     for (size_t inputIdx = 0, copyIdx = 0; inputIdx < ss->GetInputsCount(); inputIdx++) {
1536         copy->AppendInput(inst->GetInput(inputIdx));
1537         copy->SetVirtualRegister(copyIdx++, ss->GetVirtualRegister(inputIdx));
1538 
1539         if (std::find(aliases.begin(), aliases.end(), inst->GetDataFlowInput(inputIdx)) != aliases.end()) {
1540             // Mark SaveState to fix later.
1541             virtInsts.SetBit(inputIdx);
1542         }
1543     }
1544 
1545     ss->GetBasicBlock()->InsertBefore(copy, ss);
1546     return copy;
1547 }
1548 
CreatePhis()1549 void ScalarReplacement::CreatePhis()
1550 {
1551     for (auto bb : graph_->GetVectorBlocks()) {
1552         if (bb == nullptr) {
1553             continue;
1554         }
1555         for (auto &t : phis_.at(bb->GetId())) {
1556             auto state = t.second;
1557             auto phiInst = graph_->CreateInstPhi(state->GetType(), bb->GetGuestPc());
1558             allocatedPhis_[state] = phiInst;
1559             bb->AppendPhi(phiInst);
1560         }
1561     }
1562 }
1563 
MaterializeObjects()1564 void ScalarReplacement::MaterializeObjects()
1565 {
1566     for (auto &[site, state] : materializationSites_) {
1567         if (std::holds_alternative<BasicBlock *>(site)) {
1568             MaterializeInEmptyBlock(std::get<BasicBlock *>(site), state);
1569             continue;
1570         }
1571         auto siteInst = std::get<Inst *>(site);
1572         if (siteInst->GetOpcode() == Opcode::SaveState) {
1573             MaterializeAtExistingSaveState(siteInst->CastToSaveState(), state);
1574         } else {
1575             MaterializeAtNewSaveState(siteInst, state);
1576         }
1577     }
1578 }
1579 
MaterializeAtExistingSaveState(SaveStateInst * saveState,ArenaMap<Inst *,VirtualState * > & state)1580 void ScalarReplacement::MaterializeAtExistingSaveState(SaveStateInst *saveState,
1581                                                        ArenaMap<Inst *, VirtualState *> &state)
1582 {
1583     auto previousSaveState = saveState;
1584     for (auto t : state) {
1585         if (t.second == nullptr || !EscapeAnalysis::IsAllocInst(t.first)) {
1586             continue;
1587         }
1588         auto origAlloc = t.first;
1589         ASSERT(origAlloc == t.second->GetInst());
1590         auto currSs = CopySaveState(previousSaveState, t.second);
1591         previousSaveState = currSs;
1592 
1593         Materialize(origAlloc, currSs, saveState, t.second);
1594     }
1595 }
1596 
MaterializeAtNewSaveState(Inst * site,ArenaMap<Inst *,VirtualState * > & state)1597 void ScalarReplacement::MaterializeAtNewSaveState(Inst *site, ArenaMap<Inst *, VirtualState *> &state)
1598 {
1599     auto block = site->GetBasicBlock();
1600     auto ssInsertionPoint = site;
1601     for (auto t : state) {
1602         if (t.second == nullptr || !EscapeAnalysis::IsAllocInst(t.first) || t.first == site) {
1603             continue;
1604         }
1605         auto origAlloc = t.first;
1606         auto currSs = graph_->CreateInstSaveState();
1607         if (auto callerInst = FindCallerInst(block, site)) {
1608             currSs->SetMethod(callerInst->GetCallMethod());
1609             currSs->SetCallerInst(callerInst);
1610             currSs->SetInliningDepth(callerInst->GetSaveState()->GetInliningDepth() + 1);
1611         } else {
1612             currSs->SetMethod(graph_->GetMethod());
1613         }
1614         block->InsertBefore(currSs, ssInsertionPoint);
1615         ssInsertionPoint = currSs;
1616 
1617         Materialize(origAlloc, currSs, site, t.second);
1618     }
1619 }
1620 
MaterializeInEmptyBlock(BasicBlock * block,ArenaMap<Inst *,VirtualState * > & state)1621 void ScalarReplacement::MaterializeInEmptyBlock(BasicBlock *block, ArenaMap<Inst *, VirtualState *> &state)
1622 {
1623     ASSERT(block->IsEmpty());
1624     for (auto t : state) {
1625         if (t.second == nullptr || !EscapeAnalysis::IsAllocInst(t.first)) {
1626             continue;
1627         }
1628         auto origAlloc = t.first;
1629         auto currSs = graph_->CreateInstSaveState();
1630         if (auto callerInst = FindCallerInst(block)) {
1631             currSs->SetMethod(callerInst->GetCallMethod());
1632             currSs->SetCallerInst(callerInst);
1633             currSs->SetInliningDepth(callerInst->GetSaveState()->GetInliningDepth() + 1);
1634         } else {
1635             currSs->SetMethod(graph_->GetMethod());
1636         }
1637         block->PrependInst(currSs);
1638 
1639         Materialize(origAlloc, currSs, nullptr, t.second);
1640     }
1641 }
1642 
Materialize(Inst * originalInst,Inst * ssAlloc,Inst * ssInit,VirtualState * state)1643 void ScalarReplacement::Materialize(Inst *originalInst, Inst *ssAlloc, Inst *ssInit, VirtualState *state)
1644 {
1645     Inst *newAlloc {nullptr};
1646     auto &allocs = materializedObjects_.try_emplace(originalInst, graph_->GetLocalAllocator()->Adapter()).first->second;
1647     if (originalInst->GetOpcode() == Opcode::NewObject) {
1648         newAlloc = CreateNewObject(originalInst, ssAlloc);
1649     } else {
1650         ASSERT(originalInst->GetOpcode() == Opcode::NewArray);
1651         newAlloc = CreateNewArray(originalInst, ssAlloc);
1652     }
1653     InitializeObject(newAlloc, ssInit, state);
1654     allocs.push_back(newAlloc);
1655 }
1656 
CreateNewObject(Inst * originalInst,Inst * saveState)1657 Inst *ScalarReplacement::CreateNewObject(Inst *originalInst, Inst *saveState)
1658 {
1659     ASSERT(originalInst->GetOpcode() == Opcode::NewObject);
1660     auto newAlloc = graph_->CreateInstNewObject(
1661         originalInst->GetType(), originalInst->GetPc(), originalInst->GetInput(0).GetInst(), saveState,
1662         TypeIdMixin {originalInst->CastToNewObject()->GetTypeId(), originalInst->CastToNewObject()->GetMethod()});
1663     saveState->GetBasicBlock()->InsertAfter(newAlloc, saveState);
1664     COMPILER_LOG(DEBUG, PEA) << "Materialized " << originalInst->GetId() << " at SavePoint " << saveState->GetId()
1665                              << " as " << *newAlloc;
1666     return newAlloc;
1667 }
1668 
CreateNewArray(Inst * originalInst,Inst * saveState)1669 Inst *ScalarReplacement::CreateNewArray(Inst *originalInst, Inst *saveState)
1670 {
1671     ASSERT(originalInst->GetOpcode() == Opcode::NewArray);
1672     auto newAlloc = graph_->CreateInstNewArray(
1673         originalInst->GetType(), originalInst->GetPc(), originalInst->GetInput(0).GetInst(),
1674         originalInst->GetInput(1).GetInst(), saveState,
1675         TypeIdMixin {originalInst->CastToNewArray()->GetTypeId(), originalInst->CastToNewArray()->GetMethod()});
1676     saveState->InsertAfter(newAlloc);
1677     COMPILER_LOG(DEBUG, PEA) << "Materialized " << originalInst->GetId() << " at SavePoint " << saveState->GetId()
1678                              << " as " << *newAlloc;
1679     return newAlloc;
1680 }
1681 
FindCallerInst(BasicBlock * target,Inst * start)1682 CallInst *ScalarReplacement::FindCallerInst(BasicBlock *target, Inst *start)
1683 {
1684     auto block = start == nullptr ? target->GetDominator() : target;
1685     size_t depth = 0;
1686     while (block != nullptr) {
1687         auto iter = InstBackwardIterator<IterationType::INST>(*block, start);
1688         for (auto inst : iter) {
1689             if (inst == start) {
1690                 continue;
1691             }
1692             if (inst->GetOpcode() == Opcode::ReturnInlined) {
1693                 depth++;
1694             }
1695             if (!inst->IsCall()) {
1696                 continue;
1697             }
1698             auto callInst = static_cast<CallInst *>(inst);
1699             auto isInlined = callInst->IsInlined();
1700             if (isInlined && depth == 0) {
1701                 return callInst;
1702             }
1703             if (isInlined) {
1704                 depth--;
1705             }
1706         }
1707         block = block->GetDominator();
1708         start = nullptr;
1709     }
1710     return nullptr;
1711 }
1712 
InitializeObject(Inst * alloc,Inst * instBefore,VirtualState * state)1713 void ScalarReplacement::InitializeObject(Inst *alloc, Inst *instBefore, VirtualState *state)
1714 {
1715     for (auto &[fieldVariant, fieldSource] : state->GetFields()) {
1716         Inst *fieldSourceInst {nullptr};
1717         if (std::holds_alternative<Inst *>(fieldSource)) {
1718             fieldSourceInst = std::get<Inst *>(fieldSource);
1719         } else if (std::holds_alternative<PhiState *>(fieldSource)) {
1720             auto phisState = std::get<PhiState *>(fieldSource);
1721             fieldSourceInst = allocatedPhis_[phisState];
1722             ASSERT(fieldSourceInst != nullptr);
1723         } else {
1724             // Field initialized with zero constant / nullptr, it's a default value,
1725             // so there is no need to insert explicit store instruction.
1726             continue;
1727         }
1728         Inst *store {nullptr};
1729         if (std::holds_alternative<FieldPtr>(fieldVariant)) {
1730             auto field = std::get<FieldPtr>(fieldVariant);
1731             auto fieldType = graph_->GetRuntime()->GetFieldType(field);
1732             store = graph_->CreateInstStoreObject(
1733                 fieldType, alloc->GetPc(), alloc, fieldSourceInst,
1734                 TypeIdMixin {graph_->GetRuntime()->GetFieldId(field), graph_->GetMethod()}, field,
1735                 graph_->GetRuntime()->IsFieldVolatile(field), DataType::IsReference(fieldType));
1736         } else {
1737             ASSERT(std::holds_alternative<Index>(fieldVariant));
1738             auto index = std::get<Index>(fieldVariant).index;
1739             auto type = state->GetArrayComponentType();
1740             store = graph_->CreateInstStoreArray(type, alloc->GetPc(), alloc, graph_->FindOrCreateConstant(index),
1741                                                  fieldSourceInst, DataType::IsReference(type));
1742         }
1743         if (instBefore != nullptr) {
1744             instBefore->GetBasicBlock()->InsertBefore(store, instBefore);
1745         } else {
1746             alloc->GetBasicBlock()->AppendInst(store);
1747         }
1748     }
1749 }
1750 
ResolveAlias(const StateOwner & alias,const Inst * inst)1751 Inst *ScalarReplacement::ResolveAlias(const StateOwner &alias, const Inst *inst)
1752 {
1753     if (std::holds_alternative<PhiState *>(alias)) {
1754         auto phiInst = allocatedPhis_[std::get<PhiState *>(alias)];
1755         ASSERT(phiInst != nullptr);
1756         return phiInst;
1757     }
1758     if (std::holds_alternative<Inst *>(alias)) {
1759         auto target = std::get<Inst *>(alias);
1760         // try to unwind aliasing chain
1761         auto it = aliases_.find(target);
1762         if (it == aliases_.end()) {
1763             return target;
1764         }
1765         if (std::holds_alternative<Inst *>(it->second) && std::get<Inst *>(it->second) == target) {
1766             return target;
1767         }
1768         return ResolveAlias(it->second, inst);
1769     }
1770     // It's neither PhiState, nor Inst, so it should be ZeroInst.
1771     if (DataType::IsReference(inst->GetType())) {
1772         return graph_->GetOrCreateNullPtr();
1773     }
1774     if (inst->GetType() == DataType::FLOAT32) {
1775         return graph_->FindOrCreateConstant(0.0F);
1776     }
1777     if (inst->GetType() == DataType::FLOAT64) {
1778         return graph_->FindOrCreateConstant(0.0);
1779     }
1780     return graph_->FindOrCreateConstant(0U);
1781 }
1782 
ReplaceAliases()1783 void ScalarReplacement::ReplaceAliases()
1784 {
1785     ArenaVector<Inst *> replaceInputs {graph_->GetLocalAllocator()->Adapter()};
1786     for (auto &[inst, alias] : aliases_) {
1787         Inst *replacement = ResolveAlias(alias, inst);
1788         if (replacement != inst && replacement != nullptr) {
1789             for (auto &user : inst->GetUsers()) {
1790                 replaceInputs.push_back(user.GetInst());
1791             }
1792         }
1793 
1794         bool replaced = false;
1795         if (replacement != nullptr && inst->GetOpcode() == Opcode::LoadObject &&
1796             DataType::NeedCastForTypes(graph_->GetArch(), replacement->GetType(), inst->GetType())) {
1797             // In case of loads/stores explicit casts could be eliminated before scalar replacement.
1798             // To use correct values after load's replacement with a value stored into a field we
1799             // need to insert some casts back.
1800             replaced = true;
1801             for (auto user : replaceInputs) {
1802                 auto cast = graph_->CreateInstCast(inst->GetType(), inst->GetPc(), replacement, replacement->GetType());
1803                 inst->InsertBefore(cast);
1804                 replacement = cast;
1805                 user->ReplaceInput(inst, replacement);
1806             }
1807         } else if (replacement != nullptr && replacement->IsClassInst()) {
1808             auto classImm = graph_->CreateInstLoadImmediate(DataType::REFERENCE, replacement->GetPc(),
1809                                                             static_cast<ClassInst *>(replacement)->GetClass());
1810             replacement->InsertAfter(classImm);
1811             replacement = classImm;
1812         }
1813 
1814         if (replacement != nullptr) {
1815             COMPILER_LOG(DEBUG, PEA) << "Replacing " << *inst << " with " << *replacement;
1816         }
1817         if (!replaced) {
1818             for (auto user : replaceInputs) {
1819                 user->ReplaceInput(inst, replacement);
1820             }
1821         }
1822         replaceInputs.clear();
1823         EnqueueForRemoval(inst);
1824     }
1825 }
1826 
ResolvePhiInputs()1827 void ScalarReplacement::ResolvePhiInputs()
1828 {
1829     for (auto [state, inst] : allocatedPhis_) {
1830         auto preds = inst->GetBasicBlock()->GetPredsBlocks();
1831         auto inputs = state->GetInputs();
1832         ASSERT(preds.size() == inputs.size());
1833         for (size_t idx = 0; idx < inputs.size(); idx++) {
1834             auto inputInst = ResolveAlias(inputs[idx], inst);
1835             ASSERT(inputInst != nullptr);
1836             if (EscapeAnalysis::IsAllocInst(inputInst)) {
1837                 inputInst = ResolveAllocation(inputInst, preds[idx]);
1838             }
1839             inst->AppendInput(inputInst);
1840         }
1841     }
1842 }
1843 
ResolveAllocation(Inst * inst,BasicBlock * block)1844 Inst *ScalarReplacement::ResolveAllocation(Inst *inst, BasicBlock *block)
1845 {
1846     ASSERT(inst != nullptr);
1847     auto it = materializedObjects_.find(inst);
1848     if (it == materializedObjects_.end()) {
1849         return inst;
1850     }
1851     auto &allocs = it->second;
1852     for (auto alloc : allocs) {
1853         if (alloc->GetBasicBlock()->IsDominate(block)) {
1854             return alloc;
1855         }
1856     }
1857     UNREACHABLE();
1858     return inst;
1859 }
1860 
UpdateSaveStates()1861 void ScalarReplacement::UpdateSaveStates()
1862 {
1863     ArenaVector<Inst *> queue {graph_->GetLocalAllocator()->Adapter()};
1864     for (auto &[site, virtualObjects] : saveStateLiveness_) {
1865         bool isCall = site->IsCall();
1866         ASSERT(!isCall || static_cast<CallInst *>(site)->IsInlined());
1867         // Remove virtual inputs (i.e. objects that are not alive)
1868         for (ssize_t inputIdx = static_cast<ssize_t>(site->GetInputsCount()) - 1; inputIdx >= 0; --inputIdx) {
1869             if (!virtualObjects.GetBit(inputIdx)) {
1870                 continue;
1871             }
1872             if (isCall) {
1873                 site->SetInput(inputIdx, graph_->GetOrCreateNullPtr());
1874                 continue;
1875             }
1876             site->RemoveInput(inputIdx);
1877         }
1878     }
1879 }
1880 
UpdateAllocationUsers()1881 void ScalarReplacement::UpdateAllocationUsers()
1882 {
1883     ArenaVector<std::tuple<Inst *, size_t, Inst *>> queue {graph_->GetLocalAllocator()->Adapter()};
1884     for (auto &[oldAlloc, newAllocs] : materializedObjects_) {
1885         // At these point:
1886         // - all aliases should be already processed and corresponding users will be enqueued for removal
1887         // - all save states and inlined calls will be already processed
1888         // - inputs for newly allocated phis will be processed as well
1889         for (auto &user : oldAlloc->GetUsers()) {
1890             auto userInst = user.GetInst();
1891             if (IsEnqueuedForRemoval(userInst)) {
1892                 continue;
1893             }
1894             // There might be multiple materializations of the same object so we need to find
1895             // the one dominating current user.
1896             auto userBlock =
1897                 userInst->IsPhi() ? userInst->CastToPhi()->GetPhiInputBb(user.GetIndex()) : userInst->GetBasicBlock();
1898             COMPILER_LOG(DEBUG, PEA) << "User block = " << userBlock->GetId();
1899             auto replacementIt = std::find_if(newAllocs.begin(), newAllocs.end(), [userBlock](auto newAlloc) {
1900                 return newAlloc->GetBasicBlock()->IsDominate(userBlock);
1901             });
1902             ASSERT(replacementIt != newAllocs.end());
1903             queue.emplace_back(userInst, user.GetIndex(), *replacementIt);
1904         }
1905         for (auto &[user, idx, replacement] : queue) {
1906             user->SetInput(idx, replacement);
1907         }
1908         queue.clear();
1909 
1910         EnqueueForRemoval(oldAlloc);
1911     }
1912 }
1913 
EnqueueForRemoval(Inst * inst)1914 void ScalarReplacement::EnqueueForRemoval(Inst *inst)
1915 {
1916     if (IsEnqueuedForRemoval(inst)) {
1917         return;
1918     }
1919     inst->SetMarker(removeInstMarker_);
1920     removalQueue_.push_back(inst);
1921 }
1922 
IsEnqueuedForRemoval(Inst * inst) const1923 bool ScalarReplacement::IsEnqueuedForRemoval(Inst *inst) const
1924 {
1925     return inst->IsMarked(removeInstMarker_);
1926 }
1927 
InputSizeGtSize(Inst * inst)1928 static bool InputSizeGtSize(Inst *inst)
1929 {
1930     uint32_t size = 0;
1931     auto arch = inst->GetBasicBlock()->GetGraph()->GetArch();
1932 
1933     for (auto input : inst->GetInputs()) {
1934         auto inputSize = DataType::GetTypeSize(input.GetInst()->GetType(), arch);
1935         size = size < inputSize ? inputSize : size;
1936     }
1937     return size > DataType::GetTypeSize(inst->GetType(), arch);
1938 }
1939 
1940 /* Phi instruction can have inputs that are wider than
1941  * the phi type, we have to insert casts, so that, the
1942  * phi takes the types matching it's own type */
FixPhiInputTypes()1943 void ScalarReplacement::FixPhiInputTypes()
1944 {
1945     auto arch = graph_->GetArch();
1946     for (auto alphi : allocatedPhis_) {
1947         auto phi = alphi.second;
1948         if (LIKELY(!InputSizeGtSize(phi) || !phi->HasUsers())) {
1949             continue;
1950         }
1951         auto phiSize = DataType::GetTypeSize(phi->GetType(), arch);
1952         for (auto &i : phi->GetInputs()) {
1953             auto input = i.GetInst();
1954             /* constants are taken care of by constprop and existing
1955              * phis have to be properly casted already */
1956             if (LIKELY(phiSize == DataType::GetTypeSize(input->GetType(), arch) ||
1957                        input->GetOpcode() == Opcode::Constant || input->GetOpcode() == Opcode::Phi)) {
1958                 continue;
1959             }
1960             /* replace the wider-than-phi-type input with the cast */
1961             auto cast = graph_->CreateInstCast(phi->GetType(), input->GetPc(), input, input->GetType());
1962             phi->ReplaceInput(input, cast);
1963             input->InsertAfter(cast);
1964         }
1965     }
1966 }
1967 
ProcessRemovalQueue()1968 void ScalarReplacement::ProcessRemovalQueue()
1969 {
1970     for (auto inst : removalQueue_) {
1971         COMPILER_LOG(DEBUG, PEA) << "Removing inst " << inst->GetId();
1972         inst->GetBasicBlock()->RemoveInst(inst);
1973     }
1974     FixPhiInputTypes();
1975 }
1976 
Apply(ArenaSet<Inst * > & candidates)1977 void ScalarReplacement::Apply(ArenaSet<Inst *> &candidates)
1978 {
1979     removeInstMarker_ = graph_->NewMarker();
1980     CreatePhis();
1981     MaterializeObjects();
1982     ReplaceAliases();
1983     ResolvePhiInputs();
1984     UpdateSaveStates();
1985     UpdateAllocationUsers();
1986     PatchSaveStates();
1987     for (auto candidate : candidates) {
1988         EnqueueForRemoval(candidate);
1989     }
1990     ProcessRemovalQueue();
1991     graph_->EraseMarker(removeInstMarker_);
1992 }
1993 
PatchSaveStates()1994 void ScalarReplacement::PatchSaveStates()
1995 {
1996     ArenaVector<ArenaSet<Inst *>> liveness {graph_->GetLocalAllocator()->Adapter()};
1997     for (size_t idx = 0; idx < graph_->GetVectorBlocks().size(); ++idx) {
1998         liveness.emplace_back(graph_->GetLocalAllocator()->Adapter());
1999     }
2000 
2001     auto &rpo = graph_->GetBlocksRPO();
2002     for (auto it = rpo.rbegin(), end = rpo.rend(); it != end; ++it) {
2003         auto block = *it;
2004         PatchSaveStatesInBlock(block, liveness);
2005     }
2006 }
2007 
FillLiveInsts(BasicBlock * block,ArenaSet<Inst * > & liveIns,ArenaVector<ArenaSet<Inst * >> & liveness)2008 void ScalarReplacement::FillLiveInsts(BasicBlock *block, ArenaSet<Inst *> &liveIns,
2009                                       ArenaVector<ArenaSet<Inst *>> &liveness)
2010 {
2011     for (auto succ : block->GetSuccsBlocks()) {
2012         liveIns.insert(liveness[succ->GetId()].begin(), liveness[succ->GetId()].end());
2013         for (auto phiInst : succ->PhiInsts()) {
2014             auto phiInput = phiInst->CastToPhi()->GetPhiInput(block);
2015             if (DataType::IsReference(phiInput->GetType())) {
2016                 liveIns.insert(phiInput);
2017             }
2018         }
2019     }
2020 }
2021 
HasUsageOutsideBlock(Inst * inst,BasicBlock * initialBlock)2022 bool ScalarReplacement::HasUsageOutsideBlock(Inst *inst, BasicBlock *initialBlock)
2023 {
2024     for (auto &user : inst->GetUsers()) {
2025         if (user.GetInst()->GetBasicBlock() != initialBlock) {
2026             return true;
2027         }
2028     }
2029     return false;
2030 }
2031 
2032 // 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)2033 void ScalarReplacement::PatchSaveStatesInBlock(BasicBlock *block, ArenaVector<ArenaSet<Inst *>> &liveness)
2034 {
2035     auto &liveIns = liveness[block->GetId()];
2036     FillLiveInsts(block, liveIns, liveness);
2037 
2038     auto loop = block->GetLoop();
2039     bool loopIsHeader = !loop->IsRoot() && loop->GetHeader() == block;
2040     if (loopIsHeader) {
2041         for (auto inst : block->InstsReverse()) {
2042             if (IsEnqueuedForRemoval(inst) || !HasUsageOutsideBlock(inst, block)) {
2043                 continue;
2044             }
2045             // That part is neccessary only when some instructions is used outside block with definition ref
2046             AddLiveInputs(inst, liveIns);
2047         }
2048     }
2049     for (auto inst : block->InstsReverse()) {
2050         if (inst->IsSaveState()) {
2051             PatchSaveState(static_cast<SaveStateInst *>(inst), liveIns);
2052         }
2053         if (DataType::IsReference(inst->GetType())) {
2054             liveIns.erase(inst);
2055         }
2056         if (IsEnqueuedForRemoval(inst)) {
2057             continue;
2058         }
2059         AddLiveInputs(inst, liveIns);
2060     }
2061 
2062     if (!loop->IsRoot() && loop->GetHeader() == block) {
2063         for (auto phiInst : block->PhiInsts()) {
2064             bool propagateThroughLoop = false;
2065             for (auto &user : phiInst->GetUsers()) {
2066                 propagateThroughLoop =
2067                     propagateThroughLoop ||
2068                     (user.GetInst()->IsPhi() && user.GetInst()->GetBasicBlock() == phiInst->GetBasicBlock());
2069             }
2070             if (!propagateThroughLoop) {
2071                 liveIns.erase(phiInst);
2072             }
2073         }
2074         PatchSaveStatesInLoop(loop, liveIns, liveness);
2075     }
2076     for (auto phiInst : block->PhiInsts()) {
2077         liveIns.erase(phiInst);
2078     }
2079 }
2080 
AddLiveInputs(Inst * inst,ArenaSet<Inst * > & liveIns)2081 void ScalarReplacement::AddLiveInputs(Inst *inst, ArenaSet<Inst *> &liveIns)
2082 {
2083     for (auto &input : inst->GetInputs()) {
2084         auto inputInst = inst->GetDataFlowInput(input.GetInst());
2085         if (!DataType::IsReference(inputInst->GetType()) || inputInst->IsStore() || !inputInst->IsMovableObject()) {
2086             continue;
2087         }
2088         liveIns.insert(inputInst);
2089     }
2090 }
2091 
PatchSaveStatesInLoop(Loop * loop,ArenaSet<Inst * > & loopLiveIns,ArenaVector<ArenaSet<Inst * >> & liveness)2092 void ScalarReplacement::PatchSaveStatesInLoop(Loop *loop, ArenaSet<Inst *> &loopLiveIns,
2093                                               ArenaVector<ArenaSet<Inst *>> &liveness)
2094 {
2095     for (auto loopBlock : graph_->GetVectorBlocks()) {
2096         if (loopBlock == nullptr) {
2097             continue;
2098         }
2099         if (loopBlock->GetLoop() != loop && !loopBlock->GetLoop()->IsInside(loop)) {
2100             continue;
2101         }
2102         if (loopBlock != loop->GetHeader()) {
2103             auto &loopBlockLiveIns = liveness[loopBlock->GetId()];
2104             loopBlockLiveIns.insert(loopLiveIns.begin(), loopLiveIns.end());
2105         }
2106         for (auto inst : loopBlock->Insts()) {
2107             if (inst->IsSaveState()) {
2108                 PatchSaveState(static_cast<SaveStateInst *>(inst), loopLiveIns);
2109             }
2110         }
2111     }
2112 }
2113 
PatchSaveState(SaveStateInst * saveState,ArenaSet<Inst * > & liveInstructions)2114 void ScalarReplacement::PatchSaveState(SaveStateInst *saveState, ArenaSet<Inst *> &liveInstructions)
2115 {
2116     for (auto inst : liveInstructions) {
2117         inst = Inst::GetDataFlowInput(inst);
2118         if (!inst->IsMovableObject()) {
2119             continue;
2120         }
2121         auto inputs = saveState->GetInputs();
2122         auto it = std::find_if(inputs.begin(), inputs.end(), [inst](auto &in) { return in.GetInst() == inst; });
2123         if (it != inputs.end()) {
2124             continue;
2125         }
2126         saveState->AppendBridge(inst);
2127     }
2128 }
2129 }  // namespace ark::compiler
2130