Update runtime_core code

Issue: https://gitee.com/openharmony/arkcompiler_runtime_core/issues/I5G96F
Test: Test262 suit, ark unittest, rk3568 XTS, ark previewer demo

Signed-off-by: huangyu <huangyu76@huawei.com>
Change-Id: I3f63d129a07deaa27a390f556dcaa5651c098185
This commit is contained in:
huangyu 2022-07-16 17:41:58 +08:00
parent 2e9080968d
commit c658ccf319
2527 changed files with 340644 additions and 45117 deletions

View File

@ -1,9 +1,10 @@
Aleksandr Emelenko
Aleksandr Latikov
Aleksandr Lovkov
Aleksandr Popov
Aleksandr Semenov
Aleksei Grebenkin
Aleksei Sidorov
Alexander Semenov
Alexey Biryukov
Alexey Romanov
Alina Kropacheva

144
BUILD.gn
View File

@ -14,19 +14,29 @@
import("//arkcompiler/runtime_core/ark_config.gni")
import("//build/ohos.gni")
foreach(plugin, enabled_plugins) {
print("plugin $plugin is enabled")
}
group("ark_packages") {
deps = []
if (host_os != "mac") {
deps = [
deps += [
"$ark_root/libpandabase:libarkbase",
"$ark_root/libpandafile:libarkfile",
"$ark_root/libziparchive:libarkziparchive",
]
}
foreach(plugin, enabled_plugins) {
deps += [ "$ark_root/plugins/$plugin:ark_packages" ]
}
}
group("ark_host_linux_tools_packages") {
deps = []
if (host_os != "mac") {
deps = [
deps += [
"$ark_root/assembler:ark_asm(${host_toolchain})",
"$ark_root/disassembler:ark_disasm(${host_toolchain})",
"$ark_root/libpandabase:libarkbase(${host_toolchain})",
@ -34,21 +44,30 @@ group("ark_host_linux_tools_packages") {
"$ark_root/libziparchive:libarkziparchive(${host_toolchain})",
]
}
foreach(plugin, enabled_plugins) {
deps += [ "$ark_root/plugins/$plugin:ark_host_linux_tools_packages" ]
}
}
group("ark_host_windows_tools_packages") {
deps = []
if (host_os != "mac" && !ark_standalone_build) {
deps = [
deps += [
"$ark_root/assembler:ark_asm(//build/toolchain/mingw:mingw_x86_64)",
"$ark_root/disassembler:ark_disasm(//build/toolchain/mingw:mingw_x86_64)",
]
}
foreach(plugin, enabled_plugins) {
deps += [ "$ark_root/plugins/$plugin:ark_host_windows_tools_packages" ]
}
}
group("ark_host_mac_tools_packages") {
deps = []
if (host_os == "mac") {
if (host_cpu == "arm64") {
deps = [
deps += [
"$ark_root/assembler:ark_asm(//build/toolchain/mac:clang_arm64)",
"$ark_root/disassembler:ark_disasm(//build/toolchain/mac:clang_arm64)",
"$ark_root/libpandabase:libarkbase(//build/toolchain/mac:clang_arm64)",
@ -56,7 +75,7 @@ group("ark_host_mac_tools_packages") {
"$ark_root/libziparchive:libarkziparchive(//build/toolchain/mac:clang_arm64)",
]
} else {
deps = [
deps += [
"$ark_root/assembler:ark_asm(//build/toolchain/mac:clang_x64)",
"$ark_root/disassembler:ark_disasm(//build/toolchain/mac:clang_x64)",
"$ark_root/libpandabase:libarkbase(//build/toolchain/mac:clang_x64)",
@ -65,6 +84,10 @@ group("ark_host_mac_tools_packages") {
]
}
}
foreach(plugin, enabled_plugins) {
deps += [ "$ark_root/plugins/$plugin:ark_host_mac_tools_packages" ]
}
}
# Common config for ark source
@ -74,17 +97,21 @@ config("ark_config") {
}
include_dirs = [ "$ark_root" ]
defines = [ "PANDA_ENABLE_LTO" ]
defines = [ "PANDA_TARGET_MOBILE_WITH_MANAGED_LIBS=1" ]
if (is_linux) {
defines += [
"PANDA_TARGET_UNIX",
"PANDA_TARGET_LINUX",
"PANDA_WITH_BYTECODE_OPTIMIZER",
"PANDA_WITH_COMPILER",
"PANDA_USE_FUTEX",
]
} else if (is_mingw) {
defines += [
"PANDA_TARGET_WINDOWS",
"PANDA_WITH_BYTECODE_OPTIMIZER",
"PANDA_WITH_COMPILER",
"_CRTBLD",
"__LIBMSVCRT__",
]
@ -92,15 +119,21 @@ config("ark_config") {
defines += [
"PANDA_TARGET_UNIX",
"PANDA_TARGET_MACOS",
"PANDA_WITH_BYTECODE_OPTIMIZER",
"PANDA_WITH_COMPILER",
]
} else if (is_mob) {
defines += [
"PANDA_TARGET_UNIX",
"PANDA_USE_FUTEX",
"PANDA_TARGET_MOBILE",
"PANDA_TARGET_MOBILE_WITH_NATIVE_LIBS",
]
} else {
defines += [
"PANDA_TARGET_UNIX",
"PANDA_USE_FUTEX",
]
if (!is_standard_system && (current_cpu != "arm" || is_wearable_product)) {
defines += [ "PANDA_TARGET_MOBILE" ]
}
}
if (!is_debug) {
@ -113,7 +146,6 @@ config("ark_config") {
"-Wall",
"-Wextra",
"-Werror",
"-Wshadow",
"-fno-rtti",
"-fno-exceptions",
"-Wno-invalid-offsetof",
@ -138,11 +170,32 @@ config("ark_config") {
]
}
if (enable_relayout_profile) {
defines += [ "PANDA_ENABLE_RELAYOUT_PROFILE" ]
}
configs = []
foreach(plugin, enabled_plugins) {
configs += [ "$ark_root/plugins/$plugin:ark_config" ]
}
if (current_cpu == "arm") {
defines += [
"PANDA_TARGET_ARM32_ABI_SOFT=1",
"PANDA_TARGET_ARM32",
cflags_cc += [
"-march=armv7-a",
"-mfloat-abi=${arm_float_abi}",
"-marm",
"-mfpu=vfp",
]
if (arm_float_abi == "soft") {
defines += [ "PANDA_TARGET_ARM32_ABI_SOFT=1" ]
} else if (arm_float_abi == "softfp") {
defines += [ "PANDA_TARGET_ARM32_ABI_SOFTFP=1" ]
} else if (arm_float_abi == "hard") {
defines += [ "PANDA_TARGET_ARM32_ABI_HARD=1" ]
}
defines += [ "PANDA_TARGET_ARM32" ]
} else if (current_cpu == "arm64") {
defines += [
"PANDA_TARGET_ARM64",
@ -161,3 +214,68 @@ config("ark_config") {
]
}
}
plugins_yamls = []
runtime_options_yamls = []
foreach(plugin, enabled_plugins) {
plugin_dir = "$ark_root/plugins/$plugin"
source_files = read_file("$plugin_dir/subproject_sources.gn", "scope")
if (defined(source_files.option_yaml_path)) {
plugins_yamls += [ "$plugin_dir/${source_files.option_yaml_path}" ]
}
if (defined(source_files.runtime_option_yaml_path)) {
runtime_options_yamls +=
[ "$plugin_dir/${source_files.runtime_option_yaml_path}" ]
}
source_files = {
}
}
entrypoints_yamls = []
foreach(plugin, enabled_plugins) {
plugin_dir = "$ark_root/plugins/$plugin"
source_files = read_file("$plugin_dir/subproject_sources.gn", "scope")
if (defined(source_files.entrypoints_yaml_path)) {
entrypoints_yamls += [ "$plugin_dir/${source_files.entrypoints_yaml_path}" ]
}
source_files = {
}
}
inst_templates_yamls = []
foreach(plugin, enabled_plugins) {
plugin_dir = "$ark_root/plugins/$plugin"
source_files = read_file("$plugin_dir/subproject_sources.gn", "scope")
if (defined(source_files.inst_templates_yaml_path)) {
inst_templates_yamls +=
[ "$plugin_dir/${source_files.inst_templates_yaml_path}" ]
}
source_files = {
}
}
concat_yamls("concat_plugins_yamls") {
output_file = "$target_gen_dir/plugin_options.yaml"
default_file = "$ark_root/templates/plugin_options.yaml"
add_yamls = plugins_yamls
}
concat_yamls("concat_entrypoints_yamls") {
output_file = "$target_gen_dir/runtime/entrypoints.yaml"
default_file = "$ark_root/runtime/entrypoints/entrypoints.yaml"
add_yamls = entrypoints_yamls
}
concat_yamls("concat_inst_templates_yamls") {
output_file = "$target_gen_dir/compiler/generated/inst_templates.yaml"
default_file = "$ark_root/compiler/optimizer/ir_builder/inst_templates.yaml"
add_yamls = inst_templates_yamls
}
merge_yamls("merge_runtime_options_yamls") {
output_file = "$target_gen_dir/runtime_options.yaml"
add_yamls = [ "$ark_root/runtime/options.yaml" ] + runtime_options_yamls
}

View File

@ -11,7 +11,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
cmake_minimum_required(VERSION 3.14.1 FATAL_ERROR)
project(PANDA NONE)
# Add our custom configuration types to
@ -23,7 +23,7 @@ if(CMAKE_CONFIGURATION_TYPES)
CACHE STRING "CMake configuration types" FORCE)
endif()
enable_language(CXX ASM)
enable_language(CXX)
# NB! For God's sake do not touch the command below.
# See https://gitlab.kitware.com/cmake/cmake/issues/16588.
@ -56,6 +56,48 @@ endif()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic -Wall -Wextra -Werror -Wshadow")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti -fno-exceptions")
find_program(
LCOV
NAMES "lcov"
DOC "Path to lcov executable")
if(NOT LCOV)
set(PANDA_ENABLE_COVERAGE false)
endif()
find_program(
GENHTML
NAMES "genhtml"
DOC "Path to genhtml executable")
if(NOT GENHTML)
set(PANDA_ENABLE_COVERAGE false)
endif()
if(PANDA_ENABLE_COVERAGE)
# Set coverage options
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage")
set(ENABLE_BYTECODE_OPTIMIZER_COVERAGE true)
set(ENABLE_COMPILER_COVERAGE true)
add_custom_target(coverage_full DEPENDS cts-assembly tests benchmarks) # Execute tests
set(ADD_COV_FLAGS --quiet --rc lcov_branch_coverage=1)
set(COVERAGE_DIRECTORY coverage_report)
set(COVERAGE_INTERMEDIATE_REPORT coverage_full.info)
set(COVERAGE_FULL_REPORT cov.zip)
add_custom_command(TARGET coverage_full POST_BUILD
WORKING_DIRECTORY ${PANDA_BINARY_ROOT}
# Update current coverage info
COMMAND lcov --no-external -b ${PANDA_ROOT} -d ${PANDA_BINARY_ROOT} -c -o ${COVERAGE_INTERMEDIATE_REPORT} ${ADD_COV_FLAGS}
# Generate html report
COMMAND genhtml -o ${COVERAGE_DIRECTORY} ${COVERAGE_INTERMEDIATE_REPORT} --ignore-errors source ${ADD_COV_FLAGS}
COMMAND echo "Coverage report: ${PANDA_BINARY_ROOT}/${COVERAGE_DIRECTORY}/index.html"
# Delete temporary files with collected statistics
COMMAND rm ${COVERAGE_INTERMEDIATE_REPORT}
COMMAND find ${PANDA_BINARY_ROOT}/* -iname "*.gcda" -delete
)
else()
message(STATUS "Full coverage will not be calculated (may be enabled by -DPANDA_ENABLE_COVERAGE=true ).")
endif(PANDA_ENABLE_COVERAGE)
if(PANDA_TARGET_MACOS)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmacosx-version-min=10.13")
endif()
@ -96,27 +138,55 @@ endif()
if ("${CMAKE_BUILD_TYPE}" STREQUAL "RelWithDebInfo")
add_compile_options(-O2 -ggdb3 -fno-omit-frame-pointer)
elseif ("${CMAKE_BUILD_TYPE}" STREQUAL "FastVerify")
add_compile_options(-O2 -ggdb3)
add_compile_options(-O2 -ggdb3 -fno-omit-frame-pointer)
elseif ("${CMAKE_BUILD_TYPE}" STREQUAL "DebugDetailed")
add_compile_options(-Og -ggdb3)
add_compile_options(-Og -ggdb3 -fno-omit-frame-pointer)
endif()
if (PANDA_THREAD_SAFETY)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wthread-safety")
endif()
if (PANDA_ARK_JS_VM)
add_definitions(-DPANDA_ARK_JS_VM)
if (PANDA_WITH_JAVA)
set(PANDA_JAVA_PLUGIN_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/plugins/java")
set(PANDA_JAVA_PLUGIN_BINARY "${CMAKE_CURRENT_BINARY_DIR}/plugins/java")
endif()
if (PANDA_WITH_ECMASCRIPT)
set(PANDA_ECMASCRIPT_PLUGIN_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/plugins/ecmascript")
set(PANDA_ECMASCRIPT_PLUGIN_BINARY "${CMAKE_CURRENT_BINARY_DIR}/plugins/ecmascript")
add_definitions(-DENABLE_BYTECODE_OPT)
add_definitions(-DARK_INTRINSIC_SET)
endif()
if (PANDA_WITH_ACCORD)
set(PANDA_ACCORD_PLUGIN_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/plugins/accord")
set(PANDA_ACCORD_PLUGIN_BINARY "${CMAKE_CURRENT_BINARY_DIR}/plugins/accord")
endif()
if (PANDA_WITH_CANGJIE)
set(PANDA_CANGJIE_PLUGIN_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/plugins/cangjie")
set(PANDA_CANGJIE_PLUGIN_BINARY "${CMAKE_CURRENT_BINARY_DIR}/plugins/cangjie")
endif()
panda_promote_to_definitions(
PANDA_WITH_JAVA
PANDA_WITH_ECMASCRIPT
PANDA_WITH_ACCORD
PANDA_WITH_CANGJIE
PANDA_ENABLE_RELAYOUT_PROFILE
)
# ----- Deliverable executables and libraries ----------------------------------
# Please override with per-target properties if your artifact should reside
# elsewhere, like this:
# set_target_properties(... PROPERTIES RUNTIME_OUTPUT_DIRECTORY ...)
# Applicable for tests and all "internal" artifacts.
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PANDA_BINARY_ROOT}/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PANDA_BINARY_ROOT}/lib)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PANDA_BINARY_ROOT}/bin)
if(NOT HOST_TOOLS)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PANDA_BINARY_ROOT}/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PANDA_BINARY_ROOT}/lib)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PANDA_BINARY_ROOT}/bin)
endif()
# ----- Panda CMake functions --------------------------------------------------
include(cmake/PandaCmakeFunctions.cmake)
@ -127,6 +197,9 @@ include(cmake/HostTools.cmake)
# ----- Enable CCache ----------------------------------------------------------
include(cmake/PandaCCache.cmake)
# ----- Documentation generation -----------------------------------------------
include(cmake/Doxygen.cmake)
# ----- Code analysis and style ------------------------------------------------
include(cmake/ClangTidy.cmake)
include(cmake/CodeStyle.cmake)
@ -138,7 +211,6 @@ include(cmake/Sanitizers.cmake)
# Umbrella target for testing:
add_custom_target(tests COMMENT "Running all test suites")
include(cmake/Testing.cmake)
# ----- Template Based Generator -----------------------------------------------
@ -165,7 +237,7 @@ endif()
if(PANDA_WITH_TOOLCHAIN)
add_subdirectory(isa)
set(SECUREC_ROOT ${PANDA_THIRD_PARTY_SOURCES_DIR}/securec)
set(SECUREC_ROOT ${PANDA_THIRD_PARTY_SOURCES_DIR}/utils_native/base)
add_subdirectory(${PANDA_THIRD_PARTY_CONFIG_DIR}/securec)
add_subdirectory(libpandabase)
@ -182,12 +254,36 @@ if(PANDA_WITH_TOOLCHAIN)
add_subdirectory(disassembler)
if(PANDA_WITH_RUNTIME)
if (PANDA_TARGET_X86 OR PANDA_TARGET_AMD64)
add_subdirectory(verification/tests)
endif()
add_subdirectory(verification/gen)
add_subdirectory(verification)
endif()
if(PANDA_WITH_COMPILER)
add_subdirectory(bytecode_optimizer)
endif()
endif()
if(PANDA_WITH_COMPILER)
add_subdirectory(cross_values)
if (PANDA_TARGET_X86 OR PANDA_TARGET_AMD64)
set(ASMJIT_STATIC TRUE)
add_subdirectory(${PANDA_THIRD_PARTY_SOURCES_DIR}/asmjit)
add_subdirectory(${PANDA_THIRD_PARTY_SOURCES_DIR}/zydis)
endif()
add_subdirectory(${PANDA_THIRD_PARTY_SOURCES_DIR}/vixl)
add_subdirectory(irtoc)
add_subdirectory(compiler/optimizer/code_generator)
add_subdirectory(compiler)
set(IRTOC_INTRINSICS_YAML ${PANDA_ROOT}/irtoc/intrinsics.yaml)
# Irtoc is built within the host tools in cross-compiling mode.
if(NOT (CMAKE_CROSSCOMPILING OR PANDA_TARGET_OHOS))
add_subdirectory(irtoc/backend)
add_subdirectory(${PANDA_THIRD_PARTY_SOURCES_DIR}/elfio)
endif()
else()
add_library(arkcompiler ${PANDA_DEFAULT_LIB_TYPE})
add_library(arkbytecodeopt ${PANDA_DEFAULT_LIB_TYPE})
add_library(arkaotmanager ${PANDA_DEFAULT_LIB_TYPE})
endif()
if(PANDA_WITH_RUNTIME)
@ -202,27 +298,44 @@ if(PANDA_WITH_RUNTIME)
add_subdirectory(panda)
add_subdirectory(verification/verifier)
endif()
# ----- Testing ----------------------------------------------------------------
if(PANDA_WITH_TESTS)
add_subdirectory(${PANDA_THIRD_PARTY_SOURCES_DIR}/googletest)
target_compile_options(gtest PUBLIC "-Wno-shadow" "-Wno-pedantic")
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND PANDA_USE_32_BIT_POINTER AND PANDA_TARGET_AMD64)
set_target_properties(gtest PROPERTIES POSITION_INDEPENDENT_CODE ON)
set_target_properties(gtest_main PROPERTIES POSITION_INDEPENDENT_CODE ON)
set_target_properties(gmock PROPERTIES POSITION_INDEPENDENT_CODE ON)
endif()
add_subdirectory(${PANDA_THIRD_PARTY_SOURCES_DIR}/rapidcheck)
add_subdirectory(${PANDA_THIRD_PARTY_SOURCES_DIR}/rapidcheck/extras/catch)
target_compile_options(rapidcheck PUBLIC "-fexceptions" "-frtti" "-fPIC")
add_subdirectory(tests)
add_subdirectory(tools)
add_custom_target(tests_full COMMENT "Running all test suites and code checks")
add_dependencies(tests_full
check_concurrency_format
check_atomic_format
tests
tools
)
if(NOT PANDA_TARGET_MACOS)
# add_dependencies(tests_full clang_format)
add_dependencies(tests_full clang_format)
endif()
endif()
# ----- Aliases ----------------------------------------------------------------
add_custom_target(panda_bins COMMENT "Build all common Panda binaries")
add_dependencies(panda_bins panda pandasm ark_disasm paoc verifier)
# ----- Benchmarking -----------------------------------------------------------
if(PANDA_WITH_BENCHMARKS)
@ -232,9 +345,22 @@ if(PANDA_WITH_BENCHMARKS)
add_custom_target(benchmarks COMMENT "Running all benchmark suites")
add_subdirectory(tests/benchmarks)
add_subdirectory(experiments/benchmarks)
endif()
# ----- Aliases ----------------------------------------------------------------
# ----- Plugins ----------------------------------------------------------------
add_custom_target(panda_bins COMMENT "Build all common Panda binaries")
add_dependencies(panda_bins panda pandasm)
add_subdirectory(plugins)
include(cmake/PostPlugins.cmake)
# ----- Platforms --------------------------------------------------------------
add_subdirectory(platforms)
# ----- Fuzzing ----------------------------------------------------------------
add_subdirectory(fuzzing)
# ----- Quickening tool --------------------------------------------------------
add_subdirectory(quickener)

139
OAT.xml
View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Copyright (c) 2021-2022 Huawei Device Co., Ltd.
<!-- Copyright (c) 2022 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.
@ -12,104 +12,57 @@
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.
This is the configuration file template for OpenHarmony OSS Audit Tool, please copy it to your project root dir and modify it refer to OpenHarmony/tools_oat/README.
All configurations in this file will be merged to OAT-Default.xml, if you have any questions or concerns, please create issue in OpenHarmony/tools_oat and @jalenchen or chenyaxun.
licensefile:
1.If the project don't have "LICENSE" in root dir, please define all the license files in this project in , OAT will check license files according to this rule.
policylist:
1. policy: If the OAT-Default.xml policies do not meet your requirements, please add policies here.
2. policyitem: The fields type, name, path, desc is required, and the fields rule, group, filefilter is optional,the default value is:
<policyitem type="" name="" path="" desc="" rule="may" group="defaultGroup" filefilter="defaultPolicyFilter"/>
3. policyitem type:
"compatibility" is used to check license compatibility in the specified path;
"license" is used to check source license header in the specified path;
"copyright" is used to check source copyright header in the specified path;
"import" is used to check source dependency in the specified path, such as import ... ,include ...
"filetype" is used to check file type in the specified path, supported file types: archive, binary
"filename" is used to check whether the specified file exists in the specified path(projectroot means the root dir of the project), supported file names: LICENSE, README, README.OpenSource
4. policyitem name: This field is used for define the license, copyright, "*" means match all, the "!" prefix means could not match this value. For example, "!GPL" means can not use GPL license.
5. policyitem path: This field is used for define the source file scope to apply this policyitem, the "!" prefix means exclude the files. For example, "!.*/lib/.*" means files in lib dir will be exclude while process this policyitem.
6. policyitem rule and group: These two fields are used together to merge policy results. "may" policyitems in the same group means any one in this group passed, the result will be passed.
7. policyitem filefilter: Used to bind filefilter which define filter rules.
7. policyitem desc: Used to describe the reason of this policy item, committers will check this while merging the code.
8. filefilter: Filter rules, the type filename is used to filter file name, the type filepath is used to filter file path.
Note:If the text contains special characters, please escape them according to the following rules:
" == &quot;
& == &amp;
' == &apos;
< == &lt;
> == &gt;
-->
<!-- OSS Audit Tool (OAT) configuration guide:
basedir: Root dirictory. The basedir + project path is the real source file location.
licensefile:
1.If the project does not have "LICENSE" in root dir, please define all the license files in this project. OAT will check license files according to this rule.
tasklist(for batch mode only):
1. task: OAT check task. Each task will start a new thread.
2. task name: Name of the OAT check task.
3. task policy: Default policy for projects under this task. This field is mandatory and the specified policy must be defined in policylist.
4. task filter: Default filefilter for projects under this task. This field is mandatory and the specified filefilter must be defined in filefilterlist.
5. task project: Project to be checked. The source root dir of the project is defined by the path field.
policyList:
1. policy: All policyitems will be merged to default rules in OAT.xml. The policy name doesn't affect the OAT check process.
2. policyitem: The fields type, name, path and desc are mandatory, and the fields rule, group and filefilter are optional. The default value is:
<policyitem type="" name="" path="" desc="" rule="may" group="defaultGroup" filefilter="defaultPolicyFilter"/>
3. policyitem type:
"compatibility" is used to check license compatibility in the specified path;
"license" is used to check source license header in the specified path;
"copyright" is used to check source copyright header in the specified path;
"import" is used to check source dependency in the specified path, such as import ... ,include ...
"filetype" is used to check file type in the specified path, supported file types: archive, binary
"filename" is used to check whether the specified file exists in the specified path(support projectroot in default OAT.xml), supported file names: LICENSE, README, README.OpenSource
4. policyitem name: This field is used to define the license and copyright. Wherein, "*" means to match all, the "!" prefix means a failure to match this value. For example, "!GPL" means a failure to use GPL license.
5. policyitem path: This field is used to define the source file scope to apply this policyitem. The "!" prefix means to exclude the files. For example, "!.*/lib/.*" means to exclude the files in lib while processing this policyitem.
6. policyitem rule and group: These two fields are used together to merge policy results. "may" policyitems in the same group means that the result will be passed if any policyitem in this group is passed,
7. policyitem filefilter: This field is used to bind file filters, which define filter rules.
8. filefilter: This field is used to define filter rules. Wherein, filename is used to filter the file names; filepath is used to filter file paths.
Note: If the text contains special characters, please escape them according to the following rules:
" == &gt;
& == &gt;
' == &gt;
< == &gt;
> == &gt;
-->
<configuration>
<oatconfig>
<policy name="defaultPolicy" desc="" >
<policyitem type="compatibility" name="Apache" path="arkcompiler/runtime_core" rule="may" group="defaultGroup" filefilter="defaultPolicyFilter" desc=""/>
</policy>
<licensefile></licensefile>
<policylist>
<policy name="projectPolicy" desc="">
<policyitem type="compatibility" name="Apache" path=".*" desc="">
</policy>
</policylist>
<filefilterlist>
<filefilter name="binaryFileTypePolicyFilter" desc="Filer rules of the binary file verification policy" >
<filteritem type="filepath" name="docs/images/cfi_hack_bridges.png" desc="Example binary images"/>
<filteritem type="filepath" name="docs/images/panda-mm-overview.png" desc="Example binary images"/>
</filefilter>
<filefilter name="copyrightPolicyFilter" desc="Filter rules of the copyright file header verification policy" >
<filteritem type="filepath" name="AUTHORS" desc="Authors list"/>
<filteritem type="filepath" name="scripts/dep-lists/ubuntu-18-04-arm-dev" desc="Ubuntu build dependencies"/>
<filteritem type="filepath" name="scripts/dep-lists/ubuntu-18-04-build" desc="Ubuntu build dependencies"/>
<filteritem type="filepath" name="scripts/dep-lists/ubuntu-18-04-ci" desc="Ubuntu build dependencies"/>
<filteritem type="filepath" name="scripts/dep-lists/ubuntu-18-04-cross-arm-all" desc="Ubuntu build dependencies"/>
<filteritem type="filepath" name="scripts/dep-lists/ubuntu-18-04-cross-windows" desc="Ubuntu build dependencies"/>
<filteritem type="filepath" name="scripts/dep-lists/ubuntu-18-04-cross-x86" desc="Ubuntu build dependencies"/>
<filteritem type="filepath" name="scripts/dep-lists/ubuntu-18-04-dev" desc="Ubuntu build dependencies"/>
<filteritem type="filepath" name="scripts/dep-lists/ubuntu-18-04-fuzzing" desc="Ubuntu build dependencies"/>
<filteritem type="filepath" name="scripts/dep-lists/ubuntu-20-04-build" desc="Ubuntu build dependencies"/>
<filteritem type="filepath" name="scripts/dep-lists/ubuntu-20-04-ci" desc="Ubuntu build dependencies"/>
<filteritem type="filepath" name="scripts/dep-lists/ubuntu-20-04-cross-arm-all" desc="Ubuntu build dependencies"/>
<filteritem type="filepath" name="scripts/dep-lists/ubuntu-20-04-cross-windows" desc="Ubuntu build dependencies"/>
<filteritem type="filepath" name="scripts/dep-lists/ubuntu-20-04-cross-x86" desc="Ubuntu build dependencies"/>
<filteritem type="filepath" name="scripts/dep-lists/ubuntu-20-04-dev" desc="Ubuntu build dependencies"/>
<filteritem type="filepath" name="scripts/dep-lists/ubuntu-20-04-fuzzing" desc="Ubuntu build dependencies"/>
<filteritem type="filepath" name="scripts/dep-lists/ubuntu-build" desc="Ubuntu build dependencies"/>
<filteritem type="filepath" name="scripts/dep-lists/ubuntu-cross-arm-all" desc="Ubuntu build dependencies"/>
<filteritem type="filepath" name="scripts/dep-lists/ubuntu-cross-windows" desc="Ubuntu build dependencies"/>
<filteritem type="filepath" name="scripts/dep-lists/ubuntu-cross-x86" desc="Ubuntu build dependencies"/>
<filteritem type="filepath" name="scripts/dep-lists/ubuntu-dev" desc="Ubuntu build dependencies"/>
<filteritem type="filepath" name="scripts/dep-lists/ubuntu-fuzzing" desc="Ubuntu build dependencies"/>
</filefilter>
<filefilter name="defaultPolicyFilter" desc="Filter rules of the verification policy for the license file in the root directory" >
<filteritem type="filepath" name="AUTHORS" desc="开发者名单"/>
<filteritem type="filepath" name="scripts/dep-lists/ubuntu-18-04-arm-dev" desc="Ubuntu build dependencies"/>
<filteritem type="filepath" name="scripts/dep-lists/ubuntu-18-04-build" desc="Ubuntu build dependencies"/>
<filteritem type="filepath" name="scripts/dep-lists/ubuntu-18-04-ci" desc="Ubuntu build dependencies"/>
<filteritem type="filepath" name="scripts/dep-lists/ubuntu-18-04-cross-arm-all" desc="Ubuntu build dependencies"/>
<filteritem type="filepath" name="scripts/dep-lists/ubuntu-18-04-cross-windows" desc="Ubuntu build dependencies"/>
<filteritem type="filepath" name="scripts/dep-lists/ubuntu-18-04-cross-x86" desc="Ubuntu build dependencies"/>
<filteritem type="filepath" name="scripts/dep-lists/ubuntu-18-04-dev" desc="Ubuntu build dependencies"/>
<filteritem type="filepath" name="scripts/dep-lists/ubuntu-18-04-fuzzing" desc="Ubuntu build dependencies"/>
<filteritem type="filepath" name="scripts/dep-lists/ubuntu-20-04-build" desc="Ubuntu build dependencies"/>
<filteritem type="filepath" name="scripts/dep-lists/ubuntu-20-04-ci" desc="Ubuntu build dependencies"/>
<filteritem type="filepath" name="scripts/dep-lists/ubuntu-20-04-cross-arm-all" desc="Ubuntu build dependencies"/>
<filteritem type="filepath" name="scripts/dep-lists/ubuntu-20-04-cross-windows" desc="Ubuntu build dependencies"/>
<filteritem type="filepath" name="scripts/dep-lists/ubuntu-20-04-cross-x86" desc="Ubuntu build dependencies"/>
<filteritem type="filepath" name="scripts/dep-lists/ubuntu-20-04-dev" desc="Ubuntu build dependencies"/>
<filteritem type="filepath" name="scripts/dep-lists/ubuntu-20-04-fuzzing" desc="Ubuntu build dependencies"/>
<filteritem type="filepath" name="scripts/dep-lists/ubuntu-build" desc="Ubuntu build dependencies"/>
<filteritem type="filepath" name="scripts/dep-lists/ubuntu-cross-arm-all" desc="Ubuntu build dependencies"/>
<filteritem type="filepath" name="scripts/dep-lists/ubuntu-cross-windows" desc="Ubuntu build dependencies"/>
<filteritem type="filepath" name="scripts/dep-lists/ubuntu-cross-x86" desc="Ubuntu build dependencies"/>
<filteritem type="filepath" name="scripts/dep-lists/ubuntu-dev" desc="Ubuntu build dependencies"/>
<filteritem type="filepath" name="scripts/dep-lists/ubuntu-fuzzing" desc="Ubuntu build dependencies"/>
<filefilter name="defaultFilter" desc="Files not to check">
<filteritem type="filename" name="*.iml|*.json|*.txt|*.png" desc="desc files"/>
<filteritem type="filepath" name=".*/docs/.*" desc="Docs files"/>
<filteritem type="filepath" name=".*/docs/diagrams/.*" desc="Docs files"/>
<filteritem type="filepath" name="scripts/dep-lists/.*" desc="Dependency lists"/>
<filteritem type="filepath" name="third_party/.*" desc="Third party"/>
<filteritem type="filepath" name="plugins/java/.*" desc="Plugin for java"/>
</filefilter>
</filefilterlist>
</oatconfig>
</configuration>

View File

@ -145,7 +145,7 @@ For more information, please see: [ARK Runtime Usage Guide](https://gitee.com/op
## Repositories Involved<a name="section1371113476307"></a>
**[arkcompiler\_runtime\_core](https://gitee.com/openharmony/arkcompiler_runtime_core)**
**[arkcompiler\_runtime\_core](https://gitee.com/openharmony/ark_runtime_core)**
[arkcompiler\_ets\_runtime](https://gitee.com/openharmony/arkcompiler_ets_runtime)

View File

@ -11,24 +11,36 @@
# See the License for the specific language governing permissions and
# limitations under the License.
if (!defined(ark_standalone_build)) {
ark_standalone_build = false
import("//arkcompiler/runtime_core/ark_root.gni")
import("//build/ohos.gni")
if (is_standard_system) {
import("$ark_root/platforms/ohos/ark_config.gni")
}
if (ark_standalone_build) {
ark_root = "//"
ark_third_party_root = "//ark-third-party"
with_ecmascript = true
use_pbqp = true
} else {
ark_root = "//arkcompiler/runtime_core"
ark_third_party_root = "//third_party"
with_ecmascript = false
use_pbqp = false
declare_args() {
enabled_plugins = default_enabled_plugins
}
if (current_cpu == "arm") {
if (!defined(arm_float_abi) || arm_float_abi == "") {
arm_float_abi = "softfp"
}
assert(arm_float_abi == "soft" || arm_float_abi == "softfp" ||
arm_float_abi == "hard",
"arm_float_abi should be soft, softfp or hard")
}
# We don't support x64 compiler in GN build, since asmjit is not available here
ark_enable_compiler_x64 = false
ark_enable_global_register_variables = true
enable_bytecode_optimizer = false
enable_bytecode_optimizer = true
enable_relayout_profile = false
sdk_libc_secshared_dep = ""
sdk_libc_secshared_config = ""
if (ark_standalone_build) {
sdk_libc_secshared_dep = "$ark_third_party_root/securec:libc_secshared"
@ -50,6 +62,10 @@ if (ark_standalone_build) {
}
}
is_mob = !is_standard_system && (current_cpu != "arm" || is_wearable_product)
## TODO add other arch
# Generate file for a template and YAML data provided.
#
# Mandatory arguments:
@ -73,6 +89,21 @@ template("ark_gen_file") {
extra_dependencies += invoker.extra_dependencies
}
positional_argv = []
if (defined(invoker.extra_argv)) {
positional_argv += invoker.extra_argv
}
keyword_argv = [
"--template",
rebase_path(invoker.template_file, root_build_dir),
"--data",
rebase_path(invoker.data_file, root_build_dir),
"--require",
requires,
"--output",
rebase_path(invoker.output_file),
]
action("$target_name") {
script = "$ark_root/isa/gen.rb"
@ -82,15 +113,64 @@ template("ark_gen_file") {
invoker.data_file,
]
outputs = [ invoker.output_file ]
args = positional_argv + keyword_argv
deps = extra_dependencies
}
}
template("concat_yamls") {
assert(defined(invoker.output_file), "output_file is required!")
assert(defined(invoker.default_file), "default_file is required!")
extra_dependencies = []
if (defined(invoker.extra_dependencies)) {
extra_dependencies += invoker.extra_dependencies
}
outputs = [ invoker.output_file ]
action("$target_name") {
script = "$ark_root/templates/concat_yamls.sh"
# rerun action when data file or template file update
inputs = [ invoker.default_file ]
args = [
"--template",
rebase_path(invoker.template_file, root_build_dir),
"--data",
rebase_path(invoker.data_file, root_build_dir),
"--require",
requires,
"--output",
rebase_path(outputs[0]),
rebase_path(invoker.output_file, root_build_dir),
rebase_path(invoker.default_file, root_build_dir),
]
foreach(yaml, invoker.add_yamls) {
args += [ rebase_path(yaml, root_build_dir) ]
}
deps = extra_dependencies
}
}
template("merge_yamls") {
assert(defined(invoker.output_file), "output_file is required!")
assert(defined(invoker.add_yamls), "add_yamls is required!")
extra_dependencies = []
if (defined(invoker.extra_dependencies)) {
extra_dependencies += invoker.extra_dependencies
}
outputs = [ invoker.output_file ]
action("$target_name") {
script = "$ark_root/templates/merge.rb"
data = []
foreach(yaml, invoker.add_yamls) {
data += [ rebase_path(yaml, root_build_dir) ]
}
args = [
"-d",
string_join(",", data),
"-o",
rebase_path(invoker.output_file, root_build_dir),
]
deps = extra_dependencies
@ -110,6 +190,7 @@ template("ark_gen_file") {
# * sources -- a directory with templates, default is ${PROJECT_SOURCE_DIR}/templates
# * destination -- a directory for output files, default is ${PANDA_BINARY_ROOT}
# * extra_dependencies -- a list of files that should be considered as dependencies
# * extra_argv -- a list of positional arguments that could be accessed in '.erb' files via ARGV[]
template("ark_gen") {
assert(defined(invoker.data), "data were not passed to ark_gen")
assert(defined(invoker.template_files),
@ -150,6 +231,10 @@ template("ark_gen") {
if (defined(invoker.extra_dependencies)) {
extra_dependencies += invoker.extra_dependencies
}
extra_argv = []
if (defined(invoker.extra_argv)) {
extra_argv += invoker.extra_argv
}
}
}
}

24
ark_root.gni Normal file
View File

@ -0,0 +1,24 @@
# Copyright (c) 2021-2022 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 (!defined(ark_standalone_build)) {
ark_standalone_build = false
}
if (ark_standalone_build) {
ark_root = "//"
ark_third_party_root = "//ark-third-party"
} else {
ark_root = "//arkcompiler/runtime_core"
ark_third_party_root = "//third_party"
}

View File

@ -2,9 +2,7 @@
# 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.
@ -19,12 +17,16 @@ config("arkassembler_public_config") {
"$ark_root/assembler",
"$target_gen_dir",
"$target_gen_dir/include",
"$root_gen_dir/libpandabase",
"$ark_root",
"$ark_root/plugins/ecmascript/assembler",
]
defines = [ "PANDA_WITH_ECMASCRIPT" ]
}
libarkassembler_sources = [
"$ark_root/plugins/ecmascript/assembler/extension/ecmascript_meta.cpp",
"$target_gen_dir/ins_to_string.cpp",
"annotation.cpp",
"assembly-emitter.cpp",
@ -33,7 +35,6 @@ libarkassembler_sources = [
"assembly-program.cpp",
"assembly-type.cpp",
"context.cpp",
"extensions/ecmascript/ecmascript_meta.cpp",
"extensions/extensions.cpp",
"lexer.cpp",
"meta.cpp",
@ -47,14 +48,34 @@ libarkassembler_configs = [
"$ark_root/libpandafile:arkfile_public_config",
]
plugin_deps = []
foreach(plugin, enabled_plugins) {
print("add assembler plugin: $plugin")
plugin_dir = "$ark_root/plugins/$plugin"
libarkassembler_configs += [ "$plugin_dir:assembler" ]
plugin_deps += [ "$plugin_dir:assembler_deps" ]
source_files = read_file("$plugin_dir/subproject_sources.gn", "scope")
if (defined(source_files.srcs_assembler_path)) {
source_file = "$plugin_dir/${source_files.srcs_assembler_path}"
src_scope = read_file(source_file, "scope")
foreach(src, src_scope.srcs) {
libarkassembler_sources += [ "$plugin_dir/assembler/$src" ]
}
src_scope = {
}
}
source_files = []
}
source_set("libarkassembler_static") {
sources = libarkassembler_sources
public_configs = libarkassembler_configs
deps = [
":ark_asm_ecmascript_meta_gen_h",
":ark_asm_meta_gen_h",
":ark_asm_register_extensions_h",
":isa_gen_libarkassembler_ins_create_api_h",
":isa_gen_libarkassembler_ins_emit_h",
":isa_gen_libarkassembler_ins_to_string_cpp",
@ -65,10 +86,13 @@ source_set("libarkassembler_static") {
"$ark_root/libpandafile:libarkfile",
sdk_libc_secshared_dep,
]
deps += plugin_deps
}
ohos_shared_library("libarkassembler") {
deps = [ ":libarkassembler_static" ]
if (!is_standard_system) {
relative_install_dir = "ark"
}
@ -76,6 +100,7 @@ ohos_shared_library("libarkassembler") {
if (is_mingw) {
output_extension = "dll"
}
subsystem_name = "ark"
part_name = "ark"
}
@ -86,8 +111,8 @@ source_set("libarkassembler_frontend_set_static") {
public_configs = libarkassembler_configs
deps = [
":ark_asm_ecmascript_meta_gen_h",
":ark_asm_meta_gen_h",
":ark_asm_register_extensions_h",
":isa_gen_libarkassembler_ins_create_api_h",
":isa_gen_libarkassembler_ins_emit_h",
":isa_gen_libarkassembler_ins_to_string_cpp",
@ -98,6 +123,8 @@ source_set("libarkassembler_frontend_set_static") {
"$ark_root/libpandafile:libarkfile_frontend_static",
sdk_libc_secshared_dep,
]
deps += plugin_deps
}
ohos_static_library("libarkassembler_frontend_static") {
@ -110,7 +137,10 @@ ohos_static_library("libarkassembler_frontend_static") {
source_set("ark_asm_static") {
sources = [ "pandasm.cpp" ]
include_dirs = [ "$target_gen_dir" ]
include_dirs = [
"$target_gen_dir",
"$root_gen_dir/libpandabase",
]
public_configs = [
sdk_libc_secshared_config,
@ -123,12 +153,14 @@ source_set("ark_asm_static") {
deps = [
":libarkassembler_frontend_static",
"$ark_root/bytecode_optimizer:libarkbytecodeopt_frontend_static",
"$ark_root/libpandabase:libarkbase_frontend_static",
"$ark_root/libpandafile:libarkfile_frontend_static",
]
libs = platform_libs
if (!is_mac && !is_mingw && !ark_standalone_build) {
ldflags = [ "-static-libstdc++" ]
ldflags = platform_ldflags
}
}
@ -137,6 +169,7 @@ ohos_executable("ark_asm") {
install_enable = true
subsystem_name = "ark"
part_name = "ark"
}
ark_isa_gen("isa_gen_libarkassembler") {
@ -163,9 +196,10 @@ ark_gen_file("ark_asm_meta_gen_h") {
output_file = "$target_gen_dir/meta_gen.h"
}
ark_gen_file("ark_asm_ecmascript_meta_gen_h") {
template_file = "templates/meta_gen.cpp.erb"
data_file = "extensions/ecmascript/metadata.yaml"
requires = [ "asm_metadata.rb" ]
output_file = "$target_gen_dir/ecmascript_meta_gen.h"
ark_gen_file("ark_asm_register_extensions_h") {
extra_dependencies = [ "$ark_root:concat_plugins_yamls" ]
template_file = "extensions/register_extensions.h.erb"
data_file = "$target_gen_dir/../plugin_options.yaml"
requires = [ "$ark_root/templates/plugin_options.rb" ]
output_file = "$target_gen_dir/register_extensions.h"
}

View File

@ -32,7 +32,7 @@ panda_isa_gen(
"${PANDA_ROOT}/libpandafile/pandafile_isapi.rb"
)
add_library(arkassembler ${PANDA_DEFAULT_LIB_TYPE}
set(SOURCES
lexer.cpp
annotation.cpp
assembly-emitter.cpp
@ -44,11 +44,8 @@ add_library(arkassembler ${PANDA_DEFAULT_LIB_TYPE}
meta.cpp
ins_to_string.cpp
extensions/extensions.cpp
extensions/ecmascript/ecmascript_meta.cpp
)
add_dependencies(arkassembler isa_gen_assembler arkfile)
set(META_GEN_H ${CMAKE_CURRENT_BINARY_DIR}/meta_gen.h)
panda_gen_file(
DATAFILE ${CMAKE_CURRENT_SOURCE_DIR}/metadata.yaml
@ -56,24 +53,23 @@ panda_gen_file(
OUTPUTFILE ${META_GEN_H}
REQUIRES ${CMAKE_CURRENT_SOURCE_DIR}/asm_metadata.rb
)
add_custom_target(meta_gen_h DEPENDS ${META_GEN_H})
set(ECMASCRIPT_META_GEN_H ${CMAKE_CURRENT_BINARY_DIR}/ecmascript_meta_gen.h)
panda_gen_file(
DATAFILE ${CMAKE_CURRENT_SOURCE_DIR}/extensions/ecmascript/metadata.yaml
TEMPLATE ${CMAKE_CURRENT_SOURCE_DIR}/templates/meta_gen.cpp.erb
OUTPUTFILE ${ECMASCRIPT_META_GEN_H}
REQUIRES ${CMAKE_CURRENT_SOURCE_DIR}/asm_metadata.rb
add_library(arkassembler ${PANDA_DEFAULT_LIB_TYPE} ${SOURCES})
add_dependencies(arkassembler
isa_gen_assembler
arkfile
meta_gen_h
)
add_custom_target(meta_gen_assembler DEPENDS ${META_GEN_H} ${ECMASCRIPT_META_GEN_H})
add_dependencies(arkassembler meta_gen_assembler)
target_include_directories(arkassembler
PUBLIC ${CMAKE_CURRENT_BINARY_DIR}
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}
target_include_directories(arkassembler PUBLIC
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_BINARY_DIR}/libpandabase
${PANDA_ROOT}
)
panda_add_to_clang_tidy(arkassembler)
panda_add_to_clang_tidy(TARGET ark_asm CHECKS
"-cert-dcl21-cpp"
"-cppcoreguidelines-macro-usage"
@ -83,8 +79,12 @@ panda_add_to_clang_tidy(TARGET ark_asm CHECKS
target_link_libraries(arkassembler arkfile)
target_link_libraries(ark_asm arkassembler arkbase)
if(PANDA_WITH_BYTECODE_OPTIMIZER)
target_link_libraries(ark_asm arkbytecodeopt)
endif()
include_directories(${PANDA_ROOT}/libpandabase/)
include_directories(${CMAKE_BINARY_DIR}/libpandafile/include/)
panda_add_gtest(
NAME assembler_tests
@ -105,14 +105,24 @@ endif()
panda_add_sanitizers(TARGET arkassembler SANITIZERS ${PANDA_SANITIZERS_LIST})
panda_add_sanitizers(TARGET ark_asm SANITIZERS ${PANDA_SANITIZERS_LIST})
# TODO: remove after all components will use ark_asm instead of pandasm
add_custom_target(pandasm ALL
COMMAND cd $<TARGET_FILE_DIR:ark_asm> && ${CMAKE_COMMAND} -E create_symlink $<TARGET_FILE_NAME:ark_asm> pandasm)
add_dependencies(pandasm ark_asm)
add_check_style(".")
if (TARGET host_tools_depends)
add_dependencies(host_tools_depends arkassembler)
endif()
if (DEFINED PANDA_ROOT_BINARY_DIR)
# Special case for host tool build.
target_include_directories(arkassembler PUBLIC ${PANDA_ROOT_BINARY_DIR}/assembler)
endif()
if (PANDA_ENABLE_AFL)
include("${PANDA_ROOT}/fuzzing/Fuzzing.cmake")
panda_substitute_libs(TARGET arkassembler LIBS arkbase c_secshared arkfile)
endif()
add_check_style(".")

View File

@ -1,4 +1,4 @@
/*
/**
* Copyright (c) 2021-2022 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.
@ -17,7 +17,6 @@
namespace panda::pandasm {
// CODECHECK-NOLINTNEXTLINE(C_RULE_ID_FUNCTION_SIZE)
std::unique_ptr<ScalarValue> InitScalarValue(const ScalarValue &sc_val)
{
std::unique_ptr<ScalarValue> copy_val;

View File

@ -1,4 +1,4 @@
/*
/**
* Copyright (c) 2021-2022 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.
@ -25,6 +25,7 @@
#include <vector>
#include "assembly-type.h"
#include "macros.h"
namespace panda::pandasm {
@ -96,7 +97,6 @@ public:
UNKNOWN
};
// CODECHECK-NOLINTNEXTLINE(C_RULE_ID_FUNCTION_SIZE)
static constexpr char GetTypeAsChar(Type t)
{
char type = '0';
@ -164,12 +164,10 @@ public:
case Type::UNKNOWN:
default:
type = '0';
break;
}
return type;
}
// CODECHECK-NOLINTNEXTLINE(C_RULE_ID_FUNCTION_SIZE)
static constexpr char GetArrayTypeAsChar(Type t)
{
char type = '0';
@ -228,12 +226,10 @@ public:
case Type::UNKNOWN:
default:
type = '0';
break;
}
return type;
}
// CODECHECK-NOLINTNEXTLINE(C_RULE_ID_FUNCTION_SIZE)
static constexpr Type GetCharAsType(char c)
{
Type type = Type::UNKNOWN;
@ -301,12 +297,10 @@ public:
case '0':
default:
type = Type::UNKNOWN;
break;
}
return type;
}
// CODECHECK-NOLINTNEXTLINE(C_RULE_ID_FUNCTION_SIZE)
static constexpr Type GetCharAsArrayType(char c)
{
Type type = Type::UNKNOWN;
@ -365,7 +359,6 @@ public:
case '0':
default:
type = Type::UNKNOWN;
break;
}
return type;
}
@ -501,7 +494,7 @@ private:
ScalarValue(Type type, pandasm::Type value) : Value(type), value_(std::move(value)) {}
ScalarValue(Type type, const AnnotationData &value) : Value(type), value_(value) {}
ScalarValue(Type type, AnnotationData &value) : Value(type), value_(value) {}
std::variant<uint64_t, float, double, std::string, pandasm::Type, AnnotationData> value_;
};

19
assembler/asm_isapi.rb Executable file → Normal file
View File

@ -11,14 +11,14 @@
# See the License for the specific language governing permissions and
# limitations under the License.
# Assembler-specific extension of ISAPI
# Assembler specific extension of ISAPI
Instruction.class_eval do
def asm_token
mnemonic.tr('.', '_').upcase
end
def call?
properties.include?('call') || stripped_mnemonic == 'initobj'
properties.include?('call')
end
def range?
@ -26,7 +26,7 @@ Instruction.class_eval do
end
def simple_call?
call? && !range?
call? && !range? && !properties.include?('dynamic')
end
def return?
@ -76,8 +76,8 @@ module Panda
insns << IR.new('INITOBJX', ['InstFlags::PSEUDO', 'InstFlags::CALL', 'InstFlags::ACC_WRITE'], 'INVALID_REG_IDX', [])
insns << IR.new('CALLX', ['InstFlags::PSEUDO', 'InstFlags::CALL', 'InstFlags::ACC_WRITE'], 'INVALID_REG_IDX', [])
insns << IR.new('CALLX_VIRT', ['InstFlags::PSEUDO', 'InstFlags::CALL', 'InstFlags::ACC_WRITE'], 'INVALID_REG_IDX', [])
insns << IR.new('B_P_CALLIX', ['InstFlags::PSEUDO', 'InstFlags::CALL', 'InstFlags::ACC_READ'], 'INVALID_REG_IDX', [])
insns << IR.new('B_P_CALLIEX',['InstFlags::PSEUDO', 'InstFlags::CALL', 'InstFlags::ACC_READ'], 'INVALID_REG_IDX', [])
insns << IR.new('B_P_CALLIX', ['InstFlags::PSEUDO', 'InstFlags::CALL', 'InstFlags::ACC_WRITE'], 'INVALID_REG_IDX', [])
insns << IR.new('B_P_CALLIEX',['InstFlags::PSEUDO', 'InstFlags::CALL', 'InstFlags::ACC_WRITE'], 'INVALID_REG_IDX', [])
insns
end
end
@ -87,18 +87,17 @@ end
# type - type of variable in emitter code
def assembler_signature(group, is_jump)
insn = group.first
sig = format_ops(insn.format)
sig.each do |o|
format_ops(insn.format).each do |o|
if o.name.start_with?('imm')
if insn.asm_token.start_with?('F')
o.type, o.name = is_jump ? ['const std::string &', 'label'] : ["double", o.name]
o.type, o.name = is_jump ? ['const std::string &', 'label'] : ['double', o.name]
else
o.type, o.name = is_jump ? ['const std::string &', 'label'] : ["int64_t", o.name]
o.type, o.name = is_jump ? ['const std::string &', 'label'] : ['int64_t', o.name]
end
elsif o.name.start_with?('id')
o.type, o.name = ['const std::string &', 'id']
else
o.type = "uint16_t"
o.type = 'uint16_t'
end
end
end

3
assembler/asm_metadata.rb Executable file → Normal file
View File

@ -11,6 +11,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
require 'ostruct'
require 'delegate'
class Attritute < SimpleDelegator
@ -113,7 +114,7 @@ module MetadataGen
body << ""
end
body << "#{indent * 2}return Error(std::string(\"Attribute '#{attribute_name(a)}' has incorrect value '\").append(value) +"
body << "#{indent * 2}return Error(std::string(\"Attribute '#{attribute_name(a)}' have incorrect value '\").append(value) +"
body << "#{indent * 2} R\"('. Should be one of #{a.values.map(&:value)})\", Error::Type::INVALID_VALUE);"
elsif a.size?
body << "#{indent * 2}return ValidateSize(value);"

View File

@ -1,4 +1,4 @@
/*
/**
* Copyright (c) 2021-2022 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.
@ -13,8 +13,8 @@
* limitations under the License.
*/
#ifndef PANDA_ASSEMBLER_ASSEMBLY_CONTEXT_H_
#define PANDA_ASSEMBLER_ASSEMBLY_CONTEXT_H_
#ifndef _PANDA_ASSEMBLER_CONTEXT_HPP
#define _PANDA_ASSEMBLER_CONTEXT_HPP
#include <string>
#include <string_view>
@ -31,7 +31,7 @@ namespace panda::pandasm {
/*
* Used to move around tokens.
* *context :
* returns current value of a token
* Returns current value of a token
* ++context :
* sets the next token value
* returns current value of a token
@ -75,4 +75,4 @@ struct Context {
} // namespace panda::pandasm
#endif // PANDA_ASSEMBLER_ASSEMBLY_CONTEXT_H_
#endif // !_PANDA_ASSEMBLER_CONTEXT_HPP

View File

@ -1,4 +1,4 @@
/*
/**
* Copyright (c) 2021-2022 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.
@ -13,8 +13,8 @@
* limitations under the License.
*/
#ifndef PANDA_ASSEMBLER_ASSEMBLY_DEBUG_H_
#define PANDA_ASSEMBLER_ASSEMBLY_DEBUG_H_
#ifndef _PANDA_ASSEMBLY_DEBUG_H
#define _PANDA_ASSEMBLY_DEBUG_H
#include <string>
@ -22,8 +22,8 @@ namespace panda::pandasm::debuginfo {
struct Ins {
size_t line_number = 0;
size_t column_number = 0;
std::string whole_line = "";
uint32_t column_number = 0;
std::string whole_line = ""; // TODO(mbolshov): redundant given file and line_number
size_t bound_left = 0;
size_t bound_right = 0;
@ -55,4 +55,4 @@ struct LocalVariable {
} // namespace panda::pandasm::debuginfo
#endif // PANDA_ASSEMBLER_ASSEMBLY_DEBUG_H_
#endif // !_PANDA_ASSEMBLY_DEBUG_H

View File

@ -1,4 +1,4 @@
/*
/**
* Copyright (c) 2021-2022 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.
@ -62,19 +62,19 @@ using panda::panda_file::Writer;
std::unordered_map<Type::TypeId, PrimitiveTypeItem *> CreatePrimitiveTypes(ItemContainer *container)
{
auto res = std::unordered_map<Type::TypeId, PrimitiveTypeItem *> {};
res.insert({Type::TypeId::VOID, container->CreateItem<PrimitiveTypeItem>(Type::TypeId::VOID)});
res.insert({Type::TypeId::U1, container->CreateItem<PrimitiveTypeItem>(Type::TypeId::U1)});
res.insert({Type::TypeId::I8, container->CreateItem<PrimitiveTypeItem>(Type::TypeId::I8)});
res.insert({Type::TypeId::U8, container->CreateItem<PrimitiveTypeItem>(Type::TypeId::U8)});
res.insert({Type::TypeId::I16, container->CreateItem<PrimitiveTypeItem>(Type::TypeId::I16)});
res.insert({Type::TypeId::U16, container->CreateItem<PrimitiveTypeItem>(Type::TypeId::U16)});
res.insert({Type::TypeId::I32, container->CreateItem<PrimitiveTypeItem>(Type::TypeId::I32)});
res.insert({Type::TypeId::U32, container->CreateItem<PrimitiveTypeItem>(Type::TypeId::U32)});
res.insert({Type::TypeId::I64, container->CreateItem<PrimitiveTypeItem>(Type::TypeId::I64)});
res.insert({Type::TypeId::U64, container->CreateItem<PrimitiveTypeItem>(Type::TypeId::U64)});
res.insert({Type::TypeId::F32, container->CreateItem<PrimitiveTypeItem>(Type::TypeId::F32)});
res.insert({Type::TypeId::F64, container->CreateItem<PrimitiveTypeItem>(Type::TypeId::F64)});
res.insert({Type::TypeId::TAGGED, container->CreateItem<PrimitiveTypeItem>(Type::TypeId::TAGGED)});
res.insert({Type::TypeId::VOID, container->GetOrCreatePrimitiveTypeItem(Type::TypeId::VOID)});
res.insert({Type::TypeId::U1, container->GetOrCreatePrimitiveTypeItem(Type::TypeId::U1)});
res.insert({Type::TypeId::I8, container->GetOrCreatePrimitiveTypeItem(Type::TypeId::I8)});
res.insert({Type::TypeId::U8, container->GetOrCreatePrimitiveTypeItem(Type::TypeId::U8)});
res.insert({Type::TypeId::I16, container->GetOrCreatePrimitiveTypeItem(Type::TypeId::I16)});
res.insert({Type::TypeId::U16, container->GetOrCreatePrimitiveTypeItem(Type::TypeId::U16)});
res.insert({Type::TypeId::I32, container->GetOrCreatePrimitiveTypeItem(Type::TypeId::I32)});
res.insert({Type::TypeId::U32, container->GetOrCreatePrimitiveTypeItem(Type::TypeId::U32)});
res.insert({Type::TypeId::I64, container->GetOrCreatePrimitiveTypeItem(Type::TypeId::I64)});
res.insert({Type::TypeId::U64, container->GetOrCreatePrimitiveTypeItem(Type::TypeId::U64)});
res.insert({Type::TypeId::F32, container->GetOrCreatePrimitiveTypeItem(Type::TypeId::F32)});
res.insert({Type::TypeId::F64, container->GetOrCreatePrimitiveTypeItem(Type::TypeId::F64)});
res.insert({Type::TypeId::TAGGED, container->GetOrCreatePrimitiveTypeItem(Type::TypeId::TAGGED)});
return res;
}
@ -179,11 +179,30 @@ bool AsmEmitter::CheckValueType(Value::Type value_type, Type type, const Program
}
/* static */
// CODECHECK-NOLINTNEXTLINE(C_RULE_ID_FUNCTION_SIZE)
std::string AsmEmitter::GetMethodSignatureFromProgram(const std::string &name, const Program &program)
{
if (IsSignatureOrMangled(name)) {
return name;
}
const auto it_synonym = program.function_synonyms.find(name);
const bool is_method_known = (it_synonym != program.function_synonyms.end());
const bool is_single_synonym = (is_method_known && (it_synonym->second.size() == 1));
if (is_single_synonym) {
return it_synonym->second[0];
} else {
SetLastError("More than one alternative for method " + name);
return std::string("");
}
}
/* static */
panda_file::LiteralItem *AsmEmitter::CreateLiteralItem(
ItemContainer *container, const Value *value, std::vector<panda_file::LiteralItem> *out,
const std::unordered_map<std::string, panda_file::BaseMethodItem *> &methods)
{
ASSERT(out != nullptr);
auto value_type = value->GetType();
switch (value_type) {
@ -248,7 +267,7 @@ bool AsmEmitter::CheckValueRecordCase(const Value *value, const Program &program
}
auto record_name = t.GetName();
bool is_found = true;
bool is_found;
if (t.IsArray()) {
auto it = program.array_types.find(t);
is_found = it != program.array_types.cend();
@ -421,10 +440,13 @@ ScalarValueItem *AsmEmitter::CreateScalarRecordValueItem(
/* static */
ScalarValueItem *AsmEmitter::CreateScalarMethodValueItem(
ItemContainer *container, const Value *value, std::vector<ScalarValueItem> *out,
ItemContainer *container, const Value *value, std::vector<ScalarValueItem> *out, const Program &program,
const std::unordered_map<std::string, BaseMethodItem *> &methods)
{
auto name = value->GetAsScalar()->GetValue<std::string>();
name = GetMethodSignatureFromProgram(name, program);
auto it = methods.find(name);
if (it == methods.cend()) {
return nullptr;
@ -488,6 +510,7 @@ ScalarValueItem *AsmEmitter::CreateScalarValueItem(ItemContainer *container, con
const std::unordered_map<std::string, BaseMethodItem *> &methods)
{
auto value_type = value->GetType();
switch (value_type) {
case Value::Type::U1:
case Value::Type::I8:
@ -516,7 +539,7 @@ ScalarValueItem *AsmEmitter::CreateScalarValueItem(ItemContainer *container, con
return CreateScalarRecordValueItem(container, value, out, classes);
}
case Value::Type::METHOD: {
return CreateScalarMethodValueItem(container, value, out, methods);
return CreateScalarMethodValueItem(container, value, out, program, methods);
}
case Value::Type::ENUM: {
return CreateScalarEnumValueItem(container, value, out, fields);
@ -600,14 +623,17 @@ AnnotationItem *AsmEmitter::CreateAnnotationItem(ItemContainer *container, const
auto function_name = record.name + "." + name;
function_name = GetMethodSignatureFromProgram(function_name, program);
if (record.HasImplementation()) {
auto func_it = program.function_table.find(function_name);
if (func_it == program.function_table.cend()) {
// Definitions of the system annotations in the libcore haven't values.
// Definitions of the system annotations may be absent.
// So print message and continue if corresponding function isn't found.
LOG(INFO, ASSEMBLER) << "Function " << function_name << " not found";
} else {
auto &function = func_it->second;
if (!CheckValue(value, function.return_type, program)) {
SetLastError("Incorrect annotation element " + function_name + ": " + GetLastError());
return nullptr;
@ -685,25 +711,6 @@ bool AsmEmitter::AddAnnotations(T *item, ItemContainer *container, const Annotat
return true;
}
template <class T>
void SetSourceLang(T *item, extensions::Language lang)
{
switch (lang) {
case extensions::Language::ECMASCRIPT: {
item->SetSourceLang(panda_file::SourceLang::ECMASCRIPT);
break;
}
case extensions::Language::PANDA_ASSEMBLY: {
item->SetSourceLang(panda_file::SourceLang::PANDA_ASSEMBLY);
break;
}
default: {
UNREACHABLE();
break;
}
}
}
template <class T>
static void AddBytecodeIndexDependencies(MethodItem *method, const Ins &insn,
const std::unordered_map<std::string, T *> &items)
@ -767,64 +774,6 @@ void AsmEmitter::MakeStringItems(ItemContainer *items, const Program &program,
}
}
static std::unique_ptr<ScalarValue> CreateValue(const panda::pandasm::LiteralArray::Literal &literal)
{
switch (literal.tag_) {
case panda_file::LiteralTag::TAGVALUE:
[[fallthrough]];
case panda_file::LiteralTag::ACCESSOR:
[[fallthrough]];
case panda_file::LiteralTag::NULLVALUE:
return std::make_unique<ScalarValue>(
ScalarValue::Create<Value::Type::U8>(std::get<uint8_t>(literal.value_)));
case panda_file::LiteralTag::BOOL:
return std::make_unique<ScalarValue>(
ScalarValue::Create<Value::Type::U8>(static_cast<uint8_t>(std::get<bool>(literal.value_))));
case panda_file::LiteralTag::ARRAY_I8:
return std::make_unique<ScalarValue>(
ScalarValue::Create<Value::Type::I8>(std::get<uint8_t>(literal.value_)));
case panda_file::LiteralTag::METHODAFFILIATE:
return std::make_unique<ScalarValue>(
ScalarValue::Create<Value::Type::U16>(std::get<uint16_t>(literal.value_)));
case panda_file::LiteralTag::ARRAY_I16:
return std::make_unique<ScalarValue>(
ScalarValue::Create<Value::Type::I16>(std::get<uint16_t>(literal.value_)));
case panda_file::LiteralTag::INTEGER:
[[fallthrough]];
case panda_file::LiteralTag::ARRAY_I32:
return std::make_unique<ScalarValue>(
ScalarValue::Create<Value::Type::I32>(std::get<uint32_t>(literal.value_)));
case panda_file::LiteralTag::ARRAY_I64:
return std::make_unique<ScalarValue>(
ScalarValue::Create<Value::Type::I64>(std::get<uint64_t>(literal.value_)));
case panda_file::LiteralTag::FLOAT:
[[fallthrough]];
case panda_file::LiteralTag::ARRAY_F32:
return std::make_unique<ScalarValue>(
ScalarValue::Create<Value::Type::F32>(std::get<float>(literal.value_)));
case panda_file::LiteralTag::DOUBLE:
[[fallthrough]];
case panda_file::LiteralTag::ARRAY_F64:
return std::make_unique<ScalarValue>(
ScalarValue::Create<Value::Type::F64>(std::get<double>(literal.value_)));
case panda_file::LiteralTag::STRING:
[[fallthrough]];
case panda_file::LiteralTag::ARRAY_STRING:
return std::make_unique<ScalarValue>(
ScalarValue::Create<Value::Type::STRING>(std::string_view(std::get<std::string>(literal.value_))));
case panda_file::LiteralTag::METHOD:
[[fallthrough]];
case panda_file::LiteralTag::GENERATORMETHOD:
return std::make_unique<ScalarValue>(
ScalarValue::Create<Value::Type::METHOD>(std::string_view(std::get<std::string>(literal.value_))));
default:
UNREACHABLE();
break;
}
return nullptr;
}
/* static */
void AsmEmitter::MakeLiteralItems(ItemContainer *items, const Program &program,
AsmEmitter::AsmEntityCollections &entities)
@ -834,7 +783,122 @@ void AsmEmitter::MakeLiteralItems(ItemContainer *items, const Program &program,
std::vector<panda_file::LiteralItem> literal_array;
for (auto &literal : l.literals_) {
auto value = CreateValue(literal);
std::unique_ptr<ScalarValue> value;
switch (literal.tag_) {
case panda_file::LiteralTag::ARRAY_U1: {
ASSERT(program.array_types.find(Type("u1", 1)) != program.array_types.end());
value = std::make_unique<ScalarValue>(
ScalarValue::Create<Value::Type::U1>(static_cast<bool>(std::get<bool>(literal.value_))));
break;
}
case panda_file::LiteralTag::ARRAY_U8: {
ASSERT(program.array_types.find(Type("u8", 1)) != program.array_types.end());
value = std::make_unique<ScalarValue>(
ScalarValue::Create<Value::Type::U8>(std::get<uint8_t>(literal.value_)));
break;
}
case panda_file::LiteralTag::ARRAY_I8: {
ASSERT(program.array_types.find(Type("i8", 1)) != program.array_types.end());
value = std::make_unique<ScalarValue>(
ScalarValue::Create<Value::Type::I8>(std::get<uint8_t>(literal.value_)));
break;
}
case panda_file::LiteralTag::ARRAY_U16: {
ASSERT(program.array_types.find(Type("u16", 1)) != program.array_types.end());
value = std::make_unique<ScalarValue>(
ScalarValue::Create<Value::Type::U16>(std::get<uint16_t>(literal.value_)));
break;
}
case panda_file::LiteralTag::ARRAY_I16: {
ASSERT(program.array_types.find(Type("i16", 1)) != program.array_types.end());
value = std::make_unique<ScalarValue>(
ScalarValue::Create<Value::Type::I16>(std::get<uint16_t>(literal.value_)));
break;
}
case panda_file::LiteralTag::ARRAY_U32: {
ASSERT(program.array_types.find(Type("u32", 1)) != program.array_types.end());
value = std::make_unique<ScalarValue>(
ScalarValue::Create<Value::Type::U32>(std::get<uint32_t>(literal.value_)));
break;
}
case panda_file::LiteralTag::ARRAY_I32: {
ASSERT(program.array_types.find(Type("i32", 1)) != program.array_types.end());
value = std::make_unique<ScalarValue>(
ScalarValue::Create<Value::Type::I32>(std::get<uint32_t>(literal.value_)));
break;
}
case panda_file::LiteralTag::ARRAY_U64: {
ASSERT(program.array_types.find(Type("u64", 1)) != program.array_types.end());
value = std::make_unique<ScalarValue>(
ScalarValue::Create<Value::Type::U64>(std::get<uint64_t>(literal.value_)));
break;
}
case panda_file::LiteralTag::ARRAY_I64: {
ASSERT(program.array_types.find(Type("i64", 1)) != program.array_types.end());
value = std::make_unique<ScalarValue>(
ScalarValue::Create<Value::Type::I64>(std::get<uint64_t>(literal.value_)));
break;
}
case panda_file::LiteralTag::ARRAY_F32: {
ASSERT(program.array_types.find(Type("f32", 1)) != program.array_types.end());
value = std::make_unique<ScalarValue>(
ScalarValue::Create<Value::Type::F32>(std::get<float>(literal.value_)));
break;
}
case panda_file::LiteralTag::ARRAY_F64: {
ASSERT(program.array_types.find(Type("f64", 1)) != program.array_types.end());
value = std::make_unique<ScalarValue>(
ScalarValue::Create<Value::Type::F64>(std::get<double>(literal.value_)));
break;
}
case panda_file::LiteralTag::ARRAY_STRING:
ASSERT(program.array_types.find(Type(
Type::FromDescriptor(panda::panda_file::GetStringClassDescriptor(program.lang)), 1)) !=
program.array_types.end());
value = std::make_unique<ScalarValue>(ScalarValue::Create<Value::Type::STRING>(
std::string_view(std::get<std::string>(literal.value_))));
break;
case panda_file::LiteralTag::TAGVALUE:
case panda_file::LiteralTag::ACCESSOR:
case panda_file::LiteralTag::NULLVALUE:
value = std::make_unique<ScalarValue>(
ScalarValue::Create<Value::Type::U8>(static_cast<uint8_t>(std::get<uint8_t>(literal.value_))));
break;
case panda_file::LiteralTag::BOOL:
value = std::make_unique<ScalarValue>(
ScalarValue::Create<Value::Type::U8>(static_cast<uint8_t>(std::get<bool>(literal.value_))));
break;
case panda_file::LiteralTag::METHODAFFILIATE:
value = std::make_unique<ScalarValue>(
ScalarValue::Create<Value::Type::U16>(std::get<uint16_t>(literal.value_)));
break;
case panda_file::LiteralTag::INTEGER:
value = std::make_unique<ScalarValue>(
ScalarValue::Create<Value::Type::I32>(std::get<uint32_t>(literal.value_)));
break;
case panda_file::LiteralTag::FLOAT:
value = std::make_unique<ScalarValue>(
ScalarValue::Create<Value::Type::F32>(std::get<float>(literal.value_)));
break;
case panda_file::LiteralTag::DOUBLE:
value = std::make_unique<ScalarValue>(
ScalarValue::Create<Value::Type::F64>(std::get<double>(literal.value_)));
break;
case panda_file::LiteralTag::STRING:
value = std::make_unique<ScalarValue>(ScalarValue::Create<Value::Type::STRING>(
std::string_view(std::get<std::string>(literal.value_))));
break;
case panda_file::LiteralTag::METHOD:
case panda_file::LiteralTag::GENERATORMETHOD:
case panda_file::LiteralTag::ASYNCGENERATORMETHOD:
value = std::make_unique<ScalarValue>(ScalarValue::Create<Value::Type::METHOD>(
std::string_view(std::get<std::string>(literal.value_))));
break;
default:
UNREACHABLE();
}
// the return pointer of vector element should not be rewrited
CreateLiteralItem(items, value.get(), &literal_array, entities.method_items);
}
@ -960,8 +1024,7 @@ bool AsmEmitter::HandleRecord(ItemContainer *items, const Program &program, AsmE
entities.class_items.insert({name, record});
record->SetAccessFlags(rec.metadata->GetAccessFlags());
SetSourceLang(record, rec.language);
record->SetSourceLang(rec.language);
if (!rec.source_file.empty()) {
auto *source_file_item = items->GetOrCreateStringItem(rec.source_file);
@ -1006,14 +1069,12 @@ bool AsmEmitter::MakeRecordItems(
StringItem *AsmEmitter::GetMethodName(ItemContainer *items, const Function &func, const std::string &name)
{
if (func.metadata->IsCtor()) {
return items->GetOrCreateStringItem(extensions::GetCtorName(func.language));
return items->GetOrCreateStringItem(panda::panda_file::GetCtorName(func.language));
} else if (func.metadata->IsCctor()) {
return items->GetOrCreateStringItem(panda::panda_file::GetCctorName(func.language));
} else {
return items->GetOrCreateStringItem(GetItemName(name));
}
if (func.metadata->IsCctor()) {
return items->GetOrCreateStringItem(extensions::GetCctorName(func.language));
}
return items->GetOrCreateStringItem(GetItemName(name));
}
/* static */
@ -1051,7 +1112,7 @@ bool AsmEmitter::HandleRecordOnwer(ItemContainer *items, const Program &program,
if (record_owner_name.empty()) {
*area = items->GetOrCreateGlobalClassItem();
(*area)->SetAccessFlags(ACC_PUBLIC);
SetSourceLang(*area, program.lang);
(*area)->SetSourceLang(program.lang);
} else {
if (!HandleAreaForInner(items, program, area, foreign_area, name, record_owner_name)) {
return false;
@ -1147,6 +1208,7 @@ bool AsmEmitter::MakeFunctionItems(
const auto &[mangled_name, func] = f;
auto name = pandasm::DeMangleName(mangled_name);
StringItem *method_name = GetMethodName(items, func, name);
ClassItem *area = nullptr;
@ -1158,13 +1220,16 @@ bool AsmEmitter::MakeFunctionItems(
}
auto params = std::vector<MethodParamItem> {};
uint32_t access_flags = func.metadata->GetAccessFlags();
if (func.params.empty() || func.params[0].type.GetName() != record_owner_name) {
access_flags |= ACC_STATIC;
}
bool is_static = (access_flags & ACC_STATIC) != 0;
size_t idx = is_static ? 0 : 1;
if (!HandleFunctionParams(items, program, idx, name, func, primitive_types, params)) {
return false;
}
@ -1230,6 +1295,7 @@ void AsmEmitter::SetCodeAndDebugInfo(ItemContainer *items, MethodItem *method, c
{
auto *code = items->CreateItem<CodeItem>();
method->SetCode(code);
code->AddMethod(method); // we need it for Profile-Guided optimization
if (!emit_debug_info && !func.CanThrow()) {
return;
@ -1258,13 +1324,14 @@ void AsmEmitter::SetMethodSourceLang(const Program &program, MethodItem *method,
const std::string &name)
{
std::string record_name = GetOwnerName(name);
if (!record_name.empty()) {
auto &rec = program.record_table.find(record_name)->second;
if (rec.language != func.language) {
SetSourceLang(method, func.language);
}
} else {
SetSourceLang(method, func.language);
if (record_name.empty()) {
method->SetSourceLang(func.language);
return;
}
auto &rec = program.record_table.find(record_name)->second;
if (rec.language != func.language) {
method->SetSourceLang(func.language);
}
}
@ -1312,6 +1379,7 @@ bool AsmEmitter::MakeFunctionDebugInfoAndAnnotations(ItemContainer *items, const
if (func.metadata->IsForeign()) {
continue;
}
auto *method = static_cast<MethodItem *>(Find(entities.method_items, name));
if (func.metadata->HasImplementation()) {
@ -1329,7 +1397,7 @@ bool AsmEmitter::MakeFunctionDebugInfoAndAnnotations(ItemContainer *items, const
}
/* static */
void AsmEmitter::FillMap(PandaFileToPandaAsmMaps *maps, const AsmEmitter::AsmEntityCollections &entities)
void AsmEmitter::FillMap(PandaFileToPandaAsmMaps *maps, AsmEmitter::AsmEntityCollections &entities)
{
for (const auto &[name, method] : entities.method_items) {
maps->methods.insert({method->GetFileId().GetOffset(), std::string(name)});
@ -1408,7 +1476,7 @@ bool AsmEmitter::EmitFunctions(ItemContainer *items, const Program &program,
code->SetNumVregs(func.regs_num);
code->SetNumArgs(func.GetParamsNum());
size_t num_ins = static_cast<size_t>(
auto num_ins = static_cast<size_t>(
std::count_if(func.ins.begin(), func.ins.end(), [](auto it) { return it.opcode != Opcode::INVALID; }));
code->SetNumInstructions(num_ins);
@ -1430,7 +1498,8 @@ bool AsmEmitter::EmitFunctions(ItemContainer *items, const Program &program,
}
/* static */
bool AsmEmitter::Emit(ItemContainer *items, const Program &program, PandaFileToPandaAsmMaps *maps, bool emit_debug_info)
bool AsmEmitter::Emit(ItemContainer *items, const Program &program, PandaFileToPandaAsmMaps *maps, bool emit_debug_info,
panda::panda_file::pgo::ProfileOptimizer *profile_opt)
{
auto primitive_types = CreatePrimitiveTypes(items);
@ -1457,11 +1526,15 @@ bool AsmEmitter::Emit(ItemContainer *items, const Program &program, PandaFileToP
return false;
}
// Add Code and DebugInfo items last due to that they have variable size that depends on bytecode
// Add Code and DebugInfo items last due to they have variable size that depends on bytecode
if (!MakeFunctionDebugInfoAndAnnotations(items, program, entities, emit_debug_info)) {
return false;
}
if (profile_opt != nullptr) {
items->ReorderItems(profile_opt);
}
items->ComputeLayout();
if (maps != nullptr) {
@ -1476,10 +1549,11 @@ bool AsmEmitter::Emit(ItemContainer *items, const Program &program, PandaFileToP
}
bool AsmEmitter::Emit(Writer *writer, const Program &program, std::map<std::string, size_t> *stat,
PandaFileToPandaAsmMaps *maps, bool debug_info)
PandaFileToPandaAsmMaps *maps, bool debug_info,
panda::panda_file::pgo::ProfileOptimizer *profile_opt)
{
auto items = ItemContainer {};
if (!Emit(&items, program, maps, debug_info)) {
if (!Emit(&items, program, maps, debug_info, profile_opt)) {
return false;
}
@ -1491,14 +1565,15 @@ bool AsmEmitter::Emit(Writer *writer, const Program &program, std::map<std::stri
}
bool AsmEmitter::Emit(const std::string &filename, const Program &program, std::map<std::string, size_t> *stat,
PandaFileToPandaAsmMaps *maps, bool debug_info)
PandaFileToPandaAsmMaps *maps, bool debug_info,
panda::panda_file::pgo::ProfileOptimizer *profile_opt)
{
auto writer = FileWriter(filename);
if (!writer) {
SetLastError("Unable to open" + filename + " for writing");
return false;
}
return Emit(&writer, program, stat, maps, debug_info);
return Emit(&writer, program, stat, maps, debug_info, profile_opt);
}
std::unique_ptr<const panda_file::File> AsmEmitter::Emit(const Program &program, PandaFileToPandaAsmMaps *maps)
@ -1509,12 +1584,7 @@ std::unique_ptr<const panda_file::File> AsmEmitter::Emit(const Program &program,
}
size_t size = items.ComputeLayout();
// CODECHECK-NOLINTNEXTLINE(CPP_RULE_ID_SMARTPOINTER_INSTEADOF_ORIGINPOINTER)
auto *buffer = new (std::nothrow) std::byte[size];
if (buffer == nullptr) {
LOG(ERROR, ASSEMBLER) << "Invalid items size " << size;
return nullptr;
}
auto *buffer = new std::byte[size];
auto writer = MemoryBufferWriter(reinterpret_cast<uint8_t *>(buffer), size);
if (!items.Write(&writer)) {
@ -1545,6 +1615,7 @@ TypeItem *AsmEmitter::GetTypeItem(
}
auto &rec = iter->second;
if (rec.metadata->IsForeign()) {
return items->GetOrCreateForeignClassItem(type.GetDescriptor());
}
@ -1620,7 +1691,7 @@ size_t Function::GetLineNumber(size_t i) const
return ins[i].ins_debug.line_number;
}
size_t Function::GetColumnNumber(size_t i) const
uint32_t Function::GetColumnNumber(size_t i) const
{
return ins[i].ins_debug.column_number;
}
@ -1654,9 +1725,9 @@ void Function::EmitLineNumber(panda_file::LineNumberProgramItem *program, std::v
}
void Function::EmitColumnNumber(panda_file::LineNumberProgramItem *program, std::vector<uint8_t> *constant_pool,
int32_t &prev_column_number, uint32_t &pc_inc, size_t instruction_number) const
uint32_t &prev_column_number, uint32_t &pc_inc, size_t instruction_number) const
{
auto cn = static_cast<int32_t>(GetColumnNumber(instruction_number));
auto cn = GetColumnNumber(instruction_number);
if (cn != prev_column_number) {
program->EmitColumn(constant_pool, pc_inc, cn);
pc_inc = 0;
@ -1677,7 +1748,7 @@ void Function::BuildLineNumberProgram(panda_file::DebugInfoItem *debug_item, con
uint32_t pc_inc = 0;
auto prev_line_number = static_cast<int32_t>(GetLineNumber(0));
auto prev_column_number = -1;
uint32_t prev_column_number = std::numeric_limits<uint32_t>::max();
BytecodeInstruction bi(bytecode.data());
debug_item->SetLineNumber(static_cast<uint32_t>(prev_line_number));
@ -1693,7 +1764,7 @@ void Function::BuildLineNumberProgram(panda_file::DebugInfoItem *debug_item, con
EmitLineNumber(program, constant_pool, prev_line_number, pc_inc, i);
}
if (language == pandasm::extensions::Language::ECMASCRIPT && emit_debug_info) {
if (emit_debug_info) {
EmitColumnNumber(program, constant_pool, prev_column_number, pc_inc, i);
}
@ -1799,21 +1870,27 @@ void Function::DebugDump() const
}
}
std::string GetOwnerName(const std::string &name)
std::string GetOwnerName(std::string name)
{
name = DeMangleName(name);
auto super_pos = name.find_last_of(PARSE_AREA_MARKER);
if (super_pos == std::string::npos) {
return "";
}
return name.substr(0, super_pos);
}
std::string GetItemName(const std::string &name)
std::string GetItemName(std::string name)
{
name = DeMangleName(name);
auto super_pos = name.find_last_of(PARSE_AREA_MARKER);
if (super_pos == std::string::npos) {
return name;
}
return name.substr(super_pos + 1);
}

View File

@ -1,4 +1,4 @@
/*
/**
* Copyright (c) 2021-2022 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.
@ -13,8 +13,8 @@
* limitations under the License.
*/
#ifndef PANDA_ASSEMBLER_ASSEMBLY_EMITTER_H_
#define PANDA_ASSEMBLER_ASSEMBLY_EMITTER_H_
#ifndef _PANDA_ASSEMBLER_EMITTER_HPP
#define _PANDA_ASSEMBLER_EMITTER_HPP
#include <memory>
#include <type_traits>
@ -28,6 +28,7 @@
#include "assembly-function.h"
#include "bytecode_emitter.h"
#include "file_item_container.h"
#include "pgo.h"
namespace panda::pandasm {
@ -50,13 +51,15 @@ public:
};
static bool Emit(panda_file::ItemContainer *items, const Program &program, PandaFileToPandaAsmMaps *maps = nullptr,
bool emit_debug_info = true);
bool emit_debug_info = true, panda::panda_file::pgo::ProfileOptimizer *profile_opt = nullptr);
static bool Emit(panda_file::Writer *writer, const Program &program, std::map<std::string, size_t> *stat = nullptr,
PandaFileToPandaAsmMaps *maps = nullptr, bool debug_info = true);
PandaFileToPandaAsmMaps *maps = nullptr, bool debug_info = true,
panda::panda_file::pgo::ProfileOptimizer *profile_opt = nullptr);
static bool Emit(const std::string &filename, const Program &program, std::map<std::string, size_t> *stat = nullptr,
PandaFileToPandaAsmMaps *maps = nullptr, bool debug_info = true);
PandaFileToPandaAsmMaps *maps = nullptr, bool debug_info = true,
panda::panda_file::pgo::ProfileOptimizer *profile_opt = nullptr);
static std::unique_ptr<const panda_file::File> Emit(const Program &program,
PandaFileToPandaAsmMaps *maps = nullptr);
@ -127,7 +130,7 @@ private:
panda_file::MethodItem *method, const Function &func);
static bool MakeFunctionDebugInfoAndAnnotations(panda_file::ItemContainer *items, const Program &program,
const AsmEntityCollections &entities, bool emit_debug_info);
static void FillMap(PandaFileToPandaAsmMaps *maps, const AsmEntityCollections &entities);
static void FillMap(PandaFileToPandaAsmMaps *maps, AsmEntityCollections &entities);
static void EmitDebugInfo(panda_file::ItemContainer *items, const Program &program,
const std::vector<uint8_t> *bytes, const panda_file::MethodItem *method,
const Function &func, const std::string &name, bool emit_debug_info);
@ -151,6 +154,7 @@ private:
static bool CheckValueMethodCase(const Value *value, const Program &program);
static bool CheckValueRecordCase(const Value *value, const Program &program);
static bool CheckValue(const Value *value, Type type, const Program &program);
static std::string GetMethodSignatureFromProgram(const std::string &name, const Program &program);
static panda_file::LiteralItem *CreateLiteralItem(
panda_file::ItemContainer *container, const Value *value, std::vector<panda_file::LiteralItem> *out,
@ -190,7 +194,7 @@ private:
const std::unordered_map<std::string, panda_file::BaseClassItem *> &classes);
static panda_file::ScalarValueItem *CreateScalarMethodValueItem(
panda_file::ItemContainer *container, const Value *value, std::vector<panda_file::ScalarValueItem> *out,
const std::unordered_map<std::string, panda_file::BaseMethodItem *> &methods);
const Program &program, const std::unordered_map<std::string, panda_file::BaseMethodItem *> &methods);
static panda_file::ScalarValueItem *CreateScalarEnumValueItem(
panda_file::ItemContainer *container, const Value *value, std::vector<panda_file::ScalarValueItem> *out,
const std::unordered_map<std::string, panda_file::BaseFieldItem *> &fields);
@ -230,12 +234,13 @@ private:
const std::unordered_map<std::string, panda_file::BaseFieldItem *> &fields,
const std::unordered_map<std::string, panda_file::BaseMethodItem *> &methods);
// TODO(mgonopolsky): Refactor to introduce a single error-processing mechanism for parser and emitter
static std::string last_error;
};
std::string GetOwnerName(const std::string &name);
std::string GetItemName(const std::string &name);
std::string GetOwnerName(std::string name);
std::string GetItemName(std::string name);
} // namespace panda::pandasm
#endif // PANDA_ASSEMBLER_ASSEMBLY_EMITTER_H_
#endif // !_PANDA_ASSEMBLER_EMITTER_HPP

View File

@ -1,4 +1,4 @@
/*
/**
* Copyright (c) 2021-2022 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.
@ -13,8 +13,8 @@
* limitations under the License.
*/
#ifndef PANDA_ASSEMBLER_ASSEMBLY_FIELD_H_
#define PANDA_ASSEMBLER_ASSEMBLY_FIELD_H_
#ifndef _PANDA_ASSEMBLER_FIELD_HPP
#define _PANDA_ASSEMBLER_FIELD_HPP
#include <memory>
#include <string>
@ -31,14 +31,17 @@ struct Field {
std::unique_ptr<FieldMetadata> metadata;
size_t line_of_def = 0;
std::string whole_line = ""; /* The line in which the field is defined */
/* Or line in which the field is met, if the field is not defined */
/* Or line in which the field met, if the field is not defined */
size_t bound_left = 0;
size_t bound_right = 0;
bool is_defined = true;
explicit Field(extensions::Language lang) : metadata(extensions::MetadataExtension::CreateFieldMetadata(lang)) {}
explicit Field(panda::panda_file::SourceLang lang)
: metadata(extensions::MetadataExtension::CreateFieldMetadata(lang))
{
}
};
} // namespace panda::pandasm
#endif // PANDA_ASSEMBLER_ASSEMBLY_FIELD_H_
#endif // !_PANDA_ASSEMBLER_FIELD_HPP

View File

@ -1,4 +1,4 @@
/*
/**
* Copyright (c) 2021-2022 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.
@ -13,15 +13,15 @@
* limitations under the License.
*/
#ifndef PANDA_ASSEMBLER_ASSEMBLY_FILE_LOCATION_H_
#define PANDA_ASSEMBLER_ASSEMBLY_FILE_LOCATION_H_
#ifndef _PANDA_ASSEMBLER_FILE_LOCATION_HPP
#define _PANDA_ASSEMBLER_FILE_LOCATION_HPP
namespace panda::pandasm {
class FileLocation {
public:
std::string whole_line = ""; /* The line in which the field is defined */
/* Or line in which the field is met, if the field is not defined */
/* Or line in which the field met, if the field is not defined */
size_t bound_left = 0;
size_t bound_right = 0;
size_t line_number = 0;
@ -40,4 +40,4 @@ public:
} // namespace panda::pandasm
#endif // PANDA_ASSEMBLER_ASSEMBLY_FILE_LOCATION_H_
#endif // !_PANDA_ASSEMBLER_FILE_INFORMATION

View File

@ -1,4 +1,4 @@
/*
/**
* Copyright (c) 2021-2022 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.
@ -13,8 +13,8 @@
* limitations under the License.
*/
#ifndef PANDA_ASSEMBLER_ASSEMBLY_FUNCTION_H_
#define PANDA_ASSEMBLER_ASSEMBLY_FUNCTION_H_
#ifndef _PANDA_ASSEMBLER_FUNCTION_HPP
#define _PANDA_ASSEMBLER_FUNCTION_HPP
#include <memory>
#include <optional>
@ -63,14 +63,14 @@ struct Function {
Type type;
std::unique_ptr<ParamMetadata> metadata;
Parameter(Type t, extensions::Language lang)
Parameter(Type t, panda::panda_file::SourceLang lang)
: type(std::move(t)), metadata(extensions::MetadataExtension::CreateParamMetadata(lang))
{
}
};
std::string name = "";
extensions::Language language;
panda::panda_file::SourceLang language;
std::unique_ptr<FunctionMetadata> metadata;
std::unordered_map<std::string, panda::pandasm::Label> label_table;
@ -100,7 +100,8 @@ struct Function {
ins.emplace_back(instruction);
}
Function(std::string s, extensions::Language lang, size_t b_l, size_t b_r, std::string f_c, bool d, size_t l_n)
Function(std::string s, panda::panda_file::SourceLang lang, size_t b_l, size_t b_r, std::string f_c, bool d,
size_t l_n)
: name(std::move(s)),
language(lang),
metadata(extensions::MetadataExtension::CreateFunctionMetadata(lang)),
@ -108,7 +109,7 @@ struct Function {
{
}
Function(std::string s, extensions::Language lang)
Function(std::string s, panda::panda_file::SourceLang lang)
: name(std::move(s)), language(lang), metadata(extensions::MetadataExtension::CreateFunctionMetadata(lang))
{
}
@ -118,6 +119,11 @@ struct Function {
return params.size();
}
std::size_t GetTotalRegs() const
{
return regs_num;
}
bool IsStatic() const
{
return (metadata->GetAccessFlags() & ACC_STATIC) != 0;
@ -132,7 +138,7 @@ struct Function {
size_t GetLineNumber(size_t i) const;
size_t GetColumnNumber(size_t i) const;
uint32_t GetColumnNumber(size_t i) const;
void EmitLocalVariable(panda_file::LineNumberProgramItem *program, panda_file::ItemContainer *container,
std::vector<uint8_t> *constant_pool, uint32_t &pc_inc, size_t instruction_number) const;
@ -140,9 +146,10 @@ struct Function {
int32_t line_inc) const;
void EmitLineNumber(panda_file::LineNumberProgramItem *program, std::vector<uint8_t> *constant_pool,
int32_t &prev_line_number, uint32_t &pc_inc, size_t instruction_number) const;
// column number is only for javascript for now
// column number is only for dynamic language now
void EmitColumnNumber(panda_file::LineNumberProgramItem *program, std::vector<uint8_t> *constant_pool,
int32_t &prev_column_number, uint32_t &pc_inc, size_t instruction_number) const;
uint32_t &prev_column_number, uint32_t &pc_inc, size_t instruction_number) const;
void BuildLineNumberProgram(panda_file::DebugInfoItem *debug_item, const std::vector<uint8_t> &bytecode,
panda_file::ItemContainer *container, std::vector<uint8_t> *constant_pool,
@ -179,4 +186,4 @@ struct Function {
} // namespace panda::pandasm
#endif // PANDA_ASSEMBLER_ASSEMBLY_FUNCTION_H_
#endif // !_PANDA_ASSEMBLER_FUNCTION_HPP

View File

@ -73,29 +73,80 @@ std::string panda::pandasm::Ins::IdsToString(bool &first) const
return translator.str();
}
std::string panda::pandasm::Ins::OperandsToString(PrintKind print_kind, bool print_args, size_t first_arg_idx) const
std::string panda::pandasm::Ins::OperandsToString(bool print_args, size_t first_arg_idx) const
{
bool first = true;
std::stringstream ss {};
switch (print_kind) {
case PrintKind::CALL:
ss << this->IdsToString(first) << this->RegsToString(first, print_args, first_arg_idx);
if (!imms.empty()) {
ss << ImmsToString(first);
}
break;
case PrintKind::CALLI:
ss << this->IdsToString(first) << this->ImmsToString(first)
<< this->RegsToString(first, print_args, first_arg_idx);
break;
case PrintKind::DEFAULT:
default:
ss << this->RegsToString(first, print_args, first_arg_idx) << this->ImmsToString(first)
<< this->IdsToString(first);
}
ss << this->RegsToString(first, print_args, first_arg_idx) << this->ImmsToString(first) << this->IdsToString(first);
return ss.str();
}
std::string panda::pandasm::Ins::RegToString(size_t idx, bool is_first, bool print_args,
size_t first_arg_idx) const
{
if (idx >= regs.size()) {
return std::string("");
}
std::stringstream translator;
if (!is_first) {
translator << ", ";
} else {
translator << " ";
}
if (print_args && regs[idx] >= first_arg_idx) {
translator << "a" << regs[idx] - first_arg_idx;
} else {
translator << "v" << regs[idx];
}
return translator.str();
}
std::string panda::pandasm::Ins::ImmToString(size_t idx, bool is_first) const
{
if (idx >= imms.size()) {
return std::string("");
}
auto *number = std::get_if<double>(&(imms[idx]));
std::stringstream translator;
if (!is_first) {
translator << ", ";
} else {
translator << " ";
}
if (number != nullptr) {
translator << std::scientific << *number;
} else {
translator << "0x" << std::hex << std::get<int64_t>(imms[idx]);
}
return translator.str();
}
std::string panda::pandasm::Ins::IdToString(size_t idx, bool is_first) const
{
if (idx >= ids.size()) {
return std::string("");
}
std::stringstream translator;
if (!is_first) {
translator << ", ";
} else {
translator << " ";
}
translator << ids[idx];
return translator.str();
}
} // namespace panda::pandasm

View File

@ -1,4 +1,4 @@
/*
/**
* Copyright (c) 2021-2022 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.
@ -13,8 +13,8 @@
* limitations under the License.
*/
#ifndef PANDA_ASSEMBLER_ASSEMBLY_INS_H_
#define PANDA_ASSEMBLER_ASSEMBLY_INS_H_
#ifndef _PANDA_ASSEMBLER_INS_HPP
#define _PANDA_ASSEMBLER_INS_HPP
#include <array>
#include <string>
@ -53,14 +53,13 @@ enum InstFlags {
FIELD_ID = (1U << 9U),
TYPE_ID = (1U << 10U),
STRING_ID = (1U << 11U),
LITERALARRAY_ID = (1U << 12U)
LITERALARRAY_ID = (1U << 12U),
CALL_RANGE = (1U << 13U)
};
enum class PrintKind { DEFAULT, CALL, CALLI };
constexpr int INVALID_REG_IDX = -1;
constexpr size_t MAX_NUMBER_OF_SRC_REGS = 5;
constexpr size_t NUM_OPCODES = static_cast<size_t>(Opcode::NUM_OPCODES);
constexpr size_t MAX_NUMBER_OF_SRC_REGS = 5; // TODO(mbolshov): auto-generate
constexpr InstFlags operator|(InstFlags a, InstFlags b)
{
@ -69,20 +68,24 @@ constexpr InstFlags operator|(InstFlags a, InstFlags b)
}
#define OPLIST(opcode, name, optype, width, flags, def_idx, use_idxs) flags,
constexpr std::array<unsigned, NUM_OPCODES> INST_FLAGS_TABLE = {PANDA_INSTRUCTION_LIST(OPLIST)};
constexpr std::array<unsigned, static_cast<size_t>(Opcode::NUM_OPCODES)> INST_FLAGS_TABLE = {
PANDA_INSTRUCTION_LIST(OPLIST)};
#undef OPLIST
#define OPLIST(opcode, name, optype, width, flags, def_idx, use_idxs) width,
constexpr std::array<size_t, NUM_OPCODES> INST_WIDTH_TABLE = {PANDA_INSTRUCTION_LIST(OPLIST)};
constexpr std::array<size_t, static_cast<size_t>(Opcode::NUM_OPCODES)> INST_WIDTH_TABLE = {
PANDA_INSTRUCTION_LIST(OPLIST)};
#undef OPLIST
#define OPLIST(opcode, name, optype, width, flags, def_idx, use_idxs) def_idx,
constexpr std::array<int, NUM_OPCODES> DEF_IDX_TABLE = {PANDA_INSTRUCTION_LIST(OPLIST)};
constexpr std::array<int, static_cast<size_t>(Opcode::NUM_OPCODES)> DEF_IDX_TABLE = {PANDA_INSTRUCTION_LIST(OPLIST)};
#undef OPLIST
#define OPLIST(opcode, name, optype, width, flags, def_idx, use_idxs) use_idxs,
constexpr std::array<std::array<int, MAX_NUMBER_OF_SRC_REGS>, NUM_OPCODES> USE_IDXS_TABLE = {
// clang-format off
constexpr std::array<std::array<int, MAX_NUMBER_OF_SRC_REGS>, static_cast<size_t>(Opcode::NUM_OPCODES)> USE_IDXS_TABLE = {
PANDA_INSTRUCTION_LIST(OPLIST)};
// clang-format on
#undef OPLIST
struct Ins {
@ -119,7 +122,7 @@ struct Ins {
bool HasFlag(InstFlags flag) const
{
if (opcode == Opcode::INVALID) {
if (opcode == Opcode::INVALID) { // TODO(mbolshov): introduce 'label' opcode for labels
return false;
}
return (INST_FLAGS_TABLE[static_cast<size_t>(opcode)] & flag) != 0;
@ -146,6 +149,11 @@ struct Ins {
return HasFlag(InstFlags::CALL);
}
bool IsCallRange() const
{ // Range call
return HasFlag(InstFlags::CALL_RANGE);
}
bool IsPseudoCall() const
{
return HasFlag(InstFlags::PSEUDO) && HasFlag(InstFlags::CALL);
@ -175,15 +183,16 @@ struct Ins {
}
auto use_idxs = USE_IDXS_TABLE[static_cast<size_t>(opcode)];
std::vector<uint16_t> res;
std::vector<uint16_t> res(MAX_NUMBER_OF_SRC_REGS + 1);
if (HasFlag(InstFlags::ACC_READ)) {
res.push_back(Ins::ACCUMULATOR);
}
for (auto idx : use_idxs) {
if (HasFlag(InstFlags::ACC_READ)) {
res.push_back(Ins::ACCUMULATOR);
}
if (idx != INVALID_REG_IDX) {
ASSERT(static_cast<size_t>(idx) < regs.size());
res.push_back(regs[idx]);
if (idx == INVALID_REG_IDX) {
break;
}
ASSERT(static_cast<size_t>(idx) < regs.size());
res.emplace_back(regs[idx]);
}
return res;
}
@ -220,12 +229,15 @@ struct Ins {
}
private:
std::string OperandsToString(PrintKind print_kind = PrintKind::DEFAULT, bool print_args = false,
size_t first_arg_idx = 0) const;
std::string OperandsToString(bool print_args = false, size_t first_arg_idx = 0) const;
std::string RegsToString(bool &first, bool print_args = false, size_t first_arg_idx = 0) const;
std::string ImmsToString(bool &first) const;
std::string IdsToString(bool &first) const;
std::string IdToString(size_t idx, bool is_first) const;
std::string ImmToString(size_t idx, bool is_first) const;
std::string RegToString(size_t idx, bool is_first, bool print_args = false, size_t first_arg_idx = 0) const;
};
} // namespace panda::pandasm
#endif // PANDA_ASSEMBLER_ASSEMBLY_INS_H_
#endif // !_PANDA_ASSEMBLER_INS_HPP

View File

@ -1,4 +1,4 @@
/*
/**
* Copyright (c) 2021-2022 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.
@ -13,8 +13,8 @@
* limitations under the License.
*/
#ifndef PANDA_ASSEMBLER_ASSEMBLY_LABEL_H_
#define PANDA_ASSEMBLER_ASSEMBLY_LABEL_H_
#ifndef _PANDA_ASSEMBLER_LABEL_HPP
#define _PANDA_ASSEMBLER_LABEL_HPP
#include <string>
#include "assembly-file-location.h"
@ -35,4 +35,4 @@ struct Label {
} // namespace panda::pandasm
#endif // PANDA_ASSEMBLER_ASSEMBLY_LABEL_H_
#endif // !_PANDA_ASSEMBLER_LABEL_HPP

View File

@ -1,4 +1,4 @@
/*
/**
* Copyright (c) 2021-2022 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.
@ -13,21 +13,118 @@
* limitations under the License.
*/
#ifndef PANDA_ASSEMBLER_ASSEMBLY_LITERALS_H_
#define PANDA_ASSEMBLER_ASSEMBLY_LITERALS_H_
#ifndef _PANDA_ASSEMBLER_LITERALARRAY_HPP
#define _PANDA_ASSEMBLER_LITERALARRAY_HPP
#include <string>
#include <vector>
#include "../libpandafile/literal_data_accessor.h"
#include "libpandafile/literal_data_accessor-inl.h"
namespace panda::pandasm {
struct LiteralArray {
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
struct Literal {
// NOLINTNEXTLINE(misc-non-private-member-variables-in-classes)
panda_file::LiteralTag tag_;
std::variant<uint8_t, uint16_t, uint32_t, uint64_t, float, double, bool, std::string> value_;
// NOLINTNEXTLINE(misc-non-private-member-variables-in-classes)
std::variant<bool, uint8_t, uint16_t, uint32_t, uint64_t, float, double, std::string> value_;
bool IsBoolValue() const
{
switch (tag_) {
case panda_file::LiteralTag::ARRAY_U1:
case panda_file::LiteralTag::BOOL:
return true;
default:
return false;
}
}
bool IsByteValue() const
{
switch (tag_) {
case panda_file::LiteralTag::ARRAY_U8:
case panda_file::LiteralTag::ARRAY_I8:
case panda_file::LiteralTag::TAGVALUE:
case panda_file::LiteralTag::ACCESSOR:
case panda_file::LiteralTag::NULLVALUE:
return true;
default:
return false;
}
}
bool IsShortValue() const
{
switch (tag_) {
case panda_file::LiteralTag::ARRAY_U16:
case panda_file::LiteralTag::ARRAY_I16:
return true;
default:
return false;
}
}
bool IsIntegerValue() const
{
switch (tag_) {
case panda_file::LiteralTag::ARRAY_U32:
case panda_file::LiteralTag::ARRAY_I32:
case panda_file::LiteralTag::INTEGER:
return true;
default:
return false;
}
}
bool IsLongValue() const
{
switch (tag_) {
case panda_file::LiteralTag::ARRAY_U64:
case panda_file::LiteralTag::ARRAY_I64:
return true;
default:
return false;
}
}
bool IsFloatValue() const
{
switch (tag_) {
case panda_file::LiteralTag::ARRAY_F32:
case panda_file::LiteralTag::FLOAT:
return true;
default:
return false;
}
}
bool IsDoubleValue() const
{
switch (tag_) {
case panda_file::LiteralTag::ARRAY_F64:
case panda_file::LiteralTag::DOUBLE:
return true;
default:
return false;
}
}
bool IsStringValue() const
{
switch (tag_) {
case panda_file::LiteralTag::ARRAY_STRING:
case panda_file::LiteralTag::STRING:
case panda_file::LiteralTag::METHOD:
case panda_file::LiteralTag::GENERATORMETHOD:
case panda_file::LiteralTag::ASYNCGENERATORMETHOD:
return true;
default:
return false;
}
}
};
std::vector<panda::pandasm::LiteralArray::Literal>
@ -37,8 +134,38 @@ struct LiteralArray {
{
}
explicit LiteralArray() = default;
static constexpr panda_file::LiteralTag GetArrayTagFromComponentType(panda_file::Type::TypeId type)
{
switch (type) {
case panda_file::Type::TypeId::U1:
return panda_file::LiteralTag::ARRAY_U1;
case panda_file::Type::TypeId::U8:
return panda_file::LiteralTag::ARRAY_U8;
case panda_file::Type::TypeId::I8:
return panda_file::LiteralTag::ARRAY_I8;
case panda_file::Type::TypeId::U16:
return panda_file::LiteralTag::ARRAY_U16;
case panda_file::Type::TypeId::I16:
return panda_file::LiteralTag::ARRAY_I16;
case panda_file::Type::TypeId::U32:
return panda_file::LiteralTag::ARRAY_U32;
case panda_file::Type::TypeId::I32:
return panda_file::LiteralTag::ARRAY_I32;
case panda_file::Type::TypeId::U64:
return panda_file::LiteralTag::ARRAY_U64;
case panda_file::Type::TypeId::I64:
return panda_file::LiteralTag::ARRAY_I64;
case panda_file::Type::TypeId::F32:
return panda_file::LiteralTag::ARRAY_F32;
case panda_file::Type::TypeId::F64:
return panda_file::LiteralTag::ARRAY_F64;
default:
UNREACHABLE();
}
}
};
} // namespace panda::pandasm
#endif // PANDA_ASSEMBLER_ASSEMBLY_LITERALS_H_
#endif // !_PANDA_ASSEMBLER_LITERALARRAY_HPP

View File

@ -1,4 +1,4 @@
/*
/**
* Copyright (c) 2021-2022 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.
@ -13,8 +13,8 @@
* limitations under the License.
*/
#ifndef PANDA_ASSEMBLER_ASSEMBLY_METHODHANDLE_H_
#define PANDA_ASSEMBLER_ASSEMBLY_METHODHANDLE_H_
#ifndef _PANDA_ASSEMBLER_METHODHANDLE
#define _PANDA_ASSEMBLER_METHODHANDLE
#include <string>
@ -28,4 +28,4 @@ struct MethodHandle {
} // namespace panda::pandasm
#endif // PANDA_ASSEMBLER_ASSEMBLY_METHODHANDLE_H_
#endif // _PANDA_ASSEMBLER_METHODHANDLE

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
/*
/**
* Copyright (c) 2021-2022 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.
@ -13,8 +13,8 @@
* limitations under the License.
*/
#ifndef PANDA_ASSEMBLER_ASSEMBLY_PARSER_H_
#define PANDA_ASSEMBLER_ASSEMBLY_PARSER_H_
#ifndef _PANDA_ASSEMBLER_PARSER_HPP
#define _PANDA_ASSEMBLER_PARSER_HPP
#include <iostream>
#include <memory>
@ -90,6 +90,8 @@ private:
Metadata *metadata_ = nullptr;
Context context_; /* token iterator */
panda::pandasm::Record *curr_record_ = nullptr;
panda::pandasm::LiteralArray *curr_array_ = nullptr;
panda::pandasm::LiteralArray::Literal *curr_array_elem_ = nullptr;
panda::pandasm::Function *curr_func_ = nullptr;
panda::pandasm::Ins *curr_ins_ = nullptr;
panda::pandasm::Field *curr_fld_ = nullptr;
@ -98,7 +100,9 @@ private:
panda::pandasm::ErrorList war_;
bool open_ = false; /* flag of being in a code section */
bool record_def_ = false;
bool array_def_ = false;
bool func_def_ = false;
static constexpr uint32_t INTRO_CONST_ARRAY_LITERALS_NUMBER = 2;
inline Error GetError(const std::string &mess = "", Error::ErrorType err = Error::ErrorType::ERR_NONE,
int8_t shift = 0, int token_shift = 0, const std::string &add_mess = "") const
@ -138,6 +142,7 @@ private:
bool ParseFunctionCode();
bool ParseFunctionInstruction();
bool ParseFunctionFullSign();
bool UpdateFunctionName();
bool ParseFunctionReturn();
bool ParseFunctionArg();
bool ParseFunctionArgComma(bool &comma);
@ -154,11 +159,25 @@ private:
bool ParseRecordField();
bool ParseRecordName();
bool RecordValidName();
bool ParseArrayFullSign();
bool IsConstArray();
bool ParseArrayName();
bool ArrayValidName();
bool ArrayElementsValidNumber();
bool ParseArrayElements();
bool ParseArrayElement();
bool ParseArrayElementType();
bool ParseArrayElementValue();
bool ParseArrayElementValueInteger();
bool ParseArrayElementValueFloat();
bool ParseArrayElementValueString();
bool ParseFieldName();
bool ParseFieldType();
std::optional<std::string> ParseStringLiteral();
int64_t MnemonicToBuiltinId();
bool ParseInteger(int64_t *value);
bool ParseFloat(double *value, bool is_64bit);
bool ParseOperandVreg();
bool ParseOperandComma();
bool ParseOperandInteger();
@ -169,15 +188,20 @@ private:
bool ParseOperandType(Type::VerificationType ver_type);
bool ParseOperandNone();
bool ParseOperandString();
bool ParseOperandLiteralArray();
bool ParseOperandCall();
bool ParseOperandSignature(std::string *sign);
bool ParseOperandSignatureTypesList(std::string *sign);
bool ParseOperandBuiltinMnemonic();
void SetFunctionInformation();
void SetRecordInformation();
void SetArrayInformation();
void SetOperationInformation();
void ParseAsCatchall(const std::vector<Token> &tokens);
void ParseAsLanguage(const std::vector<Token> &tokens, bool &is_lang_parsed, bool &is_first_statement);
void ParseAsRecord(const std::vector<Token> &tokens);
void ParseAsArray(const std::vector<Token> &tokens);
void ParseAsFunction(const std::vector<Token> &tokens);
void ParseAsBraceRight(const std::vector<Token> &tokens);
bool ParseAfterLine(bool &is_first_statement);
@ -186,6 +210,7 @@ private:
void ParseResetTables();
void ParseResetFunctionTable();
void ParseResetRecordTable();
void ParseResetArrayTable();
void ParseAsLanguageDirective();
Function::CatchBlock PrepareCatchBlock(bool is_catchall, size_t size, size_t catchall_tokens_num,
size_t catch_tokens_num);
@ -248,4 +273,4 @@ inline auto Parser::TryEmplaceInTable(bool flag, std::unordered_map<std::string,
} // namespace panda::pandasm
#endif // PANDA_ASSEMBLER_ASSEMBLY_PARSER_H_
#endif // !_PANDA_ASSEMBLER_PARSER_HPP

View File

@ -1,4 +1,4 @@
/*
/**
* Copyright (c) 2021-2022 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.
@ -13,11 +13,10 @@
* limitations under the License.
*/
#include "assembly-program.h"
#include <sstream>
#include "ide_helpers.h"
#include "assembly-program.h"
namespace panda::pandasm {

View File

@ -1,4 +1,4 @@
/*
/**
* Copyright (c) 2021-2022 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.
@ -13,8 +13,8 @@
* limitations under the License.
*/
#ifndef PANDA_ASSEMBLER_ASSEMBLY_PROGRAM_H_
#define PANDA_ASSEMBLER_ASSEMBLY_PROGRAM_H_
#ifndef _PANDA_ASSEMBLER_PROGRAM_HPP
#define _PANDA_ASSEMBLER_PROGRAM_HPP
#include <string>
#include <unordered_set>
@ -30,9 +30,10 @@
namespace panda::pandasm {
struct Program {
extensions::Language lang {extensions::Language::PANDA_ASSEMBLY};
panda::panda_file::SourceLang lang {panda::panda_file::SourceLang::PANDA_ASSEMBLY};
std::unordered_map<std::string, panda::pandasm::Record> record_table;
std::unordered_map<std::string, panda::pandasm::Function> function_table;
std::unordered_map<std::string, std::vector<std::string>> function_synonyms;
std::map<std::string, panda::pandasm::LiteralArray> literalarray_table;
std::unordered_set<std::string> strings;
std::unordered_set<Type> array_types;
@ -45,4 +46,4 @@ struct Program {
} // namespace panda::pandasm
#endif // PANDA_ASSEMBLER_ASSEMBLY_PROGRAM_H_
#endif // !_PANDA_ASSEMBLER_PROGRAM_HPP

View File

@ -1,4 +1,4 @@
/*
/**
* Copyright (c) 2021-2022 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.
@ -13,8 +13,8 @@
* limitations under the License.
*/
#ifndef PANDA_ASSEMBLER_ASSEMBLY_RECORD_H_
#define PANDA_ASSEMBLER_ASSEMBLY_RECORD_H_
#ifndef _PANDA_ASSEMBLER_RECORD_HPP
#define _PANDA_ASSEMBLER_RECORD_HPP
#include <memory>
#include <optional>
@ -29,8 +29,8 @@ namespace panda::pandasm {
struct Record {
std::string name = "";
bool conflict = false; /* Name conflicts with panda primitive types. Need special handle. */
extensions::Language language;
bool conflict = false; /* Name is conflict with panda primitive types. Need special handle. */
panda::panda_file::SourceLang language;
std::unique_ptr<RecordMetadata> metadata;
std::vector<Field> field_list; /* class fields list */
size_t params_num = 0;
@ -39,7 +39,8 @@ struct Record {
std::string source_file; /* The file in which the record is defined or empty */
std::optional<FileLocation> file_location;
Record(std::string s, extensions::Language lang, size_t b_l, size_t b_r, std::string f_c, bool d, size_t l_n)
Record(std::string s, panda::panda_file::SourceLang lang, size_t b_l, size_t b_r, std::string f_c, bool d,
size_t l_n)
: name(std::move(s)),
language(lang),
metadata(extensions::MetadataExtension::CreateRecordMetadata(lang)),
@ -47,7 +48,7 @@ struct Record {
{
}
Record(std::string s, extensions::Language lang)
Record(std::string s, panda::panda_file::SourceLang lang)
: name(std::move(s)), language(lang), metadata(extensions::MetadataExtension::CreateRecordMetadata(lang))
{
}
@ -60,4 +61,4 @@ struct Record {
} // namespace panda::pandasm
#endif // PANDA_ASSEMBLER_ASSEMBLY_RECORD_H_
#endif // !_PANDA_ASSEMBLER_RECORD_HPP

View File

@ -1,4 +1,4 @@
/*
/**
* Copyright (c) 2021-2022 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.
@ -49,12 +49,14 @@ panda_file::Type::TypeId Type::GetId(std::string_view name, bool ignore_primitiv
if (!ignore_primitive) {
auto iter = panda_types.find(name);
if (iter == panda_types.end()) {
return panda_file::Type::TypeId::REFERENCE;
}
return iter->second;
} else {
return panda_file::Type::TypeId::REFERENCE;
}
return panda_file::Type::TypeId::REFERENCE;
}
/* static */
@ -91,6 +93,7 @@ Type Type::FromDescriptor(std::string_view descriptor)
if (is_ref_type) {
return Type(descriptor, rank);
}
return Type(reverse_primitive_types[descriptor], rank);
}
@ -107,9 +110,21 @@ Type Type::FromName(std::string_view name, bool ignore_primitive)
}
name.remove_suffix(i);
return Type(name, i / STEP, ignore_primitive);
}
/* static */
bool Type::IsStringType(const std::string &name, panda::panda_file::SourceLang lang)
{
auto string_type = Type::FromDescriptor(panda::panda_file::GetStringClassDescriptor(lang));
if (name == string_type.GetName()) {
return true;
} else {
return false;
}
}
/* static */
bool Type::IsPandaPrimitiveType(const std::string &name)
{

View File

@ -1,4 +1,4 @@
/*
/**
* Copyright (c) 2021-2022 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.
@ -13,8 +13,8 @@
* limitations under the License.
*/
#ifndef PANDA_ASSEMBLER_ASSEMBLY_TYPE_H_
#define PANDA_ASSEMBLER_ASSEMBLY_TYPE_H_
#ifndef _PANDA_ASSEMBLER_TYPE_H
#define _PANDA_ASSEMBLER_TYPE_H
#include "define.h"
#include "file_items.h"
@ -54,6 +54,13 @@ public:
return name_;
}
std::string GetPandasmName() const
{
std::string name_pa {name_};
std::replace(name_pa.begin(), name_pa.end(), '/', '.');
return name_pa;
}
std::string GetComponentName() const
{
return component_name_;
@ -159,6 +166,7 @@ public:
static Type FromName(std::string_view name, bool ignore_primitive = false);
static bool IsPandaPrimitiveType(const std::string &name);
static bool IsStringType(const std::string &name, panda::panda_file::SourceLang lang);
private:
static std::string GetName(std::string_view component_name, size_t rank);
@ -184,4 +192,4 @@ public:
} // namespace std
#endif // PANDA_ASSEMBLER_ASSEMBLY_TYPE_H_
#endif // !_PANDA_ASSEMBLER_TYPE_H

View File

@ -1,4 +1,4 @@
/*
/**
* Copyright (c) 2021-2022 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.
@ -22,9 +22,13 @@ namespace panda::pandasm {
void Context::Make(const std::vector<panda::pandasm::Token> &t)
{
err = {};
ins_number = 0;
tokens = t;
number = 1;
end = false;
token = std::string_view(&*(tokens[number - 1].whole_line.begin() + tokens[number - 1].bound_left),
@ -42,7 +46,9 @@ bool Context::ValidateRegisterName(char c, size_t n) const
{
if (token[0] == c) {
std::string_view p = token;
p.remove_prefix(1);
if (p.empty() || (p.size() > 1 && p[0] == '0')) {
return false;
}
@ -73,7 +79,9 @@ bool Context::ValidateParameterName(size_t number_of_params_already_is) const
if (token[0] == 'a') {
std::string_view p = token;
p.remove_prefix(1);
if (ToNumber(p) == number_of_params_already_is) {
return true;
}
@ -127,6 +135,7 @@ Token::Type Context::operator++(int)
if (this->tokens.size() > number) {
++number;
id = this->tokens[number - 1].type;
token = std::string_view(&*(tokens[number - 1].whole_line.begin() + tokens[number - 1].bound_left),
@ -142,6 +151,7 @@ Token::Type Context::operator++()
{
if (this->tokens.size() > number) {
++number;
id = this->tokens[number - 1].type;
token = std::string_view(&*(tokens[number - 1].whole_line.begin() + tokens[number - 1].bound_left),
@ -158,9 +168,11 @@ Token::Type Context::operator--(int)
{
Token::Type last_id = id;
if (number > 1) {
if (1 < number) {
end = false;
--number;
id = this->tokens[number - 1].type;
token = std::string_view(&*(tokens[number - 1].whole_line.begin() + tokens[number - 1].bound_left),
@ -174,9 +186,11 @@ Token::Type Context::operator--(int)
Token::Type Context::operator--()
{
if (number > 1) {
if (1 < number) {
end = false;
--number;
id = this->tokens[number - 1].type;
token = std::string_view(&*(tokens[number - 1].whole_line.begin() + tokens[number - 1].bound_left),

View File

@ -1,4 +1,4 @@
/*
/**
* Copyright (c) 2021-2022 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.
@ -13,10 +13,10 @@
* limitations under the License.
*/
#ifndef PANDA_ASSEMBLER_DEFINE_H_
#define PANDA_ASSEMBLER_DEFINE_H_
#ifndef _PANDA_ASSEMBLER_DEFINE_HPP
#define _PANDA_ASSEMBLER_DEFINE_HPP
/* Implementation-specific definitions */
/* Implementation-specific defines */
constexpr char PARSE_COMMENT_MARKER = '#';
@ -43,6 +43,7 @@ constexpr char PARSE_AREA_MARKER = '.';
_(".language", LANG) \
_(".function", FUN) \
_(".record", REC) \
_(".array", ARR) \
_(".field", FLD)
#endif // PANDA_ASSEMBLER_DEFINE_H_
#endif /* !_PANDA_ASSEMBLER_DEFINE_HPP */

View File

@ -1,4 +1,4 @@
/*
/**
* Copyright (c) 2021-2022 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.
@ -13,8 +13,8 @@
* limitations under the License.
*/
#ifndef PANDA_ASSEMBLER_ERROR_H_
#define PANDA_ASSEMBLER_ERROR_H_
#ifndef _PANDA_ASSEMBLER_ERROR_HPP
#define _PANDA_ASSEMBLER_ERROR_HPP
#include <string>
@ -43,9 +43,11 @@ struct Error {
ERR_BAD_NONEXISTING_OPERATION,
ERR_BAD_ID_FUNCTION,
ERR_BAD_ID_RECORD,
ERR_BAD_ID_ARRAY,
ERR_BAD_ID_FIELD,
ERR_BAD_FUNCTION_NAME,
ERR_BAD_RECORD_NAME,
ERR_BAD_ARRAY_NAME,
ERR_BAD_DEFINITION_METADATA,
ERR_BAD_DEFINITION_FUNCTION,
ERR_BAD_DEFINITION_RECORD,
@ -62,6 +64,15 @@ struct Error {
ERR_FUNCTION_ARGUMENT_MISMATCH,
ERR_BAD_FIELD_MISSING_NAME,
ERR_BAD_FIELD_VALUE_TYPE,
ERR_BAD_ARRAY_TYPE,
ERR_BAD_ARRAY_SIZE,
ERR_BAD_ARRAY_SIZE_VALUE,
ERR_BAD_ARRAY_ELEMENT_MISSING_VALUE,
ERR_BAD_ARRAY_ELEMENT_VALUE_TYPE,
ERR_BAD_ARRAY_ELEMENT_VALUE,
ERR_BAD_ARRAY_ELEMENT_VALUE_INTEGER,
ERR_BAD_ARRAY_ELEMENT_VALUE_FLOAT,
ERR_BAD_ARRAY_ELEMENT_VALUE_STRING,
ERR_BAD_CHARACTER,
ERR_BAD_KEYWORD,
ERR_BAD_DEFINITION,
@ -82,6 +93,9 @@ struct Error {
ERR_UNKNOWN_LANGUAGE,
ERR_BAD_MNEMONIC_NAME,
ERR_REPEATING_FIELD_NAME,
ERR_FUNCTION_MULTIPLE_ALTERNATIVES,
ERR_BAD_SIGNATURE,
ERR_BAD_SIGNATURE_PARAMETERS,
// Warnings
WAR_UNEXPECTED_RETURN_TYPE,
@ -90,7 +104,7 @@ struct Error {
ErrorClass type;
std::string whole_line;
size_t pos; // position to highlight the word
size_t pos; /* positions to highlight the word */
size_t end;
ErrorType err;
std::string message;
@ -116,4 +130,4 @@ struct Error {
using ErrorList = std::vector<Error>;
} // namespace panda::pandasm
#endif // PANDA_ASSEMBLER_ERROR_H_
#endif // !_PANDA_ASSEMBLER_ERROR_HPP

View File

@ -0,0 +1,29 @@
# Copyright (c) 2021-2022 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.
set(REGISTER_EXTENSIONS_H ${PANDA_BINARY_ROOT}/assembler/register_extensions.h)
panda_gen_file(
DATAFILE ${GEN_PLUGIN_OPTIONS_YAML}
TEMPLATE ${PANDA_ROOT}/assembler/extensions/register_extensions.h.erb
REQUIRES ${PANDA_ROOT}/templates/plugin_options.rb
EXTRA_DEPENDENCIES plugin_options_merge
OUTPUTFILE ${REGISTER_EXTENSIONS_H}
)
add_custom_target(assembler_extensions DEPENDS
plugin_options_gen
${REGISTER_EXTENSIONS_H}
)
add_dependencies(arkassembler assembler_extensions)

View File

@ -1,4 +1,4 @@
/*
/**
* Copyright (c) 2021-2022 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.
@ -14,129 +14,9 @@
*/
#include "extensions.h"
#include "ecmascript/ecmascript_meta.h"
#include "macros.h"
#include "register_extensions.h"
namespace panda::pandasm::extensions {
std::optional<Language> LanguageFromString(std::string_view lang)
{
if (lang == "ECMAScript") {
return Language::ECMASCRIPT;
}
if (lang == "PandaAssembly") {
return Language::PANDA_ASSEMBLY;
}
return {};
}
std::string LanguageToString(const Language &lang)
{
if (lang == Language::ECMASCRIPT) {
return "ECMAScript";
}
if (lang == Language::PANDA_ASSEMBLY) {
return "PandaAssembly";
}
return {};
}
std::string GetCtorName(Language lang)
{
switch (lang) {
case Language::ECMASCRIPT:
return ".ctor";
case Language::PANDA_ASSEMBLY:
return ".ctor";
default:
break;
}
UNREACHABLE();
return {};
}
std::string GetCctorName(Language lang)
{
switch (lang) {
case Language::ECMASCRIPT:
return ".cctor";
case Language::PANDA_ASSEMBLY:
return ".cctor";
default:
break;
}
UNREACHABLE();
return {};
}
/* static */
std::unique_ptr<RecordMetadata> MetadataExtension::CreateRecordMetadata(Language lang)
{
switch (lang) {
case Language::ECMASCRIPT:
return std::make_unique<ecmascript::RecordMetadata>();
case Language::PANDA_ASSEMBLY:
return std::make_unique<RecordMetadata>();
default:
break;
}
UNREACHABLE();
return {};
}
/* static */
std::unique_ptr<FieldMetadata> MetadataExtension::CreateFieldMetadata(Language lang)
{
switch (lang) {
case Language::ECMASCRIPT:
return std::make_unique<FieldMetadata>();
case Language::PANDA_ASSEMBLY:
return std::make_unique<FieldMetadata>();
default:
break;
}
UNREACHABLE();
return {};
}
/* static */
std::unique_ptr<FunctionMetadata> MetadataExtension::CreateFunctionMetadata(Language lang)
{
switch (lang) {
case Language::ECMASCRIPT:
return std::make_unique<FunctionMetadata>();
case Language::PANDA_ASSEMBLY:
return std::make_unique<FunctionMetadata>();
default:
break;
}
UNREACHABLE();
return {};
}
/* static */
std::unique_ptr<ParamMetadata> MetadataExtension::CreateParamMetadata(Language lang)
{
switch (lang) {
case Language::ECMASCRIPT:
return std::make_unique<ParamMetadata>();
case Language::PANDA_ASSEMBLY:
return std::make_unique<ParamMetadata>();
default:
break;
}
UNREACHABLE();
return {};
}
} // namespace panda::pandasm::extensions

View File

@ -1,4 +1,4 @@
/*
/**
* Copyright (c) 2021-2022 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.
@ -13,37 +13,31 @@
* limitations under the License.
*/
#ifndef PANDA_ASSEMBLER_EXTENSIONS_EXTENSIONS_H_
#define PANDA_ASSEMBLER_EXTENSIONS_EXTENSIONS_H_
#ifndef PANDA_ASSEMBLER_EXTENSIONS_H_
#define PANDA_ASSEMBLER_EXTENSIONS_H_
#include <memory>
#include <optional>
#include "meta.h"
#include "libpandafile/file_items.h"
namespace panda::pandasm::extensions {
enum class Language { ECMASCRIPT, PANDA_ASSEMBLY };
std::optional<Language> LanguageFromString(std::string_view lang);
std::string LanguageToString(const Language &lang);
std::string GetCtorName(Language lang);
std::string GetCctorName(Language lang);
// Workaround for ets_frontend. Should be removed by our colleagues.
using Language = panda::panda_file::SourceLang;
class MetadataExtension {
public:
static std::unique_ptr<RecordMetadata> CreateRecordMetadata(Language lang);
static std::unique_ptr<RecordMetadata> CreateRecordMetadata(panda::panda_file::SourceLang lang);
static std::unique_ptr<FieldMetadata> CreateFieldMetadata(Language lang);
static std::unique_ptr<FieldMetadata> CreateFieldMetadata(panda::panda_file::SourceLang lang);
static std::unique_ptr<FunctionMetadata> CreateFunctionMetadata(Language lang);
static std::unique_ptr<FunctionMetadata> CreateFunctionMetadata(panda::panda_file::SourceLang lang);
static std::unique_ptr<ParamMetadata> CreateParamMetadata(Language lang);
static std::unique_ptr<ParamMetadata> CreateParamMetadata(panda::panda_file::SourceLang lang);
};
} // namespace panda::pandasm::extensions
#endif // PANDA_ASSEMBLER_EXTENSIONS_EXTENSIONS_H_
#endif // PANDA_ASSEMBLER_EXTENSIONS_H_

View File

@ -0,0 +1,83 @@
/**
* Copyright (c) 2021-2022 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 PANDA_ASSEMBLER_EXTENSIONS_REGISTER_EXTENSIONS_H
#define PANDA_ASSEMBLER_EXTENSIONS_REGISTER_EXTENSIONS_H
#include "extensions/extensions.h"
#include "extension/ecmascript_meta.h"
namespace panda::pandasm::extensions {
std::unique_ptr<panda::pandasm::RecordMetadata> MetadataExtension::CreateRecordMetadata(panda::panda_file::SourceLang lang)
{
switch (lang) {
case Language::ECMASCRIPT:
return std::make_unique<panda::pandasm::extensions::ecmascript::RecordMetadata>();
case Language::PANDA_ASSEMBLY:
return std::make_unique<panda::pandasm::RecordMetadata>();
default:
break;
}
UNREACHABLE();
return {};
}
std::unique_ptr<panda::pandasm::FieldMetadata> MetadataExtension::CreateFieldMetadata(panda::panda_file::SourceLang lang)
{
switch (lang) {
case Language::ECMASCRIPT:
return std::make_unique<panda::pandasm::FieldMetadata>();
case Language::PANDA_ASSEMBLY:
return std::make_unique<panda::pandasm::FieldMetadata>();
default:
break;
}
UNREACHABLE();
return {};
}
std::unique_ptr<panda::pandasm::FunctionMetadata> MetadataExtension::CreateFunctionMetadata(panda::panda_file::SourceLang lang)
{
switch (lang) {
case Language::ECMASCRIPT:
return std::make_unique<panda::pandasm::FunctionMetadata>();
case Language::PANDA_ASSEMBLY:
return std::make_unique<panda::pandasm::FunctionMetadata>();
default:
break;
}
UNREACHABLE();
return {};
}
std::unique_ptr<panda::pandasm::ParamMetadata> MetadataExtension::CreateParamMetadata(panda::panda_file::SourceLang lang)
{
switch (lang) {
case Language::ECMASCRIPT:
return std::make_unique<panda::pandasm::ParamMetadata>();
case Language::PANDA_ASSEMBLY:
return std::make_unique<panda::pandasm::ParamMetadata>();
default:
break;
}
UNREACHABLE();
return {};
}
} // namespace panda::pandasm::extensions
#endif // PANDA_ASSEMBLER_EXTENSIONS_REGISTER_EXTENSIONS_H

View File

@ -1,4 +1,4 @@
/*
/**
* Copyright (c) 2021-2022 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.
@ -13,8 +13,8 @@
* limitations under the License.
*/
#ifndef PANDA_ASSEMBLER_IDE_HELPERS_H_
#define PANDA_ASSEMBLER_IDE_HELPERS_H_
#ifndef PANDA_ASSEMBLER_IDE_HELPERS_H
#define PANDA_ASSEMBLER_IDE_HELPERS_H
#include <sstream>
@ -83,4 +83,4 @@ std::string JsonSerializeProgramItems(const T &item_table)
}
} // namespace panda::pandasm
#endif // PANDA_ASSEMBLER_IDE_HELPERS_H_
#endif // PANDA_ASSEMBLER_IDE_HELPERS_H

View File

@ -1,4 +1,4 @@
/*
/**
* Copyright (c) 2021-2022 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.
@ -17,9 +17,12 @@
namespace panda::pandasm {
/*-------------------------------*/
/* Is this a delimiter ? */
Token::Type FindDelim(char c)
{
// The map of delimiters
/* The map of delimiters */
static const std::unordered_map<char, Token::Type> DELIM = {{',', Token::Type::DEL_COMMA},
{':', Token::Type::DEL_COLON},
{'{', Token::Type::DEL_BRACE_L},
@ -33,6 +36,7 @@ Token::Type FindDelim(char c)
{']', Token::Type::DEL_SQUARE_BRACKET_R}};
auto iter = DELIM.find(c);
if (iter == DELIM.end()) {
return Token::Type::ID_BAD;
}
@ -42,7 +46,7 @@ Token::Type FindDelim(char c)
Token::Type FindOperation(std::string_view s)
{
// Generate the map of OPERATIONS from ISA
/* Generate the map of OPERATIONS from ISA: */
static const std::unordered_map<std::string_view, Token::Type> OPERATIONS = {
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define OPLIST(inst_code, name, optype, width, flags, dst_idx, use_idxs) \
@ -52,6 +56,7 @@ Token::Type FindOperation(std::string_view s)
};
auto iter = OPERATIONS.find(s);
if (iter == OPERATIONS.end()) {
return Token::Type::ID_BAD;
}
@ -61,7 +66,7 @@ Token::Type FindOperation(std::string_view s)
Token::Type Findkeyword(std::string_view s)
{
// Generate the map of KEYWORDS
/* Generate the map of KEYWORDS: */
static const std::unordered_map<std::string_view, Token::Type> KEYWORDS = {
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define KEYWORDS(name, inst_code) {std::string_view(name), Token::Type::ID_##inst_code},
@ -70,6 +75,7 @@ Token::Type Findkeyword(std::string_view s)
};
auto iter = KEYWORDS.find(s);
if (iter == KEYWORDS.end()) {
return Token::Type::ID_BAD;
}
@ -77,7 +83,6 @@ Token::Type Findkeyword(std::string_view s)
return KEYWORDS.at(s);
}
// CODECHECK-NOLINTNEXTLINE(C_RULE_ID_FUNCTION_SIZE)
std::string_view TokenTypeWhat(Token::Type t)
{
if (t >= Token::Type::OPERATION && t < Token::Type::KEYWORD) {
@ -182,13 +187,13 @@ Tokens Lexer::TokenizeString(const std::string &source_str)
return std::pair<std::vector<Token>, Error>(lines_.back().tokens, err_);
}
// End of line
/* End of line? */
bool Lexer::Eol() const
{
return curr_line_->pos == curr_line_->end;
}
// Return the type of token
/* Return the type of token */
Token::Type Lexer::LexGetType(size_t beg, size_t end) const
{
if (FindDelim(curr_line_->buffer[beg]) != Token::Type::ID_BAD) { /* delimiter */
@ -196,12 +201,15 @@ Token::Type Lexer::LexGetType(size_t beg, size_t end) const
}
std::string_view p(&*(curr_line_->buffer.begin() + beg), end - beg);
Token::Type type = Findkeyword(p);
if (type != Token::Type::ID_BAD) {
return type;
}
type = FindOperation(p);
if (type != Token::Type::ID_BAD) {
return type;
}
@ -210,10 +218,10 @@ Token::Type Lexer::LexGetType(size_t beg, size_t end) const
return Token::Type::ID_STRING;
}
return Token::Type::ID; // other
return Token::Type::ID; /* other */
}
// Handle string literal
/* Handle string literal */
bool Lexer::LexString()
{
bool is_escape_seq = false;
@ -251,7 +259,7 @@ bool Lexer::LexString()
}
/*
* Tokens handling: set the corresponding
* Tokens handling: set a corresponding
* elements bound_left and bound_right of the array tokens
* to the first and last characters of a corresponding token.
*
@ -283,6 +291,7 @@ void Lexer::LexTokens()
}
size_t bound_right;
size_t bound_left;
for (int i = 0; !Eol(); ++i) {
@ -322,9 +331,10 @@ void Lexer::LexTokens()
/*
* Ignore comments:
* find PARSE_COMMENT_MARKER and move line->end to another position
* next after the last character of the last significant (not a comment)
* element in a current line: line->buffer.
* find PARSE_COMMENT_MARKER and move line->end
* to another position (next after the last character of the last
* significant (this is no a comment) element in a current
* line: line->buffer).
*
* Ex:
* [Label:] operation operand[,operand] [# comment]
@ -340,7 +350,7 @@ void Lexer::LexPreprocess()
<< std::string_view(&*(curr_line_->buffer.begin() + curr_line_->pos),
curr_line_->end - curr_line_->pos);
// Searching for comment marker located outside of the string literals.
// Searching for comment marker located outside of string literals.
bool inside_str_lit = curr_line_->buffer.size() > 0 && curr_line_->buffer[0] == '\"';
size_t cmt_pos = curr_line_->buffer.find_first_of("\"#", 0);
if (cmt_pos != std::string::npos) {
@ -382,4 +392,6 @@ void Lexer::AnalyzeLine()
LexTokens();
}
/*-------------------------------*/
} // namespace panda::pandasm

View File

@ -1,4 +1,4 @@
/*
/**
* Copyright (c) 2021-2022 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.
@ -13,8 +13,8 @@
* limitations under the License.
*/
#ifndef PANDA_ASSEMBLER_LEXER_H_
#define PANDA_ASSEMBLER_LEXER_H_
#ifndef _PANDA_ASSEMBLER_LEXER_HPP
#define _PANDA_ASSEMBLER_LEXER_HPP
#include <array>
#include <iostream>
@ -53,7 +53,7 @@ struct Token {
PANDA_INSTRUCTION_LIST(OPLIST)
#undef OPLIST
KEYWORD, /* special */
#define KEYWORDS(name, inst_code) ID_##inst_code, /* keyword type list */
#define KEYWORDS(name, inst_code) ID_##inst_code, /* keyword type List */
KEYWORDS_LIST(KEYWORDS)
#undef KEYWORDS
};
@ -77,7 +77,7 @@ using TokenSet = const std::vector<std::vector<Token>>;
struct Line {
std::vector<Token> tokens;
std::string buffer; /* raw line, as read from the file */
std::string buffer; /* Raw line, as read from the file */
size_t pos; /* current line position */
size_t end;
@ -102,7 +102,7 @@ private:
Line *curr_line_;
Error err_;
bool Eol() const; /* end of line */
bool Eol() const; /* End of line */
bool LexString();
void LexTokens();
void LexPreprocess();
@ -118,4 +118,4 @@ std::string_view TokenTypeWhat(Token::Type);
} // namespace panda::pandasm
#endif // PANDA_ASSEMBLER_LEXER_H_
#endif // !_PANDA_ASSEMBLER_LEXER_HPP

View File

@ -1,4 +1,4 @@
/*
/**
* Copyright (c) 2021-2022 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.
@ -13,8 +13,8 @@
* limitations under the License.
*/
#ifndef PANDA_ASSEMBLER_MANGLING_H_
#define PANDA_ASSEMBLER_MANGLING_H_
#ifndef _PANDA_ASSEMBLER_MANGLING_H
#define _PANDA_ASSEMBLER_MANGLING_H
#include "assembly-function.h"
@ -22,21 +22,13 @@
#include <vector>
namespace panda::pandasm {
static std::string MANGLE_BEGIN = ":";
static std::string MANGLE_SEPARATOR = ";";
constexpr const std::string_view MANGLE_BEGIN = ":";
constexpr const std::string_view MANGLE_SEPARATOR = ";";
inline std::string MangleFunctionName(const std::string &name, const std::vector<pandasm::Function::Parameter> &params,
const pandasm::Type &return_type)
{
std::string mangle_name {name};
mangle_name += MANGLE_BEGIN;
for (const auto &p : params) {
mangle_name += p.type.GetName() + MANGLE_SEPARATOR;
}
mangle_name += return_type.GetName() + MANGLE_SEPARATOR;
return mangle_name;
}
constexpr const std::string_view SIGNATURE_BEGIN = MANGLE_BEGIN;
constexpr const std::string_view SIGNATURE_TYPES_SEPARATOR = ",";
constexpr const std::string_view SIGNATURE_TYPES_SEPARATOR_L = "(";
constexpr const std::string_view SIGNATURE_TYPES_SEPARATOR_R = ")";
inline std::string DeMangleName(const std::string &name)
{
@ -47,14 +39,55 @@ inline std::string DeMangleName(const std::string &name)
return name;
}
inline std::string MangleFunctionName(const std::string &name, const std::vector<pandasm::Function::Parameter> &params,
const pandasm::Type &return_type)
{
std::string mangle_name {name};
mangle_name += MANGLE_BEGIN;
for (const auto &p : params) {
mangle_name += p.type.GetName() + std::string(MANGLE_SEPARATOR);
}
mangle_name += return_type.GetName() + std::string(MANGLE_SEPARATOR);
return mangle_name;
}
inline std::string MangleFieldName(const std::string &name, const pandasm::Type &type)
{
std::string mangle_name {name};
mangle_name += MANGLE_BEGIN;
mangle_name += type.GetName() + MANGLE_SEPARATOR;
mangle_name += type.GetName() + std::string(MANGLE_SEPARATOR);
return mangle_name;
}
inline std::string GetFunctionSignatureFromName(std::string name,
const std::vector<pandasm::Function::Parameter> &params)
{
name += SIGNATURE_BEGIN;
name += SIGNATURE_TYPES_SEPARATOR_L;
bool first = true;
for (const auto &p : params) {
name += (first) ? "" : SIGNATURE_TYPES_SEPARATOR;
first = false;
name += p.type.GetPandasmName();
}
name += SIGNATURE_TYPES_SEPARATOR_R;
return name;
}
inline std::string GetFunctionNameFromSignature(const std::string &sig)
{
return DeMangleName(sig);
}
inline bool IsSignatureOrMangled(const std::string &str)
{
return str.find(SIGNATURE_BEGIN) != std::string::npos;
}
} // namespace panda::pandasm
#endif // PANDA_ASSEMBLER_MANGLING_H_
#endif // !_PANDA_ASSEMBLER_MANGLING_HPP

View File

@ -1,4 +1,4 @@
/*
/**
* Copyright (c) 2021-2022 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.
@ -138,6 +138,7 @@ static Expected<ScalarValue, Metadata::Error> CreatePrimitiveValue(std::string_v
}
auto converted = res.Value();
if (converted > max_value) {
return Unexpected(Metadata::Error("Value is out of range", Metadata::Error::Type::INVALID_VALUE));
}
@ -145,7 +146,6 @@ static Expected<ScalarValue, Metadata::Error> CreatePrimitiveValue(std::string_v
return ScalarValue::Create<type>(converted);
}
// CODECHECK-NOLINTNEXTLINE(C_RULE_ID_FUNCTION_SIZE)
static Expected<ScalarValue, Metadata::Error> CreateValue(
Value::Type type, std::string_view value,
const std::unordered_map<std::string, std::unique_ptr<AnnotationData>> &annotation_id_map = {})
@ -219,6 +219,7 @@ std::optional<Metadata::Error> AnnotationMetadata::AnnotationElementBuilder::Add
ASSERT(type_.has_value());
auto type = type_.value();
if (type == Value::Type::ARRAY) {
ASSERT(component_type_.has_value());
type = component_type_.value();

View File

@ -1,4 +1,4 @@
/*
/**
* Copyright (c) 2021-2022 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.
@ -118,6 +118,7 @@ public:
std::optional<std::string> GetAttributeValue(const std::string &attribute) const
{
auto values = GetAttributeValues(attribute);
if (!values.empty()) {
return values.front();
}

View File

@ -38,6 +38,13 @@ attributes:
- field
- function
- name: volatile
type: bool
flags: [ACC_VOLATILE]
multiple: false
applicable_to:
- field
- name: ctor
type: bool
applicable_to:

View File

@ -1,4 +1,4 @@
/*
/**
* Copyright (c) 2021-2022 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.
@ -21,13 +21,20 @@
#include <string>
#include <vector>
#include "ark_version.h"
#include "assembly-emitter.h"
#include "assembly-parser.h"
#ifdef PANDA_WITH_BYTECODE_OPTIMIZER
#include "bytecode_optimizer/optimize_bytecode.h"
#endif
#include "file_format_version.h"
#include "error.h"
#include "lexer.h"
#include "utils/expected.h"
#include "utils/logger.h"
#include "utils/pandargs.h"
#include "pandasm.h"
namespace panda::pandasm {
@ -47,17 +54,32 @@ void PrintErrors(const panda::pandasm::ErrorList &warnings, const std::string &m
}
}
void PrintHelp(const panda::PandArgParser &pa_parser)
{
std::cerr << "Usage:" << std::endl;
std::cerr << "pandasm [OPTIONS] INPUT_FILE OUTPUT_FILE" << std::endl << std::endl;
std::cerr << "Supported options:" << std::endl << std::endl;
std::cerr << pa_parser.GetHelpString() << std::endl;
}
bool PrepareArgs(panda::PandArgParser &pa_parser, const panda::PandArg<std::string> &input_file,
const panda::PandArg<std::string> &output_file, const panda::PandArg<std::string> &log_file,
const panda::PandArg<bool> &help, const panda::PandArg<bool> &verbose, std::ifstream &inputfile,
int argc, char **argv)
const panda::PandArg<bool> &help, const panda::PandArg<bool> &verbose,
const panda::PandArg<bool> &version, std::ifstream &inputfile, int argc, const char **argv)
{
if (!pa_parser.Parse(argc, const_cast<const char **>(argv)) || input_file.GetValue().empty() ||
output_file.GetValue().empty() || help.GetValue()) {
std::cerr << "Usage:" << std::endl;
std::cerr << "ark_asm [OPTIONS] INPUT_FILE OUTPUT_FILE" << std::endl << std::endl;
std::cerr << "Supported options:" << std::endl << std::endl;
std::cerr << pa_parser.GetHelpString() << std::endl;
if (!pa_parser.Parse(argc, argv)) {
PrintHelp(pa_parser);
return false;
}
if (version.GetValue()) {
panda::PrintPandaVersion();
panda_file::PrintBytecodeVersion();
return false;
}
if (input_file.GetValue().empty() || output_file.GetValue().empty() || help.GetValue()) {
PrintHelp(pa_parser);
return false;
}
@ -65,10 +87,12 @@ bool PrepareArgs(panda::PandArgParser &pa_parser, const panda::PandArg<std::stri
if (log_file.GetValue().empty()) {
panda::Logger::ComponentMask component_mask;
component_mask.set(panda::Logger::Component::ASSEMBLER);
component_mask.set(panda::Logger::Component::BYTECODE_OPTIMIZER);
panda::Logger::InitializeStdLogging(panda::Logger::Level::DEBUG, component_mask);
} else {
panda::Logger::ComponentMask component_mask;
component_mask.set(panda::Logger::Component::ASSEMBLER);
component_mask.set(panda::Logger::Component::BYTECODE_OPTIMIZER);
panda::Logger::InitializeFileLogging(log_file.GetValue(), panda::Logger::Level::DEBUG, component_mask);
}
}
@ -92,6 +116,7 @@ bool Tokenize(panda::pandasm::Lexer &lexer, std::vector<std::vector<panda::panda
panda::pandasm::Tokens q = lexer.TokenizeString(s);
auto e = q.second;
if (e.err != panda::pandasm::Error::ErrorType::ERR_NONE) {
e.line_number = tokens.size() + 1;
PrintError(e, "ERROR");
@ -109,6 +134,7 @@ bool ParseProgram(panda::pandasm::Parser &parser, std::vector<std::vector<panda:
panda::Expected<panda::pandasm::Program, panda::pandasm::Error> &res)
{
res = parser.Parse(tokens, input_file.GetValue());
if (!res) {
PrintError(res.Error(), "ERROR");
return false;
@ -124,7 +150,7 @@ bool DumpProgramInJson(panda::pandasm::Program &program, const panda::PandArg<st
dump_file.open(scopes_file.GetValue());
if (!dump_file) {
std::cerr << "Failed to write scopes into the given file." << std::endl;
std::cerr << "Cannot write scopes into the given file." << std::endl;
return false;
}
dump_file << program.JsonDump();
@ -135,7 +161,7 @@ bool DumpProgramInJson(panda::pandasm::Program &program, const panda::PandArg<st
bool EmitProgramInBinary(panda::pandasm::Program &program, panda::PandArgParser &pa_parser,
const panda::PandArg<std::string> &output_file, panda::PandArg<bool> &optimize,
const panda::PandArg<bool> &size_stat)
panda::PandArg<bool> &size_stat)
{
auto emit_debug_info = !optimize.GetValue();
std::map<std::string, size_t> stat;
@ -148,6 +174,20 @@ bool EmitProgramInBinary(panda::pandasm::Program &program, panda::PandArgParser
return false;
}
#ifdef PANDA_WITH_BYTECODE_OPTIMIZER
if (optimize.GetValue()) {
bool is_optimized = panda::bytecodeopt::OptimizeBytecode(&program, mapsp, output_file.GetValue());
if (!panda::pandasm::AsmEmitter::Emit(output_file.GetValue(), program, statp, mapsp, emit_debug_info)) {
std::cerr << "Failed to emit binary data: " << panda::pandasm::AsmEmitter::GetLastError() << std::endl;
return false;
}
if (!is_optimized) {
std::cerr << "Bytecode optimizer reported internal errors" << std::endl;
return false;
}
}
#endif
if (size_stat.GetValue()) {
size_t total_size = 0;
std::cout << "Panda file size statistic:" << std::endl;
@ -182,7 +222,7 @@ bool BuildFiles(panda::pandasm::Program &program, panda::PandArgParser &pa_parse
} // namespace panda::pandasm
int main(int argc, char *argv[])
int main(int argc, const char *argv[])
{
panda::PandArg<bool> verbose("verbose", false, "Enable verbose output (will be printed to standard output)");
panda::PandArg<std::string> log_file("log-file", "", "(--log-file FILENAME) Set log file name");
@ -191,6 +231,8 @@ int main(int argc, char *argv[])
panda::PandArg<bool> help("help", false, "Print this message and exit");
panda::PandArg<bool> size_stat("size-stat", false, "Print panda file size statistic");
panda::PandArg<bool> optimize("optimize", false, "Run the bytecode optimization");
panda::PandArg<bool> version {"version", false,
"Ark version, file format version and minimum supported file format version"};
// tail arguments
panda::PandArg<std::string> input_file("INPUT_FILE", "", "Path to the source assembly code");
panda::PandArg<std::string> output_file("OUTPUT_FILE", "", "Path to the generated binary code");
@ -201,14 +243,15 @@ int main(int argc, char *argv[])
pa_parser.Add(&scopes_file);
pa_parser.Add(&size_stat);
pa_parser.Add(&optimize);
pa_parser.Add(&version);
pa_parser.PushBackTail(&input_file);
pa_parser.PushBackTail(&output_file);
pa_parser.EnableTail();
std::ifstream inputfile;
if (!panda::pandasm::PrepareArgs(pa_parser, input_file, output_file, log_file, help, verbose, inputfile, argc,
argv)) {
if (!panda::pandasm::PrepareArgs(pa_parser, input_file, output_file, log_file, help, verbose, version, inputfile,
argc, argv)) {
return 1;
}

View File

@ -1,4 +1,4 @@
/*
/**
* Copyright (c) 2021-2022 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.
@ -13,8 +13,8 @@
* limitations under the License.
*/
#ifndef PANDA_ASSEMBLER_PANDASM_H_
#define PANDA_ASSEMBLER_PANDASM_H_
#ifndef PANDA_PANDASM_H
#define PANDA_PANDASM_H
#include "utils/pandargs.h"
@ -48,4 +48,4 @@ bool BuildFiles(panda::pandasm::Program &program, panda::PandArgParser &pa_parse
} // namespace panda::pandasm
#endif // PANDA_ASSEMBLER_PANDASM_H_
#endif // PANDA_PANDASM_H

View File

@ -21,7 +21,7 @@
namespace panda::pandasm {
% Panda::instructions.group_by(&:mnemonic).each do |mnemonic, group|
% insn = group.first
% insn = group.first
% signature = assembler_signature(group, insn.jump?)
% signature_str = signature.map { |o| "#{o.type} #{o.name}" }.join(', ')
inline Ins Create_<%= insn.asm_token %>(<%= signature_str %>)

View File

@ -29,25 +29,8 @@ bool Ins::Emit(BytecodeEmitter& emitter, panda_file::MethodItem *method,
LOG(FATAL, ASSEMBLER);
}
switch(opcode) {
% inst_map = Panda::instructions.collect { |i| [i.mnemonic, i] }.to_h
% call_map = {
% "call.short" => ["call.short"],
% "call" => ["call.short", "call"],
% "calli.dyn.short" => ["calli.dyn.short"],
% "calli.dyn" => ["calli.dyn.short", "calli.dyn"],
% "call.acc.short" => ["call.acc.short"],
% "call.acc" => ["call.acc.short", "call.acc"],
% "call.virt.short" => ["call.virt.short"],
% "call.virt" => ["call.virt.short", "call.virt"],
% "call.virt.acc.short" => ["call.virt.acc.short"],
% "call.virt.acc" => ["call.virt.acc.short", "call.virt.acc"],
% "call.dyn.short" => ["call.dyn.short"],
% "call.dyn" => ["call.dyn.short", "call.dyn"],
% "initobj.short" => ["initobj.short"],
% "initobj" => ["initobj"],
% "builtin.idr4" => ["builtin.idr4"],
% "builtin.idr6" => ["builtin.idr6"]
%}
% instruction_hash = Panda::instructions.map { |i| [i.mnemonic, i] }.to_h
%
% Panda::instructions.group_by(&:mnemonic).each_pair do |mn, group|
% insn = group[index_of_max(group.map(&:format).map(&:size))]
%
@ -165,7 +148,12 @@ bool Ins::Emit(BytecodeEmitter& emitter, panda_file::MethodItem *method,
% end
% if insn.simple_call?
auto registers = regs;
% call_map[insn.mnemonic].map { |m| inst_map[m] }.each do |i|
% call_mnemonics = if insn.mnemonic.end_with?('.short')
% [insn.mnemonic]
% else
% ["#{insn.mnemonic}.short", insn.mnemonic]
% end
% call_mnemonics.map { |m| instruction_hash[m] }.each do |i|
% registers = i.operands.select(&:reg?)
if (registers.size() < <%= registers.size + 1 %>) {
while (registers.size() < <%= registers.size %>) {
@ -188,4 +176,4 @@ bool Ins::Emit(BytecodeEmitter& emitter, panda_file::MethodItem *method,
return true;
}
} // namespace panda::pandasm
}

View File

@ -26,28 +26,39 @@ std::string panda::pandasm::Ins::ToString(std::string endline, bool print_args /
full_operation += this->label +": ";
}
switch(this->opcode) {
% Panda::instructions.group_by(&:mnemonic).each do |mnemonic, group|
% insn = group.first
% formats = group.map(&:format)
% sig_split = insn["sig"].split(' ')
% operands = insn.operands
% properties = insn.properties
case panda::pandasm::Opcode::<%= insn.asm_token%>: {
full_operation += "<%= insn.mnemonic%>";
% print_kind = case
% when insn.call? && !insn.properties.include?('method_id')
% 'PrintKind::CALLI'
% when insn.call? || !insn.public?
% 'PrintKind::CALL'
% else
% 'PrintKind::DEFAULT'
% end
full_operation += this->OperandsToString(<%= print_kind %>, print_args, first_arg_idx);
% idx_imm = 0
% idx_reg = 0
% idx_ids = 0
% sig_split.length.times do |index|
% item = sig_split[index]
% next if index == 0
% #TODO(knazarov): refactor next line
% if (item.include?("id") || item.start_with?("imm") && insn.jump?)
full_operation += IdToString(<%= idx_ids%>, <%= index == 1 ? "true" : "false"%>);
% idx_ids += 1
% elsif item.start_with?("imm")
full_operation += ImmToString(<%= idx_imm%>, <%= index == 1 ? "true" : "false"%>);
% idx_imm += 1
% elsif item.start_with?("v")
full_operation += RegToString(<%= idx_reg%>, <%= index == 1 ? "true" : "false"%>, print_args, first_arg_idx);
% idx_reg += 1
% end
% end
} break;
% end
% Panda::pseudo_instructions.each do |insn|
case panda::pandasm::Opcode::<%= insn.opcode %>: {
full_operation += "<%= insn.opcode %>";
full_operation += this->OperandsToString(PrintKind::DEFAULT, print_args, first_arg_idx);
full_operation += this->OperandsToString(print_args, first_arg_idx);
} break;
% end
case panda::pandasm::Opcode::INVALID: {

View File

@ -26,14 +26,15 @@
% flags << "InstFlags::JUMP" if insn.jump?
% flags << "InstFlags::COND" if insn.conditional?
% flags << "InstFlags::RETURN" if insn.return?
% flags << "InstFlags::ACC_READ" if insn.properties.include? 'acc_read'
% flags << "InstFlags::ACC_WRITE" if insn.properties.include? 'acc_write'
% flags << "InstFlags::ACC_READ" if insn.acc_read?
% flags << "InstFlags::ACC_WRITE" if insn.acc_write?
% flags << "InstFlags::THROWING" if insn.throwing?
% flags << "InstFlags::METHOD_ID" if insn.properties.include? 'method_id'
% flags << "InstFlags::FIELD_ID" if insn.properties.include? 'field_id'
% flags << "InstFlags::TYPE_ID" if insn.properties.include? 'type_id'
% flags << "InstFlags::STRING_ID" if insn.properties.include? 'string_id'
% flags << "InstFlags::LITERALARRAY_ID" if insn.properties.include? 'literalarray_id'
% flags << "InstFlags::CALL_RANGE" if (insn.properties.include?('call') || insn.stripped_mnemonic == 'initobj') && insn.range?
% flags = "(#{flags.join(" | ")})"
%
% max_width = group.map do |i|

View File

@ -13,6 +13,7 @@
* limitations under the License.
*/
% Metadata::item_types.each do |item_type|
std::optional<Metadata::Error>
<%= MetadataGen::class_name(item_type) %>::Validate(<%= MetadataGen::validate_arg_list(item_type, true).join(', ') %>) const {

View File

@ -17,7 +17,6 @@
#include "utils/number-utils.h"
namespace panda::pandasm {
bool Parser::ParseOperands()
{
std::string_view p;
@ -42,7 +41,7 @@ bool Parser::ParseOperands()
% elsif insn.return_void?
if (!curr_func_->return_type.IsVoid()) {
% end
GetWarning("Unexpected return type. Correct the return type in the function definition: " +
GetWarning("Unexpected return type. Look at the return type in the function definition: " +
curr_func_->return_type.GetName(),
Error::ErrorType::WAR_UNEXPECTED_RETURN_TYPE);
}
@ -103,6 +102,8 @@ bool Parser::ParseOperands()
% end
% elsif properties.include?("string_id")
ParseOperandString();
% elsif properties.include?("literalarray_id")
ParseOperandLiteralArray();
% elsif properties.include?("method_id")
ParseOperandCall();
% elsif properties.include?("field_id")
@ -129,5 +130,4 @@ bool Parser::ParseOperands()
}
return context_.Mask();
}
} // namespace panda::pandasm
}

View File

@ -16,7 +16,6 @@
#include "assembly-parser.h"
namespace panda::pandasm {
inline std::string OperandTypePrint(panda::pandasm::Opcode op) {
switch (op) {
% Panda::instructions.group_by(&:mnemonic).each do |mnemonic, group|
@ -49,5 +48,4 @@ inline std::string OperandTypePrint(panda::pandasm::Opcode op) {
return "undefined";
}
}
} // namespace panda::pandasm
}

View File

@ -1,4 +1,4 @@
/*
/**
* Copyright (c) 2021-2022 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.
@ -34,6 +34,7 @@
#include "utils/span.h"
#include "utils/leb128.h"
#include "utils/utf.h"
#include "bytecode_instruction-inl.h"
namespace panda::test {
@ -69,6 +70,7 @@ TEST(emittertests, test)
ASSERT_NE(pf, nullptr);
// Check _GLOBAL class
{
std::string descriptor;
auto class_id = pf->GetClassId(GetTypeDescriptor("_GLOBAL", &descriptor));
@ -123,6 +125,7 @@ TEST(emittertests, test)
}
// Check R class
{
std::string descriptor;
auto class_id = pf->GetClassId(GetTypeDescriptor("R", &descriptor));
@ -419,112 +422,6 @@ TEST(emittertests, errors)
ASSERT_EQ(pf, nullptr);
ASSERT_EQ(AsmEmitter::GetLastError(), "Non-external function A.x is bound to external record");
}
{
Ins i;
Function f("test_fuzz_imms", pandasm::extensions::Language::ECMASCRIPT);
Program prog;
i.opcode = Opcode::LDAI_64;
i.imms.clear();
f.ins.push_back(i);
prog.function_table.emplace("test_fuzz_imms", std::move(f));
auto pf = AsmEmitter::Emit(prog);
ASSERT_EQ(pf, nullptr);
ASSERT_EQ(AsmEmitter::GetLastError(), "Internal error during emitting function: test_fuzz_imms");
}
{
Ins i;
Function f("test_fuzz_regs", pandasm::extensions::Language::ECMASCRIPT);
Program prog;
i.opcode = Opcode::LDA;
i.regs.clear();
f.ins.push_back(i);
prog.function_table.emplace("test_fuzz_regs", std::move(f));
auto pf = AsmEmitter::Emit(prog);
ASSERT_EQ(pf, nullptr);
ASSERT_EQ(AsmEmitter::GetLastError(), "Internal error during emitting function: test_fuzz_regs");
}
{
Ins i;
Function f("test_fuzz_ids", pandasm::extensions::Language::ECMASCRIPT);
Program prog;
i.opcode = Opcode::LDA_STR;
i.ids.push_back("testFuzz");
f.ins.push_back(i);
prog.function_table.emplace("test_fuzz_ids", std::move(f));
prog.strings.insert("testFuz_");
auto pf = AsmEmitter::Emit(prog);
ASSERT_EQ(pf, nullptr);
ASSERT_EQ(AsmEmitter::GetLastError(), "Internal error during emitting function: test_fuzz_ids");
}
}
enum class ItemType { RECORD, FIELD, FUNCTION, PARAMETER };
std::string ItemTypeToString(ItemType item_type)
{
switch (item_type) {
case ItemType::RECORD:
return "record";
case ItemType::FIELD:
return "field";
case ItemType::FUNCTION:
return "function";
case ItemType::PARAMETER:
return "parameter";
default:
break;
}
UNREACHABLE();
return "";
}
template <Value::Type type>
auto GetAnnotationElementValue(size_t idx)
{
using T = ValueTypeHelperT<type>;
if constexpr (std::is_arithmetic_v<T>) {
if constexpr (type == Value::Type::U1) {
return static_cast<uint32_t>(idx == 0 ? 0 : 1);
}
auto res = idx == 0 ? std::numeric_limits<T>::min() : std::numeric_limits<T>::max();
if constexpr (type == Value::Type::I8) {
return static_cast<int32_t>(res);
}
if constexpr (type == Value::Type::U8) {
return static_cast<uint32_t>(res);
}
if constexpr (std::is_floating_point_v<T>) {
return res * (idx == 0 ? 10 : 0.1);
}
return res;
} else {
switch (type) {
case Value::Type::STRING:
return idx == 0 ? "123" : "456";
case Value::Type::RECORD:
return idx == 0 ? "A" : "B";
case Value::Type::ENUM:
return idx == 0 ? "E.E1" : "E.E2";
case Value::Type::ANNOTATION:
return idx == 0 ? "id_A" : "id_B";
default:
break;
}
UNREACHABLE();
return "";
}
}
TEST(emittertests, language)
@ -840,14 +737,16 @@ TEST(emittertests, tagged_in_field_decl)
ASSERT_EQ(1, num_fields);
}
TEST(emittertests, get_GLOBAL_lang_for_JS_func)
TEST(emittertests, function_overloading_1)
{
Parser p;
auto source = R"(
.language ECMAScript
.function void foo(i8 a0) {}
.function u1 foo(u1 a0) {}
.function any main() {
return.dyn
.function i8 f(i32 a0) {
call foo:(i8), v0
call foo:(u1), v1
}
)";
@ -857,15 +756,55 @@ TEST(emittertests, get_GLOBAL_lang_for_JS_func)
auto pf = AsmEmitter::Emit(res.Value());
ASSERT_NE(pf, nullptr);
std::string descriptor;
std::string descriptor {};
auto class_id = pf->GetClassId(GetTypeDescriptor("_GLOBAL", &descriptor));
ASSERT_TRUE(class_id.IsValid());
panda_file::ClassDataAccessor cda(*pf, class_id);
ASSERT_TRUE(cda.GetSourceLang().has_value());
ASSERT_EQ(cda.GetSourceLang(), panda_file::SourceLang::ECMASCRIPT);
size_t num_methods = 0;
std::unordered_map<panda_file::File::EntityId, panda_file::Type> id_to_arg_type {};
panda_file::File::EntityId id_f {};
cda.EnumerateMethods([&](panda_file::MethodDataAccessor &mda) {
++num_methods;
panda_file::ProtoDataAccessor pda(*pf, mda.GetProtoId());
if (pda.GetArgType(0) == panda_file::Type(panda_file::Type::TypeId::I32)) {
id_f = mda.GetMethodId();
return;
}
id_to_arg_type.emplace(mda.GetMethodId(), pda.GetArgType(0));
});
panda_file::MethodDataAccessor mda_f(*pf, id_f);
panda_file::CodeDataAccessor cda_f(*pf, mda_f.GetCodeId().value());
const auto ins_sz = cda_f.GetCodeSize();
const auto ins_arr = cda_f.GetInstructions();
auto bc_ins = BytecodeInstruction(ins_arr);
const auto bc_ins_last = bc_ins.JumpTo(ins_sz);
while (bc_ins.GetAddress() != bc_ins_last.GetAddress()) {
const auto arg_method_idx = bc_ins.GetId().AsIndex();
const auto arg_method_id = pf->ResolveMethodIndex(id_f, arg_method_idx);
panda_file::MethodDataAccessor method_accessor(*pf, arg_method_id);
panda_file::ProtoDataAccessor proto_accessor(*pf, method_accessor.GetProtoId());
ASSERT_EQ(proto_accessor.GetArgType(0), id_to_arg_type.at(arg_method_id));
ASSERT_EQ(std::string(utf::Mutf8AsCString(pf->GetStringData(method_accessor.GetNameId()).data)), "foo");
bc_ins = bc_ins.GetNext();
}
ASSERT_EQ(3, num_methods);
}
} // namespace panda::test

View File

@ -1,4 +1,4 @@
/*
/**
* Copyright (c) 2021-2022 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.
@ -20,8 +20,6 @@
#include "../define.h"
#include "../lexer.h"
namespace panda::test {
using namespace panda::pandasm;
TEST(lexertests, test1)
@ -144,6 +142,7 @@ TEST(lexertests, test12)
Lexer l;
std::string s = ".function asd(u32){}";
Tokens tok = l.TokenizeString(s);
ASSERT_EQ(TokenTypeWhat(tok.first[0].type), "KEYWORD") << "KEYWORD expected";
ASSERT_EQ(TokenTypeWhat(tok.first[1].type), "ID") << "ID expected";
ASSERT_EQ(tok.second.err, Error::ErrorType::ERR_NONE) << "ERR_NONE expected";
@ -213,5 +212,3 @@ TEST(lexertests, array_type)
ASSERT_EQ(tok.first[1].type, Token::Type::DEL_SQUARE_BRACKET_L);
ASSERT_EQ(tok.first[2].type, Token::Type::DEL_SQUARE_BRACKET_R);
}
} // namespace panda::test

View File

@ -1,4 +1,4 @@
/*
/**
* Copyright (c) 2021-2022 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.
@ -18,14 +18,12 @@
#include "assembly-function.h"
#include "extensions/extensions.h"
namespace panda::test {
using namespace panda::pandasm;
TEST(mangling_tests, MangleFunctionName)
{
std::vector<Function::Parameter> params;
extensions::Language language {extensions::Language::PANDA_ASSEMBLY};
panda::panda_file::SourceLang language {panda::panda_file::SourceLang::PANDA_ASSEMBLY};
params.emplace_back(Type {"type1", 0}, language);
params.emplace_back(Type {"type2", 0}, language);
params.emplace_back(Type {"type3", 0}, language);
@ -42,4 +40,20 @@ TEST(mangling_tests, DeMangleFunctionName)
ASSERT_EQ(DeMangleName(name), "Asm.main");
}
} // namespace panda::test
TEST(mangling_tests, GetFunctionSignatureFromName)
{
std::vector<Function::Parameter> params;
panda::panda_file::SourceLang language {panda::panda_file::SourceLang::PANDA_ASSEMBLY};
params.emplace_back(Type {"type1", 0}, language);
params.emplace_back(Type {"type2", 0}, language);
params.emplace_back(Type {"type3", 0}, language);
std::string name = "Asm.main";
ASSERT_EQ(GetFunctionSignatureFromName(name, std::move(params)), "Asm.main:(type1,type2,type3)");
}
TEST(mangling_tests, GetFunctionNameFromSignature)
{
std::string name = "Asm.main:(type1,type2,type3,type4)";
ASSERT_EQ(GetFunctionNameFromSignature(name), "Asm.main");
}

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
/*
/**
* Copyright (c) 2021-2022 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.
@ -13,8 +13,8 @@
* limitations under the License.
*/
#ifndef PANDA_ASSEMBLER_UTILS_NUMBER_UTILS_H_
#define PANDA_ASSEMBLER_UTILS_NUMBER_UTILS_H_
#ifndef _PANDA_ASSEMBLER_NUMBERS_UTILS_H
#define _PANDA_ASSEMBLER_NUMBERS_UTILS_H
namespace panda::pandasm {
@ -28,52 +28,10 @@ constexpr size_t BIN_BASE = 2;
constexpr size_t MAX_DWORD = 65536;
inline bool ValidateHexInteger(std::string_view p)
{
std::string_view token = p;
token.remove_prefix(2U);
for (auto i : token) {
if (!((i >= '0' && i <= '9') || (i >= 'A' && i <= 'F') || (i >= 'a' && i <= 'f'))) {
return false;
}
}
return true;
}
inline bool ValidateBinInteger(std::string_view p)
{
std::string_view token = p;
token.remove_prefix(2U);
if (token.empty()) {
return false;
}
for (auto i : token) {
if (!(i == '0' || i == '1')) {
return false;
}
}
return true;
}
inline bool ValidateOctalInteger(std::string_view p)
{
std::string_view token = p;
token.remove_prefix(1);
for (auto i : token) {
if (!(i >= '0' && i <= '7')) {
return false;
}
}
return true;
}
inline bool ValidateInteger(std::string_view p)
{
constexpr size_t GENERAL_SHIFT = 2;
std::string_view token = p;
if (token.back() == '-' || token.back() == '+' || token.back() == 'x' || token == ".") {
@ -86,15 +44,41 @@ inline bool ValidateInteger(std::string_view p)
if (token[0] == '0' && token.size() > 1 && token.find('.') == std::string::npos) {
if (token[1] == 'x') {
return ValidateHexInteger(token);
token.remove_prefix(GENERAL_SHIFT);
for (auto i : token) {
if (!((i >= '0' && i <= '9') || (i >= 'A' && i <= 'F') || (i >= 'a' && i <= 'f'))) {
return false;
}
}
return true;
}
if (token[1] == 'b') {
return ValidateBinInteger(token);
token.remove_prefix(GENERAL_SHIFT);
if (token.empty()) {
return false;
}
for (auto i : token) {
if (!(i == '0' || i == '1')) {
return false;
}
}
return true;
}
if (token[1] >= '0' && token[1] <= '9' && token.find('e') == std::string::npos) {
return ValidateOctalInteger(token);
token.remove_prefix(1);
for (auto i : token) {
if (!(i >= '0' && i <= '7')) {
return false;
}
}
return true;
}
}
@ -184,8 +168,9 @@ inline double FloatNumber(std::string_view p, bool is_64bit)
char *end = nullptr;
if (is_64bit) {
return bit_cast<double>(strtoull(p.data(), &end, 0));
} else {
return bit_cast<float>(static_cast<uint32_t>(strtoull(p.data(), &end, 0)));
}
return bit_cast<float>(static_cast<uint32_t>(strtoull(p.data(), &end, 0)));
}
return std::strtold(std::string(p.data(), p.length()).c_str(), nullptr);
}
@ -207,4 +192,4 @@ inline size_t ToNumber(std::string_view p)
} // namespace panda::pandasm
#endif // PANDA_ASSEMBLER_UTILS_NUMBER_UTILS_H_
#endif // !_PANDA_ASSEMBLER_NUMBERS_UTILS_H

140
bytecode_optimizer/BUILD.gn Normal file
View File

@ -0,0 +1,140 @@
# Copyright (c) 2021-2022 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.
import("//arkcompiler/runtime_core/ark_config.gni")
import("//build/ohos.gni")
config("bytecodeopt_public_config") {
include_dirs = [
"$target_gen_dir",
"$ark_root/bytecode_optimizer",
]
if (enable_bytecode_optimizer && !ark_standalone_build) {
defines = [ "ENABLE_BYTECODE_OPT" ]
}
}
libarkbytecodeopt_sources = [
"$ark_root/bytecode_optimizer/bytecodeopt_peepholes.cpp",
"$ark_root/bytecode_optimizer/canonicalization.cpp",
"$ark_root/bytecode_optimizer/check_resolver.cpp",
"$ark_root/bytecode_optimizer/codegen.cpp",
"$ark_root/bytecode_optimizer/common.cpp",
"$ark_root/bytecode_optimizer/const_array_resolver.cpp",
"$ark_root/bytecode_optimizer/optimize_bytecode.cpp",
"$ark_root/bytecode_optimizer/reg_acc_alloc.cpp",
"$ark_root/bytecode_optimizer/reg_encoder.cpp",
]
libarkbytecodeopt_configs = [
sdk_libc_secshared_config,
"$ark_root:ark_config",
":bytecodeopt_public_config",
"$ark_root/compiler:arkcompiler_public_config",
"$ark_root/libpandabase:arkbase_public_config",
"$ark_root/libpandafile:arkfile_public_config",
"$ark_root/assembler:arkassembler_public_config",
"$ark_root/runtime:arkruntime_public_config",
]
ohos_shared_library("libarkbytecodeopt") {
sources = libarkbytecodeopt_sources
configs = libarkbytecodeopt_configs
deps = [
":bytecodeopt_options_gen_h",
":codegen_intrinsics_cpp",
":codegen_visitors_inc",
":isa_gen_arkbytecodeopt_check_width_cpp",
":isa_gen_arkbytecodeopt_check_width_h",
":isa_gen_arkbytecodeopt_insn_selection_cpp",
":isa_gen_arkbytecodeopt_insn_selection_h",
":reg_encoder_visitors_inc",
"$ark_root/assembler:libarkassembler",
"$ark_root/compiler:libarkcompiler",
"$ark_root/libpandabase:libarkbase",
"$ark_root/libpandafile:libarkfile",
]
relative_install_dir = "ark"
output_extension = "so"
subsystem_name = "ark"
}
ohos_static_library("libarkbytecodeopt_frontend_static") {
sources = libarkbytecodeopt_sources
configs = libarkbytecodeopt_configs
deps = [
":bytecodeopt_options_gen_h",
":codegen_intrinsics_cpp",
":codegen_visitors_inc",
":isa_gen_arkbytecodeopt_check_width_cpp",
":isa_gen_arkbytecodeopt_check_width_h",
":isa_gen_arkbytecodeopt_insn_selection_cpp",
":isa_gen_arkbytecodeopt_insn_selection_h",
":reg_encoder_visitors_inc",
"$ark_root/assembler:libarkassembler_frontend_static",
"$ark_root/compiler:libarkcompiler_frontend_static",
"$ark_root/libpandabase:libarkbase_frontend_static",
"$ark_root/libpandafile:libarkfile_frontend_static",
]
}
ark_isa_gen("isa_gen_arkbytecodeopt") {
template_files = [
"insn_selection.h.erb",
"insn_selection.cpp.erb",
"check_width.cpp.erb",
"check_width.h.erb",
]
sources = "templates"
destination = "$target_gen_dir/generated"
requires = [
"bytecode_optimizer_isapi.rb",
"$ark_root/assembler/asm_isapi.rb",
]
}
ark_gen_file("bytecodeopt_options_gen_h") {
template_file = "../templates/options/options.h.erb"
data_file = "options.yaml"
requires = [ "../templates/common.rb" ]
output_file = "$target_gen_dir/generated/bytecodeopt_options_gen.h"
}
ark_gen_file("reg_encoder_visitors_inc") {
extra_dependencies = [ "$ark_root:concat_plugins_yamls" ]
template_file = "templates/reg_encoder_visitors.inc.erb"
data_file = "$target_gen_dir/../plugin_options.yaml"
requires = [ "$ark_root/templates/plugin_options.rb" ]
output_file = "$target_gen_dir/generated/reg_encoder_visitors.inc"
}
ark_gen_file("codegen_visitors_inc") {
extra_dependencies = [ "$ark_root:concat_plugins_yamls" ]
template_file = "templates/codegen_visitors.inc.erb"
data_file = "$target_gen_dir/../plugin_options.yaml"
requires = [ "$ark_root/templates/plugin_options.rb" ]
output_file = "$target_gen_dir/generated/codegen_visitors.inc"
}
ark_gen_file("codegen_intrinsics_cpp") {
extra_dependencies = [ "$ark_root:concat_plugins_yamls" ]
template_file = "templates/codegen_intrinsics.cpp.erb"
data_file = "$target_gen_dir/../plugin_options.yaml"
requires = [ "$ark_root/templates/plugin_options.rb" ]
output_file = "$target_gen_dir/generated/codegen_intrinsics.cpp"
}

View File

@ -0,0 +1,122 @@
# Copyright (c) 2021-2022 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.
cmake_minimum_required(VERSION 3.5.2 FATAL_ERROR)
project(bytecode_optimizer)
include(cmake/coverage.cmake)
set(BYTECODE_OPT_SOURCES
canonicalization.cpp
codegen.cpp
bytecodeopt_peepholes.cpp
optimize_bytecode.cpp
reg_acc_alloc.cpp
reg_encoder.cpp
check_resolver.cpp
common.cpp
const_array_resolver.cpp
)
set(SOURCES ${BYTECODE_OPT_SOURCES})
set(GENERATED_DIR ${CMAKE_CURRENT_BINARY_DIR}/generated)
file(MAKE_DIRECTORY "${GENERATED_DIR}")
panda_isa_gen(
TEMPLATES
"insn_selection.h.erb"
"insn_selection.cpp.erb"
"check_width.cpp.erb"
"check_width.h.erb"
REQUIRES
"${CMAKE_CURRENT_SOURCE_DIR}/bytecode_optimizer_isapi.rb"
"${PANDA_ROOT}/assembler/asm_isapi.rb"
DESTINATION
"${GENERATED_DIR}"
)
add_library(arkbytecodeopt ${PANDA_DEFAULT_LIB_TYPE} ${SOURCES})
add_dependencies(arkbytecodeopt isa_gen_bytecode_optimizer)
panda_gen_options(TARGET arkbytecodeopt YAML_FILE options.yaml GENERATED_HEADER bytecodeopt_options_gen.h)
target_include_directories(arkbytecodeopt
PUBLIC ${PANDA_ROOT}
PUBLIC ${CMAKE_CURRENT_BINARY_DIR}
PUBLIC ${GENERATED_DIR}
PUBLIC ${GENERATED_DIR}/../compiler/generated
)
target_link_libraries(arkbytecodeopt arkcompiler arkassembler arkfile)
set(PANDA_BYTECODE_OPT_TESTS_LIBRARIES arkbytecodeopt arkfile arkbase)
if (NOT (PANDA_TARGET_MOBILE OR PANDA_TARGET_OHOS OR PANDA_ENABLE_FUZZBENCH))
list(APPEND PANDA_BYTECODE_OPT_TESTS_LIBRARIES stdc++fs)
endif()
set(BYTECODE_OPT_TEST_SOURCES
tests/bitops_bitwise_and_test.cpp
tests/bc_lowering_test.cpp
tests/codegen_test.cpp
tests/reg_acc_alloc_test.cpp
tests/reg_encoder_test.cpp
tests/runtime_adapter_test.cpp
tests/const_array_resolver_test.cpp
tests/bytecodeopt_peepholes_test.cpp
tests/check_resolver_test.cpp
tests/canonicalization_test.cpp
)
panda_add_gtest(
NAME bytecodeopt_unit_tests
SOURCES
${BYTECODE_OPT_TEST_SOURCES}
INCLUDE_DIRS
${CMAKE_CURRENT_SOURCE_DIR}
LIBRARIES
${PANDA_BYTECODE_OPT_TESTS_LIBRARIES}
SANITIZERS
${PANDA_SANITIZERS_LIST}
)
panda_add_gtest(
NO_CORES
NAME bytecodeopt_peepholes_runtime_tests
SOURCES
tests/bytecodeopt_peepholes_runtime_test.cpp
LIBRARIES
arkassembler
arkbytecodeopt
arkruntime
SANITIZERS
${PANDA_SANITIZERS_LIST}
)
panda_add_to_clang_tidy(TARGET arkbytecodeopt CHECKS
# TODO(mbolshov): elaborate
"-hicpp-use-equals-default"
"-modernize-use-equals-default"
"-cppcoreguidelines-pro-type-member-init"
"-misc-unused-parameters"
# From assembler
"-misc-non-private-member-variables-in-classes"
"-cert-dcl21-cpp"
"-cppcoreguidelines-macro-usage"
"-google-runtime-references"
"-readability-identifier-naming" # to use ins_create_api.h
)
panda_add_sanitizers(TARGET arkbytecodeopt SANITIZERS ${PANDA_SANITIZERS_LIST})
add_check_style(".")

View File

@ -0,0 +1,71 @@
/**
* Copyright (c) 2021-2022 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 PANDA_BYTECODE_OPTIMIZER_BYTECODE_ENCODER_H
#define PANDA_BYTECODE_OPTIMIZER_BYTECODE_ENCODER_H
#include <stdint.h>
#include "compiler/optimizer/code_generator/encode.h"
namespace panda::bytecodeopt {
class BytecodeEncoder final : public compiler::Encoder {
public:
explicit BytecodeEncoder(ArenaAllocator *allocator) : Encoder(allocator, Arch::NONE) {}
void Finalize() override {};
static bool CanEncodeImmHelper(int64_t imm, uint32_t size, int64_t min, int64_t max)
{
constexpr uint8_t HALF_SIZE = 32;
if (size != HALF_SIZE) {
return false;
}
return imm >= min && imm <= max;
}
bool CanEncodeImmAddSubCmp(int64_t imm, uint32_t size, [[maybe_unused]] bool signed_compare) override
{
return CanEncodeImmHelper(imm, size, INT8_MIN, INT8_MAX);
}
bool CanEncodeImmMulDivMod(uint64_t imm, uint32_t size) override
{
return CanEncodeImmAddSubCmp(imm, size, false);
}
bool CanEncodeImmLogical(uint64_t imm, uint32_t size) override
{
return CanEncodeImmHelper(imm, size, INT32_MIN, INT32_MAX);
}
bool CanEncodeShift(uint32_t size) override
{
constexpr uint32_t UNSUPPORTED_SHIFT_SIZE = 64;
return size != UNSUPPORTED_SHIFT_SIZE;
}
size_t GetLabelAddress(compiler::LabelHolder::LabelId) override
{
return 0;
}
bool LabelHasLinks(compiler::LabelHolder::LabelId) override
{
return false;
}
}; // BytecodeEncoder
} // namespace panda::bytecodeopt
#endif // PANDA_BYTECODE_OPTIMIZER_BYTECODE_ENCODER_H

View File

@ -0,0 +1,499 @@
# Copyright (c) 2021-2022 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.
# ISAPI specialization for bytecode optimizer
Instruction.class_eval do
def src_acc_kind
op = acc_and_operands.select { |op| op.acc? && op.src? }.first
raise "There is no src acc for #{mnemonic}" unless op
data_kind_helper(op)
end
def dst_acc_kind
op = acc_and_operands.select { |op| op.acc? && op.dst? }.first
raise "There is no dst acc for #{mnemonic}" unless op
data_kind_helper(op)
end
private
# return one of 32, 64, 'ref'
def data_kind_helper(op)
m = /[fiub](?<size>\d+)/.match(op.type)
if m
size = m[:size].to_i
if size == 64
return 64
else
return 32
end
end
return 'ref' if op.type == 'ref'
raise "Unexpected operand type #{op.type} in data_kind_helper"
end
end
def instruction_hash
unless defined? @instruction_hash
@instruction_hash = Hash.new { |_, key| raise "No instruction with '#{key}' mnemonic" }
Panda.instructions.each { |insn| @instruction_hash[insn.mnemonic] = insn }
end
@instruction_hash
end
# Classes for bytecode description
Visitor = Struct.new(:ir_op, :switch)
Switch = Struct.new(:expr, :cases) do
def encode
res = "switch (#{expr}) {\n"
cases.each do |c|
res << c.encode
end
res << "default:
LOG(ERROR, BYTECODE_OPTIMIZER) << \"Codegen for \" << compiler::GetOpcodeString(inst->GetOpcode()) << \" failed\";
enc->success_ = false;
}"
res
end
def check_width
res = "switch (#{expr}) {\n"
cases.each do |c|
res << c.check_width
end
res << "default:
LOG(ERROR, BYTECODE_OPTIMIZER) << \"CheckWidth for \" << compiler::GetOpcodeString(inst->GetOpcode()) << \" failed\";
re->success_ = false;
}"
res
end
end
Case = Struct.new(:types, :node) do
def proxy(method)
res = types.map { |type| "case #{type}:" }.join("\n")
res << " {\n"
res << node.send(method)
res << "break;\n}\n"
res
end
def encode
proxy(:encode)
end
def check_width
proxy(:check_width)
end
end
Leaf = Struct.new(:instruction, :args) do
def encode
res = ""
args_str = args.join(",\n")
if instruction.acc_read?
res << do_lda(instruction)
res << "\n"
end
res << "enc->result_.emplace_back(pandasm::Create_#{instruction.asm_token}(\n"
res << args_str
res << "\n));\n"
if instruction.acc_write?
res << do_sta(instruction)
res << "\n"
end
res
end
def check_width
reg = instruction.operands.select(&:reg?).first
if reg
"re->Check#{reg.width}Width(inst);\n"
else
"return;\n"
end
end
end
Empty = Struct.new(:dummy) do
def encode; end
def check_width; end
end
# Sugar for bytecode description
def visit(ir_op)
@table ||= []
@table << Visitor.new(ir_op, yield)
end
def visitors
@table
end
def switch(expr, cases)
Switch.new(expr, cases)
end
def plain(opcode, *args)
Leaf.new(instruction_hash[opcode], args.to_a)
end
def empty
Empty.new
end
def prefixed_case(prefix, types, node)
types = types.map { |t| "#{prefix}#{t}" }
Case.new(types, node)
end
def case_(types, opcode, *args)
prefixed_case("compiler::DataType::", types, plain(opcode, *args))
end
def cc_case(types, opcode, *args)
prefixed_case("compiler::CC_", types, plain(opcode, *args))
end
def case_switch(types, condition, inner_cases)
prefixed_case("compiler::DataType::", types, switch(condition, inner_cases))
end
def case_true(opcode, *args)
Case.new(['1'], plain(opcode, *args))
end
def case_false(opcode, *args)
Case.new(['0'], plain(opcode, *args))
end
# Type/cc cases for instruction selection
def i32_types
@i32_types ||= %w[BOOL UINT8 INT8 UINT16 INT16 UINT32 INT32]
end
def i64_types
@i64_types ||= %w[INT64 UINT64]
end
def f32_types
@f32_types ||= %w[FLOAT32]
end
def f64_types
@f64_types ||= %w[FLOAT64]
end
def b64_types
@b64_types ||= i64_types + f64_types
end
def b32_types
@b32_types ||= i32_types + f32_types
end
def void_types
@void_types ||= %w[VOID]
end
def cc_cases
@cc_cases ||= %w[EQ NE LT LE GT GE]
end
# Switch condition printers
def type
'inst->GetType()'
end
def src_type
'inst->GetInputType(0)'
end
# we could use switch on 'bool' type for if-else purposes, but that hurts clang_tidy
def if_acc?(reg)
"static_cast<int>(#{reg} == compiler::ACC_REG_ID)"
end
def if_fcmpg?
'static_cast<int>(inst->IsFcmpg())'
end
def if_inci?
"static_cast<int>(CanConvertToIncI(inst))"
end
# Operand printers
def dst_r
'inst->GetDstReg()'
end
def r(num)
"inst->GetSrcReg(#{num})"
end
def imm
'static_cast<int32_t>(inst->GetImm() & 0xffffffff)'
end
def label
'LabelName(inst->GetBasicBlock()->GetTrueSuccessor()->GetId())'
end
def string_id
'enc->ir_interface_->GetStringIdByOffset(inst->GetTypeId())'
end
def literalarray_id
'enc->ir_interface_->GetLiteralArrayIdByOffset(inst->GetTypeId()).value()'
end
def type_id
'enc->ir_interface_->GetTypeIdByOffset(inst->GetTypeId())'
end
def field_id
'enc->ir_interface_->GetFieldIdByOffset(inst->GetTypeId())'
end
# Lda/Sta printers
def do_lda(instruction)
lda = case instruction.src_acc_kind
when 32
instruction_hash['lda']
when 64
instruction_hash['lda.64']
when 'ref'
instruction_hash['lda.obj']
end
reg_num = if instruction.mnemonic.include?('ldarr') || instruction.mnemonic.include?('stobj') || instruction.mnemonic.include?('ststatic')
1
elsif instruction.mnemonic.include?('starr')
2
else
0
end
"if (inst->GetSrcReg(#{reg_num}) != compiler::ACC_REG_ID) {
enc->result_.emplace_back(pandasm::Create_#{lda.asm_token}(inst->GetSrcReg(#{reg_num})));
}"
end
def do_sta(instruction)
sta = case instruction.dst_acc_kind
when 32
instruction_hash['sta']
when 64
instruction_hash['sta.64']
when 'ref'
instruction_hash['sta.obj']
end
"if (inst->GetDstReg() != compiler::ACC_REG_ID) {
enc->result_.emplace_back(pandasm::Create_#{sta.asm_token}(inst->GetDstReg()));
}"
end
# Misc printers
def visitor_sig(op_name, with_class = true)
"void #{'BytecodeGen::' if with_class}Visit#{op_name}(GraphVisitor* v, Inst* inst_base)"
end
# Bytecode description itself
# Wrap all `insn` declaration in a function to call from template
# (because Panda::instructions is initialized only in templates)
def call_me_from_template
%w[and xor or shl shr].each do |op|
visit(op.capitalize) do
switch(type,
[case_switch(i32_types, if_acc?(r(0)),
[case_true("#{op}2", r(1)),
case_false("#{op}", r(0), r(1))]),
case_(i64_types, "#{op}2.64", r(1))]
)
end
end
visit('AShr') do
switch(type,
[case_switch(i32_types, if_acc?(r(0)),
[case_true("ashr2", r(1)),
case_false("ashr", r(0), r(1))]),
case_(i64_types, 'ashr2.64', r(1))]
)
end
%w[add sub mul div mod].each do |op|
visit(op.capitalize) do
switch(type,
[case_switch(i32_types, if_acc?(r(0)),
[case_true("#{op}2", r(1)),
case_false("#{op}", r(0), r(1))]),
case_(i64_types, "#{op}2.64", r(1)),
case_(f32_types, "f#{op}2", r(1)),
case_(f64_types, "f#{op}2.64", r(1))]
)
end
end
visit('AddI') do
switch(type,
[case_switch(i32_types, if_inci?,
[case_true('inci', r(0), imm),
case_false('addi', imm)])]
)
end
visit('SubI') do
switch(type,
[case_switch(i32_types, if_inci?,
[case_true('inci', r(0), "-(#{imm})"),
case_false('subi', imm)])]
)
end
visit('Not') do
switch(type,
[case_(i32_types, 'not'),
case_(i64_types, 'not.64')]
)
end
visit('Neg') do
switch(type,
[case_(i32_types, 'neg'),
case_(i64_types, 'neg.64'),
case_(f32_types, 'fneg'),
case_(f64_types, 'fneg.64')]
)
end
visit('Cmp') do
switch('inst->GetOperandsType()',
[case_(%w[UINT8 UINT16 UINT32], 'ucmp', r(1)),
case_(%w[INT64], 'cmp.64', r(1)),
case_(%w[UINT64], 'ucmp.64', r(1)),
case_switch(['FLOAT32'], if_fcmpg?,
[case_true('fcmpg', r(1)),
case_false('fcmpl', r(1))]),
case_switch(['FLOAT64'], if_fcmpg?,
[case_true('fcmpg.64', r(1)),
case_false('fcmpl.64', r(1))])]
)
end
visit('ReturnVoid') do
plain('return.void')
end
visit('Throw') do
plain('throw', r(0))
end
visit('NullPtr') do
switch(if_acc?(dst_r),
[case_true('lda.null'),
case_false('mov.null', dst_r)]
)
end
visit('LoadConstArray') do
plain('lda.const', dst_r, literalarray_id)
end
visit('NewArray') do
plain('newarr', dst_r, r(1), type_id)
end
visit('LenArray') do
plain('lenarr', r(0))
end
visit('LoadArray') do
switch(type,
[case_(['INT8'], 'ldarr.8', r(0)),
case_(['UINT8'], 'ldarru.16', r(0)),
case_(['INT16'], 'ldarr.16', r(0)),
case_(['UINT16'], 'ldarru.16', r(0)),
case_(['INT32', 'UINT32'], 'ldarr', r(0)),
case_(['INT64', 'UINT64'], 'ldarr.64', r(0)),
case_(['REFERENCE'], 'ldarr.obj', r(0)),
case_(['FLOAT32'], 'fldarr.32', r(0)),
case_(['FLOAT64'], 'fldarr.64', r(0))]
)
end
visit('StoreArray') do
switch(type,
[case_(['INT8', 'UINT8'], 'starr.8', r(0), r(1)),
case_(['INT16', 'UINT16'], 'starr.16', r(0), r(1)),
case_(['INT32', 'UINT32'], 'starr', r(0), r(1)),
case_(['INT64', 'UINT64'], 'starr.64', r(0), r(1)),
case_(['REFERENCE'], 'starr.obj', r(0), r(1)),
case_(['FLOAT32'], 'fstarr.32', r(0), r(1)),
case_(['FLOAT64'], 'fstarr.64', r(0), r(1))]
)
end
visit('LoadStatic') do
switch(type,
[case_(i32_types, 'ldstatic', field_id),
case_(i64_types, 'ldstatic.64', field_id),
case_(f32_types, 'ldstatic', field_id),
case_(f64_types, 'ldstatic.64', field_id),
case_(['REFERENCE'], 'ldstatic.obj', field_id)]
)
end
visit('StoreStatic') do
switch(type,
[case_(i32_types, 'ststatic', field_id),
case_(i64_types, 'ststatic.64', field_id),
case_(f32_types, 'ststatic', field_id),
case_(f64_types, 'ststatic.64', field_id),
case_(['REFERENCE'], 'ststatic.obj', field_id)]
)
end
visit('CheckCast') do
plain('checkcast', type_id)
end
visit('IsInstance') do
plain('isinstance', type_id)
end
visit('LoadType') do
plain('lda.type', type_id)
end
visit('NewObject') do
plain('newobj', dst_r, type_id)
end
# Empty visitors for IR instructions we want to ignore
# (Add missing IRs on demand)
%w[NullCheck BoundsCheck ZeroCheck NegativeCheck SafePoint
InitClass SaveStateDeoptimize RefTypeCheck Phi
Try SaveState LoadClass LoadAndInitClass Parameter].each do |op|
visit(op) do
empty
end
end
%w[MulI DivI ModI ShlI ShrI AShrI AndI OrI XorI].each do |op|
visit(op) do
switch(type,
[case_(i32_types, "#{op.downcase}", imm)]
)
end
end
end

View File

@ -0,0 +1,24 @@
/*
* Copyright (c) 2021-2022 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 BYTECODEOPT_OPTIONS_H
#define BYTECODEOPT_OPTIONS_H
#include "generated/bytecodeopt_options_gen.h"
namespace panda::bytecodeopt {
extern panda::bytecodeopt::Options options;
} // namespace panda::bytecodeopt
#endif // BYTECODEOPT_OPTIONS_H

View File

@ -0,0 +1,153 @@
/**
* Copyright (c) 2021-2022 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.
*/
#include "bytecodeopt_peepholes.h"
#include "libpandafile/bytecode_instruction-inl.h"
namespace panda::bytecodeopt {
bool BytecodeOptPeepholes::RunImpl()
{
VisitGraph();
return IsApplied();
}
CallInst *FindCtorCall(Inst *new_object, const compiler::ClassInst *load)
{
auto *adapter = new_object->GetBasicBlock()->GetGraph()->GetRuntime();
for (auto &user : new_object->GetUsers()) {
auto *inst = user.GetInst();
if (inst->GetOpcode() == Opcode::NullCheck) {
return FindCtorCall(inst, load);
}
if (inst->GetOpcode() != Opcode::CallStatic) {
continue;
}
auto call = inst->CastToCallStatic();
if (adapter->IsConstructor(call->GetCallMethod(), load->GetTypeId())) {
return call;
}
}
return nullptr;
}
CallInst *CreateInitObject(compiler::GraphVisitor *v, compiler::ClassInst *load, const CallInst *call_init)
{
auto *graph = static_cast<BytecodeOptPeepholes *>(v)->GetGraph();
auto *init_object = static_cast<CallInst *>(graph->CreateInst(compiler::Opcode::InitObject));
init_object->SetType(compiler::DataType::REFERENCE);
auto input_types_count = call_init->GetInputsCount();
init_object->AllocateInputTypes(graph->GetAllocator(), input_types_count);
init_object->AddInputType(compiler::DataType::REFERENCE);
init_object->AppendInput(load);
for (size_t i = 1; i < input_types_count; ++i) {
auto input_inst = call_init->GetInput(i).GetInst();
init_object->AddInputType(input_inst->GetType());
init_object->AppendInput(input_inst);
}
init_object->SetCallMethodId(call_init->GetCallMethodId());
init_object->SetCallMethod(static_cast<const CallInst *>(call_init)->GetCallMethod());
return init_object;
}
void ReplaceNewObjectUsers(Inst *new_object, Inst *null_check, CallInst *init_object)
{
for (auto it = new_object->GetUsers().begin(); it != new_object->GetUsers().end();
it = new_object->GetUsers().begin()) {
auto user = it->GetInst();
if (user != null_check) {
user->SetInput(it->GetIndex(), init_object);
} else {
new_object->RemoveUser(&(*it));
}
}
// Update throwable instructions data
auto graph = new_object->GetBasicBlock()->GetGraph();
if (graph->IsInstThrowable(new_object)) {
graph->ReplaceThrowableInst(new_object, init_object);
}
}
void BytecodeOptPeepholes::VisitNewObject(GraphVisitor *v, Inst *inst)
{
auto load = static_cast<compiler::ClassInst *>(inst->GetInput(0).GetInst());
ASSERT(load != nullptr);
CallInst *call_init = FindCtorCall(inst, load);
if (call_init == nullptr) {
return;
}
if (inst->GetBasicBlock() != call_init->GetBasicBlock()) {
return;
}
// The optimization is correct only if there are no side-effects between NewObject and constructor call.
// For simplicity, we abort it if any instruction except NullCheck and SaveState appears in-between.
// Moreover, when we are inside a try block, local register state also matters, because it may be used inside
// catch blocks. In such case we also abort if there are any instructions in corresponding bytecode.
const auto graph = static_cast<BytecodeOptPeepholes *>(v)->GetGraph();
const size_t NEWOBJ_SIZE =
BytecodeInstruction::Size( // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
BytecodeInstruction(graph->GetRuntime()->GetMethodCode(graph->GetMethod()) + inst->GetPc()).GetFormat());
if (inst->GetBasicBlock()->IsTry() && call_init->GetPc() - inst->GetPc() > NEWOBJ_SIZE) {
return;
}
Inst *null_check = nullptr;
for (auto *i = inst->GetNext(); i != call_init; i = i->GetNext()) {
if (i->GetOpcode() != Opcode::SaveState && i->GetOpcode() != Opcode::NullCheck) {
return;
}
if (i->GetOpcode() == Opcode::SaveState) {
continue;
}
for (auto null_check_input : i->GetInputs()) {
if (null_check_input.GetInst() == inst) {
ASSERT(null_check == nullptr);
null_check = i;
}
}
if (null_check == nullptr) {
return;
}
}
auto *init_object = CreateInitObject(v, load, call_init);
call_init->InsertBefore(init_object);
init_object->SetPc(call_init->GetPc());
ReplaceNewObjectUsers(inst, null_check, init_object);
inst->ClearFlag(compiler::inst_flags::NO_DCE);
if (null_check != nullptr) {
null_check->ReplaceUsers(init_object);
null_check->ClearFlag(compiler::inst_flags::NO_DCE);
null_check->RemoveInputs();
null_check->GetBasicBlock()->ReplaceInst(null_check,
static_cast<BytecodeOptPeepholes *>(v)->GetGraph()->CreateInstNOP());
}
ASSERT(!call_init->HasUsers());
call_init->ClearFlag(compiler::inst_flags::NO_DCE);
static_cast<BytecodeOptPeepholes *>(v)->SetIsApplied();
}
} // namespace panda::bytecodeopt

View File

@ -0,0 +1,90 @@
/**
* Copyright (c) 2021-2022 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 PANDA_BYTECODE_OPTIMIZER_BYTECODEOPT_PEEPHOLES_H_
#define PANDA_BYTECODE_OPTIMIZER_BYTECODEOPT_PEEPHOLES_H_
#include "bytecodeopt_options.h"
#include "compiler/optimizer/pass.h"
#include "compiler/optimizer/ir/basicblock.h"
#include "compiler/optimizer/ir/graph.h"
#include "compiler/optimizer/ir/graph_visitor.h"
#include "compiler/optimizer/ir/inst.h"
#include "libpandabase/utils/arena_containers.h"
#include "runtime_adapter.h"
/*
* BytecodeOptPeepholes
*
* BytecodeOptPeepholes includes now only transformation of NewObject and related instructions into
* InitObject
*/
namespace panda::bytecodeopt {
using compiler::BasicBlock;
using compiler::CallInst;
using compiler::Inst;
using compiler::Opcode;
class BytecodeOptPeepholes : public compiler::Optimization, public compiler::GraphVisitor {
public:
explicit BytecodeOptPeepholes(compiler::Graph *graph) : compiler::Optimization(graph) {}
~BytecodeOptPeepholes() override = default;
bool RunImpl() override;
const char *GetPassName() const override
{
return "BytecodeOptPeepholes";
}
bool IsEnable() const override
{
return options.IsBytecodeOptPeepholes();
}
/* This visitor replaces combination of NewObject, SaveState,
* NullCheck and CallStatic with InitObject. It is used in order to replace newobj, sta and call
* in some ark bytecode with one instruction initobj.
*/
const ArenaVector<BasicBlock *> &GetBlocksToVisit() const override
{
return GetGraph()->GetBlocksRPO();
}
static void VisitNewObject(GraphVisitor *v, Inst *inst);
public:
bool IsApplied()
{
return is_applied_;
}
#include "compiler/optimizer/ir/visitor.inc"
private:
void SetIsApplied()
{
is_applied_ = true;
}
bool is_applied_ {false};
};
} // namespace panda::bytecodeopt
#endif // PANDA_BYTECODE_OPTIMIZER_BYTECODEOPT_PEEPHOLES_H_

View File

@ -0,0 +1,130 @@
/*
* Copyright (c) 2021-2022 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.
*/
#include "canonicalization.h"
#include "compiler/optimizer/ir/basicblock.h"
#include "compiler/optimizer/ir/inst.h"
namespace panda::bytecodeopt {
bool Canonicalization::RunImpl()
{
Canonicalization visitor(GetGraph());
for (auto bb : GetGraph()->GetBlocksRPO()) {
for (auto inst : bb->AllInsts()) {
if (inst->IsCommutative()) {
visitor.VisitCommutative(inst);
} else {
visitor.VisitInstruction(inst);
}
}
}
return visitor.GetStatus();
}
static bool IsDominateReverseInputs(const compiler::Inst *inst)
{
auto input0 = inst->GetInput(0).GetInst();
auto input1 = inst->GetInput(1).GetInst();
return input0->IsDominate(input1);
}
static bool ConstantFitsCompareImm(const Inst *cst, uint32_t size)
{
ASSERT(cst->GetOpcode() == Opcode::Constant);
if (compiler::DataType::IsFloatType(cst->GetType())) {
return false;
}
auto val = cst->CastToConstant()->GetIntValue();
return (size == compiler::HALF_SIZE) && (val == 0);
}
static bool BetterToSwapCompareInputs(const compiler::Inst *inst, const compiler::Inst *input0,
const compiler::Inst *input1)
{
if (!input0->IsConst()) {
return false;
}
if (!input1->IsConst()) {
return true;
}
compiler::DataType::Type type = inst->CastToCompare()->GetOperandsType();
uint32_t size = (type == compiler::DataType::UINT64 || type == compiler::DataType::INT64) ? compiler::WORD_SIZE
: compiler::HALF_SIZE;
return ConstantFitsCompareImm(input0, size) && !ConstantFitsCompareImm(input1, size);
}
static bool SwapInputsIfNecessary(compiler::Inst *inst, const bool necessary)
{
if (!necessary) {
return false;
}
auto input0 = inst->GetInput(0).GetInst();
auto input1 = inst->GetInput(1).GetInst();
if ((inst->GetOpcode() == compiler::Opcode::Compare) && !BetterToSwapCompareInputs(inst, input0, input1)) {
return false;
}
inst->SwapInputs();
return true;
}
bool Canonicalization::TrySwapConstantInput(Inst *inst)
{
return SwapInputsIfNecessary(inst, inst->GetInput(0).GetInst()->IsConst());
}
bool Canonicalization::TrySwapReverseInput(Inst *inst)
{
return SwapInputsIfNecessary(inst, IsDominateReverseInputs(inst));
}
void Canonicalization::VisitCommutative(Inst *inst)
{
ASSERT(inst->IsCommutative());
ASSERT(inst->GetInputsCount() == 2); // 2 is COMMUTATIVE_INPUT_COUNT
if (options.GetOptLevel() > 1) {
result_ = TrySwapReverseInput(inst);
}
result_ = TrySwapConstantInput(inst) || result_;
}
// It is not allowed to move a constant input1 with a single user (it's processed Compare instruction).
// This is necessary for further merging of the constant and the If instrution in the Lowering pass
bool AllowSwap(const compiler::Inst *inst)
{
auto input1 = inst->GetInput(1).GetInst();
if (!input1->IsConst()) {
return true;
}
for (const auto &user : input1->GetUsers()) {
if (user.GetInst() != inst) {
return true;
}
}
return false;
}
void Canonicalization::VisitCompare([[maybe_unused]] GraphVisitor *v, Inst *inst_base)
{
auto inst = inst_base->CastToCompare();
if (AllowSwap(inst) && SwapInputsIfNecessary(inst, IsDominateReverseInputs(inst))) {
auto revert_cc = SwapOperandsConditionCode(inst->GetCc());
inst->SetCc(revert_cc);
}
}
} // namespace panda::bytecodeopt

View File

@ -0,0 +1,65 @@
/**
* Copyright (c) 2021-2022 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 PANDA_BYTECODE_OPT_CANONICALIZATION_H
#define PANDA_BYTECODE_OPT_CANONICALIZATION_H
#include "bytecodeopt_options.h"
#include "compiler/optimizer/ir/graph.h"
#include "compiler/optimizer/pass.h"
#include "compiler/optimizer/ir/graph_visitor.h"
namespace panda::bytecodeopt {
using panda::compiler::BasicBlock;
using panda::compiler::Inst;
using panda::compiler::Opcode;
class Canonicalization : public compiler::Optimization, public compiler::GraphVisitor {
public:
explicit Canonicalization(compiler::Graph *graph) : compiler::Optimization(graph) {}
~Canonicalization() override = default;
bool RunImpl() override;
const char *GetPassName() const override
{
return "Canonicalization";
}
bool IsEnable() const override
{
return options.IsCanonicalization();
}
bool GetStatus() const
{
return result_;
}
const ArenaVector<BasicBlock *> &GetBlocksToVisit() const override
{
return GetGraph()->GetBlocksRPO();
}
void VisitCommutative(Inst *inst);
static void VisitCompare([[maybe_unused]] GraphVisitor *v, Inst *inst);
static bool TrySwapReverseInput(Inst *inst);
static bool TrySwapConstantInput(Inst *inst);
#include "optimizer/ir/visitor.inc"
private:
bool result_ {false};
};
} // namespace panda::bytecodeopt
#endif // PANDA_BYTECODE_OPT_CANONICALIZATION_H

View File

@ -0,0 +1,93 @@
/**
* Copyright (c) 2021-2022 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.
*/
#include "check_resolver.h"
#include "compiler/optimizer/ir/basicblock.h"
#include "compiler/optimizer/ir/inst.h"
namespace panda::bytecodeopt {
bool CheckResolver::RunImpl()
{
bool applied = false;
for (auto bb : GetGraph()->GetBlocksRPO()) {
for (auto inst : bb->Insts()) {
auto op = inst->GetOpcode();
// replace check
if (IsCheck(inst)) {
size_t i = (op == compiler::Opcode::BoundsCheck || op == compiler::Opcode::RefTypeCheck) ? 1 : 0;
auto input = inst->GetInput(i).GetInst();
inst->ReplaceUsers(input);
inst->ClearFlag(compiler::inst_flags::NO_DCE); // DCE will remove the check inst
applied = true;
}
// set NO_DCE for Div, Mod, LoadArray, LoadStatic and LoadObject
if (NeedSetNoDCE(inst)) {
inst->SetFlag(compiler::inst_flags::NO_DCE);
applied = true;
}
// mark LenArray whose users are not all check as no_dce
// mark LenArray as no_hoist in all cases
if (inst->GetOpcode() == compiler::Opcode::LenArray) {
bool no_dce = !inst->HasUsers();
for (const auto &usr : inst->GetUsers()) {
if (!IsCheck(usr.GetInst())) {
no_dce = true;
break;
}
}
if (no_dce) {
inst->SetFlag(compiler::inst_flags::NO_DCE);
}
inst->SetFlag(compiler::inst_flags::NO_HOIST);
}
}
}
return applied;
}
bool CheckResolver::NeedSetNoDCE(const compiler::Inst *inst)
{
switch (inst->GetOpcode()) {
case compiler::Opcode::Div:
case compiler::Opcode::Mod:
case compiler::Opcode::LoadArray:
case compiler::Opcode::LoadStatic:
case compiler::Opcode::LoadObject:
return true;
default:
return false;
}
}
bool CheckResolver::IsCheck(const compiler::Inst *inst)
{
switch (inst->GetOpcode()) {
case compiler::Opcode::BoundsCheck:
case compiler::Opcode::NullCheck:
case compiler::Opcode::NegativeCheck:
case compiler::Opcode::ZeroCheck:
case compiler::Opcode::RefTypeCheck:
case compiler::Opcode::BoundsCheckI:
return true;
default:
return false;
}
}
} // namespace panda::bytecodeopt

View File

@ -0,0 +1,64 @@
/**
* Copyright (c) 2021-2022 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 PANDA_CHECK_RESOLVER_H
#define PANDA_CHECK_RESOLVER_H
#include "compiler/optimizer/ir/graph.h"
#include "compiler/optimizer/ir/inst.h"
#include "compiler/optimizer/pass.h"
#include "utils/arena_containers.h"
/*
* Check Resolver.
*
* Check Resolver is a bytecodeopt-specific pass. In bytecode optimizer, we do
* not need all check-instructions, such as BoundsCheck, NullCheck, NegativeCheck
* and ZeroCheck, because the throws of these checks will be embedded in their
* underlying instructions during runtime.
* For the sake of saving ROM size, we can delete these check-instructions. Besides,
* when bytecode optimizer optimizes the code in which there exist operations on the
* element of an array, in the generated code the redundant asm lenarr will be generated
* with ldarr and starr. Here lenarr is generated from IR LenArray and LenArray is an
* input of BoundsCheck. CodeGen will encode LenArray but ignore BoundsCheck. That is
* why dead lenarr remains. So we can also benefit from the size of generated bytecode
* by deleting check-instructions.
* However, these LenArray that are generated from asm lenarr should be keeped, as they
* may throw in the original code logic. For LoadArray, Div, Mod, LoadStatic and LoadObject,
* since they can throw but they are not generated as inputs of check-instructions, We
* should keep all such insts.
*
* For every check-instruction, we replace the corresponding input of its users by the data
* flow input. Then we clear its NO_DCE flag such that it can be removed by DCE pass. We set
* the NO_DCE flag for the insts that should be keeped.
*/
namespace panda::bytecodeopt {
class CheckResolver : public compiler::Optimization {
public:
explicit CheckResolver(compiler::Graph *graph) : compiler::Optimization(graph) {}
~CheckResolver() override = default;
bool RunImpl() override;
static bool IsCheck(const compiler::Inst *inst);
static bool NeedSetNoDCE(const compiler::Inst *inst);
const char *GetPassName() const override
{
return "CheckResolver";
}
};
} // namespace panda::bytecodeopt
#endif // PANDA_CHECK_RESOLVER_H

View File

@ -0,0 +1,57 @@
# Copyright (c) 2021-2022 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.
add_custom_target(bytecode_optimizer_coverage DEPENDS bytecodeopt_unit_tests)
add_custom_command(TARGET bytecode_optimizer_coverage POST_BUILD
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMAND bash ${PANDA_ROOT}/bytecode_optimizer/tools/optimizer_coverage.sh --binary-dir=${PANDA_BINARY_ROOT} --root-dir=${PANDA_ROOT}
)
option(ENABLE_BYTECODE_OPTIMIZER_COVERAGE "Enable coverage calculation for the bytecode optimizer" false)
find_program(
LCOV
NAMES "lcov"
DOC "Path to lcov executable")
if(NOT LCOV)
set(ENABLE_BYTECODE_OPTIMIZER_COVERAGE false)
endif()
find_program(
GENHTML
NAMES "genhtml"
DOC "Path to genhtml executable")
if(NOT GENHTML)
set(ENABLE_BYTECODE_OPTIMIZER_COVERAGE false)
endif()
if(ENABLE_BYTECODE_OPTIMIZER_COVERAGE)
# Set coverage options
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage")
add_custom_target(bo_coverage DEPENDS bytecodeopt_unit_tests) # Execute tests
set(ADD_COV_FLAGS --quiet --rc lcov_branch_coverage=1)
add_custom_command(TARGET bo_coverage POST_BUILD
WORKING_DIRECTORY ${PANDA_BINARY_ROOT}
# Update current coverage info
COMMAND lcov --no-external -b ${PANDA_ROOT}/bytecode_optimizer -d ${CMAKE_CURRENT_BINARY_DIR} -c -o bo_coverage.info ${ADD_COV_FLAGS}
# Generating an html report
COMMAND genhtml -o bo_coverage_report bo_coverage.info --ignore-errors source ${ADD_COV_FLAGS}
COMMAND echo "Coverage report: ${PANDA_BINARY_ROOT}/bo_coverage_report"
# Delete temporary files to collect statistics
COMMAND rm bo_coverage.info
COMMAND find ${PANDA_BINARY_ROOT}/* -iname "*.gcda" -delete
)
else()
message(STATUS "Coverage will not be calculated (may be enabled by -DENABLE_BYTECODE_OPTIMIZER_COVERAGE=true ).")
endif(ENABLE_BYTECODE_OPTIMIZER_COVERAGE)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,162 @@
/**
* Copyright (c) 2021-2022 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 PANDA_BYTECODE_OPT_CODEGEN_H
#define PANDA_BYTECODE_OPT_CODEGEN_H
#include "assembler/assembly-function.h"
#include "assembler/assembly-ins.h"
#include "ins_create_api.h"
#include "ir_interface.h"
#include "compiler/optimizer/pass.h"
#include "compiler/optimizer/ir/basicblock.h"
#include "compiler/optimizer/ir/graph.h"
#include "compiler/optimizer/ir/graph_visitor.h"
#include "utils/logger.h"
namespace panda::bytecodeopt {
using compiler::BasicBlock;
using compiler::Inst;
using compiler::Opcode;
void DoLdaObj(compiler::Register reg, std::vector<pandasm::Ins> &result);
void DoLda(compiler::Register reg, std::vector<pandasm::Ins> &result);
void DoLda64(compiler::Register reg, std::vector<pandasm::Ins> &result);
void DoSta(compiler::Register reg, std::vector<pandasm::Ins> &result);
void DoSta64(compiler::Register reg, std::vector<pandasm::Ins> &result);
void DoLdaDyn(compiler::Register reg, std::vector<pandasm::Ins> &result);
void DoStaDyn(compiler::Register reg, std::vector<pandasm::Ins> &result);
class BytecodeGen : public compiler::Optimization, public compiler::GraphVisitor {
public:
explicit BytecodeGen(compiler::Graph *graph, pandasm::Function *function, const BytecodeOptIrInterface *iface)
: compiler::Optimization(graph), function_(function), ir_interface_(iface)
{
}
~BytecodeGen() override = default;
bool RunImpl() override;
const char *GetPassName() const override
{
return "BytecodeGen";
}
std::vector<pandasm::Ins> GetEncodedInstructions() const
{
return res_;
}
void Reserve(size_t res_size = 0)
{
if (res_size > 0) {
result_.reserve(res_size);
}
}
bool GetStatus() const
{
return success_;
}
const std::vector<pandasm::Ins> &GetResult() const
{
return result_;
}
std::vector<pandasm::Ins> &&GetResult()
{
return std::move(result_);
}
static std::string LabelName(uint32_t id)
{
return "label_" + std::to_string(id);
}
void EmitLabel(const std::string label)
{
pandasm::Ins l;
l.label = label;
l.set_label = true;
result_.emplace_back(l);
}
void EmitJump(const BasicBlock *bb);
void EncodeSpillFillData(const compiler::SpillFillData &sf);
void EncodeSta(compiler::Register reg, compiler::DataType::Type type);
void AddLineNumber(const Inst *inst, const size_t idx);
void AddColumnNumber(const Inst *inst, const uint32_t idx);
const ArenaVector<BasicBlock *> &GetBlocksToVisit() const override
{
return GetGraph()->GetBlocksRPO();
}
static void VisitSpillFill(GraphVisitor *v, Inst *inst);
static void VisitConstant(GraphVisitor *v, Inst *inst);
static void VisitCallStatic(GraphVisitor *visitor, Inst *inst);
static void VisitCallVirtual(GraphVisitor *visitor, Inst *inst);
static void VisitInitObject(GraphVisitor *visitor, Inst *inst);
static void VisitCatchPhi(GraphVisitor *visitor, Inst *inst);
static void VisitIf(GraphVisitor *v, Inst *inst_base);
static void VisitIfImm(GraphVisitor *v, Inst *inst_base);
static void VisitCast(GraphVisitor *v, Inst *inst_base);
static void IfImmZero(GraphVisitor *v, Inst *inst_base);
static void IfImmNonZero(GraphVisitor *v, Inst *inst_base);
static void IfImm64(GraphVisitor *v, Inst *inst_base);
static void VisitIntrinsic(GraphVisitor *v, Inst *inst_base);
static void CallHandler(GraphVisitor *visitor, Inst *inst);
static void VisitStoreObject(GraphVisitor *v, Inst *inst_base);
static void VisitLoadObject(GraphVisitor *v, Inst *inst_base);
static void VisitLoadString(GraphVisitor *v, Inst *inst_base);
static void VisitReturn(GraphVisitor *v, Inst *inst_base);
static void VisitCastValueToAnyType(GraphVisitor *v, Inst *inst_base);
static void VisitEcma(GraphVisitor *v, Inst *inst_base);
static void IfEcma(GraphVisitor *v, compiler::IfInst *inst);
#include "generated/codegen_visitors.inc"
#include "generated/insn_selection.h"
void VisitDefault(Inst *inst) override
{
LOG(ERROR, BYTECODE_OPTIMIZER) << "Opcode " << compiler::GetOpcodeString(inst->GetOpcode())
<< " not yet implemented in codegen";
success_ = false;
}
#include "compiler/optimizer/ir/visitor.inc"
private:
void AppendCatchBlock(uint32_t type_id, const compiler::BasicBlock *try_begin, const compiler::BasicBlock *try_end,
const compiler::BasicBlock *catch_begin, const compiler::BasicBlock *catch_end = nullptr);
void VisitTryBegin(const compiler::BasicBlock *bb);
private:
pandasm::Function *function_;
const BytecodeOptIrInterface *ir_interface_;
std::vector<pandasm::Ins> res_;
std::vector<pandasm::Function::CatchBlock> catch_blocks_;
bool success_ {true};
std::vector<pandasm::Ins> result_;
};
} // namespace panda::bytecodeopt
#endif // PANDA_BYTECODE_OPT_CODEGEN_H

View File

@ -0,0 +1,93 @@
/**
* Copyright (c) 2021-2022 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.
*/
#include "common.h"
#include "compiler/optimizer/ir/basicblock.h"
#include "compiler/optimizer/ir/graph.h"
namespace panda::bytecodeopt {
uint8_t AccReadIndex(const compiler::Inst *inst)
{
// For calls we cannot tell static index for acc position, thus
// ensure that we don't invoke this for calls
ASSERT(!inst->IsCall());
switch (inst->GetOpcode()) {
case compiler::Opcode::LoadArray:
case compiler::Opcode::StoreObject:
case compiler::Opcode::StoreStatic:
case compiler::Opcode::NewArray:
return 1U;
case compiler::Opcode::StoreArray:
return 2U;
default: {
if (inst->IsIntrinsic() && inst->IsAccRead()) {
ASSERT(inst->GetBasicBlock()->GetGraph()->IsDynamicMethod());
ASSERT(inst->GetInputsCount() >= 2U);
return inst->GetInputsCount() - 2U;
}
return 0;
}
}
}
// This method is used by bytecode optimizer's codegen.
bool CanConvertToIncI(const compiler::BinaryImmOperation *binop)
{
ASSERT(binop->GetBasicBlock()->GetGraph()->IsRegAllocApplied());
ASSERT(binop->GetOpcode() == compiler::Opcode::AddI || binop->GetOpcode() == compiler::Opcode::SubI);
// IncI works on the same register.
if (binop->GetSrcReg(0) != binop->GetDstReg()) {
return false;
}
// IncI cannot write accumulator.
if (binop->GetSrcReg(0) == compiler::ACC_REG_ID) {
return false;
}
// IncI users cannot read from accumulator.
// While Addi/SubI stores the output in accumulator, IncI works directly on registers.
for (const auto &user : binop->GetUsers()) {
const auto *uinst = user.GetInst();
if (uinst->IsCall()) {
continue;
}
const uint8_t index = AccReadIndex(uinst);
if (uinst->GetInput(index).GetInst() == binop && uinst->GetSrcReg(index) == compiler::ACC_REG_ID) {
return false;
}
}
constexpr uint64_t bitmask = 0xffffffff;
// Define min and max values of i4 type.
constexpr int32_t min = -8;
constexpr int32_t max = 7;
int32_t imm = binop->GetImm() & bitmask;
// Note: subi 3 is the same as inci v2, -3.
if (binop->GetOpcode() == compiler::Opcode::SubI) {
imm = -imm;
}
// IncI works only with 4 bits immediates.
return imm >= min && imm <= max;
}
} // namespace panda::bytecodeopt

View File

@ -0,0 +1,39 @@
/**
* Copyright (c) 2021-2022 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 PANDA_BYTECODE_OPTIMIZER_COMMON_H
#define PANDA_BYTECODE_OPTIMIZER_COMMON_H
#include "compiler/optimizer/ir/constants.h"
#include "compiler/optimizer/ir/inst.h"
namespace panda::compiler {
class BinaryImmOperation;
} // namespace panda::compiler
namespace panda::bytecodeopt {
static constexpr compiler::Register MIN_REGISTER_NUMBER = 0;
static constexpr compiler::Register MAX_NUM_SHORT_CALL_ARGS = 2;
static constexpr compiler::Register MAX_NUM_NON_RANGE_ARGS = 4;
static constexpr compiler::Register MAX_NUM_INPUTS = MAX_NUM_NON_RANGE_ARGS;
static constexpr panda::compiler::Register NUM_COMPACTLY_ENCODED_REGS = 16;
// Get the position where accumulator read happens.
uint8_t AccReadIndex(const compiler::Inst *inst);
bool CanConvertToIncI(const compiler::BinaryImmOperation *binop);
} // namespace panda::bytecodeopt
#endif // PANDA_BYTECODE_OPTIMIZER_COMMON_H

View File

@ -0,0 +1,335 @@
/**
* Copyright (c) 2021-2022 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.
*/
#include "assembler/assembly-literals.h"
#include "compiler/optimizer/ir/basicblock.h"
#include "compiler/optimizer/optimizations/peepholes.h"
#include "const_array_resolver.h"
namespace panda::bytecodeopt {
static constexpr size_t STOREARRAY_INPUTS_NUM = 3;
static constexpr size_t SINGLE_DIM_ARRAY_RANK = 1;
static constexpr size_t MIN_ARRAY_ELEMENTS_AMOUNT = 2;
bool ConstArrayResolver::RunImpl()
{
if (ir_interface_ == nullptr) {
return false;
}
if (!FindConstantArrays()) {
return false;
}
// delete instructions for storing array elements
RemoveArraysFill();
// replace old NewArray instructions with new SaveState + LoadConst instructions
InsertLoadConstArrayInsts();
return true;
}
static bool IsPatchAllowedOpcode(Opcode opcode)
{
switch (opcode) {
case Opcode::StoreArray:
case Opcode::LoadString:
case Opcode::Constant:
case Opcode::Cast:
case Opcode::SaveState:
return true;
default:
return false;
}
}
std::optional<std::vector<pandasm::LiteralArray::Literal>> ConstArrayResolver::FillLiteralArray(Inst *inst, size_t size)
{
std::vector<pandasm::LiteralArray::Literal> literals;
std::vector<Inst *> store_insts;
auto next = inst->GetNext();
size_t index = 0;
// are looking for instructions for uninterrupted filling the array
while ((next != nullptr) && (index < size)) {
// check whether the instruction is allowed inside the filling patch
if (!IsPatchAllowedOpcode(next->GetOpcode())) {
break;
}
// find instructions for storing array elements
if (next->GetOpcode() != Opcode::StoreArray) {
next = next->GetNext();
continue;
}
auto store_array_inst = next->CastToStoreArray();
if (store_array_inst->GetArray() != inst) {
break;
}
// create a literal from the array element, if possible
pandasm::LiteralArray::Literal literal;
if (!FillLiteral(store_array_inst, &literal)) {
// if not, then we can't create a constant literal array
return std::nullopt;
}
literals.push_back(literal);
store_insts.push_back(next);
index++;
next = next->GetNext();
}
// save the literal array only if it is completely filled
// or its size exceeds the minimum number of elements to save
if ((index < size) || (store_insts.size() < MIN_ARRAY_ELEMENTS_AMOUNT)) {
return std::nullopt;
}
// save the store instructions for deleting them later
const_arrays_fill_.emplace(inst, std::move(store_insts));
return std::optional<std::vector<pandasm::LiteralArray::Literal>> {std::move(literals)};
}
void ConstArrayResolver::AddIntroLiterals(pandasm::LiteralArray *lt_ar)
{
// add an element that stores the array size (it will be stored in the first element)
pandasm::LiteralArray::Literal len_lit;
len_lit.tag_ = panda_file::LiteralTag::INTEGER;
len_lit.value_ = static_cast<uint32_t>(lt_ar->literals_.size());
lt_ar->literals_.insert(lt_ar->literals_.begin(), len_lit);
// add an element that stores the array type (it will be stored in the zero element)
pandasm::LiteralArray::Literal tag_lit;
tag_lit.tag_ = panda_file::LiteralTag::TAGVALUE;
tag_lit.value_ = static_cast<uint8_t>(lt_ar->literals_.back().tag_);
lt_ar->literals_.insert(lt_ar->literals_.begin(), tag_lit);
}
bool ConstArrayResolver::IsMultidimensionalArray(compiler::NewArrayInst *inst)
{
auto array_type = pandasm::Type::FromName(ir_interface_->GetTypeIdByOffset(inst->GetTypeId()));
return array_type.GetRank() > SINGLE_DIM_ARRAY_RANK;
}
static bool IsSameBB(Inst *inst1, Inst *inst2)
{
return inst1->GetBasicBlock() == inst2->GetBasicBlock();
}
static bool IsSameBB(Inst *inst, compiler::BasicBlock *bb)
{
return inst->GetBasicBlock() == bb;
}
static std::optional<compiler::ConstantInst *> GetConstantIfPossible(Inst *inst)
{
if (inst->GetOpcode() == Opcode::Cast) {
auto input = inst->GetInput(0).GetInst();
if ((input->GetOpcode() == Opcode::NullPtr) || !input->IsConst()) {
return std::nullopt;
}
auto constant_inst = compiler::ConstFoldingCastConst(inst, input, true);
return constant_inst == nullptr ? std::nullopt : std::optional<compiler::ConstantInst *>(constant_inst);
}
if (inst->IsConst()) {
return std::optional<compiler::ConstantInst *>(inst->CastToConstant());
}
return std::nullopt;
}
bool ConstArrayResolver::FindConstantArrays()
{
size_t init_size = ir_interface_->GetLiteralArrayTableSize();
for (auto bb : GetGraph()->GetBlocksRPO()) {
// go through the instructions of the basic block in reverse order
// until we meet the instruction for storing an array element
auto inst = bb->GetLastInst();
while ((inst != nullptr) && IsSameBB(inst, bb)) {
if (inst->GetOpcode() != Opcode::StoreArray) {
inst = inst->GetPrev();
continue;
}
// the patch for creating and filling an array should start with the NewArray instruction
auto array_inst = inst->CastToStoreArray()->GetArray();
if (array_inst->GetOpcode() != Opcode::NewArray) {
inst = inst->GetPrev();
continue;
}
auto new_array_inst = array_inst->CastToNewArray();
// the instructions included in the patch must be in one basic block
if (!IsSameBB(inst, new_array_inst)) {
inst = inst->GetPrev();
continue;
}
// TODO(aantipina): add the ability to save multidimensional arrays
if (IsMultidimensionalArray(new_array_inst)) {
if (IsSameBB(inst, new_array_inst)) {
inst = new_array_inst->GetPrev();
} else {
inst = inst->GetPrev();
}
continue;
}
auto array_size_inst =
GetConstantIfPossible(new_array_inst->GetInput(compiler::NewArrayInst::INDEX_SIZE).GetInst());
if (array_size_inst == std::nullopt) {
inst = new_array_inst->GetPrev();
continue;
}
auto array_size = (*array_size_inst)->CastToConstant()->GetIntValue();
if (array_size < MIN_ARRAY_ELEMENTS_AMOUNT) {
inst = new_array_inst->GetPrev();
continue;
}
// creating a literal array, if possible
auto raw_literal_array = FillLiteralArray(new_array_inst, array_size);
if (raw_literal_array == std::nullopt) {
inst = new_array_inst->GetPrev();
continue;
}
pandasm::LiteralArray literal_array(*raw_literal_array);
// save the type and length of the array in the first two elements
AddIntroLiterals(&literal_array);
auto id = ir_interface_->GetLiteralArrayTableSize();
ir_interface_->StoreLiteralArray(std::to_string(id), std::move(literal_array));
// save the NewArray instructions for replacing them with LoadConst instructions later
const_arrays_init_.emplace(id, new_array_inst);
inst = new_array_inst->GetPrev();
}
}
// the pass worked if the size of the literal array table increased
return init_size < ir_interface_->GetLiteralArrayTableSize();
}
void ConstArrayResolver::RemoveArraysFill()
{
for (const auto &it : const_arrays_fill_) {
for (const auto &store_inst : it.second) {
store_inst->GetBasicBlock()->RemoveInst(store_inst);
}
}
}
void ConstArrayResolver::InsertLoadConstArrayInsts()
{
for (const auto &[id, start_inst] : const_arrays_init_) {
auto method = GetGraph()->GetMethod();
compiler::LoadConstArrayInst *new_inst = GetGraph()->CreateInstLoadConstArray(REFERENCE, start_inst->GetPc());
new_inst->SetTypeId(id);
new_inst->SetMethod(method);
start_inst->ReplaceUsers(new_inst);
start_inst->RemoveInputs();
compiler::SaveStateInst *save_state = GetGraph()->CreateInstSaveState();
save_state->SetPc(start_inst->GetPc());
save_state->SetMethod(method);
save_state->ReserveInputs(0);
new_inst->SetInput(0, save_state);
start_inst->InsertBefore(save_state);
start_inst->GetBasicBlock()->ReplaceInst(start_inst, new_inst);
}
}
static bool FillPrimitiveLiteral(pandasm::LiteralArray::Literal *literal, panda_file::Type::TypeId type,
compiler::ConstantInst *value_inst)
{
auto tag = pandasm::LiteralArray::GetArrayTagFromComponentType(type);
literal->tag_ = tag;
switch (tag) {
case panda_file::LiteralTag::ARRAY_U1:
literal->value_ = static_cast<bool>(value_inst->GetInt32Value());
return true;
case panda_file::LiteralTag::ARRAY_U8:
case panda_file::LiteralTag::ARRAY_I8:
literal->value_ = static_cast<uint8_t>(value_inst->GetInt32Value());
return true;
case panda_file::LiteralTag::ARRAY_U16:
case panda_file::LiteralTag::ARRAY_I16:
literal->value_ = static_cast<uint16_t>(value_inst->GetInt32Value());
return true;
case panda_file::LiteralTag::ARRAY_U32:
case panda_file::LiteralTag::ARRAY_I32:
literal->value_ = value_inst->GetInt32Value();
return true;
case panda_file::LiteralTag::ARRAY_U64:
case panda_file::LiteralTag::ARRAY_I64:
literal->value_ = value_inst->GetInt64Value();
return true;
case panda_file::LiteralTag::ARRAY_F32:
literal->value_ = value_inst->GetFloatValue();
return true;
case panda_file::LiteralTag::ARRAY_F64:
literal->value_ = value_inst->GetDoubleValue();
return true;
default:
UNREACHABLE();
}
return false;
}
bool ConstArrayResolver::FillLiteral(compiler::StoreInst *store_array_inst, pandasm::LiteralArray::Literal *literal)
{
if (store_array_inst->GetInputsCount() > STOREARRAY_INPUTS_NUM) {
return false;
}
auto raw_elem_inst = store_array_inst->GetStoredValue();
auto new_array_inst = store_array_inst->GetArray();
auto array_type =
pandasm::Type::FromName(ir_interface_->GetTypeIdByOffset(new_array_inst->CastToNewArray()->GetTypeId()));
auto component_type = array_type.GetComponentType();
auto component_type_name = array_type.GetComponentName();
if (pandasm::Type::IsPandaPrimitiveType(component_type_name)) {
auto value_inst = GetConstantIfPossible(raw_elem_inst);
if (value_inst == std::nullopt) {
return false;
}
return FillPrimitiveLiteral(literal, component_type.GetId(), *value_inst);
}
auto string_type =
pandasm::Type::FromDescriptor(panda::panda_file::GetStringClassDescriptor(ir_interface_->GetSourceLang()));
if ((raw_elem_inst->GetOpcode() == Opcode::LoadString) && (component_type_name == string_type.GetName())) {
literal->tag_ = panda_file::LiteralTag::ARRAY_STRING;
std::string string_value = ir_interface_->GetStringIdByOffset(raw_elem_inst->CastToLoadString()->GetTypeId());
literal->value_ = string_value;
return true;
}
return false;
}
} // namespace panda::bytecodeopt

View File

@ -0,0 +1,72 @@
/**
* Copyright (c) 2021-2022 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 PANDA_CONST_ARRAY_RESOLVER_H
#define PANDA_CONST_ARRAY_RESOLVER_H
#include "assembler/assembly-function.h"
#include "bytecodeopt_options.h"
#include "ir_interface.h"
#include "compiler/optimizer/ir/graph.h"
#include "compiler/optimizer/pass.h"
#include "compiler/optimizer/ir/inst.h"
#include "compiler/optimizer/optimizations/const_folding.h"
namespace panda::bytecodeopt {
using panda::compiler::Inst;
using panda::compiler::Opcode;
using namespace panda::compiler::DataType;
class ConstArrayResolver : public compiler::Optimization {
public:
explicit ConstArrayResolver(compiler::Graph *graph, BytecodeOptIrInterface *iface)
: compiler::Optimization(graph),
ir_interface_(iface),
const_arrays_init_(graph->GetLocalAllocator()->Adapter()),
const_arrays_fill_(graph->GetLocalAllocator()->Adapter())
{
}
~ConstArrayResolver() override = default;
bool RunImpl() override;
const char *GetPassName() const override
{
return "ConstArrayResolver";
}
bool IsEnable() const override
{
return options.IsConstArrayResolver();
}
private:
bool FindConstantArrays();
void RemoveArraysFill();
void InsertLoadConstArrayInsts();
std::optional<std::vector<pandasm::LiteralArray::Literal>> FillLiteralArray(Inst *inst, size_t size);
bool FillLiteral(compiler::StoreInst *store_array_inst, pandasm::LiteralArray::Literal *literal);
void AddIntroLiterals(pandasm::LiteralArray *lt_ar);
bool IsMultidimensionalArray(compiler::NewArrayInst *inst);
private:
BytecodeOptIrInterface *ir_interface_ {nullptr};
ArenaMap<uint32_t, compiler::NewArrayInst *> const_arrays_init_; // const_arrays_[literalarray_id] = new_array_inst
ArenaMap<Inst *, std::vector<Inst *>>
const_arrays_fill_; // const_arrays_[new_array_inst] = {store_array_inst_1, ... , store_array_inst_n}
};
} // namespace panda::bytecodeopt
#endif // PANDA_CONST_ARRAY_RESOLVER_H

View File

@ -0,0 +1,150 @@
/**
* Copyright (c) 2021-2022 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 PANDA_IR_INTERFACE_H
#define PANDA_IR_INTERFACE_H
#include <string>
#include "assembler/assembly-emitter.h"
#include "libpandafile/method_data_accessor-inl.h"
#include "compiler/optimizer/ir/constants.h"
namespace panda::bytecodeopt {
class BytecodeOptIrInterface {
public:
explicit BytecodeOptIrInterface(const pandasm::AsmEmitter::PandaFileToPandaAsmMaps *maps,
pandasm::Program *prog = nullptr)
: prog_(prog), maps_(maps)
{
}
virtual ~BytecodeOptIrInterface() = default;
virtual std::string GetMethodIdByOffset(uint32_t offset) const
{
auto it = maps_->methods.find(offset);
ASSERT(it != maps_->methods.cend());
return std::string(it->second);
}
virtual std::string GetStringIdByOffset(uint32_t offset) const
{
auto it = maps_->strings.find(offset);
ASSERT(it != maps_->strings.cend());
return std::string(it->second);
}
std::optional<std::string> GetLiteralArrayIdByOffset(uint32_t offset) const
{
ASSERT(prog_ != nullptr);
if (prog_ == nullptr) {
return std::nullopt;
}
auto id = std::to_string(offset);
auto it = prog_->literalarray_table.find(id);
ASSERT(it != prog_->literalarray_table.end());
return it != prog_->literalarray_table.end() ? std::optional<std::string>(id) : std::nullopt;
}
virtual std::string GetTypeIdByOffset(uint32_t offset) const
{
auto it = maps_->classes.find(offset);
ASSERT(it != maps_->classes.cend());
return std::string(it->second);
}
virtual std::string GetFieldIdByOffset(uint32_t offset) const
{
auto it = maps_->fields.find(offset);
ASSERT(it != maps_->fields.cend());
return std::string(it->second);
}
std::unordered_map<size_t, pandasm::Ins *> *GetPcInsMap()
{
return &pc_ins_map_;
}
size_t GetLineNumberByPc(size_t pc) const
{
if (pc == compiler::INVALID_PC || pc_ins_map_.size() == 0) {
return 0;
}
auto iter = pc_ins_map_.find(pc);
if (iter == pc_ins_map_.end()) {
return 0;
}
return iter->second->ins_debug.line_number;
}
uint32_t GetColumnNumberByPc(size_t pc) const
{
if (pc == compiler::INVALID_PC || pc_ins_map_.size() == 0) {
return compiler::INVALID_COLUMN_NUM;
}
auto iter = pc_ins_map_.find(pc);
if (iter == pc_ins_map_.end()) {
return compiler::INVALID_COLUMN_NUM;
}
return iter->second->ins_debug.column_number;
}
void ClearPcInsMap()
{
pc_ins_map_.clear();
}
void StoreLiteralArray(std::string id, pandasm::LiteralArray &&literalarray)
{
ASSERT(prog_ != nullptr);
if (prog_ == nullptr) {
return;
}
prog_->literalarray_table.emplace(id, std::move(literalarray));
}
size_t GetLiteralArrayTableSize() const
{
ASSERT(prog_ != nullptr);
if (prog_ == nullptr) {
return 0;
}
return prog_->literalarray_table.size();
}
bool IsMapsSet() const
{
return maps_ != nullptr;
}
panda_file::SourceLang GetSourceLang()
{
return prog_ != nullptr ? prog_->lang : panda_file::SourceLang::PANDA_ASSEMBLY;
}
private:
pandasm::Program *prog_ {nullptr};
const pandasm::AsmEmitter::PandaFileToPandaAsmMaps *maps_ {nullptr};
std::unordered_map<size_t, pandasm::Ins *> pc_ins_map_;
};
} // namespace panda::bytecodeopt
#endif // PANDA_IR_INTERFACE_H

View File

@ -0,0 +1,369 @@
/**
* Copyright (c) 2021-2022 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.
*/
#include "optimize_bytecode.h"
#include "assembler/assembly-emitter.h"
#include "assembler/extensions/extensions.h"
#include "bytecode_instruction.h"
#include "bytecodeopt_options.h"
#include "bytecodeopt_peepholes.h"
#include "canonicalization.h"
#include "check_resolver.h"
#include "codegen.h"
#include "common.h"
#include "const_array_resolver.h"
#include "compiler/optimizer/ir/constants.h"
#include "compiler/optimizer/ir_builder/ir_builder.h"
#include "compiler/optimizer/ir_builder/pbc_iterator.h"
#include "compiler/optimizer/optimizations/branch_elimination.h"
#include "compiler/optimizer/optimizations/code_sink.h"
#include "compiler/optimizer/optimizations/cse.h"
#include "compiler/optimizer/optimizations/cleanup.h"
#include "compiler/optimizer/optimizations/licm.h"
#include "compiler/optimizer/optimizations/lowering.h"
#include "compiler/optimizer/optimizations/lse.h"
#include "compiler/optimizer/optimizations/move_constants.h"
#include "compiler/optimizer/optimizations/peepholes.h"
#include "compiler/optimizer/optimizations/regalloc/reg_alloc.h"
#include "compiler/optimizer/optimizations/vn.h"
#include "libpandabase/mem/arena_allocator.h"
#include "libpandabase/mem/pool_manager.h"
#include "libpandafile/class_data_accessor.h"
#include "libpandafile/class_data_accessor-inl.h"
#include "libpandafile/method_data_accessor.h"
#include "reg_acc_alloc.h"
#include "reg_encoder.h"
#include "runtime_adapter.h"
#include <regex>
namespace panda::bytecodeopt {
// NOLINTNEXTLINE(fuchsia-statically-constructed-objects)
panda::bytecodeopt::Options options("");
template <typename T>
constexpr void RunOpts(compiler::Graph *graph, [[maybe_unused]] BytecodeOptIrInterface *iface)
{
graph->RunPass<compiler::Cleanup>();
#ifndef NDEBUG
// NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon)
if constexpr (std::is_same_v<T, compiler::Lowering>) {
graph->SetLowLevelInstructionsEnabled();
}
#endif // NDEBUG
// NOLINTNEXTLINE(readability-braces-around-statements, readability-misleading-indentation)
if constexpr (std::is_same_v<T, compiler::Licm>) {
graph->RunPass<compiler::Licm>(compiler::options.GetCompilerLicmHoistLimit());
// NOLINTNEXTLINE(readability-braces-around-statements, readability-misleading-indentation)
} else if constexpr (std::is_same_v<T, ConstArrayResolver>) {
graph->RunPass<ConstArrayResolver>(iface);
// NOLINTNEXTLINE(readability-misleading-indentation)
} else {
graph->RunPass<T>();
}
}
template <typename First, typename Second, typename... Rest>
constexpr void RunOpts(compiler::Graph *graph, BytecodeOptIrInterface *iface = nullptr)
{
RunOpts<First>(graph, iface);
RunOpts<Second, Rest...>(graph, iface);
}
bool RunOptimizations(compiler::Graph *graph, BytecodeOptIrInterface *iface)
{
constexpr int OPT_LEVEL_0 = 0;
constexpr int OPT_LEVEL_1 = 1;
constexpr int OPT_LEVEL_2 = 2;
if (panda::bytecodeopt::options.GetOptLevel() == OPT_LEVEL_0) {
return false;
}
graph->RunPass<CheckResolver>();
graph->RunPass<compiler::Cleanup>();
// NB! Canonicalization and compiler::Lowering should be present in all levels
// since without Lowering pass, RegEncoder will not work for Compare instructions,
// and we will not be able to optimize functions where there is branching.
// Lowering can't work without Canonicalization pass.
if (graph->IsDynamicMethod()) {
RunOpts<compiler::ValNum, compiler::Lowering, compiler::MoveConstants>(graph);
} else if (panda::bytecodeopt::options.GetOptLevel() == OPT_LEVEL_1) {
RunOpts<Canonicalization, compiler::Lowering>(graph);
} else if (panda::bytecodeopt::options.GetOptLevel() == OPT_LEVEL_2) {
// ConstArrayResolver Pass is disabled as it requires fixes for stability
RunOpts<ConstArrayResolver, compiler::BranchElimination, compiler::ValNum, compiler::Cse, compiler::Peepholes,
compiler::Licm, compiler::Lse, compiler::ValNum, compiler::Cse, Canonicalization, compiler::Lowering,
compiler::MoveConstants, BytecodeOptPeepholes>(graph, iface);
} else {
UNREACHABLE();
}
// this pass should run just before register allocator
graph->RunPass<compiler::Cleanup>();
graph->RunPass<RegAccAlloc>();
graph->RunPass<compiler::Cleanup>();
if (!RegAlloc(graph)) {
LOG(ERROR, BYTECODE_OPTIMIZER) << "Failed compiler::RegAlloc";
return false;
}
graph->RunPass<compiler::Cleanup>();
if (!graph->RunPass<RegEncoder>()) {
LOG(ERROR, BYTECODE_OPTIMIZER) << "Failed RegEncoder";
return false;
}
return true;
}
void BuildMapFromPcToIns(pandasm::Function &function, BytecodeOptIrInterface &ir_interface,
const compiler::Graph *graph, compiler::RuntimeInterface::MethodPtr method_ptr)
{
function.local_variable_debug.clear();
auto *pc_ins_map = ir_interface.GetPcInsMap();
pc_ins_map->reserve(function.ins.size());
auto instructions_buf = graph->GetRuntime()->GetMethodCode(method_ptr);
compiler::BytecodeInstructions instructions(instructions_buf, graph->GetRuntime()->GetMethodCodeSize(method_ptr));
size_t idx = 0;
for (auto insn : instructions) {
pandasm::Ins &ins = function.ins[idx++];
pc_ins_map->emplace(instructions.GetPc(insn), &ins);
if (idx >= function.ins.size()) {
break;
}
}
}
static void ColumnNumberPropagate(pandasm::Function *function)
{
auto &ins_vec = function->ins;
uint32_t cn = compiler::INVALID_COLUMN_NUM;
// handle the instructions that are at the beginning of code but do not have column number
size_t k = 0;
while (k < ins_vec.size() && cn == compiler::INVALID_COLUMN_NUM) {
cn = ins_vec[k++].ins_debug.column_number;
}
if (cn == compiler::INVALID_COLUMN_NUM) {
LOG(DEBUG, BYTECODE_OPTIMIZER) << "Failed ColumnNumberPropagate: All insts have invalid column number";
return;
}
for (size_t j = 0; j < k - 1; j++) {
ins_vec[j].ins_debug.SetColumnNumber(cn);
}
// handle other instructions that do not have column number
for (; k < ins_vec.size(); k++) {
if (ins_vec[k].ins_debug.column_number != compiler::INVALID_COLUMN_NUM) {
cn = ins_vec[k].ins_debug.column_number;
} else {
ins_vec[k].ins_debug.SetColumnNumber(cn);
}
}
}
static void LineNumberPropagate(pandasm::Function *function)
{
if (function == nullptr || function->ins.empty()) {
return;
}
size_t ln = 0;
auto &ins_vec = function->ins;
// handle the instructions that are at the beginning of code but do not have line number
size_t i = 0;
while (i < ins_vec.size() && ln == 0) {
ln = ins_vec[i++].ins_debug.line_number;
}
if (ln == 0) {
LOG(DEBUG, BYTECODE_OPTIMIZER) << "Failed LineNumberPropagate: All insts have invalid line number";
return;
}
for (size_t j = 0; j < i - 1; j++) {
ins_vec[j].ins_debug.SetLineNumber(ln);
}
// handle other instructions that do not have line number
for (; i < ins_vec.size(); i++) {
if (ins_vec[i].ins_debug.line_number != 0) {
ln = ins_vec[i].ins_debug.line_number;
} else {
ins_vec[i].ins_debug.SetLineNumber(ln);
}
}
}
static void DebugInfoPropagate(pandasm::Function &function, const compiler::Graph *graph,
BytecodeOptIrInterface &ir_interface)
{
LineNumberPropagate(&function);
if (graph->IsDynamicMethod()) {
ColumnNumberPropagate(&function);
}
ir_interface.ClearPcInsMap();
}
static bool SkipFunction(const pandasm::Function &function, const std::string &func_name)
{
if (panda::bytecodeopt::options.WasSetMethodRegex()) {
static std::regex rgx(panda::bytecodeopt::options.GetMethodRegex());
if (!std::regex_match(func_name, rgx)) {
LOG(INFO, BYTECODE_OPTIMIZER) << "Skip Function " << func_name << ": Function's name doesn't match regex";
return true;
}
}
if (panda::bytecodeopt::options.IsSkipMethodsWithEh() && !function.catch_blocks.empty()) {
LOG(INFO, BYTECODE_OPTIMIZER) << "Was not optimized " << func_name << ": Function has catch blocks";
return true;
}
if ((function.regs_num + function.GetParamsNum()) > compiler::VIRTUAL_FRAME_SIZE) {
LOG(ERROR, BYTECODE_OPTIMIZER) << "Unable to optimize " << func_name
<< ": Function frame size is larger than allowed one";
return true;
}
return false;
}
static void SetCompilerOptions(bool is_dynamic)
{
compiler::options.SetCompilerUseSafepoint(false);
compiler::options.SetCompilerSupportInitObjectInst(true);
if (!compiler::options.WasSetCompilerMaxBytecodeSize()) {
compiler::options.SetCompilerMaxBytecodeSize(~0U);
}
if (is_dynamic) {
panda::bytecodeopt::options.SetSkipMethodsWithEh(true);
}
}
bool OptimizeFunction(pandasm::Program *prog, const pandasm::AsmEmitter::PandaFileToPandaAsmMaps *maps,
const panda_file::MethodDataAccessor &mda, bool is_dynamic)
{
ArenaAllocator allocator {SpaceType::SPACE_TYPE_COMPILER};
ArenaAllocator local_allocator {SpaceType::SPACE_TYPE_COMPILER, nullptr, true};
SetCompilerOptions(is_dynamic);
auto ir_interface = BytecodeOptIrInterface(maps, prog);
auto func_name = ir_interface.GetMethodIdByOffset(mda.GetMethodId().GetOffset());
LOG(INFO, BYTECODE_OPTIMIZER) << "Optimizing function: " << func_name;
auto it = prog->function_table.find(func_name);
if (it == prog->function_table.end()) {
LOG(ERROR, BYTECODE_OPTIMIZER) << "Cannot find function: " << func_name;
return false;
}
auto method_ptr = reinterpret_cast<compiler::RuntimeInterface::MethodPtr>(mda.GetMethodId().GetOffset());
panda::BytecodeOptimizerRuntimeAdapter adapter(mda.GetPandaFile());
auto graph = allocator.New<compiler::Graph>(&allocator, &local_allocator, Arch::NONE, method_ptr, &adapter, false,
nullptr, is_dynamic, true);
panda::pandasm::Function &function = it->second;
if (SkipFunction(function, func_name)) {
return false;
}
// build map from pc to pandasm::ins (to re-build line-number info in BytecodeGen)
BuildMapFromPcToIns(function, ir_interface, graph, method_ptr);
if ((graph == nullptr) || !graph->RunPass<panda::compiler::IrBuilder>()) {
LOG(ERROR, BYTECODE_OPTIMIZER) << "Optimizing " << func_name << ": IR builder failed!";
return false;
}
if (graph->HasIrreducibleLoop()) {
LOG(ERROR, BYTECODE_OPTIMIZER) << "Optimizing " << func_name << ": Graph has irreducible loop!";
return false;
}
if (!RunOptimizations(graph, &ir_interface)) {
LOG(ERROR, BYTECODE_OPTIMIZER) << "Optimizing " << func_name << ": Running optimizations failed!";
return false;
}
if (!graph->RunPass<BytecodeGen>(&function, &ir_interface)) {
LOG(ERROR, BYTECODE_OPTIMIZER) << "Optimizing " << func_name << ": Code generation failed!";
return false;
}
DebugInfoPropagate(function, graph, ir_interface);
function.value_of_first_param =
static_cast<int64_t>(graph->GetStackSlotsCount()) - 1; // Work-around promotion rules
function.regs_num = static_cast<size_t>(function.value_of_first_param + 1);
if (auto frame_size = function.regs_num + function.GetParamsNum(); frame_size >= NUM_COMPACTLY_ENCODED_REGS) {
LOG(INFO, BYTECODE_OPTIMIZER) << "Function " << func_name << " has frame size " << frame_size;
}
LOG(DEBUG, BYTECODE_OPTIMIZER) << "Optimized " << func_name;
return true;
}
bool OptimizePandaFile(pandasm::Program *prog, const pandasm::AsmEmitter::PandaFileToPandaAsmMaps *maps,
const std::string &pfile_name, bool is_dynamic)
{
auto pfile = panda_file::OpenPandaFile(pfile_name);
if (!pfile) {
LOG(FATAL, BYTECODE_OPTIMIZER) << "Can not open binary file: " << pfile_name;
}
bool result = true;
for (uint32_t id : pfile->GetClasses()) {
panda_file::File::EntityId record_id {id};
if (pfile->IsExternal(record_id)) {
continue;
}
panda_file::ClassDataAccessor cda {*pfile, record_id};
cda.EnumerateMethods([prog, maps, is_dynamic, &result](panda_file::MethodDataAccessor &mda) {
if (!mda.IsExternal() && !mda.IsAbstract() && !mda.IsNative()) {
result = OptimizeFunction(prog, maps, mda, is_dynamic) && result;
}
});
}
return result;
}
bool OptimizeBytecode(pandasm::Program *prog, const pandasm::AsmEmitter::PandaFileToPandaAsmMaps *maps,
const std::string &pandafile_name, bool is_dynamic, bool has_memory_pool)
{
ASSERT(prog != nullptr);
ASSERT(maps != nullptr);
if (!has_memory_pool) {
PoolManager::Initialize(PoolType::MALLOC);
}
auto res = OptimizePandaFile(prog, maps, pandafile_name, is_dynamic);
if (!has_memory_pool) {
PoolManager::Finalize();
}
return res;
}
} // namespace panda::bytecodeopt

View File

@ -0,0 +1,30 @@
/**
* Copyright (c) 2021-2022 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 PANDA_BYTECODE_OPTIMIZER_OPTIMIZE_BYTECODE_H_
#define PANDA_BYTECODE_OPTIMIZER_OPTIMIZE_BYTECODE_H_
#include "assembler/assembly-emitter.h"
#include "assembler/assembly-program.h"
#include "compiler/optimizer/ir/graph.h"
#include "ir_interface.h"
namespace panda::bytecodeopt {
bool RunOptimizations(compiler::Graph *graph, BytecodeOptIrInterface *iface = nullptr);
bool OptimizeBytecode(pandasm::Program *prog, const pandasm::AsmEmitter::PandaFileToPandaAsmMaps *maps,
const std::string &pandafile_name, bool is_dynamic = false, bool has_memory_pool = false);
} // namespace panda::bytecodeopt
#endif // PANDA_BYTECODE_OPTIMIZER_OPTIMIZE_BYTECODE_H_

View File

@ -0,0 +1,58 @@
# Copyright (c) 2021-2022 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.
module:
name: bytecodeopt
namespace: panda::bytecodeopt
options:
- name: opt-level
type: int
default: 2
possible_values:
- 0
- 1
- 2
description: |
Optimization level for bytecode optimizer. N=0: No optimizations. N=1: New optimizer is turned on, only DCE is applied. N=2: (default): New optimizer is turned on, all compiler's optimizations are turned on.
- name: canonicalization
type: bool
default: true
description: Enable Canonicalization Pass
- name: const-resolver
type: bool
default: true
description: Enable Constant Resolver Pass
- name: method-regex
type: std::string
default: ""
description: A regular expression that specifies methods to optimize
- name: skip-methods-with-eh
type: bool
default: false
description: Disable optimizer for methods with exceptions handlers
- name: bytecode-opt-peepholes
type: bool
default: true
description: Enable BytecodeOptPeepholes Pass
- name: const-array-resolver
type: bool
default: true
description: Enable ConstArray Resolver Pass

View File

@ -0,0 +1,329 @@
/**
* Copyright (c) 2021-2022 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.
*/
#include "reg_acc_alloc.h"
#include "common.h"
#include "compiler/optimizer/ir/basicblock.h"
#include "compiler/optimizer/ir/inst.h"
namespace panda::bytecodeopt {
/**
* Decide if accumulator register gets dirty between two instructions.
*/
bool IsAccWriteBetween(compiler::Inst *src_inst, compiler::Inst *dst_inst)
{
ASSERT(src_inst != dst_inst);
compiler::BasicBlock *block = src_inst->GetBasicBlock();
compiler::Inst *inst = src_inst->GetNext();
while (inst != dst_inst) {
if (UNLIKELY(inst == nullptr)) {
do {
// TODO(rtakacs): visit all the successors to get information about the
// accumulator usage. Only linear flow is supported right now.
if (block->GetSuccsBlocks().size() > 1) {
return true;
}
ASSERT(block->GetSuccsBlocks().size() == 1);
block = block->GetSuccessor(0);
// TODO(rtakacs): only linear flow is supported right now.
if (!dst_inst->IsPhi() && block->GetPredsBlocks().size() > 1) {
return true;
}
} while (block->IsEmpty() && !block->HasPhi());
// Get first phi instruction if exist.
// This is requred if dst_inst is a phi node.
inst = *(block->AllInsts());
} else {
if (inst->IsAccWrite()) {
return true;
}
if (inst->IsAccRead()) {
compiler::Inst *input = inst->GetInput(AccReadIndex(inst)).GetInst();
if (input->GetDstReg() != compiler::ACC_REG_ID) {
return true;
}
}
inst = inst->GetNext();
}
}
return false;
}
/**
* Return true if Phi instruction is marked as optimizable.
*/
inline bool RegAccAlloc::IsPhiOptimizable(compiler::Inst *phi) const
{
ASSERT(phi->GetOpcode() == compiler::Opcode::Phi);
return phi->IsMarked(acc_marker_);
}
/**
* Return true if instruction can read the accumulator.
*/
bool RegAccAlloc::IsAccRead(compiler::Inst *inst) const
{
return UNLIKELY(inst->IsPhi()) ? IsPhiOptimizable(inst) : inst->IsAccRead();
}
bool UserNeedSwapInputs(compiler::Inst *inst, compiler::Inst *user)
{
if (!user->IsCommutative()) {
return false;
}
return user->GetInput(AccReadIndex(user)).GetInst() != inst;
}
/**
* Return true if instruction can write the accumulator.
*/
bool RegAccAlloc::IsAccWrite(compiler::Inst *inst) const
{
return UNLIKELY(inst->IsPhi()) ? IsPhiOptimizable(inst) : inst->IsAccWrite();
}
/**
* Decide if user can use accumulator as source.
* Do modifications on the order of inputs if necessary.
*
* Return true, if user can be optimized.
*/
bool RegAccAlloc::CanUserReadAcc(compiler::Inst *inst, compiler::Inst *user) const
{
if (user->IsPhi()) {
return IsPhiOptimizable(user);
}
if (!IsAccRead(user) || IsAccWriteBetween(inst, user)) {
return false;
}
bool found = false;
// Check if the instrucion occures more times as input.
// v2. SUB v0, v1
// v3. Add v2, v2
for (auto input : user->GetInputs()) {
compiler::Inst *uinput = input.GetInst();
if (uinput != inst) {
continue;
}
if (!found) {
found = true;
} else {
return false;
}
}
if (user->IsCall()) {
return user->GetInputsCount() <= (MAX_NUM_NON_RANGE_ARGS + 1); // +1 for SaveState
}
return user->GetInput(AccReadIndex(user)).GetInst() == inst || user->IsCommutative();
}
/**
* Check if all the Phi inputs and outputs can use the accumulator register.
*
* Return true, if Phi can be optimized.
*/
bool RegAccAlloc::IsPhiAccReady(compiler::Inst *phi) const
{
ASSERT(phi->GetOpcode() == compiler::Opcode::Phi);
// TODO(rtakacs): there can be cases when the input/output of a Phi is an other Phi.
// These cases are not optimized for accumulator.
for (auto input : phi->GetInputs()) {
compiler::Inst *phi_input = input.GetInst();
if (!IsAccWrite(phi_input) || IsAccWriteBetween(phi_input, phi)) {
return false;
}
}
std::unordered_set<compiler::Inst *> users_that_required_swap_inputs;
for (auto &user : phi->GetUsers()) {
compiler::Inst *uinst = user.GetInst();
if (!CanUserReadAcc(phi, uinst)) {
return false;
}
if (UserNeedSwapInputs(phi, uinst)) {
users_that_required_swap_inputs.insert(uinst);
}
}
for (auto uinst : users_that_required_swap_inputs) {
uinst->SwapInputs();
}
return true;
}
/**
* For most insts we can use their src_reg on the acc-read position
* to characterise whether we need lda in the codegen pass.
*/
void RegAccAlloc::SetNeedLda(compiler::Inst *inst, bool need)
{
if (inst->IsPhi() || inst->IsCatchPhi()) {
return;
}
if (!IsAccRead(inst)) {
return;
}
if (inst->IsCall()) { // we never need lda for calls
return;
}
compiler::Register reg = need ? compiler::INVALID_REG : compiler::ACC_REG_ID;
inst->SetSrcReg(AccReadIndex(inst), reg);
}
/**
* Determine the accumulator usage between instructions.
* Eliminate unnecessary register allocations by applying
* a special value (ACC_REG_ID) to the destination and
* source registers.
* This special value is a marker for the code generator
* not to produce lda/sta instructions.
*/
bool RegAccAlloc::RunImpl()
{
GetGraph()->InitDefaultLocations();
// Initialize all source register of all instructions.
for (auto block : GetGraph()->GetBlocksRPO()) {
for (auto inst : block->Insts()) {
if (inst->IsSaveState() || inst->IsCatchPhi()) {
continue;
}
if (inst->IsConst()) {
inst->SetFlag(compiler::inst_flags::ACC_WRITE);
}
for (size_t i = 0; i < inst->GetInputsCount(); ++i) {
inst->SetSrcReg(i, compiler::INVALID_REG);
if ((inst->GetOpcode() == compiler::Opcode::LoadObject) || (inst->IsConst())) {
inst->SetDstReg(compiler::INVALID_REG);
}
}
}
}
// Drop the pass if the function contains unsupported opcodes
// TODO(rtakacs): support these opcodes.
if (!GetGraph()->IsDynamicMethod()) {
for (auto block : GetGraph()->GetBlocksRPO()) {
for (auto inst : block->AllInsts()) {
if (inst->GetOpcode() == compiler::Opcode::Builtin) {
return false;
}
}
}
}
// Mark Phi instructions if they can be optimized for acc.
for (auto block : GetGraph()->GetBlocksRPO()) {
for (auto phi : block->PhiInsts()) {
if (IsPhiAccReady(phi)) {
phi->SetMarker(acc_marker_);
}
}
}
// Mark instructions if they can be optimized for acc.
for (auto block : GetGraph()->GetBlocksRPO()) {
for (auto inst : block->AllInsts()) {
if (inst->NoDest() || !IsAccWrite(inst)) {
continue;
}
bool use_acc_dst_reg = true;
std::unordered_set<compiler::Inst *> users_that_required_swap_inputs;
for (auto &user : inst->GetUsers()) {
compiler::Inst *uinst = user.GetInst();
if (uinst->IsSaveState()) {
continue;
}
if (CanUserReadAcc(inst, uinst)) {
if (UserNeedSwapInputs(inst, uinst)) {
users_that_required_swap_inputs.insert(uinst);
}
SetNeedLda(uinst, false);
} else {
use_acc_dst_reg = false;
}
}
for (auto uinst : users_that_required_swap_inputs) {
uinst->SwapInputs();
}
if (use_acc_dst_reg) {
inst->SetDstReg(compiler::ACC_REG_ID);
} else if ((inst->GetOpcode() == compiler::Opcode::LoadObject) || inst->IsConst()) {
inst->ClearFlag(compiler::inst_flags::ACC_WRITE);
for (auto &user : inst->GetUsers()) {
compiler::Inst *uinst = user.GetInst();
if (uinst->IsSaveState()) {
continue;
}
SetNeedLda(uinst, true);
}
}
}
for (auto inst : block->Insts()) {
if (inst->GetInputsCount() == 0) {
continue;
}
if (inst->IsCall()) {
continue;
}
compiler::Inst *input = inst->GetInput(AccReadIndex(inst)).GetInst();
if (IsAccWriteBetween(input, inst)) {
input->SetDstReg(compiler::INVALID_REG);
SetNeedLda(inst, true);
if ((input->GetOpcode() == compiler::Opcode::LoadObject) || (input->IsConst())) {
input->ClearFlag(compiler::inst_flags::ACC_WRITE);
for (auto &user : input->GetUsers()) {
compiler::Inst *uinst = user.GetInst();
SetNeedLda(uinst, true);
}
}
}
}
}
#ifndef NDEBUG
GetGraph()->SetRegAccAllocApplied();
#endif // NDEBUG
return true;
}
} // namespace panda::bytecodeopt

View File

@ -0,0 +1,59 @@
/**
* Copyright (c) 2021-2022 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 PANDA_REG_ACC_ALLOC_H_
#define PANDA_REG_ACC_ALLOC_H_
#include "optimizer/ir/graph.h"
#include "optimizer/pass.h"
#include "compiler_options.h"
namespace panda::bytecodeopt {
class RegAccAlloc : public compiler::Optimization {
using Optimization::Optimization;
public:
explicit RegAccAlloc(compiler::Graph *graph) : compiler::Optimization(graph), acc_marker_(graph->NewMarker()) {};
~RegAccAlloc() override = default;
bool IsEnable() const override
{
return compiler::options.IsCompilerRegAccAlloc();
}
const char *GetPassName() const override
{
return "RegAccAlloc";
}
bool RunImpl() override;
private:
bool IsPhiOptimizable(compiler::Inst *phi) const;
bool IsAccRead(compiler::Inst *inst) const;
bool IsAccWrite(compiler::Inst *inst) const;
bool CanUserReadAcc(compiler::Inst *inst, compiler::Inst *user) const;
bool IsPhiAccReady(compiler::Inst *phi) const;
void SetNeedLda(compiler::Inst *inst, bool need);
compiler::Marker acc_marker_ {0};
};
} // namespace panda::bytecodeopt
#endif // PANDA_REG_ACC_ALLOC_H_

View File

@ -0,0 +1,639 @@
/**
* Copyright (c) 2021-2022 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.
*/
#include "reg_encoder.h"
#include "common.h"
#include "compiler/optimizer/ir/basicblock.h"
namespace panda::bytecodeopt {
static bool IsIntrinsicRange(Inst *inst)
{
if (inst->GetOpcode() != compiler::Opcode::Intrinsic) {
return false;
}
#ifdef ENABLE_BYTECODE_OPT
switch (inst->CastToIntrinsic()->GetIntrinsicId()) {
#ifdef ARK_INTRINSIC_SET
case compiler::RuntimeInterface::IntrinsicId::INTRINSIC_CALLI_RANGE_DYN:
case compiler::RuntimeInterface::IntrinsicId::INTRINSIC_CALLI_THIS_RANGE_DYN:
case compiler::RuntimeInterface::IntrinsicId::INTRINSIC_NEWOBJ_DYNRANGE:
case compiler::RuntimeInterface::IntrinsicId::INTRINSIC_SUPER_CALL:
#else
case compiler::RuntimeInterface::IntrinsicId::ECMA_CALLIRANGEDYN_PREF_IMM16_V8:
case compiler::RuntimeInterface::IntrinsicId::ECMA_CALLITHISRANGEDYN_PREF_IMM16_V8:
case compiler::RuntimeInterface::IntrinsicId::ECMA_NEWOBJDYNRANGE_PREF_IMM16_V8:
case compiler::RuntimeInterface::IntrinsicId::ECMA_SUPERCALL_PREF_IMM16_V8:
case compiler::RuntimeInterface::IntrinsicId::ECMA_CREATEOBJECTWITHEXCLUDEDKEYS_PREF_IMM16_V8_V8:
#endif
return true;
default:
return false;
}
#endif
return false;
}
static bool CanHoldRange(Inst *inst)
{
switch (inst->GetOpcode()) {
case compiler::Opcode::CallStatic:
case compiler::Opcode::CallVirtual:
case compiler::Opcode::InitObject:
return true;
case compiler::Opcode::Intrinsic:
return IsIntrinsicRange(inst);
default:
return false;
}
}
static compiler::Register CalculateNumNeededRangeTemps(const compiler::Graph *graph)
{
compiler::Register ret = 0;
for (auto bb : graph->GetBlocksRPO()) {
for (const auto &inst : bb->AllInsts()) {
if (!CanHoldRange(inst)) {
continue;
}
auto nargs = inst->GetInputsCount() - (inst->RequireState() ? 1 : 0);
if (inst->GetOpcode() == compiler::Opcode::InitObject) {
ASSERT(nargs > 0);
nargs -= 1; // exclude LoadAndInitClass
}
if (ret < nargs) {
if (nargs > MAX_NUM_NON_RANGE_ARGS || IsIntrinsicRange(inst)) {
ret = nargs;
}
}
}
}
return ret;
}
bool RegEncoder::RunImpl()
{
ASSERT(state_ == RegEncoderState::IDLE);
num_max_range_input_ = CalculateNumNeededRangeTemps(GetGraph());
state_ = RegEncoderState::RENUMBER_ARGS;
if (!RenumberArgRegs()) {
return false;
}
state_ = RegEncoderState::RESERVE_TEMPS;
ASSERT(num_temps_ == 0);
const auto num_regs = GetNumRegs();
auto max_num_temps = num_temps_;
CalculateNumNeededTemps();
while (max_num_temps != num_temps_) {
ASSERT(num_temps_ > max_num_temps);
if (num_regs > compiler::VIRTUAL_FRAME_SIZE - num_temps_) { // to avoid overflow
return false; // no more free registers left in the frame
}
auto delta = static_cast<compiler::Register>(num_temps_ - max_num_temps);
range_temps_start_ += delta;
RenumberRegs(MIN_REGISTER_NUMBER, delta);
max_num_temps = num_temps_;
CalculateNumNeededTemps();
}
if (num_temps_ > 0 || num_max_range_input_ > 0) {
state_ = RegEncoderState::INSERT_SPILLS;
InsertSpills();
auto usage_mask = GetGraph()->GetUsedRegs<compiler::DataType::INT64>();
for (compiler::Register r = 0; r < num_regs; r++) {
usage_mask->at(num_regs + num_temps_ - r - 1) = usage_mask->at(num_regs - r - 1);
}
std::fill(usage_mask->begin(), usage_mask->begin() + num_temps_, true);
}
SaveNumLocalsToGraph(GetNumLocalsFromGraph() + num_temps_);
state_ = RegEncoderState::IDLE;
return true;
}
static panda::compiler::DataType::Type GetRegType(panda::compiler::DataType::Type type)
{
if (type == panda::compiler::DataType::REFERENCE) {
return type;
}
if (panda::compiler::DataType::Is32Bits(type, Arch::NONE)) {
return panda::compiler::DataType::UINT32;
}
return panda::compiler::DataType::UINT64;
}
static bool RegNeedsRenumbering(panda::compiler::Register r)
{
return r != panda::compiler::ACC_REG_ID && r != panda::compiler::INVALID_REG;
}
static panda::compiler::Register RenumberReg(const panda::compiler::Register r, const panda::compiler::Register delta)
{
if (r == panda::compiler::ACC_REG_ID) {
return r;
}
return r + delta;
}
static void RenumberSpillFillRegs(panda::compiler::SpillFillInst *inst, const panda::compiler::Register min_reg,
const panda::compiler::Register delta)
{
for (auto &sf : inst->GetSpillFills()) {
if (sf.SrcType() == compiler::LocationType::REGISTER && sf.SrcValue() >= min_reg) {
sf.SetSrc(compiler::Location::MakeRegister(RenumberReg(sf.SrcValue(), delta)));
}
if (sf.DstType() == compiler::LocationType::REGISTER && sf.DstValue() >= min_reg) {
sf.SetDst(compiler::Location::MakeRegister(RenumberReg(sf.DstValue(), delta)));
}
}
}
void RegEncoder::RenumberRegs(const compiler::Register min_reg, const compiler::Register delta)
{
// Renumbering always advances register number `delta` positions forward,
// wrapping around on overflows with well-defined behavour.
// Hence the requirement to keep delta unsigned.
static_assert(std::is_unsigned<compiler::Register>::value, "compiler::Register must be unsigned");
ASSERT(delta > 0);
for (auto *bb : GetGraph()->GetBlocksRPO()) {
for (auto inst : bb->AllInsts()) {
// Renumber output of any instruction, if applicable:
if (RegNeedsRenumbering(inst->GetDstReg()) && inst->GetDstReg() >= min_reg) {
inst->SetDstReg(RenumberReg(inst->GetDstReg(), delta));
}
if (inst->IsPhi() || inst->IsCatchPhi()) {
continue;
}
// Renumber inputs and outputs of SpillFill instructions:
if (inst->IsSpillFill()) {
RenumberSpillFillRegs(inst->CastToSpillFill(), min_reg, delta);
continue;
}
// Fix inputs of common instructions:
for (size_t i = 0; i < inst->GetInputsCount(); i++) {
if (RegNeedsRenumbering(inst->GetSrcReg(i)) && inst->GetSrcReg(i) >= min_reg) {
inst->SetSrcReg(i, RenumberReg(inst->GetSrcReg(i), delta));
}
}
}
}
}
bool RegEncoder::RenumberArgRegs()
{
const auto usage_mask = GetGraph()->GetUsedRegs<compiler::DataType::INT64>();
ASSERT(usage_mask->size() == compiler::VIRTUAL_FRAME_SIZE);
auto frame_size = static_cast<compiler::Register>(usage_mask->size());
const auto num_args = GetNumArgsFromGraph();
ASSERT(frame_size >= num_args);
auto num_non_args = static_cast<compiler::Register>(frame_size - num_args);
if (num_max_range_input_ > num_non_args) {
LOG(DEBUG, BYTECODE_OPTIMIZER) << "RegEncoder: The free regs for range call are not enough";
return false;
}
compiler::Register num_locals = 0;
if (num_non_args != 0) {
while (num_locals != num_non_args && usage_mask->at(num_locals)) {
++num_locals;
}
}
compiler::Register num_temps = 0;
if (num_locals != num_non_args) {
compiler::Register r = num_non_args - 1;
while (r < num_non_args && usage_mask->at(r)) {
++num_temps;
--r;
}
}
if (num_locals + num_temps > num_non_args - num_max_range_input_) {
LOG(DEBUG, BYTECODE_OPTIMIZER) << "RegEncoder: The free regs for range call are not enough";
return false;
}
range_temps_start_ = num_locals;
bool do_renumber = true;
if (num_non_args == 0 && num_max_range_input_ == 0) { // all registers are arguments: no need to renumber
do_renumber = false;
}
// All free regs will be just enough to encode call.rang: no need to renumber
if (num_locals + num_temps + num_max_range_input_ == num_non_args) {
do_renumber = false;
}
if (num_temps + num_args == 0) { // no temps and no args: nothing to renumber
do_renumber = false;
}
if (do_renumber) {
const auto min_reg = static_cast<compiler::Register>(num_non_args - num_temps);
ASSERT(min_reg > MIN_REGISTER_NUMBER);
// Assert that if temps are present, they are marked allocated in the mask:
for (compiler::Register r = min_reg; r < min_reg + num_temps; r++) {
ASSERT(usage_mask->at(r));
}
// Assert that there are no used regs between locals and temps + arguments:
for (compiler::Register r = num_locals; r < min_reg; r++) {
ASSERT(!usage_mask->at(r));
}
auto delta = static_cast<compiler::Register>(num_locals + num_temps + num_max_range_input_ - num_non_args);
RenumberRegs(min_reg, delta);
for (compiler::Register r = min_reg; r < frame_size; r++) {
usage_mask->at(RenumberReg(r, delta)) = usage_mask->at(r);
usage_mask->at(r) = false;
}
}
SaveNumLocalsToGraph(num_locals + num_temps + num_max_range_input_);
return true;
}
void RegEncoder::InsertSpills()
{
ASSERT(num_max_range_input_ > 0 || (num_temps_ > 0 && num_temps_ <= MAX_NUM_INPUTS));
for (auto *bb : GetGraph()->GetBlocksRPO()) {
for (auto inst : bb->AllInstsSafe()) {
if (inst->GetInputsCount() == 0) {
continue;
}
VisitInstruction(inst);
// TODO(aantipina): Enable assert here for GetStatus() as soon code generation is fully supported
}
}
}
void RegEncoder::CalculateNumNeededTemps()
{
num_temps_ = 0;
for (auto bb : GetGraph()->GetBlocksRPO()) {
for (auto inst : bb->AllInstsSafe()) {
if (inst->GetInputsCount() == 0) {
continue;
}
VisitInstruction(inst);
// TODO(aantipina): Enable here for GetStatus() as soon code generation is fully supported
}
}
LOG(DEBUG, BYTECODE_OPTIMIZER) << GetGraph()->GetRuntime()->GetMethodFullName(GetGraph()->GetMethod())
<< ": num_temps_ = " << std::to_string(num_temps_);
}
template <typename T>
static void AddMoveBefore(Inst *inst, const T &sp_container)
{
if (sp_container.empty()) {
return;
}
auto sf_inst = inst->GetBasicBlock()->GetGraph()->CreateInstSpillFill();
for (auto const &[src, dst] : sp_container) {
ASSERT(src != compiler::ACC_REG_ID);
sf_inst->AddMove(src, dst.reg, GetRegType(dst.type));
LOG(DEBUG, BYTECODE_OPTIMIZER) << "RegEncoder: Move v" << static_cast<int>(dst.reg) << " <- v"
<< static_cast<int>(src) << " was added";
}
inst->GetBasicBlock()->InsertBefore(sf_inst, inst);
}
static bool IsAccReadPosition(compiler::Inst *inst, size_t pos)
{
// Calls can have accumulator at any position, return false for them
return !inst->IsCall() && inst->IsAccRead() && pos == AccReadIndex(inst);
}
void RegEncoder::InsertSpillsForDynInputsInst(compiler::Inst *inst)
{
ASSERT(state_ == RegEncoderState::INSERT_SPILLS);
ASSERT(inst->IsStaticCall() || inst->IsVirtualCall() || inst->IsInitObject() || inst->IsIntrinsic());
RegContentMap spill_map(GetGraph()->GetLocalAllocator()->Adapter()); // src -> (dst, src_type), non-callrange
RegContentVec spill_vec(GetGraph()->GetLocalAllocator()->Adapter()); // spill_vec is used to handle callrange
auto nargs = inst->GetInputsCount() - (inst->RequireState() ? 1 : 0);
auto start = GetStartInputIndex(inst);
bool range = IsIntrinsicRange(inst) || (nargs - start > MAX_NUM_NON_RANGE_ARGS && CanHoldRange(inst));
compiler::Register temp = range ? range_temps_start_ : 0;
for (size_t i = start; i < nargs; ++i) {
auto src_reg = inst->GetSrcReg(i);
auto type = inst->GetInputType(i);
// do not spillfill for acc-read position. For example, Intrinsic.FSTARR32
if (IsAccReadPosition(inst, i)) {
continue;
}
if (!range) {
if (!RegNeedsRenumbering(src_reg) || src_reg < NUM_COMPACTLY_ENCODED_REGS) {
continue;
}
auto res = spill_map.emplace(src_reg, RegContent(temp, type));
if (res.second) {
inst->SetSrcReg(i, temp++);
} else {
// Such register is already in map.
// It can be ok for cases like: CallStatic v49, v49
// Such instructions can be generated by optimizer too.
const RegContent &reg_cont = res.first->second;
inst->SetSrcReg(i, reg_cont.reg);
}
} else {
spill_vec.emplace_back(src_reg, RegContent(temp, type));
inst->SetSrcReg(i, temp++);
}
}
AddMoveBefore(inst, spill_map);
AddMoveBefore(inst, spill_vec);
}
size_t RegEncoder::GetStartInputIndex(compiler::Inst *inst)
{
return inst->GetOpcode() == compiler::Opcode::InitObject ? 1 : 0; // exclude LoadAndInitClass
}
static void AddMoveAfter(Inst *inst, compiler::Register src, RegContent dst)
{
auto sf_inst = inst->GetBasicBlock()->GetGraph()->CreateInstSpillFill();
sf_inst->AddMove(src, dst.reg, dst.type);
LOG(DEBUG, BYTECODE_OPTIMIZER) << "RegEncoder: Move v" << static_cast<int>(dst.reg) << " <- v"
<< static_cast<int>(src) << " was added";
inst->GetBasicBlock()->InsertAfter(sf_inst, inst);
}
static bool isMoveAfter(const compiler::Inst *inst)
{
switch (inst->GetOpcode()) {
case compiler::Opcode::LoadObject:
// Special case for LoadObject, because it can be register instruction.
return inst->GetDstReg() != compiler::ACC_REG_ID;
case compiler::Opcode::NewArray:
return true;
default:
return false;
}
}
void RegEncoder::InsertSpillsForInst(compiler::Inst *inst)
{
ASSERT(state_ == RegEncoderState::INSERT_SPILLS);
RegContentMap spill_map(GetGraph()->GetLocalAllocator()->Adapter()); // src -> (dst, src_type)
if (inst->IsOperandsDynamic()) {
InsertSpillsForDynInputsInst(inst);
return;
}
compiler::Register temp = 0;
for (size_t i = 0; i < inst->GetInputsCount(); i++) {
// TODO(mbolshov): make a better solution to skip instructions, that are not relevant to bytecode_opt
if (inst->GetInput(i).GetInst()->GetOpcode() == Opcode::LoadAndInitClass) {
continue;
}
auto reg = inst->GetSrcReg(i);
if (RegNeedsRenumbering(reg) && reg >= NUM_COMPACTLY_ENCODED_REGS) {
auto res = spill_map.emplace(reg, RegContent(temp, GetRegType(inst->GetInputType(i))));
if (res.second) {
inst->SetSrcReg(i, temp++);
} else {
// Such register is already in map.
// It can be ok for cases like: and v49, v49
// Such instructions can be generated by optimizer too.
const RegContent &reg_cont = res.first->second;
inst->SetSrcReg(i, reg_cont.reg);
}
}
}
AddMoveBefore(inst, spill_map);
if (isMoveAfter(inst)) {
auto reg = inst->GetDstReg();
if (RegNeedsRenumbering(reg) && reg >= NUM_COMPACTLY_ENCODED_REGS) {
inst->SetDstReg(temp);
AddMoveAfter(inst, temp, RegContent(reg, GetRegType(inst->GetType())));
}
}
}
static void IncTempsIfNeeded(const compiler::Register reg, compiler::Register &num_temps)
{
if (RegNeedsRenumbering(reg) && reg >= NUM_COMPACTLY_ENCODED_REGS) {
num_temps++;
}
}
void RegEncoder::CalculateNumNeededTempsForInst(compiler::Inst *inst)
{
ASSERT(state_ == RegEncoderState::RESERVE_TEMPS);
compiler::Register num_temps = 0;
if (inst->IsOperandsDynamic()) {
if (IsIntrinsicRange(inst)) {
return;
}
ASSERT(inst->IsStaticCall() || inst->IsVirtualCall() || inst->IsInitObject() || inst->IsIntrinsic());
auto nargs = inst->GetInputsCount() - (inst->RequireState() ? 1 : 0);
size_t start = inst->GetOpcode() == compiler::Opcode::InitObject ? 1 : 0;
if (nargs - start > MAX_NUM_NON_RANGE_ARGS) { // is call.range
return;
}
for (size_t i = start; i < nargs; i++) {
if (IsAccReadPosition(inst, i)) {
continue;
}
auto reg = inst->GetSrcReg(i);
if (RegNeedsRenumbering(reg) && reg >= NUM_COMPACTLY_ENCODED_REGS) {
num_temps++;
}
}
} else {
for (size_t i = 0; i < inst->GetInputsCount(); i++) {
// TODO(mbolshov): make a better solution to skip instructions, that are not relevant to bytecode_opt
if (inst->GetInput(i).GetInst()->GetOpcode() == Opcode::LoadAndInitClass) {
continue;
}
IncTempsIfNeeded(inst->GetSrcReg(i), num_temps);
}
if (isMoveAfter(inst)) {
IncTempsIfNeeded(inst->GetDstReg(), num_temps);
}
}
ASSERT(num_temps <= MAX_NUM_INPUTS);
num_temps_ = std::max(num_temps, num_temps_);
}
void RegEncoder::Check4Width(compiler::Inst *inst)
{
switch (state_) {
case RegEncoderState::RESERVE_TEMPS: {
CalculateNumNeededTempsForInst(inst);
break;
}
case RegEncoderState::INSERT_SPILLS: {
InsertSpillsForInst(inst);
break;
}
default:
UNREACHABLE();
}
}
void RegEncoder::Check8Width([[maybe_unused]] compiler::Inst *inst)
{
// TODO(aantipina): implement after it became possible to use register numbers more than 256 (#2697)
}
void RegEncoder::VisitCallStatic(GraphVisitor *visitor, Inst *inst)
{
CallHelper(visitor, inst);
}
void RegEncoder::VisitCallVirtual(GraphVisitor *visitor, Inst *inst)
{
CallHelper(visitor, inst);
}
void RegEncoder::VisitInitObject(GraphVisitor *visitor, Inst *inst)
{
CallHelper(visitor, inst);
}
void RegEncoder::VisitIntrinsic(GraphVisitor *visitor, Inst *inst)
{
auto re = static_cast<RegEncoder *>(visitor);
if (IsIntrinsicRange(inst)) {
re->Check4Width(inst->CastToIntrinsic());
return;
}
re->Check8Width(inst->CastToIntrinsic());
}
void RegEncoder::VisitLoadObject(GraphVisitor *v, Inst *inst_base)
{
bool is_acc_type = inst_base->GetDstReg() == compiler::ACC_REG_ID;
auto re = static_cast<RegEncoder *>(v);
auto inst = inst_base->CastToLoadObject();
switch (inst->GetType()) {
case compiler::DataType::BOOL:
case compiler::DataType::UINT8:
case compiler::DataType::INT8:
case compiler::DataType::UINT16:
case compiler::DataType::INT16:
case compiler::DataType::UINT32:
case compiler::DataType::INT32:
case compiler::DataType::INT64:
case compiler::DataType::UINT64:
case compiler::DataType::FLOAT32:
case compiler::DataType::FLOAT64:
case compiler::DataType::REFERENCE:
if (is_acc_type) {
re->Check8Width(inst);
} else {
re->Check4Width(inst);
}
break;
default:
LOG(ERROR, BYTECODE_OPTIMIZER)
<< "Wrong DataType for " << compiler::GetOpcodeString(inst->GetOpcode()) << " failed";
re->success_ = false;
}
}
void RegEncoder::VisitStoreObject(GraphVisitor *v, Inst *inst_base)
{
bool is_acc_type = inst_base->GetSrcReg(1) == compiler::ACC_REG_ID;
auto re = static_cast<RegEncoder *>(v);
auto inst = inst_base->CastToStoreObject();
switch (inst->GetType()) {
case compiler::DataType::BOOL:
case compiler::DataType::UINT8:
case compiler::DataType::INT8:
case compiler::DataType::UINT16:
case compiler::DataType::INT16:
case compiler::DataType::UINT32:
case compiler::DataType::INT32:
case compiler::DataType::INT64:
case compiler::DataType::UINT64:
case compiler::DataType::FLOAT32:
case compiler::DataType::FLOAT64:
case compiler::DataType::REFERENCE:
if (is_acc_type) {
re->Check8Width(inst);
} else {
re->Check4Width(inst);
}
break;
default:
LOG(ERROR, BYTECODE_OPTIMIZER)
<< "Wrong DataType for " << compiler::GetOpcodeString(inst->GetOpcode()) << " failed";
re->success_ = false;
}
}
void RegEncoder::VisitSpillFill([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst) {}
void RegEncoder::VisitConstant([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst) {}
void RegEncoder::VisitLoadString([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst) {}
void RegEncoder::VisitReturn([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst) {}
void RegEncoder::VisitCatchPhi([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst) {}
void RegEncoder::VisitCastValueToAnyType([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst) {}
#include "generated/check_width.cpp"
} // namespace panda::bytecodeopt

View File

@ -0,0 +1,211 @@
/**
* Copyright (c) 2021-2022 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 PANDA_REG_ENCODER_H
#define PANDA_REG_ENCODER_H
#include "compiler/optimizer/ir/graph.h"
#include "compiler/optimizer/pass.h"
#include "compiler/optimizer/ir/inst.h"
#include "compiler/optimizer/ir/graph_visitor.h"
/*
* Register Encoder.
*
* After compiler's register allocation layout of the virtual frame looks like:
*
* |<-locals->|<-free registers->|<-RegAlloc temps->|<-arguments->|
*
* where:
*
* * locals (>= 0) are registers allocated for function's local variables;
* * temps (>= 0) are temporary registers that can be allocated by spill-fill
* resolver during breaking mov chain cycles;
* * arguments (>= 0) are registers allocated for function's arguemnts.
*
* The size of the frame is fixed (see register allocator for details).
*
* Locals and temps are allocated densely: if there are more than 0 locals
* (or temps) then all registers go strictly in order without "holes".
* Space for arguments, however, can contain "holes" (each "hole" corresponds
* to an unused argument).
*
* For locals and temps, it is not guaranteed that their number equals to the
* number of registers used in the unoptimized bytecode. Total number of arguments
* is of course constant throughout optimization pipeline.
*
* This pass solves following problems:
*
* 1. Shift temps and arguments to the left adjacently to locals, and reserve the
* range temps from free registers if necessary, consistently changing all register
* numbers for affected instructions. After this is done, the virtual frame looks like:
*
* |<-locals->|<-range temps->|<-RegAlloc temps->|<-arguments->|<-free registers->|
*
* 2. Check if IR contains any instructions that can encode only [r0, r15] with
* actual inputs r16+. If such instructions are found, some lower registers
* (starting with r0, but no more than MAX_NUM_INPUTS registers) are reserved as
* temps and corresponding spills are emitted where needed. After this is done,
* the virtual frame looks like:
*
* |<-Renumber temps->|<-locals->|<-range temps->|<-RegAlloc temps->|<-arguments->|<-free registers->|
*
* After the pass:
*
* * Usage mask is updated accordingly.
* * num_locals + num_temps + num_max_range_input is stored to graph with SetStackSlotsCount
*/
namespace panda::bytecodeopt {
struct RegContent {
compiler::Register reg;
compiler::DataType::Type type;
RegContent() : reg(compiler::INVALID_REG), type(compiler::DataType::NO_TYPE) {}
RegContent(compiler::Register r, compiler::DataType::Type t) : reg(r), type(t) {}
bool operator==(const RegContent &other) const
{
return reg == other.reg && type == other.type;
}
bool operator!=(const RegContent &other) const
{
return !(*this == other);
}
};
using RegContentMap = ArenaUnorderedMap<compiler::Register, RegContent>;
using RegContentVec = ArenaVector<std::pair<compiler::Register, RegContent>>;
enum class RegEncoderState { IDLE, RENUMBER_ARGS, RESERVE_TEMPS, INSERT_SPILLS };
using panda::compiler::BasicBlock;
using panda::compiler::Inst;
using panda::compiler::Opcode;
class RegEncoder : public compiler::Optimization, public compiler::GraphVisitor {
public:
explicit RegEncoder(compiler::Graph *graph)
: compiler::Optimization(graph), num_temps_(0), state_(RegEncoderState::IDLE)
{
}
~RegEncoder() override = default;
// true: Pass applied successfully
// false: Unable to apply pass because of insufficient number of registers
bool RunImpl() override;
const char *GetPassName() const override
{
return "RegEncoder";
}
void Check4Width(compiler::Inst *inst);
void Check8Width(compiler::Inst *inst);
const ArenaVector<BasicBlock *> &GetBlocksToVisit() const override
{
return GetGraph()->GetBlocksRPO();
}
static void VisitSpillFill([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst);
static void VisitConstant([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst);
static void VisitCatchPhi([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst);
static void VisitCallStatic(GraphVisitor *v, Inst *inst);
static void VisitCallVirtual(GraphVisitor *v, Inst *inst);
static void VisitInitObject(GraphVisitor *v, Inst *inst);
static void VisitIntrinsic(GraphVisitor *v, Inst *inst);
static void VisitLoadObject(GraphVisitor *v, Inst *inst_base);
static void VisitStoreObject(GraphVisitor *v, Inst *inst_base);
static void VisitLoadString([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst);
static void VisitReturn([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst);
static void VisitCastValueToAnyType(GraphVisitor *v, Inst *inst);
#include "generated/reg_encoder_visitors.inc"
#include "generated/check_width.h"
void VisitDefault(Inst *inst) override
{
LOG(ERROR, BYTECODE_OPTIMIZER) << "Opcode " << compiler::GetOpcodeString(inst->GetOpcode())
<< " not yet implemented in RegEncoder";
success_ = false;
}
#include "compiler/optimizer/ir/visitor.inc"
private:
void CalculateNumNeededTemps();
void CalculateNumNeededTempsForInst(compiler::Inst *inst);
void RenumberRegs(const compiler::Register min_reg, const compiler::Register delta);
bool RenumberArgRegs();
void InsertSpills();
void InsertSpillsForInst(compiler::Inst *inst);
void InsertSpillsForDynInputsInst(compiler::Inst *inst);
size_t GetStartInputIndex(compiler::Inst *inst);
compiler::Register GetNumArgsFromGraph() const
{
auto adapter = GetGraph()->GetRuntime();
auto method = GetGraph()->GetMethod();
auto num_args = adapter->GetMethodTotalArgumentsCount(method);
ASSERT(num_args <= compiler::VIRTUAL_FRAME_SIZE);
return num_args;
}
compiler::Register GetNumLocalsFromGraph() const
{
auto num_locals = GetGraph()->GetStackSlotsCount();
ASSERT(num_locals <= compiler::VIRTUAL_FRAME_SIZE);
return num_locals;
}
compiler::Register GetNumRegs() const
{
auto num_regs = GetNumLocalsFromGraph() + GetNumArgsFromGraph();
ASSERT(num_regs <= compiler::VIRTUAL_FRAME_SIZE);
return num_regs;
}
void SaveNumLocalsToGraph(uint32_t num_locals) const
{
ASSERT(num_locals <= compiler::VIRTUAL_FRAME_SIZE);
GetGraph()->SetStackSlotsCount(num_locals);
}
bool GetStatus() const
{
return success_;
}
static void CallHelper(compiler::GraphVisitor *visitor, Inst *inst)
{
auto *re = static_cast<RegEncoder *>(visitor);
re->Check4Width(inst);
}
private:
compiler::Register num_temps_;
RegEncoderState state_;
compiler::Register num_max_range_input_ {0};
compiler::Register range_temps_start_ {0};
bool success_ {true};
};
} // namespace panda::bytecodeopt
#endif // PANDA_REG_ENCODER_H

View File

@ -0,0 +1,403 @@
/**
* Copyright (c) 2021-2022 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 PANDA_BYTECODE_OPTIMIZER_RUNTIME_ADAPTER_H_
#define PANDA_BYTECODE_OPTIMIZER_RUNTIME_ADAPTER_H_
#include "compiler/optimizer/ir/runtime_interface.h"
#include "libpandafile/bytecode_instruction.h"
#include "libpandafile/class_data_accessor.h"
#include "libpandafile/code_data_accessor.h"
#include "libpandafile/field_data_accessor.h"
#include "libpandafile/file.h"
#include "libpandafile/file_items.h"
#include "libpandafile/method_data_accessor.h"
#include "libpandafile/proto_data_accessor.h"
#include "libpandafile/proto_data_accessor-inl.h"
#include "libpandafile/type_helper.h"
namespace panda {
using compiler::RuntimeInterface;
class BytecodeOptimizerRuntimeAdapter : public RuntimeInterface {
public:
explicit BytecodeOptimizerRuntimeAdapter(const panda_file::File &panda_file) : panda_file_(panda_file) {}
~BytecodeOptimizerRuntimeAdapter() override = default;
BinaryFilePtr GetBinaryFileForMethod([[maybe_unused]] MethodPtr method) const override
{
return const_cast<panda_file::File *>(&panda_file_);
}
MethodId ResolveMethodIndex(MethodPtr parent_method, MethodIndex index) const override
{
return panda_file_.ResolveMethodIndex(MethodCast(parent_method), index).GetOffset();
}
FieldId ResolveFieldIndex(MethodPtr parent_method, FieldIndex index) const override
{
return panda_file_.ResolveFieldIndex(MethodCast(parent_method), index).GetOffset();
}
IdType ResolveTypeIndex(MethodPtr parent_method, TypeIndex index) const override
{
return panda_file_.ResolveClassIndex(MethodCast(parent_method), index).GetOffset();
}
MethodPtr GetMethodById([[maybe_unused]] MethodPtr caller, MethodId id) const override
{
return reinterpret_cast<MethodPtr>(id);
}
MethodId GetMethodId(MethodPtr method) const override
{
return static_cast<MethodId>(reinterpret_cast<uintptr_t>(method));
}
compiler::DataType::Type GetMethodReturnType(MethodPtr method) const override
{
panda_file::MethodDataAccessor mda(panda_file_, MethodCast(method));
panda_file::ProtoDataAccessor pda(panda_file_, mda.GetProtoId());
return ToCompilerType(panda_file::GetEffectiveType(pda.GetReturnType()));
}
compiler::DataType::Type GetMethodTotalArgumentType(MethodPtr method, size_t index) const override
{
panda_file::MethodDataAccessor mda(panda_file_, MethodCast(method));
if (!mda.IsStatic()) {
if (index == 0) {
return ToCompilerType(
panda_file::GetEffectiveType(panda_file::Type(panda_file::Type::TypeId::REFERENCE)));
}
--index;
}
panda_file::ProtoDataAccessor pda(panda_file_, mda.GetProtoId());
return ToCompilerType(panda_file::GetEffectiveType(pda.GetArgType(index)));
}
compiler::DataType::Type GetMethodArgumentType([[maybe_unused]] MethodPtr caller, MethodId id,
size_t index) const override
{
panda_file::MethodDataAccessor mda(panda_file_, panda_file::File::EntityId(id));
panda_file::ProtoDataAccessor pda(panda_file_, mda.GetProtoId());
return ToCompilerType(panda_file::GetEffectiveType(pda.GetArgType(index)));
}
size_t GetMethodTotalArgumentsCount(MethodPtr method) const override
{
panda_file::MethodDataAccessor mda(panda_file_, MethodCast(method));
ASSERT(!mda.IsExternal() && !mda.IsAbstract() && !mda.IsNative());
panda_file::CodeDataAccessor cda(panda_file_, mda.GetCodeId().value());
return cda.GetNumArgs();
}
size_t GetMethodArgumentsCount([[maybe_unused]] MethodPtr caller, MethodId id) const override
{
panda_file::MethodDataAccessor mda(panda_file_, panda_file::File::EntityId(id));
panda_file::ProtoDataAccessor pda(panda_file_, mda.GetProtoId());
return pda.GetNumArgs();
}
compiler::DataType::Type GetMethodReturnType(MethodPtr caller, MethodId id) const override
{
return GetMethodReturnType(GetMethodById(caller, id));
}
size_t GetMethodRegistersCount(MethodPtr method) const override
{
panda_file::MethodDataAccessor mda(panda_file_, MethodCast(method));
ASSERT(!mda.IsExternal() && !mda.IsAbstract() && !mda.IsNative());
panda_file::CodeDataAccessor cda(panda_file_, mda.GetCodeId().value());
return cda.GetNumVregs();
}
const uint8_t *GetMethodCode(MethodPtr method) const override
{
panda_file::MethodDataAccessor mda(panda_file_, MethodCast(method));
ASSERT(!mda.IsExternal() && !mda.IsAbstract() && !mda.IsNative());
panda_file::CodeDataAccessor cda(panda_file_, mda.GetCodeId().value());
return cda.GetInstructions();
}
size_t GetMethodCodeSize(MethodPtr method) const override
{
panda_file::MethodDataAccessor mda(panda_file_, MethodCast(method));
ASSERT(!mda.IsExternal() && !mda.IsAbstract() && !mda.IsNative());
panda_file::CodeDataAccessor cda(panda_file_, mda.GetCodeId().value());
return cda.GetCodeSize();
}
compiler::SourceLanguage GetMethodSourceLanguage(MethodPtr method) const override
{
panda_file::MethodDataAccessor mda(panda_file_, MethodCast(method));
ASSERT(!mda.IsExternal() && !mda.IsAbstract() && !mda.IsNative());
auto source_lang = mda.GetSourceLang();
ASSERT(source_lang.has_value());
return static_cast<compiler::SourceLanguage>(source_lang.value());
}
size_t GetClassIdForField([[maybe_unused]] MethodPtr method, size_t field_id) const override
{
panda_file::FieldDataAccessor fda(panda_file_, panda_file::File::EntityId(field_id));
return static_cast<size_t>(fda.GetClassId().GetOffset());
}
ClassPtr GetClassForField(FieldPtr field) const override
{
panda_file::FieldDataAccessor fda(panda_file_, FieldCast(field));
return reinterpret_cast<ClassPtr>(fda.GetClassId().GetOffset());
}
size_t GetClassIdForMethod(MethodPtr method) const override
{
panda_file::MethodDataAccessor mda(panda_file_, MethodCast(method));
return static_cast<size_t>(mda.GetClassId().GetOffset());
}
size_t GetClassIdForMethod([[maybe_unused]] MethodPtr caller, size_t method_id) const override
{
panda_file::MethodDataAccessor mda(panda_file_, panda_file::File::EntityId(method_id));
return static_cast<size_t>(mda.GetClassId().GetOffset());
}
bool IsMethodExternal([[maybe_unused]] MethodPtr caller, MethodPtr callee) const override
{
panda_file::MethodDataAccessor mda(panda_file_, MethodCast(callee));
return mda.IsExternal();
}
bool IsMethodIntrinsic([[maybe_unused]] MethodPtr method) const override
{
return false;
}
bool IsMethodIntrinsic([[maybe_unused]] MethodPtr caller, [[maybe_unused]] MethodId id) const override
{
return false;
}
bool IsMethodStatic(MethodPtr method) const override
{
panda_file::MethodDataAccessor mda(panda_file_, MethodCast(method));
return mda.IsStatic();
}
bool IsMethodStatic([[maybe_unused]] MethodPtr caller, MethodId id) const override
{
panda_file::MethodDataAccessor mda(panda_file_, panda_file::File::EntityId(id));
return mda.IsStatic();
}
// return true if the method is Jni with exception
virtual bool HasNativeException([[maybe_unused]] MethodPtr method) const override
{
return false;
}
std::string GetClassNameFromMethod(MethodPtr method) const override
{
panda_file::MethodDataAccessor mda(panda_file_, MethodCast(method));
auto string_data = panda_file_.GetStringData(mda.GetClassId());
return std::string(reinterpret_cast<const char *>(string_data.data));
}
std::string GetClassName(ClassPtr cls) const override
{
auto string_data = panda_file_.GetStringData(ClassCast(cls));
return std::string(reinterpret_cast<const char *>(string_data.data));
}
std::string GetMethodName(MethodPtr method) const override
{
panda_file::MethodDataAccessor mda(panda_file_, MethodCast(method));
auto string_data = panda_file_.GetStringData(mda.GetNameId());
return std::string(reinterpret_cast<const char *>(string_data.data));
}
bool IsConstructor(MethodPtr method, uint32_t class_id) override
{
if (GetClassIdForMethod(method) != class_id) {
return false;
}
panda_file::File::EntityId entity_id(class_id);
panda_file::SourceLang lang = panda_file::SourceLang::PANDA_ASSEMBLY;
if (!panda_file_.IsExternal(entity_id)) {
panda_file::ClassDataAccessor cda(panda_file_, entity_id);
lang = cda.GetSourceLang().value_or(lang);
}
return GetMethodName(method) == GetCtorName(lang);
}
std::string GetMethodFullName(MethodPtr method, bool /* with_signature */) const override
{
auto class_name = GetClassNameFromMethod(method);
auto method_name = GetMethodName(method);
return class_name + "::" + method_name;
}
ClassPtr GetClass(MethodPtr method) const override
{
panda_file::MethodDataAccessor mda(panda_file_, MethodCast(method));
return reinterpret_cast<ClassPtr>(mda.GetClassId().GetOffset());
}
std::string GetBytecodeString(MethodPtr method, uintptr_t pc) const override
{
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
BytecodeInstruction inst(GetMethodCode(method) + pc);
std::stringstream ss;
ss << inst;
return ss.str();
}
bool IsArrayClass([[maybe_unused]] MethodPtr method, IdType id) const override
{
panda_file::File::EntityId cid(id);
return panda_file::IsArrayDescriptor(panda_file_.GetStringData(cid).data);
}
FieldPtr ResolveField([[maybe_unused]] MethodPtr method, size_t id, [[maybe_unused]] bool allow_external,
uint32_t * /* class_id */) override
{
return reinterpret_cast<FieldPtr>(id);
}
compiler::DataType::Type GetFieldType(FieldPtr field) const override
{
panda_file::FieldDataAccessor fda(panda_file_, FieldCast(field));
return ToCompilerType(panda_file::Type::GetTypeFromFieldEncoding(fda.GetType()));
}
compiler::DataType::Type GetFieldTypeById([[maybe_unused]] MethodPtr parent_method, IdType id) const override
{
panda_file::FieldDataAccessor fda(panda_file_, panda_file::File::EntityId(id));
return ToCompilerType(panda_file::Type::GetTypeFromFieldEncoding(fda.GetType()));
}
bool IsFieldVolatile(FieldPtr field) const override
{
panda_file::FieldDataAccessor fda(panda_file_, FieldCast(field));
return fda.IsVolatile();
}
ClassPtr ResolveType([[maybe_unused]] MethodPtr method, size_t id) const override
{
return reinterpret_cast<ClassPtr>(id);
}
std::string GetFieldName(FieldPtr field) const override
{
panda_file::FieldDataAccessor fda(panda_file_, FieldCast(field));
auto string_data = panda_file_.GetStringData(fda.GetNameId());
return utf::Mutf8AsCString(string_data.data);
}
private:
static compiler::DataType::Type ToCompilerType(panda_file::Type type)
{
switch (type.GetId()) {
case panda_file::Type::TypeId::VOID:
return compiler::DataType::VOID;
case panda_file::Type::TypeId::U1:
return compiler::DataType::BOOL;
case panda_file::Type::TypeId::I8:
return compiler::DataType::INT8;
case panda_file::Type::TypeId::U8:
return compiler::DataType::UINT8;
case panda_file::Type::TypeId::I16:
return compiler::DataType::INT16;
case panda_file::Type::TypeId::U16:
return compiler::DataType::UINT16;
case panda_file::Type::TypeId::I32:
return compiler::DataType::INT32;
case panda_file::Type::TypeId::U32:
return compiler::DataType::UINT32;
case panda_file::Type::TypeId::I64:
return compiler::DataType::INT64;
case panda_file::Type::TypeId::U64:
return compiler::DataType::UINT64;
case panda_file::Type::TypeId::F32:
return compiler::DataType::FLOAT32;
case panda_file::Type::TypeId::F64:
return compiler::DataType::FLOAT64;
case panda_file::Type::TypeId::REFERENCE:
return compiler::DataType::REFERENCE;
case panda_file::Type::TypeId::TAGGED:
case panda_file::Type::TypeId::INVALID:
return compiler::DataType::ANY;
default:
break;
}
UNREACHABLE();
}
static panda_file::File::EntityId MethodCast(RuntimeInterface::MethodPtr method)
{
return panda_file::File::EntityId(reinterpret_cast<uintptr_t>(method));
}
static panda_file::File::EntityId ClassCast(RuntimeInterface::ClassPtr cls)
{
return panda_file::File::EntityId(reinterpret_cast<uintptr_t>(cls));
}
static panda_file::File::EntityId FieldCast(RuntimeInterface::FieldPtr field)
{
return panda_file::File::EntityId(reinterpret_cast<uintptr_t>(field));
}
const panda_file::File &panda_file_;
};
} // namespace panda
#endif // PANDA_BYTECODE_OPTIMIZER_RUNTIME_ADAPTER_H_

View File

@ -0,0 +1,61 @@
# Copyright (c) 2021-2022 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.
set(CODEGEN_VISITORS_INC ${PANDA_BINARY_ROOT}/bytecode_optimizer/generated/codegen_visitors.inc)
panda_gen_file(
DATAFILE ${GEN_PLUGIN_OPTIONS_YAML}
TEMPLATE ${PANDA_ROOT}/bytecode_optimizer/templates/codegen_visitors.inc.erb
REQUIRES ${PANDA_ROOT}/templates/plugin_options.rb
EXTRA_DEPENDENCIES plugin_options_merge
OUTPUTFILE ${CODEGEN_VISITORS_INC}
)
add_custom_target(bytecode_optimizer_codegen_visitors_inc DEPENDS
plugin_options_gen
${CODEGEN_VISITORS_INC}
)
set(REG_ENCODER_VISITORS_INC ${PANDA_BINARY_ROOT}/bytecode_optimizer/generated/reg_encoder_visitors.inc)
panda_gen_file(
DATAFILE ${GEN_PLUGIN_OPTIONS_YAML}
TEMPLATE ${PANDA_ROOT}/bytecode_optimizer/templates/reg_encoder_visitors.inc.erb
REQUIRES ${PANDA_ROOT}/templates/plugin_options.rb
EXTRA_DEPENDENCIES plugin_options_merge
OUTPUTFILE ${REG_ENCODER_VISITORS_INC}
)
add_custom_target(bytecode_optimizer_reg_encoder_visitors_inc DEPENDS
plugin_options_gen
${REG_ENCODER_VISITORS_INC}
)
set(CODEGEN_INTRINSICS_CPP ${PANDA_BINARY_ROOT}/bytecode_optimizer/generated/codegen_intrinsics.cpp)
panda_gen_file(
DATAFILE ${GEN_PLUGIN_OPTIONS_YAML}
TEMPLATE ${PANDA_ROOT}/bytecode_optimizer/templates/codegen_intrinsics.cpp.erb
REQUIRES ${PANDA_ROOT}/templates/plugin_options.rb
EXTRA_DEPENDENCIES plugin_options_merge
OUTPUTFILE ${CODEGEN_INTRINSICS_CPP}
)
add_custom_target(bytecode_optimizer_codegen_intrinsics_cpp DEPENDS
plugin_options_gen
${CODEGEN_INTRINSICS_CPP}
)
add_dependencies(arkbytecodeopt
bytecode_optimizer_codegen_visitors_inc
bytecode_optimizer_reg_encoder_visitors_inc
bytecode_optimizer_codegen_intrinsics_cpp
)

View File

@ -0,0 +1,66 @@
/*
* Copyright (c) 2021-2022 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.
*/
// Autogenerated file -- DO NOT EDIT!
void RegEncoder::VisitIf([[maybe_unused]] GraphVisitor* v, Inst* inst_base) {
[[maybe_unused]] auto re = static_cast<RegEncoder*>(v);
[[maybe_unused]] auto inst = inst_base->CastToIf();
switch (inst->GetCc()) {
case compiler::CC_EQ:
case compiler::CC_NE:
case compiler::CC_LT:
case compiler::CC_LE:
case compiler::CC_GT:
case compiler::CC_GE: {
re->Check8Width(inst);
break;
}
default:
LOG(DEBUG, BYTECODE_OPTIMIZER) << "CheckWidth for " << compiler::GetOpcodeString(inst->GetOpcode()) << " failed";
re->success_ = false;
}
}
void RegEncoder::VisitIfImm([[maybe_unused]] GraphVisitor* v, Inst* inst_base) {
[[maybe_unused]] auto re = static_cast<RegEncoder*>(v);
[[maybe_unused]] auto inst = inst_base->CastToIfImm();
switch (inst->GetCc()) {
case compiler::CC_EQ:
case compiler::CC_NE:
case compiler::CC_LT:
case compiler::CC_LE:
case compiler::CC_GT:
case compiler::CC_GE: {
break;
}
default:
LOG(DEBUG, BYTECODE_OPTIMIZER) << "CheckWidth for " << compiler::GetOpcodeString(inst->GetOpcode()) << " failed";
re->success_ = false;
}
}
void RegEncoder::VisitCast([[maybe_unused]] GraphVisitor* v, Inst* inst_base) {
[[maybe_unused]] auto re = static_cast<RegEncoder*>(v);
[[maybe_unused]] auto inst = inst_base->CastToCast();
return;
}
% call_me_from_template
% visitors.each do |visitor|
void RegEncoder::Visit<%= visitor.ir_op %>([[maybe_unused]] GraphVisitor* v, Inst* inst_base) {
[[maybe_unused]] auto re = static_cast<RegEncoder*>(v);
[[maybe_unused]] auto inst = inst_base->CastTo<%= visitor.ir_op %>();
<%= visitor.switch.check_width %>
}
% end

View File

@ -0,0 +1,25 @@
/*
* Copyright (c) 2021-2022 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.
*/
// Autogenerated file -- DO NOT EDIT!
static void VisitIf([[maybe_unused]] GraphVisitor* v, Inst* inst_base);
static void VisitIfImm([[maybe_unused]] GraphVisitor* v, Inst* inst_base);
static void VisitCast([[maybe_unused]] GraphVisitor* v, Inst* inst_base);
% call_me_from_template
% visitors.each do |visitor|
static void Visit<%= visitor.ir_op %>([[maybe_unused]] GraphVisitor* v, Inst* inst_base);
% end

View File

@ -0,0 +1,45 @@
/*
* Copyright (c) 2021-2022 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.
*/
// Autogenerated file -- DO NOT EDIT!
// NOLINTNEXTLINE(readability-function-size)
/*
void BytecodeGen::VisitIntrinsic(GraphVisitor *visitor, Inst *inst_base)
{
ASSERT(inst_base->IsIntrinsic());
auto inst = inst_base->CastToIntrinsic();
auto enc = static_cast<BytecodeGen *>(visitor);
if (!enc->GetGraph()->IsDynamicMethod()) {
LOG(ERROR, BYTECODE_OPTIMIZER) << "Codegen for " << compiler::GetOpcodeString(inst->GetOpcode()) << " failed";
enc->success_ = false;
return;
}
switch (inst->GetIntrinsicId()) {
% Common::plugins.each do |plugin_lang, plugin_opts|
// Lang <%= plugin_lang %>
% next unless plugin_opts["bytecodeopt"]
% next unless plugin_opts["bytecodeopt"]["codegen_intrinsics_inc"]
#include "<%= plugin_opts["bytecodeopt"]["codegen_intrinsics_inc"] %>"
% end
default: {
enc->success_ = false;
LOG(ERROR,COMPILER) << "Unsupported intrinsic";
}
}
}
*/

View File

@ -13,13 +13,10 @@
* limitations under the License.
*/
#ifndef PANDA_VERIFICATION_THREAD_VERIFIER_THREAD_H_
#define PANDA_VERIFICATION_THREAD_VERIFIER_THREAD_H_
// Autogenerated file -- DO NOT EDIT!
#include <cstdlib>
namespace panda::verifier {
void VerifierThread(size_t n);
} // namespace panda::verifier
#endif // PANDA_VERIFICATION_THREAD_VERIFIER_THREAD_H_
% Common::plugins.each do |plugin_lang, plugin_opts|
% next unless plugin_opts["bytecodeopt"]
% next unless plugin_opts["bytecodeopt"]["codegen_visitors_inc"]
#include "<%= plugin_opts["bytecodeopt"]["codegen_visitors_inc"] %>"
% end

View File

@ -13,14 +13,15 @@
* limitations under the License.
*/
#include "runtime/include/tooling/pt_method.h"
#include "runtime/include/method.h"
#include "pt_method_private.h"
// Autogenerated file -- DO NOT EDIT!
namespace panda::tooling {
const char *PtMethod::GetName() const
{
Method *method = PtMethodToMethod(*this);
return utf::Mutf8AsCString(method->GetName().data);
% call_me_from_template
% visitors.each do |visitor|
<%= visitor_sig(visitor.ir_op) %> {
pandasm::Ins ins;
[[maybe_unused]] auto enc = static_cast<BytecodeGen*>(v);
[[maybe_unused]] auto inst = inst_base->CastTo<%= visitor.ir_op %>();
<%= visitor.switch.encode %>
}
} // namespace panda::tooling
% end

View File

@ -13,10 +13,10 @@
* limitations under the License.
*/
#include "ecmascript_meta.h"
// Autogenerated file -- DO NOT EDIT!
namespace panda::pandasm::extensions::ecmascript {
% call_me_from_template
#include <ecmascript_meta_gen.h>
} // namespace panda::pandasm::extensions::ecmascript
% visitors.each do |visitor|
static <%= visitor_sig(visitor.ir_op, false) %>;
% end

View File

@ -13,13 +13,10 @@
* limitations under the License.
*/
#ifndef PANDA_VERIFICATION_JOB_QUEUE_JOB_FILL_H_
#define PANDA_VERIFICATION_JOB_QUEUE_JOB_FILL_H_
// Autogenerated file -- DO NOT EDIT!
#include "verification/job_queue/job_queue.h"
namespace panda::verifier {
bool FillJob(Job &job);
} // namespace panda::verifier
#endif // PANDA_VERIFICATION_JOB_QUEUE_JOB_FILL_H_
% Common::plugins.each do |plugin_lang, plugin_opts|
% next unless plugin_opts["bytecodeopt"]
% next unless plugin_opts["bytecodeopt"]["reg_encoder_visitors_inc"]
#include "<%= plugin_opts["bytecodeopt"]["reg_encoder_visitors_inc"] %>"
% end

View File

@ -0,0 +1,371 @@
/**
* Copyright (c) 2021-2022 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.
*/
#include "common.h"
#include "check_resolver.h"
#include "compiler/optimizer/optimizations/cleanup.h"
#include "compiler/optimizer/optimizations/lowering.h"
namespace panda::bytecodeopt::test {
class LoweringTest : public CommonTest {
};
// Checks the results of lowering pass if negative operands are used
TEST_F(IrBuilderTest, Lowering)
{
// ISA opcodes with expected lowering IR opcodes
std::map<std::string, compiler::Opcode> opcodes = {
{"add", compiler::Opcode::SubI}, {"sub", compiler::Opcode::AddI}, {"mul", compiler::Opcode::MulI},
{"and", compiler::Opcode::AndI}, {"xor", compiler::Opcode::XorI}, {"or", compiler::Opcode::OrI},
{"div", compiler::Opcode::DivI}, {"mod", compiler::Opcode::ModI},
};
const std::string template_source = R"(
.function i32 main() {
movi v0, 0x3
movi v1, 0xffffffffffffffe2
OPCODE v0, v1
return
}
)";
for (auto const &opcode : opcodes) {
// Specialize template source to the current opcode
std::string source(template_source);
size_t start_pos = source.find("OPCODE");
source.replace(start_pos, 6 /* OPCODE */, opcode.first);
ASSERT_TRUE(ParseToGraph(source, "main"));
#ifndef NDEBUG
GetGraph()->SetLowLevelInstructionsEnabled();
#endif
GetGraph()->RunPass<CheckResolver>();
GetGraph()->RunPass<compiler::Lowering>();
GetGraph()->RunPass<compiler::Cleanup>();
int32_t imm = -30;
// Note: `AddI -30` is handled as `SubI 30`. `SubI -30` is handled as `AddI 30`.
if (opcode.second == compiler::Opcode::AddI || opcode.second == compiler::Opcode::SubI) {
imm = 30;
}
auto expected = CreateEmptyGraph();
GRAPH(expected)
{
CONSTANT(1, 3).s32();
BASIC_BLOCK(2, -1)
{
INST(2, opcode.second).s32().Inputs(1).Imm(imm);
INST(3, Opcode::Return).s32().Inputs(2);
}
}
EXPECT_TRUE(GraphComparator().Compare(GetGraph(), expected));
}
}
TEST_F(LoweringTest, AddSub)
{
auto init = CreateEmptyGraph();
GRAPH(init)
{
PARAMETER(0, 0).u32();
PARAMETER(1, 1).u64();
PARAMETER(2, 2).f32();
CONSTANT(3, 12).s32();
CONSTANT(4, 150).s32();
CONSTANT(5, 0).s64();
CONSTANT(6, 1.2f).f32();
CONSTANT(7, -1).s32();
BASIC_BLOCK(2, -1)
{
INST(8, Opcode::Add).u32().Inputs(0, 3);
INST(9, Opcode::Sub).u32().Inputs(0, 3);
INST(10, Opcode::Add).u32().Inputs(0, 4);
INST(11, Opcode::Sub).u32().Inputs(0, 4);
INST(12, Opcode::Add).u64().Inputs(1, 5);
INST(13, Opcode::Sub).f32().Inputs(2, 6);
INST(14, Opcode::Sub).u32().Inputs(0, 7);
INST(15, Opcode::SaveState).NoVregs();
INST(20, Opcode::CallStatic).b().InputsAutoType(8, 9, 10, 11, 12, 13, 14, 15);
INST(21, Opcode::Return).b().Inputs(20);
}
}
#ifndef NDEBUG
init->SetLowLevelInstructionsEnabled();
#endif
init->RunPass<compiler::Lowering>();
init->RunPass<compiler::Cleanup>();
auto expected = CreateEmptyGraph();
GRAPH(expected)
{
PARAMETER(0, 0).u32();
PARAMETER(1, 1).u64();
PARAMETER(2, 2).f32();
CONSTANT(4, 150).s32();
CONSTANT(5, 0).s64();
CONSTANT(6, 1.2f).f32();
BASIC_BLOCK(2, -1)
{
INST(22, Opcode::AddI).u32().Inputs(0).Imm(0xc);
INST(23, Opcode::SubI).u32().Inputs(0).Imm(0xc);
INST(10, Opcode::Add).u32().Inputs(0, 4);
INST(11, Opcode::Sub).u32().Inputs(0, 4);
INST(12, Opcode::Add).u64().Inputs(1, 5);
INST(13, Opcode::Sub).f32().Inputs(2, 6);
INST(24, Opcode::AddI).u32().Inputs(0).Imm(1);
INST(19, Opcode::SaveState).NoVregs();
INST(20, Opcode::CallStatic).b().InputsAutoType(22, 23, 10, 11, 12, 13, 24, 19);
INST(21, Opcode::Return).b().Inputs(20);
}
}
EXPECT_TRUE(GraphComparator().Compare(init, expected));
}
TEST_F(LoweringTest, MulDivMod)
{
auto init = CreateEmptyGraph();
GRAPH(init)
{
PARAMETER(0, 0).u32();
PARAMETER(1, 1).u64();
PARAMETER(2, 2).f32();
CONSTANT(3, 12).s32();
CONSTANT(4, 150).s32();
CONSTANT(5, 0).s64();
CONSTANT(6, 1.2f).f32();
CONSTANT(7, -1).s32();
BASIC_BLOCK(2, -1)
{
INST(8, Opcode::Div).s32().Inputs(0, 3);
INST(9, Opcode::Div).u32().Inputs(0, 4);
INST(10, Opcode::Div).u64().Inputs(1, 5);
INST(11, Opcode::Div).f32().Inputs(2, 6);
INST(12, Opcode::Div).s32().Inputs(0, 7);
INST(13, Opcode::Mod).s32().Inputs(0, 3);
INST(14, Opcode::Mod).u32().Inputs(0, 4);
INST(15, Opcode::Mod).u64().Inputs(1, 5);
INST(16, Opcode::Mod).f32().Inputs(2, 6);
INST(17, Opcode::Mod).s32().Inputs(0, 7);
INST(18, Opcode::Mul).s32().Inputs(0, 3);
INST(19, Opcode::Mul).u32().Inputs(0, 4);
INST(20, Opcode::Mul).u64().Inputs(1, 5);
INST(21, Opcode::Mul).f32().Inputs(2, 6);
INST(22, Opcode::Mul).s32().Inputs(0, 7);
INST(31, Opcode::SaveState).NoVregs();
INST(23, Opcode::CallStatic)
.b()
.InputsAutoType(8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 31);
INST(24, Opcode::Return).b().Inputs(23);
}
}
#ifndef NDEBUG
init->SetLowLevelInstructionsEnabled();
#endif
init->RunPass<compiler::Lowering>();
init->RunPass<compiler::Cleanup>();
auto expected = CreateEmptyGraph();
GRAPH(expected)
{
PARAMETER(0, 0).u32();
PARAMETER(1, 1).u64();
PARAMETER(2, 2).f32();
CONSTANT(4, 150).s32();
CONSTANT(5, 0).s64();
CONSTANT(6, 1.2f).f32();
BASIC_BLOCK(2, -1)
{
INST(25, Opcode::DivI).s32().Inputs(0).Imm(0xc);
INST(9, Opcode::Div).u32().Inputs(0, 4);
INST(10, Opcode::Div).u64().Inputs(1, 5);
INST(11, Opcode::Div).f32().Inputs(2, 6);
INST(26, Opcode::DivI).s32().Inputs(0).Imm(static_cast<uint64_t>(-1));
INST(27, Opcode::ModI).s32().Inputs(0).Imm(0xc);
INST(14, Opcode::Mod).u32().Inputs(0, 4);
INST(15, Opcode::Mod).u64().Inputs(1, 5);
INST(16, Opcode::Mod).f32().Inputs(2, 6);
INST(28, Opcode::ModI).s32().Inputs(0).Imm(static_cast<uint64_t>(-1));
INST(29, Opcode::MulI).s32().Inputs(0).Imm(0xc);
INST(19, Opcode::Mul).u32().Inputs(0, 4);
INST(20, Opcode::Mul).u64().Inputs(1, 5);
INST(21, Opcode::Mul).f32().Inputs(2, 6);
INST(30, Opcode::MulI).s32().Inputs(0).Imm(static_cast<uint64_t>(-1));
INST(31, Opcode::SaveState).NoVregs();
INST(23, Opcode::CallStatic)
.b()
.InputsAutoType(25, 9, 10, 11, 26, 27, 14, 15, 16, 28, 29, 19, 20, 21, 30, 31);
INST(24, Opcode::Return).b().Inputs(23);
}
}
EXPECT_TRUE(GraphComparator().Compare(init, expected));
}
TEST_F(LoweringTest, Logic)
{
auto init = CreateEmptyGraph();
GRAPH(init)
{
PARAMETER(0, 0).u32();
PARAMETER(24, 1).s64();
CONSTANT(1, 12).s32();
CONSTANT(2, 50).s32();
CONSTANT(25, 0).s64();
CONSTANT(27, 300).s32();
BASIC_BLOCK(2, -1)
{
INST(3, Opcode::Or).u32().Inputs(0, 1);
INST(5, Opcode::Or).u32().Inputs(0, 2);
INST(6, Opcode::And).u32().Inputs(0, 1);
INST(8, Opcode::And).u32().Inputs(0, 2);
INST(9, Opcode::Xor).u32().Inputs(0, 1);
INST(11, Opcode::Xor).u32().Inputs(0, 2);
INST(12, Opcode::Or).u8().Inputs(0, 1);
INST(13, Opcode::And).u32().Inputs(0, 0);
INST(26, Opcode::And).s64().Inputs(24, 25);
INST(28, Opcode::Xor).u32().Inputs(0, 27);
INST(29, Opcode::Or).s64().Inputs(24, 25);
INST(30, Opcode::Xor).s64().Inputs(24, 25);
INST(31, Opcode::And).u32().Inputs(0, 27);
INST(32, Opcode::Or).u32().Inputs(0, 27);
INST(15, Opcode::SaveState).NoVregs();
INST(14, Opcode::CallStatic).b().InputsAutoType(3, 5, 6, 8, 9, 11, 12, 13, 26, 28, 29, 30, 31, 32, 15);
INST(23, Opcode::Return).b().Inputs(14);
}
}
#ifndef NDEBUG
init->SetLowLevelInstructionsEnabled();
#endif
init->RunPass<compiler::Lowering>();
init->RunPass<compiler::Cleanup>();
auto expected = CreateEmptyGraph();
GRAPH(expected)
{
PARAMETER(0, 0).u32();
PARAMETER(24, 1).s64();
CONSTANT(1, 12).s32();
CONSTANT(25, 0).s64();
BASIC_BLOCK(2, -1)
{
INST(33, Opcode::OrI).u32().Inputs(0).Imm(0xc);
INST(34, Opcode::OrI).u32().Inputs(0).Imm(0x32);
INST(35, Opcode::AndI).u32().Inputs(0).Imm(0xc);
INST(36, Opcode::AndI).u32().Inputs(0).Imm(0x32);
INST(37, Opcode::XorI).u32().Inputs(0).Imm(0xc);
INST(38, Opcode::XorI).u32().Inputs(0).Imm(0x32);
INST(12, Opcode::Or).u8().Inputs(0, 1);
INST(13, Opcode::And).u32().Inputs(0, 0);
INST(26, Opcode::And).s64().Inputs(24, 25);
INST(39, Opcode::XorI).u32().Inputs(0).Imm(0x12c);
INST(29, Opcode::Or).s64().Inputs(24, 25);
INST(30, Opcode::Xor).s64().Inputs(24, 25);
INST(40, Opcode::AndI).u32().Inputs(0).Imm(0x12c);
INST(41, Opcode::OrI).u32().Inputs(0).Imm(0x12c);
INST(15, Opcode::SaveState).NoVregs();
INST(14, Opcode::CallStatic).b().InputsAutoType(33, 34, 35, 36, 37, 38, 12, 13, 26, 39, 29, 30, 40, 41, 15);
INST(23, Opcode::Return).b().Inputs(14);
}
}
EXPECT_TRUE(GraphComparator().Compare(init, expected));
}
TEST_F(LoweringTest, Shift)
{
auto init = CreateEmptyGraph();
GRAPH(init)
{
PARAMETER(0, 0).u32();
PARAMETER(24, 1).s64();
CONSTANT(1, 12).s32();
CONSTANT(2, 64).s32();
CONSTANT(25, 0).s64();
CONSTANT(27, 200).s32();
BASIC_BLOCK(2, -1)
{
INST(3, Opcode::Shr).u32().Inputs(0, 1);
INST(5, Opcode::Shr).u32().Inputs(0, 2);
INST(6, Opcode::AShr).u32().Inputs(0, 1);
INST(8, Opcode::AShr).u32().Inputs(0, 2);
INST(9, Opcode::Shl).u32().Inputs(0, 1);
INST(11, Opcode::Shl).u32().Inputs(0, 2);
INST(12, Opcode::Shl).u8().Inputs(0, 1);
INST(13, Opcode::Shr).u32().Inputs(0, 0);
INST(26, Opcode::Shr).s64().Inputs(24, 25);
INST(28, Opcode::AShr).s32().Inputs(0, 27);
INST(29, Opcode::AShr).s64().Inputs(24, 25);
INST(30, Opcode::Shl).s64().Inputs(24, 25);
INST(31, Opcode::Shr).s32().Inputs(0, 27);
INST(32, Opcode::Shl).s32().Inputs(0, 27);
INST(15, Opcode::SaveState).NoVregs();
INST(14, Opcode::CallStatic).b().InputsAutoType(3, 5, 6, 8, 9, 11, 12, 13, 26, 28, 29, 30, 31, 32, 15);
INST(23, Opcode::Return).b().Inputs(14);
}
}
#ifndef NDEBUG
init->SetLowLevelInstructionsEnabled();
#endif
init->RunPass<compiler::Lowering>();
init->RunPass<compiler::Cleanup>();
auto expected = CreateEmptyGraph();
GRAPH(expected)
{
PARAMETER(0, 0).u32();
PARAMETER(24, 1).s64();
CONSTANT(1, 12).s32();
CONSTANT(2, 64).s32();
CONSTANT(25, 0).s64();
CONSTANT(27, 200).s32();
BASIC_BLOCK(2, -1)
{
INST(33, Opcode::ShrI).u32().Inputs(0).Imm(0xc);
INST(5, Opcode::Shr).u32().Inputs(0, 2);
INST(34, Opcode::AShrI).u32().Inputs(0).Imm(0xc);
INST(8, Opcode::AShr).u32().Inputs(0, 2);
INST(35, Opcode::ShlI).u32().Inputs(0).Imm(0xc);
INST(11, Opcode::Shl).u32().Inputs(0, 2);
INST(12, Opcode::Shl).u8().Inputs(0, 1);
INST(13, Opcode::Shr).u32().Inputs(0, 0);
INST(26, Opcode::Shr).s64().Inputs(24, 25);
INST(28, Opcode::AShr).s32().Inputs(0, 27);
INST(29, Opcode::AShr).s64().Inputs(24, 25);
INST(30, Opcode::Shl).s64().Inputs(24, 25);
INST(31, Opcode::Shr).s32().Inputs(0, 27);
INST(32, Opcode::Shl).s32().Inputs(0, 27);
INST(15, Opcode::SaveState).NoVregs();
INST(14, Opcode::CallStatic).b().InputsAutoType(33, 5, 34, 8, 35, 11, 12, 13, 26, 28, 29, 30, 31, 32, 15);
INST(23, Opcode::Return).b().Inputs(14);
}
}
EXPECT_TRUE(GraphComparator().Compare(init, expected));
}
} // namespace panda::bytecodeopt::test

View File

@ -0,0 +1,178 @@
## Benchmark of Bytecode
This folder contains scripts and documentaions for benchmarking the size
of the bytecode.
## `run_benchmark.py`
### Help
```sh
usage: Run bytecode benchmarks [-h] [--testdir TESTDIR] [--bindir BINDIR]
[--input-type {class,pa}] [--compiler-options LIST]
[--compiler-options-file FILE] [--json FILE] [--verbose]
optional arguments:
-h, --help show this help message and exit
--testdir TESTDIR Directory with tests (*.class or *.pa). Default:
'./bytecode_optimizer/tests/benchmark/suite'
--bindir BINDIR Directory with compiled binaries (eg.: c2p). Default: './build/bin'
--input-type {class,pa}
Type of the test input. Default: 'class'
--compiler-options LIST
Comma separated list of compiler options for C2P (see 'build/bin/c2p
--help' for details
--compiler-options-file FILE
Input file containing compiler options for C2P (see 'build/bin/c2p
--help' for details
--json FILE JSON dump file name
--verbose, -v Enable verbose messages
```
### How to use
```sh
bytecode_optimizer/tests/benchmark/run_benchmark.py --testdir=../benchmark-input --compiler-options-file=compiler.config --json=benchmark-results.json -v
```
where `compiler.config` contains a list of compiler options one per line, e.g.:
```sh
--compiler-lowering=true
--compiler-inlining=true
--compiler-lowering=true
--compiler-loop-peeling=true
--compiler-lse=true
--compiler-loop-unroll=true
```
### Example output
```
Average sizes (in bytes):
annotation_item section: 34
class_idx_item section: 32
class_item section: 223
code_item section: 158
debug_info_item section: 25
foreign_item section: 226
header_item section: 40
proto_item section: 61
string_item section: 239
total: 1042
Minimum sizes (in bytes):
annotation_item section: 16
class_idx_item section: 24
class_item section: 143
code_item section: 11
debug_info_item section: 7
foreign_item section: 144
header_item section: 40
proto_item section: 48
string_item section: 149
total: 713
Maximum sizes (in bytes):
annotation_item section: 72
class_idx_item section: 40
class_item section: 282
code_item section: 264
debug_info_item section: 43
foreign_item section: 307
header_item section: 40
proto_item section: 68
string_item section: 347
total: 1333
Summary:
========
Tests : 10
Passed: 7
Failed: 3
```
### JSON example
```
{
"StrictMath$RandomNumberGeneratorHolder.class": {
"annotation_item section": 43,
"class_idx_item section": 24,
"class_item section": 141,
"code_item section": 31,
"debug_info_item section": 12,
"foreign_item section": 158,
"header_item section": 40,
"line_number_program_item section": 8,
"proto_item section": 16,
"string_item section": 171,
"total": 644
},
"HttpDate.class": {
"error": "c2p: /panda/bytecode_optimizer/inst_builder.cpp:128:
void panda::bytecodeopt::InstBuilder::AddCatchPhi():
Assertion `catch_begin->IsCatchBegin()' failed.\n"
},
"SSLContextSpi.class": {
"error": "Unsupported instruction in alias analysis: 7.ref CatchPhi v10, v12, v12 -> (v19, v19, v18, v17, v17, v17)
c2p: /panda/compiler/optimizer/analysis/alias_analysis.cpp:395:
virtual void panda::compiler::AliasVisitor::VisitDefault(panda::compiler::Inst*):
Assertion inst->GetType() != DataType::REFERENCE && cond_val' failed."
}
}
```
## `compare.py`
This script can be used to compare the results of the `run_becnhmark.py` script.
### Help
```sh
usage: Compare benchmark results [-h] --old JSON_FILE_PATH --new JSON_FILE_PATH --failed JSON_FILE_PATH
optional arguments:
-h, --help show this help message and exit
--old JSON_FILE_PATH Base or reference benchmark result
--new JSON_FILE_PATH Benchmark result to be compared with the reference
--failed JSON_FILE_PATH
File to log error messages from c2p
```
### How to use
```sh
bytecode_optimizer/tests/benchmark/compare.py --old=reference.json --new=new.json --failed=error.json
```
### Example output
```
Classes that have been optimized:
Code_item section size:
|Old: |New: |Diff:|Per: |File:
| 1045| 1037| 8| 0.77%| ICULocaleService$LocaleKey.class
| 302| 294| 8| 2.65%| Locale$LocaleKey.class
| 5700| 5693| 7| 0.12%| LanguageTag.class
| 957| 949| 8| 0.84%| LinkedList$ListItr.class
| 780| 772| 8| 1.03%| FieldPosition.class
Summary:
=============
Total code_item section size of baseline files: 9201 bytes
Total code_item section size of compared files: 9162 bytes
Difference: 39 bytes [0.42%]
Number of optimized files: 5
Number of not optimized files : 1
Files with no code item section: 0
Files that are bigger than baseline: 0
Failed tests on baseline: 1
Failed tests compared to baseline: 0
=============
Statistics on optimized files:
=============
Total code_item section size of baseline files: 8784 bytes
Total code_item section size of compared files: 8745 bytes
Difference: 39 bytes [0.44%]
=============
```

View File

@ -0,0 +1,136 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright (c) 2021-2022 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.
import argparse
import os
import json
import subprocess
SRC_PATH = os.path.realpath(os.path.dirname(__file__))
parser = argparse.ArgumentParser("Compare benchmark results")
parser.add_argument("--old", metavar="JSON_FILE_PATH", required=True,
help="Base or reference benchmark result")
parser.add_argument("--new", metavar="JSON_FILE_PATH", required=True,
help="Benchmark result to be compared with the reference")
parser.add_argument("--failed", metavar="JSON_FILE_PATH", required=False,
help="File to log error messages from c2p")
def exists(name, d):
if name in d:
return True
return False
args = parser.parse_args()
if __name__ == '__main__':
if not os.path.exists(args.old):
print("Input file (%s) does not exists." % args.old)
exit(1)
if not os.path.exists(args.new):
print("Input file (%s) does not exists." % args.new)
exit(1)
with open(args.old, 'r') as old_fp, open(args.new, 'r') as new_fp:
old_res, new_res = json.load(old_fp), json.load(new_fp)
result = {}
failed_log_new = {}
failed_log_old = {}
flog = {}
optimized_files = 0 # number of files that have been optimized
not_optimized_files = 0 # files that are not optimized
de_optimized_files = 0 # code_item section is bigger than baseline
empty_files = 0 # files with no code_item section
old_size = 0
new_size = 0
sum_old = 0
sum_new = 0
failed_old = 0
failed_new = 0
sum_optimized_old = 0
sum_optimized_new = 0
for filename in old_res.keys():
if not exists("error", old_res[filename]):
if not exists("error", new_res[filename]):
if exists("code_item section", new_res[filename]):
old_size = old_res[filename]["code_item section"]
new_size = new_res[filename]["code_item section"]
sum_old += old_size
sum_new += new_size
diff = old_size - new_size
result[filename] = {"old_size": old_size,
"new_size": new_size, "diff": diff}
if diff > 0:
optimized_files += 1
sum_optimized_old += old_size
sum_optimized_new += new_size
elif diff < 0:
de_optimized_files += 1
else:
not_optimized_files += 1
else:
empty_files += 1
else:
failed_new += 1
failed_log_new[filename] = new_res[filename]
else:
failed_old += 1
failed_log_old[filename] = old_res[filename]
print("Classes that have been optimized:\n Code_item section size:\n|Old: |New: |Diff:|Per: |File:")
for r in result:
if result[r]["diff"] > 0:
print("|{:5d}|{:5d}|{:5d}|{:5.2f}%| {:s}".format(
result[r]["old_size"],
result[r]["new_size"],
result[r]["diff"],
100 * (1 - float(result[r]["new_size"]) / result[r]["old_size"]), r))
print("""\nSummary:\n=============\
\n Total code_item section size of baseline files: {:d} bytes\
\n Total code_item section size of compared files: {:d} bytes\
\n Difference: {:d} bytes [{:3.2f}%]\
\n Number of optimized files: {:d}\
\n Number of not optimized files : {:d}\
\n Files with no code item section: {:d}\
\n Files that are bigger than baseline: {:d}\
\n Failed tests on baseline: {:d}\
\n Failed tests compared to baseline: {:d}\
\n============="""
.format(sum_old, sum_new, sum_old - sum_new,
100 * (1 - float(sum_new) / sum_old) if sum_old != 0 else 0,
optimized_files, not_optimized_files, empty_files, de_optimized_files, failed_old, failed_new))
print("""\nStatistics on optimized files:\n============= \
\n Total code_item section size of baseline files: {:d} bytes\
\n Total code_item section size of compared files: {:d} bytes\
\n Difference: {:d} bytes [{:3.2f}%]\
\n============="""
.format(sum_optimized_old, sum_optimized_new,
sum_optimized_old - sum_optimized_new,
100 * (1 - float(sum_optimized_new) / sum_optimized_old) if sum_optimized_old != 0 else 0))
if args.failed:
with open(args.failed, 'w') as fp:
flog = {"Errors on baseline tests": failed_log_old,
"Errors on compared tests": failed_log_new}
json.dump(flog, fp, indent=4)
fp.write("\n")

View File

@ -0,0 +1,174 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright (c) 2021-2022 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.
import argparse
import os
import json
import subprocess
import tempfile
SRC_PATH = os.path.realpath(os.path.dirname(__file__))
testdir = os.path.join(SRC_PATH, "suite")
bindir = os.path.join(SRC_PATH, "..", "..", "..", "build", "bin")
parser = argparse.ArgumentParser("Run bytecode benchmarks")
parser.add_argument("--testdir", default=testdir,
help="Directory with tests (*.class or *.pa). Default: './%s'" % os.path.relpath(testdir))
parser.add_argument("--bindir", default=bindir,
help="Directory with compiled binaries (eg.: c2p). Default: './%s'" % os.path.relpath(bindir))
parser.add_argument("--input-type", choices=["class", "pa"], default="class",
help="Type of the test input. Default: '%(default)s'"),
parser.add_argument("--compiler-options", metavar="LIST",
help="Comma separated list of compiler options for C2P (see '%s --help' for details"
% (os.path.relpath(os.path.join(bindir, "c2p")))),
parser.add_argument("--compiler-options-file", metavar="FILE",
help="Input file containing compiler options for C2P (see '%s --help' for details"
% (os.path.relpath(os.path.join(bindir, "c2p")))),
parser.add_argument("--json", metavar="FILE",
help="JSON dump file name"),
parser.add_argument("--verbose", "-v", action='store_true',
help="Enable verbose messages")
args = parser.parse_args()
def log(msg):
if args.verbose:
print(msg)
def parse_c2p_output(name, stdout, stderr, returncode, d):
d[name] = {} # {'name': name: {'code_item section': 11}, {'total': 123}, ..}
result = {} # { 'name': name }
# TODO: Handle segmentation fault correctly
if returncode != 0:
d[name] = {"error": stderr.decode('ascii')}
else:
for stdout_line in stdout.decode('ascii').split("\n"):
if not stdout_line:
continue
key, value = stdout_line.split(":")[:2]
if value:
result[key] = int(value.strip())
d[name][key] = int(value.strip())
return result
def print_sizes_dict(sizes):
for d in sorted(sizes):
print("%28s: %d" % (d, sizes[d]))
def calc_statistics(sizes):
total = len(sizes)
if total == 0:
return None
keys = sizes[0].keys()
avgs = dict.fromkeys(keys, 0)
mins = dict.fromkeys(keys, 99999999)
maxs = dict.fromkeys(keys, 0)
for d in sizes:
for k in keys:
if k not in d:
continue
avgs[k] += d[k]
if d[k] < mins[k]:
mins[k] = d[k]
if d[k] > maxs[k]:
maxs[k] = d[k]
for d in avgs:
avgs[d] = round(avgs[d] / total, 1)
print("\nAverage sizes (in bytes):")
print_sizes_dict(avgs)
print("\nMinimum sizes (in bytes):")
print_sizes_dict(mins)
print("\nMaximum sizes (in bytes):")
print_sizes_dict(maxs)
return {"Average sizes (in bytes)": avgs,
"Minimum sizes (in bytes)": mins,
"Maximum sizes (in bytes)": maxs}
def run_c2p(test_dir, bin_dir, c2p_opts):
sizes = []
result = {}
tests_passed = 0
tests_failed = 0
c2p = os.path.join(bin_dir, "c2p")
if not os.path.exists(c2p):
print("c2p executable does not exists (%s)." % os.path.relpath(c2p))
exit(2)
fp = tempfile.NamedTemporaryFile()
for dirpath, dirnames, filenames in os.walk(test_dir):
for name in filenames:
if name.endswith(".class"):
test_path = os.path.join(dirpath, name)
proc = subprocess.Popen([c2p, "--size-stat"] + c2p_opts + [
test_path, fp.name], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = proc.communicate()
sizes.append(parse_c2p_output(test_path, stdout,
stderr, proc.returncode, result))
if proc.returncode == 0:
tests_passed += 1
else:
tests_failed += 1
log("Could not process the class file (%s)." % test_path)
pass
fp.close()
return sizes, tests_passed, tests_failed, result
############################################
if __name__ == '__main__':
if not os.path.exists(args.testdir):
print("Test directory (%s) does not exists." % args.testdir)
exit(1)
num_of_tests = 0
passed_tests = 0
failed_tests = 0
if args.input_type == "class":
c2p_options = []
if args.compiler_options_file:
with open(args.compiler_options_file) as conf_fp:
lines = conf_fp.readlines()
for line in lines:
c2p_options.append(line.strip())
if args.compiler_options:
c2p_options += args.compiler_options.split(",")
c2p_sizes, passed_tests, failed_tests, c2p_res = run_c2p(
args.testdir, args.bindir, c2p_options)
num_of_tests = passed_tests + failed_tests
stats = calc_statistics(c2p_sizes)
if args.json:
with open(args.json, 'w') as json_file:
json.dump(c2p_res, json_file, indent=4, sort_keys=True)
json_file.write("\n")
else:
print("Non class input types have not been implemented.")
exit(2)
print("Summary:\n========\n Tests : %d\n Passed: %d\n Failed: %d\n"
% (num_of_tests, passed_tests, failed_tests))

Some files were not shown because too many files have changed in this diff Show More