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