1 /*
2  * Copyright (c) 2021-2022 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 "regAllocator.h"
17 
18 #include <compiler/core/pandagen.h>
19 
20 #include <algorithm>
21 
22 namespace panda::es2panda::compiler {
23 
24 // FrontAllocator
25 
FrontAllocator(PandaGen *pg)26 FrontAllocator::FrontAllocator(PandaGen *pg)
27     : pg_(pg), insn_(std::move(pg_->Insns()), pg_->Allocator()->Adapter())
28 {
29 }
30 
~FrontAllocator()31 FrontAllocator::~FrontAllocator()
32 {
33     pg_->Insns().splice(pg_->Insns().end(), std::move(insn_));
34 }
35 
36 // RegAllocator
37 
PushBack(IRNode *ins)38 void RegAllocator::PushBack(IRNode *ins)
39 {
40     pg_->Insns().push_back(ins);
41 }
42 
Allocator() const43 ArenaAllocator *RegAllocator::Allocator() const
44 {
45     return pg_->Allocator();
46 }
47 
HasSpill() const48 bool RegAllocator::HasSpill() const
49 {
50     return hasSpill_;
51 }
52 
GetSpillRegsCount() const53 uint16_t RegAllocator::GetSpillRegsCount() const
54 {
55     return spillRegs_;
56 }
57 
UpdateIcSlot(IRNode *node)58 void RegAllocator::UpdateIcSlot(IRNode *node)
59 {
60     CHECK_NOT_NULL(node);
61     auto inc = node->SetIcSlot(pg_->GetCurrentSlot());
62     constexpr static ICSlot invalid = 0xFF;
63     if (node->InlineCacheEnabled() && (node->GetIcSlot() == invalid) && !pg_->IsIcOverFlow()) {
64         pg_->SetIcOverFlow();
65     }
66     pg_->IncreaseCurrentSlot(inc);
67 }
68 
AllocLabel(std::string &&id)69 Label *RegAllocator::AllocLabel(std::string &&id)
70 {
71     const auto *lastInsNode = pg_->Insns().empty() ? FIRST_NODE_OF_FUNCTION : pg_->Insns().back()->Node();
72     return Alloc<Label>(lastInsNode, std::move(id));
73 }
74 
Run(IRNode *ins)75 void RegAllocator::Run(IRNode *ins)
76 {
77     CHECK_NOT_NULL(ins);
78     std::array<VReg *, IRNode::MAX_REG_OPERAND> regs {};
79     auto regCnt = ins->Registers(&regs);
80     if (regCnt == 0) {
81         return PushBack(ins);
82     }
83 
84     auto registers = Span<VReg *>(regs.data(), regs.data() + regCnt);
85     spillRegs_ = std::max(spillRegs_, static_cast<uint16_t>(regCnt));
86 
87     if (!CheckRegIndices(ins, registers)) {
88         hasSpill_ = true;
89     }
90 
91     PushBack(ins);
92 }
93 
Run(IRNode *ins, size_t argCount)94 void RegAllocator::Run(IRNode *ins, size_t argCount)
95 {
96     CHECK_NOT_NULL(ins);
97     std::array<VReg *, IRNode::MAX_REG_OPERAND> regs {};
98     auto regCnt = ins->Registers(&regs);
99     ASSERT(regCnt != 0);
100     auto registers = Span<VReg *>(regs.data(), regs.data() + regCnt);
101     spillRegs_ = std::max(spillRegs_, static_cast<uint16_t>(argCount + regCnt - 1));
102 
103     if (!CheckRegIndices(ins, registers)) {
104         hasSpill_ = true;
105     }
106 
107     PushBack(ins);
108 }
109 
AdjustInsRegWhenHasSpill()110 void RegAllocator::AdjustInsRegWhenHasSpill()
111 {
112     if (!hasSpill_) {
113         spillRegs_ = 0;
114         return;
115     }
116 
117     if ((spillRegs_ + pg_->TotalRegsNum()) > UINT16_MAX) {
118         throw Error(ErrorType::GENERIC, "Can't adjust spill insns when regs run out");
119     }
120 
121     ArenaList<IRNode *> newInsns(Allocator()->Adapter());
122     auto &insns = pg_->Insns();
123     for (auto it = insns.begin(); it != insns.end(); ++it) {
124         IRNode *ins = *it;
125         std::vector<OperandKind> regsKind;
126         std::array<VReg *, IRNode::MAX_REG_OPERAND> regs {};
127         auto regCnt = ins->Registers(&regs);
128         if (regCnt == 0) {
129             newInsns.push_back(ins);
130             continue;
131         }
132 
133         auto registers = Span<VReg *>(regs.data(), regs.data() + regCnt);
134         for (auto *reg : registers) {
135             *reg = *reg + spillRegs_;
136         }
137 
138         if (CheckRegIndices(ins, registers, &regsKind)) {
139             // current ins has no spill, continue iterating
140             newInsns.push_back(ins);
141             continue;
142         }
143 
144         // current ins has spill
145         if (ins->IsRangeInst()) {
146             AdjustRangeInsSpill(registers, ins, newInsns);
147             continue;
148         }
149 
150         AdjustInsSpill(registers, ins, newInsns, regsKind);
151     }
152     pg_->SetInsns(newInsns);
153 }
154 
AdjustInsSpill(const Span<VReg *> &registers, IRNode *ins, ArenaList<IRNode *> &newInsns, const std::vector<OperandKind> &regsKind)155 void RegAllocator::AdjustInsSpill(const Span<VReg *> &registers, IRNode *ins, ArenaList<IRNode *> &newInsns,
156                                   const std::vector<OperandKind> &regsKind)
157 {
158     ASSERT(spillIndex_ == 0);
159     ASSERT(!regsKind.empty());
160     int idx = 0;
161     for (auto *reg : registers) {
162         if (IsRegisterCorrect(reg)) {
163             idx++;
164             continue;
165         }
166 
167         const auto originReg = *reg;
168         VReg spillReg = spillIndex_;
169         if (regsKind[idx] == OperandKind::SRC_VREG || regsKind[idx] == OperandKind::SRC_DST_VREG) {
170             Add<Mov>(newInsns, ins->Node(), spillReg, originReg);
171         }
172         if (regsKind[idx] == OperandKind::DST_VREG || regsKind[idx] == OperandKind::SRC_DST_VREG) {
173             dstRegSpills_.push_back(std::make_pair(originReg, spillReg));
174         }
175         *reg = spillIndex_++;
176         idx++;
177     }
178 
179     newInsns.push_back(ins);
180 
181     for (auto spillPair : dstRegSpills_) {
182         Add<Mov>(newInsns, ins->Node(), spillPair.first, spillPair.second);
183     }
184 
185     FreeSpill();
186     dstRegSpills_.clear();
187 }
188 
AdjustRangeInsSpill(Span<VReg *> &registers, IRNode *ins, ArenaList<IRNode *> &newInsns)189 void RegAllocator::AdjustRangeInsSpill(Span<VReg *> &registers, IRNode *ins, ArenaList<IRNode *> &newInsns)
190 {
191     ASSERT(spillIndex_ == 0);
192     ASSERT(ins->IsRangeInst());
193     auto rangeRegCount = ins->RangeRegsCount();
194     auto *iter = registers.begin();
195     auto *rangeStartIter = iter + registers.size() - 1;
196 
197     while (iter != rangeStartIter) {
198         VReg *reg = *iter;
199         Add<Mov>(newInsns, ins->Node(), spillIndex_, *reg);
200         *reg = spillIndex_++;
201         iter++;
202     }
203 
204     VReg *rangeStartReg = *rangeStartIter;
205     auto originReg = *rangeStartReg;
206     *rangeStartReg = spillIndex_;
207 
208     while (rangeRegCount--) {
209         Add<Mov>(newInsns, ins->Node(), spillIndex_++, originReg++);
210     }
211 
212     newInsns.push_back(ins);
213     FreeSpill();
214 }
215 
216 }  // namespace panda::es2panda::compiler
217