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