1 /**
2 * Copyright (c) 2024 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 "libabckit/src/irbuilder_dynamic/inst_builder_dyn.h"
17 #include "libabckit/src/irbuilder_dynamic/phi_resolver_dyn.h"
18
19 namespace libabckit {
20
21 // NOLINTNEXTLINE(google-build-using-namespace)
22 using namespace ark;
23
Prepare()24 void InstBuilder::Prepare()
25 {
26 SetCurrentBlock(GetGraph()->GetStartBlock());
27 #ifndef PANDA_TARGET_WINDOWS
28 GetGraph()->ResetParameterInfo();
29 #endif
30 auto numArgs = GetRuntime()->GetMethodTotalArgumentsCount(GetMethod());
31 // Create Parameter instructions for all arguments
32 for (size_t i = 0; i < numArgs; i++) {
33 auto paramInst = GetGraph()->AddNewParameter(i);
34 auto type = compiler::DataType::Type::ANY;
35 auto regNum = GetRuntime()->GetMethodRegistersCount(GetMethod()) + i;
36 ASSERT(!GetGraph()->IsBytecodeOptimizer() || regNum != ark::compiler::GetInvalidReg());
37
38 paramInst->SetType(type);
39 SetParamSpillFill(GetGraph(), paramInst, numArgs, i, type);
40
41 UpdateDefinition(regNum, paramInst);
42 }
43 }
44
UpdateDefsForCatch()45 void InstBuilder::UpdateDefsForCatch()
46 {
47 compiler::Inst *catchPhi = currentBb_->GetFirstInst();
48 ASSERT(catchPhi != nullptr);
49 for (size_t vreg = 0; vreg < GetVRegsCount(); vreg++) {
50 ASSERT(catchPhi->IsCatchPhi());
51 defs_[currentBb_->GetId()][vreg] = catchPhi;
52 catchPhi = catchPhi->GetNext();
53 }
54 }
55
UpdateDefsForLoopHead()56 void InstBuilder::UpdateDefsForLoopHead()
57 {
58 // If current block is a loop header, then propagate all definitions from preheader's predecessors to
59 // current block.
60 ASSERT(currentBb_->GetLoop()->GetPreHeader());
61 auto predDefs = defs_[currentBb_->GetLoop()->GetPreHeader()->GetId()];
62 for (size_t vreg = 0; vreg < GetVRegsCount(); vreg++) {
63 auto defInst = predDefs[vreg];
64 if (defInst != nullptr) {
65 auto phi = GetGraph()->CreateInstPhi();
66 phi->SetMarker(GetNoTypeMarker());
67 phi->SetLinearNumber(vreg);
68 currentBb_->AppendPhi(phi);
69 (*currentDefs_)[vreg] = phi;
70 }
71 }
72 }
73
AddPhiToDifferent()74 void InstBuilder::AddPhiToDifferent()
75 {
76 for (size_t vreg = 0; vreg < GetVRegsCount(); vreg++) {
77 compiler::Inst *value = nullptr;
78 bool different = false;
79 for (auto predBb : currentBb_->GetPredsBlocks()) {
80 // When irreducible loop header is visited before it's back-edge, phi should be created,
81 // since we do not know if definitions are different at this point
82 if (!predBb->IsMarked(visitedBlockMarker_)) {
83 ASSERT(currentBb_->GetLoop()->IsIrreducible());
84 different = true;
85 break;
86 }
87 if (value == nullptr) {
88 value = defs_[predBb->GetId()][vreg];
89 } else if (value != defs_[predBb->GetId()][vreg]) {
90 different = true;
91 break;
92 }
93 }
94 if (different) {
95 auto phi = GetGraph()->CreateInstPhi();
96 phi->SetMarker(GetNoTypeMarker());
97 phi->SetLinearNumber(vreg);
98 currentBb_->AppendPhi(phi);
99 (*currentDefs_)[vreg] = phi;
100 } else {
101 (*currentDefs_)[vreg] = value;
102 }
103 }
104 }
105
UpdateDefs()106 void InstBuilder::UpdateDefs()
107 {
108 currentBb_->SetMarker(visitedBlockMarker_);
109 if (currentBb_->IsCatchBegin()) {
110 UpdateDefsForCatch();
111 } else if (currentBb_->IsLoopHeader() && !currentBb_->GetLoop()->IsIrreducible()) {
112 UpdateDefsForLoopHead();
113 } else if (currentBb_->GetPredsBlocks().size() == 1) {
114 // Only one predecessor - simply copy all its definitions
115 auto &predDefs = defs_[currentBb_->GetPredsBlocks()[0]->GetId()];
116 std::copy(predDefs.begin(), predDefs.end(), currentDefs_->begin());
117 } else if (currentBb_->GetPredsBlocks().size() > 1) {
118 AddPhiToDifferent();
119 }
120 }
121
AddCatchPhiInputs(const ArenaUnorderedSet<compiler::BasicBlock * > & catchHandlers,const compiler::InstVector & defs,compiler::Inst * throwableInst)122 void InstBuilder::AddCatchPhiInputs(const ArenaUnorderedSet<compiler::BasicBlock *> &catchHandlers,
123 const compiler::InstVector &defs, compiler::Inst *throwableInst)
124 {
125 ASSERT(!catchHandlers.empty());
126 for (auto catchBb : catchHandlers) {
127 auto inst = catchBb->GetFirstInst();
128 while (!inst->IsCatchPhi()) {
129 inst = inst->GetNext();
130 }
131 ASSERT(inst != nullptr);
132 GetGraph()->AppendThrowableInst(throwableInst, catchBb);
133 for (size_t vreg = 0; vreg < GetVRegsCount(); vreg++, inst = inst->GetNext()) {
134 ASSERT(inst->GetOpcode() == ark::compiler::Opcode::CatchPhi);
135 auto catchPhi = inst->CastToCatchPhi();
136 if (catchPhi->IsAcc()) {
137 ASSERT(vreg == vregsAndArgsCount_);
138 continue;
139 }
140 auto inputInst = defs[vreg];
141 if (inputInst != nullptr && inputInst != catchPhi) {
142 catchPhi->AppendInput(inputInst);
143 catchPhi->AppendThrowableInst(throwableInst);
144 }
145 }
146 }
147 }
148
SetParamSpillFill(compiler::Graph * graph,compiler::ParameterInst * paramInst,size_t numArgs,size_t i,compiler::DataType::Type type)149 void InstBuilder::SetParamSpillFill(compiler::Graph *graph, compiler::ParameterInst *paramInst, size_t numArgs,
150 size_t i, compiler::DataType::Type type)
151 {
152 if (graph->IsBytecodeOptimizer()) {
153 auto regSrc = static_cast<compiler::Register>(compiler::GetFrameSize() - numArgs + i);
154 compiler::DataType::Type regType;
155 if (compiler::DataType::IsReference(type)) {
156 regType = compiler::DataType::REFERENCE;
157 } else if (compiler::DataType::Is64Bits(type, graph->GetArch())) {
158 regType = compiler::DataType::UINT64;
159 } else {
160 regType = compiler::DataType::UINT32;
161 }
162
163 paramInst->SetLocationData(
164 {compiler::LocationType::REGISTER, compiler::LocationType::REGISTER, regSrc, regSrc, regType});
165 }
166 }
167
168 /**
169 * Set type of instruction, then recursively set type to its inputs.
170 */
SetTypeRec(compiler::Inst * inst,compiler::DataType::Type type)171 void InstBuilder::SetTypeRec(compiler::Inst *inst, compiler::DataType::Type type)
172 {
173 inst->SetType(type);
174 inst->ResetMarker(GetNoTypeMarker());
175 for (auto input : inst->GetInputs()) {
176 if (input.GetInst()->IsMarked(GetNoTypeMarker())) {
177 SetTypeRec(input.GetInst(), type);
178 }
179 }
180 }
181
182 // CC-OFFNXT(WordsTool.190) sensitive word conflict
183 /**
184 * Remove vreg from SaveState for the case
185 * BB 1
186 * ....
187 * succs: [bb 2, bb 3]
188 *
189 * BB 2: preds: [bb 1]
190 * 89.i64 Sub v85, v88 -> (v119, v90)
191 * 90.f64 Cast v89 -> (v96, v92)
192 * succs: [bb 3]
193 *
194 * BB 3: preds: [bb 1, bb 2]
195 * .....
196 * 119. SaveState v105(vr0), v106(vr1), v94(vr4), v89(vr8), v0(vr10), v1(vr11) -> (v120)
197 *
198 * v89(vr8) used only in BB 2, so we need to remove its from "119. SaveState"
199 */
200 /* static */
RemoveNotDominateInputs(compiler::SaveStateInst * saveState)201 void InstBuilder::RemoveNotDominateInputs(compiler::SaveStateInst *saveState)
202 {
203 size_t idx = 0;
204 size_t inputsCount = saveState->GetInputsCount();
205 while (idx < inputsCount) {
206 auto inputInst = saveState->GetInput(idx).GetInst();
207 // We can don't call IsDominate, if save_state and input_inst in one basic block.
208 // It's reduce number of IsDominate calls.
209 if (!inputInst->InSameBlockOrDominate(saveState)) {
210 saveState->RemoveInput(idx);
211 inputsCount--;
212 } else {
213 ASSERT(inputInst->GetBasicBlock() != saveState->GetBasicBlock() || inputInst->IsDominate(saveState));
214 idx++;
215 }
216 }
217 }
218
UpdatePreds(compiler::BasicBlock * bb,compiler::Inst * inst)219 void InstBuilder::UpdatePreds(compiler::BasicBlock *bb, compiler::Inst *inst)
220 {
221 inst->ReserveInputs(bb->GetPredsBlocks().size());
222 for (auto &predBb : bb->GetPredsBlocks()) {
223 if (inst->GetLinearNumber() == compiler::INVALID_LINEAR_NUM) {
224 continue;
225 }
226 auto pred = defs_[predBb->GetId()][inst->GetLinearNumber()];
227 if (pred == nullptr) {
228 // If any input of phi instruction is not defined then we assume that phi is dead. DCE should
229 // remove it.
230 continue;
231 }
232 inst->AppendInput(pred);
233 }
234 }
235
SetType(compiler::Inst * inst)236 void InstBuilder::SetType(compiler::Inst *inst)
237 {
238 if (inst->IsSaveState()) {
239 RemoveNotDominateInputs(static_cast<compiler::SaveStateInst *>(inst));
240 return;
241 }
242 auto inputIdx = 0;
243 for (auto input : inst->GetInputs()) {
244 if (input.GetInst()->IsMarked(GetNoTypeMarker())) {
245 auto inputType = inst->GetInputType(inputIdx);
246 if (inputType != compiler::DataType::NO_TYPE) {
247 SetTypeRec(input.GetInst(), inputType);
248 }
249 }
250 inputIdx++;
251 }
252 }
253
254 /**
255 * Fix instructions that can't be fully completed in building process.
256 */
FixInstructions()257 void InstBuilder::FixInstructions()
258 {
259 // Remove dead Phi and set types to phi which have not type.
260 // Phi may not have type if all it users are pseudo instructions, like SaveState
261 for (auto bb : GetGraph()->GetBlocksRPO()) {
262 for (auto inst : bb->PhiInstsSafe()) {
263 UpdatePreds(bb, inst);
264 }
265 }
266
267 // Check all instructions that have no type and fix it. Type is got from instructions with known input types.
268 for (auto bb : GetGraph()->GetBlocksRPO()) {
269 for (auto inst : bb->AllInsts()) {
270 SetType(inst);
271 }
272 }
273 // Resolve dead and inconsistent phi instructions
274 PhiResolver phiResolver(GetGraph());
275 phiResolver.Run();
276 ResolveConstants();
277 CleanupCatchPhis();
278 }
279
CreateSaveState(compiler::Opcode opc,size_t pc)280 compiler::SaveStateInst *InstBuilder::CreateSaveState([[maybe_unused]] compiler::Opcode opc, size_t pc)
281 {
282 ASSERT(opc == ark::compiler::Opcode::SaveState);
283 compiler::SaveStateInst *inst = GetGraph()->CreateInstSaveState();
284 inst->SetPc(pc);
285 inst->SetMethod(GetMethod());
286 inst->ReserveInputs(0);
287 return inst;
288 }
289
GetMethodArgumentsCount(uintptr_t id) const290 size_t InstBuilder::GetMethodArgumentsCount(uintptr_t id) const
291 {
292 return GetRuntime()->GetMethodArgumentsCount(GetMethod(), id);
293 }
294
GetPc(const uint8_t * instPtr) const295 size_t InstBuilder::GetPc(const uint8_t *instPtr) const
296 {
297 return instPtr - instructionsBuf_;
298 }
299
ResolveConstants()300 void InstBuilder::ResolveConstants()
301 {
302 compiler::ConstantInst *currConst = GetGraph()->GetFirstConstInst();
303 while (currConst != nullptr) {
304 SplitConstant(currConst);
305 currConst = currConst->GetNextConst();
306 }
307 }
308
SplitConstant(compiler::ConstantInst * constInst)309 void InstBuilder::SplitConstant(compiler::ConstantInst *constInst)
310 {
311 if (constInst->GetType() != compiler::DataType::INT64 || !constInst->HasUsers()) {
312 return;
313 }
314 auto users = constInst->GetUsers();
315 auto currIt = users.begin();
316 while (currIt != users.end()) {
317 auto user = (*currIt).GetInst();
318 compiler::DataType::Type type = user->GetInputType(currIt->GetIndex());
319 ++currIt;
320 if (type != compiler::DataType::FLOAT32 && type != compiler::DataType::FLOAT64) {
321 continue;
322 }
323 compiler::ConstantInst *newConst = nullptr;
324 if (type == compiler::DataType::FLOAT32) {
325 auto val = bit_cast<float>(static_cast<uint32_t>(constInst->GetIntValue()));
326 newConst = GetGraph()->FindOrCreateConstant(val);
327 } else {
328 auto val = bit_cast<double, uint64_t>(constInst->GetIntValue());
329 newConst = GetGraph()->FindOrCreateConstant(val);
330 }
331 user->ReplaceInput(constInst, newConst);
332 }
333 }
334
CleanupInst(compiler::BasicBlock * block,compiler::Inst * inst)335 void InstBuilder::CleanupInst(compiler::BasicBlock *block, compiler::Inst *inst)
336 {
337 if (!inst->IsCatchPhi() || inst->GetInputs().Empty()) {
338 return;
339 }
340 // Remove catch-phis without real users
341 bool hasSsUsersOnly = true;
342 for (const auto &user : inst->GetUsers()) {
343 if (!user.GetInst()->IsSaveState()) {
344 hasSsUsersOnly = false;
345 break;
346 }
347 }
348 if (hasSsUsersOnly) {
349 auto users = inst->GetUsers();
350 while (!users.Empty()) {
351 auto &user = users.Front();
352 user.GetInst()->RemoveInput(user.GetIndex());
353 }
354 block->RemoveInst(inst);
355 }
356 }
357
CleanupCatchPhis()358 void InstBuilder::CleanupCatchPhis()
359 {
360 for (auto block : GetGraph()->GetBlocksRPO()) {
361 for (auto inst : block->AllInstsSafe()) {
362 CleanupInst(block, inst);
363 }
364 }
365 }
366
367 } // namespace libabckit
368