From 51275a576e032fc74f43328b7e436b9265496f9b Mon Sep 17 00:00:00 2001 From: Aleksandr Semenov Date: Sat, 11 Nov 2023 11:02:49 +0300 Subject: [PATCH 1/2] es2panda: Parse jit-compiler and bco options Signed-off-by: Aleksandr Semenov --- ets2panda/test/CMakeLists.txt | 2 + ets2panda/test/options/CMakeLists.txt | 36 +++++++++++ ets2panda/util/options.cpp | 88 ++++++++++++++++++++++++++- 3 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 ets2panda/test/options/CMakeLists.txt diff --git a/ets2panda/test/CMakeLists.txt b/ets2panda/test/CMakeLists.txt index a602e480e5..ba88fb843f 100644 --- a/ets2panda/test/CMakeLists.txt +++ b/ets2panda/test/CMakeLists.txt @@ -118,3 +118,5 @@ if(PANDA_WITH_ETS) add_subdirectory(tsconfig) endif() + +add_subdirectory(options) diff --git a/ets2panda/test/options/CMakeLists.txt b/ets2panda/test/options/CMakeLists.txt new file mode 100644 index 0000000000..dd46aabd24 --- /dev/null +++ b/ets2panda/test/options/CMakeLists.txt @@ -0,0 +1,36 @@ +# Copyright (c) 2021-2023 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. + +if(CMAKE_CROSSCOMPILING) + return() +endif() + +add_custom_target(es2panda_tests_options) +add_dependencies(es2panda_tests es2panda_tests_options) + +function(check_option_help_otput target_name INPUT_ARGS OUTPUT_HELP_LINE) + separate_arguments(INPUT_ARGS) + add_custom_target(es2panda_check_opts_${target_name} + COMMENT "es2panda: checking option ${INPUT_ARGS}" + COMMAND es2panda ${INPUT_ARGS} 2> ${CMAKE_BINARY_DIR}/es2panda_check_opts_${target_name}.out || true + COMMAND grep -q ${OUTPUT_HELP_LINE} ${CMAKE_BINARY_DIR}/es2panda_check_opts_${target_name}.out + DEPENDS es2panda + ) + + add_dependencies(es2panda_tests_options es2panda_check_opts_${target_name}) +endfunction() + +check_option_help_otput(bco_opt "--help" "bco-optimizer:") +check_option_help_otput(bco_opt_help "--bco-optimizer --help" "bytecode-opt-peepholes:") +check_option_help_otput(comp_opt "--help" "bco-compiler:") +check_option_help_otput(comp_opt_help "--bco-compiler --help" "compiler-disasm-dump:") diff --git a/ets2panda/util/options.cpp b/ets2panda/util/options.cpp index c7f2350ca8..60e35b3075 100644 --- a/ets2panda/util/options.cpp +++ b/ets2panda/util/options.cpp @@ -19,6 +19,11 @@ #include +#ifdef PANDA_WITH_BYTECODE_OPTIMIZER +#include "bytecode_optimizer/bytecodeopt_options.h" +#include "compiler/compiler_options.h" +#endif + namespace panda::es2panda::util { template T RemoveExtension(T const &filename) @@ -55,9 +60,84 @@ static std::unordered_set StringToStringSet(const std::string &str) return res; } +// NOLINTNEXTLINE(modernize-avoid-c-arrays, hicpp-avoid-c-arrays) +static void SplitArgs(int argc, const char *argv[], std::vector &es2panda_args, + std::vector &bco_compiler_args, std::vector &bytecodeopt_args) +{ + constexpr std::string_view COMPILER_PREFIX = "--bco-compiler"; + constexpr std::string_view OPTIMIZER_PREFIX = "--bco-optimizer"; + + enum class OptState { ES2PANDA, JIT_COMPILER, OPTIMIZER }; + OptState opt_state = OptState::ES2PANDA; + + std::unordered_map *> args_map = {{OptState::ES2PANDA, &es2panda_args}, + {OptState::JIT_COMPILER, &bco_compiler_args}, + {OptState::OPTIMIZER, &bytecodeopt_args}}; + + for (int i = 1; i < argc; i++) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + const char *arg_i = argv[i]; + if (COMPILER_PREFIX == arg_i) { + opt_state = OptState::JIT_COMPILER; + continue; + } + + if (OPTIMIZER_PREFIX == arg_i) { + opt_state = OptState::OPTIMIZER; + continue; + } + + args_map[opt_state]->emplace_back(arg_i); + opt_state = OptState::ES2PANDA; + } +} + +template +static bool ParseComponentArgs(const std::vector &args, T &options) +{ + panda::PandArgParser parser; + options.AddOptions(&parser); + if (!parser.Parse(args)) { + std::cerr << parser.GetErrorString(); + std::cerr << parser.GetHelpString(); + return false; + } + + if (auto options_err = options.Validate(); options_err) { + std::cerr << "Error: " << options_err.value().GetMessage() << std::endl; + return false; + } + + return true; +} + +static bool ParseBCOCompilerOptions([[maybe_unused]] const std::vector &compiler_args, + [[maybe_unused]] const std::vector &bytecodeopt_args) +{ +#ifdef PANDA_WITH_BYTECODE_OPTIMIZER + if (!ParseComponentArgs(compiler_args, panda::compiler::OPTIONS)) { + return false; + } + if (!ParseComponentArgs(bytecodeopt_args, panda::bytecodeopt::OPTIONS)) { + return false; + } +#endif + + return true; +} + // NOLINTNEXTLINE(readability-function-size) bool Options::Parse(int argc, const char **argv) { + std::vector es2panda_args; + std::vector bco_compiler_args; + std::vector bytecodeopt_args; + + SplitArgs(argc, argv, es2panda_args, bco_compiler_args, bytecodeopt_args); + if (!ParseBCOCompilerOptions(bco_compiler_args, bytecodeopt_args)) { + return false; + } + panda::PandArg op_help("help", false, "Print this message and exit"); // parser @@ -128,7 +208,7 @@ bool Options::Parse(int argc, const char **argv) argparser_->EnableTail(); argparser_->EnableRemainder(); - if (!argparser_->Parse(argc, argv) || op_help.GetValue()) { + if (!argparser_->Parse(es2panda_args) || op_help.GetValue()) { std::stringstream ss; ss << argparser_->GetErrorString() << std::endl; @@ -139,6 +219,12 @@ bool Options::Parse(int argc, const char **argv) ss << "optional arguments:" << std::endl; ss << argparser_->GetHelpString() << std::endl; + ss << std::endl; + ss << "--bco-optimizer: Argument directly to bytecode optimizer can be passed after this prefix" << std::endl; + ss << "--bco-compiler: Argument directly to jit-compiler inside bytecode optimizer can be passed after this " + "prefix" + << std::endl; + error_msg_ = ss.str(); return false; } -- Gitee From 5808a7a333446319a5c6099d5577fb9c77bd5334 Mon Sep 17 00:00:00 2001 From: Zelentsov Dmitry Date: Mon, 20 Nov 2023 14:04:41 +0300 Subject: [PATCH 2/2] Check constraint circular dependency for type parameters. Issue: https://gitee.com/openharmony/arkcompiler_ets_frontend/issues/I8HODT?from=project-issue Tests: .../05.generics/01.generic_declarations/generic_interfaces/generic_interface_self_dependency4,5.ets Signed-off-by: Zelentsov Dmitry --- ets2panda/checker/ETSchecker.h | 5 +++ ets2panda/checker/ets/object.cpp | 55 +++++++++++++++++++++++++------- 2 files changed, 48 insertions(+), 12 deletions(-) diff --git a/ets2panda/checker/ETSchecker.h b/ets2panda/checker/ETSchecker.h index 87fc469310..e6acf295cc 100644 --- a/ets2panda/checker/ETSchecker.h +++ b/ets2panda/checker/ETSchecker.h @@ -587,7 +587,12 @@ private: } ArenaVector CreateTypeForTypeParameters(ir::TSTypeParameterDeclaration *type_params); + Type *CreateTypeParameterType(ir::TSTypeParameter *param); + + using Type2TypeMap = std::unordered_map; + void CheckTypeParameterConstraint(ir::TSTypeParameter *param, Type2TypeMap &extends); + void SetUpTypeParameterConstraint(ir::TSTypeParameter *param); ETSObjectType *SetUpParameterType(ir::TSTypeParameter *param); ETSObjectType *CreateETSObjectTypeCheckBuiltins(util::StringView name, ir::AstNode *decl_node, diff --git a/ets2panda/checker/ets/object.cpp b/ets2panda/checker/ets/object.cpp index 3de0b51247..5baa4a4ab0 100644 --- a/ets2panda/checker/ets/object.cpp +++ b/ets2panda/checker/ets/object.cpp @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Huawei Device Co., Ltd. + * Copyright (c) 2021-2023 - 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 @@ -175,9 +175,20 @@ ArenaVector ETSChecker::CreateTypeForTypeParameters(ir::TSTypeParameterD ArenaVector result {Allocator()->Adapter()}; checker::ScopeContext scope_ctx(this, type_params->Scope()); - for (auto *const param : type_params->Params()) { - result.push_back(CreateTypeParameterType(param)); + // Note: we have to run pure check loop first to avoid endless loop because of possible circular dependencies + Type2TypeMap extends {}; + for (auto *const type_param : type_params->Params()) { + if (auto *const constraint = type_param->Constraint(); + constraint != nullptr && constraint->IsETSTypeReference() && + constraint->AsETSTypeReference()->Part()->Name()->IsIdentifier()) { + CheckTypeParameterConstraint(type_param, extends); + } + } + + for (auto *const type_param : type_params->Params()) { + result.emplace_back(CreateTypeParameterType(type_param)); } + // The type parameter might be used in the constraint, like 'K extend Comparable', // so we need to create their type first, then set up the constraint for (auto *const param : type_params->Params()) { @@ -187,6 +198,35 @@ ArenaVector ETSChecker::CreateTypeForTypeParameters(ir::TSTypeParameterD return result; } +void ETSChecker::CheckTypeParameterConstraint(ir::TSTypeParameter *param, Type2TypeMap &extends) +{ + const auto type_param_name = param->Name()->Name().Utf8(); + const auto constraint_name = + param->Constraint()->AsETSTypeReference()->Part()->Name()->AsIdentifier()->Name().Utf8(); + + if (type_param_name == constraint_name) { + ThrowTypeError({"Type parameter '", type_param_name, "' cannot extend/implement itself."}, + param->Constraint()->Start()); + } + + auto it = extends.find(type_param_name); + if (it != extends.cend()) { + ThrowTypeError({"Type parameter '", type_param_name, "' is duplicated in the list."}, + param->Constraint()->Start()); + } + + it = extends.find(constraint_name); + while (it != extends.cend()) { + if (it->second == type_param_name) { + ThrowTypeError({"Type parameter '", type_param_name, "' has circular constraint dependency."}, + param->Constraint()->Start()); + } + it = extends.find(it->second); + } + + extends.emplace(type_param_name, constraint_name); +} + Type *ETSChecker::CreateTypeParameterType(ir::TSTypeParameter *const param) { auto const instantiate_supertype = [this](TypeFlag nullish_flags) { @@ -217,19 +257,10 @@ void ETSChecker::SetUpTypeParameterConstraint(ir::TSTypeParameter *const param) param_type = param->Name()->Variable()->TsType()->AsETSObjectType(); } if (param->Constraint() != nullptr) { - if (param->Constraint()->IsETSTypeReference() && - param->Constraint()->AsETSTypeReference()->Part()->Name()->IsIdentifier() && - param->Name()->Name() == - param->Constraint()->AsETSTypeReference()->Part()->Name()->AsIdentifier()->Name()) { - ThrowTypeError({"Type variable '", param->Name()->Name(), "' cannot depend on itself"}, - param->Constraint()->Start()); - } - if (param->Constraint()->IsETSTypeReference()) { const auto constraint_name = param->Constraint()->AsETSTypeReference()->Part()->Name()->AsIdentifier()->Name(); const auto *const type_param_scope = param->Parent()->AsTSTypeParameterDeclaration()->Scope(); - if (auto *const found_param = type_param_scope->FindLocal(constraint_name, varbinder::ResolveBindingOptions::BINDINGS); found_param != nullptr) { -- Gitee