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