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 EmitUnboxingConversion(const ir::AstNode *node);
397 checker::Type *EmitBoxedType(ir::BoxingUnboxingFlags boxingFlag, const ir::AstNode *node);
398 void EmitBoxingConversion(const ir::AstNode *node);
399 void SwapBinaryOpArgs(const ir::AstNode *node, VReg lhs);
400 VReg MoveAccToReg(const ir::AstNode *node);
401
402 void LoadArrayLength(const ir::AstNode *node, VReg arrayReg);
403 void LoadArrayElement(const ir::AstNode *node, VReg objectReg);
404 void StoreArrayElement(const ir::AstNode *node, VReg objectReg, VReg index, const checker::Type *elementType);
405
406 template <typename T>
MoveImmediateToRegister(const ir::AstNode * node,VReg reg,const checker::TypeFlag valueType,T const value)407 void MoveImmediateToRegister(const ir::AstNode *node, VReg reg, const checker::TypeFlag valueType, T const value)
408 {
409 switch (valueType) {
410 case checker::TypeFlag::ETS_BOOLEAN:
411 [[fallthrough]];
412 case checker::TypeFlag::BYTE: {
413 Ra().Emit<Movi>(node, reg, static_cast<checker::ByteType::UType>(value));
414 SetVRegType(reg, Checker()->GlobalByteType());
415 break;
416 }
417 case checker::TypeFlag::CHAR: {
418 Ra().Emit<Movi>(node, reg, static_cast<checker::CharType::UType>(value));
419 SetVRegType(reg, Checker()->GlobalCharType());
420 break;
421 }
422 case checker::TypeFlag::SHORT: {
423 Ra().Emit<Movi>(node, reg, static_cast<checker::ShortType::UType>(value));
424 SetVRegType(reg, Checker()->GlobalShortType());
425 break;
426 }
427 case checker::TypeFlag::INT: {
428 Ra().Emit<Movi>(node, reg, static_cast<checker::IntType::UType>(value));
429 SetVRegType(reg, Checker()->GlobalIntType());
430 break;
431 }
432 case checker::TypeFlag::LONG: {
433 Ra().Emit<MoviWide>(node, reg, static_cast<checker::LongType::UType>(value));
434 SetVRegType(reg, Checker()->GlobalLongType());
435 break;
436 }
437 case checker::TypeFlag::FLOAT: {
438 Ra().Emit<Fmovi>(node, reg, static_cast<checker::FloatType::UType>(value));
439 SetVRegType(reg, Checker()->GlobalFloatType());
440 break;
441 }
442 case checker::TypeFlag::DOUBLE: {
443 Ra().Emit<FmoviWide>(node, reg, static_cast<checker::DoubleType::UType>(value));
444 SetVRegType(reg, Checker()->GlobalDoubleType());
445 break;
446 }
447 default: {
448 UNREACHABLE();
449 }
450 }
451 }
452
453 template <typename T>
IncrementImmediateRegister(const ir::AstNode * node,VReg reg,const checker::TypeFlag valueType,T const value)454 void IncrementImmediateRegister(const ir::AstNode *node, VReg reg, const checker::TypeFlag valueType, T const value)
455 {
456 switch (valueType) {
457 // NOTE: operand of increment instruction (INCI) is defined in spec as 32-bit integer,
458 // but its current implementation actually can work with 64-bit integers as well.
459 case checker::TypeFlag::INT: {
460 Ra().Emit<Inci>(node, reg, static_cast<checker::IntType::UType>(value));
461 break;
462 }
463 case checker::TypeFlag::CHAR: {
464 Ra().Emit<Inci>(node, reg, static_cast<checker::CharType::UType>(value));
465 break;
466 }
467 case checker::TypeFlag::SHORT: {
468 Ra().Emit<Inci>(node, reg, static_cast<checker::ShortType::UType>(value));
469 break;
470 }
471 case checker::TypeFlag::ETS_BOOLEAN:
472 [[fallthrough]];
473 case checker::TypeFlag::BYTE: {
474 Ra().Emit<Inci>(node, reg, static_cast<checker::ByteType::UType>(value));
475 break;
476 }
477 default: {
478 UNREACHABLE();
479 }
480 }
481 }
482
483 template <typename IntCompare>
JumpCompareRegister(const ir::AstNode * node,VReg lhs,Label * ifFalse)484 void JumpCompareRegister(const ir::AstNode *node, VReg lhs, Label *ifFalse)
485 {
486 Ra().Emit<IntCompare>(node, lhs, ifFalse);
487 }
488
489 void LoadStringLength(const ir::AstNode *node);
490 void LoadStringChar(const ir::AstNode *node, VReg stringObj, VReg charIndex);
491
492 void FloatIsNaN(const ir::AstNode *node);
493 void DoubleIsNaN(const ir::AstNode *node);
494
495 void CompileStatements(const ArenaVector<ir::Statement *> &statements);
496
497 // Cast
498 void CastToBoolean(const ir::AstNode *node);
499 void CastToByte(const ir::AstNode *node);
500 void CastToChar(const ir::AstNode *node);
501 void CastToShort(const ir::AstNode *node);
502 void CastToDouble(const ir::AstNode *node);
503 void CastToFloat(const ir::AstNode *node);
504 void CastToLong(const ir::AstNode *node);
505 void CastToInt(const ir::AstNode *node);
506 void CastToString(const ir::AstNode *node);
507 void CastToDynamic(const ir::AstNode *node, const checker::ETSDynamicType *type);
508 void CastDynamicTo(const ir::AstNode *node, enum checker::TypeFlag typeFlag);
509 void CastToReftype(const ir::AstNode *node, const checker::Type *targetType, bool unchecked);
510 void CastDynamicToObject(const ir::AstNode *node, const checker::Type *targetType);
511 void CastUnionToFunctionType(const ir::AstNode *node, const checker::ETSUnionType *unionType,
512 checker::Signature *signatureTarget);
513
514 void InternalIsInstance(const ir::AstNode *node, const checker::Type *target);
515 void InternalCheckCast(const ir::AstNode *node, const checker::Type *target);
516 void CheckedReferenceNarrowing(const ir::AstNode *node, const checker::Type *target);
517 void GuardUncheckedType(const ir::AstNode *node, const checker::Type *unchecked, const checker::Type *target);
518
519 // Call, Construct
520 void NewArray(const ir::AstNode *node, VReg arr, VReg dim, const checker::Type *arrType);
521 void NewObject(const ir::AstNode *node, util::StringView name, VReg athis);
522 void BuildString(const ir::Expression *node);
523 void CallBigIntUnaryOperator(const ir::Expression *node, VReg arg, util::StringView signature);
524 void CallBigIntBinaryOperator(const ir::Expression *node, VReg lhs, VReg rhs, util::StringView signature);
525 void CallBigIntBinaryComparison(const ir::Expression *node, VReg lhs, VReg rhs, util::StringView signature);
526 void BuildTemplateString(const ir::TemplateLiteral *node);
InitObject(const ir::AstNode * node,checker::Signature const * signature,const ArenaVector<ir::Expression * > & arguments)527 void InitObject(const ir::AstNode *node, checker::Signature const *signature,
528 const ArenaVector<ir::Expression *> &arguments)
529 {
530 CallImpl<InitobjShort, Initobj, InitobjRange>(node, signature, arguments);
531 }
532
IsDevirtualizedSignature(const checker::Signature * signature)533 bool IsDevirtualizedSignature(const checker::Signature *signature)
534 {
535 ASSERT(!signature->HasSignatureFlag(checker::SignatureFlags::STATIC));
536 return signature->HasSignatureFlag(checker::SignatureFlags::FINAL | checker::SignatureFlags::PRIVATE |
537 checker::SignatureFlags::CONSTRUCTOR);
538 }
539
CallExact(const ir::AstNode * node,checker::Signature * signature,const ArenaVector<ir::Expression * > & arguments)540 void CallExact(const ir::AstNode *node, checker::Signature *signature,
541 const ArenaVector<ir::Expression *> &arguments)
542 {
543 CallImpl<CallShort, Call, CallRange>(node, signature, arguments);
544 }
545
CallExact(const ir::AstNode * const node,const checker::Signature * signature,const VReg arg0,const ArenaVector<ir::Expression * > & arguments)546 void CallExact(const ir::AstNode *const node, const checker::Signature *signature, const VReg arg0,
547 const ArenaVector<ir::Expression *> &arguments)
548 {
549 CallArgStart<CallShort, Call, CallRange>(node, signature, arg0, arguments);
550 }
551
CallExact(const ir::AstNode * const node,const util::StringView name)552 void CallExact(const ir::AstNode *const node, const util::StringView name)
553 {
554 Ra().Emit<CallShort, 0>(node, name, dummyReg_, dummyReg_);
555 }
556
CallExact(const ir::AstNode * const node,const util::StringView name,const VReg arg0)557 void CallExact(const ir::AstNode *const node, const util::StringView name, const VReg arg0)
558 {
559 Ra().Emit<CallShort, 1>(node, name, arg0, dummyReg_);
560 }
561
CallExact(const ir::AstNode * const node,const util::StringView name,const VReg arg0,const VReg arg1)562 void CallExact(const ir::AstNode *const node, const util::StringView name, const VReg arg0, const VReg arg1)
563 {
564 Ra().Emit<CallShort>(node, name, arg0, arg1);
565 }
566
CallExact(const ir::AstNode * const node,const util::StringView name,const VReg arg0,const VReg arg1,const VReg arg2)567 void CallExact(const ir::AstNode *const node, const util::StringView name, const VReg arg0, const VReg arg1,
568 const VReg arg2)
569 {
570 Ra().Emit<Call, 3U>(node, name, arg0, arg1, arg2, dummyReg_);
571 }
572
CallVirtual(const ir::AstNode * const node,const checker::Signature * signature,const VReg athis,const ArenaVector<ir::Expression * > & arguments)573 void CallVirtual(const ir::AstNode *const node, const checker::Signature *signature, const VReg athis,
574 const ArenaVector<ir::Expression *> &arguments)
575 {
576 ASSERT(!signature->HasSignatureFlag(checker::SignatureFlags::STATIC));
577 ASSERT(!signature->Owner()->GetDeclNode()->IsFinal() || signature->IsFinal());
578 if (IsDevirtualizedSignature(signature)) {
579 CallArgStart<CallShort, Call, CallRange>(node, signature, athis, arguments);
580 } else {
581 CallArgStart<CallVirtShort, CallVirt, CallVirtRange>(node, signature, athis, arguments);
582 }
583 }
584
CallVirtual(const ir::AstNode * const node,const checker::Signature * signature,const VReg athis)585 void CallVirtual(const ir::AstNode *const node, const checker::Signature *signature, const VReg athis)
586 {
587 if (IsDevirtualizedSignature(signature)) {
588 CallExact(node, signature->InternalName(), athis);
589 } else {
590 CallVirtual(node, signature->InternalName(), athis);
591 }
592 }
593
CallVirtual(const ir::AstNode * const node,const checker::Signature * signature,const VReg athis,const VReg arg0)594 void CallVirtual(const ir::AstNode *const node, const checker::Signature *signature, const VReg athis,
595 const VReg arg0)
596 {
597 if (IsDevirtualizedSignature(signature)) {
598 CallExact(node, signature->InternalName(), athis, arg0);
599 } else {
600 CallVirtual(node, signature->InternalName(), athis, arg0);
601 }
602 }
603
CallVirtual(const ir::AstNode * const node,const util::StringView name,const VReg athis)604 void CallVirtual(const ir::AstNode *const node, const util::StringView name, const VReg athis)
605 {
606 Ra().Emit<CallVirtShort, 1>(node, name, athis, dummyReg_);
607 }
608
CallVirtual(const ir::AstNode * const node,const util::StringView name,const VReg athis,const VReg arg0)609 void CallVirtual(const ir::AstNode *const node, const util::StringView name, const VReg athis, const VReg arg0)
610 {
611 Ra().Emit<CallVirtShort>(node, name, athis, arg0);
612 }
613
614 struct CallDynamicData {
615 const ir::AstNode *node = nullptr;
616 VReg obj;
617 VReg param2;
618 };
619
CallDynamic(CallDynamicData data,checker::Signature * signature,const ArenaVector<ir::Expression * > & arguments)620 void CallDynamic(CallDynamicData data, checker::Signature *signature,
621 const ArenaVector<ir::Expression *> &arguments)
622 {
623 CallDynamicImpl<CallShort, Call, CallRange>(data, signature, arguments);
624 }
625
CallDynamic(CallDynamicData data,VReg param3,checker::Signature * signature,const ArenaVector<ir::Expression * > & arguments)626 void CallDynamic(CallDynamicData data, VReg param3, checker::Signature *signature,
627 const ArenaVector<ir::Expression *> &arguments)
628 {
629 CallDynamicImpl<CallShort, Call, CallRange>(data, param3, signature, arguments);
630 }
631
632 #ifdef PANDA_WITH_ETS
633 // The functions below use ETS specific instructions.
634 // Compilation of es2panda fails if ETS plugin is disabled
LaunchExact(const ir::AstNode * node,checker::Signature * signature,const ArenaVector<ir::Expression * > & arguments)635 void LaunchExact(const ir::AstNode *node, checker::Signature *signature,
636 const ArenaVector<ir::Expression *> &arguments)
637 {
638 CallImpl<EtsLaunchShort, EtsLaunch, EtsLaunchRange>(node, signature, arguments);
639 }
640
LaunchVirtual(const ir::AstNode * const node,checker::Signature * const signature,const VReg athis,const ArenaVector<ir::Expression * > & arguments)641 void LaunchVirtual(const ir::AstNode *const node, checker::Signature *const signature, const VReg athis,
642 const ArenaVector<ir::Expression *> &arguments)
643 {
644 if (IsDevirtualizedSignature(signature)) {
645 CallArgStart<EtsLaunchShort, EtsLaunch, EtsLaunchRange>(node, signature, athis, arguments);
646 } else {
647 CallArgStart<EtsLaunchVirtShort, EtsLaunchVirt, EtsLaunchVirtRange>(node, signature, athis, arguments);
648 }
649 }
650 #endif // PANDA_WITH_ETS
651
652 void CreateBigIntObject(const ir::AstNode *node, VReg arg0,
653 std::string_view signature = Signatures::BUILTIN_BIGINT_CTOR);
654
GetType(const ir::AstNode * node,bool isEtsPrimitive)655 void GetType(const ir::AstNode *node, bool isEtsPrimitive)
656 {
657 if (isEtsPrimitive) {
658 // NOTE: SzD. LoadStaticProperty if ETS stdlib has static TYPE constants otherwise fallback to LdaType
659 } else {
660 auto classRef = GetAccumulatorType()->AsETSObjectType()->AssemblerName();
661 Sa().Emit<LdaType>(node, classRef);
662 }
663 }
664
665 ~ETSGen() override = default;
666 NO_COPY_SEMANTIC(ETSGen);
667 NO_MOVE_SEMANTIC(ETSGen);
668
669 void EmitUnboxEnum(const ir::AstNode *node, const checker::Type *enumType);
670
671 private:
672 const VReg dummyReg_ = VReg::RegStart();
673
674 void EmitUnboxedCall(const ir::AstNode *node, std::string_view signatureFlag, const checker::Type *targetType,
675 const checker::Type *boxedType);
676
677 void LoadConstantObject(const ir::Expression *node, const checker::Type *type);
678 void StringBuilderAppend(const ir::AstNode *node, VReg builder);
679 void AppendString(const ir::Expression *binExpr, VReg builder);
680 void StringBuilder(const ir::Expression *left, const ir::Expression *right, VReg builder);
681 util::StringView FormClassPropReference(varbinder::Variable const *var);
682 void UnaryMinus(const ir::AstNode *node);
683 void UnaryTilde(const ir::AstNode *node);
684 void UnaryDollarDollar(const ir::AstNode *node);
685
686 util::StringView ToAssemblerType(const es2panda::checker::Type *type) const;
687 void TestIsInstanceConstituent(const ir::AstNode *node, Label *ifTrue, Label *ifFalse, checker::Type const *target,
688 bool acceptUndefined);
689 void CheckedReferenceNarrowingObject(const ir::AstNode *node, const checker::Type *target);
690
691 void HandleLooseNullishEquality(const ir::AstNode *node, VReg lhs, VReg rhs, Label *ifFalse, Label *ifTrue);
692
EmitIsUndefined(const ir::AstNode * node)693 void EmitIsUndefined([[maybe_unused]] const ir::AstNode *node)
694 {
695 #ifdef PANDA_WITH_ETS
696 Sa().Emit<EtsIsundefined>(node);
697 #else
698 UNREACHABLE();
699 #endif // PANDA_WITH_ETS
700 }
701
EmitEtsEquals(const ir::AstNode * node,const VReg lhs,const VReg rhs)702 void EmitEtsEquals([[maybe_unused]] const ir::AstNode *node, [[maybe_unused]] const VReg lhs,
703 [[maybe_unused]] const VReg rhs)
704 {
705 #ifdef PANDA_WITH_ETS
706 Ra().Emit<EtsEquals>(node, lhs, rhs);
707 #else
708 UNREACHABLE();
709 #endif // PANDA_WITH_ETS
710 }
711
712 template <typename T>
StoreValueIntoArray(const ir::AstNode * const node,const VReg arr,const VReg index)713 void StoreValueIntoArray(const ir::AstNode *const node, const VReg arr, const VReg index)
714 {
715 Ra().Emit<T>(node, arr, index);
716 }
717
718 template <typename LongOp, typename IntOp, typename DoubleOp, typename FloatOp>
UpdateOperator(const ir::AstNode * node)719 void UpdateOperator(const ir::AstNode *node)
720 {
721 switch (checker::ETSChecker::ETSType(GetAccumulatorType())) {
722 case checker::TypeFlag::LONG: {
723 RegScope scope(this);
724 VReg reg = AllocReg();
725 Ra().Emit<MoviWide>(node, reg, 1LL);
726 Ra().Emit<LongOp>(node, reg);
727 break;
728 }
729 case checker::TypeFlag::INT: {
730 Sa().Emit<IntOp>(node, 1);
731 break;
732 }
733 case checker::TypeFlag::CHAR: {
734 Sa().Emit<IntOp>(node, 1);
735 Sa().Emit<I32tou16>(node);
736 break;
737 }
738 case checker::TypeFlag::SHORT: {
739 Sa().Emit<IntOp>(node, 1);
740 Sa().Emit<I32toi16>(node);
741 break;
742 }
743 case checker::TypeFlag::BYTE: {
744 Sa().Emit<IntOp>(node, 1);
745 Sa().Emit<I32toi8>(node);
746 break;
747 }
748 case checker::TypeFlag::DOUBLE: {
749 RegScope scope(this);
750 VReg reg = AllocReg();
751 Ra().Emit<FmoviWide>(node, reg, 1.0);
752 Ra().Emit<DoubleOp>(node, reg);
753 break;
754 }
755 case checker::TypeFlag::FLOAT: {
756 RegScope scope(this);
757 VReg reg = AllocReg();
758 Ra().Emit<Fmovi>(node, reg, 1.0F);
759 Ra().Emit<FloatOp>(node, reg);
760 break;
761 }
762 default: {
763 UNREACHABLE();
764 }
765 }
766 }
767
768 template <typename Br>
769 void InverseCondition(const ir::AstNode *node, Br const &br, Label *target, bool inverse = true)
770 {
771 if (!inverse) {
772 br(target);
773 return;
774 }
775 Label *loc = AllocLabel();
776 br(loc);
777 JumpTo(node, target);
778 SetLabel(node, loc);
779 }
780
781 void RefEqualityLoose(const ir::AstNode *node, VReg lhs, VReg rhs, Label *ifFalse);
782 void RefEqualityLooseDynamic(const ir::AstNode *node, VReg lhs, VReg rhs, Label *ifFalse);
783
784 template <typename Compare, typename Cond>
BinaryNumberComparison(const ir::AstNode * node,VReg lhs,Label * ifFalse)785 void BinaryNumberComparison(const ir::AstNode *node, VReg lhs, Label *ifFalse)
786 {
787 Ra().Emit<Compare>(node, lhs);
788 Sa().Emit<Cond>(node, ifFalse);
789 }
790
791 template <typename DynCompare>
RefEqualityStrictDynamic(const ir::AstNode * node,VReg lhs,Label * ifFalse)792 void RefEqualityStrictDynamic(const ir::AstNode *node, VReg lhs, Label *ifFalse)
793 {
794 ASSERT(GetAccumulatorType()->IsETSDynamicType() && GetVRegType(lhs)->IsETSDynamicType());
795 RegScope scope(this);
796 Ra().Emit<CallShort, 2U>(node, Signatures::BUILTIN_JSRUNTIME_STRICT_EQUAL, lhs, MoveAccToReg(node));
797 Ra().Emit<DynCompare>(node, ifFalse);
798 }
799
800 template <typename ObjCompare, typename IntCompare, typename CondCompare, typename DynCompare>
BinaryEquality(const ir::AstNode * node,VReg lhs,Label * ifFalse)801 void BinaryEquality(const ir::AstNode *node, VReg lhs, Label *ifFalse)
802 {
803 BinaryEqualityCondition<ObjCompare, IntCompare, CondCompare>(node, lhs, ifFalse);
804 ToBinaryResult(node, ifFalse);
805 SetAccumulatorType(Checker()->GlobalETSBooleanType());
806 }
807
808 template <typename ObjCompare, typename IntCompare, typename CondCompare>
BinaryEqualityCondition(const ir::AstNode * node,VReg lhs,Label * ifFalse)809 void BinaryEqualityCondition(const ir::AstNode *node, VReg lhs, Label *ifFalse)
810 {
811 if (targetType_->IsETSReferenceType()) {
812 RegScope rs(this);
813 VReg arg0 = AllocReg();
814 StoreAccumulator(node, arg0);
815 InverseCondition(
816 node, [this, node, lhs, arg0](Label *tgt) { RefEqualityLoose(node, lhs, arg0, tgt); }, ifFalse,
817 std::is_same_v<CondCompare, Jeqz>);
818 SetAccumulatorType(Checker()->GlobalETSBooleanType());
819 return;
820 }
821
822 auto typeKind = checker::ETSChecker::TypeKind(targetType_);
823
824 switch (typeKind) {
825 case checker::TypeFlag::DOUBLE: {
826 BinaryFloatingPointComparison<FcmpgWide, FcmplWide, CondCompare>(node, lhs, ifFalse);
827 break;
828 }
829 case checker::TypeFlag::FLOAT: {
830 BinaryFloatingPointComparison<Fcmpg, Fcmpl, CondCompare>(node, lhs, ifFalse);
831 break;
832 }
833 case checker::TypeFlag::LONG: {
834 BinaryNumberComparison<CmpWide, CondCompare>(node, lhs, ifFalse);
835 break;
836 }
837 case checker::TypeFlag::ETS_INT_ENUM:
838 case checker::TypeFlag::ETS_STRING_ENUM:
839 case checker::TypeFlag::ETS_BOOLEAN:
840 case checker::TypeFlag::BYTE:
841 case checker::TypeFlag::CHAR:
842 case checker::TypeFlag::SHORT:
843 case checker::TypeFlag::INT: {
844 Ra().Emit<IntCompare>(node, lhs, ifFalse);
845 break;
846 }
847 default: {
848 UNREACHABLE();
849 }
850 }
851
852 SetAccumulatorType(Checker()->GlobalETSBooleanType());
853 }
854
855 template <typename ObjCompare, typename DynCompare>
RefEqualityStrict(const ir::AstNode * node,VReg lhs,Label * ifFalse)856 void RefEqualityStrict(const ir::AstNode *node, VReg lhs, Label *ifFalse)
857 {
858 if (GetAccumulatorType()->IsETSDynamicType() || GetVRegType(lhs)->IsETSDynamicType()) {
859 RefEqualityStrictDynamic<DynCompare>(node, lhs, ifFalse);
860 } else {
861 Ra().Emit<ObjCompare>(node, lhs, ifFalse);
862 }
863
864 ToBinaryResult(node, ifFalse);
865 SetAccumulatorType(Checker()->GlobalETSBooleanType());
866 }
867
868 template <typename IntCompare, typename CondCompare>
BinaryRelation(const ir::AstNode * node,VReg lhs,Label * ifFalse)869 void BinaryRelation(const ir::AstNode *node, VReg lhs, Label *ifFalse)
870 {
871 BinaryRelationCondition<IntCompare, CondCompare>(node, lhs, ifFalse);
872 ToBinaryResult(node, ifFalse);
873 SetAccumulatorType(Checker()->GlobalETSBooleanType());
874 }
875
876 template <typename IntCompare, typename CondCompare>
BinaryRelationCondition(const ir::AstNode * node,VReg lhs,Label * ifFalse)877 void BinaryRelationCondition(const ir::AstNode *node, VReg lhs, Label *ifFalse)
878 {
879 auto typeKind = checker::ETSChecker::TypeKind(targetType_);
880
881 switch (typeKind) {
882 case checker::TypeFlag::DOUBLE: {
883 BinaryFloatingPointComparison<FcmpgWide, FcmplWide, CondCompare>(node, lhs, ifFalse);
884 break;
885 }
886 case checker::TypeFlag::FLOAT: {
887 BinaryFloatingPointComparison<Fcmpg, Fcmpl, CondCompare>(node, lhs, ifFalse);
888 break;
889 }
890 case checker::TypeFlag::LONG: {
891 BinaryNumberComparison<CmpWide, CondCompare>(node, lhs, ifFalse);
892 break;
893 }
894 case checker::TypeFlag::ETS_BOOLEAN:
895 case checker::TypeFlag::BYTE:
896 case checker::TypeFlag::SHORT:
897 case checker::TypeFlag::CHAR:
898 case checker::TypeFlag::INT: {
899 Ra().Emit<IntCompare>(node, lhs, ifFalse);
900 break;
901 }
902 default: {
903 UNREACHABLE();
904 }
905 }
906
907 SetAccumulatorType(Checker()->GlobalETSBooleanType());
908 }
909
910 template <typename CompareGreater, typename CompareLess, typename CondCompare>
BinaryFloatingPointComparison(const ir::AstNode * node,VReg lhs,Label * ifFalse)911 void BinaryFloatingPointComparison(const ir::AstNode *node, VReg lhs, Label *ifFalse)
912 {
913 if constexpr (std::is_same_v<CondCompare, Jgez> || std::is_same_v<CondCompare, Jgtz>) {
914 BinaryNumberComparison<CompareGreater, CondCompare>(node, lhs, ifFalse);
915 } else {
916 BinaryNumberComparison<CompareLess, CondCompare>(node, lhs, ifFalse);
917 }
918 }
919
920 template <typename IntOp, typename LongOp, typename FloatOp, typename DoubleOp>
BinaryArithmetic(const ir::AstNode * node,VReg lhs)921 void BinaryArithmetic(const ir::AstNode *node, VReg lhs)
922 {
923 auto typeKind = checker::ETSChecker::TypeKind(targetType_);
924
925 switch (typeKind) {
926 case checker::TypeFlag::DOUBLE: {
927 Ra().Emit<DoubleOp>(node, lhs);
928 SetAccumulatorType(Checker()->GlobalDoubleType());
929 break;
930 }
931 case checker::TypeFlag::FLOAT: {
932 Ra().Emit<FloatOp>(node, lhs);
933 SetAccumulatorType(Checker()->GlobalFloatType());
934 break;
935 }
936 default: {
937 BinaryBitwiseArithmetic<IntOp, LongOp>(node, lhs);
938 }
939 }
940 }
941
942 template <typename IntOp, typename LongOp>
BinaryBitwiseArithmetic(const ir::AstNode * node,VReg lhs)943 void BinaryBitwiseArithmetic(const ir::AstNode *node, VReg lhs)
944 {
945 auto typeKind = checker::ETSChecker::TypeKind(targetType_);
946
947 switch (typeKind) {
948 case checker::TypeFlag::LONG: {
949 Ra().Emit<LongOp>(node, lhs);
950 SetAccumulatorType(Checker()->GlobalLongType());
951 break;
952 }
953 case checker::TypeFlag::BYTE:
954 case checker::TypeFlag::SHORT:
955 case checker::TypeFlag::INT: {
956 Ra().Emit<IntOp>(node, lhs);
957 SetAccumulatorType(Checker()->GlobalIntType());
958 break;
959 }
960 case checker::TypeFlag::ETS_BOOLEAN: {
961 Ra().Emit<IntOp>(node, lhs);
962 SetAccumulatorType(Checker()->GlobalETSBooleanType());
963 break;
964 }
965 case checker::TypeFlag::CHAR: {
966 Ra().Emit<IntOp>(node, lhs);
967 SetAccumulatorType(Checker()->GlobalCharType());
968 break;
969 }
970 default: {
971 UNREACHABLE();
972 }
973 }
974 }
975 // NOLINTBEGIN(cppcoreguidelines-macro-usage, readability-container-size-empty)
976 #define COMPILE_ARG(idx) \
977 ASSERT((idx) < arguments.size()); \
978 ASSERT((idx) < signature->Params().size() || signature->RestVar() != nullptr); \
979 auto *param##idx = (idx) < signature->Params().size() ? signature->Params()[(idx)] : signature->RestVar(); \
980 auto *paramType##idx = param##idx->TsType(); \
981 auto ttctx##idx = TargetTypeContext(this, paramType##idx); \
982 arguments[idx]->Compile(this); \
983 VReg arg##idx = AllocReg(); \
984 ApplyConversion(arguments[idx], nullptr); \
985 ApplyConversionAndStoreAccumulator(arguments[idx], arg##idx, paramType##idx)
986
987 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)988 void CallArgStart(const ir::AstNode *const node, const checker::Signature *signature, const VReg argStart,
989 const ArenaVector<ir::Expression *> &arguments)
990 {
991 RegScope rs(this);
992 const auto name = signature->InternalName();
993
994 switch (arguments.size()) {
995 case 0U: {
996 Ra().Emit<Short, 1>(node, name, argStart, dummyReg_);
997 break;
998 }
999 case 1U: {
1000 COMPILE_ARG(0);
1001 Ra().Emit<Short>(node, name, argStart, arg0);
1002 break;
1003 }
1004 case 2U: {
1005 COMPILE_ARG(0);
1006 COMPILE_ARG(1);
1007 Ra().Emit<General, 3U>(node, name, argStart, arg0, arg1, dummyReg_);
1008 break;
1009 }
1010 case 3U: {
1011 COMPILE_ARG(0);
1012 COMPILE_ARG(1);
1013 COMPILE_ARG(2);
1014 Ra().Emit<General>(node, name, argStart, arg0, arg1, arg2);
1015 break;
1016 }
1017 default: {
1018 for (size_t idx = 0; idx < arguments.size(); idx++) {
1019 COMPILE_ARG(idx);
1020 }
1021
1022 Rra().Emit<Range>(node, argStart, arguments.size() + 1, name, argStart);
1023 break;
1024 }
1025 }
1026 }
1027
1028 template <typename Short, typename General, typename Range>
CallImpl(const ir::AstNode * node,checker::Signature const * signature,const ArenaVector<ir::Expression * > & arguments)1029 void CallImpl(const ir::AstNode *node, checker::Signature const *signature,
1030 const ArenaVector<ir::Expression *> &arguments)
1031 {
1032 ASSERT(signature != nullptr);
1033 RegScope rs(this);
1034
1035 switch (arguments.size()) {
1036 case 0U: {
1037 Ra().Emit<Short, 0U>(node, signature->InternalName(), dummyReg_, dummyReg_);
1038 break;
1039 }
1040 case 1U: {
1041 COMPILE_ARG(0);
1042 Ra().Emit<Short, 1U>(node, signature->InternalName(), arg0, dummyReg_);
1043 break;
1044 }
1045 case 2U: {
1046 COMPILE_ARG(0);
1047 COMPILE_ARG(1);
1048 Ra().Emit<Short, 2U>(node, signature->InternalName(), arg0, arg1);
1049 break;
1050 }
1051 case 3U: {
1052 COMPILE_ARG(0);
1053 COMPILE_ARG(1);
1054 COMPILE_ARG(2);
1055 Ra().Emit<General, 3U>(node, signature->InternalName(), arg0, arg1, arg2, dummyReg_);
1056 break;
1057 }
1058 case 4U: {
1059 COMPILE_ARG(0);
1060 COMPILE_ARG(1);
1061 COMPILE_ARG(2);
1062 COMPILE_ARG(3);
1063 Ra().Emit<General, 4U>(node, signature->InternalName(), arg0, arg1, arg2, arg3);
1064 break;
1065 }
1066 default: {
1067 VReg argStart = NextReg();
1068
1069 for (size_t idx = 0; idx < arguments.size(); idx++) {
1070 COMPILE_ARG(idx);
1071 }
1072
1073 Rra().Emit<Range>(node, argStart, arguments.size(), signature->InternalName(), argStart);
1074 break;
1075 }
1076 }
1077 }
1078 #undef COMPILE_ARG
1079
1080 #define COMPILE_ARG(idx, shift) \
1081 ASSERT((idx) < arguments.size()); \
1082 ASSERT((idx) + (shift) < signature->Params().size() || signature->RestVar() != nullptr); \
1083 auto *paramType##idx = (idx) + (shift) < signature->Params().size() \
1084 ? signature->Params()[(idx) + (shift)]->TsType() \
1085 : signature->RestVar()->TsType(); \
1086 auto ttctx##idx = TargetTypeContext(this, paramType##idx); \
1087 VReg arg##idx = AllocReg(); \
1088 arguments[idx]->Compile(this); \
1089 ApplyConversionAndStoreAccumulator(arguments[idx], arg##idx, paramType##idx)
1090
1091 template <typename Short, typename General, typename Range>
CallDynamicImpl(CallDynamicData data,checker::Signature * signature,const ArenaVector<ir::Expression * > & arguments)1092 void CallDynamicImpl(CallDynamicData data, checker::Signature *signature,
1093 const ArenaVector<ir::Expression *> &arguments)
1094 {
1095 RegScope rs(this);
1096 const auto name = signature->InternalName();
1097
1098 switch (arguments.size()) {
1099 case 0U: {
1100 Ra().Emit<Short>(data.node, name, data.obj, data.param2);
1101 break;
1102 }
1103 case 1U: {
1104 COMPILE_ARG(0, 2U);
1105 Ra().Emit<General, 3U>(data.node, name, data.obj, data.param2, arg0, dummyReg_);
1106 break;
1107 }
1108 case 2U: {
1109 COMPILE_ARG(0, 2U);
1110 COMPILE_ARG(1, 2U);
1111 Ra().Emit<General>(data.node, name, data.obj, data.param2, arg0, arg1);
1112 break;
1113 }
1114 default: {
1115 for (size_t idx = 0; idx < arguments.size(); idx++) {
1116 COMPILE_ARG(idx, 2U);
1117 }
1118
1119 Rra().Emit<Range>(data.node, data.obj, arguments.size() + 2U, name, data.obj);
1120 break;
1121 }
1122 }
1123 }
1124
1125 template <typename Short, typename General, typename Range>
CallDynamicImpl(CallDynamicData data,VReg param3,checker::Signature * signature,const ArenaVector<ir::Expression * > & arguments)1126 void CallDynamicImpl(CallDynamicData data, VReg param3, checker::Signature *signature,
1127 const ArenaVector<ir::Expression *> &arguments)
1128 {
1129 RegScope rs(this);
1130 const auto name = signature->InternalName();
1131
1132 switch (arguments.size()) {
1133 case 0U: {
1134 Ra().Emit<General, 3U>(data.node, name, data.obj, data.param2, param3, dummyReg_);
1135 break;
1136 }
1137 case 1U: {
1138 COMPILE_ARG(0, 3U);
1139 Ra().Emit<General>(data.node, name, data.obj, data.param2, param3, arg0);
1140 break;
1141 }
1142 default: {
1143 for (size_t idx = 0; idx < arguments.size(); idx++) {
1144 COMPILE_ARG(idx, 3U);
1145 }
1146
1147 Rra().Emit<Range>(data.node, data.obj, arguments.size() + 3U, name, data.obj);
1148 break;
1149 }
1150 }
1151 }
1152
1153 #undef COMPILE_ARG
1154 // NOLINTEND(cppcoreguidelines-macro-usage, readability-container-size-empty)
1155
1156 void ToBinaryResult(const ir::AstNode *node, Label *ifFalse);
1157
1158 template <typename T>
1159 void LoadAccumulatorNumber(const ir::AstNode *node, T number, checker::TypeFlag targetType);
1160 template <typename T>
1161 void SetAccumulatorTargetType(const ir::AstNode *node, checker::TypeFlag typeKind, T number);
1162 void InitializeContainingClass();
1163
1164 util::StringView FormDynamicModulePropReference(const varbinder::Variable *var);
1165 util::StringView FormDynamicModulePropReference(const ir::ETSImportDeclaration *import);
1166
1167 friend class TargetTypeContext;
1168
1169 VReg acc_ {};
1170 const checker::Type *targetType_ {};
1171 const checker::ETSObjectType *containingObjectType_ {};
1172 };
1173
1174 template <typename T>
SetAccumulatorTargetType(const ir::AstNode * node,checker::TypeFlag typeKind,T number)1175 void ETSGen::SetAccumulatorTargetType(const ir::AstNode *node, checker::TypeFlag typeKind, T number)
1176 {
1177 switch (typeKind) {
1178 case checker::TypeFlag::ETS_BOOLEAN:
1179 case checker::TypeFlag::BYTE: {
1180 Sa().Emit<Ldai>(node, static_cast<checker::ByteType::UType>(number));
1181 SetAccumulatorType(Checker()->GlobalByteType());
1182 break;
1183 }
1184 case checker::TypeFlag::CHAR: {
1185 Sa().Emit<Ldai>(node, static_cast<checker::CharType::UType>(number));
1186 SetAccumulatorType(Checker()->GlobalCharType());
1187 break;
1188 }
1189 case checker::TypeFlag::SHORT: {
1190 Sa().Emit<Ldai>(node, static_cast<checker::ShortType::UType>(number));
1191 SetAccumulatorType(Checker()->GlobalShortType());
1192 break;
1193 }
1194 case checker::TypeFlag::INT: {
1195 Sa().Emit<Ldai>(node, static_cast<checker::IntType::UType>(number));
1196 SetAccumulatorType(Checker()->GlobalIntType());
1197 break;
1198 }
1199 case checker::TypeFlag::LONG: {
1200 Sa().Emit<LdaiWide>(node, static_cast<checker::LongType::UType>(number));
1201 SetAccumulatorType(Checker()->GlobalLongType());
1202 break;
1203 }
1204 case checker::TypeFlag::FLOAT: {
1205 Sa().Emit<Fldai>(node, static_cast<checker::FloatType::UType>(number));
1206 SetAccumulatorType(Checker()->GlobalFloatType());
1207 break;
1208 }
1209 case checker::TypeFlag::DOUBLE: {
1210 Sa().Emit<FldaiWide>(node, static_cast<checker::DoubleType::UType>(number));
1211 SetAccumulatorType(Checker()->GlobalDoubleType());
1212 break;
1213 }
1214 case checker::TypeFlag::ETS_STRING_ENUM:
1215 [[fallthrough]];
1216 case checker::TypeFlag::ETS_INT_ENUM: {
1217 Sa().Emit<Ldai>(node, static_cast<checker::ETSEnumType::UType>(number));
1218 SetAccumulatorType(Checker()->GlobalIntType());
1219 break;
1220 }
1221 default: {
1222 UNREACHABLE();
1223 }
1224 }
1225 }
1226
1227 template <typename T>
LoadAccumulatorNumber(const ir::AstNode * node,T number,checker::TypeFlag targetType)1228 void ETSGen::LoadAccumulatorNumber(const ir::AstNode *node, T number, checker::TypeFlag targetType)
1229 {
1230 auto typeKind = targetType_ && (!targetType_->IsETSObjectType() && !targetType_->IsETSUnionType() &&
1231 !targetType_->IsETSArrayType())
1232 ? checker::ETSChecker::TypeKind(targetType_)
1233 : targetType;
1234
1235 SetAccumulatorTargetType(node, typeKind, number);
1236
1237 if (targetType_ && (targetType_->IsETSObjectType() || targetType_->IsETSUnionType())) {
1238 ApplyConversion(node, targetType_);
1239 }
1240 }
1241
1242 } // namespace ark::es2panda::compiler
1243
1244 #endif
1245