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 ¶m2, 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 ¶m2, 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