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 "optimizer/analysis/reg_alloc_verifier.h"
17 #include "optimizer/analysis/liveness_analyzer.h"
18 #include "optimizer/analysis/live_registers.h"
19 #include "optimizer/ir/basicblock.h"
20 #include "optimizer/code_generator/callconv.h"
21 #include "optimizer/code_generator/codegen.h"
22 #include "optimizer/optimizations/regalloc/reg_type.h"
23
24 namespace ark::compiler {
25 namespace {
ToString(LocationState::State state)26 std::string ToString(LocationState::State state)
27 {
28 switch (state) {
29 case LocationState::State::UNKNOWN:
30 return "UNKNOWN";
31 case LocationState::State::KNOWN:
32 return "KNOWN";
33 case LocationState::State::CONFLICT:
34 return "CONFLICT";
35 default:
36 UNREACHABLE();
37 }
38 }
39
GetPhiLocationState(const BlockState & state,const ArenaVector<LocationState> & immediates,const LifeIntervals * interval,bool isHigh)40 const LocationState &GetPhiLocationState(const BlockState &state, const ArenaVector<LocationState> &immediates,
41 const LifeIntervals *interval, bool isHigh)
42 {
43 auto location = interval->GetLocation();
44 if (location.IsStack()) {
45 ASSERT(!isHigh);
46 return state.GetStack(location.GetValue());
47 }
48 if (location.IsConstant()) {
49 ASSERT(location.GetValue() < immediates.size());
50 return immediates[location.GetValue()];
51 }
52 if (location.IsFpRegister()) {
53 ASSERT(IsFloatType(interval->GetType()));
54 return state.GetVReg(location.GetValue() + (isHigh ? 1U : 0U));
55 }
56 ASSERT(location.IsRegister());
57 return state.GetReg(location.GetValue() + (isHigh ? 1U : 0U));
58 }
59
GetPhiLocationState(BlockState * state,const LifeIntervals * interval,bool isHigh)60 LocationState &GetPhiLocationState(BlockState *state, const LifeIntervals *interval, bool isHigh)
61 {
62 auto location = interval->GetLocation();
63 if (location.IsStack()) {
64 ASSERT(!isHigh);
65 return state->GetStack(location.GetValue());
66 }
67 if (location.IsFpRegister()) {
68 ASSERT(IsFloatType(interval->GetType()));
69 return state->GetVReg(location.GetValue() + (isHigh ? 1U : 0U));
70 }
71 ASSERT(location.IsRegister());
72 return state->GetReg(location.GetValue() + (isHigh ? 1U : 0U));
73 }
74
MergeImpl(const ArenaVector<LocationState> & from,ArenaVector<LocationState> * to)75 bool MergeImpl(const ArenaVector<LocationState> &from, ArenaVector<LocationState> *to)
76 {
77 bool updated = false;
78 for (size_t offset = 0; offset < from.size(); ++offset) {
79 if (to->at(offset).ShouldSkip()) {
80 to->at(offset).SetSkip(false);
81 continue;
82 }
83 updated |= to->at(offset).Merge(from[offset]);
84 }
85 return updated;
86 }
87
88 class BlockStates {
89 public:
BlockStates(size_t regs,size_t vregs,size_t stackSlots,size_t stackParams,ArenaAllocator * allocator)90 BlockStates(size_t regs, size_t vregs, size_t stackSlots, size_t stackParams, ArenaAllocator *allocator)
91 : startState_(regs, vregs, stackSlots, stackParams, allocator),
92 endState_(regs, vregs, stackSlots, stackParams, allocator)
93 {
94 }
95 ~BlockStates() = default;
96 NO_COPY_SEMANTIC(BlockStates);
97 NO_MOVE_SEMANTIC(BlockStates);
98
GetStart()99 BlockState &GetStart()
100 {
101 return startState_;
102 }
103
GetEnd()104 BlockState &GetEnd()
105 {
106 return endState_;
107 }
108
ResetEndToStart()109 BlockState &ResetEndToStart()
110 {
111 endState_.Copy(&startState_);
112 return endState_;
113 }
114
IsUpdated() const115 bool IsUpdated() const
116 {
117 return updated_;
118 }
119
SetUpdated(bool upd)120 void SetUpdated(bool upd)
121 {
122 updated_ = upd;
123 }
124
IsVisited() const125 bool IsVisited() const
126 {
127 return visited_;
128 }
129
SetVisited(bool visited)130 void SetVisited(bool visited)
131 {
132 visited_ = visited;
133 }
134
135 private:
136 BlockState startState_;
137 BlockState endState_;
138 bool updated_ {false};
139 bool visited_ {false};
140 };
141
InitStates(ArenaUnorderedMap<uint32_t,BlockStates> * blocks,const Graph * graph)142 void InitStates(ArenaUnorderedMap<uint32_t, BlockStates> *blocks, const Graph *graph)
143 {
144 auto usedRegs = graph->GetUsedRegs<DataType::INT64>()->size();
145 auto usedVregs = graph->GetUsedRegs<DataType::FLOAT64>()->size();
146 for (auto &bb : graph->GetVectorBlocks()) {
147 if (bb == nullptr) {
148 continue;
149 }
150 [[maybe_unused]] auto res = blocks->try_emplace(bb->GetId(), usedRegs, usedVregs, GetMaxNumStackSlots(),
151 GetMaxNumStackSlots(), graph->GetLocalAllocator());
152 ASSERT(res.second);
153 }
154 }
155
CheckAllBlocksVisited(const ArenaUnorderedMap<uint32_t,BlockStates> & blocks)156 void CheckAllBlocksVisited([[maybe_unused]] const ArenaUnorderedMap<uint32_t, BlockStates> &blocks)
157 {
158 #ifndef NDEBUG
159 for (auto &blockState : blocks) {
160 ASSERT(blockState.second.IsVisited());
161 }
162 #endif
163 }
164
IsRegisterPair(Location location,DataType::Type type,Graph * graph)165 bool IsRegisterPair(Location location, DataType::Type type, Graph *graph)
166 {
167 if (!location.IsRegister()) {
168 return false;
169 }
170 return Is64Bits(type, graph->GetArch()) && !Is64BitsArch(graph->GetArch());
171 }
172
IsRegisterPair(const LifeIntervals * li)173 bool IsRegisterPair(const LifeIntervals *li)
174 {
175 if (!li->HasReg()) {
176 return false;
177 }
178 return IsRegisterPair(Location::MakeRegister(li->GetReg()), li->GetType(),
179 li->GetInst()->GetBasicBlock()->GetGraph());
180 }
181 } // namespace
182
Merge(const LocationState & other)183 bool LocationState::Merge(const LocationState &other)
184 {
185 if (state_ == LocationState::State::CONFLICT) {
186 return false;
187 }
188 if (state_ == LocationState::State::UNKNOWN) {
189 state_ = other.state_;
190 id_ = other.id_;
191 return other.state_ != LocationState::State::UNKNOWN;
192 }
193 if (other.state_ == LocationState::State::CONFLICT) {
194 state_ = LocationState::State::CONFLICT;
195 return true;
196 }
197 if (other.state_ == LocationState::State::KNOWN && state_ == LocationState::State::KNOWN && other.id_ != id_) {
198 state_ = LocationState::State::CONFLICT;
199 return true;
200 }
201 return false;
202 }
203
BlockState(size_t regs,size_t vregs,size_t stackSlots,size_t stackParams,ArenaAllocator * alloc)204 BlockState::BlockState(size_t regs, size_t vregs, size_t stackSlots, size_t stackParams, ArenaAllocator *alloc)
205 : regs_(regs, alloc->Adapter()),
206 vregs_(vregs, alloc->Adapter()),
207 stack_(stackSlots, alloc->Adapter()),
208 stackParam_(stackParams, alloc->Adapter()),
209 stackArg_(0, alloc->Adapter())
210
211 {
212 }
213
Merge(const BlockState & state,const PhiInstSafeIter & phis,BasicBlock * pred,const ArenaVector<LocationState> & immediates,const LivenessAnalyzer & la)214 bool BlockState::Merge(const BlockState &state, const PhiInstSafeIter &phis, BasicBlock *pred,
215 const ArenaVector<LocationState> &immediates, const LivenessAnalyzer &la)
216 {
217 bool updated = false;
218 /* Phi instructions are resolved into moves at the end of predecessor blocks,
219 * but corresponding locations contain ids of instructions merged by a phi.
220 * Phi's location at the beginning of current block is only updated when
221 * the same location at the end of predecessor holds result of an instruction
222 * that is a phi's input. Otherwise phi's location state will be either remains unknown,
223 * or it will be merged to conflicting state.
224 */
225 constexpr std::array<bool, 2U> IS_HIGH = {false, true};
226 for (auto phi : phis) {
227 auto phiInterval = la.GetInstLifeIntervals(phi);
228 auto input = phi->CastToPhi()->GetPhiDataflowInput(pred);
229 for (size_t isHighIdx = 0; isHighIdx < (IsRegisterPair(phiInterval) ? 2U : 1U); isHighIdx++) {
230 auto &inputState = GetPhiLocationState(state, immediates, phiInterval, IS_HIGH[isHighIdx]);
231 auto &phiState = GetPhiLocationState(this, phiInterval, IS_HIGH[isHighIdx]);
232
233 if (inputState.GetState() != LocationState::State::KNOWN) {
234 continue;
235 }
236 if (inputState.IsValid(input)) {
237 updated = phiState.GetState() != LocationState::State::KNOWN || phiState.GetId() != phi->GetId();
238 phiState.SetId(phi->GetId());
239 // don't merge it
240 phiState.SetSkip(true);
241 }
242 }
243 }
244 updated |= MergeImpl(state.regs_, ®s_);
245 updated |= MergeImpl(state.vregs_, &vregs_);
246 updated |= MergeImpl(state.stack_, &stack_);
247 updated |= MergeImpl(state.stackParam_, &stackParam_);
248 // note that stack_arg_ is not merged as it is only used during call handing
249 return updated;
250 }
251
Copy(BlockState * state)252 void BlockState::Copy(BlockState *state)
253 {
254 std::copy(state->regs_.begin(), state->regs_.end(), regs_.begin());
255 std::copy(state->vregs_.begin(), state->vregs_.end(), vregs_.begin());
256 std::copy(state->stack_.begin(), state->stack_.end(), stack_.begin());
257 std::copy(state->stackParam_.begin(), state->stackParam_.end(), stackParam_.begin());
258 // note that stack_arg_ is not merged as it is only used during call handing
259 }
260
RegAllocVerifier(Graph * graph,bool saveLiveRegsOnCall)261 RegAllocVerifier::RegAllocVerifier(Graph *graph, bool saveLiveRegsOnCall)
262 : Analysis(graph),
263 immediates_(graph->GetLocalAllocator()->Adapter()),
264 savedRegs_(GetGraph()->GetLocalAllocator()->Adapter()),
265 savedVregs_(GetGraph()->GetLocalAllocator()->Adapter()),
266 saveLiveRegs_(saveLiveRegsOnCall)
267 {
268 }
269
InitImmediates()270 void RegAllocVerifier::InitImmediates()
271 {
272 immediates_.clear();
273 for (size_t immSlot = 0; immSlot < GetGraph()->GetSpilledConstantsCount(); ++immSlot) {
274 auto con = GetGraph()->GetSpilledConstant(immSlot);
275 immediates_.emplace_back(LocationState::State::KNOWN, con->GetId());
276 }
277 }
278
GetLocationState(Location location)279 LocationState &RegAllocVerifier::GetLocationState(Location location)
280 {
281 auto loc = location.GetKind();
282 auto offset = location.GetValue();
283
284 if (loc == LocationType::STACK) {
285 return currentState_->GetStack(offset);
286 }
287 if (loc == LocationType::REGISTER) {
288 return currentState_->GetReg(offset);
289 }
290 if (loc == LocationType::FP_REGISTER) {
291 return currentState_->GetVReg(offset);
292 }
293 if (loc == LocationType::STACK_ARGUMENT) {
294 return currentState_->GetStackArg(offset);
295 }
296 if (loc == LocationType::STACK_PARAMETER) {
297 return currentState_->GetStackParam(offset);
298 }
299 ASSERT(loc == LocationType::IMMEDIATE);
300 ASSERT(offset < immediates_.size());
301 return immediates_[offset];
302 }
303
304 // Apply callback to a location corresponding to loc, offset and type.
305 // If location is represented by registers pair then apply the callback to both parts.
306 // Return true if all invocations of the callback returned true.
307 template <typename T>
ForEachLocation(Location location,DataType::Type type,T callback)308 bool RegAllocVerifier::ForEachLocation(Location location, DataType::Type type, T callback)
309 {
310 bool success = callback(GetLocationState(location));
311 if (IsRegisterPair(location, type, GetGraph())) {
312 auto piredReg = Location::MakeRegister(location.GetValue() + 1);
313 success &= callback(GetLocationState(piredReg));
314 }
315 return success;
316 }
317
UpdateLocation(Location location,DataType::Type type,uint32_t instId)318 void RegAllocVerifier::UpdateLocation(Location location, DataType::Type type, uint32_t instId)
319 {
320 ForEachLocation(location, type, [instId](auto &state) {
321 state.SetId(instId);
322 return true;
323 });
324 }
325
HandleDest(Inst * inst)326 void RegAllocVerifier::HandleDest(Inst *inst)
327 {
328 if (inst->NoDest()) {
329 return;
330 }
331 auto type = inst->GetType();
332 if (GetGraph()->GetZeroReg() != GetInvalidReg() && inst->GetDstReg() == GetGraph()->GetZeroReg()) {
333 UpdateLocation(Location::MakeRegister(inst->GetDstReg()), type, LocationState::ZERO_INST);
334 return;
335 }
336
337 if (inst->GetDstCount() == 1) {
338 UpdateLocation(Location::MakeRegister(inst->GetDstReg(), type), type, inst->GetId());
339 return;
340 }
341
342 [[maybe_unused]] size_t handledUsers = 0;
343 for (auto &user : inst->GetUsers()) {
344 if (IsPseudoUserOfMultiOutput(user.GetInst())) {
345 auto instId = user.GetInst()->GetId();
346 auto idx = user.GetInst()->CastToLoadPairPart()->GetSrcRegIndex();
347 UpdateLocation(Location::MakeRegister(inst->GetDstReg(idx), type), type, instId);
348 ++handledUsers;
349 }
350 }
351 ASSERT(handledUsers == inst->GetDstCount());
352 }
353
PropagateBlockState(BasicBlock * currentBlock,BlockState * currentState,const ArenaVector<LocationState> & immediates,ArenaUnorderedMap<uint32_t,BlockStates> * blocks)354 BasicBlock *PropagateBlockState(BasicBlock *currentBlock, BlockState *currentState,
355 const ArenaVector<LocationState> &immediates,
356 ArenaUnorderedMap<uint32_t, BlockStates> *blocks)
357 {
358 BasicBlock *nextBlock {nullptr};
359 auto &la = currentBlock->GetGraph()->GetAnalysis<LivenessAnalyzer>();
360 for (auto succ : currentBlock->GetSuccsBlocks()) {
361 auto phis = succ->PhiInstsSafe();
362 auto &succState = blocks->at(succ->GetId());
363 bool mergeUpdated = succState.GetStart().Merge(*currentState, phis, currentBlock, immediates, la);
364 // if merged did not update the state, but successor is not visited yet then force its visit
365 mergeUpdated |= !succState.IsVisited();
366 succState.SetUpdated(succState.IsUpdated() || mergeUpdated);
367 if (mergeUpdated) {
368 nextBlock = succ;
369 }
370 }
371 return nextBlock;
372 }
373
RunImpl()374 bool RegAllocVerifier::RunImpl()
375 {
376 ArenaUnorderedMap<uint32_t, BlockStates> blocks(GetGraph()->GetLocalAllocator()->Adapter());
377 InitStates(&blocks, GetGraph());
378 InitImmediates();
379 implicitNullCheckHandledMarker_ = GetGraph()->NewMarker();
380
381 success_ = true;
382 currentBlock_ = GetGraph()->GetStartBlock();
383 while (success_) {
384 ASSERT(currentBlock_ != nullptr);
385
386 blocks.at(currentBlock_->GetId()).SetUpdated(false);
387 blocks.at(currentBlock_->GetId()).SetVisited(true);
388 // use state at the end of the block as current state and reset it to the state
389 // at the beginning of the block before processing.
390 currentState_ = &blocks.at(currentBlock_->GetId()).ResetEndToStart();
391
392 ProcessCurrentBlock();
393 if (!success_) {
394 break;
395 }
396
397 currentBlock_ = PropagateBlockState(currentBlock_, currentState_, immediates_, &blocks);
398 if (currentBlock_ != nullptr) {
399 continue;
400 }
401 // pick a block pending processing
402 auto it = std::find_if(blocks.begin(), blocks.end(), [](auto &t) { return t.second.IsUpdated(); });
403 if (it == blocks.end()) {
404 break;
405 }
406 const auto &bbs = GetGraph()->GetVectorBlocks();
407 auto bbId = it->first;
408 auto blockIt =
409 std::find_if(bbs.begin(), bbs.end(), [bbId](auto bb) { return bb != nullptr && bb->GetId() == bbId; });
410 ASSERT(blockIt != bbs.end());
411 currentBlock_ = *blockIt;
412 }
413
414 GetGraph()->EraseMarker(implicitNullCheckHandledMarker_);
415 if (success_) {
416 CheckAllBlocksVisited(blocks);
417 }
418 return success_;
419 }
420
ProcessCurrentBlock()421 void RegAllocVerifier::ProcessCurrentBlock()
422 {
423 for (auto inst : currentBlock_->InstsSafe()) {
424 if (!success_) {
425 return;
426 }
427 #ifdef PANDA_WITH_IRTOC
428 if (IsSaveRestoreRegisters(inst)) {
429 HandleSaveRestoreRegisters(inst);
430 continue;
431 }
432 #endif
433 if (inst->GetOpcode() == Opcode::ReturnInlined) {
434 // ReturnInlined is pseudo instruction having SaveState input that
435 // only prolongs SaveState input's lifetime. There are no guarantees that
436 // SaveState's inputs will be stored in locations specified in SaveState
437 // at ReturnInlined.
438 continue;
439 }
440
441 TryHandleImplicitNullCheck(inst);
442 // pseudo user of multi output will be handled by HandleDest
443 if (inst->IsSaveState() || IsPseudoUserOfMultiOutput(inst)) {
444 continue;
445 }
446
447 if (inst->IsCall() && static_cast<CallInst *>(inst)->IsInlined()) {
448 ASSERT(inst->GetDstReg() == GetInvalidReg());
449 continue;
450 }
451
452 if (inst->IsParameter()) {
453 HandleParameter(inst->CastToParameter());
454 } else if (inst->IsSpillFill()) {
455 HandleSpillFill(inst->CastToSpillFill());
456 } else if (inst->IsConst()) {
457 HandleConst(inst->CastToConstant());
458 } else {
459 HandleInst(inst);
460 }
461 }
462 }
463
464 // Set inst's id to corresponding location
HandleParameter(ParameterInst * inst)465 void RegAllocVerifier::HandleParameter(ParameterInst *inst)
466 {
467 auto sf = inst->GetLocationData();
468 UpdateLocation(sf.GetDst(), inst->GetType(), inst->GetId());
469 }
470
SameState(LocationState & left,LocationState & right)471 bool SameState(LocationState &left, LocationState &right)
472 {
473 return left.GetId() == right.GetId() && left.GetState() == right.GetState();
474 }
475
476 // Copy inst's id from source location to destination location,
477 // source location should contain known value.
HandleSpillFill(SpillFillInst * inst)478 void RegAllocVerifier::HandleSpillFill(SpillFillInst *inst)
479 {
480 for (auto sf : inst->GetSpillFills()) {
481 LocationState *state = nullptr;
482 success_ &= ForEachLocation(
483 sf.GetSrc(), sf.GetType(), [&state, &sf, inst, arch = GetGraph()->GetArch()](LocationState &st) {
484 if (state == nullptr) {
485 state = &st;
486 } else if (!SameState(*state, st)) {
487 COMPILER_LOG(ERROR, REGALLOC) << "SpillFill is accessing " << sf.GetSrc().ToString(arch)
488 << " with different state for high and low parts";
489 return false;
490 }
491 if (st.GetState() == LocationState::State::KNOWN) {
492 return true;
493 }
494 COMPILER_LOG(ERROR, REGALLOC)
495 << "SpillFill is copying value from location with state " << ToString(st.GetState()) << ", "
496 << inst->GetId() << " read from " << sf.GetSrc().ToString(arch);
497 return false;
498 });
499 ASSERT(state != nullptr);
500 UpdateLocation(sf.GetDst(), sf.GetType(), state->GetId());
501 }
502 }
503
504 // Set instn's id to corresponding location.
HandleConst(ConstantInst * inst)505 void RegAllocVerifier::HandleConst(ConstantInst *inst)
506 {
507 if (inst->GetDstReg() != GetInvalidReg()) {
508 HandleDest(inst);
509 return;
510 }
511
512 // if const inst does not have valid register then it was spilled to imm table.
513 auto immSlot = GetGraph()->FindSpilledConstantSlot(inst->CastToConstant());
514 // zero const is a special case - we're using zero reg to encode it.
515 if (immSlot == GetInvalidImmTableSlot() && IsZeroConstantOrNullPtr(inst)) {
516 return;
517 }
518
519 // if there is no place in the imm table, constant will be spilled to the stack.
520 ASSERT(immSlot != GetInvalidImmTableSlot() || !GetGraph()->HasAvailableConstantSpillSlots());
521 }
522
523 // Verify instn's inputs and set instn's id to destination location.
524 // CC-OFFNXT(huge_depth[C++]) solid logic
HandleInst(Inst * inst)525 void RegAllocVerifier::HandleInst(Inst *inst)
526 {
527 if (!InstHasPseudoInputs(inst)) {
528 for (size_t i = 0; i < inst->GetInputsCount(); i++) {
529 auto input = inst->GetDataFlowInput(i);
530 if (input->NoDest() && !IsPseudoUserOfMultiOutput(input)) {
531 ASSERT(!inst->GetLocation(i).IsFixedRegister());
532 continue;
533 }
534
535 auto inputType = inst->GetInputType(i);
536 if (inputType == DataType::NO_TYPE) {
537 ASSERT(inst->GetOpcode() == Opcode::CallIndirect);
538 inputType = Is64BitsArch(GetGraph()->GetArch()) ? DataType::INT64 : DataType::INT32;
539 } else {
540 inputType = ConvertRegType(GetGraph(), inputType);
541 }
542
543 success_ &= ForEachLocation(inst->GetLocation(i), inputType, [input, inst, i](LocationState &location) {
544 if (location.GetState() != LocationState::State::KNOWN) {
545 COMPILER_LOG(ERROR, REGALLOC) << "Input #" << i << " is loaded from location with state "
546 << ToString(location.GetState()) << std::endl
547 << "Affected inst: " << *inst << std::endl
548 << "Input inst: " << *input;
549 return false;
550 }
551 if (location.IsValid(input)) {
552 return true;
553 }
554 COMPILER_LOG(ERROR, REGALLOC) << "Input #" << i << " is loaded from location holding instruction "
555 << location.GetId() << " instead of " << input->GetId() << std::endl
556 << "Affected inst: " << *inst;
557 return false;
558 });
559 }
560 }
561 HandleDest(inst);
562 }
563
564 #ifdef PANDA_WITH_IRTOC
IsSaveRestoreRegisters(Inst * inst)565 bool RegAllocVerifier::IsSaveRestoreRegisters(Inst *inst)
566 {
567 if (!inst->IsIntrinsic()) {
568 return false;
569 }
570 auto intrinsicId = inst->CastToIntrinsic()->GetIntrinsicId();
571 return intrinsicId == RuntimeInterface::IntrinsicId::INTRINSIC_SAVE_REGISTERS_EP ||
572 intrinsicId == RuntimeInterface::IntrinsicId::INTRINSIC_RESTORE_REGISTERS_EP;
573 }
574
HandleSaveRestoreRegisters(Inst * inst)575 void RegAllocVerifier::HandleSaveRestoreRegisters(Inst *inst)
576 {
577 ASSERT(inst->IsIntrinsic());
578 auto usedRegs = GetGraph()->GetUsedRegs<DataType::INT64>()->size();
579 switch (inst->CastToIntrinsic()->GetIntrinsicId()) {
580 case RuntimeInterface::IntrinsicId::INTRINSIC_SAVE_REGISTERS_EP:
581 ASSERT(savedRegs_.empty());
582 for (size_t reg = 0; reg < usedRegs; reg++) {
583 savedRegs_.push_back(currentState_->GetReg(reg));
584 }
585 break;
586 case RuntimeInterface::IntrinsicId::INTRINSIC_RESTORE_REGISTERS_EP:
587 ASSERT(!savedRegs_.empty());
588 for (size_t reg = 0; reg < usedRegs; reg++) {
589 if (savedRegs_[reg].GetState() == LocationState::State::UNKNOWN) {
590 continue;
591 }
592 currentState_->GetReg(reg) = savedRegs_[reg];
593 }
594 savedRegs_.clear();
595 break;
596 default:
597 UNREACHABLE();
598 }
599
600 HandleDest(inst);
601 }
602 #endif
603
604 // Implicit null checks are not encoded as machine instructions, but instead
605 // added to method's metadata that allow to treat some memory-access related
606 // signals as null pointer exceptions.
607 // SaveState instruction bound to implicit null check captures locations
608 // of its inputs at first null check's user (See RegAllocResolver::GetExplicitUser).
609 // Check if current instruction use implicit null check that was not yet handled
610 // and handle its save state.
TryHandleImplicitNullCheck(Inst * inst)611 void RegAllocVerifier::TryHandleImplicitNullCheck(Inst *inst)
612 {
613 NullCheckInst *nc = nullptr;
614 for (auto &input : inst->GetInputs()) {
615 if (input.GetInst()->IsNullCheck() && input.GetInst()->CastToNullCheck()->IsImplicit() &&
616 !input.GetInst()->IsMarked(implicitNullCheckHandledMarker_)) {
617 nc = input.GetInst()->CastToNullCheck();
618 break;
619 }
620 }
621 if (nc == nullptr) {
622 return;
623 }
624 nc->SetMarker(implicitNullCheckHandledMarker_);
625 }
626
627 } // namespace ark::compiler
628