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