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 ®Cont = 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 ®Cont = 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