1 /*
2 * Copyright (c) 2021-2022 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 #include "codegen.h"
16 #include "common.h"
17 #include "generate_ecma.inl"
18 #include "tagged_value.h"
19
20 namespace panda::bytecodeopt {
21
22 using panda_file::LiteralTag;
23
DoLda(compiler::Register reg,std::vector<pandasm::Ins> & result)24 void DoLda(compiler::Register reg, std::vector<pandasm::Ins> &result)
25 {
26 if (reg != compiler::ACC_REG_ID) {
27 result.emplace_back(pandasm::Create_LDA(reg));
28 }
29 }
30
DoSta(compiler::Register reg,std::vector<pandasm::Ins> & result)31 void DoSta(compiler::Register reg, std::vector<pandasm::Ins> &result)
32 {
33 if (reg != compiler::ACC_REG_ID) {
34 result.emplace_back(pandasm::Create_STA(reg));
35 }
36 }
37
AppendCatchBlock(uint32_t type_id,const compiler::BasicBlock * try_begin,const compiler::BasicBlock * try_end,const compiler::BasicBlock * catch_begin,const compiler::BasicBlock * catch_end)38 void BytecodeGen::AppendCatchBlock(uint32_t type_id, const compiler::BasicBlock *try_begin,
39 const compiler::BasicBlock *try_end, const compiler::BasicBlock *catch_begin,
40 const compiler::BasicBlock *catch_end)
41 {
42 auto cb = pandasm::Function::CatchBlock();
43 if (type_id != 0) {
44 cb.exception_record = ir_interface_->GetTypeIdByOffset(type_id);
45 }
46 cb.try_begin_label = BytecodeGen::LabelName(try_begin->GetId());
47 cb.try_end_label = "end_" + BytecodeGen::LabelName(try_end->GetId());
48 cb.catch_begin_label = BytecodeGen::LabelName(catch_begin->GetId());
49 cb.catch_end_label =
50 catch_end == nullptr ? cb.catch_begin_label : "end_" + BytecodeGen::LabelName(catch_end->GetId());
51 catch_blocks_.emplace_back(cb);
52 }
53
VisitTryBegin(const compiler::BasicBlock * bb)54 void BytecodeGen::VisitTryBegin(const compiler::BasicBlock *bb)
55 {
56 ASSERT(bb->IsTryBegin());
57 auto try_inst = GetTryBeginInst(bb);
58 auto try_end = try_inst->GetTryEndBlock();
59 ASSERT(try_end != nullptr && try_end->IsTryEnd());
60
61 bb->EnumerateCatchHandlers([&, bb, try_end](BasicBlock *catch_handler, size_t type_id) {
62 AppendCatchBlock(type_id, bb, try_end, catch_handler);
63 return true;
64 });
65 }
66
RunImpl()67 bool BytecodeGen::RunImpl()
68 {
69 Reserve(function_->ins.size());
70 int32_t insn_order = 0;
71 if (!GetGraph()->GetTryBeginBlocks().empty()) {
72 // Workaround for AOT and JIT
73 result_.emplace_back(pandasm::Create_NOP());
74 }
75 for (auto *bb : GetGraph()->GetBlocksLinearOrder()) {
76 EmitLabel(BytecodeGen::LabelName(bb->GetId()));
77 if (bb->IsTryEnd() || bb->IsCatchEnd()) {
78 auto label = "end_" + BytecodeGen::LabelName(bb->GetId());
79 EmitLabel(label);
80 }
81 for (const auto &inst : bb->AllInsts()) {
82 auto start = GetResult().size();
83 VisitInstruction(inst);
84 if (!GetStatus()) {
85 return false;
86 }
87 auto end = GetResult().size();
88 ASSERT(end >= start);
89 for (auto i = start; i < end; ++i) {
90 AddLineNumber(inst, i);
91 AddColumnNumber(inst, i);
92 }
93 }
94 if (bb->NeedsJump()) {
95 EmitJump(bb);
96 insn_order++;
97 }
98 }
99 if (!GetStatus()) {
100 return false;
101 }
102 // Visit try-blocks in order they were declared
103 for (auto *bb : GetGraph()->GetTryBeginBlocks()) {
104 VisitTryBegin(bb);
105 }
106 function_->ins = std::move(GetResult());
107 function_->catch_blocks = catch_blocks_;
108 return true;
109 }
110
EmitJump(const BasicBlock * bb)111 void BytecodeGen::EmitJump(const BasicBlock *bb)
112 {
113 BasicBlock *suc_bb = nullptr;
114 ASSERT(bb != nullptr);
115
116 if (bb->GetLastInst() == nullptr) {
117 ASSERT(bb->IsEmpty());
118 suc_bb = bb->GetSuccsBlocks()[0];
119 result_.push_back(pandasm::Create_JMP(BytecodeGen::LabelName(suc_bb->GetId())));
120 return;
121 }
122
123 ASSERT(bb->GetLastInst() != nullptr);
124 switch (bb->GetLastInst()->GetOpcode()) {
125 case Opcode::If:
126 case Opcode::IfImm:
127 ASSERT(bb->GetSuccsBlocks().size() == compiler::MAX_SUCCS_NUM);
128 suc_bb = bb->GetFalseSuccessor();
129 break;
130 default:
131 suc_bb = bb->GetSuccsBlocks()[0];
132 break;
133 }
134 result_.push_back(pandasm::Create_JMP(BytecodeGen::LabelName(suc_bb->GetId())));
135 }
136
AddLineNumber(const Inst * inst,const size_t idx)137 void BytecodeGen::AddLineNumber(const Inst *inst, const size_t idx)
138 {
139 if (ir_interface_ != nullptr && idx < result_.size()) {
140 auto ln = ir_interface_->GetLineNumberByPc(inst->GetPc());
141 result_[idx].ins_debug.SetLineNumber(ln);
142 }
143 }
144
AddColumnNumber(const Inst * inst,const uint32_t idx)145 void BytecodeGen::AddColumnNumber(const Inst *inst, const uint32_t idx)
146 {
147 if (ir_interface_ != nullptr && idx < result_.size()) {
148 auto cn = ir_interface_->GetColumnNumberByPc(inst->GetPc());
149 result_[idx].ins_debug.SetColumnNumber(cn);
150 }
151 }
152
EncodeSpillFillData(const compiler::SpillFillData & sf)153 void BytecodeGen::EncodeSpillFillData(const compiler::SpillFillData &sf)
154 {
155 if (sf.SrcType() != compiler::LocationType::REGISTER || sf.DstType() != compiler::LocationType::REGISTER) {
156 LOG(ERROR, BYTECODE_OPTIMIZER) << "EncodeSpillFillData with unknown move type, src_type: "
157 << static_cast<int>(sf.SrcType())
158 << " dst_type: " << static_cast<int>(sf.DstType());
159 success_ = false;
160 UNREACHABLE();
161 return;
162 }
163 ASSERT(sf.GetType() != compiler::DataType::NO_TYPE);
164 ASSERT(sf.SrcValue() != compiler::INVALID_REG && sf.DstValue() != compiler::INVALID_REG);
165
166 if (sf.SrcValue() == sf.DstValue()) {
167 return;
168 }
169
170 pandasm::Ins move;
171 result_.emplace_back(pandasm::Create_MOV(sf.DstValue(), sf.SrcValue()));
172 return;
173 }
174
VisitSpillFill(GraphVisitor * visitor,Inst * inst)175 void BytecodeGen::VisitSpillFill(GraphVisitor *visitor, Inst *inst)
176 {
177 auto *enc = static_cast<BytecodeGen *>(visitor);
178 for (auto sf : inst->CastToSpillFill()->GetSpillFills()) {
179 enc->EncodeSpillFillData(sf);
180 }
181 }
182
183 template <typename UnaryPred>
HasUserPredicate(Inst * inst,UnaryPred p)184 bool HasUserPredicate(Inst *inst, UnaryPred p)
185 {
186 bool found = false;
187 for (auto const &u : inst->GetUsers()) {
188 if (p(u.GetInst())) {
189 found = true;
190 break;
191 }
192 }
193 return found;
194 }
195
VisitConstant(GraphVisitor * visitor,Inst * inst)196 void BytecodeGen::VisitConstant(GraphVisitor *visitor, Inst *inst)
197 {
198 auto *enc = static_cast<BytecodeGen *>(visitor);
199 auto type = inst->GetType();
200
201 /* Do not emit unused code for Const -> CastValueToAnyType chains */
202 if (!HasUserPredicate(inst,
203 [](Inst const *i) { return i->GetOpcode() != compiler::Opcode::CastValueToAnyType; })) {
204 return;
205 }
206
207 pandasm::Ins movi;
208 movi.regs.emplace_back(inst->GetDstReg());
209 switch (type) {
210 case compiler::DataType::INT64:
211 case compiler::DataType::UINT64:
212 enc->result_.emplace_back(pandasm::Create_LDAI(inst->CastToConstant()->GetInt64Value()));
213 DoSta(inst->GetDstReg(), enc->result_);
214 break;
215 case compiler::DataType::FLOAT64:
216 enc->result_.emplace_back(pandasm::Create_FLDAI(inst->CastToConstant()->GetDoubleValue()));
217 DoSta(inst->GetDstReg(), enc->result_);
218 break;
219 case compiler::DataType::INT32:
220 case compiler::DataType::UINT32:
221 enc->result_.emplace_back(pandasm::Create_LDAI(inst->CastToConstant()->GetInt32Value()));
222 DoSta(inst->GetDstReg(), enc->result_);
223 break;
224 default:
225 UNREACHABLE();
226 LOG(ERROR, BYTECODE_OPTIMIZER) << "VisitConstant with unknown type" << type;
227 enc->success_ = false;
228 }
229 }
230
EncodeSta(compiler::Register reg,compiler::DataType::Type type)231 void BytecodeGen::EncodeSta(compiler::Register reg, compiler::DataType::Type type)
232 {
233 pandasm::Opcode opc;
234 switch (type) {
235 case compiler::DataType::ANY:
236 opc = pandasm::Opcode::STA;
237 break;
238 default:
239 UNREACHABLE();
240 LOG(ERROR, BYTECODE_OPTIMIZER) << "EncodeSta with unknown type" << type;
241 success_ = false;
242 }
243 pandasm::Ins sta;
244 sta.opcode = opc;
245 sta.regs.emplace_back(reg);
246
247 result_.emplace_back(sta);
248 }
249
250 // NOLINTNEXTLINE(readability-function-size)
VisitIf(GraphVisitor * v,Inst * inst_base)251 void BytecodeGen::VisitIf(GraphVisitor *v, Inst *inst_base)
252 {
253 auto enc = static_cast<BytecodeGen *>(v);
254 auto inst = inst_base->CastToIf();
255 switch (inst->GetInputType(0)) {
256 case compiler::DataType::ANY: {
257 #if defined(ENABLE_BYTECODE_OPT) && defined(PANDA_WITH_ECMASCRIPT) && defined(ARK_INTRINSIC_SET)
258 IfEcma(v, inst);
259 break;
260 #else
261 [[fallthrough]];
262 #endif
263 }
264 default:
265 LOG(ERROR, BYTECODE_OPTIMIZER)
266 << "Codegen for " << compiler::GetOpcodeString(inst->GetOpcode()) << " failed";
267 enc->success_ = false;
268 break;
269 }
270 }
271
272 #if defined(ENABLE_BYTECODE_OPT) && defined(PANDA_WITH_ECMASCRIPT)
IsEcmaConstTemplate(Inst const * inst)273 static std::optional<coretypes::TaggedValue> IsEcmaConstTemplate(Inst const *inst)
274 {
275 if (inst->GetOpcode() != compiler::Opcode::CastValueToAnyType) {
276 return {};
277 }
278 auto cvat_inst = inst->CastToCastValueToAnyType();
279 if (!cvat_inst->GetInput(0).GetInst()->IsConst()) {
280 return {};
281 }
282 auto const_inst = cvat_inst->GetInput(0).GetInst()->CastToConstant();
283
284 switch (cvat_inst->GetAnyType()) {
285 case compiler::AnyBaseType::ECMASCRIPT_UNDEFINED_TYPE:
286 return coretypes::TaggedValue(coretypes::TaggedValue::VALUE_UNDEFINED);
287 case compiler::AnyBaseType::ECMASCRIPT_INT_TYPE:
288 return coretypes::TaggedValue(static_cast<int32_t>(const_inst->GetIntValue()));
289 case compiler::AnyBaseType::ECMASCRIPT_DOUBLE_TYPE:
290 return coretypes::TaggedValue(const_inst->GetDoubleValue());
291 case compiler::AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE:
292 return coretypes::TaggedValue(static_cast<bool>(const_inst->GetInt64Value() != 0));
293 case compiler::AnyBaseType::ECMASCRIPT_NULL_TYPE:
294 return coretypes::TaggedValue(coretypes::TaggedValue::VALUE_NULL);
295 default:
296 return {};
297 }
298 }
299
300 #if defined(ARK_INTRINSIC_SET)
IfEcma(GraphVisitor * v,compiler::IfInst * inst)301 void BytecodeGen::IfEcma(GraphVisitor *v, compiler::IfInst *inst)
302 {
303 auto enc = static_cast<BytecodeGen *>(v);
304
305 compiler::Register reg = compiler::INVALID_REG_ID;
306 coretypes::TaggedValue cmp_val;
307
308 auto test_lhs = IsEcmaConstTemplate(inst->GetInput(0).GetInst());
309 auto test_rhs = IsEcmaConstTemplate(inst->GetInput(1).GetInst());
310
311 if (test_lhs.has_value() && test_lhs->IsBoolean()) {
312 cmp_val = test_lhs.value();
313 reg = inst->GetSrcReg(1);
314 } else if (test_rhs.has_value() && test_rhs->IsBoolean()) {
315 cmp_val = test_rhs.value();
316 reg = inst->GetSrcReg(0);
317 } else {
318 LOG(ERROR, BYTECODE_OPTIMIZER) << "Codegen for " << compiler::GetOpcodeString(inst->GetOpcode()) << " failed";
319 enc->success_ = false;
320 return;
321 }
322
323 DoLda(reg, enc->result_);
324 switch (inst->GetCc()) {
325 case compiler::CC_EQ: {
326 if (cmp_val.IsTrue()) {
327 enc->result_.emplace_back(
328 pandasm::Create_ECMA_JTRUE(LabelName(inst->GetBasicBlock()->GetTrueSuccessor()->GetId())));
329 } else {
330 enc->result_.emplace_back(
331 pandasm::Create_ECMA_JFALSE(LabelName(inst->GetBasicBlock()->GetTrueSuccessor()->GetId())));
332 }
333 break;
334 }
335 case compiler::CC_NE: {
336 if (cmp_val.IsTrue()) {
337 enc->result_.emplace_back(pandasm::Create_ECMA_ISTRUE());
338 } else {
339 enc->result_.emplace_back(pandasm::Create_ECMA_ISFALSE());
340 }
341 enc->result_.emplace_back(
342 pandasm::Create_ECMA_JFALSE(LabelName(inst->GetBasicBlock()->GetTrueSuccessor()->GetId())));
343 break;
344 }
345 default:
346 LOG(ERROR, BYTECODE_OPTIMIZER)
347 << "Codegen for " << compiler::GetOpcodeString(inst->GetOpcode()) << " failed";
348 enc->success_ = false;
349 return;
350 }
351 }
352 #endif
353 #endif
354
VisitIfImm(GraphVisitor * v,Inst * inst_base)355 void BytecodeGen::VisitIfImm(GraphVisitor *v, Inst *inst_base)
356 {
357 auto inst = inst_base->CastToIfImm();
358 auto imm = inst->GetImm();
359 if (imm == 0) {
360 IfImmZero(v, inst_base);
361 return;
362 }
363 }
364
IfImmZero(GraphVisitor * v,Inst * inst_base)365 void BytecodeGen::IfImmZero(GraphVisitor *v, Inst *inst_base)
366 {
367 auto enc = static_cast<BytecodeGen *>(v);
368 auto inst = inst_base->CastToIfImm();
369 DoLda(inst->GetSrcReg(0), enc->result_);
370 auto label = LabelName(inst->GetBasicBlock()->GetTrueSuccessor()->GetId());
371 switch (inst->GetCc()) {
372 case compiler::CC_EQ:
373 enc->result_.emplace_back(pandasm::Create_JEQZ(label));
374 return;
375 case compiler::CC_NE:
376 enc->result_.emplace_back(pandasm::Create_JNEZ(label));
377 return;
378 default:
379 UNREACHABLE();
380 }
381 }
382
VisitLoadString(GraphVisitor * v,Inst * inst_base)383 void BytecodeGen::VisitLoadString(GraphVisitor *v, Inst *inst_base)
384 {
385 pandasm::Ins ins;
386 auto enc = static_cast<BytecodeGen *>(v);
387 auto inst = inst_base->CastToLoadString();
388 /* Do not emit unused code for Str -> CastValueToAnyType chains */
389 if (!HasUserPredicate(inst,
390 [](Inst const *i) { return i->GetOpcode() != compiler::Opcode::CastValueToAnyType; })) {
391 return;
392 }
393
394 enc->result_.emplace_back(pandasm::Create_LDA_STR(enc->ir_interface_->GetStringIdByOffset(inst->GetTypeId())));
395 if (inst->GetDstReg() != compiler::ACC_REG_ID) {
396 enc->result_.emplace_back(pandasm::Create_STA(inst->GetDstReg()));
397 }
398 }
399
VisitReturn(GraphVisitor * v,Inst * inst_base)400 void BytecodeGen::VisitReturn(GraphVisitor *v, Inst *inst_base)
401 {
402 pandasm::Ins ins;
403 auto enc = static_cast<BytecodeGen *>(v);
404 auto inst = inst_base->CastToReturn();
405 switch (inst->GetType()) {
406 case compiler::DataType::ANY: {
407 #if defined(ENABLE_BYTECODE_OPT) && defined(PANDA_WITH_ECMASCRIPT)
408 auto test_arg = IsEcmaConstTemplate(inst->GetInput(0).GetInst());
409 if (test_arg.has_value() && test_arg->IsUndefined()) {
410 enc->result_.emplace_back(pandasm::Create_RETURNUNDEFINED());
411 break;
412 }
413 #endif
414 DoLda(inst->GetSrcReg(0), enc->result_);
415 enc->result_.emplace_back(pandasm::Create_RETURN());
416 break;
417 }
418 default:
419 LOG(ERROR, BYTECODE_OPTIMIZER)
420 << "Codegen for " << compiler::GetOpcodeString(inst->GetOpcode()) << " failed";
421 enc->success_ = false;
422 }
423 }
424
VisitCastValueToAnyType(GraphVisitor * v,Inst * inst_base)425 void BytecodeGen::VisitCastValueToAnyType([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst_base)
426 {
427 auto enc = static_cast<BytecodeGen *>(v);
428
429 #if defined(ENABLE_BYTECODE_OPT) && defined(PANDA_WITH_ECMASCRIPT)
430 auto cvat = inst_base->CastToCastValueToAnyType();
431 switch (cvat->GetAnyType()) {
432 case compiler::AnyBaseType::ECMASCRIPT_NULL_TYPE:
433 enc->result_.emplace_back(pandasm::Create_LDNULL());
434 break;
435 case compiler::AnyBaseType::ECMASCRIPT_UNDEFINED_TYPE:
436 if (!HasUserPredicate(cvat,
437 [](Inst const *inst) { return inst->GetOpcode() != compiler::Opcode::Return; })) {
438 return;
439 }
440 enc->result_.emplace_back(pandasm::Create_LDUNDEFINED());
441 break;
442 case compiler::AnyBaseType::ECMASCRIPT_INT_TYPE: {
443 ASSERT(cvat->GetInput(0).GetInst()->IsConst());
444 auto input = cvat->GetInput(0).GetInst()->CastToConstant();
445 enc->result_.emplace_back(pandasm::Create_LDAI(input->GetIntValue()));
446 break;
447 }
448 case compiler::AnyBaseType::ECMASCRIPT_DOUBLE_TYPE: {
449 ASSERT(cvat->GetInput(0).GetInst()->IsConst());
450 auto input = cvat->GetInput(0).GetInst()->CastToConstant();
451 enc->result_.emplace_back(pandasm::Create_FLDAI(input->GetDoubleValue()));
452 break;
453 }
454 case compiler::AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE: {
455 ASSERT(cvat->GetInput(0).GetInst()->IsBoolConst());
456 auto input = cvat->GetInput(0).GetInst()->CastToConstant();
457 if (!HasUserPredicate(cvat, [](Inst const *inst) { return inst->GetOpcode() != compiler::Opcode::If; })) {
458 return;
459 }
460 uint64_t val = input->GetInt64Value();
461 if (val != 0) {
462 enc->result_.emplace_back(pandasm::Create_LDTRUE());
463 } else {
464 enc->result_.emplace_back(pandasm::Create_LDFALSE());
465 }
466 break;
467 }
468 case compiler::AnyBaseType::ECMASCRIPT_STRING_TYPE: {
469 auto input = cvat->GetInput(0).GetInst()->CastToLoadString();
470 enc->result_.emplace_back(
471 pandasm::Create_LDA_STR(enc->ir_interface_->GetStringIdByOffset(input->GetTypeId())));
472 break;
473 }
474 default:
475 return;
476 }
477 DoSta(cvat->GetDstReg(), enc->result_);
478 #else
479 LOG(ERROR, BYTECODE_OPTIMIZER) << "Codegen for " << compiler::GetOpcodeString(inst_base->GetOpcode()) << " failed";
480 enc->success_ = false;
481 #endif
482 }
483
484 // NOLINTNEXTLINE(readability-function-size)
VisitIntrinsic(GraphVisitor * visitor,Inst * inst_base)485 void BytecodeGen::VisitIntrinsic(GraphVisitor *visitor, Inst *inst_base)
486 {
487 ASSERT(inst_base->IsIntrinsic());
488 VisitEcma(visitor, inst_base);
489 }
490
VisitCatchPhi(GraphVisitor * visitor,Inst * inst)491 void BytecodeGen::VisitCatchPhi(GraphVisitor *visitor, Inst *inst)
492 {
493 // The Acc register stores the exception object.
494 // Create an STA instruction if the exception is used later in virtual registers.
495 if (inst->CastToCatchPhi()->IsAcc()) {
496 bool hasRealUsers = false;
497 for (auto &user : inst->GetUsers()) {
498 if (!user.GetInst()->IsSaveState()) {
499 hasRealUsers = true;
500 break;
501 }
502 }
503 if (hasRealUsers) {
504 auto enc = static_cast<BytecodeGen *>(visitor);
505 DoSta(inst->GetDstReg(), enc->result_);
506 }
507 }
508 }
509
510 #include "generated/codegen_intrinsics.cpp"
511 #include "generated/insn_selection.cpp"
512 } // namespace panda::bytecodeopt
513