mirror of
https://gitee.com/openharmony/arkcompiler_runtime_core
synced 2024-11-23 06:40:32 +00:00
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:
parent
2e9080968d
commit
c658ccf319
3
AUTHORS
3
AUTHORS
@ -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
144
BUILD.gn
@ -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
|
||||
}
|
||||
|
168
CMakeLists.txt
168
CMakeLists.txt
@ -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
139
OAT.xml
@ -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:
|
||||
" == "
|
||||
& == &
|
||||
' == '
|
||||
< == <
|
||||
> == >
|
||||
-->
|
||||
<!-- 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:
|
||||
" == >
|
||||
& == >
|
||||
' == >
|
||||
< == >
|
||||
> == >
|
||||
-->
|
||||
<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>
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
127
ark_config.gni
127
ark_config.gni
@ -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
24
ark_root.gni
Normal 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"
|
||||
}
|
@ -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"
|
||||
}
|
||||
|
@ -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(".")
|
||||
|
@ -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;
|
||||
|
@ -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
19
assembler/asm_isapi.rb
Executable file → Normal 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
3
assembler/asm_metadata.rb
Executable file → Normal 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);"
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
@ -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
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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),
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
|
29
assembler/extensions/AssemblerExtPostPlugins.cmake
Normal file
29
assembler/extensions/AssemblerExtPostPlugins.cmake
Normal 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)
|
@ -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
|
||||
|
@ -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_
|
||||
|
83
assembler/extensions/register_extensions.h.erb
Normal file
83
assembler/extensions/register_extensions.h.erb
Normal 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
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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> ¶ms,
|
||||
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> ¶ms,
|
||||
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> ¶ms)
|
||||
{
|
||||
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
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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: {
|
||||
|
@ -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|
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
@ -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
140
bytecode_optimizer/BUILD.gn
Normal 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"
|
||||
}
|
122
bytecode_optimizer/CMakeLists.txt
Normal file
122
bytecode_optimizer/CMakeLists.txt
Normal 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(".")
|
71
bytecode_optimizer/bytecode_encoder.h
Normal file
71
bytecode_optimizer/bytecode_encoder.h
Normal 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
|
499
bytecode_optimizer/bytecode_optimizer_isapi.rb
Normal file
499
bytecode_optimizer/bytecode_optimizer_isapi.rb
Normal 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
|
24
bytecode_optimizer/bytecodeopt_options.h
Normal file
24
bytecode_optimizer/bytecodeopt_options.h
Normal 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
|
153
bytecode_optimizer/bytecodeopt_peepholes.cpp
Normal file
153
bytecode_optimizer/bytecodeopt_peepholes.cpp
Normal 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
|
90
bytecode_optimizer/bytecodeopt_peepholes.h
Normal file
90
bytecode_optimizer/bytecodeopt_peepholes.h
Normal 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_
|
130
bytecode_optimizer/canonicalization.cpp
Normal file
130
bytecode_optimizer/canonicalization.cpp
Normal 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
|
65
bytecode_optimizer/canonicalization.h
Normal file
65
bytecode_optimizer/canonicalization.h
Normal 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
|
93
bytecode_optimizer/check_resolver.cpp
Normal file
93
bytecode_optimizer/check_resolver.cpp
Normal 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
|
64
bytecode_optimizer/check_resolver.h
Normal file
64
bytecode_optimizer/check_resolver.h
Normal 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
|
57
bytecode_optimizer/cmake/coverage.cmake
Normal file
57
bytecode_optimizer/cmake/coverage.cmake
Normal 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)
|
1768
bytecode_optimizer/codegen.cpp
Normal file
1768
bytecode_optimizer/codegen.cpp
Normal file
File diff suppressed because it is too large
Load Diff
162
bytecode_optimizer/codegen.h
Normal file
162
bytecode_optimizer/codegen.h
Normal 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
|
93
bytecode_optimizer/common.cpp
Normal file
93
bytecode_optimizer/common.cpp
Normal 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
|
39
bytecode_optimizer/common.h
Normal file
39
bytecode_optimizer/common.h
Normal 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
|
335
bytecode_optimizer/const_array_resolver.cpp
Normal file
335
bytecode_optimizer/const_array_resolver.cpp
Normal 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
|
72
bytecode_optimizer/const_array_resolver.h
Normal file
72
bytecode_optimizer/const_array_resolver.h
Normal 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
|
150
bytecode_optimizer/ir_interface.h
Normal file
150
bytecode_optimizer/ir_interface.h
Normal 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
|
369
bytecode_optimizer/optimize_bytecode.cpp
Normal file
369
bytecode_optimizer/optimize_bytecode.cpp
Normal 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
|
30
bytecode_optimizer/optimize_bytecode.h
Normal file
30
bytecode_optimizer/optimize_bytecode.h
Normal 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_
|
58
bytecode_optimizer/options.yaml
Normal file
58
bytecode_optimizer/options.yaml
Normal 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
|
329
bytecode_optimizer/reg_acc_alloc.cpp
Normal file
329
bytecode_optimizer/reg_acc_alloc.cpp
Normal 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
|
59
bytecode_optimizer/reg_acc_alloc.h
Normal file
59
bytecode_optimizer/reg_acc_alloc.h
Normal 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_
|
639
bytecode_optimizer/reg_encoder.cpp
Normal file
639
bytecode_optimizer/reg_encoder.cpp
Normal 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 ®_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 ®_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
|
211
bytecode_optimizer/reg_encoder.h
Normal file
211
bytecode_optimizer/reg_encoder.h
Normal 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
|
403
bytecode_optimizer/runtime_adapter.h
Normal file
403
bytecode_optimizer/runtime_adapter.h
Normal 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_
|
61
bytecode_optimizer/templates/BytecodeOptPostPlugins.cmake
Normal file
61
bytecode_optimizer/templates/BytecodeOptPostPlugins.cmake
Normal 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
|
||||
)
|
66
bytecode_optimizer/templates/check_width.cpp.erb
Normal file
66
bytecode_optimizer/templates/check_width.cpp.erb
Normal 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
|
25
bytecode_optimizer/templates/check_width.h.erb
Normal file
25
bytecode_optimizer/templates/check_width.h.erb
Normal 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
|
45
bytecode_optimizer/templates/codegen_intrinsics.cpp.erb
Normal file
45
bytecode_optimizer/templates/codegen_intrinsics.cpp.erb
Normal 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";
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
371
bytecode_optimizer/tests/bc_lowering_test.cpp
Normal file
371
bytecode_optimizer/tests/bc_lowering_test.cpp
Normal 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
|
178
bytecode_optimizer/tests/benchmark/README.md
Normal file
178
bytecode_optimizer/tests/benchmark/README.md
Normal 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%]
|
||||
=============
|
||||
```
|
136
bytecode_optimizer/tests/benchmark/compare.py
Executable file
136
bytecode_optimizer/tests/benchmark/compare.py
Executable 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")
|
174
bytecode_optimizer/tests/benchmark/run_benchmark.py
Executable file
174
bytecode_optimizer/tests/benchmark/run_benchmark.py
Executable 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))
|
124
bytecode_optimizer/tests/bitops_bitwise_and_test.cpp
Normal file
124
bytecode_optimizer/tests/bitops_bitwise_and_test.cpp
Normal file
@ -0,0 +1,124 @@
|
||||
/**
|
||||
* 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 "codegen.h"
|
||||
#include "optimize_bytecode.h"
|
||||
#include "mangling.h"
|
||||
|
||||
namespace panda::bytecodeopt::test {
|
||||
|
||||
TEST_F(AsmTest, BitopsBitwiseAnd)
|
||||
{
|
||||
// naive translation of bitops-bitwise-and benchmark
|
||||
auto source = R"(
|
||||
.function u1 main() {
|
||||
movi.64 v0, 0x100000000
|
||||
mov.64 v4, v0
|
||||
movi v0, 0x1
|
||||
mov v6, v0
|
||||
label_1: mov v0, v6
|
||||
movi v1, 0x5f5e100
|
||||
lda v0
|
||||
jge v1, label_0
|
||||
mov.64 v0, v4
|
||||
mov v2, v6
|
||||
lda v2
|
||||
i32toi64
|
||||
sta.64 v2
|
||||
lda.64 v0
|
||||
and2.64 v2
|
||||
sta.64 v0
|
||||
mov.64 v4, v0
|
||||
inci v6, 0x1
|
||||
jmp label_1
|
||||
label_0: mov.64 v0, v4
|
||||
mov.64 v6, v0
|
||||
movi.64 v0, 0x0
|
||||
mov.64 v3, v0
|
||||
mov.64 v0, v6
|
||||
lda.64 v0
|
||||
cmp.64 v3
|
||||
sta v0
|
||||
lda v0
|
||||
jeqz label_2
|
||||
movi v0, 0x1
|
||||
lda v0
|
||||
return
|
||||
label_2: movi v0, 0x2
|
||||
lda v0
|
||||
return
|
||||
}
|
||||
)";
|
||||
|
||||
pandasm::Parser parser;
|
||||
auto res = parser.Parse(source);
|
||||
ASSERT_TRUE(res);
|
||||
auto &program = res.Value();
|
||||
|
||||
ASSERT_TRUE(ParseToGraph(&program, "main"));
|
||||
|
||||
EXPECT_TRUE(RunOptimizations(GetGraph()));
|
||||
|
||||
auto expected = CreateEmptyGraph();
|
||||
GRAPH(expected)
|
||||
{
|
||||
using namespace compiler::DataType;
|
||||
|
||||
BASIC_BLOCK(2, 3)
|
||||
{
|
||||
CONSTANT(23, 0x5f5e100).s32();
|
||||
CONSTANT(2, 1).s32();
|
||||
CONSTANT(1, 0x100000000).s64();
|
||||
INST(0, Opcode::SaveStateDeoptimize).NoVregs();
|
||||
INST(26, Opcode::SpillFill);
|
||||
}
|
||||
BASIC_BLOCK(3, 5, 4)
|
||||
{
|
||||
INST(4, Opcode::Phi).s64().Inputs(1, 10);
|
||||
INST(5, Opcode::Phi).s32().Inputs(2, 20);
|
||||
INST(19, Opcode::If).CC(compiler::CC_GE).SrcType(INT32).Inputs(5, 23);
|
||||
}
|
||||
BASIC_BLOCK(4, 3)
|
||||
{
|
||||
INST(9, Opcode::Cast).s64().SrcType(INT32).Inputs(5);
|
||||
INST(10, Opcode::And).s64().Inputs(9, 4);
|
||||
INST(20, Opcode::AddI).s32().Inputs(5).Imm(1);
|
||||
}
|
||||
BASIC_BLOCK(5, 7, 6)
|
||||
{
|
||||
CONSTANT(22, 0).s64();
|
||||
INST(13, Opcode::Cmp).s32().Inputs(4, 22);
|
||||
INST(15, Opcode::IfImm).SrcType(INT32).CC(compiler::CC_EQ).Imm(0).Inputs(13);
|
||||
}
|
||||
BASIC_BLOCK(6, -1)
|
||||
{
|
||||
INST(16, Opcode::Return).b().Inputs(2);
|
||||
}
|
||||
BASIC_BLOCK(7, -1)
|
||||
{
|
||||
CONSTANT(21, 2).s32();
|
||||
INST(18, Opcode::Return).b().Inputs(21);
|
||||
}
|
||||
}
|
||||
EXPECT_TRUE(GraphComparator().Compare(GetGraph(), expected));
|
||||
|
||||
const auto sig_main = pandasm::GetFunctionSignatureFromName("main", {});
|
||||
|
||||
auto &function = program.function_table.at(sig_main);
|
||||
EXPECT_TRUE(GetGraph()->RunPass<BytecodeGen>(&function, GetIrInterface()));
|
||||
}
|
||||
|
||||
} // namespace panda::bytecodeopt::test
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user