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 "locations_builder.h"
17 #include "compiler/optimizer/code_generator/callconv.h"
18 #include "compiler/optimizer/ir/graph.h"
19 #include "compiler/optimizer/ir/locations.h"
20
21 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
22 #define LOCATIONS_BUILDER(type) \
23 template <Arch arch> \
24 type LocationsBuilder<arch>
25
26 namespace ark::compiler {
RunLocationsBuilder(Graph * graph)27 bool RunLocationsBuilder(Graph *graph)
28 {
29 if (graph->GetCallingConvention() == nullptr) {
30 return true;
31 }
32 auto pinfo = graph->GetCallingConvention()->GetParameterInfo(0);
33 switch (graph->GetArch()) {
34 case Arch::X86_64: {
35 return graph->RunPass<LocationsBuilder<Arch::X86_64>>(pinfo);
36 }
37 case Arch::AARCH64: {
38 return graph->RunPass<LocationsBuilder<Arch::AARCH64>>(pinfo);
39 }
40 case Arch::AARCH32: {
41 return graph->RunPass<LocationsBuilder<Arch::AARCH32>>(pinfo);
42 }
43 default:
44 break;
45 }
46 LOG(ERROR, COMPILER) << "Unknown target architecture";
47 return false;
48 }
49
LOCATIONS_BUILDER()50 LOCATIONS_BUILDER()::LocationsBuilder(Graph *graph, ParameterInfo *pinfo) : Optimization(graph), paramsInfo_(pinfo)
51 {
52 if (graph->SupportManagedCode()) {
53 /* We skip first parameter location in managed mode, because calling convention for Panda methods requires
54 pointer to the called method as a first parameter. So we reserve one native parameter for it. */
55 GetParameterInfo()->GetNextLocation(DataType::POINTER);
56 }
57 }
58
LOCATIONS_BUILDER(const ArenaVector<BasicBlock * > &)59 LOCATIONS_BUILDER(const ArenaVector<BasicBlock *> &)::GetBlocksToVisit() const
60 {
61 return GetGraph()->GetBlocksRPO();
62 }
63
LOCATIONS_BUILDER(bool)64 LOCATIONS_BUILDER(bool)::RunImpl()
65 {
66 VisitGraph();
67 return true;
68 }
69
LOCATIONS_BUILDER(void)70 LOCATIONS_BUILDER(void)::ProcessManagedCall(Inst *inst, ParameterInfo *pinfo)
71 {
72 ArenaAllocator *allocator = GetGraph()->GetAllocator();
73 LocationsInfo *locations = allocator->New<LocationsInfo>(allocator, inst);
74
75 if (pinfo == nullptr) {
76 pinfo = GetResetParameterInfo();
77 if (inst->GetOpcode() != Opcode::CallResolvedVirtual && inst->GetOpcode() != Opcode::CallResolvedStatic) {
78 pinfo->GetNextLocation(GetWordType());
79 }
80 }
81
82 size_t inputsCount = inst->GetInputsCount() - (inst->RequireState() ? 1 : 0);
83 size_t stackArgs = 0;
84 for (size_t i = 0; i < inputsCount; i++) {
85 ASSERT(inst->GetInputType(i) != DataType::NO_TYPE);
86 if (i == 0U && inst->GetOpcode() == Opcode::CallNative) {
87 // NOTE: this is a hack for workarounding regalloc bug (i.e. this input can get some parameter register
88 // location by mistake)!
89 ASSERT(locations != nullptr);
90 locations->SetLocation(0U, Location::MakeRegister(GetFirstCalleeReg(GetGraph()->GetArch(), false)));
91 continue;
92 }
93 auto param = pinfo->GetNextLocation(inst->GetInputType(i));
94 if (i == 0 && inst->IsIntrinsic() && inst->CastToIntrinsic()->HasIdInput()) {
95 param = Location::MakeRegister(GetTarget().GetParamRegId(0)); // place id input before imms
96 }
97 locations->SetLocation(i, param);
98 if (param.IsStackArgument()) {
99 stackArgs++;
100 }
101 }
102 if (inst->IsIntrinsic() && stackArgs > 0) {
103 inst->CastToIntrinsic()->SetArgumentsOnStack();
104 }
105 if (!inst->NoDest()) {
106 locations->SetDstLocation(GetLocationForReturn(inst));
107 }
108
109 GetGraph()->UpdateStackSlotsCount(stackArgs);
110 }
111
LOCATIONS_BUILDER(void)112 LOCATIONS_BUILDER(void)::ProcessManagedCallStackRange(Inst *inst, size_t rangeStart, ParameterInfo *pinfo)
113 {
114 ArenaAllocator *allocator = GetGraph()->GetAllocator();
115 LocationsInfo *locations = allocator->New<LocationsInfo>(allocator, inst);
116
117 /* Reserve first parameter for the callee method. It will be set by codegen. */
118 if (pinfo == nullptr) {
119 pinfo = GetResetParameterInfo();
120 pinfo->GetNextLocation(GetWordType());
121 }
122
123 size_t inputsCount = inst->GetInputsCount() - (inst->RequireState() ? 1 : 0);
124 size_t stackArgs = 0;
125 ASSERT(inputsCount >= rangeStart);
126 for (size_t i = 0; i < rangeStart; i++) {
127 ASSERT(inst->GetInputType(i) != DataType::NO_TYPE);
128 auto param = pinfo->GetNextLocation(inst->GetInputType(i));
129 ASSERT(locations != nullptr);
130 locations->SetLocation(i, param);
131 if (param.IsStackArgument()) {
132 stackArgs++;
133 }
134 }
135 if (inst->IsIntrinsic() && stackArgs > 0) {
136 inst->CastToIntrinsic()->SetArgumentsOnStack();
137 }
138 for (size_t i = rangeStart; i < inputsCount; i++) {
139 locations->SetLocation(i, Location::MakeStackArgument(stackArgs++));
140 }
141 locations->SetDstLocation(GetLocationForReturn(inst));
142 GetGraph()->UpdateStackSlotsCount(stackArgs);
143 }
144
LOCATIONS_BUILDER(void)145 LOCATIONS_BUILDER(void)::VisitResolveStatic([[maybe_unused]] GraphVisitor *visitor, Inst *inst)
146 {
147 inst->CastToResolveStatic()->SetDstLocation(GetLocationForReturn(inst));
148 }
149
LOCATIONS_BUILDER(void)150 LOCATIONS_BUILDER(void)::VisitCallResolvedStatic(GraphVisitor *visitor, Inst *inst)
151 {
152 if (inst->CastToCallResolvedStatic()->IsInlined()) {
153 return;
154 }
155 static_cast<LocationsBuilder *>(visitor)->ProcessManagedCall(inst);
156 }
157
LOCATIONS_BUILDER(void)158 LOCATIONS_BUILDER(void)::VisitResolveVirtual([[maybe_unused]] GraphVisitor *visitor, Inst *inst)
159 {
160 inst->CastToResolveVirtual()->SetDstLocation(GetLocationForReturn(inst));
161 }
162
LOCATIONS_BUILDER(void)163 LOCATIONS_BUILDER(void)::VisitCallResolvedVirtual(GraphVisitor *visitor, Inst *inst)
164 {
165 if (inst->CastToCallResolvedVirtual()->IsInlined()) {
166 return;
167 }
168 static_cast<LocationsBuilder *>(visitor)->ProcessManagedCall(inst);
169 }
170
LOCATIONS_BUILDER(void)171 LOCATIONS_BUILDER(void)::VisitCallStatic(GraphVisitor *visitor, Inst *inst)
172 {
173 if (inst->CastToCallStatic()->IsInlined()) {
174 return;
175 }
176 static_cast<LocationsBuilder *>(visitor)->ProcessManagedCall(inst);
177 }
178
LOCATIONS_BUILDER(void)179 LOCATIONS_BUILDER(void)::VisitCallVirtual(GraphVisitor *visitor, Inst *inst)
180 {
181 if (inst->CastToCallVirtual()->IsInlined()) {
182 return;
183 }
184 static_cast<LocationsBuilder *>(visitor)->ProcessManagedCall(inst);
185 }
186
LOCATIONS_BUILDER(void)187 LOCATIONS_BUILDER(void)::VisitCallDynamic(GraphVisitor *visitor, Inst *inst)
188 {
189 ArenaAllocator *allocator = static_cast<LocationsBuilder *>(visitor)->GetGraph()->GetAllocator();
190 LocationsInfo *locations = allocator->New<LocationsInfo>(allocator, inst);
191
192 if (inst->CastToCallDynamic()->IsInlined()) {
193 ASSERT(inst->GetInputType(CallConvDynInfo::REG_METHOD) == DataType::ANY);
194 auto param = Location::MakeRegister(GetTarget().GetParamRegId(CallConvDynInfo::REG_METHOD));
195 locations->SetLocation(CallConvDynInfo::REG_METHOD, param);
196 return;
197 }
198
199 auto pinfo = static_cast<LocationsBuilder *>(visitor)->GetResetParameterInfo();
200 for (uint8_t i = 0; i < CallConvDynInfo::REG_COUNT; i++) {
201 [[maybe_unused]] Location loc = pinfo->GetNextLocation(GetWordType());
202 ASSERT(loc.IsRegister());
203 }
204 size_t inputsCount = inst->GetInputsCount() - (inst->RequireState() ? 1 : 0);
205
206 for (size_t i = 0; i < inputsCount; ++i) {
207 ASSERT(inst->GetInputType(i) == DataType::ANY);
208 auto param = Location::MakeStackArgument(i + CallConvDynInfo::FIXED_SLOT_COUNT);
209 locations->SetLocation(i, param);
210 }
211 locations->SetDstLocation(GetLocationForReturn(inst));
212 static_cast<LocationsBuilder *>(visitor)->GetGraph()->UpdateStackSlotsCount(inputsCount);
213 }
214
LOCATIONS_BUILDER(void)215 LOCATIONS_BUILDER(void)::VisitCallIndirect(GraphVisitor *visitor, Inst *inst)
216 {
217 ArenaAllocator *allocator = static_cast<LocationsBuilder *>(visitor)->GetGraph()->GetAllocator();
218 LocationsInfo *locations = allocator->New<LocationsInfo>(allocator, inst);
219 auto params = static_cast<LocationsBuilder *>(visitor)->GetResetParameterInfo();
220
221 /* First input is an address of the memory to call. So it hasn't exact location and maybe be any register */
222 ASSERT(locations != nullptr);
223 locations->SetLocation(0, Location::RequireRegister());
224
225 /* Inputs, starting from 1, are the native call arguments */
226 for (size_t i = 1; i < inst->GetInputsCount(); i++) {
227 auto param = params->GetNextLocation(inst->GetInputType(i));
228 locations->SetLocation(i, param);
229 }
230 if (!inst->NoDest()) {
231 locations->SetDstLocation(GetLocationForReturn(inst));
232 }
233 }
234
LOCATIONS_BUILDER(void)235 LOCATIONS_BUILDER(void)::VisitCall(GraphVisitor *visitor, Inst *inst)
236 {
237 ArenaAllocator *allocator = static_cast<LocationsBuilder *>(visitor)->GetGraph()->GetAllocator();
238 LocationsInfo *locations = allocator->New<LocationsInfo>(allocator, inst);
239 auto params = static_cast<LocationsBuilder *>(visitor)->GetResetParameterInfo();
240
241 for (size_t i = 0; i < inst->GetInputsCount(); i++) {
242 auto param = params->GetNextLocation(inst->GetInputType(i));
243 ASSERT(locations != nullptr);
244 locations->SetLocation(i, param);
245 }
246 if (!inst->NoDest()) {
247 locations->SetDstLocation(GetLocationForReturn(inst));
248 }
249 }
250
LOCATIONS_BUILDER(void)251 LOCATIONS_BUILDER(void)::VisitIntrinsic(GraphVisitor *visitor, Inst *inst)
252 {
253 auto graph = static_cast<LocationsBuilder *>(visitor)->GetGraph();
254 auto intrinsic = inst->CastToIntrinsic();
255 auto id = intrinsic->GetIntrinsicId();
256 if (intrinsic->IsNativeCall() || IntrinsicNeedsParamLocations(id)) {
257 auto pinfo = static_cast<LocationsBuilder *>(visitor)->GetResetParameterInfo();
258 if (intrinsic->IsMethodFirstInput()) {
259 pinfo->GetNextLocation(GetWordType());
260 }
261 if (intrinsic->HasImms() && graph->SupportManagedCode()) {
262 for ([[maybe_unused]] auto imm : intrinsic->GetImms()) {
263 pinfo->GetNextLocation(DataType::INT32);
264 }
265 }
266 size_t explicitArgs;
267 if (IsStackRangeIntrinsic(id, &explicitArgs)) {
268 static_cast<LocationsBuilder *>(visitor)->ProcessManagedCallStackRange(inst, explicitArgs, pinfo);
269 } else {
270 static_cast<LocationsBuilder *>(visitor)->ProcessManagedCall(inst, pinfo);
271 }
272 return;
273 }
274 ArenaAllocator *allocator = static_cast<LocationsBuilder *>(visitor)->GetGraph()->GetAllocator();
275 LocationsInfo *locations = allocator->New<LocationsInfo>(allocator, inst);
276 auto inputsCount = inst->GetInputsCount() - (inst->RequireState() ? 1 : 0);
277 for (size_t i = 0; i < inputsCount; i++) {
278 ASSERT(locations != nullptr);
279 locations->SetLocation(i, Location::RequireRegister());
280 }
281 }
282
LOCATIONS_BUILDER(void)283 LOCATIONS_BUILDER(void)::VisitCallNative(GraphVisitor *visitor, Inst *inst)
284 {
285 auto *pinfo = static_cast<LocationsBuilder *>(visitor)->GetResetParameterInfo();
286 static_cast<LocationsBuilder *>(visitor)->ProcessManagedCall(inst, pinfo);
287 }
288
LOCATIONS_BUILDER(void)289 LOCATIONS_BUILDER(void)::VisitNewArray([[maybe_unused]] GraphVisitor *visitor, Inst *inst)
290 {
291 inst->CastToNewArray()->SetLocation(0, Location::MakeRegister(GetTarget().GetParamRegId(0)));
292 inst->CastToNewArray()->SetLocation(1, Location::MakeRegister(GetTarget().GetParamRegId(1)));
293 inst->CastToNewArray()->SetDstLocation(GetLocationForReturn(inst));
294 }
295
LOCATIONS_BUILDER(void)296 LOCATIONS_BUILDER(void)::VisitNewObject([[maybe_unused]] GraphVisitor *visitor, Inst *inst)
297 {
298 inst->CastToNewObject()->SetLocation(0, Location::MakeRegister(GetTarget().GetParamRegId(0)));
299 inst->CastToNewObject()->SetDstLocation(GetLocationForReturn(inst));
300 }
301
LOCATIONS_BUILDER(void)302 LOCATIONS_BUILDER(void)::VisitParameter([[maybe_unused]] GraphVisitor *visitor, [[maybe_unused]] Inst *inst)
303 {
304 /* Currently we can't process parameters in the Locations Builder, because Parameter instructions may be removed
305 * during optimizations pipeline. Thus, locations is set by IR builder before optimizations.
306 * NOTE(compiler): we need to move Parameters' locations here, thereby we get rid of arch structures in the Graph,
307 * such as ParameterInfo, CallingConvention, etc.
308 */
309 }
310
LOCATIONS_BUILDER(void)311 LOCATIONS_BUILDER(void)::VisitReturn([[maybe_unused]] GraphVisitor *visitor, Inst *inst)
312 {
313 auto returnReg =
314 DataType::IsFloatType(inst->GetType()) ? GetTarget().GetReturnRegId() : GetTarget().GetReturnFpRegId();
315 inst->CastToReturn()->SetLocation(0, Location::MakeRegister(returnReg));
316 }
317
LOCATIONS_BUILDER(void)318 LOCATIONS_BUILDER(void)::VisitThrow([[maybe_unused]] GraphVisitor *visitor, Inst *inst)
319 {
320 inst->CastToThrow()->SetLocation(0, Location::MakeRegister(GetTarget().GetParamRegId(0)));
321 }
322
LOCATIONS_BUILDER(void)323 LOCATIONS_BUILDER(void)::VisitLiveOut([[maybe_unused]] GraphVisitor *visitor, Inst *inst)
324 {
325 inst->CastToLiveOut()->SetLocation(0, Location::MakeRegister(inst->GetDstReg()));
326 }
327
LOCATIONS_BUILDER(void)328 LOCATIONS_BUILDER(void)::VisitMultiArray(GraphVisitor *visitor, Inst *inst)
329 {
330 auto graph = static_cast<LocationsBuilder *>(visitor)->GetGraph();
331 ArenaAllocator *allocator = graph->GetAllocator();
332 LocationsInfo *locations = allocator->New<LocationsInfo>(allocator, inst);
333 ASSERT(locations != nullptr);
334 locations->SetLocation(0, Location::RequireRegister());
335
336 for (size_t i = 1; i < inst->GetInputsCount() - 1; i++) {
337 locations->SetLocation(i, Location::MakeStackArgument(i - 1));
338 }
339 auto stackArgs = inst->GetInputsCount() - 2U;
340 graph->UpdateStackSlotsCount(RoundUp(stackArgs, 2U));
341 locations->SetDstLocation(GetLocationForReturn(inst));
342 }
343
LOCATIONS_BUILDER(void)344 LOCATIONS_BUILDER(void)::VisitStoreStatic([[maybe_unused]] GraphVisitor *visitor, Inst *inst)
345 {
346 if (inst->CastToStoreStatic()->GetNeedBarrier()) {
347 inst->SetFlag(inst_flags::REQUIRE_TMP);
348 }
349 }
350
LOCATIONS_BUILDER(void)351 LOCATIONS_BUILDER(void)::VisitResolveByName([[maybe_unused]] GraphVisitor *visitor, Inst *inst)
352 {
353 inst->CastToResolveByName()->SetDstLocation(GetLocationForReturn(inst));
354 }
355
356 template <Arch ARCH>
GetLocationForReturn(Inst * inst)357 Location LocationsBuilder<ARCH>::GetLocationForReturn(Inst *inst)
358 {
359 auto returnReg =
360 DataType::IsFloatType(inst->GetType()) ? GetTarget().GetReturnFpRegId() : GetTarget().GetReturnRegId();
361 return Location::MakeRegister(returnReg, inst->GetType());
362 }
363
364 template <Arch ARCH>
GetResetParameterInfo()365 ParameterInfo *LocationsBuilder<ARCH>::GetResetParameterInfo()
366 {
367 paramsInfo_->Reset();
368 return paramsInfo_;
369 }
370 } // namespace ark::compiler
371