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