/* * Copyright (c) 2021-2024 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ES2PANDA_COMPILER_CORE_ETSGEN_H #define ES2PANDA_COMPILER_CORE_ETSGEN_H #include "ir/astNode.h" #include "varbinder/ETSBinder.h" #include "compiler/core/codeGen.h" #include "compiler/core/ETSfunction.h" #include "compiler/core/targetTypeContext.h" #include "checker/ETSchecker.h" #include "util/helpers.h" namespace ark::es2panda::compiler { class ETSGen final : public CodeGen { public: explicit ETSGen(ArenaAllocator *allocator, RegSpiller *spiller, public_lib::Context *context, std::tuple toCompile) noexcept; [[nodiscard]] const checker::ETSChecker *Checker() const noexcept; [[nodiscard]] const varbinder::ETSBinder *VarBinder() const noexcept; [[nodiscard]] const checker::Type *ReturnType() const noexcept; [[nodiscard]] const checker::ETSObjectType *ContainingObjectType() const noexcept; [[nodiscard]] VReg &Acc() noexcept; [[nodiscard]] VReg Acc() const noexcept; void SetAccumulatorType(const checker::Type *type); [[nodiscard]] const checker::Type *GetAccumulatorType() const; void CompileAndCheck(const ir::Expression *expr); [[nodiscard]] VReg StoreException(const ir::AstNode *node); void ApplyConversionAndStoreAccumulator(const ir::AstNode *node, VReg vreg, const checker::Type *targetType); void StoreAccumulator(const ir::AstNode *node, VReg vreg); void LoadAccumulator(const ir::AstNode *node, VReg vreg); [[nodiscard]] IRNode *AllocMov(const ir::AstNode *node, VReg vd, VReg vs) override; [[nodiscard]] IRNode *AllocMov(const ir::AstNode *node, OutVReg vd, VReg vs) override; void MoveVreg(const ir::AstNode *node, VReg vd, VReg vs); [[nodiscard]] checker::Type const *TypeForVar(varbinder::Variable const *var) const noexcept override; void LoadVar(const ir::Identifier *node, varbinder::Variable const *var); void LoadDynamicModuleVariable(const ir::AstNode *node, varbinder::Variable const *var); void LoadDynamicNamespaceVariable(const ir::AstNode *node, varbinder::Variable const *var); void StoreVar(const ir::Identifier *node, const varbinder::ConstScopeFindResult &result); void LoadStaticProperty(const ir::AstNode *node, const checker::Type *propType, const util::StringView &fullName); void StoreStaticProperty(const ir::AstNode *node, const checker::Type *propType, const util::StringView &fullName); void StoreStaticOwnProperty(const ir::AstNode *node, const checker::Type *propType, const util::StringView &name); [[nodiscard]] util::StringView FormClassPropReference(const checker::ETSObjectType *classType, const util::StringView &name); void StoreProperty(const ir::AstNode *node, const checker::Type *propType, VReg objReg, const util::StringView &name); void LoadProperty(const ir::AstNode *node, const checker::Type *propType, VReg objReg, const util::StringView &fullName); void StorePropertyDynamic(const ir::AstNode *node, const checker::Type *propType, VReg objReg, const util::StringView &propName); void LoadPropertyDynamic(const ir::AstNode *node, const checker::Type *propType, VReg objReg, const util::StringView &propName); void StoreElementDynamic(const ir::AstNode *node, VReg objectReg, VReg index); void LoadElementDynamic(const ir::AstNode *node, VReg objectReg); void StoreUnionProperty(const ir::AstNode *node, const checker::Type *propType, VReg objReg, const util::StringView &propName); void LoadUnionProperty(const ir::AstNode *node, const checker::Type *propType, VReg objReg, const util::StringView &propName); void LoadUndefinedDynamic(const ir::AstNode *node, Language lang); void LoadThis(const ir::AstNode *node); [[nodiscard]] VReg GetThisReg() const; const checker::Type *LoadDefaultValue(const ir::AstNode *node, const checker::Type *type); void EmitReturnVoid(const ir::AstNode *node); void ReturnAcc(const ir::AstNode *node); void BranchIfIsInstance(const ir::AstNode *node, VReg srcReg, const checker::Type *target, Label *ifTrue); void IsInstance(const ir::AstNode *node, VReg srcReg, checker::Type const *target); void IsInstanceDynamic(const ir::BinaryExpression *node, VReg srcReg, VReg tgtReg); void EmitFailedTypeCastException(const ir::AstNode *node, VReg src, checker::Type const *target); void BinaryLogic(const ir::AstNode *node, lexer::TokenType op, VReg lhs); void BinaryArithmLogic(const ir::AstNode *node, lexer::TokenType op, VReg lhs); void Binary(const ir::AstNode *node, lexer::TokenType op, VReg lhs); void Unary(const ir::AstNode *node, lexer::TokenType op); void Update(const ir::AstNode *node, lexer::TokenType op); void UpdateBigInt(const ir::Expression *node, VReg arg, lexer::TokenType op); bool TryLoadConstantExpression(const ir::Expression *node); void Condition(const ir::AstNode *node, lexer::TokenType op, VReg lhs, Label *ifFalse); template void ResolveConditionalResultFloat(const ir::AstNode *node, Label *realEndLabel) { auto type = GetAccumulatorType(); VReg tmpReg = AllocReg(); StoreAccumulator(node, tmpReg); if (type->IsFloatType()) { FloatIsNaN(node); } else { DoubleIsNaN(node); } Sa().Emit(node, 1); BranchIfFalse(node, realEndLabel); LoadAccumulator(node, tmpReg); VReg zeroReg = AllocReg(); if (type->IsFloatType()) { MoveImmediateToRegister(node, zeroReg, checker::TypeFlag::FLOAT, 0); BinaryNumberComparison(node, zeroReg, realEndLabel); } else { MoveImmediateToRegister(node, zeroReg, checker::TypeFlag::DOUBLE, 0); BinaryNumberComparison(node, zeroReg, realEndLabel); } } template void ResolveConditionalResultNumeric(const ir::AstNode *node, [[maybe_unused]] Label *ifFalse, Label **end) { auto type = GetAccumulatorType(); ASSERT(type != nullptr); auto realEndLabel = [end, ifFalse, this](bool useFalseLabel) { if (useFalseLabel) { return ifFalse; } if ((*end) == nullptr) { (*end) = AllocLabel(); } return (*end); }(USE_FALSE_LABEL); if (type->IsDoubleType() || type->IsFloatType()) { ResolveConditionalResultFloat(node, realEndLabel); } if (type->IsLongType()) { VReg zeroReg = AllocReg(); MoveImmediateToRegister(node, zeroReg, checker::TypeFlag::LONG, 0); BinaryNumberComparison(node, zeroReg, realEndLabel); } if constexpr (BEFORE_LOGICAL_NOT) { Label *zeroPrimitive = AllocLabel(); BranchIfFalse(node, zeroPrimitive); ToBinaryResult(node, zeroPrimitive); } } template void ResolveConditionalResultReference(const ir::AstNode *node) { auto const testString = [this, node]() { LoadStringLength(node); if constexpr (BEFORE_LOGICAL_NOT) { Label *zeroLenth = AllocLabel(); BranchIfFalse(node, zeroLenth); ToBinaryResult(node, zeroLenth); } }; auto type = GetAccumulatorType(); if (!type->PossiblyETSString()) { Sa().Emit(node, 1); return; } if (type->IsETSStringType()) { // should also be valid for string|null|undefined testString(); return; } Label *isString = AllocLabel(); Label *end = AllocLabel(); compiler::VReg objReg = AllocReg(); StoreAccumulator(node, objReg); Sa().Emit(node, Checker()->GlobalBuiltinETSStringType()->AssemblerName()); BranchIfTrue(node, isString); Sa().Emit(node, 1); Branch(node, end); SetLabel(node, isString); LoadAccumulator(node, objReg); InternalCheckCast(node, Checker()->GlobalBuiltinETSStringType()); // help verifier testString(); SetLabel(node, end); } template void ResolveConditionalResult(const ir::AstNode *node, [[maybe_unused]] Label *ifFalse) { auto type = GetAccumulatorType(); if (type->IsETSBooleanType()) { return; } Label *ifNullish {nullptr}; Label *end {nullptr}; if (type->PossiblyETSNullish()) { if constexpr (USE_FALSE_LABEL) { BranchIfNullish(node, ifFalse); } else { ifNullish = AllocLabel(); end = AllocLabel(); BranchIfNullish(node, ifNullish); } } if (type->DefinitelyETSNullish()) { // skip } else if (type->IsETSReferenceType()) { ResolveConditionalResultReference(node); } else { ResolveConditionalResultNumeric(node, ifFalse, &end); } if (ifNullish != nullptr) { Branch(node, end); SetLabel(node, ifNullish); Sa().Emit(node, 0); } if (end != nullptr) { SetLabel(node, end); } } template void ResolveConditionalResultIfFalse(const ir::AstNode *node, Label *ifFalse = nullptr) { ResolveConditionalResult(node, ifFalse); } template void ResolveConditionalResultIfTrue(const ir::AstNode *node, Label *ifFalse = nullptr) { ResolveConditionalResult(node, ifFalse); } void BranchIfFalse(const ir::AstNode *node, Label *ifFalse) { Sa().Emit(node, ifFalse); } void BranchIfTrue(const ir::AstNode *node, Label *ifTrue) { Sa().Emit(node, ifTrue); } void BranchIfNull(const ir::AstNode *node, Label *ifNull) { Sa().Emit(node, ifNull); } void BranchIfUndefined([[maybe_unused]] const ir::AstNode *node, [[maybe_unused]] Label *ifUndefined) { #ifdef PANDA_WITH_ETS Sa().Emit(node); Sa().Emit(node, ifUndefined); #else UNREACHABLE(); #endif // PANDA_WITH_ETS } void BranchIfNotUndefined([[maybe_unused]] const ir::AstNode *node, [[maybe_unused]] Label *ifUndefined) { #ifdef PANDA_WITH_ETS Sa().Emit(node); Sa().Emit(node, ifUndefined); #else UNREACHABLE(); #endif // PANDA_WITH_ETS } void BranchIfNotNull(const ir::AstNode *node, Label *ifNotNull) { Sa().Emit(node, ifNotNull); } void BranchIfNullish(const ir::AstNode *node, Label *ifNullish); void BranchIfNotNullish(const ir::AstNode *node, Label *ifNotNullish); void AssumeNonNullish(const ir::AstNode *node, checker::Type const *targetType); void JumpTo(const ir::AstNode *node, Label *labelTo) { Sa().Emit(node, labelTo); } void EmitThrow(const ir::AstNode *node, VReg err) { Ra().Emit(node, err); } void EmitNullishException(const ir::AstNode *node); void ThrowException(const ir::Expression *expr); bool ExtendWithFinalizer(ir::AstNode const *node, const ir::AstNode *originalNode, Label *prevFinnaly = nullptr); void Negate(const ir::AstNode *node); void LogicalNot(const ir::AstNode *node); void LoadAccumulatorByte(const ir::AstNode *node, int8_t number) { LoadAccumulatorNumber(node, number, checker::TypeFlag::BYTE); } void LoadAccumulatorShort(const ir::AstNode *node, int16_t number) { LoadAccumulatorNumber(node, number, checker::TypeFlag::SHORT); } void LoadAccumulatorInt(const ir::AstNode *node, int32_t number) { LoadAccumulatorNumber(node, number, checker::TypeFlag::INT); } void LoadAccumulatorWideInt(const ir::AstNode *node, int64_t number) { LoadAccumulatorNumber(node, number, checker::TypeFlag::LONG); } void LoadAccumulatorFloat(const ir::AstNode *node, float number) { LoadAccumulatorNumber(node, number, checker::TypeFlag::FLOAT); } void LoadAccumulatorDouble(const ir::AstNode *node, double number) { LoadAccumulatorNumber(node, number, checker::TypeFlag::DOUBLE); } void LoadAccumulatorBoolean(const ir::AstNode *node, bool value) { Sa().Emit(node, value ? 1 : 0); SetAccumulatorType(Checker()->GlobalETSBooleanType()); ApplyConversion(node, Checker()->GlobalETSBooleanType()); } void LoadAccumulatorString(const ir::AstNode *node, util::StringView str) { Sa().Emit(node, str); SetAccumulatorType(Checker()->GlobalETSStringLiteralType()); } void LoadAccumulatorBigInt(const ir::AstNode *node, util::StringView str) { Sa().Emit(node, str); SetAccumulatorType(Checker()->GlobalETSBigIntType()); } void LoadAccumulatorNull(const ir::AstNode *node, const checker::Type *type) { Sa().Emit(node); SetAccumulatorType(type); } void LoadAccumulatorUndefined([[maybe_unused]] const ir::AstNode *node) { #ifdef PANDA_WITH_ETS Sa().Emit(node); SetAccumulatorType(Checker()->GlobalETSUndefinedType()); #else UNREACHABLE(); #endif // PANDA_WITH_ETS } void LoadAccumulatorChar(const ir::AstNode *node, char16_t value) { Sa().Emit(node, value); SetAccumulatorType(Checker()->GlobalCharType()); ApplyConversion(node, Checker()->GlobalCharType()); } void LoadAccumulatorDynamicModule(const ir::AstNode *node, const ir::ETSImportDeclaration *import); void ApplyBoxingConversion(const ir::AstNode *node); void ApplyUnboxingConversion(const ir::AstNode *node); void ApplyConversion(const ir::AstNode *node) { if (targetType_ != nullptr) { ApplyConversion(node, targetType_); } } void ApplyConversionCast(const ir::AstNode *node, const checker::Type *targetType); void ApplyConversion(const ir::AstNode *node, const checker::Type *targetType); void ApplyCast(const ir::AstNode *node, const checker::Type *targetType); void ApplyCastToBoxingFlags(const ir::AstNode *node, const ir::BoxingUnboxingFlags targetType); void EmitBoxingConversion(ir::BoxingUnboxingFlags boxingFlag, const ir::AstNode *node); void EmitBoxingConversion(const ir::AstNode *node); void SwapBinaryOpArgs(const ir::AstNode *node, VReg lhs); VReg MoveAccToReg(const ir::AstNode *node); void LoadArrayLength(const ir::AstNode *node, VReg arrayReg); void LoadArrayElement(const ir::AstNode *node, VReg objectReg); void StoreArrayElement(const ir::AstNode *node, VReg objectReg, VReg index, const checker::Type *elementType); template void MoveImmediateToRegister(const ir::AstNode *node, VReg reg, const checker::TypeFlag valueType, T const value) { switch (valueType) { case checker::TypeFlag::ETS_BOOLEAN: [[fallthrough]]; case checker::TypeFlag::BYTE: { Ra().Emit(node, reg, static_cast(value)); SetVRegType(reg, Checker()->GlobalByteType()); break; } case checker::TypeFlag::CHAR: { Ra().Emit(node, reg, static_cast(value)); SetVRegType(reg, Checker()->GlobalCharType()); break; } case checker::TypeFlag::SHORT: { Ra().Emit(node, reg, static_cast(value)); SetVRegType(reg, Checker()->GlobalShortType()); break; } case checker::TypeFlag::INT: { Ra().Emit(node, reg, static_cast(value)); SetVRegType(reg, Checker()->GlobalIntType()); break; } case checker::TypeFlag::LONG: { Ra().Emit(node, reg, static_cast(value)); SetVRegType(reg, Checker()->GlobalLongType()); break; } case checker::TypeFlag::FLOAT: { Ra().Emit(node, reg, static_cast(value)); SetVRegType(reg, Checker()->GlobalFloatType()); break; } case checker::TypeFlag::DOUBLE: { Ra().Emit(node, reg, static_cast(value)); SetVRegType(reg, Checker()->GlobalDoubleType()); break; } default: { UNREACHABLE(); } } } template void IncrementImmediateRegister(const ir::AstNode *node, VReg reg, const checker::TypeFlag valueType, T const value) { switch (valueType) { // NOTE: operand of increment instruction (INCI) is defined in spec as 32-bit integer, // but its current implementation actually can work with 64-bit integers as well. case checker::TypeFlag::INT: { Ra().Emit(node, reg, static_cast(value)); break; } case checker::TypeFlag::CHAR: { Ra().Emit(node, reg, static_cast(value)); break; } case checker::TypeFlag::SHORT: { Ra().Emit(node, reg, static_cast(value)); break; } case checker::TypeFlag::ETS_BOOLEAN: [[fallthrough]]; case checker::TypeFlag::BYTE: { Ra().Emit(node, reg, static_cast(value)); break; } default: { UNREACHABLE(); } } } template void JumpCompareRegister(const ir::AstNode *node, VReg lhs, Label *ifFalse) { Ra().Emit(node, lhs, ifFalse); } void LoadStringLength(const ir::AstNode *node); void LoadStringChar(const ir::AstNode *node, VReg stringObj, VReg charIndex); void FloatIsNaN(const ir::AstNode *node); void DoubleIsNaN(const ir::AstNode *node); void CompileStatements(const ArenaVector &statements); // Cast void CastToBoolean(const ir::AstNode *node); void CastToByte(const ir::AstNode *node); void CastToChar(const ir::AstNode *node); void CastToShort(const ir::AstNode *node); void CastToDouble(const ir::AstNode *node); void CastToFloat(const ir::AstNode *node); void CastToLong(const ir::AstNode *node); void CastToInt(const ir::AstNode *node); void CastToString(const ir::AstNode *node); void CastToDynamic(const ir::AstNode *node, const checker::ETSDynamicType *type); void CastDynamicTo(const ir::AstNode *node, enum checker::TypeFlag typeFlag); void CastToReftype(const ir::AstNode *node, const checker::Type *targetType, bool unchecked); void CastDynamicToObject(const ir::AstNode *node, const checker::Type *targetType); void CastUnionToFunctionType(const ir::AstNode *node, const checker::ETSUnionType *unionType, checker::Signature *signatureTarget); void InternalIsInstance(const ir::AstNode *node, const checker::Type *target); void InternalCheckCast(const ir::AstNode *node, const checker::Type *target); void CheckedReferenceNarrowing(const ir::AstNode *node, const checker::Type *target); void GuardUncheckedType(const ir::AstNode *node, const checker::Type *unchecked, const checker::Type *target); // Call, Construct void NewArray(const ir::AstNode *node, VReg arr, VReg dim, const checker::Type *arrType); void NewObject(const ir::AstNode *node, util::StringView name, VReg athis); void BuildString(const ir::Expression *node); void CallBigIntUnaryOperator(const ir::Expression *node, VReg arg, util::StringView signature); void CallBigIntBinaryOperator(const ir::Expression *node, VReg lhs, VReg rhs, util::StringView signature); void CallBigIntBinaryComparison(const ir::Expression *node, VReg lhs, VReg rhs, util::StringView signature); void BuildTemplateString(const ir::TemplateLiteral *node); void InitObject(const ir::AstNode *node, checker::Signature const *signature, const ArenaVector &arguments) { CallImpl(node, signature, arguments); } bool IsDevirtualizedSignature(const checker::Signature *signature) { ASSERT(!signature->HasSignatureFlag(checker::SignatureFlags::STATIC)); return signature->HasSignatureFlag(checker::SignatureFlags::FINAL | checker::SignatureFlags::PRIVATE | checker::SignatureFlags::CONSTRUCTOR); } void CallExact(const ir::AstNode *node, checker::Signature *signature, const ArenaVector &arguments) { CallImpl(node, signature, arguments); } void CallExact(const ir::AstNode *const node, const checker::Signature *signature, const VReg arg0, const ArenaVector &arguments) { CallArgStart(node, signature, arg0, arguments); } void CallExact(const ir::AstNode *const node, const util::StringView name) { Ra().Emit(node, name, dummyReg_, dummyReg_); } void CallExact(const ir::AstNode *const node, const util::StringView name, const VReg arg0) { Ra().Emit(node, name, arg0, dummyReg_); } void CallExact(const ir::AstNode *const node, const util::StringView name, const VReg arg0, const VReg arg1) { Ra().Emit(node, name, arg0, arg1); } void CallExact(const ir::AstNode *const node, const util::StringView name, const VReg arg0, const VReg arg1, const VReg arg2) { Ra().Emit(node, name, arg0, arg1, arg2, dummyReg_); } void CallVirtual(const ir::AstNode *const node, const checker::Signature *signature, const VReg athis, const ArenaVector &arguments) { ASSERT(!signature->HasSignatureFlag(checker::SignatureFlags::STATIC)); ASSERT(!signature->Owner()->GetDeclNode()->IsFinal() || signature->IsFinal()); if (IsDevirtualizedSignature(signature)) { CallArgStart(node, signature, athis, arguments); } else { CallArgStart(node, signature, athis, arguments); } } void CallVirtual(const ir::AstNode *const node, const checker::Signature *signature, const VReg athis) { if (IsDevirtualizedSignature(signature)) { CallExact(node, signature->InternalName(), athis); } else { CallVirtual(node, signature->InternalName(), athis); } } void CallVirtual(const ir::AstNode *const node, const checker::Signature *signature, const VReg athis, const VReg arg0) { if (IsDevirtualizedSignature(signature)) { CallExact(node, signature->InternalName(), athis, arg0); } else { CallVirtual(node, signature->InternalName(), athis, arg0); } } void CallVirtual(const ir::AstNode *const node, const util::StringView name, const VReg athis) { Ra().Emit(node, name, athis, dummyReg_); } void CallVirtual(const ir::AstNode *const node, const util::StringView name, const VReg athis, const VReg arg0) { Ra().Emit(node, name, athis, arg0); } struct CallDynamicData { const ir::AstNode *node = nullptr; VReg obj; VReg param2; }; void CallDynamic(CallDynamicData data, checker::Signature *signature, const ArenaVector &arguments) { CallDynamicImpl(data, signature, arguments); } void CallDynamic(CallDynamicData data, VReg param3, checker::Signature *signature, const ArenaVector &arguments) { CallDynamicImpl(data, param3, signature, arguments); } #ifdef PANDA_WITH_ETS // The functions below use ETS specific instructions. // Compilation of es2panda fails if ETS plugin is disabled void LaunchExact(const ir::AstNode *node, checker::Signature *signature, const ArenaVector &arguments) { CallImpl(node, signature, arguments); } void LaunchVirtual(const ir::AstNode *const node, checker::Signature *const signature, const VReg athis, const ArenaVector &arguments) { if (IsDevirtualizedSignature(signature)) { CallArgStart(node, signature, athis, arguments); } else { CallArgStart(node, signature, athis, arguments); } } #endif // PANDA_WITH_ETS void CreateBigIntObject(const ir::AstNode *node, VReg arg0, std::string_view signature = Signatures::BUILTIN_BIGINT_CTOR); void GetType(const ir::AstNode *node, bool isEtsPrimitive) { if (isEtsPrimitive) { // NOTE: SzD. LoadStaticProperty if ETS stdlib has static TYPE constants otherwise fallback to LdaType } else { auto classRef = GetAccumulatorType()->AsETSObjectType()->AssemblerName(); Sa().Emit(node, classRef); } } ~ETSGen() override = default; NO_COPY_SEMANTIC(ETSGen); NO_MOVE_SEMANTIC(ETSGen); private: const VReg dummyReg_ = VReg::RegStart(); void EmitUnboxedCall(const ir::AstNode *node, std::string_view signatureFlag, const checker::Type *targetType, const checker::Type *boxedType); void LoadConstantObject(const ir::Expression *node, const checker::Type *type); void StringBuilderAppend(const ir::AstNode *node, VReg builder); void AppendString(const ir::Expression *binExpr, VReg builder); void StringBuilder(const ir::Expression *left, const ir::Expression *right, VReg builder); util::StringView FormClassPropReference(varbinder::Variable const *var); void UnaryMinus(const ir::AstNode *node); void UnaryTilde(const ir::AstNode *node); util::StringView ToAssemblerType(const es2panda::checker::Type *type) const; void TestIsInstanceConstant(const ir::AstNode *node, Label *ifTrue, VReg srcReg, checker::Type const *target); void TestIsInstanceConstituent(const ir::AstNode *node, std::tuple