• 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::InsPtr> & result)23 void DoLda(compiler::Register reg, std::vector<pandasm::InsPtr> &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::InsPtr> & result)30 void DoSta(compiler::Register reg, std::vector<pandasm::InsPtr> &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 = 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_.emplace_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_.emplace_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     result_.emplace_back(pandasm::Create_MOV(sf.DstValue(), sf.SrcValue()));
170     return;
171 }
172 
VisitSpillFill(GraphVisitor * visitor,Inst * inst)173 void BytecodeGen::VisitSpillFill(GraphVisitor *visitor, Inst *inst)
174 {
175     auto *enc = static_cast<BytecodeGen *>(visitor);
176     for (auto sf : inst->CastToSpillFill()->GetSpillFills()) {
177         enc->EncodeSpillFillData(sf);
178     }
179 }
180 
181 template <typename UnaryPred>
HasUserPredicate(Inst * inst,UnaryPred p)182 bool HasUserPredicate(Inst *inst, UnaryPred p)
183 {
184     bool found = false;
185     for (auto const &u : inst->GetUsers()) {
186         if (p(u.GetInst())) {
187             found = true;
188             break;
189         }
190     }
191     return found;
192 }
193 
VisitConstant(GraphVisitor * visitor,Inst * inst)194 void BytecodeGen::VisitConstant(GraphVisitor *visitor, Inst *inst)
195 {
196     auto *enc = static_cast<BytecodeGen *>(visitor);
197     auto type = inst->GetType();
198 
199     /* Do not emit unused code for Const -> CastValueToAnyType chains */
200     if (!HasUserPredicate(inst,
201                           [](Inst const *i) { return i->GetOpcode() != compiler::Opcode::CastValueToAnyType; })) {
202         return;
203     }
204 
205     switch (type) {
206         case compiler::DataType::INT64:
207         case compiler::DataType::UINT64:
208             enc->result_.emplace_back(pandasm::Create_LDAI(inst->CastToConstant()->GetInt64Value()));
209             DoSta(inst->GetDstReg(), enc->result_);
210             break;
211         case compiler::DataType::FLOAT64:
212             enc->result_.emplace_back(pandasm::Create_FLDAI(inst->CastToConstant()->GetDoubleValue()));
213             DoSta(inst->GetDstReg(), enc->result_);
214             break;
215         case compiler::DataType::INT32:
216         case compiler::DataType::UINT32:
217             enc->result_.emplace_back(pandasm::Create_LDAI(inst->CastToConstant()->GetInt32Value()));
218             DoSta(inst->GetDstReg(), enc->result_);
219             break;
220         default:
221             UNREACHABLE();
222             LOG(ERROR, BYTECODE_OPTIMIZER) << "VisitConstant with unknown type" << type;
223             enc->success_ = false;
224     }
225 }
226 
EncodeSta(compiler::Register reg,compiler::DataType::Type type)227 void BytecodeGen::EncodeSta(compiler::Register reg, compiler::DataType::Type type)
228 {
229     pandasm::Opcode opc;
230     switch (type) {
231         case compiler::DataType::ANY:
232             opc = pandasm::Opcode::STA;
233             break;
234         default:
235             UNREACHABLE();
236             LOG(ERROR, BYTECODE_OPTIMIZER) << "EncodeSta with unknown type" << type;
237             success_ = false;
238     }
239 
240     result_.emplace_back(pandasm::Create_STA(reg));
241 }
242 
243 // NOLINTNEXTLINE(readability-function-size)
VisitIf(GraphVisitor * v,Inst * inst_base)244 void BytecodeGen::VisitIf(GraphVisitor *v, Inst *inst_base)
245 {
246     auto enc = static_cast<BytecodeGen *>(v);
247     auto inst = inst_base->CastToIf();
248     switch (inst->GetInputType(0)) {
249         case compiler::DataType::ANY: {
250 #if defined(ENABLE_BYTECODE_OPT) && defined(PANDA_WITH_ECMASCRIPT) && defined(ARK_INTRINSIC_SET)
251             IfEcma(v, inst);
252             break;
253 #else
254             [[fallthrough]];
255 #endif
256         }
257         default:
258             LOG(ERROR, BYTECODE_OPTIMIZER)
259                 << "Codegen for " << compiler::GetOpcodeString(inst->GetOpcode()) << " failed";
260             enc->success_ = false;
261             break;
262     }
263 }
264 
265 #if defined(ENABLE_BYTECODE_OPT) && defined(PANDA_WITH_ECMASCRIPT)
IsEcmaConstTemplate(Inst const * inst)266 static std::optional<coretypes::TaggedValue> IsEcmaConstTemplate(Inst const *inst)
267 {
268     if (inst->GetOpcode() != compiler::Opcode::CastValueToAnyType) {
269         return {};
270     }
271     auto cvat_inst = inst->CastToCastValueToAnyType();
272     if (!cvat_inst->GetInput(0).GetInst()->IsConst()) {
273         return {};
274     }
275     auto const_inst = cvat_inst->GetInput(0).GetInst()->CastToConstant();
276 
277     switch (cvat_inst->GetAnyType()) {
278         case compiler::AnyBaseType::ECMASCRIPT_UNDEFINED_TYPE:
279             return coretypes::TaggedValue(coretypes::TaggedValue::VALUE_UNDEFINED);
280         case compiler::AnyBaseType::ECMASCRIPT_INT_TYPE:
281             return coretypes::TaggedValue(static_cast<int32_t>(const_inst->GetIntValue()));
282         case compiler::AnyBaseType::ECMASCRIPT_DOUBLE_TYPE:
283             return coretypes::TaggedValue(const_inst->GetDoubleValue());
284         case compiler::AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE:
285             return coretypes::TaggedValue(static_cast<bool>(const_inst->GetInt64Value() != 0));
286         case compiler::AnyBaseType::ECMASCRIPT_NULL_TYPE:
287             return coretypes::TaggedValue(coretypes::TaggedValue::VALUE_NULL);
288         default:
289             return {};
290     }
291 }
292 
293 #if defined(ARK_INTRINSIC_SET)
IfEcma(GraphVisitor * v,compiler::IfInst * inst)294 void BytecodeGen::IfEcma(GraphVisitor *v, compiler::IfInst *inst)
295 {
296     auto enc = static_cast<BytecodeGen *>(v);
297 
298     compiler::Register reg = compiler::INVALID_REG_ID;
299     coretypes::TaggedValue cmp_val;
300 
301     auto test_lhs = IsEcmaConstTemplate(inst->GetInput(0).GetInst());
302     auto test_rhs = IsEcmaConstTemplate(inst->GetInput(1).GetInst());
303 
304     if (test_lhs.has_value() && test_lhs->IsBoolean()) {
305         cmp_val = test_lhs.value();
306         reg = inst->GetSrcReg(1);
307     } else if (test_rhs.has_value() && test_rhs->IsBoolean()) {
308         cmp_val = test_rhs.value();
309         reg = inst->GetSrcReg(0);
310     } else {
311         LOG(ERROR, BYTECODE_OPTIMIZER) << "Codegen for " << compiler::GetOpcodeString(inst->GetOpcode()) << " failed";
312         enc->success_ = false;
313         return;
314     }
315 
316     DoLda(reg, enc->result_);
317     switch (inst->GetCc()) {
318         case compiler::CC_EQ: {
319             if (cmp_val.IsTrue()) {
320                 enc->result_.emplace_back(
321                     pandasm::Create_ECMA_JTRUE(LabelName(inst->GetBasicBlock()->GetTrueSuccessor()->GetId())));
322             } else {
323                 enc->result_.emplace_back(
324                     pandasm::Create_ECMA_JFALSE(LabelName(inst->GetBasicBlock()->GetTrueSuccessor()->GetId())));
325             }
326             break;
327         }
328         case compiler::CC_NE: {
329             if (cmp_val.IsTrue()) {
330                 enc->result_.emplace_back(pandasm::Create_ECMA_ISTRUE());
331             } else {
332                 enc->result_.emplace_back(pandasm::Create_ECMA_ISFALSE());
333             }
334             enc->result_.emplace_back(
335                 pandasm::Create_ECMA_JFALSE(LabelName(inst->GetBasicBlock()->GetTrueSuccessor()->GetId())));
336             break;
337         }
338         default:
339             LOG(ERROR, BYTECODE_OPTIMIZER)
340                 << "Codegen for " << compiler::GetOpcodeString(inst->GetOpcode()) << " failed";
341             enc->success_ = false;
342             return;
343     }
344 }
345 #endif
346 #endif
347 
VisitIfImm(GraphVisitor * v,Inst * inst_base)348 void BytecodeGen::VisitIfImm(GraphVisitor *v, Inst *inst_base)
349 {
350     auto inst = inst_base->CastToIfImm();
351     auto imm = inst->GetImm();
352     if (imm == 0) {
353         IfImmZero(v, inst_base);
354         return;
355     }
356 }
357 
IfImmZero(GraphVisitor * v,Inst * inst_base)358 void BytecodeGen::IfImmZero(GraphVisitor *v, Inst *inst_base)
359 {
360     auto enc = static_cast<BytecodeGen *>(v);
361     auto inst = inst_base->CastToIfImm();
362     DoLda(inst->GetSrcReg(0), enc->result_);
363     auto label = LabelName(inst->GetBasicBlock()->GetTrueSuccessor()->GetId());
364     switch (inst->GetCc()) {
365         case compiler::CC_EQ:
366             enc->result_.emplace_back(pandasm::Create_JEQZ(label));
367             return;
368         case compiler::CC_NE:
369             enc->result_.emplace_back(pandasm::Create_JNEZ(label));
370             return;
371         default:
372             UNREACHABLE();
373     }
374 }
375 
VisitLoadString(GraphVisitor * v,Inst * inst_base)376 void BytecodeGen::VisitLoadString(GraphVisitor *v, Inst *inst_base)
377 {
378     auto enc = static_cast<BytecodeGen *>(v);
379     auto inst = inst_base->CastToLoadString();
380     /* Do not emit unused code for Str -> CastValueToAnyType chains */
381     if (!HasUserPredicate(inst,
382                           [](Inst const *i) { return i->GetOpcode() != compiler::Opcode::CastValueToAnyType; })) {
383         return;
384     }
385 
386     enc->result_.emplace_back(pandasm::Create_LDA_STR(enc->ir_interface_->GetStringIdByOffset(inst->GetTypeId())));
387     if (inst->GetDstReg() != compiler::ACC_REG_ID) {
388         enc->result_.emplace_back(pandasm::Create_STA(inst->GetDstReg()));
389     }
390 }
391 
VisitReturn(GraphVisitor * v,Inst * inst_base)392 void BytecodeGen::VisitReturn(GraphVisitor *v, Inst *inst_base)
393 {
394     auto enc = static_cast<BytecodeGen *>(v);
395     auto inst = inst_base->CastToReturn();
396     switch (inst->GetType()) {
397         case compiler::DataType::ANY: {
398 #if defined(ENABLE_BYTECODE_OPT) && defined(PANDA_WITH_ECMASCRIPT)
399             auto test_arg = IsEcmaConstTemplate(inst->GetInput(0).GetInst());
400             if (test_arg.has_value() && test_arg->IsUndefined()) {
401                 enc->result_.emplace_back(pandasm::Create_RETURNUNDEFINED());
402                 break;
403             }
404 #endif
405             DoLda(inst->GetSrcReg(0), enc->result_);
406             enc->result_.emplace_back(pandasm::Create_RETURN());
407             break;
408         }
409         default:
410             LOG(ERROR, BYTECODE_OPTIMIZER)
411                 << "Codegen for " << compiler::GetOpcodeString(inst->GetOpcode()) << " failed";
412             enc->success_ = false;
413     }
414 }
415 
VisitCastValueToAnyType(GraphVisitor * v,Inst * inst_base)416 void BytecodeGen::VisitCastValueToAnyType([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst_base)
417 {
418     auto enc = static_cast<BytecodeGen *>(v);
419 
420 #if defined(ENABLE_BYTECODE_OPT) && defined(PANDA_WITH_ECMASCRIPT)
421     auto cvat = inst_base->CastToCastValueToAnyType();
422     switch (cvat->GetAnyType()) {
423         case compiler::AnyBaseType::ECMASCRIPT_NULL_TYPE:
424             enc->result_.emplace_back(pandasm::Create_LDNULL());
425             break;
426         case compiler::AnyBaseType::ECMASCRIPT_UNDEFINED_TYPE:
427             if (!HasUserPredicate(cvat,
428                                   [](Inst const *inst) { return inst->GetOpcode() != compiler::Opcode::Return; })) {
429                 return;
430             }
431             enc->result_.emplace_back(pandasm::Create_LDUNDEFINED());
432             break;
433         case compiler::AnyBaseType::ECMASCRIPT_INT_TYPE: {
434             ASSERT(cvat->GetInput(0).GetInst()->IsConst());
435             auto input = cvat->GetInput(0).GetInst()->CastToConstant();
436             enc->result_.emplace_back(pandasm::Create_LDAI(input->GetIntValue()));
437             break;
438         }
439         case compiler::AnyBaseType::ECMASCRIPT_DOUBLE_TYPE: {
440             ASSERT(cvat->GetInput(0).GetInst()->IsConst());
441             auto input = cvat->GetInput(0).GetInst()->CastToConstant();
442             enc->result_.emplace_back(pandasm::Create_FLDAI(input->GetDoubleValue()));
443             break;
444         }
445         case compiler::AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE: {
446             ASSERT(cvat->GetInput(0).GetInst()->IsBoolConst());
447             auto input = cvat->GetInput(0).GetInst()->CastToConstant();
448             if (!HasUserPredicate(cvat, [](Inst const *inst) { return inst->GetOpcode() != compiler::Opcode::If; })) {
449                 return;
450             }
451             uint64_t val = input->GetInt64Value();
452             if (val != 0) {
453                 enc->result_.emplace_back(pandasm::Create_LDTRUE());
454             } else {
455                 enc->result_.emplace_back(pandasm::Create_LDFALSE());
456             }
457             break;
458         }
459         case compiler::AnyBaseType::ECMASCRIPT_STRING_TYPE: {
460             auto input = cvat->GetInput(0).GetInst()->CastToLoadString();
461             enc->result_.emplace_back(
462                 pandasm::Create_LDA_STR(enc->ir_interface_->GetStringIdByOffset(input->GetTypeId())));
463             break;
464         }
465         default:
466             return;
467     }
468     DoSta(cvat->GetDstReg(), enc->result_);
469 #else
470     LOG(ERROR, BYTECODE_OPTIMIZER) << "Codegen for " << compiler::GetOpcodeString(inst_base->GetOpcode()) << " failed";
471     enc->success_ = false;
472 #endif
473 }
474 
475 // NOLINTNEXTLINE(readability-function-size)
VisitIntrinsic(GraphVisitor * visitor,Inst * inst_base)476 void BytecodeGen::VisitIntrinsic(GraphVisitor *visitor, Inst *inst_base)
477 {
478     ASSERT(inst_base->IsIntrinsic());
479     VisitEcma(visitor, inst_base);
480 }
481 
VisitCatchPhi(GraphVisitor * visitor,Inst * inst)482 void BytecodeGen::VisitCatchPhi(GraphVisitor *visitor, Inst *inst)
483 {
484     // The Acc register stores the exception object.
485     // Create an STA instruction if the exception is used later in virtual registers.
486     if (inst->CastToCatchPhi()->IsAcc()) {
487         bool hasRealUsers = false;
488         for (auto &user : inst->GetUsers()) {
489             if (!user.GetInst()->IsSaveState()) {
490                 hasRealUsers = true;
491                 break;
492             }
493         }
494         if (hasRealUsers) {
495             auto enc = static_cast<BytecodeGen *>(visitor);
496             DoSta(inst->GetDstReg(), enc->result_);
497         }
498     }
499 }
500 
501 #include "generated/codegen_intrinsics.cpp"
502 #include "generated/insn_selection.cpp"
503 }  // namespace panda::bytecodeopt
504