• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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