• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023-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 "reg_encoder.h"
17 #include "common.h"
18 #include "compiler/optimizer/ir/basicblock.h"
19 #if defined(ENABLE_LIBABCKIT)
20 #include "generated/abckit_intrinsics_vreg_width.h"
21 #else
IsDstRegNeedRenumbering(ark::compiler::Inst * inst)22 static bool IsDstRegNeedRenumbering([[maybe_unused]] ark::compiler::Inst *inst)
23 {
24     UNREACHABLE();
25 }
CheckWidthAbcKitIntrinsic(ark::bytecodeopt::RegEncoder * re,ark::compiler::Inst * inst)26 static void CheckWidthAbcKitIntrinsic([[maybe_unused]] ark::bytecodeopt::RegEncoder *re,
27                                       [[maybe_unused]] ark::compiler::Inst *inst)
28 {
29     UNREACHABLE();
30 }
31 #endif
32 
33 namespace ark::bytecodeopt {
34 
IsIntrinsicRange(Inst * inst)35 static bool IsIntrinsicRange(Inst *inst)
36 {
37     if (inst->GetOpcode() != compiler::Opcode::Intrinsic) {
38         return false;
39     }
40     if (inst->GetBasicBlock()->GetGraph()->IsAbcKit()) {
41         return IsAbcKitIntrinsicRange(inst->CastToIntrinsic()->GetIntrinsicId());
42     }
43 #if defined(ENABLE_BYTECODE_OPT) && defined(PANDA_WITH_ECMASCRIPT)
44     switch (inst->CastToIntrinsic()->GetIntrinsicId()) {
45 #ifdef ARK_INTRINSIC_SET
46         case compiler::RuntimeInterface::IntrinsicId::INTRINSIC_CALLI_RANGE_DYN:
47         case compiler::RuntimeInterface::IntrinsicId::INTRINSIC_CALLI_THIS_RANGE_DYN:
48         case compiler::RuntimeInterface::IntrinsicId::INTRINSIC_NEWOBJ_DYNRANGE:
49         case compiler::RuntimeInterface::IntrinsicId::INTRINSIC_SUPER_CALL:
50 #else
51         case compiler::RuntimeInterface::IntrinsicId::ECMA_CALLIRANGEDYN_PREF_IMM16_V8:
52         case compiler::RuntimeInterface::IntrinsicId::ECMA_CALLITHISRANGEDYN_PREF_IMM16_V8:
53         case compiler::RuntimeInterface::IntrinsicId::ECMA_NEWOBJDYNRANGE_PREF_IMM16_V8:
54         case compiler::RuntimeInterface::IntrinsicId::ECMA_SUPERCALL_PREF_IMM16_V8:
55         case compiler::RuntimeInterface::IntrinsicId::ECMA_CREATEOBJECTWITHEXCLUDEDKEYS_PREF_IMM16_V8_V8:
56 #endif
57             return true;
58         default:
59             return false;
60     }
61 #endif
62     return false;
63 }
64 
CanHoldRange(Inst * inst)65 static bool CanHoldRange(Inst *inst)
66 {
67     switch (inst->GetOpcode()) {
68         case compiler::Opcode::CallStatic:
69         case compiler::Opcode::CallVirtual:
70         case compiler::Opcode::InitObject:
71         case compiler::Opcode::Intrinsic:
72             return true;
73         default:
74             return false;
75     }
76 }
77 
CalculateNumNeededRangeTemps(const compiler::Graph * graph)78 static compiler::Register CalculateNumNeededRangeTemps(const compiler::Graph *graph)
79 {
80     compiler::Register ret = 0;
81 
82     for (auto bb : graph->GetBlocksRPO()) {
83         for (const auto &inst : bb->AllInsts()) {
84             if (!CanHoldRange(inst)) {
85                 continue;
86             }
87             auto nargs = inst->GetInputsCount() - (inst->RequireState() ? 1U : 0U);
88             if (inst->GetOpcode() == compiler::Opcode::InitObject) {
89                 ASSERT(nargs > 0U);
90                 nargs -= 1;  // exclude LoadAndInitClass
91             }
92             if (ret < nargs && (nargs > MAX_NUM_NON_RANGE_ARGS || IsIntrinsicRange(inst))) {
93                 ret = nargs;
94             }
95         }
96     }
97 
98     return ret;
99 }
100 
RegsDiff(compiler::Register r1,compiler::Register r2)101 static compiler::Register RegsDiff(compiler::Register r1, compiler::Register r2)
102 {
103     if (r1 >= r2) {
104         return r1 - r2;
105     }
106     if (compiler::IsFrameSizeLarge()) {
107         return compiler::GetInvalidReg() + 1 - (r2 - r1);
108     }
109     return static_cast<uint8_t>(r1 - r2);
110 }
111 
RegsSum(compiler::Register r1,compiler::Register r2)112 static compiler::Register RegsSum(compiler::Register r1, compiler::Register r2)
113 {
114     auto sum = static_cast<size_t>(r1 + r2);
115     if (sum <= compiler::GetInvalidReg()) {
116         return sum;
117     }
118     if (compiler::IsFrameSizeLarge()) {
119         return sum - (compiler::GetInvalidReg() + 1);
120     }
121     return static_cast<uint8_t>(r1 + r2);
122 }
123 
RunImpl()124 bool RegEncoder::RunImpl()
125 {
126     ASSERT(state_ == RegEncoderState::IDLE);
127 
128     numMaxRangeInput_ = CalculateNumNeededRangeTemps(GetGraph());
129 
130     state_ = RegEncoderState::RENUMBER_ARGS;
131     if (!RenumberArgRegs()) {
132         return false;
133     }
134 
135     state_ = RegEncoderState::RESERVE_TEMPS;
136     ASSERT(numTemps_ == 0U);
137 
138     const auto numRegs = GetNumRegs();
139 
140     auto maxNumTemps = numTemps_;
141     CalculateNumNeededTemps();
142     if (!CheckStatus()) {
143         return false;
144     }
145 
146     while (maxNumTemps != numTemps_) {
147         ASSERT(numTemps_ > maxNumTemps);
148 
149         if (numRegs > compiler::GetFrameSize() - numTemps_) {  // to avoid overflow
150             return false;                                      // no more free registers left in the frame
151         }
152 
153         auto delta = RegsDiff(numTemps_, maxNumTemps);
154         rangeTempsStart_ = RegsSum(rangeTempsStart_, delta);
155         RenumberRegs(MIN_REGISTER_NUMBER, delta);
156 
157         maxNumTemps = numTemps_;
158         CalculateNumNeededTemps();
159         if (!CheckStatus()) {
160             return false;
161         }
162         if (numTemps_ == 0U) {
163             break;
164         }
165     }
166 
167     numTemps_ = numTemps_ > 0U ? numTemps_ : numChangedWidth_;
168     if (numTemps_ > 0U || numMaxRangeInput_ > 0U) {
169         state_ = RegEncoderState::INSERT_SPILLS;
170         InsertSpills();
171         if (!CheckStatus()) {
172             return false;
173         }
174 
175         auto usageMask = GetGraph()->GetUsedRegs<compiler::DataType::INT64>();
176         for (compiler::Register r = 0; r < numRegs; r++) {
177             usageMask->at(numRegs + numTemps_ - r - 1L) = usageMask->at(numRegs - r - 1L);
178         }
179         std::fill(usageMask->begin(), usageMask->begin() + numTemps_, true);
180     }
181 
182     SaveNumLocalsToGraph(GetNumLocalsFromGraph() + numTemps_);
183     state_ = RegEncoderState::IDLE;
184 
185     return true;
186 }
187 
GetRegType(ark::compiler::DataType::Type type)188 static ark::compiler::DataType::Type GetRegType(ark::compiler::DataType::Type type)
189 {
190     if (type == ark::compiler::DataType::REFERENCE) {
191         return type;
192     }
193     if (ark::compiler::DataType::Is32Bits(type, Arch::NONE)) {
194         return ark::compiler::DataType::UINT32;
195     }
196     return ark::compiler::DataType::UINT64;
197 }
198 
RegNeedsRenumbering(ark::compiler::Register r)199 static bool RegNeedsRenumbering(ark::compiler::Register r)
200 {
201     return r != ark::compiler::GetAccReg() && r != ark::compiler::GetInvalidReg();
202 }
203 
RenumberReg(const ark::compiler::Register r,const ark::compiler::Register delta)204 static ark::compiler::Register RenumberReg(const ark::compiler::Register r, const ark::compiler::Register delta)
205 {
206     if (r == ark::compiler::GetAccReg()) {
207         return r;
208     }
209     return RegsSum(r, delta);
210 }
211 
RenumberSpillFillRegs(ark::compiler::SpillFillInst * inst,const ark::compiler::Register minReg,const ark::compiler::Register delta)212 static void RenumberSpillFillRegs(ark::compiler::SpillFillInst *inst, const ark::compiler::Register minReg,
213                                   const ark::compiler::Register delta)
214 {
215     for (auto &sf : inst->GetSpillFills()) {
216         if (sf.SrcType() == compiler::LocationType::REGISTER && sf.SrcValue() >= minReg) {
217             sf.SetSrc(compiler::Location::MakeRegister(RenumberReg(sf.SrcValue(), delta)));
218         }
219         if (sf.DstType() == compiler::LocationType::REGISTER && sf.DstValue() >= minReg) {
220             sf.SetDst(compiler::Location::MakeRegister(RenumberReg(sf.DstValue(), delta)));
221         }
222     }
223 }
224 
RenumberRegsForInst(compiler::Inst * inst,const compiler::Register minReg,const compiler::Register delta)225 static void RenumberRegsForInst(compiler::Inst *inst, const compiler::Register minReg, const compiler::Register delta)
226 {
227     // Renumber output of any instruction, if applicable:
228     if (RegNeedsRenumbering(inst->GetDstReg()) && inst->GetDstReg() >= minReg) {
229         inst->SetDstReg(RenumberReg(inst->GetDstReg(), delta));
230     }
231 
232     if (inst->IsPhi() || inst->IsCatchPhi()) {
233         return;
234     }
235 
236     // Renumber inputs and outputs of SpillFill instructions:
237     if (inst->IsSpillFill()) {
238         RenumberSpillFillRegs(inst->CastToSpillFill(), minReg, delta);
239         return;
240     }
241 
242     // Fix inputs of common instructions:
243     for (size_t i = 0; i < inst->GetInputsCount(); i++) {
244         if (RegNeedsRenumbering(inst->GetSrcReg(i)) && inst->GetSrcReg(i) >= minReg) {
245             inst->SetSrcReg(i, RenumberReg(inst->GetSrcReg(i), delta));
246         }
247     }
248 }
249 
RenumberRegs(const compiler::Register minReg,const compiler::Register delta)250 void RegEncoder::RenumberRegs(const compiler::Register minReg, const compiler::Register delta)
251 {
252     // Renumbering always advances register number `delta` positions forward,
253     // wrapping around on overflows with well-defined behavour.
254     // Hence the requirement to keep delta unsigned.
255     static_assert(std::is_unsigned<compiler::Register>::value, "compiler::Register must be unsigned");
256     ASSERT(delta > 0U);
257 
258     for (auto *bb : GetGraph()->GetBlocksRPO()) {
259         for (auto inst : bb->AllInsts()) {
260             RenumberRegsForInst(inst, minReg, delta);
261         }
262     }
263 }
264 
CalculateNumLocals(const ArenaVector<bool> * usageMask,compiler::Register numNonArgs)265 static compiler::Register CalculateNumLocals(const ArenaVector<bool> *usageMask, compiler::Register numNonArgs)
266 {
267     compiler::Register numLocals = 0;
268     if (numNonArgs != 0U) {
269         while (numLocals != numNonArgs && usageMask->at(numLocals)) {
270             ++numLocals;
271         }
272     }
273     return numLocals;
274 }
275 
CalculateNumTemps(const ArenaVector<bool> * usageMask,compiler::Register numNonArgs,compiler::Register numLocals)276 static compiler::Register CalculateNumTemps(const ArenaVector<bool> *usageMask, compiler::Register numNonArgs,
277                                             compiler::Register numLocals)
278 {
279     compiler::Register numTemps = 0;
280     if (numLocals != numNonArgs) {
281         compiler::Register r = numNonArgs - 1L;
282         while (r < numNonArgs && usageMask->at(r)) {
283             ++numTemps;
284             --r;
285         }
286     }
287     return numTemps;
288 }
289 
RenumberArgRegs()290 bool RegEncoder::RenumberArgRegs()
291 {
292     const auto usageMask = GetGraph()->GetUsedRegs<compiler::DataType::INT64>();
293     ASSERT(usageMask->size() == compiler::GetFrameSize());
294 
295     auto frameSize = static_cast<compiler::Register>(usageMask->size());
296     const auto numArgs = GetNumArgsFromGraph();
297     ASSERT(frameSize >= numArgs);
298 
299     auto numNonArgs = static_cast<compiler::Register>(frameSize - numArgs);
300     if (numMaxRangeInput_ > numNonArgs) {
301         LOG(DEBUG, BYTECODE_OPTIMIZER) << "RegEncoder: The free regs for range call are not enough";
302         return false;
303     }
304 
305     auto numLocals = CalculateNumLocals(usageMask, numNonArgs);
306     auto numTemps = CalculateNumTemps(usageMask, numNonArgs, numLocals);
307     if (numLocals + numTemps > numNonArgs - numMaxRangeInput_) {
308         LOG(DEBUG, BYTECODE_OPTIMIZER) << "RegEncoder: The free regs for range call are not enough";
309         return false;
310     }
311 
312     rangeTempsStart_ = numLocals;
313     SaveNumLocalsToGraph(numLocals + numTemps + numMaxRangeInput_);
314 
315     if (numNonArgs == 0U && numMaxRangeInput_ == 0U) {  // all registers are arguments: no need to renumber
316         return true;
317     }
318 
319     // All free regs will be just enough to encode call.range: no need to renumber
320     if (numLocals + numTemps + numMaxRangeInput_ == numNonArgs) {
321         return true;
322     }
323 
324     if (numTemps + numArgs == 0U) {  // no temps and no args: nothing to renumber
325         return true;
326     }
327 
328     const auto minReg = RegsDiff(numNonArgs, numTemps);
329     ASSERT(minReg > MIN_REGISTER_NUMBER);
330 
331     // Assert that if temps are present, they are marked allocated in the mask:
332     for (compiler::Register r = minReg; r < minReg + numTemps; r++) {
333         ASSERT(usageMask->at(r));
334     }
335 
336     // Assert that there are no used regs between locals and temps + arguments:
337     for (compiler::Register r = numLocals; r < minReg; r++) {
338         ASSERT(!usageMask->at(r));
339     }
340 
341     auto delta = RegsDiff(numLocals + numTemps + numMaxRangeInput_, numNonArgs);
342     RenumberRegs(minReg, delta);
343 
344     for (compiler::Register r = minReg; r < frameSize; r++) {
345         usageMask->at(RenumberReg(r, delta)) = usageMask->at(r);
346         usageMask->at(r) = false;
347     }
348     return true;
349 }
350 
InsertSpills()351 void RegEncoder::InsertSpills()
352 {
353     ASSERT(numMaxRangeInput_ > 0U || (numTemps_ > 0U && numTemps_ <= MAX_NUM_INPUTS));
354 
355     for (auto *bb : GetGraph()->GetBlocksRPO()) {
356         for (auto inst : bb->AllInstsSafe()) {
357             if (inst->GetInputsCount() == 0U) {
358                 continue;
359             }
360 
361             VisitInstruction(inst);
362             if (!CheckStatus()) {
363                 return;
364             }
365         }
366     }
367 }
368 
CalculateNumNeededTemps()369 void RegEncoder::CalculateNumNeededTemps()
370 {
371     numTemps_ = 0;
372 
373     for (auto bb : GetGraph()->GetBlocksRPO()) {
374         for (auto inst : bb->AllInstsSafe()) {
375             if (inst->GetInputsCount() == 0U) {
376                 continue;
377             }
378 
379             VisitInstruction(inst);
380             if (!CheckStatus()) {
381                 return;
382             }
383         }
384     }
385 
386     LOG(DEBUG, BYTECODE_OPTIMIZER) << GetGraph()->GetRuntime()->GetMethodFullName(GetGraph()->GetMethod())
387                                    << ": num_temps_ = " << std::to_string(numTemps_);
388 }
389 
390 template <typename T>
AddMoveBefore(Inst * inst,const T & spContainer)391 static void AddMoveBefore(Inst *inst, const T &spContainer)
392 {
393     if (spContainer.empty()) {
394         return;
395     }
396     auto sfInst = inst->GetBasicBlock()->GetGraph()->CreateInstSpillFill();
397     for (auto const &[src, dst] : spContainer) {
398         ASSERT(src != compiler::GetAccReg());
399         sfInst->AddMove(src, dst.reg, GetRegType(dst.type));
400         LOG(DEBUG, BYTECODE_OPTIMIZER) << "RegEncoder: Move v" << static_cast<int>(dst.reg) << " <- v"
401                                        << static_cast<int>(src) << " was added";
402     }
403     inst->GetBasicBlock()->InsertBefore(sfInst, inst);
404 }
405 
IsAccReadPosition(compiler::Inst * inst,size_t pos)406 static bool IsAccReadPosition(compiler::Inst *inst, size_t pos)
407 {
408     // Calls can have accumulator at any position, return false for them
409     if (inst->GetBasicBlock()->GetGraph()->IsAbcKit() && inst->IsIntrinsic()) {
410         return inst->IsAccRead() && pos == AccReadIndex(inst);
411     }
412     return !inst->IsCallOrIntrinsic() && inst->IsAccRead() && pos == AccReadIndex(inst);
413 }
414 
AddMoveAfter(Inst * inst,compiler::Register src,RegContent dst)415 static void AddMoveAfter(Inst *inst, compiler::Register src, RegContent dst)
416 {
417     auto *sfInst = inst->GetBasicBlock()->GetGraph()->CreateInstSpillFill();
418     sfInst->AddMove(src, dst.reg, dst.type);
419     LOG(DEBUG, BYTECODE_OPTIMIZER) << "RegEncoder: Move v" << static_cast<int>(dst.reg) << " <- v"
420                                    << static_cast<int>(src) << " was added";
421     inst->GetBasicBlock()->InsertAfter(sfInst, inst);
422 }
423 
AddMoveBefore(Inst * inst,compiler::Register src,RegContent dst)424 static void AddMoveBefore(Inst *inst, compiler::Register src, RegContent dst)
425 {
426     auto *sfInst = inst->GetBasicBlock()->GetGraph()->CreateInstSpillFill();
427     sfInst->AddMove(src, dst.reg, dst.type);
428     LOG(DEBUG, BYTECODE_OPTIMIZER) << "RegEncoder: Move v" << static_cast<int>(dst.reg) << " <- v"
429                                    << static_cast<int>(src) << " was added";
430     inst->GetBasicBlock()->InsertBefore(sfInst, inst);
431 }
432 
RenumberDstReg(compiler::Inst * inst,size_t temp,size_t rangeTemp,bool largeTemp)433 void RegEncoder::RenumberDstReg(compiler::Inst *inst, size_t temp, size_t rangeTemp, bool largeTemp)
434 {
435     if (!GetGraph()->IsAbcKit() || !IsDstRegNeedRenumbering(inst)) {
436         return;
437     }
438     auto reg = inst->GetDstReg();
439     if (!RegNeedsRenumbering(reg) || reg < NUM_COMPACTLY_ENCODED_REGS) {
440         return;
441     }
442     auto type = GetRegType(inst->GetType());
443     inst->SetDstReg(temp);
444     AddMoveAfter(inst, temp, RegContent(reg, type));
445     if (largeTemp) {
446         AddMoveBefore(inst, temp, RegContent(rangeTemp, type));
447     }
448 }
449 
InsertSpillsForDynRangeInst(compiler::Inst * inst,size_t nargs,size_t start)450 bool RegEncoder::InsertSpillsForDynRangeInst(compiler::Inst *inst, size_t nargs, size_t start)
451 {
452     RegContentVec spillVec(GetGraph()->GetLocalAllocator()->Adapter());  // spill_vec is used to handle callrange
453     compiler::Register temp = rangeTempsStart_;
454     compiler::Register rangeTemp = rangeTempsStart_;
455     bool largeTemp = false;
456     if (temp > compiler::INVALID_REG) {
457         ASSERT(GetGraph()->IsAbcKit());
458         ASSERT(compiler::IsFrameSizeLarge());
459         temp = 0;
460         largeTemp = true;
461     }
462 
463     for (size_t i = start; i < nargs; ++i) {
464         auto srcReg = inst->GetSrcReg(i);
465         auto type = inst->GetInputType(i);
466         // do not spillfill for acc-read position. For example, Intrinsic.FSTARR32
467         if (IsAccReadPosition(inst, i)) {
468             continue;
469         }
470         if (largeTemp) {
471             spillVec.emplace_back(temp, RegContent(rangeTemp, type));
472             AddMoveAfter(inst, rangeTemp, RegContent(temp, type));
473         }
474         spillVec.emplace_back(srcReg, RegContent(temp, type));
475         if (GetGraph()->IsAbcKit() && ((temp >= compiler::GetFrameSize()) || (rangeTemp >= compiler::GetFrameSize()))) {
476             success_ = false;
477             return success_;
478         }
479         inst->SetSrcReg(i, temp);
480 
481         temp++;
482         rangeTemp++;
483     }
484 
485     AddMoveBefore(inst, spillVec);
486 
487     RenumberDstReg(inst, temp, rangeTemp, largeTemp);
488 
489     return success_;
490 }
491 
InsertSpillsForDynInputsInst(compiler::Inst * inst)492 void RegEncoder::InsertSpillsForDynInputsInst(compiler::Inst *inst)
493 {
494     ASSERT(state_ == RegEncoderState::INSERT_SPILLS);
495     ASSERT(inst->IsStaticCall() || inst->IsVirtualCall() || inst->IsInitObject() || inst->IsIntrinsic());
496 
497     RegContentMap spillMap(GetGraph()->GetLocalAllocator()->Adapter());  // src -> (dst, src_type), non-callrange
498 
499     auto nargs = inst->GetInputsCount() - (inst->RequireState() ? 1U : 0U);
500     auto start = GetStartInputIndex(inst);
501     bool range = IsIntrinsicRange(inst) || (nargs - start > MAX_NUM_NON_RANGE_ARGS && CanHoldRange(inst));
502 
503     compiler::Register temp = range ? rangeTempsStart_ : 0U;
504     if (range) {
505         InsertSpillsForDynRangeInst(inst, nargs, start);
506         return;
507     }
508 
509     for (size_t i = start; i < nargs; ++i) {
510         auto srcReg = inst->GetSrcReg(i);
511         auto type = inst->GetInputType(i);
512 
513         // do not spillfill for acc-read position. For example, Intrinsic.FSTARR32
514         if (IsAccReadPosition(inst, i)) {
515             continue;
516         }
517 
518         if (!RegNeedsRenumbering(srcReg) || srcReg < NUM_COMPACTLY_ENCODED_REGS) {
519             continue;
520         }
521 
522         auto res = spillMap.emplace(srcReg, RegContent(temp, type));
523         if (res.second) {
524             inst->SetSrcReg(i, temp++);
525         } else {
526             // Such register is already in map.
527             // It can be ok for cases like: CallStatic v49, v49
528             // Such instructions can be generated by optimizer too.
529             const RegContent &regCont = res.first->second;
530             inst->SetSrcReg(i, regCont.reg);
531         }
532     }
533 
534     AddMoveBefore(inst, spillMap);
535     RenumberDstReg(inst, temp);
536 }
537 
GetStartInputIndex(compiler::Inst * inst)538 size_t RegEncoder::GetStartInputIndex(compiler::Inst *inst)
539 {
540     return inst->GetOpcode() == compiler::Opcode::InitObject ? 1U : 0U;  // exclude LoadAndInitClass and NewObject
541 }
542 
IsBoundDstSrc(const compiler::Inst * inst)543 static bool IsBoundDstSrc(const compiler::Inst *inst)
544 {
545     if (!inst->IsBinaryInst()) {
546         return false;
547     }
548     auto src0 = inst->GetSrcReg(0U);
549     auto src1 = inst->GetSrcReg(1U);
550     auto dst = inst->GetDstReg();
551     if (inst->IsCommutative()) {
552         return src0 == dst || src1 == dst;
553     }
554     return src0 == dst;
555 }
556 
IsMoveAfter(const compiler::Inst * inst)557 static bool IsMoveAfter(const compiler::Inst *inst)
558 {
559     auto writesToDest = inst->GetDstReg() != compiler::GetAccReg();
560     if (inst->IsBinaryImmInst()) {
561         return writesToDest;
562     }
563     if (inst->IsBinaryInst()) {
564         return writesToDest && IsBoundDstSrc(inst);
565     }
566     switch (inst->GetOpcode()) {
567         case compiler::Opcode::LoadObject:
568             // Special case for LoadObject, because it can be register instruction.
569             return writesToDest;
570         case compiler::Opcode::NewArray:
571             return true;
572         default:
573             return false;
574     }
575 }
576 
InsertSpillsForInst(compiler::Inst * inst)577 void RegEncoder::InsertSpillsForInst(compiler::Inst *inst)
578 {
579     ASSERT(state_ == RegEncoderState::INSERT_SPILLS);
580 
581     RegContentMap spillMap(GetGraph()->GetLocalAllocator()->Adapter());  // src -> (dst, src_type)
582 
583     if (inst->IsOperandsDynamic()) {
584         InsertSpillsForDynInputsInst(inst);
585         return;
586     }
587 
588     compiler::Register temp = 0;
589     for (size_t i = 0; i < inst->GetInputsCount(); i++) {
590         // NOTE(mbolshov): make a better solution to skip instructions, that are not relevant to bytecode_opt
591         if (inst->GetInput(i).GetInst()->GetOpcode() == Opcode::LoadAndInitClass) {
592             continue;
593         }
594         auto reg = inst->GetSrcReg(i);
595         if (RegNeedsRenumbering(reg) && reg >= NUM_COMPACTLY_ENCODED_REGS) {
596             auto res = spillMap.emplace(reg, RegContent(temp, GetRegType(inst->GetInputType(i))));
597             if (res.second) {
598                 inst->SetSrcReg(i, temp++);
599             } else {
600                 // Such register is already in map.
601                 // It can be ok for cases like: and v49, v49
602                 // Such instructions can be generated by optimizer too.
603                 const RegContent &regCont = res.first->second;
604                 inst->SetSrcReg(i, regCont.reg);
605             }
606         }
607     }
608 
609     AddMoveBefore(inst, spillMap);
610     if (IsMoveAfter(inst)) {
611         auto reg = inst->GetDstReg();
612         if (RegNeedsRenumbering(reg) && reg >= NUM_COMPACTLY_ENCODED_REGS) {
613             inst->SetDstReg(temp);
614             AddMoveAfter(inst, temp, RegContent(reg, GetRegType(inst->GetType())));
615         }
616     }
617 }
618 
IncTempsIfNeeded(compiler::Inst * inst,const compiler::Register reg,compiler::Register & numTemps,compiler::Register & numChangedWidth)619 static void IncTempsIfNeeded(compiler::Inst *inst, const compiler::Register reg, compiler::Register &numTemps,
620                              compiler::Register &numChangedWidth)
621 {
622     if (RegNeedsRenumbering(reg) && reg >= NUM_COMPACTLY_ENCODED_REGS) {
623         numTemps++;
624         if (inst->IsBinaryInst()) {
625             numChangedWidth++;
626         }
627     }
628 }
629 
CalculateNumNeededTempsForInst(compiler::Inst * inst)630 void RegEncoder::CalculateNumNeededTempsForInst(compiler::Inst *inst)
631 {
632     ASSERT(state_ == RegEncoderState::RESERVE_TEMPS);
633 
634     compiler::Register numTemps = 0;
635     compiler::Register numChangedWidth = 0;
636 
637     if (inst->IsOperandsDynamic()) {
638         if (IsIntrinsicRange(inst)) {
639             return;
640         }
641         ASSERT(inst->IsStaticCall() || inst->IsVirtualCall() || inst->IsInitObject() || inst->IsIntrinsic());
642 
643         auto nargs = inst->GetInputsCount() - (inst->RequireState() ? 1U : 0U);
644         size_t start = inst->GetOpcode() == compiler::Opcode::InitObject ? 1U : 0U;
645         if (nargs - start > MAX_NUM_NON_RANGE_ARGS) {  // is call.range
646             return;
647         }
648 
649         for (size_t i = start; i < nargs; i++) {
650             if (IsAccReadPosition(inst, i)) {
651                 continue;
652             }
653             auto reg = inst->GetSrcReg(i);
654             if (!RegNeedsRenumbering(reg) || reg < NUM_COMPACTLY_ENCODED_REGS) {
655                 continue;
656             }
657             numTemps++;
658             if (inst->IsBinaryInst()) {
659                 numChangedWidth++;
660             }
661         }
662     } else {
663         for (size_t i = 0; i < inst->GetInputsCount(); i++) {
664             // NOTE(mbolshov): make a better solution to skip instructions, that are not relevant to bytecode_opt
665             if (inst->GetInput(i).GetInst()->GetOpcode() == Opcode::LoadAndInitClass) {
666                 continue;
667             }
668             IncTempsIfNeeded(inst, inst->GetSrcReg(i), numTemps, numChangedWidth);
669         }
670 
671         if (IsMoveAfter(inst) && !IsBoundDstSrc(inst)) {
672             IncTempsIfNeeded(inst, inst->GetDstReg(), numTemps, numChangedWidth);
673         }
674     }
675 
676     ASSERT(numTemps <= MAX_NUM_INPUTS);
677 
678     numTemps_ = std::max(numTemps, numTemps_);
679     numChangedWidth_ = std::max(numChangedWidth, numChangedWidth_);
680 }
681 
Check4Width(compiler::Inst * inst)682 void RegEncoder::Check4Width(compiler::Inst *inst)
683 {
684     switch (state_) {
685         case RegEncoderState::RESERVE_TEMPS: {
686             CalculateNumNeededTempsForInst(inst);
687             break;
688         }
689         case RegEncoderState::INSERT_SPILLS: {
690             InsertSpillsForInst(inst);
691             break;
692         }
693         default:
694             UNREACHABLE();
695     }
696 }
697 
Check8Width(compiler::Inst * inst)698 void RegEncoder::Check8Width([[maybe_unused]] compiler::Inst *inst)
699 {
700     // NOTE(aantipina): implement after it became possible to use register numbers more than 256 (#2697)
701 }
702 
VisitCallStatic(GraphVisitor * visitor,Inst * inst)703 void RegEncoder::VisitCallStatic(GraphVisitor *visitor, Inst *inst)
704 {
705     CallHelper(visitor, inst);
706 }
707 
VisitCallVirtual(GraphVisitor * visitor,Inst * inst)708 void RegEncoder::VisitCallVirtual(GraphVisitor *visitor, Inst *inst)
709 {
710     CallHelper(visitor, inst);
711 }
712 
VisitInitObject(GraphVisitor * visitor,Inst * inst)713 void RegEncoder::VisitInitObject(GraphVisitor *visitor, Inst *inst)
714 {
715     CallHelper(visitor, inst);
716 }
717 
VisitIntrinsic(GraphVisitor * visitor,Inst * inst)718 void RegEncoder::VisitIntrinsic(GraphVisitor *visitor, Inst *inst)
719 {
720     if (inst->GetBasicBlock()->GetGraph()->IsAbcKit() && inst->IsIntrinsic()) {
721         auto re = static_cast<RegEncoder *>(visitor);
722         if (IsIntrinsicRange(inst)) {
723             re->Check4Width(inst);
724             return;
725         }
726         if (IsAbcKitIntrinsic(inst->CastToIntrinsic()->GetIntrinsicId())) {
727             CheckWidthAbcKitIntrinsic(re, inst);
728         }
729         CallHelper(visitor, inst);
730         return;
731     }
732     if (inst->IsCallOrIntrinsic()) {
733         CallHelper(visitor, inst);
734         return;
735     }
736     auto re = static_cast<RegEncoder *>(visitor);
737     if (IsIntrinsicRange(inst)) {
738         re->Check4Width(inst);
739         return;
740     }
741 
742     re->Check8Width(inst);
743 }
744 
VisitLoadObject(GraphVisitor * v,Inst * instBase)745 void RegEncoder::VisitLoadObject(GraphVisitor *v, Inst *instBase)
746 {
747     bool isAccType = instBase->GetDstReg() == compiler::GetAccReg();
748 
749     auto re = static_cast<RegEncoder *>(v);
750     auto inst = instBase->CastToLoadObject();
751     switch (inst->GetType()) {
752         case compiler::DataType::BOOL:
753         case compiler::DataType::UINT8:
754         case compiler::DataType::INT8:
755         case compiler::DataType::UINT16:
756         case compiler::DataType::INT16:
757         case compiler::DataType::UINT32:
758         case compiler::DataType::INT32:
759         case compiler::DataType::INT64:
760         case compiler::DataType::UINT64:
761         case compiler::DataType::FLOAT32:
762         case compiler::DataType::FLOAT64:
763         case compiler::DataType::REFERENCE:
764             if (isAccType) {
765                 re->Check8Width(inst);
766             } else {
767                 re->Check4Width(inst);
768             }
769             break;
770         default:
771             LOG(ERROR, BYTECODE_OPTIMIZER)
772                 << "Wrong DataType for " << compiler::GetOpcodeString(inst->GetOpcode()) << " failed";
773             re->success_ = false;
774     }
775 }
776 
VisitLoadStatic(GraphVisitor * v,Inst * instBase)777 void RegEncoder::VisitLoadStatic(GraphVisitor *v, Inst *instBase)
778 {
779     auto re = static_cast<RegEncoder *>(v);
780     auto inst = instBase->CastToLoadStatic();
781 
782     switch (inst->GetType()) {
783         case compiler::DataType::BOOL:
784         case compiler::DataType::UINT8:
785         case compiler::DataType::INT8:
786         case compiler::DataType::UINT16:
787         case compiler::DataType::INT16:
788         case compiler::DataType::UINT32:
789         case compiler::DataType::INT32:
790         case compiler::DataType::INT64:
791         case compiler::DataType::UINT64:
792         case compiler::DataType::FLOAT32:
793         case compiler::DataType::FLOAT64:
794         case compiler::DataType::REFERENCE:
795             return;
796         default:
797             LOG(ERROR, BYTECODE_OPTIMIZER)
798                 << "Wrong DataType for " << compiler::GetOpcodeString(inst->GetOpcode()) << " failed";
799             re->success_ = false;
800     }
801 }
802 
VisitStoreObject(GraphVisitor * v,Inst * instBase)803 void RegEncoder::VisitStoreObject(GraphVisitor *v, Inst *instBase)
804 {
805     bool isAccType = instBase->GetSrcReg(1U) == compiler::GetAccReg();
806 
807     auto re = static_cast<RegEncoder *>(v);
808     auto inst = instBase->CastToStoreObject();
809     switch (inst->GetType()) {
810         case compiler::DataType::BOOL:
811         case compiler::DataType::UINT8:
812         case compiler::DataType::INT8:
813         case compiler::DataType::UINT16:
814         case compiler::DataType::INT16:
815         case compiler::DataType::UINT32:
816         case compiler::DataType::INT32:
817         case compiler::DataType::INT64:
818         case compiler::DataType::UINT64:
819         case compiler::DataType::FLOAT32:
820         case compiler::DataType::FLOAT64:
821         case compiler::DataType::REFERENCE:
822             if (isAccType) {
823                 re->Check8Width(inst);
824             } else {
825                 re->Check4Width(inst);
826             }
827             break;
828         default:
829             LOG(ERROR, BYTECODE_OPTIMIZER)
830                 << "Wrong DataType for " << compiler::GetOpcodeString(inst->GetOpcode()) << " failed";
831             re->success_ = false;
832     }
833 }
834 
VisitStoreStatic(GraphVisitor * v,Inst * instBase)835 void RegEncoder::VisitStoreStatic(GraphVisitor *v, Inst *instBase)
836 {
837     auto re = static_cast<RegEncoder *>(v);
838     auto inst = instBase->CastToStoreStatic();
839 
840     switch (inst->GetType()) {
841         case compiler::DataType::BOOL:
842         case compiler::DataType::UINT8:
843         case compiler::DataType::INT8:
844         case compiler::DataType::UINT16:
845         case compiler::DataType::INT16:
846         case compiler::DataType::UINT32:
847         case compiler::DataType::INT32:
848         case compiler::DataType::INT64:
849         case compiler::DataType::UINT64:
850         case compiler::DataType::FLOAT32:
851         case compiler::DataType::FLOAT64:
852         case compiler::DataType::REFERENCE:
853             return;
854         default:
855             LOG(ERROR, BYTECODE_OPTIMIZER)
856                 << "Wrong DataType for " << compiler::GetOpcodeString(inst->GetOpcode()) << " failed";
857             re->success_ = false;
858     }
859 }
VisitSpillFill(GraphVisitor * v,Inst * inst)860 void RegEncoder::VisitSpillFill([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst) {}
VisitConstant(GraphVisitor * v,Inst * inst)861 void RegEncoder::VisitConstant([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst) {}
VisitLoadString(GraphVisitor * v,Inst * inst)862 void RegEncoder::VisitLoadString([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst) {}
VisitReturn(GraphVisitor * v,Inst * inst)863 void RegEncoder::VisitReturn([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst) {}
VisitCatchPhi(GraphVisitor * v,Inst * inst)864 void RegEncoder::VisitCatchPhi([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst) {}
VisitCastValueToAnyType(GraphVisitor * v,Inst * inst)865 void RegEncoder::VisitCastValueToAnyType([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst) {}
866 
867 #include "generated/check_width.cpp"
868 }  // namespace ark::bytecodeopt
869