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