• 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 "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