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