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