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