• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2024 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 
16 #ifndef ES2PANDA_COMPILER_CORE_ETSGEN_H
17 #define ES2PANDA_COMPILER_CORE_ETSGEN_H
18 
19 #include "ir/astNode.h"
20 #include "varbinder/ETSBinder.h"
21 #include "compiler/core/codeGen.h"
22 #include "compiler/core/ETSfunction.h"
23 #include "compiler/core/targetTypeContext.h"
24 #include "checker/ETSchecker.h"
25 #include "util/helpers.h"
26 
27 namespace panda::es2panda::compiler {
28 
29 class ETSGen final : public CodeGen {
30 public:
31     explicit ETSGen(ArenaAllocator *allocator, RegSpiller *spiller, CompilerContext *context,
32                     varbinder::FunctionScope *scope, ProgramElement *programElement, AstCompiler *astcompiler) noexcept;
33 
34     [[nodiscard]] const checker::ETSChecker *Checker() const noexcept;
35     [[nodiscard]] const varbinder::ETSBinder *VarBinder() const noexcept;
36     [[nodiscard]] const checker::Type *ReturnType() const noexcept;
37     [[nodiscard]] const checker::ETSObjectType *ContainingObjectType() const noexcept;
38 
39     [[nodiscard]] VReg &Acc() noexcept;
40     [[nodiscard]] VReg Acc() const noexcept;
41 
42     void SetAccumulatorType(const checker::Type *type);
43     [[nodiscard]] const checker::Type *GetAccumulatorType() const;
44     void CompileAndCheck(const ir::Expression *expr);
45 
46     [[nodiscard]] VReg StoreException(const ir::AstNode *node);
47     void ApplyConversionAndStoreAccumulator(const ir::AstNode *node, VReg vreg, const checker::Type *targetType);
48     void StoreAccumulator(const ir::AstNode *node, VReg vreg);
49     void LoadAccumulator(const ir::AstNode *node, VReg vreg);
50     [[nodiscard]] IRNode *AllocMov(const ir::AstNode *node, VReg vd, VReg vs) override;
51     [[nodiscard]] IRNode *AllocMov(const ir::AstNode *node, OutVReg vd, VReg vs) override;
52     void MoveVreg(const ir::AstNode *node, VReg vd, VReg vs);
53 
54     [[nodiscard]] checker::Type const *TypeForVar(varbinder::Variable const *var) const noexcept override;
55 
56     void LoadVar(const ir::AstNode *node, varbinder::Variable const *var);
57     void LoadDynamicModuleVariable(const ir::AstNode *node, varbinder::Variable const *var);
58     void LoadDynamicNamespaceVariable(const ir::AstNode *node, varbinder::Variable const *var);
59     void StoreVar(const ir::AstNode *node, const varbinder::ConstScopeFindResult &result);
60 
61     void LoadStaticProperty(const ir::AstNode *node, const checker::Type *propType, const util::StringView &fullName);
62     void StoreStaticProperty(const ir::AstNode *node, const checker::Type *propType, const util::StringView &fullName);
63 
64     void StoreStaticOwnProperty(const ir::AstNode *node, const checker::Type *propType, const util::StringView &name);
65     [[nodiscard]] util::StringView FormClassPropReference(const checker::ETSObjectType *classType,
66                                                           const util::StringView &name);
67 
68     void StoreProperty(const ir::AstNode *node, const checker::Type *propType, VReg objReg,
69                        const util::StringView &name);
70     void LoadProperty(const ir::AstNode *node, const checker::Type *propType, VReg objReg,
71                       const util::StringView &fullName);
72     void StorePropertyDynamic(const ir::AstNode *node, const checker::Type *propType, VReg objReg,
73                               const util::StringView &name);
74     void LoadPropertyDynamic(const ir::AstNode *node, const checker::Type *propType, VReg objReg,
75                              const util::StringView &propName);
76 
77     void StoreElementDynamic(const ir::AstNode *node, VReg objectReg, VReg index);
78     void LoadElementDynamic(const ir::AstNode *node, VReg objectReg);
79 
80     void StoreUnionProperty(const ir::AstNode *node, VReg objReg, const util::StringView &name);
81     void LoadUnionProperty(const ir::AstNode *node, const checker::Type *propType, VReg objReg,
82                            const util::StringView &propName);
83 
84     void LoadUndefinedDynamic(const ir::AstNode *node, Language lang);
85 
86     void LoadThis(const ir::AstNode *node);
87     [[nodiscard]] VReg GetThisReg() const;
88 
89     void LoadDefaultValue(const ir::AstNode *node, const checker::Type *type);
90     void EmitReturnVoid(const ir::AstNode *node);
91     void LoadBuiltinVoid(const ir::AstNode *node);
92     void ReturnAcc(const ir::AstNode *node);
93 
94     void EmitIsInstance(const ir::AstNode *node, VReg objReg);
95 
96     void Binary(const ir::AstNode *node, lexer::TokenType op, VReg lhs);
97     void Unary(const ir::AstNode *node, lexer::TokenType op);
98     void Update(const ir::AstNode *node, lexer::TokenType op);
99     void UpdateBigInt(const ir::Expression *node, VReg arg, lexer::TokenType op);
100 
101     bool TryLoadConstantExpression(const ir::Expression *node);
102     void Condition(const ir::AstNode *node, lexer::TokenType op, VReg lhs, Label *ifFalse);
103 
104     template <typename CondCompare, bool BEFORE_LOGICAL_NOT>
ResolveConditionalResultFloat(const ir::AstNode * node,Label * realEndLabel)105     void ResolveConditionalResultFloat(const ir::AstNode *node, Label *realEndLabel)
106     {
107         auto type = node->IsExpression() && !node->AsExpression()->IsUnaryExpression() ? node->AsExpression()->TsType()
108                                                                                        : GetAccumulatorType();
109         VReg tmpReg = AllocReg();
110         StoreAccumulator(node, tmpReg);
111         if (type->IsFloatType()) {
112             FloatIsNaN(node);
113         } else {
114             DoubleIsNaN(node);
115         }
116         Sa().Emit<Xori>(node, 1);
117 
118         BranchIfFalse(node, realEndLabel);
119         LoadAccumulator(node, tmpReg);
120         VReg zeroReg = AllocReg();
121 
122         if (type->IsFloatType()) {
123             MoveImmediateToRegister(node, zeroReg, checker::TypeFlag::FLOAT, 0);
124             BinaryNumberComparison<Fcmpl, Jeqz>(node, zeroReg, realEndLabel);
125         } else {
126             MoveImmediateToRegister(node, zeroReg, checker::TypeFlag::DOUBLE, 0);
127             BinaryNumberComparison<FcmplWide, Jeqz>(node, zeroReg, realEndLabel);
128         }
129     }
130 
131     template <typename CondCompare, bool BEFORE_LOGICAL_NOT, bool USE_FALSE_LABEL>
ResolveConditionalResultNumeric(const ir::AstNode * node,Label * ifFalse,Label ** end)132     void ResolveConditionalResultNumeric(const ir::AstNode *node, [[maybe_unused]] Label *ifFalse, Label **end)
133     {
134         auto type = node->IsExpression() && !node->AsExpression()->IsUnaryExpression() ? node->AsExpression()->TsType()
135                                                                                        : GetAccumulatorType();
136 
137         auto realEndLabel = [end, ifFalse, this](bool useFalseLabel) {
138             if (useFalseLabel) {
139                 return ifFalse;
140             }
141             if ((*end) == nullptr) {
142                 (*end) = AllocLabel();
143             }
144             return (*end);
145         }(USE_FALSE_LABEL);
146         if (type->IsDoubleType() || type->IsFloatType()) {
147             ResolveConditionalResultFloat<CondCompare, BEFORE_LOGICAL_NOT>(node, realEndLabel);
148         }
149         if (type->IsLongType()) {
150             VReg zeroReg = AllocReg();
151             MoveImmediateToRegister(node, zeroReg, checker::TypeFlag::LONG, 0);
152             BinaryNumberComparison<CmpWide, CondCompare>(node, zeroReg, realEndLabel);
153         }
154         if constexpr (BEFORE_LOGICAL_NOT) {
155             Label *zeroPrimitive = AllocLabel();
156             BranchIfFalse(node, zeroPrimitive);
157             ToBinaryResult(node, zeroPrimitive);
158         }
159     }
160 
161     template <typename CondCompare, bool BEFORE_LOGICAL_NOT>
ResolveConditionalResultObject(const ir::AstNode * node)162     void ResolveConditionalResultObject(const ir::AstNode *node)
163     {
164         auto type = node->IsExpression() && !node->AsExpression()->IsUnaryExpression() ? node->AsExpression()->TsType()
165                                                                                        : GetAccumulatorType();
166         if (type->IsETSStringType()) {
167             LoadStringLength(node);
168             if constexpr (BEFORE_LOGICAL_NOT) {
169                 Label *zeroLenth = AllocLabel();
170                 BranchIfFalse(node, zeroLenth);
171                 ToBinaryResult(node, zeroLenth);
172             }
173         } else if (node->IsExpression() && node->AsExpression()->IsIdentifier() &&
174                    node->AsExpression()->AsIdentifier()->Variable()->HasFlag(varbinder::VariableFlags::VAR)) {
175             Label *isString = AllocLabel();
176             Label *end = AllocLabel();
177             compiler::VReg objReg = AllocReg();
178             StoreAccumulator(node, objReg);
179 
180             Sa().Emit<Isinstance>(node, Checker()->GlobalBuiltinETSStringType()->AsETSStringType()->AssemblerName());
181             BranchIfTrue(node, isString);
182             Sa().Emit<Ldai>(node, 1);
183             Branch(node, end);
184             SetLabel(node, isString);
185             LoadAccumulator(node, objReg);
186             CastToString(node);
187             LoadStringLength(node);
188             if constexpr (BEFORE_LOGICAL_NOT) {
189                 Label *zeroLenth = AllocLabel();
190                 BranchIfFalse(node, zeroLenth);
191                 ToBinaryResult(node, zeroLenth);
192             }
193             SetLabel(node, end);
194         } else {
195             Sa().Emit<Ldai>(node, 1);
196         }
197     }
198 
199     template <typename CondCompare, bool BEFORE_LOGICAL_NOT, bool USE_FALSE_LABEL>
ResolveConditionalResultExpression(const ir::AstNode * node,Label * ifFalse)200     void ResolveConditionalResultExpression(const ir::AstNode *node, [[maybe_unused]] Label *ifFalse)
201     {
202         auto exprNode = node->AsExpression();
203         if (Checker()->IsNullLikeOrVoidExpression(exprNode)) {
204             if constexpr (USE_FALSE_LABEL) {
205                 Branch(node, ifFalse);
206             } else {
207                 Sa().Emit<Ldai>(node, 0);
208             }
209             return;
210         }
211     }
212 
213     template <typename CondCompare, bool BEFORE_LOGICAL_NOT, bool USE_FALSE_LABEL>
ResolveConditionalResult(const ir::AstNode * node,Label * ifFalse)214     void ResolveConditionalResult(const ir::AstNode *node, [[maybe_unused]] Label *ifFalse)
215     {
216         auto type = node->IsExpression() && !node->AsExpression()->IsUnaryExpression() ? node->AsExpression()->TsType()
217                                                                                        : GetAccumulatorType();
218         if (type->IsETSBooleanType()) {
219             return;
220         }
221 
222         if (node->IsExpression()) {
223             ResolveConditionalResultExpression<CondCompare, BEFORE_LOGICAL_NOT, USE_FALSE_LABEL>(node, ifFalse);
224         }
225         Label *ifNullish {nullptr};
226         Label *end {nullptr};
227         if (Checker()->MayHaveNulllikeValue(type)) {
228             if constexpr (USE_FALSE_LABEL) {
229                 BranchIfNullish(node, ifFalse);
230             } else {
231                 ifNullish = AllocLabel();
232                 end = AllocLabel();
233                 BranchIfNullish(node, ifNullish);
234             }
235         }
236         if (type->IsETSArrayType()) {
237             compiler::VReg objReg = AllocReg();
238             StoreAccumulator(node, objReg);
239             LoadArrayLength(node, objReg);
240         } else if (type->IsETSObjectType()) {
241             ResolveConditionalResultObject<CondCompare, BEFORE_LOGICAL_NOT>(node);
242         } else {
243             ResolveConditionalResultNumeric<CondCompare, BEFORE_LOGICAL_NOT, USE_FALSE_LABEL>(node, ifFalse, &end);
244         }
245         if (ifNullish != nullptr) {
246             Branch(node, end);
247             SetLabel(node, ifNullish);
248             Sa().Emit<Ldai>(node, 0);
249         }
250         if (end != nullptr) {
251             SetLabel(node, end);
252         }
253     }
254 
255     template <bool BEFORE_LOGICAL_NOT = false, bool FALSE_LABEL_EXISTED = true>
256     void ResolveConditionalResultIfFalse(const ir::AstNode *node, Label *ifFalse = nullptr)
257     {
258         ResolveConditionalResult<Jeqz, BEFORE_LOGICAL_NOT, FALSE_LABEL_EXISTED>(node, ifFalse);
259     }
260 
261     template <bool BEFORE_LOGICAL_NOT = false, bool FALSE_LABEL_EXISTED = true>
262     void ResolveConditionalResultIfTrue(const ir::AstNode *node, Label *ifFalse = nullptr)
263     {
264         ResolveConditionalResult<Jnez, BEFORE_LOGICAL_NOT, FALSE_LABEL_EXISTED>(node, ifFalse);
265     }
266 
BranchIfFalse(const ir::AstNode * node,Label * ifFalse)267     void BranchIfFalse(const ir::AstNode *node, Label *ifFalse)
268     {
269         Sa().Emit<Jeqz>(node, ifFalse);
270     }
271 
BranchIfTrue(const ir::AstNode * node,Label * ifTrue)272     void BranchIfTrue(const ir::AstNode *node, Label *ifTrue)
273     {
274         Sa().Emit<Jnez>(node, ifTrue);
275     }
276 
BranchIfNull(const ir::AstNode * node,Label * ifNull)277     void BranchIfNull(const ir::AstNode *node, Label *ifNull)
278     {
279         Sa().Emit<JeqzObj>(node, ifNull);
280     }
281 
BranchIfNotNull(const ir::AstNode * node,Label * ifNotNull)282     void BranchIfNotNull(const ir::AstNode *node, Label *ifNotNull)
283     {
284         Sa().Emit<JnezObj>(node, ifNotNull);
285     }
286 
287     void BranchIfNullish(const ir::AstNode *node, Label *ifNullish);
288     void BranchIfNotNullish(const ir::AstNode *node, Label *ifNotNullish);
289     void ConvertToNonNullish(const ir::AstNode *node);
290 
JumpTo(const ir::AstNode * node,Label * labelTo)291     void JumpTo(const ir::AstNode *node, Label *labelTo)
292     {
293         Sa().Emit<Jmp>(node, labelTo);
294     }
295 
EmitThrow(const ir::AstNode * node,VReg err)296     void EmitThrow(const ir::AstNode *node, VReg err)
297     {
298         Ra().Emit<Throw>(node, err);
299     }
300 
301     void EmitNullishException(const ir::AstNode *node);
302     void EmitNullishGuardian(const ir::AstNode *node);
303 
304     template <typename F>
EmitMaybeOptional(const ir::Expression * node,F const & compile,bool isOptional)305     void EmitMaybeOptional(const ir::Expression *node, F const &compile, bool isOptional)
306     {
307         auto *const type = GetAccumulatorType();
308 
309         if (!Checker()->MayHaveNulllikeValue(type)) {
310             compile();
311         } else if (type->IsETSNullLike()) {
312             if (isOptional) {
313                 LoadAccumulatorUndefined(node);
314             } else {  // NOTE: vpukhov. should be a CTE
315                 EmitNullishException(node);
316                 LoadAccumulatorUndefined(node);
317             }
318             SetAccumulatorType(node->TsType());
319         } else if (!isOptional) {  // NOTE: vpukhov. should be a CTE
320             EmitNullishGuardian(node);
321             compile();
322         } else {
323             compiler::Label *ifNotNullish = AllocLabel();
324             compiler::Label *endLabel = AllocLabel();
325 
326             BranchIfNotNullish(node, ifNotNullish);
327             LoadAccumulatorUndefined(node);
328             Branch(node, endLabel);
329 
330             SetLabel(node, ifNotNullish);
331             SetAccumulatorType(type);
332             ConvertToNonNullish(node);
333             compile();
334             ApplyConversion(node, node->TsType());
335             SetLabel(node, endLabel);
336             SetAccumulatorType(node->TsType());
337         }
338     }
339 
340     void ThrowException(const ir::Expression *expr);
341     bool ExtendWithFinalizer(ir::AstNode *node, const ir::AstNode *originalNode, Label *prevFinnaly = nullptr);
342 
343     void Negate(const ir::AstNode *node);
344     void LogicalNot(const ir::AstNode *node);
345 
LoadAccumulatorByte(const ir::AstNode * node,int8_t number)346     void LoadAccumulatorByte(const ir::AstNode *node, int8_t number)
347     {
348         LoadAccumulatorNumber<int8_t>(node, number, checker::TypeFlag::BYTE);
349     }
350 
LoadAccumulatorShort(const ir::AstNode * node,int16_t number)351     void LoadAccumulatorShort(const ir::AstNode *node, int16_t number)
352     {
353         LoadAccumulatorNumber<int16_t>(node, number, checker::TypeFlag::SHORT);
354     }
355 
LoadAccumulatorInt(const ir::AstNode * node,int32_t number)356     void LoadAccumulatorInt(const ir::AstNode *node, int32_t number)
357     {
358         LoadAccumulatorNumber<int32_t>(node, number, checker::TypeFlag::INT);
359     }
360 
LoadAccumulatorWideInt(const ir::AstNode * node,int64_t number)361     void LoadAccumulatorWideInt(const ir::AstNode *node, int64_t number)
362     {
363         LoadAccumulatorNumber<int64_t>(node, number, checker::TypeFlag::LONG);
364     }
365 
LoadAccumulatorFloat(const ir::AstNode * node,float number)366     void LoadAccumulatorFloat(const ir::AstNode *node, float number)
367     {
368         LoadAccumulatorNumber<float>(node, number, checker::TypeFlag::FLOAT);
369     }
370 
LoadAccumulatorDouble(const ir::AstNode * node,double number)371     void LoadAccumulatorDouble(const ir::AstNode *node, double number)
372     {
373         LoadAccumulatorNumber<double>(node, number, checker::TypeFlag::DOUBLE);
374     }
375 
LoadAccumulatorBoolean(const ir::AstNode * node,bool value)376     void LoadAccumulatorBoolean(const ir::AstNode *node, bool value)
377     {
378         Sa().Emit<Ldai>(node, value ? 1 : 0);
379         SetAccumulatorType(Checker()->GlobalETSBooleanType());
380         ApplyConversion(node, nullptr);
381     }
382 
LoadAccumulatorString(const ir::AstNode * node,util::StringView str)383     void LoadAccumulatorString(const ir::AstNode *node, util::StringView str)
384     {
385         Sa().Emit<LdaStr>(node, str);
386         SetAccumulatorType(Checker()->GlobalETSStringLiteralType());
387     }
388 
LoadAccumulatorBigInt(const ir::AstNode * node,util::StringView str)389     void LoadAccumulatorBigInt(const ir::AstNode *node, util::StringView str)
390     {
391         Sa().Emit<LdaStr>(node, str);
392         SetAccumulatorType(Checker()->GlobalETSBigIntType());
393     }
394 
LoadAccumulatorNull(const ir::AstNode * node,const checker::Type * type)395     void LoadAccumulatorNull(const ir::AstNode *node, const checker::Type *type)
396     {
397         Sa().Emit<LdaNull>(node);
398         SetAccumulatorType(type);
399     }
400 
LoadAccumulatorUndefined(const ir::AstNode * node)401     void LoadAccumulatorUndefined([[maybe_unused]] const ir::AstNode *node)
402     {
403 #ifdef PANDA_WITH_ETS
404         Sa().Emit<EtsLdundefined>(node);
405         SetAccumulatorType(Checker()->GlobalETSUndefinedType());
406 #else
407         UNREACHABLE();
408 #endif  // PANDA_WITH_ETS
409     }
410 
LoadAccumulatorChar(const ir::AstNode * node,char16_t value)411     void LoadAccumulatorChar(const ir::AstNode *node, char16_t value)
412     {
413         Sa().Emit<Ldai>(node, value);
414         SetAccumulatorType(Checker()->GlobalCharType());
415         ApplyConversion(node);
416     }
417 
418     void LoadAccumulatorDynamicModule(const ir::AstNode *node, const ir::ETSImportDeclaration *import);
419 
420     void ApplyBoxingConversion(const ir::AstNode *node);
421     void ApplyUnboxingConversion(const ir::AstNode *node);
ApplyConversion(const ir::AstNode * node)422     void ApplyConversion(const ir::AstNode *node)
423     {
424         if (targetType_ != nullptr) {
425             ApplyConversion(node, targetType_);
426         }
427     }
428     void ApplyConversionCast(const ir::AstNode *node, const checker::Type *targetType);
429     void ApplyConversion(const ir::AstNode *node, const checker::Type *targetType);
430     void ApplyCast(const ir::AstNode *node, const checker::Type *targetType);
431     void EmitUnboxingConversion(const ir::AstNode *node);
432     void EmitBoxingConversion(const ir::AstNode *node);
433     void SwapBinaryOpArgs(const ir::AstNode *node, VReg lhs);
434     VReg MoveAccToReg(const ir::AstNode *node);
435 
436     void EmitLocalBoxCtor(ir::AstNode const *node);
437     void EmitLocalBoxGet(ir::AstNode const *node, checker::Type const *contentType);
438     void EmitLocalBoxSet(ir::AstNode const *node, varbinder::LocalVariable *lhs);
439 
440     void LoadArrayLength(const ir::AstNode *node, VReg arrayReg);
441     void LoadArrayElement(const ir::AstNode *node, VReg objectReg);
442     void StoreArrayElement(const ir::AstNode *node, VReg objectReg, VReg index, const checker::Type *elementType);
443 
444     template <typename T>
MoveImmediateToRegister(const ir::AstNode * node,VReg reg,const checker::TypeFlag valueType,T const value)445     void MoveImmediateToRegister(const ir::AstNode *node, VReg reg, const checker::TypeFlag valueType, T const value)
446     {
447         switch (valueType) {
448             case checker::TypeFlag::ETS_BOOLEAN:
449                 [[fallthrough]];
450             case checker::TypeFlag::BYTE: {
451                 Ra().Emit<Movi>(node, reg, static_cast<checker::ByteType::UType>(value));
452                 SetVRegType(reg, Checker()->GlobalByteType());
453                 break;
454             }
455             case checker::TypeFlag::CHAR: {
456                 Ra().Emit<Movi>(node, reg, static_cast<checker::CharType::UType>(value));
457                 SetVRegType(reg, Checker()->GlobalCharType());
458                 break;
459             }
460             case checker::TypeFlag::SHORT: {
461                 Ra().Emit<Movi>(node, reg, static_cast<checker::ShortType::UType>(value));
462                 SetVRegType(reg, Checker()->GlobalShortType());
463                 break;
464             }
465             case checker::TypeFlag::INT: {
466                 Ra().Emit<Movi>(node, reg, static_cast<checker::IntType::UType>(value));
467                 SetVRegType(reg, Checker()->GlobalIntType());
468                 break;
469             }
470             case checker::TypeFlag::LONG: {
471                 Ra().Emit<MoviWide>(node, reg, static_cast<checker::LongType::UType>(value));
472                 SetVRegType(reg, Checker()->GlobalLongType());
473                 break;
474             }
475             case checker::TypeFlag::FLOAT: {
476                 Ra().Emit<Fmovi>(node, reg, static_cast<checker::FloatType::UType>(value));
477                 SetVRegType(reg, Checker()->GlobalFloatType());
478                 break;
479             }
480             case checker::TypeFlag::DOUBLE: {
481                 Ra().Emit<FmoviWide>(node, reg, static_cast<checker::DoubleType::UType>(value));
482                 SetVRegType(reg, Checker()->GlobalDoubleType());
483                 break;
484             }
485             default: {
486                 UNREACHABLE();
487             }
488         }
489     }
490 
491     template <typename T>
IncrementImmediateRegister(const ir::AstNode * node,VReg reg,const checker::TypeFlag valueType,T const value)492     void IncrementImmediateRegister(const ir::AstNode *node, VReg reg, const checker::TypeFlag valueType, T const value)
493     {
494         switch (valueType) {
495             // NOTE: operand of increment instruction (INCI) is defined in spec as 32-bit integer,
496             // but its current implementation actually can work with 64-bit integers as well.
497             case checker::TypeFlag::INT: {
498                 Ra().Emit<Inci>(node, reg, static_cast<checker::IntType::UType>(value));
499                 break;
500             }
501             case checker::TypeFlag::CHAR: {
502                 Ra().Emit<Inci>(node, reg, static_cast<checker::CharType::UType>(value));
503                 break;
504             }
505             case checker::TypeFlag::SHORT: {
506                 Ra().Emit<Inci>(node, reg, static_cast<checker::ShortType::UType>(value));
507                 break;
508             }
509             case checker::TypeFlag::ETS_BOOLEAN:
510                 [[fallthrough]];
511             case checker::TypeFlag::BYTE: {
512                 Ra().Emit<Inci>(node, reg, static_cast<checker::ByteType::UType>(value));
513                 break;
514             }
515             default: {
516                 UNREACHABLE();
517             }
518         }
519     }
520 
521     template <typename IntCompare>
JumpCompareRegister(const ir::AstNode * node,VReg lhs,Label * ifFalse)522     void JumpCompareRegister(const ir::AstNode *node, VReg lhs, Label *ifFalse)
523     {
524         Ra().Emit<IntCompare>(node, lhs, ifFalse);
525     }
526 
527     void LoadStringLength(const ir::AstNode *node);
528     void LoadStringChar(const ir::AstNode *node, VReg stringObj, VReg charIndex);
529 
530     void FloatIsNaN(const ir::AstNode *node);
531     void DoubleIsNaN(const ir::AstNode *node);
532 
533     void CompileStatements(const ArenaVector<ir::Statement *> &statements);
534 
535     // Cast
536     void CastToBoolean(const ir::AstNode *node);
537     void CastToByte(const ir::AstNode *node);
538     void CastToChar(const ir::AstNode *node);
539     void CastToShort(const ir::AstNode *node);
540     void CastToDouble(const ir::AstNode *node);
541     void CastToFloat(const ir::AstNode *node);
542     void CastToLong(const ir::AstNode *node);
543     void CastToInt(const ir::AstNode *node);
544     void CastToString(const ir::AstNode *node);
545     void CastToDynamic(const ir::AstNode *node, const checker::ETSDynamicType *type);
546     void CastDynamicTo(const ir::AstNode *node, enum checker::TypeFlag typeFlag);
547     void CastToArrayOrObject(const ir::AstNode *node, const checker::Type *targetType, bool unchecked);
548     void CastDynamicToObject(const ir::AstNode *node, const checker::Type *targetType);
549 
550     void InternalCheckCast(const ir::AstNode *node, const checker::Type *target);
551     void CheckedReferenceNarrowing(const ir::AstNode *node, const checker::Type *target);
552     void GuardUncheckedType(const ir::AstNode *node, const checker::Type *unchecked, const checker::Type *target);
553 
554     // Call, Construct
555     void NewArray(const ir::AstNode *node, VReg arr, VReg dim, const checker::Type *arrType);
556     void NewObject(const ir::AstNode *node, VReg ctor, util::StringView name);
557     void BuildString(const ir::Expression *node);
558     void CallBigIntUnaryOperator(const ir::Expression *node, VReg arg, util::StringView signature);
559     void CallBigIntBinaryOperator(const ir::Expression *node, VReg lhs, VReg rhs, util::StringView signature);
560     void CallBigIntBinaryComparison(const ir::Expression *node, VReg lhs, VReg rhs, util::StringView signature);
561     void BuildTemplateString(const ir::TemplateLiteral *node);
InitObject(const ir::AstNode * node,checker::Signature * signature,const ArenaVector<ir::Expression * > & arguments)562     void InitObject(const ir::AstNode *node, checker::Signature *signature,
563                     const ArenaVector<ir::Expression *> &arguments)
564     {
565         CallImpl<InitobjShort, Initobj, InitobjRange>(node, signature, arguments);
566     }
567 
CallStatic(const ir::AstNode * node,checker::Signature * signature,const ArenaVector<ir::Expression * > & arguments)568     void CallStatic(const ir::AstNode *node, checker::Signature *signature,
569                     const ArenaVector<ir::Expression *> &arguments)
570     {
571         CallImpl<CallShort, Call, CallRange>(node, signature, arguments);
572     }
573 
CallThisStatic(const ir::AstNode * const node,const VReg ctor,checker::Signature * const signature,const ArenaVector<ir::Expression * > & arguments)574     void CallThisStatic(const ir::AstNode *const node, const VReg ctor, checker::Signature *const signature,
575                         const ArenaVector<ir::Expression *> &arguments)
576     {
577         CallThisImpl<CallShort, Call, CallRange>(node, ctor, signature, arguments);
578     }
579 
CallThisVirtual(const ir::AstNode * const node,const VReg ctor,checker::Signature * const signature,const ArenaVector<ir::Expression * > & arguments)580     void CallThisVirtual(const ir::AstNode *const node, const VReg ctor, checker::Signature *const signature,
581                          const ArenaVector<ir::Expression *> &arguments)
582     {
583         CallThisImpl<CallVirtShort, CallVirt, CallVirtRange>(node, ctor, signature, arguments);
584     }
585 
CallThisVirtual0(const ir::AstNode * const node,const VReg ctor,const util::StringView name)586     void CallThisVirtual0(const ir::AstNode *const node, const VReg ctor, const util::StringView name)
587     {
588         Ra().Emit<CallVirtShort, 1>(node, name, ctor, dummyReg_);
589     }
590 
CallThisVirtual1(const ir::AstNode * const node,const VReg ctor,const util::StringView name,const VReg arg0)591     void CallThisVirtual1(const ir::AstNode *const node, const VReg ctor, const util::StringView name, const VReg arg0)
592     {
593         Ra().Emit<CallVirtShort>(node, name, ctor, arg0);
594     }
595 
CallStatic0(const ir::AstNode * const node,const util::StringView name)596     void CallStatic0(const ir::AstNode *const node, const util::StringView name)
597     {
598         Ra().Emit<CallShort, 0>(node, name, dummyReg_, dummyReg_);
599     }
600 
CallThisStatic0(const ir::AstNode * const node,const VReg ctor,const util::StringView name)601     void CallThisStatic0(const ir::AstNode *const node, const VReg ctor, const util::StringView name)
602     {
603         Ra().Emit<CallShort, 1>(node, name, ctor, dummyReg_);
604     }
605 
CallThisStatic1(const ir::AstNode * const node,const VReg ctor,const util::StringView name,const VReg arg0)606     void CallThisStatic1(const ir::AstNode *const node, const VReg ctor, const util::StringView name, const VReg arg0)
607     {
608         Ra().Emit<CallShort>(node, name, ctor, arg0);
609     }
610 
CallThisStatic2(const ir::AstNode * const node,const VReg ctor,const util::StringView name,const VReg arg0,const VReg arg1)611     void CallThisStatic2(const ir::AstNode *const node, const VReg ctor, const util::StringView name, const VReg arg0,
612                          const VReg arg1)
613     {
614         Ra().Emit<Call, 3U>(node, name, ctor, arg0, arg1, dummyReg_);
615     }
616 
CallDynamic(const ir::AstNode * node,VReg & obj,VReg & param2,checker::Signature * signature,const ArenaVector<ir::Expression * > & arguments)617     void CallDynamic(const ir::AstNode *node, VReg &obj, VReg &param2, checker::Signature *signature,
618                      const ArenaVector<ir::Expression *> &arguments)
619     {
620         CallDynamicImpl<CallShort, Call, CallRange>(node, obj, param2, signature, arguments);
621     }
622 
623 #ifdef PANDA_WITH_ETS
624     // The functions below use ETS specific instructions.
625     // Compilation of es2panda fails if ETS plugin is disabled
LaunchStatic(const ir::AstNode * node,checker::Signature * signature,const ArenaVector<ir::Expression * > & arguments)626     void LaunchStatic(const ir::AstNode *node, checker::Signature *signature,
627                       const ArenaVector<ir::Expression *> &arguments)
628     {
629         CallImpl<EtsLaunchShort, EtsLaunch, EtsLaunchRange>(node, signature, arguments);
630     }
631 
LaunchThisStatic(const ir::AstNode * const node,const VReg ctor,checker::Signature * const signature,const ArenaVector<ir::Expression * > & arguments)632     void LaunchThisStatic(const ir::AstNode *const node, const VReg ctor, checker::Signature *const signature,
633                           const ArenaVector<ir::Expression *> &arguments)
634     {
635         CallThisImpl<EtsLaunchShort, EtsLaunch, EtsLaunchRange>(node, ctor, signature, arguments);
636     }
637 
LaunchThisVirtual(const ir::AstNode * const node,const VReg ctor,checker::Signature * const signature,const ArenaVector<ir::Expression * > & arguments)638     void LaunchThisVirtual(const ir::AstNode *const node, const VReg ctor, checker::Signature *const signature,
639                            const ArenaVector<ir::Expression *> &arguments)
640     {
641         CallThisImpl<EtsLaunchVirtShort, EtsLaunchVirt, EtsLaunchVirtRange>(node, ctor, signature, arguments);
642     }
643 #endif  // PANDA_WITH_ETS
644 
645     void CreateBigIntObject(const ir::AstNode *node, VReg arg0);
646     void CreateLambdaObjectFromIdentReference(const ir::AstNode *node, ir::ClassDefinition *lambdaObj);
647     void CreateLambdaObjectFromMemberReference(const ir::AstNode *node, ir::Expression *obj,
648                                                ir::ClassDefinition *lambdaObj);
649     void InitLambdaObject(const ir::AstNode *node, checker::Signature *signature, std::vector<VReg> &arguments);
650 
GetType(const ir::AstNode * node,bool isEtsPrimitive)651     void GetType(const ir::AstNode *node, bool isEtsPrimitive)
652     {
653         if (isEtsPrimitive) {
654             // NOTE: SzD. LoadStaticProperty if ETS stdlib has static TYPE constants otherwise fallback to LdaType
655         } else {
656             auto classRef = GetAccumulatorType()->AsETSObjectType()->AssemblerName();
657             Sa().Emit<LdaType>(node, classRef);
658         }
659     }
660 
661     ~ETSGen() override = default;
662     NO_COPY_SEMANTIC(ETSGen);
663     NO_MOVE_SEMANTIC(ETSGen);
664 
665 private:
666     const VReg dummyReg_ = VReg::RegStart();
667 
668     void EmitIsInstanceNonNullish(const ir::AstNode *node, VReg objReg, checker::ETSObjectType const *clsType);
669     void EmitUnboxedCall(const ir::AstNode *node, std::string_view signatureFlag, const checker::Type *targetType,
670                          const checker::Type *boxedType);
671 
672     void LoadConstantObject(const ir::Expression *node, const checker::Type *type);
673     void StringBuilderAppend(const ir::AstNode *node, VReg builder);
674     void AppendString(const ir::Expression *binExpr, VReg builder);
675     void StringBuilder(const ir::Expression *left, const ir::Expression *right, VReg builder);
676     util::StringView FormClassPropReference(varbinder::Variable const *var);
677     void UnaryMinus(const ir::AstNode *node);
678     void UnaryTilde(const ir::AstNode *node);
679     void UnaryDollarDollar(const ir::AstNode *node);
680 
681     util::StringView ToAssemblerType(const es2panda::checker::Type *type) const;
682 
683     template <typename T>
StoreValueIntoArray(const ir::AstNode * const node,const VReg arr,const VReg index)684     void StoreValueIntoArray(const ir::AstNode *const node, const VReg arr, const VReg index)
685     {
686         Ra().Emit<T>(node, arr, index);
687     }
688 
689     template <typename LongOp, typename IntOp, typename DoubleOp, typename FloatOp>
UpdateOperator(const ir::AstNode * node)690     void UpdateOperator(const ir::AstNode *node)
691     {
692         switch (checker::ETSChecker::ETSType(GetAccumulatorType())) {
693             case checker::TypeFlag::LONG: {
694                 RegScope scope(this);
695                 VReg reg = AllocReg();
696                 Ra().Emit<MoviWide>(node, reg, 1LL);
697                 Ra().Emit<LongOp>(node, reg);
698                 break;
699             }
700             case checker::TypeFlag::INT: {
701                 Sa().Emit<IntOp>(node, 1);
702                 break;
703             }
704             case checker::TypeFlag::CHAR: {
705                 Sa().Emit<IntOp>(node, 1);
706                 Sa().Emit<I32tou16>(node);
707                 break;
708             }
709             case checker::TypeFlag::SHORT: {
710                 Sa().Emit<IntOp>(node, 1);
711                 Sa().Emit<I32toi16>(node);
712                 break;
713             }
714             case checker::TypeFlag::BYTE: {
715                 Sa().Emit<IntOp>(node, 1);
716                 Sa().Emit<I32toi8>(node);
717                 break;
718             }
719             case checker::TypeFlag::DOUBLE: {
720                 RegScope scope(this);
721                 VReg reg = AllocReg();
722                 Ra().Emit<FmoviWide>(node, reg, 1.0);
723                 Ra().Emit<DoubleOp>(node, reg);
724                 break;
725             }
726             case checker::TypeFlag::FLOAT: {
727                 RegScope scope(this);
728                 VReg reg = AllocReg();
729                 Ra().Emit<Fmovi>(node, reg, 1.0F);
730                 Ra().Emit<FloatOp>(node, reg);
731                 break;
732             }
733             default: {
734                 UNREACHABLE();
735             }
736         }
737     }
738 
739     void BinaryEqualityRef(const ir::AstNode *node, bool testEqual, VReg lhs, VReg rhs, Label *ifFalse);
740     void BinaryEqualityRefDynamic(const ir::AstNode *node, bool testEqual, VReg lhs, VReg rhs, Label *ifFalse);
741 
742     template <typename Compare, typename Cond>
BinaryNumberComparison(const ir::AstNode * node,VReg lhs,Label * ifFalse)743     void BinaryNumberComparison(const ir::AstNode *node, VReg lhs, Label *ifFalse)
744     {
745         Ra().Emit<Compare>(node, lhs);
746         Sa().Emit<Cond>(node, ifFalse);
747     }
748 
749     template <typename DynCompare>
BinaryDynamicStrictEquality(const ir::AstNode * node,VReg lhs,Label * ifFalse)750     void BinaryDynamicStrictEquality(const ir::AstNode *node, VReg lhs, Label *ifFalse)
751     {
752         ASSERT(GetAccumulatorType()->IsETSDynamicType() && GetVRegType(lhs)->IsETSDynamicType());
753         Ra().Emit<CallShort, 2U>(node, Signatures::BUILTIN_JSRUNTIME_STRICT_EQUAL, lhs, MoveAccToReg(node));
754         Ra().Emit<DynCompare>(node, ifFalse);
755     }
756 
757     template <typename ObjCompare, typename IntCompare, typename CondCompare, typename DynCompare>
BinaryEquality(const ir::AstNode * node,VReg lhs,Label * ifFalse)758     void BinaryEquality(const ir::AstNode *node, VReg lhs, Label *ifFalse)
759     {
760         BinaryEqualityCondition<ObjCompare, IntCompare, CondCompare>(node, lhs, ifFalse);
761         ToBinaryResult(node, ifFalse);
762         SetAccumulatorType(Checker()->GlobalETSBooleanType());
763     }
764 
765     template <typename ObjCompare, typename IntCompare, typename CondCompare>
BinaryEqualityCondition(const ir::AstNode * node,VReg lhs,Label * ifFalse)766     void BinaryEqualityCondition(const ir::AstNode *node, VReg lhs, Label *ifFalse)
767     {
768         auto typeKind = checker::ETSChecker::TypeKind(targetType_);
769 
770         switch (typeKind) {
771             case checker::TypeFlag::ETS_OBJECT:
772             case checker::TypeFlag::ETS_TYPE_PARAMETER:
773             case checker::TypeFlag::ETS_DYNAMIC_TYPE: {
774                 RegScope rs(this);
775                 VReg arg0 = AllocReg();
776                 StoreAccumulator(node, arg0);
777                 BinaryEqualityRef(node, !std::is_same_v<CondCompare, Jeqz>, lhs, arg0, ifFalse);
778                 return;
779             }
780             case checker::TypeFlag::DOUBLE: {
781                 BinaryFloatingPointComparison<FcmpgWide, FcmplWide, CondCompare>(node, lhs, ifFalse);
782                 break;
783             }
784             case checker::TypeFlag::FLOAT: {
785                 BinaryFloatingPointComparison<Fcmpg, Fcmpl, CondCompare>(node, lhs, ifFalse);
786                 break;
787             }
788             case checker::TypeFlag::LONG: {
789                 BinaryNumberComparison<CmpWide, CondCompare>(node, lhs, ifFalse);
790                 break;
791             }
792             case checker::TypeFlag::ETS_ENUM:
793             case checker::TypeFlag::ETS_STRING_ENUM:
794             case checker::TypeFlag::ETS_BOOLEAN:
795             case checker::TypeFlag::BYTE:
796             case checker::TypeFlag::CHAR:
797             case checker::TypeFlag::SHORT:
798             case checker::TypeFlag::INT: {
799                 Ra().Emit<IntCompare>(node, lhs, ifFalse);
800                 break;
801             }
802             default: {
803                 UNREACHABLE();
804             }
805         }
806 
807         SetAccumulatorType(Checker()->GlobalETSBooleanType());
808     }
809 
810     template <typename ObjCompare, typename DynCompare>
BinaryStrictEquality(const ir::AstNode * node,VReg lhs,Label * ifFalse)811     void BinaryStrictEquality(const ir::AstNode *node, VReg lhs, Label *ifFalse)
812     {
813         if (GetAccumulatorType()->IsETSDynamicType() || GetVRegType(lhs)->IsETSDynamicType()) {
814             BinaryDynamicStrictEquality<DynCompare>(node, lhs, ifFalse);
815         } else {
816             Ra().Emit<ObjCompare>(node, lhs, ifFalse);
817         }
818 
819         ToBinaryResult(node, ifFalse);
820         SetAccumulatorType(Checker()->GlobalETSBooleanType());
821     }
822 
823     template <typename IntCompare, typename CondCompare>
BinaryRelation(const ir::AstNode * node,VReg lhs,Label * ifFalse)824     void BinaryRelation(const ir::AstNode *node, VReg lhs, Label *ifFalse)
825     {
826         BinaryRelationCondition<IntCompare, CondCompare>(node, lhs, ifFalse);
827         ToBinaryResult(node, ifFalse);
828         SetAccumulatorType(Checker()->GlobalETSBooleanType());
829     }
830 
831     template <typename IntCompare, typename CondCompare>
BinaryRelationCondition(const ir::AstNode * node,VReg lhs,Label * ifFalse)832     void BinaryRelationCondition(const ir::AstNode *node, VReg lhs, Label *ifFalse)
833     {
834         auto typeKind = checker::ETSChecker::TypeKind(targetType_);
835 
836         switch (typeKind) {
837             case checker::TypeFlag::DOUBLE: {
838                 BinaryFloatingPointComparison<FcmpgWide, FcmplWide, CondCompare>(node, lhs, ifFalse);
839                 break;
840             }
841             case checker::TypeFlag::FLOAT: {
842                 BinaryFloatingPointComparison<Fcmpg, Fcmpl, CondCompare>(node, lhs, ifFalse);
843                 break;
844             }
845             case checker::TypeFlag::LONG: {
846                 BinaryNumberComparison<CmpWide, CondCompare>(node, lhs, ifFalse);
847                 break;
848             }
849             case checker::TypeFlag::ETS_BOOLEAN:
850             case checker::TypeFlag::BYTE:
851             case checker::TypeFlag::SHORT:
852             case checker::TypeFlag::CHAR:
853             case checker::TypeFlag::INT: {
854                 Ra().Emit<IntCompare>(node, lhs, ifFalse);
855                 break;
856             }
857             default: {
858                 UNREACHABLE();
859             }
860         }
861 
862         SetAccumulatorType(Checker()->GlobalETSBooleanType());
863     }
864 
865     template <typename CompareGreater, typename CompareLess, typename CondCompare>
BinaryFloatingPointComparison(const ir::AstNode * node,VReg lhs,Label * ifFalse)866     void BinaryFloatingPointComparison(const ir::AstNode *node, VReg lhs, Label *ifFalse)
867     {
868         if constexpr (std::is_same_v<CondCompare, Jgez> || std::is_same_v<CondCompare, Jgtz>) {
869             BinaryNumberComparison<CompareGreater, CondCompare>(node, lhs, ifFalse);
870         } else {
871             BinaryNumberComparison<CompareLess, CondCompare>(node, lhs, ifFalse);
872         }
873     }
874 
875     template <typename IntOp, typename LongOp, typename FloatOp, typename DoubleOp>
BinaryArithmetic(const ir::AstNode * node,VReg lhs)876     void BinaryArithmetic(const ir::AstNode *node, VReg lhs)
877     {
878         auto typeKind = checker::ETSChecker::TypeKind(targetType_);
879 
880         switch (typeKind) {
881             case checker::TypeFlag::DOUBLE: {
882                 Ra().Emit<DoubleOp>(node, lhs);
883                 SetAccumulatorType(Checker()->GlobalDoubleType());
884                 break;
885             }
886             case checker::TypeFlag::FLOAT: {
887                 Ra().Emit<FloatOp>(node, lhs);
888                 SetAccumulatorType(Checker()->GlobalFloatType());
889                 break;
890             }
891             default: {
892                 BinaryBitwiseArithmetic<IntOp, LongOp>(node, lhs);
893             }
894         }
895     }
896 
897     template <typename IntOp, typename LongOp>
BinaryBitwiseArithmetic(const ir::AstNode * node,VReg lhs)898     void BinaryBitwiseArithmetic(const ir::AstNode *node, VReg lhs)
899     {
900         auto typeKind = checker::ETSChecker::TypeKind(targetType_);
901 
902         switch (typeKind) {
903             case checker::TypeFlag::LONG: {
904                 Ra().Emit<LongOp>(node, lhs);
905                 SetAccumulatorType(Checker()->GlobalLongType());
906                 break;
907             }
908             case checker::TypeFlag::BYTE:
909             case checker::TypeFlag::SHORT:
910             case checker::TypeFlag::INT: {
911                 Ra().Emit<IntOp>(node, lhs);
912                 SetAccumulatorType(Checker()->GlobalIntType());
913                 break;
914             }
915             case checker::TypeFlag::ETS_BOOLEAN: {
916                 Ra().Emit<IntOp>(node, lhs);
917                 SetAccumulatorType(Checker()->GlobalETSBooleanType());
918                 break;
919             }
920             case checker::TypeFlag::CHAR: {
921                 Ra().Emit<IntOp>(node, lhs);
922                 SetAccumulatorType(Checker()->GlobalCharType());
923                 break;
924             }
925             default: {
926                 UNREACHABLE();
927             }
928         }
929     }
930 // NOLINTBEGIN(cppcoreguidelines-macro-usage, readability-container-size-empty)
931 #define COMPILE_ARG(idx)                                                                                      \
932     ASSERT((idx) < arguments.size());                                                                         \
933     ASSERT((idx) < signature->Params().size() || signature->RestVar() != nullptr);                            \
934     auto *paramType##idx = Checker()->MaybeBoxedType(                                                         \
935         (idx) < signature->Params().size() ? signature->Params()[(idx)] : signature->RestVar(), Allocator()); \
936     auto ttctx##idx = TargetTypeContext(this, paramType##idx);                                                \
937     arguments[idx]->Compile(this);                                                                            \
938     VReg arg##idx = AllocReg();                                                                               \
939     ApplyConversion(arguments[idx], nullptr);                                                                 \
940     ApplyConversionAndStoreAccumulator(arguments[idx], arg##idx, paramType##idx)
941 
942     template <typename Short, typename General, typename Range>
CallThisImpl(const ir::AstNode * const node,const VReg ctor,checker::Signature * const signature,const ArenaVector<ir::Expression * > & arguments)943     void CallThisImpl(const ir::AstNode *const node, const VReg ctor, checker::Signature *const signature,
944                       const ArenaVector<ir::Expression *> &arguments)
945     {
946         RegScope rs(this);
947         const auto name = signature->InternalName();
948 
949         switch (arguments.size()) {
950             case 0U: {
951                 Ra().Emit<Short, 1>(node, name, ctor, dummyReg_);
952                 break;
953             }
954             case 1U: {
955                 COMPILE_ARG(0);
956                 Ra().Emit<Short>(node, name, ctor, arg0);
957                 break;
958             }
959             case 2U: {
960                 COMPILE_ARG(0);
961                 COMPILE_ARG(1);
962                 Ra().Emit<General, 3U>(node, name, ctor, arg0, arg1, dummyReg_);
963                 break;
964             }
965             case 3U: {
966                 COMPILE_ARG(0);
967                 COMPILE_ARG(1);
968                 COMPILE_ARG(2);
969                 Ra().Emit<General>(node, name, ctor, arg0, arg1, arg2);
970                 break;
971             }
972             default: {
973                 for (const auto *arg : arguments) {
974                     auto ttctx = TargetTypeContext(this, arg->TsType());
975                     VReg argReg = AllocReg();
976                     arg->Compile(this);
977                     StoreAccumulator(node, argReg);
978                 }
979 
980                 Rra().Emit<Range>(node, ctor, arguments.size() + 1, name, ctor);
981                 break;
982             }
983         }
984     }
985 
986     template <typename Short, typename General, typename Range>
CallImpl(const ir::AstNode * node,checker::Signature * signature,const ArenaVector<ir::Expression * > & arguments)987     void CallImpl(const ir::AstNode *node, checker::Signature *signature,
988                   const ArenaVector<ir::Expression *> &arguments)
989     {
990         RegScope rs(this);
991         const auto name = signature->InternalName();
992 
993         switch (arguments.size()) {
994             case 0U: {
995                 Ra().Emit<Short, 0>(node, name, dummyReg_, dummyReg_);
996                 break;
997             }
998             case 1U: {
999                 COMPILE_ARG(0);
1000                 Ra().Emit<Short, 1>(node, name, arg0, dummyReg_);
1001                 break;
1002             }
1003             case 2U: {
1004                 COMPILE_ARG(0);
1005                 COMPILE_ARG(1);
1006                 Ra().Emit<Short>(node, name, arg0, arg1);
1007                 break;
1008             }
1009             case 3U: {
1010                 COMPILE_ARG(0);
1011                 COMPILE_ARG(1);
1012                 COMPILE_ARG(2);
1013                 Ra().Emit<General, 3U>(node, name, arg0, arg1, arg2, dummyReg_);
1014                 break;
1015             }
1016             case 4U: {
1017                 COMPILE_ARG(0);
1018                 COMPILE_ARG(1);
1019                 COMPILE_ARG(2);
1020                 COMPILE_ARG(3);
1021                 Ra().Emit<General>(node, name, arg0, arg1, arg2, arg3);
1022                 break;
1023             }
1024             default: {
1025                 VReg argStart = NextReg();
1026 
1027                 for (const auto *arg : arguments) {
1028                     auto ttctx = TargetTypeContext(this, arg->TsType());
1029                     VReg argReg = AllocReg();
1030                     arg->Compile(this);
1031                     StoreAccumulator(node, argReg);
1032                 }
1033 
1034                 Rra().Emit<Range>(node, argStart, arguments.size(), name, argStart);
1035                 break;
1036             }
1037         }
1038     }
1039 #undef COMPILE_ARG
1040 
1041 #define COMPILE_ARG(idx)                                                                                       \
1042     ASSERT((idx) < arguments.size());                                                                          \
1043     ASSERT((idx) + 2U < signature->Params().size() || signature->RestVar() != nullptr);                        \
1044     auto *paramType##idx = (idx) + 2U < signature->Params().size() ? signature->Params()[(idx) + 2U]->TsType() \
1045                                                                    : signature->RestVar()->TsType();           \
1046     auto ttctx##idx = TargetTypeContext(this, paramType##idx);                                                 \
1047     VReg arg##idx = AllocReg();                                                                                \
1048     arguments[idx]->Compile(this);                                                                             \
1049     ApplyConversionAndStoreAccumulator(arguments[idx], arg##idx, paramType##idx)
1050 
1051     template <typename Short, typename General, typename Range>
CallDynamicImpl(const ir::AstNode * node,VReg & obj,VReg & param2,checker::Signature * signature,const ArenaVector<ir::Expression * > & arguments)1052     void CallDynamicImpl(const ir::AstNode *node, VReg &obj, VReg &param2, checker::Signature *signature,
1053                          const ArenaVector<ir::Expression *> &arguments)
1054     {
1055         RegScope rs(this);
1056         const auto name = signature->InternalName();
1057 
1058         switch (arguments.size()) {
1059             case 0U: {
1060                 Ra().Emit<Short>(node, name, obj, param2);
1061                 break;
1062             }
1063             case 1U: {
1064                 COMPILE_ARG(0);
1065                 Ra().Emit<General, 3U>(node, name, obj, param2, arg0, dummyReg_);
1066                 break;
1067             }
1068             case 2U: {
1069                 COMPILE_ARG(0);
1070                 COMPILE_ARG(1);
1071                 Ra().Emit<General>(node, name, obj, param2, arg0, arg1);
1072                 break;
1073             }
1074             default: {
1075                 size_t index = 0;
1076                 for (const auto *arg : arguments) {
1077                     auto ttctx = TargetTypeContext(this, arg->TsType());
1078                     VReg argReg = AllocReg();
1079                     arg->Compile(this);
1080                     // + 2U since we need to skip first 2 args in signature; first args is obj,
1081                     // second arg is param2
1082                     auto *argType = signature->Params()[index + 2U]->TsType();
1083                     ApplyConversionAndStoreAccumulator(node, argReg, argType);
1084                     index++;
1085                 }
1086 
1087                 Rra().Emit<Range>(node, obj, arguments.size() + 2U, name, obj);
1088                 break;
1089             }
1090         }
1091     }
1092 
1093 #undef COMPILE_ARG
1094     // NOLINTEND(cppcoreguidelines-macro-usage, readability-container-size-empty)
1095 
1096     void ToBinaryResult(const ir::AstNode *node, Label *ifFalse);
1097 
1098     template <typename T>
1099     void LoadAccumulatorNumber(const ir::AstNode *node, T number, checker::TypeFlag targetType);
1100     void InitializeContainingClass();
1101 
1102     util::StringView FormDynamicModulePropReference(const varbinder::Variable *var);
1103     util::StringView FormDynamicModulePropReference(const ir::ETSImportDeclaration *import);
1104 
1105     friend class TargetTypeContext;
1106 
1107     VReg acc_ {};
1108     const checker::Type *targetType_ {};
1109     const checker::ETSObjectType *containingObjectType_ {};
1110 };
1111 
1112 template <typename T>
LoadAccumulatorNumber(const ir::AstNode * node,T number,checker::TypeFlag targetType)1113 void ETSGen::LoadAccumulatorNumber(const ir::AstNode *node, T number, checker::TypeFlag targetType)
1114 {
1115     auto typeKind = targetType_ && (!targetType_->IsETSObjectType() && !targetType_->IsETSUnionType())
1116                         ? checker::ETSChecker::TypeKind(targetType_)
1117                         : targetType;
1118 
1119     switch (typeKind) {
1120         case checker::TypeFlag::ETS_BOOLEAN:
1121         case checker::TypeFlag::BYTE: {
1122             Sa().Emit<Ldai>(node, static_cast<checker::ByteType::UType>(number));
1123             SetAccumulatorType(Checker()->GlobalByteType());
1124             break;
1125         }
1126         case checker::TypeFlag::CHAR: {
1127             Sa().Emit<Ldai>(node, static_cast<checker::CharType::UType>(number));
1128             SetAccumulatorType(Checker()->GlobalCharType());
1129             break;
1130         }
1131         case checker::TypeFlag::SHORT: {
1132             Sa().Emit<Ldai>(node, static_cast<checker::ShortType::UType>(number));
1133             SetAccumulatorType(Checker()->GlobalShortType());
1134             break;
1135         }
1136         case checker::TypeFlag::INT: {
1137             Sa().Emit<Ldai>(node, static_cast<checker::IntType::UType>(number));
1138             SetAccumulatorType(Checker()->GlobalIntType());
1139             break;
1140         }
1141         case checker::TypeFlag::LONG: {
1142             Sa().Emit<LdaiWide>(node, static_cast<checker::LongType::UType>(number));
1143             SetAccumulatorType(Checker()->GlobalLongType());
1144             break;
1145         }
1146         case checker::TypeFlag::FLOAT: {
1147             Sa().Emit<Fldai>(node, static_cast<checker::FloatType::UType>(number));
1148             SetAccumulatorType(Checker()->GlobalFloatType());
1149             break;
1150         }
1151         case checker::TypeFlag::DOUBLE: {
1152             Sa().Emit<FldaiWide>(node, static_cast<checker::DoubleType::UType>(number));
1153             SetAccumulatorType(Checker()->GlobalDoubleType());
1154             break;
1155         }
1156         case checker::TypeFlag::ETS_STRING_ENUM:
1157             [[fallthrough]];
1158         case checker::TypeFlag::ETS_ENUM: {
1159             Sa().Emit<Ldai>(node, static_cast<checker::ETSEnumInterface::UType>(number));
1160             SetAccumulatorType(Checker()->GlobalIntType());
1161             break;
1162         }
1163         default: {
1164             UNREACHABLE();
1165         }
1166     }
1167 
1168     if (targetType_ && (targetType_->IsETSObjectType() || targetType_->IsETSUnionType())) {
1169         ApplyConversion(node, targetType_);
1170     }
1171 }
1172 
1173 }  // namespace panda::es2panda::compiler
1174 
1175 #endif
1176