1 /*
2 * Copyright (c) 2021-2023 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 "inst_builder.h"
17 #include "phi_resolver.h"
18 #include "optimizer/code_generator/encode.h"
19 #include "compiler_logger.h"
20 #ifndef PANDA_TARGET_WINDOWS
21 #include "callconv.h"
22 #endif
23
24 namespace panda::compiler {
25
26 /* static */
ConvertPbcType(panda_file::Type type)27 DataType::Type InstBuilder::ConvertPbcType(panda_file::Type type)
28 {
29 switch (type.GetId()) {
30 case panda_file::Type::TypeId::VOID:
31 return DataType::VOID;
32 case panda_file::Type::TypeId::U1:
33 return DataType::BOOL;
34 case panda_file::Type::TypeId::I8:
35 return DataType::INT8;
36 case panda_file::Type::TypeId::U8:
37 return DataType::UINT8;
38 case panda_file::Type::TypeId::I16:
39 return DataType::INT16;
40 case panda_file::Type::TypeId::U16:
41 return DataType::UINT16;
42 case panda_file::Type::TypeId::I32:
43 return DataType::INT32;
44 case panda_file::Type::TypeId::U32:
45 return DataType::UINT32;
46 case panda_file::Type::TypeId::I64:
47 return DataType::INT64;
48 case panda_file::Type::TypeId::U64:
49 return DataType::UINT64;
50 case panda_file::Type::TypeId::F32:
51 return DataType::FLOAT32;
52 case panda_file::Type::TypeId::F64:
53 return DataType::FLOAT64;
54 case panda_file::Type::TypeId::REFERENCE:
55 return DataType::REFERENCE;
56 case panda_file::Type::TypeId::TAGGED:
57 case panda_file::Type::TypeId::INVALID:
58 default:
59 UNREACHABLE();
60 }
61 UNREACHABLE();
62 }
63
Prepare(bool isInlinedGraph)64 void InstBuilder::Prepare(bool isInlinedGraph)
65 {
66 SetCurrentBlock(GetGraph()->GetStartBlock());
67 #ifndef PANDA_TARGET_WINDOWS
68 GetGraph()->ResetParameterInfo();
69 #endif
70 // Create parameter for actual num args
71 if (!GetGraph()->IsBytecodeOptimizer() && GetGraph()->IsDynamicMethod() && !GetGraph()->GetMode().IsDynamicStub()) {
72 auto paramInst = GetGraph()->AddNewParameter(ParameterInst::DYNAMIC_NUM_ARGS);
73 paramInst->SetType(DataType::UINT32);
74 paramInst->SetLocationData(GetGraph()->GetDataForNativeParam(DataType::UINT32));
75 }
76 size_t argRefNum = 0;
77 if (GetRuntime()->GetMethodReturnType(GetMethod()) == DataType::REFERENCE) {
78 argRefNum = 1;
79 }
80 auto numArgs = GetRuntime()->GetMethodTotalArgumentsCount(GetMethod());
81 bool isStatic = GetRuntime()->IsMethodStatic(GetMethod());
82 // Create Parameter instructions for all arguments
83 for (size_t i = 0; i < numArgs; i++) {
84 auto paramInst = GetGraph()->AddNewParameter(i);
85 auto type = GetCurrentMethodArgumentType(i);
86 auto regNum = GetRuntime()->GetMethodRegistersCount(GetMethod()) + i;
87 ASSERT(!GetGraph()->IsBytecodeOptimizer() || regNum != INVALID_REG);
88
89 paramInst->SetType(type);
90 // This parameter in virtaul method is implicit, so skipped
91 if (type == DataType::REFERENCE && (isStatic || i > 0)) {
92 paramInst->SetArgRefNumber(argRefNum++);
93 }
94 SetParamSpillFill(GetGraph(), paramInst, numArgs, i, type);
95
96 UpdateDefinition(regNum, paramInst);
97 }
98
99 // We don't need to create SafePoint at the beginning of the callee graph
100 if (g_options.IsCompilerUseSafepoint() && !isInlinedGraph) {
101 GetGraph()->GetStartBlock()->AppendInst(CreateSafePoint(GetGraph()->GetStartBlock()));
102 }
103 methodProfile_ = GetRuntime()->GetMethodProfile(GetMethod(), !GetGraph()->IsAotMode());
104 }
105
UpdateDefsForCatch()106 void InstBuilder::UpdateDefsForCatch()
107 {
108 Inst *catchPhi = currentBb_->GetFirstInst();
109 ASSERT(catchPhi != nullptr);
110 for (size_t vreg = 0; vreg < GetVRegsCount(); vreg++) {
111 ASSERT(catchPhi->IsCatchPhi());
112 defs_[currentBb_->GetId()][vreg] = catchPhi;
113 catchPhi = catchPhi->GetNext();
114 }
115 }
116
UpdateDefsForLoopHead()117 void InstBuilder::UpdateDefsForLoopHead()
118 {
119 // If current block is a loop header, then propagate all definitions from preheader's predecessors to
120 // current block.
121 ASSERT(currentBb_->GetLoop()->GetPreHeader());
122 auto predDefs = defs_[currentBb_->GetLoop()->GetPreHeader()->GetId()];
123 COMPILER_LOG(DEBUG, IR_BUILDER) << "basic block is loop header";
124 for (size_t vreg = 0; vreg < GetVRegsCount(); vreg++) {
125 auto defInst = predDefs[vreg];
126 if (defInst != nullptr) {
127 auto phi = GetGraph()->CreateInstPhi();
128 if (vreg > vregsAndArgsCount_) {
129 phi->SetType(DataType::ANY);
130 }
131 phi->SetMarker(GetNoTypeMarker());
132 phi->SetLinearNumber(vreg);
133 currentBb_->AppendPhi(phi);
134 (*currentDefs_)[vreg] = phi;
135 COMPILER_LOG(DEBUG, IR_BUILDER) << "create Phi(id=" << phi->GetId() << ") for r" << vreg
136 << "(def id=" << predDefs[vreg]->GetId() << ")";
137 }
138 }
139 }
140
UpdateDefsForPreds(size_t vreg,std::optional<Inst * > & value)141 bool InstBuilder::UpdateDefsForPreds(size_t vreg, std::optional<Inst *> &value)
142 {
143 for (auto predBb : currentBb_->GetPredsBlocks()) {
144 // When irreducible loop header is visited before it's back-edge, phi should be created,
145 // since we do not know if definitions are different at this point
146 if (!predBb->IsMarked(visitedBlockMarker_)) {
147 ASSERT(currentBb_->GetLoop()->IsIrreducible());
148 return true;
149 }
150 if (!value.has_value()) {
151 value = defs_[predBb->GetId()][vreg];
152 } else if (value.value() != defs_[predBb->GetId()][vreg]) {
153 return true;
154 }
155 }
156 return false;
157 }
158
UpdateDefs()159 void InstBuilder::UpdateDefs()
160 {
161 currentBb_->SetMarker(visitedBlockMarker_);
162 if (currentBb_->IsCatchBegin()) {
163 UpdateDefsForCatch();
164 } else if (currentBb_->IsLoopHeader() && !currentBb_->GetLoop()->IsIrreducible()) {
165 UpdateDefsForLoopHead();
166 } else if (currentBb_->GetPredsBlocks().size() == 1) {
167 // Only one predecessor - simply copy all its definitions
168 auto &predDefs = defs_[currentBb_->GetPredsBlocks()[0]->GetId()];
169 std::copy(predDefs.begin(), predDefs.end(), currentDefs_->begin());
170 } else if (currentBb_->GetPredsBlocks().size() > 1) {
171 // If there are multiple predecessors, then add phi for each register that has different definitions
172 for (size_t vreg = 0; vreg < GetVRegsCount(); vreg++) {
173 std::optional<Inst *> value;
174 bool different = UpdateDefsForPreds(vreg, value);
175 if (different) {
176 auto phi = GetGraph()->CreateInstPhi();
177 phi->SetMarker(GetNoTypeMarker());
178 phi->SetLinearNumber(vreg);
179 currentBb_->AppendPhi(phi);
180 (*currentDefs_)[vreg] = phi;
181 COMPILER_LOG(DEBUG, IR_BUILDER) << "create Phi(id=" << phi->GetId() << ") for r" << vreg;
182 } else {
183 (*currentDefs_)[vreg] = value.value();
184 }
185 }
186 }
187 }
188
AddCatchPhiInputs(const ArenaUnorderedSet<BasicBlock * > & catchHandlers,const InstVector & defs,Inst * throwableInst)189 void InstBuilder::AddCatchPhiInputs(const ArenaUnorderedSet<BasicBlock *> &catchHandlers, const InstVector &defs,
190 Inst *throwableInst)
191 {
192 ASSERT(!catchHandlers.empty());
193 for (auto catchBb : catchHandlers) {
194 auto inst = catchBb->GetFirstInst();
195 while (!inst->IsCatchPhi()) {
196 inst = inst->GetNext();
197 }
198 ASSERT(inst != nullptr);
199 GetGraph()->AppendThrowableInst(throwableInst, catchBb);
200 for (size_t vreg = 0; vreg < GetVRegsCount(); vreg++, inst = inst->GetNext()) {
201 ASSERT(inst->GetOpcode() == Opcode::CatchPhi);
202 auto catchPhi = inst->CastToCatchPhi();
203 if (catchPhi->IsAcc()) {
204 ASSERT(vreg == vregsAndArgsCount_);
205 continue;
206 }
207 auto inputInst = defs[vreg];
208 if (inputInst != nullptr && inputInst != catchPhi) {
209 catchPhi->AppendInput(inputInst);
210 catchPhi->AppendThrowableInst(throwableInst);
211 }
212 }
213 }
214 }
215
SetParamSpillFill(Graph * graph,ParameterInst * paramInst,size_t numArgs,size_t i,DataType::Type type)216 void InstBuilder::SetParamSpillFill(Graph *graph, ParameterInst *paramInst, size_t numArgs, size_t i,
217 DataType::Type type)
218 {
219 if (graph->IsBytecodeOptimizer()) {
220 auto regSrc = static_cast<Register>(VIRTUAL_FRAME_SIZE - numArgs + i);
221 DataType::Type regType;
222 if (DataType::IsReference(type)) {
223 regType = DataType::REFERENCE;
224 } else if (DataType::Is64Bits(type, graph->GetArch())) {
225 regType = DataType::UINT64;
226 } else {
227 regType = DataType::UINT32;
228 }
229
230 paramInst->SetLocationData({LocationType::REGISTER, LocationType::REGISTER, regSrc, regSrc, regType});
231 } else {
232 #ifndef PANDA_TARGET_WINDOWS
233 if (graph->IsDynamicMethod() && !graph->GetMode().IsDynamicStub()) {
234 ASSERT(type == DataType::ANY);
235 uint16_t slot = i + CallConvDynInfo::FIXED_SLOT_COUNT;
236 ASSERT(slot <= UINT8_MAX);
237 paramInst->SetLocationData(
238 {LocationType::STACK_PARAMETER, LocationType::INVALID, slot, INVALID_REG, DataType::UINT64});
239 } else {
240 paramInst->SetLocationData(graph->GetDataForNativeParam(type));
241 }
242 #endif
243 }
244 }
245
246 /// Set type of instruction, then recursively set type to its inputs.
SetTypeRec(Inst * inst,DataType::Type type)247 void InstBuilder::SetTypeRec(Inst *inst, DataType::Type type)
248 {
249 inst->SetType(type);
250 inst->ResetMarker(GetNoTypeMarker());
251 for (auto input : inst->GetInputs()) {
252 if (input.GetInst()->IsMarked(GetNoTypeMarker())) {
253 SetTypeRec(input.GetInst(), type);
254 }
255 }
256 }
257
258 /**
259 * Remove vreg from SaveState for the case
260 * BB 1
261 * ....
262 * succs: [bb 2, bb 3]
263 *
264 * BB 2: preds: [bb 1]
265 * 89.i64 Sub v85, v88 -> (v119, v90)
266 * 90.f64 Cast v89 -> (v96, v92)
267 * succs: [bb 3]
268 *
269 * BB 3: preds: [bb 1, bb 2]
270 * .....
271 * 119. SaveState v105(vr0), v106(vr1), v94(vr4), v89(vr8), v0(vr10), v1(vr11) -> (v120)
272 *
273 * v89(vr8) used only in BB 2, so we need to remove its from "119. SaveState"
274 */
275 /* static */
RemoveNotDominateInputs(SaveStateInst * saveState)276 void InstBuilder::RemoveNotDominateInputs(SaveStateInst *saveState)
277 {
278 size_t idx = 0;
279 size_t inputsCount = saveState->GetInputsCount();
280 while (idx < inputsCount) {
281 auto inputInst = saveState->GetInput(idx).GetInst();
282 // We can don't call IsDominate, if save_state and input_inst in one basic block.
283 // It's reduce number of IsDominate calls.
284 if (!inputInst->InSameBlockOrDominate(saveState)) {
285 saveState->RemoveInput(idx);
286 inputsCount--;
287 } else {
288 ASSERT(inputInst->GetBasicBlock() != saveState->GetBasicBlock() || inputInst->IsDominate(saveState));
289 idx++;
290 }
291 }
292 }
293
294 /// Fix instructions that can't be fully completed in building process.
FixInstructions()295 void InstBuilder::FixInstructions()
296 {
297 // Remove dead Phi and set types to phi which have not type.
298 // Phi may not have type if all it users are pseudo instructions, like SaveState
299 for (auto bb : GetGraph()->GetBlocksRPO()) {
300 for (auto inst : bb->PhiInstsSafe()) {
301 inst->ReserveInputs(bb->GetPredsBlocks().size());
302 for (auto &predBb : bb->GetPredsBlocks()) {
303 if (inst->GetLinearNumber() == INVALID_LINEAR_NUM) {
304 continue;
305 }
306 auto pred = defs_[predBb->GetId()][inst->GetLinearNumber()];
307 if (pred == nullptr) {
308 // If any input of phi instruction is not defined then we assume that phi is dead. DCE should
309 // remove it.
310 continue;
311 }
312 inst->AppendInput(pred);
313 }
314 }
315 }
316
317 // Check all instructions that have no type and fix it. Type is got from instructions with known input types.
318 for (auto bb : GetGraph()->GetBlocksRPO()) {
319 for (auto inst : bb->AllInsts()) {
320 if (inst->IsSaveState()) {
321 RemoveNotDominateInputs(static_cast<SaveStateInst *>(inst));
322 continue;
323 }
324 auto inputIdx = 0;
325 for (auto input : inst->GetInputs()) {
326 if (input.GetInst()->IsMarked(GetNoTypeMarker())) {
327 auto inputType = inst->GetInputType(inputIdx);
328 if (inputType != DataType::NO_TYPE) {
329 SetTypeRec(input.GetInst(), inputType);
330 }
331 }
332 inputIdx++;
333 }
334 }
335 }
336 // Resolve dead and inconsistent phi instructions
337 PhiResolver phiResolver(GetGraph());
338 phiResolver.Run();
339 ResolveConstants();
340 }
341
CreateSaveState(Opcode opc,size_t pc)342 SaveStateInst *InstBuilder::CreateSaveState(Opcode opc, size_t pc)
343 {
344 ASSERT(opc == Opcode::SaveState || opc == Opcode::SafePoint || opc == Opcode::SaveStateOsr ||
345 opc == Opcode::SaveStateDeoptimize);
346 SaveStateInst *inst;
347 bool withoutNumericInputs = false;
348 auto liveVergsCount =
349 std::count_if(currentDefs_->begin(), currentDefs_->end(), [](Inst *p) { return p != nullptr; });
350 if (opc == Opcode::SaveState) {
351 inst = GetGraph()->CreateInstSaveState(pc, GetMethod(), callerInst_, inliningDepth_);
352 } else if (opc == Opcode::SaveStateOsr) {
353 inst = GetGraph()->CreateInstSaveStateOsr(pc, GetMethod(), callerInst_, inliningDepth_);
354 } else if (opc == Opcode::SafePoint) {
355 inst = GetGraph()->CreateInstSafePoint(pc, GetMethod(), callerInst_, inliningDepth_);
356 withoutNumericInputs = true;
357 } else {
358 inst = GetGraph()->CreateInstSaveStateDeoptimize(pc, GetMethod(), callerInst_, inliningDepth_);
359 }
360 if (GetGraph()->IsBytecodeOptimizer()) {
361 inst->ReserveInputs(0);
362 return inst;
363 }
364 inst->ReserveInputs(liveVergsCount);
365
366 for (VirtualRegister::ValueType regIdx = 0; regIdx < vregsAndArgsCount_; ++regIdx) {
367 auto defInst = (*currentDefs_)[regIdx];
368 if (defInst != nullptr && (!withoutNumericInputs || !DataType::IsTypeNumeric(defInst->GetType()))) {
369 auto inputIdx = inst->AppendInput(defInst);
370 inst->SetVirtualRegister(inputIdx, VirtualRegister(regIdx, VRegType::VREG));
371 }
372 }
373 VirtualRegister::ValueType regIdx = vregsAndArgsCount_;
374 auto defInst = (*currentDefs_)[regIdx];
375 if (defInst != nullptr && (!withoutNumericInputs || !DataType::IsTypeNumeric(defInst->GetType()))) {
376 auto inputIdx = inst->AppendInput(defInst);
377 inst->SetVirtualRegister(inputIdx, VirtualRegister(regIdx, VRegType::ACC));
378 }
379 regIdx++;
380 if (GetGraph()->IsDynamicMethod() && !GetGraph()->IsBytecodeOptimizer() && (*currentDefs_)[regIdx] != nullptr) {
381 for (uint8_t envIdx = 0; envIdx < VRegInfo::ENV_COUNT; ++envIdx) {
382 auto inputIdx = inst->AppendInput(GetEnvDefinition(envIdx));
383 inst->SetVirtualRegister(inputIdx, VirtualRegister(regIdx++, VRegType(VRegType::ENV_BEGIN + envIdx)));
384 }
385 if (additionalDef_ != nullptr) {
386 inst->AppendBridge(additionalDef_);
387 }
388 }
389 return inst;
390 }
391
CreateLoadAndInitClassGeneric(uint32_t classId,size_t pc)392 ClassInst *InstBuilder::CreateLoadAndInitClassGeneric(uint32_t classId, size_t pc)
393 {
394 auto classPtr = GetRuntime()->ResolveType(GetGraph()->GetMethod(), classId);
395 ClassInst *inst = nullptr;
396 if (classPtr == nullptr) {
397 ASSERT(!graph_->IsBytecodeOptimizer());
398 inst = graph_->CreateInstUnresolvedLoadAndInitClass(DataType::REFERENCE, pc, nullptr, classId,
399 GetGraph()->GetMethod(), classPtr);
400 if (!GetGraph()->IsAotMode() && !GetGraph()->IsBytecodeOptimizer()) {
401 GetRuntime()->GetUnresolvedTypes()->AddTableSlot(GetMethod(), classId,
402 UnresolvedTypesInterface::SlotKind::CLASS);
403 }
404 } else {
405 inst = graph_->CreateInstLoadAndInitClass(DataType::REFERENCE, pc, nullptr, classId, GetGraph()->GetMethod(),
406 classPtr);
407 }
408 return inst;
409 }
410
GetCurrentMethodReturnType() const411 DataType::Type InstBuilder::GetCurrentMethodReturnType() const
412 {
413 return GetRuntime()->GetMethodReturnType(GetMethod());
414 }
415
GetCurrentMethodArgumentType(size_t index) const416 DataType::Type InstBuilder::GetCurrentMethodArgumentType(size_t index) const
417 {
418 return GetRuntime()->GetMethodTotalArgumentType(GetMethod(), index);
419 }
420
GetCurrentMethodArgumentsCount() const421 size_t InstBuilder::GetCurrentMethodArgumentsCount() const
422 {
423 return GetRuntime()->GetMethodTotalArgumentsCount(GetMethod());
424 }
425
GetMethodReturnType(uintptr_t id) const426 DataType::Type InstBuilder::GetMethodReturnType(uintptr_t id) const
427 {
428 return GetRuntime()->GetMethodReturnType(GetMethod(), id);
429 }
430
GetMethodArgumentType(uintptr_t id,size_t index) const431 DataType::Type InstBuilder::GetMethodArgumentType(uintptr_t id, size_t index) const
432 {
433 return GetRuntime()->GetMethodArgumentType(GetMethod(), id, index);
434 }
435
GetMethodArgumentsCount(uintptr_t id) const436 size_t InstBuilder::GetMethodArgumentsCount(uintptr_t id) const
437 {
438 return GetRuntime()->GetMethodArgumentsCount(GetMethod(), id);
439 }
440
GetPc(const uint8_t * instPtr) const441 size_t InstBuilder::GetPc(const uint8_t *instPtr) const
442 {
443 return instPtr - instructionsBuf_;
444 }
445
ResolveConstants()446 void InstBuilder::ResolveConstants()
447 {
448 ConstantInst *currConst = GetGraph()->GetFirstConstInst();
449 while (currConst != nullptr) {
450 SplitConstant(currConst);
451 currConst = currConst->GetNextConst();
452 }
453 }
454
SplitConstant(ConstantInst * constInst)455 void InstBuilder::SplitConstant(ConstantInst *constInst)
456 {
457 if (constInst->GetType() != DataType::INT64 || !constInst->HasUsers()) {
458 return;
459 }
460 auto users = constInst->GetUsers();
461 auto currIt = users.begin();
462 while (currIt != users.end()) {
463 auto user = (*currIt).GetInst();
464 DataType::Type type = user->GetInputType(currIt->GetIndex());
465 ++currIt;
466 if (type != DataType::FLOAT32 && type != DataType::FLOAT64) {
467 continue;
468 }
469 ConstantInst *newConst = nullptr;
470 if (type == DataType::FLOAT32) {
471 auto val = bit_cast<float>(static_cast<uint32_t>(constInst->GetIntValue()));
472 newConst = GetGraph()->FindOrCreateConstant(val);
473 } else {
474 auto val = bit_cast<double, uint64_t>(constInst->GetIntValue());
475 newConst = GetGraph()->FindOrCreateConstant(val);
476 }
477 user->ReplaceInput(constInst, newConst);
478 }
479 }
480
SyncWithGraph()481 void InstBuilder::SyncWithGraph()
482 {
483 size_t idx = currentDefs_ - &defs_[0];
484 size_t size = defs_.size();
485 defs_.resize(graph_->GetVectorBlocks().size(), InstVector(graph_->GetLocalAllocator()->Adapter()));
486 for (size_t i = size; i < defs_.size(); i++) {
487 defs_[i].resize(vregsAndArgsCount_ + 1 + graph_->GetEnvCount());
488 std::copy(defs_[idx].cbegin(), defs_[idx].cend(), defs_[i].begin());
489 }
490 currentDefs_ = &defs_[currentBb_->GetId()];
491 }
492
493 } // namespace panda::compiler
494