diff --git a/ets2panda/compiler/core/ETSCompiler.cpp b/ets2panda/compiler/core/ETSCompiler.cpp index 75fd7d3de505e7bba5f6d34adae7e5d6b9110aeb..000f3eeb2e7dd61290b3502f678f9e485a0ca1ac 100644 --- a/ets2panda/compiler/core/ETSCompiler.cpp +++ b/ets2panda/compiler/core/ETSCompiler.cpp @@ -613,7 +613,7 @@ void ETSCompiler::Compile(const ir::BinaryExpression *expr) const compiler::VReg lhs = etsg->AllocReg(); if (expr->OperatorType() == lexer::TokenType::PUNCTUATOR_PLUS && expr->OperationType()->IsETSStringType()) { - etsg->BuildString(expr); + etsg->BuildString(expr, lhs); return; } diff --git a/ets2panda/compiler/core/ETSGen.cpp b/ets2panda/compiler/core/ETSGen.cpp index 5b08e72fe6e600776d6686a52e1bd0f32e7d4ef2..01335696faec196aa136ed7be6ab8afb3edeb547 100644 --- a/ets2panda/compiler/core/ETSGen.cpp +++ b/ets2panda/compiler/core/ETSGen.cpp @@ -15,11 +15,13 @@ #include "ETSGen-inl.h" +#include "compiler/core/codeGen.h" #include "compiler/core/regScope.h" #include "generated/isa.h" #include "generated/signatures.h" #include "ir/base/scriptFunction.h" #include "ir/base/classDefinition.h" +#include "ir/expression.h" #include "ir/statement.h" #include "ir/expressions/assignmentExpression.h" #include "ir/expressions/identifier.h" @@ -34,6 +36,8 @@ #include "compiler/base/lreference.h" #include "compiler/base/catchTable.h" #include "compiler/core/dynamicContext.h" +#include "macros.h" +#include "util/es2pandaMacros.h" #include "varbinder/ETSBinder.h" #include "varbinder/variable.h" #include "checker/types/type.h" @@ -2311,6 +2315,77 @@ void ETSGen::UpdateBigInt(const ir::Expression *node, VReg arg, lexer::TokenType } } +void ETSGen::ConcatStrings(const ir::BinaryExpression *node, VReg lhs) +{ + ES2PANDA_ASSERT(node->OperationType()->IsETSStringType()); + node->CompileOperands(this, lhs); + RegScope rs(this); + auto rhs = AllocReg(); + StoreAccumulator(node->Right(), rhs); + + ToString(node->Left(), lhs); + StoreAccumulator(node->Left(), lhs); + + ToString(node->Right(), rhs); + StoreAccumulator(node->Right(), rhs); + + ES2PANDA_ASSERT(GetVRegType(lhs)->IsETSStringType()); + ES2PANDA_ASSERT(GetVRegType(rhs)->IsETSStringType()); + CallExact(node, Signatures::BUILTIN_STRING_BUILDER_CONCAT_STRING, lhs, rhs); + SetAccumulatorType(node->TsType()); +} + +void ETSGen::ToString(const ir::Expression *node, VReg arg) +{ + const auto regType = GetVRegType(arg); + if (regType->IsETSReferenceType()) { + if (regType->PossiblyETSUndefined()) { + const auto ifUndefined = AllocLabel(); + const auto end = AllocLabel(); + LoadAccumulator(node, arg); + BranchIfUndefined(node, ifUndefined); + CallVirtual(node, Signatures::BUILTIN_OBJECT_TO_STRING, arg); + SetAccumulatorType(Checker()->GlobalBuiltinETSStringType()); + JumpTo(node, end); + + SetLabel(node, ifUndefined); + LoadAccumulatorString(node, "undefined"); + + SetLabel(node, end); + return; + } + if (regType->IsETSStringType()) { + LoadAccumulator(node, arg); + } else { + CallVirtual(node, Signatures::BUILTIN_OBJECT_TO_STRING, arg); + SetAccumulatorType(Checker()->GlobalBuiltinETSStringType()); + } + return; + } + + using TSign = std::pair; + constexpr std::array TO_STRING_METHODS { + TSign {checker::TypeFlag::ETS_BOOLEAN, Signatures::BUILTIN_STRING_BUILDER_TO_STRING_BOOLEAN}, + TSign {checker::TypeFlag::CHAR, Signatures::BUILTIN_STRING_BUILDER_TO_STRING_CHAR}, + TSign {checker::TypeFlag::SHORT, Signatures::BUILTIN_STRING_BUILDER_TO_STRING_INT}, + TSign {checker::TypeFlag::BYTE, Signatures::BUILTIN_STRING_BUILDER_TO_STRING_INT}, + TSign {checker::TypeFlag::INT, Signatures::BUILTIN_STRING_BUILDER_TO_STRING_INT}, + TSign {checker::TypeFlag::LONG, Signatures::BUILTIN_STRING_BUILDER_TO_STRING_LONG}, + TSign {checker::TypeFlag::FLOAT, Signatures::BUILTIN_STRING_BUILDER_TO_STRING_FLOAT}, + TSign {checker::TypeFlag::DOUBLE, Signatures::BUILTIN_STRING_BUILDER_TO_STRING_DOUBLE}, + }; + + const auto typeFlag = checker::ETSChecker::ETSType(regType); + const auto iter = std::find_if(TO_STRING_METHODS.begin(), TO_STRING_METHODS.end(), + [typeFlag](TSign p) { return p.first == typeFlag; }); + if (iter != TO_STRING_METHODS.end()) { + CallExact(node, iter->second, arg); + SetAccumulatorType(Checker()->GlobalBuiltinETSStringType()); + return; + } + ES2PANDA_UNREACHABLE(); +} + void ETSGen::StringBuilderAppend(const ir::AstNode *node, VReg builder) { RegScope rs(this); @@ -2386,7 +2461,18 @@ void ETSGen::StringBuilder(const ir::Expression *const left, const ir::Expressio StringBuilderAppend(right, builder); } -void ETSGen::BuildString(const ir::Expression *node) +void ETSGen::BuildString(const ir::BinaryExpression *node, VReg lhs) +{ + // #26986 use concat instead of append + if (Context()->config->options->IsEtsStringsConcat()) { + node->CompileOperands(this, lhs); + ConcatStrings(node, lhs); + return; + } + CreateStringBuilder(node); +} + +void ETSGen::CreateStringBuilder(const ir::Expression *node) { RegScope rs(this); @@ -2425,6 +2511,16 @@ void ETSGen::CallBigIntBinaryComparison(const ir::Expression *node, VReg lhs, VR } void ETSGen::BuildTemplateString(const ir::TemplateLiteral *node) +{ + // #26986 use concat instead of append + if (Context()->config->options->IsEtsStringsConcat()) { + ConcatTemplateString(node); + } else { + AppendTemplateString(node); + } +} + +void ETSGen::AppendTemplateString(const ir::TemplateLiteral *node) { RegScope rs(this); @@ -2433,21 +2529,17 @@ void ETSGen::BuildTemplateString(const ir::TemplateLiteral *node) auto builder = AllocReg(); StoreAccumulator(node, builder); - // Just to reduce extra nested level(s): auto const appendExpressions = [this, &builder](ArenaVector const &expressions, ArenaVector const &quasis) -> void { - auto const num = expressions.size(); - std::size_t i = 0U; - - while (i < num) { + ES2PANDA_ASSERT(quasis.size() == expressions.size() + 1); + for (size_t i = 0; i < expressions.size();) { StringBuilderAppend(expressions[i], builder); if (!quasis[++i]->Raw().Empty()) { StringBuilderAppend(quasis[i], builder); } } }; - if (auto const &quasis = node->Quasis(); !quasis.empty()) { if (!quasis[0]->Raw().Empty()) { StringBuilderAppend(quasis[0], builder); @@ -2457,9 +2549,56 @@ void ETSGen::BuildTemplateString(const ir::TemplateLiteral *node) appendExpressions(expressions, quasis); } } - CallExact(node, Signatures::BUILTIN_STRING_BUILDER_TO_STRING, builder); + SetAccumulatorType(node->TsType()); +} +void ETSGen::ConcatTemplateString(const ir::TemplateLiteral *node) +{ + const auto &quasis = node->Quasis(); + const auto &expressions = node->Expressions(); + if (quasis.empty()) { + LoadAccumulatorString(node, ""); + return; + } + ES2PANDA_ASSERT(quasis.size() == expressions.size() + 1); + const RegScope rs {this}; + const auto result = AllocReg(); + const auto reg = AllocReg(); + // collect expressions + std::vector buffer {}; + buffer.reserve(quasis.size() + expressions.size()); + if (!quasis[0]->Raw().Empty()) { + buffer.push_back(quasis[0]); + } + for (size_t i = 0; i < expressions.size(); ++i) { + buffer.push_back(expressions[i]); + if (!quasis[i + 1]->Raw().Empty()) { + buffer.push_back(quasis[i + 1]); + } + } + // concat buffered expressions + if (buffer.empty()) { + LoadAccumulatorString(node, ""); + return; + } + const auto node2str = [this, reg](const ir::Expression *expr) { + expr->Compile(this); + StoreAccumulator(expr, reg); + ToString(expr, reg); + ES2PANDA_ASSERT(GetAccumulatorType()->IsETSStringType()); + }; + auto iter = buffer.begin(); + node2str(*iter); + StoreAccumulator(node, result); + for (++iter; iter != buffer.end(); ++iter) { + node2str(*iter); + StoreAccumulator(*iter, reg); + CallExact(node, Signatures::BUILTIN_STRING_BUILDER_CONCAT_STRING, result, reg); + SetAccumulatorType(Checker()->GlobalBuiltinETSStringType()); + StoreAccumulator(node, result); + } + LoadAccumulator(node, result); SetAccumulatorType(node->TsType()); } diff --git a/ets2panda/compiler/core/ETSGen.h b/ets2panda/compiler/core/ETSGen.h index 71ce602b2cb96500fec0689a1ffb17d324e71db5..4b306cccb2659231a1f06fa6f9dd783fe9a40d51 100644 --- a/ets2panda/compiler/core/ETSGen.h +++ b/ets2panda/compiler/core/ETSGen.h @@ -287,7 +287,9 @@ public: // 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 BuildString(const ir::BinaryExpression *node, VReg lhs); + void ConcatStrings(const ir::BinaryExpression *node, VReg lhs); + void ToString(const ir::Expression *node, VReg arg); 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); @@ -477,9 +479,12 @@ private: const VReg dummyReg_ = VReg::RegStart(); void LoadConstantObject(const ir::Expression *node, const checker::Type *type); + void CreateStringBuilder(const ir::Expression *node); 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); + void AppendTemplateString(const ir::TemplateLiteral *node); + void ConcatTemplateString(const ir::TemplateLiteral *node); util::StringView FormClassPropReference(varbinder::Variable const *var); void UnaryMinus(const ir::AstNode *node); void UnaryTilde(const ir::AstNode *node); diff --git a/ets2panda/compiler/scripts/signatures.yaml b/ets2panda/compiler/scripts/signatures.yaml index 15dfcdbfe96bd5c3900cd74d7e3162fbbc665e31..473010f26664a0275561b3cc7719c270cd385917 100644 --- a/ets2panda/compiler/scripts/signatures.yaml +++ b/ets2panda/compiler/scripts/signatures.yaml @@ -962,54 +962,116 @@ signatures: return_type: PRIMITIVE_VOID ref: BUILTIN_STRING_BUILDER_CTOR + #26986 - callee: BUILTIN_STRING_BUILDER method_name: append params: [PRIMITIVE_BOOLEAN] return_type: BUILTIN_STRING_BUILDER ref: BUILTIN_STRING_BUILDER_APPEND_BOOLEAN + #26986 - callee: BUILTIN_STRING_BUILDER method_name: append params: [PRIMITIVE_CHAR] return_type: BUILTIN_STRING_BUILDER ref: BUILTIN_STRING_BUILDER_APPEND_CHAR + #26986 - callee: BUILTIN_STRING_BUILDER method_name: append params: [PRIMITIVE_INT] return_type: BUILTIN_STRING_BUILDER ref: BUILTIN_STRING_BUILDER_APPEND_INT + #26986 - callee: BUILTIN_STRING_BUILDER method_name: append params: [PRIMITIVE_LONG] return_type: BUILTIN_STRING_BUILDER ref: BUILTIN_STRING_BUILDER_APPEND_LONG + #26986 - callee: BUILTIN_STRING_BUILDER method_name: append params: [PRIMITIVE_FLOAT] return_type: BUILTIN_STRING_BUILDER ref: BUILTIN_STRING_BUILDER_APPEND_FLOAT + #26986 - callee: BUILTIN_STRING_BUILDER method_name: append params: [PRIMITIVE_DOUBLE] return_type: BUILTIN_STRING_BUILDER ref: BUILTIN_STRING_BUILDER_APPEND_DOUBLE + #26986 - callee: BUILTIN_STRING_BUILDER method_name: append params: [BUILTIN_STRING] return_type: BUILTIN_STRING_BUILDER ref: BUILTIN_STRING_BUILDER_APPEND_BUILTIN_STRING + #26986 - callee: BUILTIN_STRING_BUILDER method_name: toString params: [] return_type: BUILTIN_STRING ref: BUILTIN_STRING_BUILDER_TO_STRING + - callee: BUILTIN_STRING_BUILDER + method_name: toString + params: [PRIMITIVE_BOOLEAN] + return_type: BUILTIN_STRING + ref: BUILTIN_STRING_BUILDER_TO_STRING_BOOLEAN + + - callee: BUILTIN_STRING_BUILDER + method_name: toString + params: [PRIMITIVE_CHAR] + return_type: BUILTIN_STRING + ref: BUILTIN_STRING_BUILDER_TO_STRING_CHAR + + - callee: BUILTIN_STRING_BUILDER + method_name: toString + params: [PRIMITIVE_SHORT] + return_type: BUILTIN_STRING + ref: BUILTIN_STRING_BUILDER_TO_STRING_SHORT + + - callee: BUILTIN_STRING_BUILDER + method_name: toString + params: [PRIMITIVE_BYTE] + return_type: BUILTIN_STRING + ref: BUILTIN_STRING_BUILDER_TO_STRING_BYTE + + - callee: BUILTIN_STRING_BUILDER + method_name: toString + params: [PRIMITIVE_INT] + return_type: BUILTIN_STRING + ref: BUILTIN_STRING_BUILDER_TO_STRING_INT + + - callee: BUILTIN_STRING_BUILDER + method_name: toString + params: [PRIMITIVE_LONG] + return_type: BUILTIN_STRING + ref: BUILTIN_STRING_BUILDER_TO_STRING_LONG + + - callee: BUILTIN_STRING_BUILDER + method_name: toString + params: [PRIMITIVE_FLOAT] + return_type: BUILTIN_STRING + ref: BUILTIN_STRING_BUILDER_TO_STRING_FLOAT + + - callee: BUILTIN_STRING_BUILDER + method_name: toString + params: [PRIMITIVE_DOUBLE] + return_type: BUILTIN_STRING + ref: BUILTIN_STRING_BUILDER_TO_STRING_DOUBLE + + - callee: BUILTIN_STRING_BUILDER + method_name: concatStrings + params: [BUILTIN_STRING, BUILTIN_STRING] + return_type: BUILTIN_STRING + ref: BUILTIN_STRING_BUILDER_CONCAT_STRING + - callee: BUILTIN_BOOLEAN method_name: valueOf params: [PRIMITIVE_BOOLEAN] diff --git a/ets2panda/util/options.yaml b/ets2panda/util/options.yaml index 5aef15cb8f7068291c8f4876df2fae12a57de17b..a0b6072768ff8412ee4333e68a04dc3a38f090be 100644 --- a/ets2panda/util/options.yaml +++ b/ets2panda/util/options.yaml @@ -384,3 +384,9 @@ options: type: bool default: false description: compile all the files to abc in once. + +#26986 +- name: ets-strings-concat + type: bool + default: false + description: "(experimental) Use StringBuilder.concat instead of StringBuilder.append"