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