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