1 /*
2 * Copyright (c) 2021-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 "regAllocator.h"
17
18 #include "compiler/core/codeGen.h"
19 #include "checker/types/type.h"
20
21 #include <algorithm>
22 #include <vector>
23
24 namespace ark::es2panda::compiler {
25 // AllocatorBase
26
AllocatorBase(CodeGen * const cg)27 AllocatorBase::AllocatorBase(CodeGen *const cg) noexcept : cg_(cg) {}
28
PushBack(IRNode * const ins) const29 void AllocatorBase::PushBack(IRNode *const ins) const
30 {
31 cg_->Insns().push_back(ins);
32 }
33
GetCodeGen()34 CodeGen &AllocatorBase::GetCodeGen() noexcept
35 {
36 return *cg_;
37 }
38
GetCodeGen() const39 const CodeGen &AllocatorBase::GetCodeGen() const noexcept
40 {
41 return *cg_;
42 }
43
Allocator()44 ArenaAllocator &AllocatorBase::Allocator() noexcept
45 {
46 return *cg_->Allocator();
47 }
48
Allocator() const49 const ArenaAllocator &AllocatorBase::Allocator() const noexcept
50 {
51 return *cg_->Allocator();
52 }
53
54 // SimpleAllocator
55
SimpleAllocator(CodeGen * const cg)56 SimpleAllocator::SimpleAllocator(CodeGen *const cg) noexcept : AllocatorBase(cg) {}
57
AllocLabel(std::string && id)58 Label *SimpleAllocator::AllocLabel(std::string &&id)
59 {
60 const auto *lastInsNode =
61 GetCodeGen().Insns().empty() ? FIRST_NODE_OF_FUNCTION : GetCodeGen().Insns().back()->Node();
62 return Alloc<Label>(lastInsNode, std::move(id));
63 }
64
AddLabel(Label * const label) const65 void SimpleAllocator::AddLabel(Label *const label) const
66 {
67 PushBack(label);
68 }
69
70 // RegAllocatorBase
71
RegAllocatorBase(CodeGen * const cg,RegSpiller * const spiller)72 RegAllocatorBase::RegAllocatorBase(CodeGen *const cg, RegSpiller *const spiller) noexcept
73 : AllocatorBase(cg), spiller_(spiller)
74 {
75 }
76
Spiller()77 RegSpiller &RegAllocatorBase::Spiller() noexcept
78 {
79 return *spiller_;
80 }
81
Spiller() const82 const RegSpiller &RegAllocatorBase::Spiller() const noexcept
83 {
84 return *spiller_;
85 }
86
RegIndicesValid(const IRNode * const ins,const Span<VReg * > & registers)87 std::pair<bool, std::size_t> RegAllocatorBase::RegIndicesValid(const IRNode *const ins, const Span<VReg *> ®isters)
88 {
89 const auto &formats = ins->GetFormats();
90 std::size_t limit = 0;
91
92 for (const auto &format : formats) {
93 for (const auto &formatItem : format.GetFormatItem()) {
94 if (formatItem.IsVReg()) {
95 limit = 1U << formatItem.BitWidth();
96 break;
97 }
98 }
99
100 if (std::all_of(registers.begin(), registers.end(),
101 [limit](const VReg *const reg) { return reg->IsValid(limit); })) {
102 return {true, limit};
103 }
104 }
105
106 return {false, limit};
107 }
108
Spill(IRNode * const ins,const VReg reg) const109 VReg RegAllocatorBase::Spill(IRNode *const ins, const VReg reg) const
110 {
111 const auto [spill_info, origin_type] = spiller_->New();
112
113 if (origin_type != nullptr) {
114 if (auto *const mov = spiller_->MoveReg(ins->Node(), spill_info.SpillReg(), spill_info.OriginReg(), true);
115 mov != nullptr) {
116 PushBack(mov);
117 }
118 }
119
120 if (auto *const mov = spiller_->MoveReg(ins->Node(), spill_info.OriginReg(), reg, false); mov != nullptr) {
121 PushBack(mov);
122 spiller_->GetCodeGen()->SetVRegType(spill_info.OriginReg(), origin_type);
123 }
124
125 return spill_info.OriginReg();
126 }
127
Restore(const IRNode * const ins) const128 void RegAllocatorBase::Restore(const IRNode *const ins) const
129 {
130 const auto spillInfo = spiller_->Restore();
131 if (spiller_->GetCodeGen()->GetVRegType(spillInfo.OriginReg()) == nullptr) {
132 return;
133 }
134
135 if (auto *const mov = spiller_->MoveReg(ins->Node(), spillInfo.OriginReg(), spillInfo.SpillReg(), false);
136 mov != nullptr) {
137 PushBack(mov);
138 }
139 }
140
141 // RegAllocator
142
RegAllocator(CodeGen * const cg,RegSpiller * const spiller)143 RegAllocator::RegAllocator(CodeGen *const cg, RegSpiller *const spiller) noexcept : RegAllocatorBase(cg, spiller) {}
144
Run(IRNode * const ins,const int32_t spillMax)145 void RegAllocator::Run(IRNode *const ins, const int32_t spillMax)
146 {
147 ES2PANDA_ASSERT(Spiller().Restored());
148 std::array<VReg *, IRNode::MAX_REG_OPERAND> regs {};
149 ES2PANDA_ASSERT(ins != nullptr);
150 const auto regCnt = ins->Registers(®s);
151 const auto registers =
152 Span<VReg *>(regs.data(), regs.data() + (spillMax == std::numeric_limits<int32_t>::max() ? regCnt : spillMax));
153
154 std::array<OutVReg, IRNode::MAX_REG_OPERAND> dstRegs {};
155 ins->OutRegisters(&dstRegs);
156
157 const auto [indices_valid, limit] = RegIndicesValid(ins, registers);
158 if (indices_valid) {
159 PushBack(ins);
160 return;
161 }
162
163 const auto rs = Spiller().Start(GetCodeGen());
164
165 std::unordered_set<VReg> validRegs;
166 for (auto *const reg : registers) {
167 if (!reg->IsValid(limit)) {
168 continue;
169 }
170
171 validRegs.insert(*reg);
172 }
173
174 std::vector<IRNode *> dstMoves;
175 size_t i = 0;
176 for (auto *const reg : registers) {
177 auto dstInfo = dstRegs[i++];
178 if (reg->IsValid(limit)) {
179 continue;
180 }
181
182 Spiller().Adjust(validRegs);
183
184 auto r = Spill(ins, *reg);
185
186 if (dstInfo.reg != nullptr) {
187 dstMoves.push_back(GetCodeGen().AllocMov(ins->Node(), dstInfo, r));
188 }
189
190 *reg = r;
191 }
192
193 PushBack(ins);
194
195 for (auto *mov : dstMoves) {
196 PushBack(mov);
197 }
198
199 while (!Spiller().Restored()) {
200 Restore(ins);
201 }
202
203 Spiller().Finalize();
204 }
205
206 // RangeRegAllocator
207
RangeRegAllocator(CodeGen * const cg,RegSpiller * const spiller)208 RangeRegAllocator::RangeRegAllocator(CodeGen *const cg, RegSpiller *const spiller) noexcept
209 : RegAllocatorBase(cg, spiller)
210 {
211 }
212
Run(IRNode * const ins,VReg rangeStart,const std::size_t argCount)213 void RangeRegAllocator::Run(IRNode *const ins, VReg rangeStart, const std::size_t argCount)
214 {
215 ES2PANDA_ASSERT(Spiller().Restored());
216 const auto rangeEnd = rangeStart + argCount;
217
218 std::array<VReg *, IRNode::MAX_REG_OPERAND> regs {};
219 ES2PANDA_ASSERT(ins != nullptr);
220 const auto regCnt = ins->Registers(®s);
221 const auto registers = Span<VReg *>(regs.data(), regs.data() + regCnt);
222 if (RegIndicesValid(ins, registers).first) {
223 PushBack(ins);
224 return;
225 }
226
227 const auto rs = Spiller().Start(GetCodeGen());
228
229 auto regIter = registers.begin();
230 const auto regIterEnd = regIter + registers.size() - 1; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
231
232 while (regIter != regIterEnd) {
233 auto *const reg = *regIter;
234
235 *reg = Spill(ins, *reg);
236 regIter++; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
237 }
238
239 auto *const regStartReg = *regIter;
240 auto reg = rangeStart++;
241 *regStartReg = Spill(ins, reg);
242
243 while (rangeStart != rangeEnd) {
244 reg = rangeStart++;
245 Spill(ins, reg);
246 }
247
248 PushBack(ins);
249
250 while (!Spiller().Restored()) {
251 Restore(ins);
252 }
253
254 Spiller().Finalize();
255 }
256 } // namespace ark::es2panda::compiler
257