Add arkplatform and static_core

Commit in SIG repository is d4f68dcd4885305251453b0f4d5f986efbf7cb2a

Change-Id: I0e2de819f97978468c9dc36c2f5755179f374ed4
Signed-off-by: Artem Udovichenko <artem.udovichenko@huawei.com>
This commit is contained in:
Artem Udovichenko 2023-09-28 10:35:23 +03:00
parent edcdc248b3
commit d386df8ba4
6362 changed files with 1056672 additions and 2 deletions

11
.gitignore vendored
View File

@ -14,3 +14,14 @@ tags
.byebug_history
.cache
*.swp
static_core/extras/*
static_core/jenkins-ci
static_core/libllvmaot
static_core/scripts/*
static_core/platforms/*
static_core/plugins/accord
static_core/plugins/java
static_core/plugins/cangjie
static_core/plugins/ecmascript
static_core/plugins/hz_ecmascript
static_core/plugins/tests_java_ecmascript

24
arkplatform/BUILD.gn Normal file
View File

@ -0,0 +1,24 @@
import("//arkcompiler/runtime_core/ark_config.gni")
group("arkplatform_packages") {
deps = [ ":libarkplatform" ]
}
config("arkplatform_public_config") {
include_dirs = [
"include",
]
}
ohos_shared_library("libarkplatform") {
sources = [ "src/arkplatform.cpp" ]
include_dirs = [
"$ark_root/arkplatform",
"//base/hiviewdfx/hilog/interfaces/native/innerkits/include"
]
output_extension = "so"
part_name = "runtime_core"
subsystem_name = "arkcompiler"
external_deps = [ "hilog:libhilog" ]
}

View File

@ -0,0 +1,10 @@
#include <string>
namespace arkplatform {
class ArkPlatform {
public:
static void Create(const std::string& s);
};
}

View File

@ -0,0 +1,14 @@
#include <hilog/log.h>
#include "include/arkplatform.h"
namespace arkplatform {
void ArkPlatform::Create(const std::string& s) {
constexpr static unsigned int ARK_DOMAIN = 0xD003F00;
constexpr static auto TAG = "ArkPlatform";
constexpr static OHOS::HiviewDFX::HiLogLabel LABEL = {LOG_CORE, ARK_DOMAIN, TAG};
OHOS::HiviewDFX::HiLog::Info(LABEL, "%{public}s", "UDAV: Hello from arkplatform");
}
}

View File

@ -22,10 +22,15 @@
"ram": "",
"deps": {
"components": [
"hilog"
"hilog",
"napi"
],
"third_party": [
"bounds_checking_function"
"bounds_checking_function",
"icu",
"musl",
"vixl",
"zlib"
]
},
"build": {
@ -95,6 +100,27 @@
"header_files": [],
"header_base": "//arkcompiler/runtime_core/compiler"
}
},
{
"name": "//arkcompiler/runtime_core/arkplatform:libarkplatform",
"header": {
"header_files": [],
"header_base": "//arkcompiler/runtime_core/arkplatform/include"
}
},
{
"name": "//arkcompiler/runtime_core/static_core/runtime:libarkruntime",
"header": {
"header_files": [],
"header_base": "//arkcompiler/runtime_core/static_core/runtime/include"
}
},
{
"name": "//arkcompiler/runtime_core/static_core/plugins/ets/runtime/interop_js:ets_interop_js_napi",
"header": {
"header_files": [],
"header_base": "//arkcompiler/runtime_core/static_core/plugins/ets/runtime/interop_js"
}
}
],
"test": [

114
static_core/.clang-format Normal file
View File

@ -0,0 +1,114 @@
---
Language: Cpp
BasedOnStyle: Google
AccessModifierOffset: -4
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Left
AlignOperands: true
AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: true
AlwaysBreakTemplateDeclarations: true
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterClass: false
AfterControlStatement: false
AfterEnum: false
AfterFunction: true
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
AfterExternBlock: false
BeforeCatch: false
BeforeElse: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Custom
BreakBeforeInheritanceComma: false
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeColon
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 120
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IncludeBlocks: Regroup
IncludeCategories:
- Regex: '^<ext/.*\.h>'
Priority: 2
- Regex: '^<.*\.h>'
Priority: 1
- Regex: '^<.*'
Priority: 2
- Regex: '.*'
Priority: 3
IncludeIsMainRegex: '([-_](test|unittest))?$'
IndentCaseLabels: true
IndentPPDirectives: None
IndentWidth: 4
IndentWrappedFunctionNames: false
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: false
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBlockIndentWidth: 4
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 200
PointerAlignment: Right
ReflowComments: true
SortIncludes: false
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: true
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 2
SpacesInAngles: false
SpacesInContainerLiterals: false
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Cpp11
TabWidth: 4
UseTab: Never
...

139
static_core/.clang-tidy Normal file
View File

@ -0,0 +1,139 @@
---
WarningsAsErrors: "*"
AnalyzeTemporaryDtors: false
User: user
CheckOptions:
- key: google-readability-braces-around-statements.ShortStatementLines
value: "1"
- key: google-readability-function-size.StatementThreshold
value: "800"
- key: google-readability-namespace-comments.ShortNamespaceLines
value: "10"
- key: google-readability-namespace-comments.SpacesBeforeComments
value: "2"
- key: modernize-loop-convert.MaxCopySize
value: "16"
- key: modernize-loop-convert.MinConfidence
value: reasonable
- key: modernize-loop-convert.NamingStyle
value: CamelCase
- key: modernize-pass-by-value.IncludeStyle
value: llvm
- key: modernize-replace-auto-ptr.IncludeStyle
value: llvm
- key: modernize-use-nullptr.NullMacros
value: "NULL"
- key: readability-function-size.LineThreshold
value: 200
- key: readability-magic-numbers.IgnoredIntegerValues
value: "1;2;3;4;5;6;7;8"
- key: readability-identifier-naming.AbstractClassCase
value: CamelCase
- key: readability-identifier-naming.ClassCase
value: CamelCase
- key: readability-identifier-naming.ClassConstantCase
value: UPPER_CASE
- key: readability-identifier-naming.ClassMemberCase
value: lower_case
- key: readability-identifier-naming.ClassMemberSuffix
value: _
- key: readability-identifier-naming.ClassMethodCase
value: CamelCase
- key: readability-identifier-naming.ConstantCase
value: UPPER_CASE
- key: readability-identifier-naming.ConstantMemberCase
value: lower_case
- key: readability-identifier-naming.ConstantMemberSuffix
value: _
- key: readability-identifier-naming.ConstantParameterCase
value: lower_case
- key: readability-identifier-naming.ConstantPointerParameterCase
value: lower_case
- key: readability-identifier-naming.ConstexprFunctionCase
value: CamelCase
- key: readability-identifier-naming.ConstexprMethodCase
value: CamelCase
- key: readability-identifier-naming.ConstexprVariableCase
value: UPPER_CASE
- key: readability-identifier-naming.EnumCase
value: CamelCase
- key: readability-identifier-naming.EnumConstantCase
value: UPPER_CASE
- key: readability-identifier-naming.FunctionCase
value: CamelCase
- key: readability-identifier-naming.GlobalConstantCase
value: UPPER_CASE
- key: readability-identifier-naming.GlobalConstantPointerCase
value: UPPER_CASE
- key: readability-identifier-naming.GlobalFunctionCase
value: CamelCase
- key: readability-identifier-naming.GlobalPointerCase
value: UPPER_CASE
- key: readability-identifier-naming.GlobalVariableCase
value: UPPER_CASE
- key: readability-identifier-naming.InlineNamespaceCase
value: lower_case
- key: readability-identifier-naming.LocalConstantCase
value: lower_case
- key: readability-identifier-naming.LocalConstantPointerCase
value: lower_case
- key: readability-identifier-naming.LocalPointerCase
value: lower_case
- key: readability-identifier-naming.LocalVariableCase
value: lower_case
- key: readability-identifier-naming.MemberCase
value: lower_case
- key: readability-identifier-naming.MemberSuffix
value: _
- key: readability-identifier-naming.MethodCase
value: CamelCase
- key: readability-identifier-naming.NamespaceCase
value: lower_case
- key: readability-identifier-naming.ParameterCase
value: lower_case
- key: readability-identifier-naming.ParameterPackCase
value: lower_case
- key: readability-identifier-naming.PointerParameterCase
value: lower_case
- key: readability-identifier-naming.PrivateMemberCase
value: lower_case
- key: readability-identifier-naming.PrivateMemberSuffix
value: _
- key: readability-identifier-naming.PrivateMethodCase
value: CamelCase
- key: readability-identifier-naming.ProtectedMemberCase
value: lower_case
- key: readability-identifier-naming.ProtectedMemberSuffix
value: _
- key: readability-identifier-naming.ProtectedMethodCase
value: CamelCase
- key: readability-identifier-naming.PublicMemberCase
value: lower_case
- key: readability-identifier-naming.PublicMemberSuffix
value: ""
- key: readability-identifier-naming.PublicMethodCase
value: CamelCase
- key: readability-identifier-naming.StaticConstantCase
value: UPPER_CASE
- key: readability-identifier-naming.StaticVariableCase
value: lower_case
- key: readability-identifier-naming.StructCase
value: CamelCase
- key: readability-identifier-naming.TemplateParameterCase
value: CamelCase
- key: readability-identifier-naming.TemplateTemplateParameterCase
value: CamelCase
- key: readability-identifier-naming.TypeAliasCase
value: CamelCase
- key: readability-identifier-naming.TypedefCase
value: CamelCase
- key: readability-identifier-naming.TypeTemplateParameterCase
value: CamelCase
- key: readability-identifier-naming.UnionCase
value: CamelCase
- key: readability-identifier-naming.ValueTemplateParameterCase
value: UPPER_CASE
- key: readability-identifier-naming.VariableCase
value: lower_case
- key: readability-identifier-naming.VirtualMethodCase
value: CamelCase

23
static_core/.gn Normal file
View File

@ -0,0 +1,23 @@
# 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.
# This is an entry point for Ark standalone build with gn.
# It is ignored when Ark is built as a part of OpenHarmony source tree.
buildconfig = "//build/config/BUILDCONFIG.gn"
secondary_source = "//gn"
script_executable = "/usr/bin/env"
exec_script("//scripts/install-third-party")
exec_script("//gn/build/fixup-third-party.sh")

189
static_core/AUTHORS Normal file
View File

@ -0,0 +1,189 @@
Aleksandr Emelenko
Aleksandr Latikov
Aleksandr Lovkov
Aleksandr Popov
Aleksandr Semenov
Aleksandr Tokmakov
Aleksandr Urakov
Aleksandr Vorobyev
Alexander Polyakov
Aleksei Akmaev
Aleksei Grebenkin
Aleksei Nedoria
Aleksei Sidorov
Alexey Biryukov
Alexey Kanatov
Alexey Romanov
Alexey Volokhov
Alina Chernikova
Alina Kropacheva
An Guanglin
Andrey Efremov
Anna Antipina
Anton Makarenko
Anton Romanov
Anton Soldatov
Artem Udovichenko
Chen Mudan
Chen Qingyuan
Chen Qiuyao
Chen Tingwei
Chen Yilong
Csaba Osztrogonac
Csaba Repasi
Cui Guihua
Dai Huina
Daniel Batiz
Daniel Batyai
Daniil Kochergin
Daniil Kofanov
Denis Krylov
Denis Silakov
Denis Slynko
Denis Tomashev
Denis Zakharov
Denis Zavedeev
Ding Ding
Ding Shenke
Ding Wen
Dmitrii Trubenkov
Dmitry Bubnov
Dmitry Buzmakov
Dmitry Kovalenko
Dmitry Nechitaev
Dmitry Popov
Dmitry Solomennikov
Dong Kaixing
Du Qing
Evgenii Kudriashov
Evgeny Erokhin
Evgeny Gavrin
Evgeny Gerlits
Evgeny Sharygin
Feng Feng
Gan Lan
Gao Lei
Ge Tingke
Georgy Bronnikov
Gong Junsong
Guo Bingbing
Hao Tuo
Hong Tao
Hu Feng
Hu Xiaowei
Huang Feijie
Huang Haitao
Huang Huijin
Huang Yu
Igor Gorban
Igor Petrov
Ilya Makarov
Ilya Trubachev
Ilya Tyulyandin
Ivan Vagin
Ivan Burimskii
Ivan Trubachev
Ivan Tyulyandin
Ji Andong
Jiang Han
Jiang Suqin
Kirill Galitskiy
Kira Prokopenko
Konstantin Baladurin
Konstantin Nazarov
Laszlo Lango
Leonid Dyachkov
Leonid Skvortsov
Li Chenshuai
Li Wentao
Li Yiming
Li Yongbiao
Lin Xiang
Liu Chiming
Liu Jialiang
Liu Xin
Lu Kai
Luo Chuhao
Maria Filippova
Mark Gonopolskiy
Martin Negyorku
Maxim Bolshov
Maxim Morozov
Maxim Tsyngaev
Maxim Zimnyukov
Mikhail Aksenov
Mikhail Chernov
Mikhail Kaskov
Mikhail Redkin
Mikhail Sherstennikov
Mikita Strizhak
Nikita Kharitonov
Nikita Sizov
Pan Zhenyu
Pang Desong
Pavel Andrianov
Pavel Ishin
Pavel Kosov
Pei Jiajun
Peng Biao
Petr Shumilov
Qiu Yu
Robert Fancsik
Robert Sipka
Roland Takacs
Roman Baev
Roman Sannikov
Roman Zhuykov
Sergei Shadrin
Sergey Chernykh
Sergey Goldenberg
Sergey Nikitin
Stepan Dyatkovskiy
Su Chongwei
Sun Yuanlei
Sun Zhe
Tang Jiajun
Vadim Mutilin
Vadim Sofin
Vasil Dyadov
Viktor Foteev
Viktor Kutuzov
Vitalii Mordan
Vladimir Kuznetsov
Vladimir Popov
Vladimir Rubanov
Vladislav Ivanishin
Vsevolod Pukhov
Vyacheslav Cherkashin
Wan Yanglan
Wang Deyu
Wang Gang
Wang Shangling
Wang Xin
Wang Yantian
Wang Yaofeng
Wang Zhaoyong
Weng Changcheng
Wu Pengyong
Wu Zhefeng
Xian Yuqiang
Xiong Luo
Xu Cheng
Xu Jie
Xu Liang
Yan Churkin
Yang Yang
Yao Jian
Ye Xiangrun
Yi Honglie
Yin Chuang
Ying Guofeng
Yu Jianchao
Yury Nikitin
Zhang Quan
Zhang Rengao
Zhang Yukun
Zhao Gaoyi
Zheng Jiahuan
Zhou Cong
Zhou Mingsong

318
static_core/BUILD.gn Normal file
View File

@ -0,0 +1,318 @@
# 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/static_core/ark_config.gni")
import("//build/ohos.gni")
import("$ark_root/plugins/plugins.gni")
group("ark_packages") {
deps = plugin_ark_packages
if (host_os != "mac") {
deps += [
"$ark_root/compiler:libarktscompiler",
"$ark_root/compiler/aot:libarkaotmanager",
"$ark_root/compiler/optimizer/code_generator:libarkencoder",
"$ark_root/libpandabase:libarktsbase",
"$ark_root/libpandafile:libarktsfile",
"$ark_root/libpandafile/external:libarkfileExt",
"$ark_root/libpandafile/external:libarksupport",
"$ark_root/libziparchive:libarktsziparchive",
"$ark_root/panda:arkts_bin",
"$ark_root/runtime:libarkruntime",
"$ark_root/runtime/tooling/inspector:libarkinspector",
"$ark_root/verification/verifier:verifier.config",
"$ark_root/verification/verifier:verifier_bin",
]
# TODO: don't exclude these targets from OHOS build
if (ark_standalone_build) {
deps += [
"$ark_root/compiler/tools/paoc:ark_aot",
]
}
}
}
group("ark_host_linux_tools_packages") {
deps = plugin_ark_host_linux_tools_packages
if (host_os != "mac" && current_cpu != "arm") {
deps += [
"$ark_root/assembler:arkts_asm(${host_toolchain})",
"$ark_root/compiler:libarktscompiler(${host_toolchain})",
"$ark_root/compiler/aot:libarkaotmanager(${host_toolchain})",
"$ark_root/compiler/optimizer/code_generator:libarkencoder(${host_toolchain})",
"$ark_root/libpandabase:libarktsbase(${host_toolchain})",
"$ark_root/libpandafile:libarktsfile(${host_toolchain})",
"$ark_root/libpandafile/external:libarkfileExt(${host_toolchain})",
"$ark_root/libpandafile/external:libarksupport(${host_toolchain})",
"$ark_root/libziparchive:libarktsziparchive(${host_toolchain})",
"$ark_root/panda:arkts_bin(${host_toolchain})",
"$ark_root/runtime:libarkruntime(${host_toolchain})",
]
# TODO: don't exclude these targets from OHOS build
if (ark_standalone_build) {
deps += [
"$ark_root/compiler/tools/paoc:ark_aot(${host_toolchain})",
"$ark_root/compiler/tools/aotdump:ark_aotdump(${host_toolchain})",
"$ark_root/disassembler:ark_disasm(${host_toolchain})",
"$ark_root/static_linker:ark_link(${host_toolchain})",
]
}
}
}
group("ark_host_windows_tools_packages") {
deps = plugin_ark_host_windows_tools_packages
if (host_os != "mac" && current_cpu != "arm" && !ark_standalone_build) {
deps += [
"$ark_root/assembler:arkts_asm(//build/toolchain/mingw:mingw_x86_64)",
"$ark_root/disassembler:ark_disasm(//build/toolchain/mingw:mingw_x86_64)",
"$ark_root/static_linker:ark_link(//build/toolchain/mingw:mingw_x86_64)",
]
}
}
group("ark_host_mac_tools_packages") {
deps = plugin_ark_host_mac_tools_packages
if (host_os == "mac") {
deps += [
"$ark_root/assembler:arkts_asm(//build/toolchain/mac:clang_x64)",
"$ark_root/disassembler:ark_disasm(//build/toolchain/mac:clang_x64)",
"$ark_root/static_linker:ark_link(//build/toolchain/mac:clang_x64)",
"$ark_root/libpandabase:libarktsbase(//build/toolchain/mac:clang_x64)",
"$ark_root/libpandafile:libarktsfile(//build/toolchain/mac:clang_x64)",
"$ark_root/libziparchive:libarktsziparchive(//build/toolchain/mac:clang_x64)",
]
}
}
group("ark_host_tests") {
testonly = true
deps = []
if (defined(extras)) {
foreach(ext, extras) {
deps += [ "${ext}:host_tests" ]
}
}
}
# Common config for ark source
config("ark_config") {
if (!ark_standalone_build) {
visibility = [
"./*",
"$ark_third_party_root/asmjit:*",
"$ark_third_party_root/vixl:*"
]
}
# In GN build we just create merged files, without any content. Because no languages are involved in GN build.
if (current_toolchain == default_toolchain) {
generated_file("create_pipeline") { outputs = ["$target_gen_dir/plugins/create_pipeline.h"] contents = "" }
generated_file("create_pipeline_includes") { outputs = ["$target_gen_dir/plugins/create_pipeline_includes.h"] contents = "" }
generated_file("clear_profile") { outputs = ["$target_gen_dir/plugins/clear_profile.h"] contents = "" }
generated_file("destroy_profile") { outputs = ["$target_gen_dir/plugins/destroy_profile.h"] contents = "" }
generated_file("dump_profile") { outputs = ["$target_gen_dir/plugins/dump_profile.h"] contents = "" }
generated_file("find_method_in_profile") { outputs = ["$target_gen_dir/plugins/find_method_in_profile.h"] contents = "" }
generated_file("profiling_includes_disasm") { outputs = ["$target_gen_dir/plugins/profiling_includes_disasm.h"] contents = "" }
generated_file("profiling_includes") { outputs = ["$target_gen_dir/plugins/profiling_includes.h"] contents = "" }
generated_file("read_profile") { outputs = ["$target_gen_dir/plugins/read_profile.h"] contents = "" }
}
include_dirs = [
"$ark_root",
get_label_info(":create_pipeline(${default_toolchain})", "target_gen_dir")
]
defines = [ "PANDA_TARGET_MOBILE_WITH_MANAGED_LIBS=1" ]
if (is_llvmaot) {
defines += [
"PANDA_LLVMAOT",
"PANDA_LLVMAOT_IR_INLINING",
]
}
if (is_ohos && is_standard_system) {
defines += [ "PANDA_TARGET_OHOS" ]
include_dirs += [
"//base/hiviewdfx/hilog/interfaces/innerkits/include"
]
}
if (is_linux) {
defines += [
"PANDA_TARGET_UNIX",
"PANDA_TARGET_LINUX",
"PANDA_WITH_BYTECODE_OPTIMIZER",
"PANDA_WITH_COMPILER",
]
if (!is_asan) {
defines += [ "PANDA_USE_FUTEX" ]
}
} else if (is_mingw) {
defines += [
"PANDA_TARGET_WINDOWS",
"PANDA_WITH_BYTECODE_OPTIMIZER",
"PANDA_WITH_COMPILER",
"_CRTBLD",
"__LIBMSVCRT__",
]
} else if (is_mac) {
defines += [
"PANDA_TARGET_UNIX",
"PANDA_TARGET_MACOS",
"PANDA_WITH_BYTECODE_OPTIMIZER",
"PANDA_WITH_COMPILER",
# "PANDA_USE_FUTEX",
]
} else if (is_mob) {
defines += [
"PANDA_TARGET_UNIX",
"PANDA_USE_FUTEX",
"PANDA_TARGET_MOBILE",
"PANDA_TARGET_MOBILE_WITH_NATIVE_LIBS",
]
} else if (is_ohos) {
defines += [
# TODO: use PANDA_TARGET_OHOS instead of PANDA_TARGET_UNIX
"PANDA_TARGET_UNIX",
"PANDA_USE_FUTEX",
]
} else {
defines += [
"PANDA_TARGET_UNIX",
"PANDA_USE_FUTEX",
]
}
if (!is_debug) {
defines += [ "NDEBUG" ]
}
cflags_cc = [
"-std=c++17",
"-pedantic",
"-Wall",
"-Wextra",
"-Werror",
"-fno-rtti",
"-fno-exceptions",
"-Wno-invalid-offsetof",
"-Wno-gnu-statement-expression",
"-Wno-unused-parameter",
"-Wno-unused-result",
]
if (ark_standalone_build) {
cflags_cc += [ "-Wno-bitwise-instead-of-logical" ]
}
if (!is_mac && use_pbqp) {
cflags_cc += [
# PBQP regalloc
"-mllvm",
"-regalloc=pbqp",
]
}
if (is_debug) {
cflags_cc += [
"-Og",
"-ggdb3",
"-gdwarf-4",
]
}
if (is_asan) {
cflags_cc += [
"-fsanitize=address",
"-g",
]
defines += [ "__SANITIZE_ADDRESS__" ]
print("ASAN is enabled")
}
if (enable_relayout_profile) {
defines += [ "PANDA_ENABLE_RELAYOUT_PROFILE" ]
}
configs = plugin_ark_configs
if (current_cpu == "arm") {
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_32",
"PANDA_TARGET_ARM32"
]
} else if (current_cpu == "arm64") {
defines += [
"PANDA_TARGET_ARM64",
"PANDA_TARGET_64",
"PANDA_ENABLE_GLOBAL_REGISTER_VARIABLES",
"PANDA_USE_32_BIT_POINTER",
]
} else if (current_cpu == "x86") {
defines += [ "PANDA_TARGET_X86" ]
} else if (current_cpu == "amd64" || current_cpu == "x64" ||
current_cpu == "x86_64") {
defines += [
"PANDA_TARGET_64",
"PANDA_TARGET_AMD64",
"PANDA_USE_32_BIT_POINTER",
]
}
}
concat_yamls("concat_plugins_yamls") {
output_file = "$ark_plugin_options_yaml"
default_file = "$ark_root/templates/plugin_options.yaml"
add_yamls = plugin_option_yamls
}
concat_yamls("concat_entrypoints_yamls") {
output_file = "$target_gen_dir/runtime/entrypoints.yaml"
default_file = "$ark_root/runtime/entrypoints/entrypoints.yaml"
add_yamls = plugin_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 = plugin_inst_templates_yamls
}
merge_yamls("merge_runtime_options_yamls") {
output_file = "$target_gen_dir/runtime_options.yaml"
add_yamls = ["$ark_root/runtime/options.yaml"] + plugin_runtime_options_yamls
}
merge_yamls("merge_compiler_options_yamls") {
output_file = "$target_gen_dir/compiler/generated/compiler_options.yaml"
add_yamls = ["$ark_root/compiler/compiler.yaml"] + plugin_compiler_options_yamls
}

396
static_core/CMakeLists.txt Normal file
View File

@ -0,0 +1,396 @@
# 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.10 FATAL_ERROR)
project(PANDA NONE)
# Add our custom configuration types to
# multi-configuration generators (i.e. Visual Studio):
if(CMAKE_CONFIGURATION_TYPES)
list(APPEND CMAKE_CONFIGURATION_TYPES "FastVerify" "DebugDetailed")
list(REMOVE_DUPLICATES CMAKE_CONFIGURATION_TYPES)
set(CMAKE_CONFIGURATION_TYPES ${CMAKE_CONFIGURATION_TYPES}
CACHE STRING "CMake configuration types" FORCE)
endif()
enable_language(CXX)
# NB! For God's sake do not touch the command below.
# See https://gitlab.kitware.com/cmake/cmake/issues/16588.
# and https://clang.llvm.org/docs/JSONCompilationDatabase.html
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# ----- Default flags ----------------------------------------------------------
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
# ----- Global variables -------------------------------------------------------
# Please don't use CMAKE_SOURCE_DIR and CMAKE_BINARY_DIR to allow building
# Panda as a cmake subdirectory. You can use the following variables if you
# need to refer the Panda root directories
set(PANDA_ROOT ${CMAKE_CURRENT_SOURCE_DIR})
set(PANDA_BINARY_ROOT ${CMAKE_CURRENT_BINARY_DIR})
set(PANDA_THIRD_PARTY_SOURCES_DIR ${PANDA_ROOT}/third_party)
set(PANDA_THIRD_PARTY_CONFIG_DIR ${PANDA_ROOT}/cmake/third_party)
# List for accumulation of all core gtests binary paths.
# It's used by CI to archive these binaries into a single artifact
# and send it to second stage where unit tests will use them.
set_property(GLOBAL PROPERTY stash_list "")
# ----- Policies ---------------------------------------------------------------
# Allows the target_link_libraries() command to be called from any directory
# to add link dependencies and link interface libraries to targets created in
# other directories
if(POLICY CMP0079)
cmake_policy(SET CMP0079 NEW)
endif()
# ----- Platform definitions ---------------------------------------------------
include(cmake/Definitions.cmake)
if (NOT "${CMAKE_BUILD_TYPE}" MATCHES "Release" AND NOT PANDA_TARGET_WINDOWS)
# Needed for stacktrace printing
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -rdynamic")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -rdynamic")
endif()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic -Wall -Wextra -Werror -Wshadow")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti -fno-exceptions")
include(cmake/toolchain/coverage/unit_tests_lcov.cmake)
if(ENABLE_UNIT_TESTS_FULL_COVERAGE)
add_custom_target(coverage_full DEPENDS cts-assembly tests benchmarks)
collect_coverage_for_target(TARGET_NAME coverage_full)
else()
message(STATUS "Full coverage will not be calculated (may be enabled by -DENABLE_UNIT_TESTS_FULL_COVERAGE=true ).")
endif(ENABLE_UNIT_TESTS_FULL_COVERAGE)
if(PANDA_TARGET_MACOS)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmacosx-version-min=10.13")
endif()
set(PANDA_PGO_PROFGEN_PATH "/data/local/tmp")
if (PANDA_TARGET_MOBILE AND (PANDA_TARGET_ARM64 OR PANDA_TARGET_ARM32))
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fuse-ld=lld")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fuse-ld=lld")
endif()
if (PANDA_PGO_INSTRUMENT)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-generate=${PANDA_PGO_PROFGEN_PATH}")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fprofile-generate=${PANDA_PGO_PROFGEN_PATH}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fprofile-generate=${PANDA_PGO_PROFGEN_PATH}")
endif()
if (PANDA_PGO_OPTIMIZE)
if (NOT PANDA_PGO_PROFILE_DATA)
message(FATAL_ERROR "PANDA_PGO_PROFILE_DATA is not set")
endif()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-use=${PANDA_PGO_PROFILE_DATA}")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fprofile-use=${PANDA_PGO_PROFILE_DATA}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fprofile-use=${PANDA_PGO_PROFILE_DATA}")
endif()
if (PANDA_ENABLE_LTO)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -flto=thin")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -flto=thin")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -flto=thin")
endif()
if (PANDA_LLVM_REGALLOC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mllvm -regalloc=${PANDA_LLVM_REGALLOC}")
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 -fno-omit-frame-pointer)
elseif ("${CMAKE_BUILD_TYPE}" STREQUAL "DebugDetailed")
add_compile_options(-Og -ggdb3 -fno-omit-frame-pointer)
endif()
if (PANDA_THREAD_SAFETY)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wthread-safety")
endif()
include(cmake/RegisterPlugins.cmake)
panda_promote_to_definitions(
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.
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()
if (PANDA_LLVMAOT AND NOT EXISTS "${PANDA_ROOT}/libllvmaot")
message(FATAL_ERROR "PANDA_LLVMAOT is ${PANDA_LLVMAOT}, but the libllvmaot dir does not exist")
endif()
# ----- Alias for source generating targets ------------------------------------
add_custom_target(panda_gen_files COMMENT "Generate all sources")
# ----- Panda CMake functions --------------------------------------------------
include(cmake/PandaCmakeFunctions.cmake)
if (PANDA_LLVMAOT)
# ----- Enable LLVM AOT compiler -------------------------------------------
include(libllvmaot/cmake/LLVM.cmake)
# ----- Enable LLVM Inline modules -----------------------------------------
include(libllvmaot/cmake/LLVMInlineModules.cmake)
endif ()
# ----- Bootstrapping (for parts of platform written in managed code ) ---------
include(cmake/HostTools.cmake)
if (PANDA_ENABLE_CCACHE)
# ----- Enable CCache ----------------------------------------------------------
include(cmake/PandaCCache.cmake)
# ----- Enable SCCache ---------------------------------------------------------
include(cmake/PandaSCCache.cmake)
endif()
# ----- Documentation generation -----------------------------------------------
include(cmake/Doxygen.cmake)
# ----- Code analysis and style ------------------------------------------------
include(cmake/ClangTidy.cmake)
include(cmake/CodeStyle.cmake)
# ----- Sanitizers testing -----------------------------------------------------
include(cmake/Sanitizers.cmake)
# ----- Enable testing ---------------------------------------------------------
# Umbrella target for testing:
add_custom_target(tests COMMENT "Running all test suites")
define_property(TARGET
PROPERTY first-level-tests-dependency
BRIEF_DOCS "Whether the target is a first-level dependency of tests"
FULL_DOCS "Whether the target is a first-level dependency of tests")
add_custom_target(core_tests COMMENT "Running core test suites")
# NB! ADDING THIS PROPERTY IS ALLOWED ONLY IN SPECIAL CASES. DO NOT COPY-PASTE IT.
set_target_properties(core_tests PROPERTIES first-level-tests-dependency TRUE)
add_dependencies(tests core_tests)
include(cmake/Testing.cmake)
# ----- Template Based Generator -----------------------------------------------
include(cmake/TemplateBasedGen.cmake)
# ----- Enable panda assemblies building ---------------------------------------
include(cmake/PandaAssembly.cmake)
# Some compilers use x87 fp instructions by default in 32-bit mode.
# We need to use SSE one to correspond x86_64 build.
if (PANDA_TARGET_X86)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfpmath=sse -msse3")
endif()
# ----- Targets ----------------------------------------------------------------
execute_process(COMMAND ${PANDA_ROOT}/scripts/install-third-party
WORKING_DIRECTORY ${PANDA_ROOT}
RESULT_VARIABLE THIRD_PARTY_OK)
if (NOT THIRD_PARTY_OK EQUAL 0)
message(FATAL_ERROR "Unable to install required third-party dependencies")
endif()
if(PANDA_WITH_TOOLCHAIN)
add_subdirectory(isa)
set(SECUREC_ROOT ${PANDA_THIRD_PARTY_SOURCES_DIR}/utils_native/base)
add_subdirectory(${PANDA_THIRD_PARTY_CONFIG_DIR}/securec)
add_subdirectory(libpandabase)
set(ZLIB_ROOT ${PANDA_THIRD_PARTY_SOURCES_DIR}/zlib)
add_subdirectory(${PANDA_THIRD_PARTY_CONFIG_DIR}/zlib)
add_subdirectory(libziparchive)
add_subdirectory(libpandafile)
if(NOT PANDA_TARGET_WINDOWS)
add_subdirectory(libpandafile/external)
endif()
add_subdirectory(assembler)
add_subdirectory(disassembler)
add_subdirectory(static_linker)
if(PANDA_WITH_RUNTIME)
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)
set(PREV_CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-bitwise-instead-of-logical -Wno-unused-but-set-variable -Wno-deprecated-copy -Wno-unknown-warning-option")
add_subdirectory(${PANDA_THIRD_PARTY_SOURCES_DIR}/asmjit)
add_subdirectory(${PANDA_THIRD_PARTY_SOURCES_DIR}/zydis)
set(CMAKE_CXX_FLAGS "${PREV_CMAKE_CXX_FLAGS}")
endif()
if (PANDA_LLVMAOT)
add_subdirectory(libllvmaot)
endif()
set(PREV_CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-bitwise-instead-of-logical -Wno-unknown-warning-option")
add_subdirectory(${PANDA_THIRD_PARTY_SOURCES_DIR}/vixl)
set(CMAKE_CXX_FLAGS "${PREV_CMAKE_CXX_FLAGS}")
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()
panda_add_library(arkcompiler ${PANDA_DEFAULT_LIB_TYPE})
panda_add_library(arkbytecodeopt ${PANDA_DEFAULT_LIB_TYPE})
panda_add_library(arkaotmanager ${PANDA_DEFAULT_LIB_TYPE})
endif()
if(PANDA_WITH_RUNTIME)
add_subdirectory(pandastdlib)
if(NOT PANDA_TARGET_WINDOWS)
add_subdirectory(dprof)
endif()
add_subdirectory(runtime)
add_subdirectory(panda)
add_subdirectory(verification/verifier)
set(ICU_ROOT ${PANDA_THIRD_PARTY_SOURCES_DIR}/icu)
add_subdirectory(${PANDA_THIRD_PARTY_CONFIG_DIR}/icu)
endif()
# ----- Testing ----------------------------------------------------------------
if(PANDA_WITH_TESTS)
add_subdirectory(${PANDA_THIRD_PARTY_SOURCES_DIR}/googletest)
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)
option(RC_ENABLE_RTTI OFF)
if(NOT PANDA_USE_PREBUILT_TARGETS)
# Skip rapidcheck building when using prebuilt targets, because we don't need static
# libraries at this stage
add_subdirectory(${PANDA_THIRD_PARTY_SOURCES_DIR}/rapidcheck)
add_subdirectory(${PANDA_THIRD_PARTY_SOURCES_DIR}/rapidcheck/extras/gtest)
panda_target_compile_options(rapidcheck PUBLIC "-fexceptions" "-fno-rtti" "-fPIC")
endif()
add_subdirectory(tests)
add_custom_target(tests_full COMMENT "Running all test suites and code checks")
add_dependencies(tests_full
tests
cmake-checker
test-cmake-checker
)
if (NOT PANDA_TARGET_MACOS)
add_dependencies(tests_full code-style-check)
endif()
add_subdirectory(scripts)
endif()
# ----- Aliases ----------------------------------------------------------------
add_custom_target(panda_bins COMMENT "Build all common Panda binaries")
add_dependencies(panda_bins panda pandasm ark_disasm ark_link paoc verifier aspt_converter)
# ----- Benchmarking -----------------------------------------------------------
if(PANDA_WITH_BENCHMARKS)
# NB! Please do not merge benchmarks and tests unless you want to mess with
# slow builds, etc. If you want some coupling, you might want to make benchmarks
# depend on tests some day.
add_custom_target(benchmarks COMMENT "Running all benchmark suites")
add_subdirectory(tests/benchmarks)
endif()
# ----- Plugins ----------------------------------------------------------------
add_subdirectory(plugins)
include(cmake/PostPlugins.cmake)
# ----- Platforms --------------------------------------------------------------
add_subdirectory(platforms)
# ----- Extras ----------------------------------------------------------------
add_subdirectory(extras)
# ----- Quickening tool --------------------------------------------------------
add_subdirectory(quickener)
# ----- Panda tools -----------------------------------------------
add_subdirectory(tools)
# ----- Collecting core gtests paths for CI stash ---------------------------------
# NB! This must be the last section!
# Check that the tests target has only first-level groups as dependencies.
# If you want to create a new first-level group of tests, mark it as 'first-level-tests-dependency'
# using the following command set_target_properties(<target> PROPERTIES first-level-tests-dependency TRUE)
# and don't forget to handle it on CI.
get_target_property(dependencies tests MANUALLY_ADDED_DEPENDENCIES)
foreach(dep IN LISTS dependencies)
get_target_property(first_level_dep ${dep} first-level-tests-dependency)
if(NOT first_level_dep)
message(FATAL_ERROR "Target ${dep} must not be added to tests dependencies directly.")
endif()
endforeach()
# Write to a file the list of all binaries
get_property(stash_files GLOBAL PROPERTY stash_list)
list(JOIN stash_files "\n" file_content)
file(WRITE ${PANDA_BINARY_ROOT}/core_stash_files.txt "${file_content}\n")

177
static_core/LICENSE Normal file
View File

@ -0,0 +1,177 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

68
static_core/OAT.xml Normal file
View File

@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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.
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.
This is the configuration file template for OpenHarmony OSS Audit Tool, please copy it to your project root dir and modify it refer to OpenHarmony/tools_oat/README.
All configurations in this file will be merged to OAT-Default.xml, if you have any questions or concerns, please create issue in OpenHarmony/tools_oat and @jalenchen or chenyaxun.
licensefile:
1.If the project don't have "LICENSE" in root dir, please define all the license files in this project in , OAT will check license files according to this rule.
policylist:
1. policy: If the OAT-Default.xml policies do not meet your requirements, please add policies here.
2. policyitem: The fields type, name, path, desc is required, and the fields rule, group, filefilter is optional,the default value is:
<policyitem type="" name="" path="" desc="" rule="may" group="defaultGroup" filefilter="defaultPolicyFilter"/>
3. policyitem type:
"compatibility" is used to check license compatibility in the specified path;
"license" is used to check source license header in the specified path;
"copyright" is used to check source copyright header in the specified path;
"import" is used to check source dependency in the specified path, such as import ... ,include ...
"filetype" is used to check file type in the specified path, supported file types: archive, binary
"filename" is used to check whether the specified file exists in the specified path(projectroot means the root dir of the project), supported file names: LICENSE, README, README.OpenSource
4. policyitem name: This field is used for define the license, copyright, "*" means match all, the "!" prefix means could not match this value. For example, "!GPL" means can not use GPL license.
5. policyitem path: This field is used for define the source file scope to apply this policyitem, the "!" prefix means exclude the files. For example, "!.*/lib/.*" means files in lib dir will be exclude while process this policyitem.
6. policyitem rule and group: These two fields are used together to merge policy results. "may" policyitems in the same group means any one in this group passed, the result will be passed.
7. policyitem filefilter: Used to bind filefilter which define filter rules.
7. policyitem desc: Used to describe the reason of this policy item, committers will check this while merging the code.
8. filefilter: Filter rules, the type filename is used to filter file name, the type filepath is used to filter file path.
Note:If the text contains special characters, please escape them according to the following rules:
" == &quot;
& == &amp;
' == &apos;
< == &lt;
> == &gt;
-->
<configuration>
<oatconfig>
<licensefile></licensefile>
<policylist>
<policy name="projectPolicy" desc="">
<policyitem type="compatibility" name="Apache" path=".*" desc="">
</policy>
</policylist>
<filefilterlist>
<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>

171
static_core/README.md Normal file
View File

@ -0,0 +1,171 @@
# Bootstrapping
Currently an officially supported host OS for development, building and testing is Ubuntu 18.04 and Ubuntu 20.04.
The full list of packages required for building and testing the project is specified in
`scripts/deps-lists` files. These packages can be installed either manually or by running
a bootstrap script:
```
$ sudo ./scripts/install-deps-ubuntu
```
For more bootstrapping options, run:
```
$ sudo ./scripts/install-deps-ubuntu --help
```
E.g. for development purposes you should run:
```
$ sudo ./scripts/install-deps-ubuntu -i=dev
```
If you want additionally to install python dependencies for running tests add a parameter `-i=test`:
```
$ sudo ./scripts/install-deps-ubuntu -i=dev -i=test
```
It creates a virtual environment .venv-panda in your home directory with all required dependencies.
Later, tests python scripts can activate this environment. If you already have run with the parameter `-i=test`
the second time it might be skipped.
# Third party
Panda uses third party libraries. To install the libraries and apply patches, run:
```
$ ./scripts/install-third-party --force-clone
```
# Building
Assuming your system is bootstrapped, please run the following commands in the root of the project:
```
$ mkdir panda-build
$ cd panda-build
$ cmake /path/to/panda/repository -GNinja
$ ninja
```
This will build Panda in debug mode with your default C++ compiler. All supported compilers can be
found in `cmake/toolchain`. E.g., to build with Clang 14, pass a path to the corresponding toolchain
file during configuration:
```
$ cmake -DCMAKE_TOOLCHAIN_FILE=/path/to/panda/repository/cmake/toolchain/host_clang_14.cmake /path/to/panda/repository
```
## Explicitly setting build types
Recommended way to build Panda is to set `CMAKE_BUILD_TYPE` variable explicitly during configurations. Supported values are:
| Mode | Assertions | Optimizations | Debug info |
| ---- | ---------- | ------------- | ---------- |
| `Debug ` | Yes | None (CMake default) | `-g` (CMake default) |
| `Release` | No | `-O3` (CMake default) | None (CMake default) |
| `FastVerify` | Yes | `-O2` | `-ggdb3` |
Notes:
* Other common modes (`RelWithDebInfo`, `MinSizeRel`, `DebugDetailed`) should work but they are not tested in CI.
* Unlike `RelWithDebInfo`, `FastVerify` preserves assertions (and provides more verbose debug information).
Use this build type for running heavy test suites when you want both fast-running code and debuggability.
* `DebugDetailed` gives more debug information than `Debug`, it can be usefull for debugging unit tests for example.
Example:
```
$ cmake -DCMAKE_BUILD_TYPE=Release ...
```
## Running tests with QEMU for cross-compiled aarch64/arm builds
Recommended QEMU version for running testst is 6.2.0 (but 5.1+ should be ok, too). By default, it is downloaded and installed during environment bootstrap. Any system-installed package is left intact. If recommened QEMU version is not accessible via $PATH it can be specified during configuration time:
```
# If QEMU is available as /opt/qemu-6.2.0/bin/qemu-aarch64
$ cmake -DQEMU_PREFIX=/opt/qemu-6.2.0 ...
```
## Building with GN
1. Getting GN binary
```
$ git clone https://gn.googlesource.com/gn
$ cd gn
$ python build/gen.py
$ ninja -C out
```
2. Build panda using gn (`ark_asm`, `ark_disasm`, `ark_aot`, `ark_aotdump`, `c2abc`, `ark_bin`, `es2panda`, `verifier_bin` and `ark` targets are supported)
```
$ cd /path/to/panda/repository
$ /path/to/gn/repository/out/gn gen out
$ ninja -C out ark
```
When standard system, use
```
$ cd /path/to/panda/repository
$ /path/to/gn/repository/out/gn --args=-is_standard_system=true gen out
$ ninja -C out <target name>
```
## Further reading
* For more details, please see [build system readme](cmake/README.md).
# Testing
For testing, the following umbrella targets that guarantee building prior to running may be used:
* `tests`, for running all testing suites.
* `tests_full`, for running all testing suites and various code linters.
# Clang tools
`clang-format` and `clang-tidy` checks are integrated into build system and can be called by target of build system:
```
$ ninja code-style-check # clang-format
$ ninja clang-tidy-check # clang-tidy
```
# Test coverage
In order to measure bytecode optimizer coverage, configure your build with -DENABLE_BYTECODE_OPTIMIZER_COVERAGE=true. Then run:
```
$ ninja bytecode_optimizer_coverage
```
# Benchmarking
To build and run benchmarks, please use the umbrella target `benchmarks` or any of its
dependencies. Please see the root `CMakeLists.txt` for more details.
NB! Make sure that you configure your build with `-DCMAKE_BUILD_TYPE=Release`, otherwise
your run will most likely be dead slow.
# Running
## Running assembler
Assuming that you are in `panda-build` directory, please run:
```
$ ./bin/ark_asm /path/to/panda-assembly.pa /path/to/binary/output.abc
```
## Running interpreter
Assuming that your main function is defined as `.function main(...)` in the assembly listing,
and `/path/to/binary/output.abc` is the result of the assembler, please run:
```
$ ./bin/ark /path/to/binary/output.abc _GLOBAL::main
```

335
static_core/ark_config.gni Normal file
View File

@ -0,0 +1,335 @@
# Copyright (c) 2021-2022 Huawei Device Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import("//arkcompiler/runtime_core/static_core/ark_root.gni")
import("//build/ohos.gni")
ark_subsystem_name = "arkcompiler"
ark_part_name = "runtime_core"
if (is_standard_system) {
import("$ark_root/platforms/ohos/ark_config.gni")
} else {
import("$ark_root/platforms/mobile/ark_config.gni")
}
declare_args() {
ark_enabled_plugins = default_ark_enabled_plugins
extras = []
if (!defined(is_ohos)) {
is_ohos = false
}
if (!defined(is_standard_system)) {
is_standard_system = true
}
}
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")
}
ark_root_gen_dir = "$root_gen_dir/ark_sig"
concat_gen_dir =
get_label_info("$ark_root:concat_plugins_yamls", "target_gen_dir")
ark_plugin_options_yaml = "$concat_gen_dir/plugin_options.yaml"
isa_gen_dir = get_label_info("$ark_root/isa:isa_combine", "target_gen_dir")
ark_isa_yaml = "$isa_gen_dir/isa.yaml"
is_llvmaot = false
ark_enable_global_register_variables = true
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"
sdk_libc_secshared_config =
"$ark_third_party_root/securec:libsec_public_config"
if (is_mingw || is_mac || is_linux) {
sdk_libc_secshared_dep = "$ark_third_party_root/securec:libc_secstatic"
}
} else {
# For OpenHarmony build, always link with the static lib:
sdk_libc_secshared_dep =
"$ark_third_party_root/bounds_checking_function:libsec_static"
sdk_libc_secshared_config =
"$ark_third_party_root/bounds_checking_function:libsec_public_config"
}
is_mob = !is_ohos && !is_linux && !is_mingw && !is_mac &&
(current_cpu != "arm" || is_wearable_product)
## TODO add other arch
# Generate file for a template and YAML data provided.
#
# Mandatory arguments:
# data_file -- YAML data full name
# template_file -- template full name
# output_file -- output file full name
# requires -- a list of scripts that provide data-querying API for templates
# extra_dependencies -- a list of files that should be considered as dependencies, must be lable
template("ark_gen_file") {
assert(defined(invoker.data_file), "data_file is required!")
assert(defined(invoker.template_file), "template_file is required!")
assert(defined(invoker.output_file), "output_file is required!")
requires = ""
if (defined(invoker.requires)) {
requires = string_join(",", rebase_path(invoker.requires, root_build_dir))
}
extra_dependencies = []
if (defined(invoker.extra_dependencies)) {
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"
# rerun action when data file or template file update
inputs = [
invoker.template_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 = [
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
}
}
# Generate files based on templates and YAML data provided.
# Adds targets for every template. Also adds a target for the whole function invocation
# with name ${data_name}_gen_${PROJECT_NAME} for ease of declaring dependencies on generated files.
#
# Mandatory arguments:
# * data -- data source, YAML file
# * template_files -- a list of templates to generate files
# * requires -- a list of Ruby scripts that provide data-querying API for templates
#
# Optional arguments:
# * 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),
"template_files were not passed to ark_gen")
dir = ""
if (defined(invoker.sources)) {
dir = invoker.sources
} else {
dir = "templates"
}
destination = ""
if (defined(invoker.destination)) {
destination = invoker.destination
} else {
destination = target_out_dir
}
input_requires = ""
if (defined(invoker.requires)) {
input_requires = invoker.requires
}
foreach(t, invoker.template_files) {
name = string_replace(t, ".erb", "")
output = "${destination}/${name}"
name = string_replace(name, ".", "_")
name = string_replace(name, "/", "_")
target = "${target_name}_${name}"
ark_gen_file(target) {
data_file = invoker.data
template_file = "${dir}/${t}"
output_file = output
requires = input_requires
extra_dependencies = []
if (defined(invoker.extra_dependencies)) {
extra_dependencies += invoker.extra_dependencies
}
extra_argv = []
if (defined(invoker.extra_argv)) {
extra_argv += invoker.extra_argv
}
}
}
}
# Calls `ark_gen` for ISA YAML.
# Adds targets for every template. Also adds a target for the whole function invocation
# with name isa_gen_${PROJECT_NAME} for ease of declaring dependencies on generated files.
#
# Mandatory arguments:
# * template_files -- a list of templates to generate files
#
# Optional arguments:
# * sources -- a directory with templates, default is ${PROJECT_SOURCE_DIR}/templates
# * destination -- a directory for output files, default is ${target_out_dir}
# * requires -- if defined, will require additional Ruby files for template generation, must be list
# * extra_dependencies -- a list of files that should be considered as dependencies lable, must be list, not used
template("ark_isa_gen") {
isa_data = "$ark_isa_yaml"
isa_requires = [ "$ark_root/isa/isapi.rb" ]
if (defined(invoker.requires)) {
isa_requires += invoker.requires
}
dependencies = [ "$ark_root/isa:isa_combine" ]
if (defined(invoker.extra_dependencies)) {
dependencies += invoker.extra_dependencies
}
ark_gen("$target_name") {
data = isa_data
template_files = invoker.template_files
sources = invoker.sources
destination = invoker.destination
requires = isa_requires
extra_dependencies = dependencies
}
}
# Run ark_asm
#
# Arguments:
# * input_file -- Path to the source assembly code
# * output_file -- Path to the generated binary code
template("ark_asm_gen") {
action(target_name) {
ark_asm_root_out =
get_label_info("$ark_root/assembler:arkts_asm(${host_toolchain})",
"root_out_dir")
script =
ark_asm_root_out + "/$ark_subsystem_name/$ark_part_name/ark_asm"
inputs = [ invoker.input_file ]
outputs = [ invoker.output_file ]
args = [
rebase_path(invoker.input_file, root_build_dir),
rebase_path(invoker.output_file, root_build_dir),
]
deps = [ "$ark_root/assembler:arkts_asm(${host_toolchain})" ]
}
}
# Run es2panda
#
# Arguments:
# The same you would pass to action except script and deps.
template("es2panda_gen") {
action(target_name) {
es2panda_root_out = get_label_info(
"$ark_root/plugins/ecmascript/es2panda/aot:es2panda(${host_toolchain})",
"root_out_dir")
script =
es2panda_root_out + "/$ark_subsystem_name/$ark_part_name/es2panda"
deps = [
"$ark_root/plugins/ecmascript/es2panda/aot:es2panda(${host_toolchain})",
]
inputs = invoker.inputs
outputs = invoker.outputs
sources = invoker.sources
args = invoker.args
}
}

24
static_core/ark_root.gni Normal file
View File

@ -0,0 +1,24 @@
# Copyright (c) 2021-2022 Huawei Device Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
if (!defined(ark_standalone_build)) {
ark_standalone_build = false
}
if (ark_standalone_build) {
ark_root = "//"
} else {
ark_root = "//arkcompiler/runtime_core/static_core"
}
ark_third_party_root = "//third_party"

View File

@ -0,0 +1,185 @@
# 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/static_core/ark_config.gni")
import("//build/ohos.gni")
import("$ark_root/plugins/plugins.gni")
config("arkassembler_public_config") {
include_dirs = [
"$ark_root/assembler",
"$target_gen_dir",
"$target_gen_dir/include",
"$ark_root_gen_dir",
"$ark_root",
]
configs = [ "$ark_root/runtime:profiling_gen_public_config" ]
defines = [ "PANDA_WITH_ECMASCRIPT" ]
}
libarkassembler_sources = [
"$target_gen_dir/ins_to_string.cpp",
"annotation.cpp",
"assembly-emitter.cpp",
"assembly-ins.cpp",
"assembly-parser.cpp",
"assembly-program.cpp",
"assembly-type.cpp",
"context.cpp",
"extensions/extensions.cpp",
"lexer.cpp",
"meta.cpp",
]
libarkassembler_sources += plugin_libarkassembler_sources
libarkassembler_configs = [
sdk_libc_secshared_config,
"$ark_root:ark_config",
":arkassembler_public_config",
"$ark_root/runtime:arkruntime_public_config",
"$ark_root/libpandabase:arkbase_public_config",
"$ark_root/libpandafile:arkfile_public_config",
"$ark_root/compiler:arkcompiler_public_config",
]
libarkassembler_configs += plugin_libarkassembler_configs
source_set("libarkassembler_static") {
sources = libarkassembler_sources
public_configs = libarkassembler_configs
deps = [
":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",
":isa_gen_libarkassembler_isa_h",
":isa_gen_libarkassembler_opcode_parsing_h",
":isa_gen_libarkassembler_operand_types_print_h",
"$ark_root/compiler:libarktscompiler",
"$ark_root/libpandabase:libarktsbase",
"$ark_root/libpandafile:libarktsfile",
"$ark_root/runtime:profiling_gen_profiling_gen_h",
sdk_libc_secshared_dep,
]
deps += plugin_assembler_deps
}
ohos_shared_library("libarktsassembler") {
deps = [ ":libarkassembler_static" ]
output_extension = "so"
if (is_mingw) {
output_extension = "dll"
}
part_name = ark_part_name
subsystem_name = "$ark_subsystem_name"
}
source_set("libarkassembler_frontend_set_static") {
sources = libarkassembler_sources
public_configs = libarkassembler_configs
deps = [
":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",
":isa_gen_libarkassembler_isa_h",
":isa_gen_libarkassembler_opcode_parsing_h",
":isa_gen_libarkassembler_operand_types_print_h",
"$ark_root/libpandafile:libarktsfile_frontend_static",
"$ark_root/runtime:profiling_gen_profiling_gen_h",
sdk_libc_secshared_dep,
]
deps += plugin_assembler_deps
}
ohos_static_library("libarktsassembler_frontend_static") {
deps = [ ":libarkassembler_frontend_set_static" ]
part_name = ark_part_name
subsystem_name = "$ark_subsystem_name"
}
source_set("ark_asm_static") {
sources = [ "pandasm.cpp" ]
include_dirs = [
"$target_gen_dir",
]
public_configs = [
sdk_libc_secshared_config,
":arkassembler_public_config",
"$ark_root:ark_config",
"$ark_root/libpandabase:arkbase_public_config",
"$ark_root/libpandafile:arkfile_public_config",
"$ark_root/compiler:arkcompiler_public_config",
"$ark_root/runtime:arkruntime_public_config",
]
deps = [
":libarktsassembler_frontend_static",
"$ark_root/bytecode_optimizer:libarktsbytecodeopt_frontend_static",
"$ark_root/libpandabase:libarktsbase_frontend_static",
"$ark_root/libpandafile:libarktsfile_frontend_static",
]
}
ohos_executable("arkts_asm") {
deps = [ ":ark_asm_static" ]
libs = platform_libs
ldflags = platform_ldflags
install_enable = true
part_name = ark_part_name
subsystem_name = "$ark_subsystem_name"
}
ark_isa_gen("isa_gen_libarkassembler") {
template_files = [
"isa.h.erb",
"ins_emit.h.erb",
"ins_to_string.cpp.erb",
"ins_create_api.h.erb",
"opcode_parsing.h.erb",
"operand_types_print.h.erb",
]
sources = "templates"
destination = "$target_gen_dir"
requires = [
"asm_isapi.rb",
"../libpandafile/pandafile_isapi.rb",
]
}
ark_gen_file("ark_asm_meta_gen_h") {
template_file = "templates/meta_gen.cpp.erb"
data_file = "metadata.yaml"
requires = [ "asm_metadata.rb" ]
output_file = "$target_gen_dir/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 = "$ark_plugin_options_yaml"
requires = [ "$ark_root/templates/plugin_options.rb" ]
output_file = "$target_gen_dir/register_extensions.h"
}

View File

@ -0,0 +1,136 @@
# 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.3.2 FATAL_ERROR)
project(assembler CXX)
panda_add_executable(ark_asm pandasm.cpp)
set(PANDASM_BIN_TESTS ${CMAKE_CURRENT_BINARY_DIR}/tests)
file(MAKE_DIRECTORY "${PANDASM_BIN_TESTS}")
panda_isa_gen(
TEMPLATES
"isa.h.erb"
"ins_emit.h.erb"
"ins_to_string.cpp.erb"
"ins_create_api.h.erb"
"opcode_parsing.h.erb"
"operand_types_print.h.erb"
REQUIRES
"${CMAKE_CURRENT_SOURCE_DIR}/asm_isapi.rb"
"${PANDA_ROOT}/libpandafile/pandafile_isapi.rb"
)
set(SOURCES
lexer.cpp
annotation.cpp
assembly-emitter.cpp
assembly-parser.cpp
assembly-program.cpp
assembly-type.cpp
assembly-ins.cpp
context.cpp
meta.cpp
ins_to_string.cpp
extensions/extensions.cpp
)
set(META_GEN_H ${CMAKE_CURRENT_BINARY_DIR}/meta_gen.h)
panda_gen_file(
DATAFILE ${CMAKE_CURRENT_SOURCE_DIR}/metadata.yaml
TEMPLATE ${CMAKE_CURRENT_SOURCE_DIR}/templates/meta_gen.cpp.erb
OUTPUTFILE ${META_GEN_H}
REQUIRES ${CMAKE_CURRENT_SOURCE_DIR}/asm_metadata.rb
)
add_custom_target(meta_gen_h DEPENDS ${META_GEN_H})
add_dependencies(panda_gen_files meta_gen_h)
panda_add_library(arkassembler_obj OBJECT ${SOURCES})
add_dependencies(arkassembler_obj
isa_gen_assembler
arkfile
meta_gen_h
profiling_gen
)
set_target_properties(arkassembler_obj PROPERTIES
POSITION_INDEPENDENT_CODE ON
CXX_VISIBILITY_PRESET hidden
VISIBILITY_INLINES_HIDDEN ON
)
panda_target_include_directories(arkassembler_obj PUBLIC
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_BINARY_DIR}/libpandabase
${PANDA_ROOT}
${CMAKE_BINARY_DIR}
)
panda_target_link_libraries(arkassembler_obj arkfile)
panda_target_link_libraries(ark_asm arkassembler arkbase)
if(PANDA_WITH_BYTECODE_OPTIMIZER)
panda_target_link_libraries(ark_asm arkbytecodeopt)
endif()
include_directories(${PANDA_ROOT}/libpandabase/)
include_directories(${CMAKE_BINARY_DIR}/libpandafile/include/)
panda_add_library(arkassembler_static STATIC $<TARGET_OBJECTS:arkassembler_obj>)
panda_target_link_libraries(arkassembler_static arkassembler_obj)
panda_add_gtest(
NAME assembler_tests
SOURCES
tests/lexer_test.cpp
tests/parser_test.cpp
tests/emitter_test.cpp
tests/mangling_tests.cpp
LIBRARIES
arkbase arkassembler_static
SANITIZERS
${PANDA_SANITIZERS_LIST}
)
if(TARGET assembler_tests)
panda_target_compile_options(assembler_tests PUBLIC "-Wno-ignored-attributes")
endif()
panda_add_sanitizers(TARGET arkassembler_obj 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)
if (TARGET host_tools_depends)
add_dependencies(host_tools_depends arkassembler_obj)
endif()
if (DEFINED PANDA_ROOT_BINARY_DIR)
# Special case for host tool build.
panda_target_include_directories(arkassembler_obj PUBLIC ${PANDA_ROOT_BINARY_DIR}/assembler)
endif()
panda_add_library(arkassembler ${PANDA_DEFAULT_LIB_TYPE} $<TARGET_OBJECTS:arkassembler_obj>)
panda_target_link_libraries(arkassembler arkassembler_obj)
if (PANDA_ENABLE_AFL)
include("${PANDA_ROOT}/extras/fuzzing/Fuzzing.cmake")
panda_substitute_libs(TARGET arkassembler_obj LIBS arkbase c_secshared arkfile)
panda_add_library(arkassembler_fuzz ${PANDA_DEFAULT_LIB_TYPE} $<TARGET_OBJECTS:arkassembler_obj_fuzz>)
panda_target_link_libraries(arkassembler_fuzz arkassembler_obj_fuzz)
endif()

View File

@ -0,0 +1,245 @@
/**
* 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 "annotation.h"
namespace panda::pandasm {
std::unique_ptr<ScalarValue> InitScalarValue(const ScalarValue &sc_val)
{
std::unique_ptr<ScalarValue> copy_val;
switch (sc_val.GetType()) {
case Value::Type::U1: {
copy_val = std::make_unique<ScalarValue>(ScalarValue::Create<Value::Type::U1>(sc_val.GetValue<uint8_t>()));
break;
}
case Value::Type::U8: {
copy_val = std::make_unique<ScalarValue>(ScalarValue::Create<Value::Type::U8>(sc_val.GetValue<uint8_t>()));
break;
}
case Value::Type::U16: {
copy_val =
std::make_unique<ScalarValue>(ScalarValue::Create<Value::Type::U16>(sc_val.GetValue<uint16_t>()));
break;
}
case Value::Type::U32: {
copy_val =
std::make_unique<ScalarValue>(ScalarValue::Create<Value::Type::U32>(sc_val.GetValue<uint32_t>()));
break;
}
case Value::Type::U64: {
copy_val =
std::make_unique<ScalarValue>(ScalarValue::Create<Value::Type::U64>(sc_val.GetValue<uint64_t>()));
break;
}
case Value::Type::I8: {
copy_val = std::make_unique<ScalarValue>(ScalarValue::Create<Value::Type::I8>(sc_val.GetValue<int8_t>()));
break;
}
case Value::Type::I16: {
copy_val = std::make_unique<ScalarValue>(ScalarValue::Create<Value::Type::I16>(sc_val.GetValue<int16_t>()));
break;
}
case Value::Type::I32: {
copy_val = std::make_unique<ScalarValue>(ScalarValue::Create<Value::Type::I32>(sc_val.GetValue<int32_t>()));
break;
}
case Value::Type::I64: {
copy_val = std::make_unique<ScalarValue>(ScalarValue::Create<Value::Type::I64>(sc_val.GetValue<int64_t>()));
break;
}
case Value::Type::F32: {
copy_val = std::make_unique<ScalarValue>(ScalarValue::Create<Value::Type::F32>(sc_val.GetValue<float>()));
break;
}
case Value::Type::F64: {
copy_val = std::make_unique<ScalarValue>(ScalarValue::Create<Value::Type::F64>(sc_val.GetValue<double>()));
break;
}
case Value::Type::STRING: {
copy_val =
std::make_unique<ScalarValue>(ScalarValue::Create<Value::Type::STRING>(sc_val.GetValue<std::string>()));
break;
}
case Value::Type::STRING_NULLPTR: {
copy_val = std::make_unique<ScalarValue>(
ScalarValue::Create<Value::Type::STRING_NULLPTR>(sc_val.GetValue<int32_t>()));
break;
}
case Value::Type::RECORD: {
copy_val = std::make_unique<ScalarValue>(ScalarValue::Create<Value::Type::RECORD>(sc_val.GetValue<Type>()));
break;
}
case Value::Type::METHOD: {
copy_val =
std::make_unique<ScalarValue>(ScalarValue::Create<Value::Type::METHOD>(sc_val.GetValue<std::string>()));
break;
}
case Value::Type::ENUM: {
copy_val =
std::make_unique<ScalarValue>(ScalarValue::Create<Value::Type::ENUM>(sc_val.GetValue<std::string>()));
break;
}
case Value::Type::ANNOTATION: {
copy_val = std::make_unique<ScalarValue>(
ScalarValue::Create<Value::Type::ANNOTATION>(sc_val.GetValue<AnnotationData>()));
break;
}
default: {
UNREACHABLE();
copy_val = nullptr;
break;
}
}
return copy_val;
}
std::unique_ptr<Value> MakingValue(const AnnotationElement &ann_elem)
{
std::unique_ptr<Value> copy_val;
switch (ann_elem.GetValue()->GetType()) {
case Value::Type::U1:
case Value::Type::U8:
case Value::Type::U16:
case Value::Type::U32:
case Value::Type::U64:
case Value::Type::I8:
case Value::Type::I16:
case Value::Type::I32:
case Value::Type::I64:
case Value::Type::F32:
case Value::Type::F64:
case Value::Type::STRING:
case Value::Type::STRING_NULLPTR:
case Value::Type::RECORD:
case Value::Type::METHOD:
case Value::Type::ENUM:
case Value::Type::ANNOTATION: {
copy_val = InitScalarValue(*static_cast<ScalarValue *>(ann_elem.GetValue()));
break;
}
case Value::Type::ARRAY: {
Value::Type c_type;
auto *elem_arr = static_cast<ArrayValue *>(ann_elem.GetValue());
if (elem_arr->GetValues().empty()) {
c_type = Value::Type::VOID;
} else {
c_type = elem_arr->GetValues().front().GetType();
}
std::vector<ScalarValue> sc_vals;
for (const auto &sc_val : elem_arr->GetValues()) {
sc_vals.push_back(*InitScalarValue(sc_val));
}
copy_val = std::make_unique<ArrayValue>(c_type, std::move(sc_vals));
break;
}
default: {
UNREACHABLE();
copy_val = nullptr;
break;
}
}
return copy_val;
}
AnnotationElement::AnnotationElement(const AnnotationElement &ann_elem)
{
this->value_ = MakingValue(ann_elem);
this->name_ = ann_elem.GetName();
}
AnnotationElement &AnnotationElement::operator=(const AnnotationElement &ann_elem)
{
if (this == &ann_elem) {
return *this;
}
this->value_ = MakingValue(ann_elem);
this->name_ = ann_elem.GetName();
return *this;
}
ScalarValue *Value::GetAsScalar()
{
ASSERT(!IsArray());
return static_cast<ScalarValue *>(this);
}
const ScalarValue *Value::GetAsScalar() const
{
ASSERT(!IsArray());
return static_cast<const ScalarValue *>(this);
}
ArrayValue *Value::GetAsArray()
{
ASSERT(IsArray());
return static_cast<ArrayValue *>(this);
}
const ArrayValue *Value::GetAsArray() const
{
ASSERT(IsArray());
return static_cast<const ArrayValue *>(this);
}
/* static */
std::string AnnotationElement::TypeToString(Value::Type type)
{
switch (type) {
case Value::Type::U1:
return "u1";
case Value::Type::I8:
return "i8";
case Value::Type::U8:
return "u8";
case Value::Type::I16:
return "i16";
case Value::Type::U16:
return "u16";
case Value::Type::I32:
return "i32";
case Value::Type::U32:
return "u32";
case Value::Type::I64:
return "i64";
case Value::Type::U64:
return "u64";
case Value::Type::F32:
return "f32";
case Value::Type::F64:
return "f64";
case Value::Type::STRING:
return "string";
case Value::Type::RECORD:
return "record";
case Value::Type::METHOD:
return "method";
case Value::Type::ENUM:
return "enum";
case Value::Type::ANNOTATION:
return "annotation";
case Value::Type::ARRAY:
return "array";
case Value::Type::VOID:
return "void";
default: {
UNREACHABLE();
return "unknown";
}
}
}
} // namespace panda::pandasm

View File

@ -0,0 +1,558 @@
/**
* 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_ANNOTATION_H_
#define PANDA_ASSEMBLER_ANNOTATION_H_
#include <cstddef>
#include <memory>
#include <string>
#include <string_view>
#include <type_traits>
#include <variant>
#include <vector>
#include "assembly-type.h"
#include "macros.h"
namespace panda::pandasm {
class AnnotationElement;
class AnnotationData {
public:
AnnotationData(std::string_view record_name, std::vector<AnnotationElement> elements)
: record_name_(record_name), elements_(std::move(elements))
{
}
explicit AnnotationData(std::string_view record_name) : record_name_(record_name) {}
DEFAULT_MOVE_SEMANTIC(AnnotationData);
DEFAULT_COPY_SEMANTIC(AnnotationData);
~AnnotationData() = default;
std::string GetName() const
{
return record_name_;
}
const std::vector<AnnotationElement> &GetElements() const
{
return elements_;
}
void AddElement(AnnotationElement &&element)
{
elements_.push_back(std::forward<AnnotationElement>(element));
}
private:
std::string record_name_;
std::vector<AnnotationElement> elements_;
};
class ScalarValue;
class ArrayValue;
class Value {
public:
enum class Type {
U1,
I8,
U8,
I16,
U16,
I32,
U32,
I64,
U64,
F32,
F64,
STRING,
STRING_NULLPTR,
RECORD,
METHOD,
ENUM,
ANNOTATION,
ARRAY,
VOID,
METHOD_HANDLE,
UNKNOWN
};
static constexpr char GetTypeAsChar(Type t)
{
char type = '0';
switch (t) {
case Type::U1:
type = '1';
break;
case Type::I8:
type = '2';
break;
case Type::U8:
type = '3';
break;
case Type::I16:
type = '4';
break;
case Type::U16:
type = '5';
break;
case Type::I32:
type = '6';
break;
case Type::U32:
type = '7';
break;
case Type::I64:
type = '8';
break;
case Type::U64:
type = '9';
break;
case Type::F32:
type = 'A';
break;
case Type::F64:
type = 'B';
break;
case Type::STRING:
type = 'C';
break;
case Type::RECORD:
type = 'D';
break;
case Type::METHOD:
type = 'E';
break;
case Type::ENUM:
type = 'F';
break;
case Type::ANNOTATION:
type = 'G';
break;
case Type::ARRAY:
type = 'H';
break;
case Type::VOID:
type = 'I';
break;
case Type::METHOD_HANDLE:
type = 'J';
break;
case Type::STRING_NULLPTR:
type = '*';
break;
case Type::UNKNOWN:
default:
type = '0';
}
return type;
}
static constexpr char GetArrayTypeAsChar(Type t)
{
char type = '0';
switch (t) {
case Type::U1:
type = 'K';
break;
case Type::I8:
type = 'L';
break;
case Type::U8:
type = 'M';
break;
case Type::I16:
type = 'N';
break;
case Type::U16:
type = 'O';
break;
case Type::I32:
type = 'P';
break;
case Type::U32:
type = 'Q';
break;
case Type::I64:
type = 'R';
break;
case Type::U64:
type = 'S';
break;
case Type::F32:
type = 'T';
break;
case Type::F64:
type = 'U';
break;
case Type::STRING:
type = 'V';
break;
case Type::RECORD:
type = 'W';
break;
case Type::METHOD:
type = 'X';
break;
case Type::ENUM:
type = 'Y';
break;
case Type::ANNOTATION:
type = 'Z';
break;
case Type::METHOD_HANDLE:
type = '@';
break;
case Type::UNKNOWN:
default:
type = '0';
}
return type;
}
static constexpr Type GetCharAsType(char c)
{
Type type = Type::UNKNOWN;
switch (c) {
case '1':
type = Type::U1;
break;
case '2':
type = Type::I8;
break;
case '3':
type = Type::U8;
break;
case '4':
type = Type::I16;
break;
case '5':
type = Type::U16;
break;
case '6':
type = Type::I32;
break;
case '7':
type = Type::U32;
break;
case '8':
type = Type::I64;
break;
case '9':
type = Type::U64;
break;
case 'A':
type = Type::F32;
break;
case 'B':
type = Type::F64;
break;
case 'C':
type = Type::STRING;
break;
case 'D':
type = Type::RECORD;
break;
case 'E':
type = Type::METHOD;
break;
case 'F':
type = Type::ENUM;
break;
case 'G':
type = Type::ANNOTATION;
break;
case 'H':
type = Type::ARRAY;
break;
case 'I':
type = Type::VOID;
break;
case 'J':
type = Type::METHOD_HANDLE;
break;
case '*':
type = Type::STRING_NULLPTR;
break;
case '0':
default:
type = Type::UNKNOWN;
}
return type;
}
static constexpr Type GetCharAsArrayType(char c)
{
Type type = Type::UNKNOWN;
switch (c) {
case 'K':
type = Type::U1;
break;
case 'L':
type = Type::I8;
break;
case 'M':
type = Type::U8;
break;
case 'N':
type = Type::I16;
break;
case 'O':
type = Type::U16;
break;
case 'P':
type = Type::I32;
break;
case 'Q':
type = Type::U32;
break;
case 'R':
type = Type::I64;
break;
case 'S':
type = Type::U64;
break;
case 'T':
type = Type::F32;
break;
case 'U':
type = Type::F64;
break;
case 'V':
type = Type::STRING;
break;
case 'W':
type = Type::RECORD;
break;
case 'X':
type = Type::METHOD;
break;
case 'Y':
type = Type::ENUM;
break;
case 'Z':
type = Type::ANNOTATION;
break;
case '@':
type = Type::METHOD_HANDLE;
break;
case '0':
default:
type = Type::UNKNOWN;
}
return type;
}
Type GetType() const
{
return type_;
}
bool IsArray() const
{
return type_ == Type::ARRAY;
}
PANDA_PUBLIC_API ScalarValue *GetAsScalar();
PANDA_PUBLIC_API const ScalarValue *GetAsScalar() const;
PANDA_PUBLIC_API ArrayValue *GetAsArray();
PANDA_PUBLIC_API const ArrayValue *GetAsArray() const;
virtual ~Value() = default;
DEFAULT_COPY_SEMANTIC(Value);
DEFAULT_MOVE_SEMANTIC(Value);
protected:
explicit Value(Type type) : type_(type) {}
private:
Type type_;
};
// clang-format off
template <Value::Type VALUE_TYPE>
struct ValueTypeHelper {
// Disable checks due to clang-tidy bug https://bugs.llvm.org/show_bug.cgi?id=40640
// NOLINTNEXTLINE(readability-magic-numbers)
using Type = std::conditional_t<VALUE_TYPE == Value::Type::U1, uint8_t,
// NOLINTNEXTLINE(readability-magic-numbers)
std::conditional_t<VALUE_TYPE == Value::Type::I8, int8_t,
// NOLINTNEXTLINE(readability-magic-numbers)
std::conditional_t<VALUE_TYPE == Value::Type::U8, uint8_t,
// NOLINTNEXTLINE(readability-magic-numbers)
std::conditional_t<VALUE_TYPE == Value::Type::I16, int16_t,
// NOLINTNEXTLINE(readability-magic-numbers)
std::conditional_t<VALUE_TYPE == Value::Type::U16, uint16_t,
// NOLINTNEXTLINE(readability-magic-numbers)
std::conditional_t<VALUE_TYPE == Value::Type::I32, int32_t,
// NOLINTNEXTLINE(readability-magic-numbers)
std::conditional_t<VALUE_TYPE == Value::Type::U32, uint32_t,
// NOLINTNEXTLINE(readability-magic-numbers)
std::conditional_t<VALUE_TYPE == Value::Type::I64, int64_t,
// NOLINTNEXTLINE(readability-magic-numbers)
std::conditional_t<VALUE_TYPE == Value::Type::U64, uint64_t,
// NOLINTNEXTLINE(readability-magic-numbers)
std::conditional_t<VALUE_TYPE == Value::Type::F32, float,
// NOLINTNEXTLINE(readability-magic-numbers)
std::conditional_t<VALUE_TYPE == Value::Type::F64, double,
// NOLINTNEXTLINE(readability-magic-numbers)
std::conditional_t<VALUE_TYPE == Value::Type::STRING, std::string_view,
// NOLINTNEXTLINE(readability-magic-numbers)
std::conditional_t<VALUE_TYPE == Value::Type::STRING_NULLPTR, uint32_t,
// NOLINTNEXTLINE(readability-magic-numbers)
std::conditional_t<VALUE_TYPE == Value::Type::RECORD, pandasm::Type,
// NOLINTNEXTLINE(readability-magic-numbers)
std::conditional_t<VALUE_TYPE == Value::Type::METHOD, std::string_view,
// NOLINTNEXTLINE(readability-magic-numbers)
std::conditional_t<VALUE_TYPE == Value::Type::ENUM, std::string_view,
// NOLINTNEXTLINE(readability-magic-numbers)
std::conditional_t<VALUE_TYPE == Value::Type::ANNOTATION, AnnotationData,
void>>>>>>>>>>>>>>>>>;
};
// clang-format on
template <Value::Type TYPE>
using ValueTypeHelperT = typename ValueTypeHelper<TYPE>::Type;
class ScalarValue : public Value {
public:
template <Value::Type TYPE>
// Disable checks due to clang-tidy bug https://bugs.llvm.org/show_bug.cgi?id=40640
// NOLINTNEXTLINE(readability-magic-numbers)
static ScalarValue Create(ValueTypeHelperT<TYPE> value)
{
// NOLINTNEXTLINE(readability-magic-numbers)
using T = ValueTypeHelperT<TYPE>;
// Disable checks due to clang-tidy bug https://bugs.llvm.org/show_bug.cgi?id=32203
// NOLINTNEXTLINE(readability-braces-around-statements, hicpp-braces-around-statements)
if constexpr (std::is_integral_v<T>) { // NOLINT(bugprone-suspicious-semicolon)
// NOLINTNEXTLINE(readability-magic-numbers)
return ScalarValue(TYPE, static_cast<uint64_t>(value));
}
// NOLINTNEXTLINE(readability-braces-around-statements, hicpp-braces-around-statements)
if constexpr (!std::is_integral_v<T>) { // NOLINT(bugprone-suspicious-semicolon)
// NOLINTNEXTLINE(readability-magic-numbers)
return ScalarValue(TYPE, value);
}
}
template <class T>
T GetValue() const
{
// Disable checks due to clang-tidy bug https://bugs.llvm.org/show_bug.cgi?id=32203
// NOLINTNEXTLINE(readability-braces-around-statements, hicpp-braces-around-statements)
if constexpr (std::is_integral_v<T>) { // NOLINT(bugprone-suspicious-semicolon)
return static_cast<T>(std::get<uint64_t>(value_));
}
// NOLINTNEXTLINE(readability-braces-around-statements, hicpp-braces-around-statements)
if constexpr (!std::is_integral_v<T>) { // NOLINT(bugprone-suspicious-semicolon)
return std::get<T>(value_);
}
}
DEFAULT_MOVE_SEMANTIC(ScalarValue);
DEFAULT_COPY_SEMANTIC(ScalarValue);
~ScalarValue() override = default;
private:
ScalarValue(Type type, uint64_t value) : Value(type), value_(value) {}
ScalarValue(Type type, float value) : Value(type), value_(value) {}
ScalarValue(Type type, double value) : Value(type), value_(value) {}
ScalarValue(Type type, std::string_view value) : Value(type), value_(std::string(value)) {}
ScalarValue(Type type, pandasm::Type value) : Value(type), value_(std::move(value)) {}
ScalarValue(Type type, AnnotationData &value) : Value(type), value_(value) {}
std::variant<uint64_t, float, double, std::string, pandasm::Type, AnnotationData> value_;
};
class ArrayValue : public Value {
public:
ArrayValue(Type component_type, std::vector<ScalarValue> values)
: Value(Type::ARRAY), component_type_(component_type), values_(std::move(values))
{
}
DEFAULT_MOVE_SEMANTIC(ArrayValue);
DEFAULT_COPY_SEMANTIC(ArrayValue);
~ArrayValue() override = default;
const std::vector<ScalarValue> &GetValues() const
{
return values_;
}
Type GetComponentType() const
{
return component_type_;
}
private:
Type component_type_;
std::vector<ScalarValue> values_;
};
class AnnotationElement {
public:
PANDA_PUBLIC_API AnnotationElement(std::string_view name, std::unique_ptr<Value> value)
: name_(name), value_(std::move(value))
{
}
PANDA_PUBLIC_API AnnotationElement(const AnnotationElement &ann_elem);
PANDA_PUBLIC_API AnnotationElement &operator=(const AnnotationElement &ann_elem);
DEFAULT_MOVE_SEMANTIC(AnnotationElement);
~AnnotationElement() = default;
std::string GetName() const
{
return name_;
}
Value *GetValue() const
{
return value_.get();
}
PANDA_PUBLIC_API static std::string TypeToString(Value::Type type);
private:
std::string name_;
std::unique_ptr<Value> value_;
};
} // namespace panda::pandasm
#endif // PANDA_ASSEMBLER_ANNOTATION_H_

View File

@ -0,0 +1,103 @@
# 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.
# Assembler specific extension of ISAPI
Instruction.class_eval do
def asm_token
mnemonic.tr('.', '_').upcase
end
def call?
properties.include?('call')
end
def range?
mnemonic.split('.')[-1] == 'range'
end
def simple_call?
call? && !range? && !properties.include?('dynamic')
end
def return?
stripped_mnemonic == 'return'
end
def return_obj?
mnemonic == 'return.obj'
end
def return64?
mnemonic == 'return.64'
end
def return32?
mnemonic == 'return'
end
def return_void?
mnemonic == 'return.void'
end
end
def bit_cast(what, to_type, from_type)
"bit_cast<#{to_type}, #{from_type}>(static_cast<#{from_type}>(std::get<double>(#{what})))"
end
def index_of_max(a)
a.each_with_index.max[1] # returns index of `last` max value
end
def max_number_of_src_regs
Panda::instructions.map do |insn|
insn.operands.select(&:reg?).select(&:src?).size
end.max
end
IR = Struct.new(:opcode, :flags, :dst_idx, :use_idxs)
module Panda
def self.pseudo_instructions
insns = []
insns << IR.new('MOVX', ['InstFlags::PSEUDO'], 0, [1])
insns << IR.new('LDAX', ['InstFlags::PSEUDO', 'InstFlags::ACC_WRITE'], 'INVALID_REG_IDX', [0])
insns << IR.new('STAX', ['InstFlags::PSEUDO', 'InstFlags::ACC_READ'], 0, [])
insns << IR.new('NEWX', ['InstFlags::PSEUDO'], 0, [])
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_WRITE'], 'INVALID_REG_IDX', [])
insns << IR.new('B_P_CALLIEX',['InstFlags::PSEUDO', 'InstFlags::CALL', 'InstFlags::ACC_WRITE'], 'INVALID_REG_IDX', [])
insns
end
end
# returns array of OpenStruct with fields
# name - name of variable in emitter code
# type - type of variable in emitter code
def assembler_signature(group, is_jump)
insn = group.first
format_ops(insn.format).select { |o| o.name != 'prof'}.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]
else
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'
end
end
end

View File

@ -0,0 +1,249 @@
# 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.
require 'ostruct'
require 'delegate'
class Attritute < SimpleDelegator
def getter_name
r = name.capitalize
type == 'bool' ? 'Is' + r : 'Get' + r
end
def setter_name
'Set' + name.capitalize
end
def bool?
type == 'bool'
end
def enum?
type == 'enum'
end
def size?
type == 'size'
end
def multiple?
!bool? && multiple
end
def applicable_to?(item_type)
applicable_to.include?(item_type)
end
def set_flags?
(defined? flags) && flags.any? || enum? && values.any? { |v| v.flags && v.flags.any? }
end
end
module Metadata
module_function
def attributes
@data.attributes.map do |op|
Attritute.new(op)
end
end
def language
@data.language || ''
end
def extends_default?
!language.empty?
end
def item_types
['record', 'field', 'function', 'param']
end
def wrap_data(data)
@data = data
end
end
def Gen.on_require(data)
Metadata.wrap_data(data)
end
module MetadataGen
module_function
def attribute_name(attribute)
return attribute.name if Metadata.language.empty?
"#{Metadata.language.downcase}.#{attribute.name}"
end
def class_name(item_type)
item_type.capitalize + 'Metadata'
end
def validate_body(item_type, is_bool)
attributes = Metadata::attributes.select { |a| a.applicable_to?(item_type) && a.bool? == is_bool }
body = []
indent = ' ' * 4
attributes.each do |a|
body << "#{indent}if (attribute == \"#{attribute_name(a)}\") {"
unless a.multiple?
body << "#{indent * 2}if (HasAttribute(attribute)) {"
body << "#{indent * 3}return Error(\"Attribute '#{attribute_name(a)}' already defined\","
body << "#{indent * 3} Error::Type::MULTIPLE_ATTRIBUTE);"
body << "#{indent * 2}}"
end
if a.enum?
a.values.each do |v|
body << "#{indent * 2}if (value == \"#{v.value}\") {"
body << "#{indent * 3}return {};"
body << "#{indent * 2}}"
body << ""
end
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);"
else
body << "#{indent * 2}return {};"
end
body << "#{indent}}"
body << ""
end
Metadata::attributes.select { |a| a.applicable_to?(item_type) && a.bool? != is_bool }.each do |a|
body << "#{indent}if (attribute == \"#{attribute_name(a)}\") {"
body << "#{indent * 2}return Error(\"Attribute '#{attribute_name(a)}' #{is_bool ? "must" : "must not"} have a value\","
body << "#{indent * 2} #{is_bool ? "Error::Type::MISSING_VALUE" : "Error::Type::UNEXPECTED_VALUE"});"
body << "#{indent}}"
body << ""
end
if Metadata::extends_default?
args = ['attribute']
args << 'value' unless is_bool
body << "#{indent}return pandasm::#{class_name(item_type)}::Validate(#{args.join(', ')});"
else
body << "#{indent}return Error(std::string(\"Unknown attribute '\").append(attribute) + \"'\","
body << "#{indent} Error::Type::UNKNOWN_ATTRIBUTE);"
end
body
end
def set_flags_body(item_type, is_bool)
attributes = Metadata::attributes.select { |a| a.applicable_to?(item_type) && a.bool? == is_bool && a.set_flags? }
body = []
indent = ' ' * 4
attributes.each do |a|
body << "#{indent}if (attribute == \"#{attribute_name(a)}\") {"
if defined? a.flags
body << "#{indent * 2}SetAccessFlags(GetAccessFlags() | #{a.flags.join(' | ')});"
end
if a.enum?
a.values.select { |v| v.flags && v.flags.any? }.each do |v|
body << "#{indent * 2}if (value == \"#{v.value}\") {"
body << "#{indent * 3}SetAccessFlags(GetAccessFlags() | #{v.flags.join(' | ')});"
body << "#{indent * 2}}"
body << ""
end
end
body << "#{indent}}"
end
if Metadata::extends_default?
args = ['attribute']
args << 'value' unless is_bool
body << "#{indent}pandasm::#{class_name(item_type)}::SetFlags(#{args.join(', ')});"
end
body
end
def remove_flags_body(item_type, is_bool)
attributes = Metadata::attributes.select { |a| a.applicable_to?(item_type) && a.bool? == is_bool && a.set_flags? }
body = []
indent = ' ' * 4
attributes.each do |a|
body << "#{indent}if (attribute == \"#{attribute_name(a)}\") {"
if defined? a.flags
body << "#{indent * 2}if ((GetAccessFlags() & #{a.flags.join(' | ')}) != 0) {"
body << "#{indent * 3}SetAccessFlags(GetAccessFlags() ^ (#{a.flags.join(' | ')}));"
body << "#{indent * 2}}"
end
if a.enum?
a.values.select { |v| v.flags && v.flags.any? }.each do |v|
body << "#{indent * 2}if (value == \"#{v.value}\") {"
body << "#{indent * 3}if ((GetAccessFlags() & (#{v.flags.join(' | ')})) != 0) {"
body << "#{indent * 4}SetAccessFlags(GetAccessFlags() ^ (#{v.flags.join(' | ')}));"
body << "#{indent * 3}}"
body << "#{indent * 2}}"
end
end
body << "#{indent}}"
end
if Metadata::extends_default?
args = ['attribute']
args << 'value' unless is_bool
body << "#{indent}pandasm::#{class_name(item_type)}::RemoveFlags(#{args.join(', ')});"
end
body
end
def arg_list(is_bool)
args = ['std::string_view attribute']
args << 'std::string_view value' if !is_bool
args
end
def add_unused_attribute(arg)
"[[maybe_unused]] #{arg}"
end
def validate_arg_list(item_type, is_bool)
args = arg_list(is_bool)
return args if Metadata::extends_default?
attributes = Metadata::attributes.select { |a| a.applicable_to?(item_type) && a.bool? == is_bool }
args[0] = add_unused_attribute(args[0]) if attributes.none?
args[1] = add_unused_attribute(args[1]) if args[1] && attributes.none? { |a| a.enum? }
args
end
def flags_arg_list(item_type, is_bool)
args = arg_list(is_bool)
return args if Metadata::extends_default?
attributes = Metadata::attributes.select { |a| a.applicable_to?(item_type) && a.bool? == is_bool && a.set_flags? }
use_value = attributes.any? { |a| a.enum? && a.values.any? { |v| v.flags && v.flags.any? } }
args[0] = add_unused_attribute(args[0]) if attributes.none?
args[1] = add_unused_attribute(args[1]) if args[1] && !use_value
args
end
end

View File

@ -0,0 +1,80 @@
/**
* 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_CONTEXT_HPP
#define _PANDA_ASSEMBLER_CONTEXT_HPP
#include <string>
#include <string_view>
#include <unordered_map>
#include <vector>
#include "assembly-ins.h"
#include "assembly-type.h"
#include "error.h"
#include "lexer.h"
namespace panda::pandasm {
// NOLINTBEGIN(misc-non-private-member-variables-in-classes)
/*
* Used to move around tokens.
* *context :
* Returns current value of a token
* ++context :
* sets the next token value
* returns current value of a token
* context++ :
* sets the next token value
* returns the next value of a token
* similarly --context and context--
*/
struct Context {
std::string_view token; /* current token */
std::vector<panda::pandasm::Token> tokens; /* token list */
size_t number = 0; /* line number */
bool end = false; /* end of line flag */
Token::Type id = Token::Type::ID_BAD; /* current token type */
Token::Type signop = Token::Type::ID_BAD; /* current token operand type (if it is an operation) */
panda::pandasm::Error err; /* current error */
int64_t *max_value_of_reg = nullptr;
size_t ins_number = 0;
Type curr_func_return_type;
std::vector<std::pair<size_t, size_t>> *function_arguments_list = nullptr;
std::unordered_map<std::string, std::vector<std::pair<size_t, size_t>>> function_arguments_lists;
void Make(const std::vector<panda::pandasm::Token> &t);
void UpSignOperation();
bool ValidateRegisterName(char c, size_t n = 0) const;
bool ValidateParameterName(size_t number_of_params_already_is) const;
bool ValidateLabel();
bool Mask();
bool NextMask();
size_t Len() const;
std::string_view GiveToken();
Token::Type WaitFor();
Token::Type Next();
Token::Type operator++(int); // NOLINT(cert-dcl21-cpp)
Token::Type operator--(int); // NOLINT(cert-dcl21-cpp)
Token::Type operator++();
Token::Type operator--();
Token::Type operator*();
};
// NOLINTEND(misc-non-private-member-variables-in-classes)
} // namespace panda::pandasm
#endif // !_PANDA_ASSEMBLER_CONTEXT_HPP

View File

@ -0,0 +1,60 @@
/**
* 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_ASSEMBLY_DEBUG_H
#define _PANDA_ASSEMBLY_DEBUG_H
#include <string>
namespace panda::pandasm::debuginfo {
// NOLINTBEGIN(misc-non-private-member-variables-in-classes)
struct Ins {
size_t line_number = 0;
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;
void SetLineNumber(size_t ln)
{
line_number = ln;
}
void SetColumnNumber(size_t cn)
{
column_number = cn;
}
Ins() = default;
Ins(size_t l_n, std::string &f_c, size_t b_l, size_t b_r)
: line_number(l_n), whole_line(std::move(f_c)), bound_left(b_l), bound_right(b_r)
{
}
};
// NOLINTEND(misc-non-private-member-variables-in-classes)
struct LocalVariable {
std::string name;
std::string signature;
std::string signature_type;
int32_t reg = 0;
uint32_t start = 0;
uint32_t length = 0;
};
} // namespace panda::pandasm::debuginfo
#endif // !_PANDA_ASSEMBLY_DEBUG_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,252 @@
/**
* 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_EMITTER_HPP
#define _PANDA_ASSEMBLER_EMITTER_HPP
#include <memory>
#include <type_traits>
#include <vector>
#include <unordered_map>
#include "assembly-ins.h"
#include "assembly-literals.h"
#include "assembly-program.h"
#include "assembly-type.h"
#include "assembly-function.h"
#include "bytecode_emitter.h"
#include "file_item_container.h"
#include "pgo.h"
namespace panda::pandasm {
class AsmEmitter {
public:
struct PandaFileToPandaAsmMaps {
std::unordered_map<uint32_t, std::string> methods;
std::unordered_map<uint32_t, std::string> fields;
std::unordered_map<uint32_t, std::string> classes;
std::unordered_map<uint32_t, std::string> strings;
std::unordered_map<uint32_t, std::string> literalarrays;
};
struct AsmEntityCollections {
std::unordered_map<std::string, panda_file::BaseMethodItem *> method_items;
std::unordered_map<std::string, panda_file::BaseFieldItem *> field_items;
std::unordered_map<std::string, panda_file::BaseClassItem *> class_items;
std::unordered_map<std::string_view, panda_file::StringItem *> string_items;
std::unordered_map<std::string, panda_file::LiteralArrayItem *> literalarray_items;
};
PANDA_PUBLIC_API static bool Emit(panda_file::ItemContainer *items, const Program &program,
PandaFileToPandaAsmMaps *maps = nullptr, bool emit_debug_info = true,
panda::panda_file::pgo::ProfileOptimizer *profile_opt = nullptr);
PANDA_PUBLIC_API 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,
panda::panda_file::pgo::ProfileOptimizer *profile_opt = nullptr);
PANDA_PUBLIC_API 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,
panda::panda_file::pgo::ProfileOptimizer *profile_opt = nullptr);
PANDA_PUBLIC_API static std::unique_ptr<const panda_file::File> Emit(const Program &program,
PandaFileToPandaAsmMaps *maps = nullptr);
PANDA_PUBLIC_API static bool AssignProfileInfo(Program *program);
PANDA_PUBLIC_API static std::string GetLastError()
{
return last_error_;
}
private:
static void MakeStringItems(panda_file::ItemContainer *items, const Program &program,
AsmEntityCollections &entities);
static void MakeLiteralItems(panda_file::ItemContainer *items, const Program &program,
AsmEmitter::AsmEntityCollections &entities);
static void MakeArrayTypeItems(panda_file::ItemContainer *items, const Program &program,
AsmEntityCollections &entities);
static bool HandleRecordAsForeign(
panda_file::ItemContainer *items, const Program &program, AsmEntityCollections &entities,
const std::unordered_map<panda_file::Type::TypeId, panda_file::PrimitiveTypeItem *> &primitive_types,
const std::string &name, const Record &rec);
static bool HandleBaseRecord(panda_file::ItemContainer *items, const Program &program, const std::string &name,
const Record &rec, panda_file::ClassItem *record);
static bool HandleInterfaces(panda_file::ItemContainer *items, const Program &program, const std::string &name,
const Record &rec, panda_file::ClassItem *record);
static bool HandleFields(
panda_file::ItemContainer *items, const Program &program, AsmEmitter::AsmEntityCollections &entities,
const std::unordered_map<panda_file::Type::TypeId, panda_file::PrimitiveTypeItem *> &primitive_types,
const std::string &name, const Record &rec, panda_file::ClassItem *record);
static bool HandleRecord(
panda_file::ItemContainer *items, const Program &program, AsmEntityCollections &entities,
const std::unordered_map<panda_file::Type::TypeId, panda_file::PrimitiveTypeItem *> &primitive_types,
const std::string &name, const Record &rec);
static bool MakeRecordItems(
panda_file::ItemContainer *items, const Program &program, AsmEntityCollections &entities,
const std::unordered_map<panda_file::Type::TypeId, panda_file::PrimitiveTypeItem *> &primitive_types);
static panda_file::StringItem *GetMethodName(panda_file::ItemContainer *items, const Function &func,
const std::string &name);
static bool HandleAreaForInner(panda_file::ItemContainer *items, const Program &program,
panda_file::ClassItem **area, panda_file::ForeignClassItem **foreign_area,
const std::string &name, const std::string &record_owner_name);
static bool HandleRecordOnwer(panda_file::ItemContainer *items, const Program &program,
panda_file::ClassItem **area, panda_file::ForeignClassItem **foreign_area,
const std::string &name, const std::string &record_owner_name);
static bool HandleFunctionParams(
panda_file::ItemContainer *items, const Program &program, size_t idx, const std::string &name,
const Function &func,
const std::unordered_map<panda_file::Type::TypeId, panda_file::PrimitiveTypeItem *> &primitive_types,
std::vector<panda_file::MethodParamItem> &params);
static bool HandleFunctionLocalVariables(panda_file::ItemContainer *items, const Function &func,
const std::string &name);
static bool CreateMethodItem(panda_file::ItemContainer *items, AsmEmitter::AsmEntityCollections &entities,
const Function &func, panda_file::TypeItem *type_item, panda_file::ClassItem *area,
panda_file::ForeignClassItem *foreign_area, uint32_t access_flags,
panda_file::StringItem *method_name, const std::string &mangled_name,
const std::string &name, std::vector<panda_file::MethodParamItem> &params);
static bool MakeFunctionItems(
panda_file::ItemContainer *items, const Program &program, AsmEntityCollections &entities,
const std::unordered_map<panda_file::Type::TypeId, panda_file::PrimitiveTypeItem *> &primitive_types,
bool emit_debug_info);
static bool MakeRecordAnnotations(panda_file::ItemContainer *items, const Program &program,
const AsmEntityCollections &entities);
static void SetCodeAndDebugInfo(panda_file::ItemContainer *items, panda_file::MethodItem *method,
const Function &func, bool emit_debug_info);
static void SetMethodSourceLang(const Program &program, panda_file::MethodItem *method, const Function &func,
const std::string &name);
static bool AddMethodAndParamsAnnotations(panda_file::ItemContainer *items, const Program &program,
const AsmEmitter::AsmEntityCollections &entities,
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, 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);
static bool EmitFunctions(panda_file::ItemContainer *items, const Program &program,
const AsmEntityCollections &entities, bool emit_debug_info);
static panda_file::TypeItem *GetTypeItem(
panda_file::ItemContainer *items,
const std::unordered_map<panda_file::Type::TypeId, panda_file::PrimitiveTypeItem *> &primitive_types,
const Type &type, const Program &program);
PANDA_PUBLIC_API static void SetLastError(const std::string &message)
{
last_error_ = message;
}
static bool CheckValueType(Value::Type value_type, const Type &type, const Program &program);
static bool CheckValueEnumCase(const Value *value, const Type &type, const Program &program);
static bool CheckValueArrayCase(const Value *value, const Type &type, const Program &program);
static bool CheckValueMethodCase(const Value *value, const Program &program);
static bool CheckValueRecordCase(const Value *value, const Program &program);
static bool CheckValue(const Value *value, const 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,
const std::unordered_map<std::string, panda_file::BaseMethodItem *> &methods);
template <class PrimType>
static panda_file::ScalarValueItem *CreateScalarPrimValueItem(panda_file::ItemContainer *container,
const Value *value,
std::vector<panda_file::ScalarValueItem> *out)
{
static_assert(std::is_arithmetic<PrimType>::value);
auto v = value->GetAsScalar()->GetValue<PrimType>();
if (out != nullptr) {
out->emplace_back(v);
return &out->back();
}
if constexpr (std::is_same<PrimType, uint32_t>::value) {
return container->GetOrCreateIntegerValueItem(v);
} else if constexpr (std::is_same<PrimType, uint64_t>::value) {
return container->GetOrCreateLongValueItem(v);
} else if constexpr (std::is_same<PrimType, float>::value) {
return container->GetOrCreateFloatValueItem(v);
} else if constexpr (std::is_same<PrimType, double>::value) {
return container->GetOrCreateDoubleValueItem(v);
} else {
UNREACHABLE();
return nullptr;
}
}
static panda_file::ScalarValueItem *CreateScalarStringValueItem(panda_file::ItemContainer *container,
const Value *value,
std::vector<panda_file::ScalarValueItem> *out);
static panda_file::ScalarValueItem *CreateScalarRecordValueItem(
panda_file::ItemContainer *container, const Value *value, std::vector<panda_file::ScalarValueItem> *out,
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 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);
static panda_file::ScalarValueItem *CreateScalarAnnotationValueItem(
panda_file::ItemContainer *container, const Value *value, std::vector<panda_file::ScalarValueItem> *out,
const Program &program, const std::unordered_map<std::string, panda_file::BaseClassItem *> &classes,
const std::unordered_map<std::string, panda_file::BaseFieldItem *> &fields,
const std::unordered_map<std::string, panda_file::BaseMethodItem *> &methods);
static panda_file::ScalarValueItem *CreateScalarValueItem(
panda_file::ItemContainer *container, const Value *value, std::vector<panda_file::ScalarValueItem> *out,
const Program &program, const std::unordered_map<std::string, panda_file::BaseClassItem *> &classes,
const std::unordered_map<std::string, panda_file::BaseFieldItem *> &fields,
const std::unordered_map<std::string, panda_file::BaseMethodItem *> &methods);
static panda_file::ValueItem *CreateValueItem(
panda_file::ItemContainer *container, const Value *value, const Program &program,
const std::unordered_map<std::string, panda_file::BaseClassItem *> &classes,
const std::unordered_map<std::string, panda_file::BaseFieldItem *> &fields,
const std::unordered_map<std::string, panda_file::BaseMethodItem *> &methods);
static panda_file::AnnotationItem *CreateAnnotationItem(
panda_file::ItemContainer *container, const AnnotationData &annotation, const Program &program,
const std::unordered_map<std::string, panda_file::BaseClassItem *> &classes,
const std::unordered_map<std::string, panda_file::BaseFieldItem *> &fields,
const std::unordered_map<std::string, panda_file::BaseMethodItem *> &methods);
static panda_file::MethodHandleItem *CreateMethodHandleItem(
panda_file::ItemContainer *container, const MethodHandle &mh,
const std::unordered_map<std::string, panda_file::BaseFieldItem *> &fields,
const std::unordered_map<std::string, panda_file::BaseMethodItem *> &methods);
template <class T>
static bool AddAnnotations(T *item, panda_file::ItemContainer *container, const AnnotationMetadata &metadata,
const Program &program,
const std::unordered_map<std::string, panda_file::BaseClassItem *> &classes,
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
// NOLINTNEXTLINE(fuchsia-statically-constructed-objects)
PANDA_PUBLIC_API static std::string last_error_;
};
std::string GetOwnerName(std::string name);
std::string GetItemName(std::string name);
} // namespace panda::pandasm
#endif // !_PANDA_ASSEMBLER_EMITTER_HPP

View File

@ -0,0 +1,49 @@
/**
* 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_FIELD_HPP
#define _PANDA_ASSEMBLER_FIELD_HPP
#include <memory>
#include <string>
#include "assembly-type.h"
#include "extensions/extensions.h"
#include "meta.h"
namespace panda::pandasm {
// NOLINTBEGIN(misc-non-private-member-variables-in-classes)
struct Field {
Type type;
std::string name;
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 met, if the field is not defined */
size_t bound_left = 0;
size_t bound_right = 0;
bool is_defined = true;
explicit Field(panda::panda_file::SourceLang lang)
: metadata(extensions::MetadataExtension::CreateFieldMetadata(lang))
{
}
};
// NOLINTEND(misc-non-private-member-variables-in-classes)
} // namespace panda::pandasm
#endif // !_PANDA_ASSEMBLER_FIELD_HPP

View File

@ -0,0 +1,45 @@
/**
* Copyright (c) 2021-2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _PANDA_ASSEMBLER_FILE_LOCATION_HPP
#define _PANDA_ASSEMBLER_FILE_LOCATION_HPP
namespace panda::pandasm {
// NOLINTBEGIN(misc-non-private-member-variables-in-classes)
class FileLocation {
public:
std::string whole_line; /* The line in which the field is 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;
bool is_defined = false;
public:
FileLocation(std::string &f_c, size_t b_l, size_t b_r, size_t l_n, bool d)
: whole_line(std::move(f_c)), bound_left(b_l), bound_right(b_r), line_number(l_n), is_defined(d)
{
}
~FileLocation() = default;
DEFAULT_MOVE_SEMANTIC(FileLocation);
DEFAULT_COPY_SEMANTIC(FileLocation);
};
// NOLINTEND(misc-non-private-member-variables-in-classes)
} // namespace panda::pandasm
#endif // !_PANDA_ASSEMBLER_FILE_INFORMATION

View File

@ -0,0 +1,192 @@
/**
* 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_FUNCTION_HPP
#define _PANDA_ASSEMBLER_FUNCTION_HPP
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <unordered_map>
#include <vector>
#include "assembly-ins.h"
#include "assembly-label.h"
#include "assembly-type.h"
#include "assembly-debug.h"
#include "assembly-file-location.h"
#include "bytecode_emitter.h"
#include "extensions/extensions.h"
#include "file_items.h"
#include "file_item_container.h"
#include "ide_helpers.h"
#include "meta.h"
namespace panda::pandasm {
// NOLINTBEGIN(misc-non-private-member-variables-in-classes)
struct Function {
struct CatchBlock {
std::string whole_line;
std::string exception_record;
std::string try_begin_label;
std::string try_end_label;
std::string catch_begin_label;
std::string catch_end_label;
};
struct TryCatchInfo {
std::unordered_map<std::string_view, size_t> try_catch_labels;
std::unordered_map<std::string, std::vector<const CatchBlock *>> try_catch_map;
std::vector<std::string> try_catch_order;
TryCatchInfo(std::unordered_map<std::string_view, size_t> &labels,
std::unordered_map<std::string, std::vector<const CatchBlock *>> &map,
std::vector<std::string> &param_try_catch_order)
: try_catch_labels(labels), try_catch_map(map), try_catch_order(param_try_catch_order)
{
}
};
struct Parameter {
Type type;
std::unique_ptr<ParamMetadata> metadata;
Parameter(Type t, panda::panda_file::SourceLang lang)
: type(std::move(t)), metadata(extensions::MetadataExtension::CreateParamMetadata(lang))
{
}
};
std::string name;
panda::panda_file::SourceLang language;
std::unique_ptr<FunctionMetadata> metadata;
std::unordered_map<std::string, panda::pandasm::Label> label_table;
std::vector<panda::pandasm::Ins> ins; /* function instruction list */
std::vector<panda::pandasm::debuginfo::LocalVariable> local_variable_debug;
std::string source_file; /* The file in which the function is defined or empty */
std::string source_code;
std::vector<CatchBlock> catch_blocks;
int64_t value_of_first_param = -1;
size_t regs_num = 0;
size_t profile_size {0};
std::vector<Parameter> params;
bool body_presence = false;
Type return_type;
SourceLocation body_location;
std::optional<FileLocation> file_location;
void SetInsDebug(const std::vector<debuginfo::Ins> &ins_debug)
{
ASSERT(ins_debug.size() == ins.size());
for (std::size_t i = 0; i < ins.size(); i++) {
ins[i].ins_debug = ins_debug[i];
}
}
void AddInstruction(const panda::pandasm::Ins &instruction)
{
ins.emplace_back(instruction);
}
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)),
file_location({f_c, b_l, b_r, l_n, d})
{
}
Function(std::string s, panda::panda_file::SourceLang lang)
: name(std::move(s)), language(lang), metadata(extensions::MetadataExtension::CreateFunctionMetadata(lang))
{
}
std::size_t GetParamsNum() const
{
return params.size();
}
std::size_t GetTotalRegs() const
{
return regs_num;
}
bool IsStatic() const
{
return (metadata->GetAccessFlags() & ACC_STATIC) != 0;
}
bool Emit(BytecodeEmitter &emitter, panda_file::MethodItem *method,
const std::unordered_map<std::string, panda_file::BaseMethodItem *> &methods,
const std::unordered_map<std::string, panda_file::BaseFieldItem *> &fields,
const std::unordered_map<std::string, panda_file::BaseClassItem *> &classes,
const std::unordered_map<std::string_view, panda_file::StringItem *> &strings,
const std::unordered_map<std::string, panda_file::LiteralArrayItem *> &literalarrays) const;
size_t GetLineNumber(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;
void EmitNumber(panda_file::LineNumberProgramItem *program, std::vector<uint8_t> *constant_pool, uint32_t pc_inc,
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 dynamic language now
void EmitColumnNumber(panda_file::LineNumberProgramItem *program, std::vector<uint8_t> *constant_pool,
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,
bool emit_debug_info) const;
Function::TryCatchInfo MakeOrderAndOffsets(const std::vector<uint8_t> &bytecode) const;
std::vector<panda_file::CodeItem::TryBlock> BuildTryBlocks(
panda_file::MethodItem *method, const std::unordered_map<std::string, panda_file::BaseClassItem *> &class_items,
const std::vector<uint8_t> &bytecode) const;
bool HasImplementation() const
{
return !metadata->IsForeign() && metadata->HasImplementation();
}
bool IsParameter(uint32_t reg_number) const
{
return reg_number >= regs_num;
}
bool CanThrow() const
{
return std::any_of(ins.cbegin(), ins.cend(), [](const Ins &insn) { return insn.CanThrow(); });
}
bool HasDebugInfo() const
{
return std::any_of(ins.cbegin(), ins.cend(), [](const Ins &insn) { return insn.HasDebugInfo(); });
}
void DebugDump() const;
};
// NOLINTEND(misc-non-private-member-variables-in-classes)
} // namespace panda::pandasm
#endif // !_PANDA_ASSEMBLER_FUNCTION_HPP

View File

@ -0,0 +1,152 @@
/**
* 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 <iostream>
#include "assembly-ins.h"
namespace panda::pandasm {
std::string panda::pandasm::Ins::RegsToString(bool &first, bool print_args, size_t first_arg_idx) const
{
std::stringstream translator;
for (const auto &reg : this->regs) {
if (!first) {
translator << ",";
} else {
first = false;
}
if (print_args && reg >= first_arg_idx) {
translator << " a" << reg - first_arg_idx;
} else {
translator << " v" << reg;
}
}
return translator.str();
}
std::string panda::pandasm::Ins::ImmsToString(bool &first) const
{
std::stringstream translator;
for (const auto &imm : this->imms) {
if (!first) {
translator << ",";
} else {
first = false;
}
auto *number = std::get_if<double>(&imm);
if (number != nullptr) {
translator << " " << std::scientific << *number;
} else {
translator << " 0x" << std::hex << std::get<int64_t>(imm);
}
translator.clear();
}
return translator.str();
}
std::string panda::pandasm::Ins::IdsToString(bool &first) const
{
std::stringstream translator;
for (const auto &id : this->ids) {
if (!first) {
translator << ",";
} else {
first = false;
}
translator << " " << id;
}
return translator.str();
}
std::string panda::pandasm::Ins::OperandsToString(bool print_args, size_t first_arg_idx) const
{
bool first = true;
std::stringstream ss {};
ss << this->RegsToString(first, print_args, first_arg_idx) << this->ImmsToString(first) << this->IdsToString(first);
return ss.str();
}
std::string panda::pandasm::Ins::RegToString(const 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(const 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(const size_t idx, bool is_first) const
{
if (idx >= ids.size()) {
return std::string("");
}
std::stringstream translator;
if (!is_first) {
translator << ", ";
} else {
translator << " ";
}
translator << ids[idx];
return translator.str();
}
} // namespace panda::pandasm

View File

@ -0,0 +1,259 @@
/**
* 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_INS_HPP
#define _PANDA_ASSEMBLER_INS_HPP
#include <array>
#include <string>
#include <string_view>
#include <unordered_map>
#include <variant>
#include <vector>
#include "assembly-debug.h"
#include "bytecode_emitter.h"
#include "file_items.h"
#include "isa.h"
#include "lexer.h"
namespace panda::pandasm {
enum class Opcode {
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define OPLIST(opcode, name, optype, width, flags, def_idx, use_idxs, prof_size) opcode,
PANDA_INSTRUCTION_LIST(OPLIST)
#undef OPLIST
INVALID,
NUM_OPCODES = INVALID
};
enum InstFlags {
NONE = 0,
JUMP = (1U << 0U),
COND = (1U << 1U),
CALL = (1U << 2U),
RETURN = (1U << 3U),
ACC_READ = (1U << 4U),
ACC_WRITE = (1U << 5U),
PSEUDO = (1U << 6U),
THROWING = (1U << 7U),
METHOD_ID = (1U << 8U),
FIELD_ID = (1U << 9U),
TYPE_ID = (1U << 10U),
STRING_ID = (1U << 11U),
LITERALARRAY_ID = (1U << 12U),
CALL_RANGE = (1U << 13U)
};
constexpr int INVALID_REG_IDX = -1;
constexpr size_t MAX_NUMBER_OF_SRC_REGS = 5; // TODO(mbolshov): auto-generate
constexpr InstFlags operator|(InstFlags a, InstFlags b)
{
using Utype = std::underlying_type_t<InstFlags>;
return static_cast<InstFlags>(static_cast<Utype>(a) | static_cast<Utype>(b));
}
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define OPLIST(opcode, name, optype, width, flags, def_idx, use_idxs, prof_size) flags,
constexpr std::array<unsigned, static_cast<size_t>(Opcode::NUM_OPCODES)> INST_FLAGS_TABLE = {
PANDA_INSTRUCTION_LIST(OPLIST)};
#undef OPLIST
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define OPLIST(opcode, name, optype, width, flags, def_idx, use_idxs, prof_size) width,
constexpr std::array<size_t, static_cast<size_t>(Opcode::NUM_OPCODES)> INST_WIDTH_TABLE = {
PANDA_INSTRUCTION_LIST(OPLIST)};
#undef OPLIST
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define OPLIST(opcode, name, optype, width, flags, def_idx, use_idxs, prof_size) def_idx,
constexpr std::array<int, static_cast<size_t>(Opcode::NUM_OPCODES)> DEF_IDX_TABLE = {PANDA_INSTRUCTION_LIST(OPLIST)};
#undef OPLIST
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define OPLIST(opcode, name, optype, width, flags, def_idx, use_idxs, prof_size) use_idxs,
// 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
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define OPLIST(opcode, name, optype, width, flags, def_idx, use_idxs, prof_size) prof_size,
constexpr std::array<unsigned, static_cast<size_t>(Opcode::NUM_OPCODES) + 1> INST_PROFILE_SIZES = {
PANDA_INSTRUCTION_LIST(OPLIST) 0};
#undef OPLIST
// NOLINTBEGIN(misc-non-private-member-variables-in-classes)
struct Ins {
using IType = std::variant<int64_t, double>;
constexpr static uint16_t ACCUMULATOR = -1;
constexpr static size_t MAX_CALL_SHORT_ARGS = 2;
constexpr static size_t MAX_CALL_ARGS = 4;
constexpr static uint16_t MAX_NON_RANGE_CALL_REG = 15;
constexpr static uint16_t MAX_RANGE_CALL_START_REG = 255;
Opcode opcode = Opcode::INVALID; /* operation type */
std::vector<uint16_t> regs; /* list of arguments - registers */
std::vector<std::string> ids; /* list of arguments - identifiers */
std::vector<IType> imms; /* list of arguments - immediates */
std::string label; /* label at the beginning of a line */
bool set_label = false; /* whether this label is defined */
debuginfo::Ins ins_debug;
uint16_t profile_id {0}; /* Index in the profile vector */
PANDA_PUBLIC_API std::string ToString(const std::string &endline = "", bool print_args = false,
size_t first_arg_idx = 0) const;
bool Emit(BytecodeEmitter &emitter, panda_file::MethodItem *method,
const std::unordered_map<std::string, panda_file::BaseMethodItem *> &methods,
const std::unordered_map<std::string, panda_file::BaseFieldItem *> &fields,
const std::unordered_map<std::string, panda_file::BaseClassItem *> &classes,
const std::unordered_map<std::string_view, panda_file::StringItem *> &strings,
const std::unordered_map<std::string, panda_file::LiteralArrayItem *> &literalarrays,
const std::unordered_map<std::string_view, panda::Label> &labels) const;
size_t OperandListLength() const
{
return regs.size() + ids.size() + imms.size();
}
bool HasFlag(InstFlags flag) const
{
if (opcode == Opcode::INVALID) { // TODO(mbolshov): introduce 'label' opcode for labels
return false;
}
return (INST_FLAGS_TABLE[static_cast<size_t>(opcode)] & flag) != 0;
}
bool CanThrow() const
{
return HasFlag(InstFlags::THROWING) || HasFlag(InstFlags::METHOD_ID) || HasFlag(InstFlags::FIELD_ID) ||
HasFlag(InstFlags::TYPE_ID) || HasFlag(InstFlags::STRING_ID);
}
bool IsJump() const
{
return HasFlag(InstFlags::JUMP);
}
bool IsConditionalJump() const
{
return IsJump() && HasFlag(InstFlags::COND);
}
bool IsCall() const
{ // Non-range call
return HasFlag(InstFlags::CALL);
}
bool IsCallRange() const
{ // Range call
return HasFlag(InstFlags::CALL_RANGE);
}
bool IsPseudoCall() const
{
return HasFlag(InstFlags::PSEUDO) && HasFlag(InstFlags::CALL);
}
bool IsReturn() const
{
return HasFlag(InstFlags::RETURN);
}
size_t MaxRegEncodingWidth() const
{
if (opcode == Opcode::INVALID) {
return 0;
}
return INST_WIDTH_TABLE[static_cast<size_t>(opcode)];
}
std::vector<uint16_t> Uses() const
{
if (IsPseudoCall()) {
return regs;
}
if (opcode == Opcode::INVALID) {
return {};
}
auto use_idxs = USE_IDXS_TABLE[static_cast<size_t>(opcode)];
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 (idx == INVALID_REG_IDX) {
break;
}
ASSERT(static_cast<size_t>(idx) < regs.size());
res.emplace_back(regs[idx]);
}
return res;
}
std::optional<size_t> Def() const
{
if (opcode == Opcode::INVALID) {
return {};
}
auto def_idx = DEF_IDX_TABLE[static_cast<size_t>(opcode)];
if (def_idx != INVALID_REG_IDX) {
return regs[def_idx];
}
if (HasFlag(InstFlags::ACC_WRITE)) {
return Ins::ACCUMULATOR;
}
return {};
}
bool IsValidToEmit() const
{
const auto invalid_reg_num = 1U << MaxRegEncodingWidth();
for (auto reg : regs) {
if (reg >= invalid_reg_num) {
return false;
}
}
return true;
}
bool HasDebugInfo() const
{
return ins_debug.line_number != 0;
}
private:
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;
};
// NOLINTEND(misc-non-private-member-variables-in-classes)
} // namespace panda::pandasm
#endif // !_PANDA_ASSEMBLER_INS_HPP

View File

@ -0,0 +1,38 @@
/**
* 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_LABEL_HPP
#define _PANDA_ASSEMBLER_LABEL_HPP
#include <string>
#include "assembly-file-location.h"
namespace panda::pandasm {
struct Label {
std::string name; // NOLINT(misc-non-private-member-variables-in-classes)
std::optional<FileLocation> file_location; // NOLINT(misc-non-private-member-variables-in-classes)
Label(std::string s, size_t b_l, size_t b_r, std::string f_c, bool d, size_t l_n)
: name(std::move(s)), file_location({f_c, b_l, b_r, l_n, d})
{
}
explicit Label(std::string s) : name(std::move(s)) {}
};
} // namespace panda::pandasm
#endif // !_PANDA_ASSEMBLER_LABEL_HPP

View File

@ -0,0 +1,244 @@
/**
* 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_LITERALARRAY_HPP
#define _PANDA_ASSEMBLER_LITERALARRAY_HPP
#include <string>
#include <vector>
#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;
// 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 IsSigned() const
{
switch (tag) {
case panda_file::LiteralTag::ARRAY_I8:
case panda_file::LiteralTag::ARRAY_I16:
case panda_file::LiteralTag::ARRAY_I32:
case panda_file::LiteralTag::ARRAY_I64:
case panda_file::LiteralTag::INTEGER:
case panda_file::LiteralTag::BIGINT:
return true;
default:
return false;
}
}
bool IsArray() const
{
switch (tag) {
case panda_file::LiteralTag::ARRAY_U1:
case panda_file::LiteralTag::ARRAY_U8:
case panda_file::LiteralTag::ARRAY_I8:
case panda_file::LiteralTag::ARRAY_U16:
case panda_file::LiteralTag::ARRAY_I16:
case panda_file::LiteralTag::ARRAY_U32:
case panda_file::LiteralTag::ARRAY_I32:
case panda_file::LiteralTag::ARRAY_U64:
case panda_file::LiteralTag::ARRAY_I64:
case panda_file::LiteralTag::ARRAY_F32:
case panda_file::LiteralTag::ARRAY_F64:
case panda_file::LiteralTag::ARRAY_STRING:
return true;
default:
return false;
}
}
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:
case panda_file::LiteralTag::BIGINT:
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:
case panda_file::LiteralTag::ASYNCMETHOD:
return true;
default:
return false;
}
}
bool operator==(const LiteralArray::Literal &literal)
{
return tag == literal.tag && value == literal.value;
}
bool operator!=(const LiteralArray::Literal &literal)
{
return !(*this == literal);
}
};
std::vector<panda::pandasm::LiteralArray::Literal>
literals; // NOLINT(misc-non-private-member-variables-in-classes)
explicit LiteralArray(std::vector<panda::pandasm::LiteralArray::Literal> literals_vec)
: literals(std::move(literals_vec))
{
}
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();
}
}
static constexpr panda_file::LiteralTag GetLiteralTagFromComponentType(panda_file::Type::TypeId type)
{
switch (type) {
case panda_file::Type::TypeId::U1:
return panda_file::LiteralTag::BOOL;
case panda_file::Type::TypeId::U8:
case panda_file::Type::TypeId::I8:
case panda_file::Type::TypeId::U16:
case panda_file::Type::TypeId::I16:
case panda_file::Type::TypeId::U32:
case panda_file::Type::TypeId::I32:
return panda_file::LiteralTag::INTEGER;
case panda_file::Type::TypeId::U64:
case panda_file::Type::TypeId::I64:
return panda_file::LiteralTag::BIGINT;
case panda_file::Type::TypeId::F32:
return panda_file::LiteralTag::FLOAT;
case panda_file::Type::TypeId::F64:
return panda_file::LiteralTag::DOUBLE;
default:
UNREACHABLE();
}
}
};
} // namespace panda::pandasm
#endif // !_PANDA_ASSEMBLER_LITERALARRAY_HPP

View File

@ -0,0 +1,31 @@
/**
* 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_METHODHANDLE
#define _PANDA_ASSEMBLER_METHODHANDLE
#include <string>
namespace panda::pandasm {
struct MethodHandle {
std::string item_name; // NOLINT(misc-non-private-member-variables-in-classes)
panda::panda_file::MethodHandleType type; // NOLINT(misc-non-private-member-variables-in-classes)
MethodHandle(std::string s, panda::panda_file::MethodHandleType t) : item_name(std::move(s)), type(t) {}
};
} // namespace panda::pandasm
#endif // _PANDA_ASSEMBLER_METHODHANDLE

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,280 @@
/**
* 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_PARSER_HPP
#define _PANDA_ASSEMBLER_PARSER_HPP
#include <iostream>
#include <memory>
#include <string>
#include <string_view>
#include "assembly-context.h"
#include "assembly-emitter.h"
#include "assembly-field.h"
#include "assembly-function.h"
#include "assembly-ins.h"
#include "assembly-label.h"
#include "assembly-program.h"
#include "assembly-record.h"
#include "assembly-type.h"
#include "define.h"
#include "error.h"
#include "ide_helpers.h"
#include "lexer.h"
#include "meta.h"
#include "utils/expected.h"
namespace panda::pandasm {
using Instructions = std::pair<std::vector<Ins>, Error>;
using Functions = std::pair<std::unordered_map<std::string, Function>, std::unordered_map<std::string, Record>>;
class Parser {
public:
Parser() = default;
NO_MOVE_SEMANTIC(Parser);
NO_COPY_SEMANTIC(Parser);
~Parser() = default;
/*
* The main function of parsing, which takes a vector of token vectors and a name of the source file.
* Returns a program or an error value: Expected<Program, Error>
* This function analyzes code containing several functions:
* - Each function used must be declared.
* - The correct function declaration looks like this: .function ret_type fun_name([param_type aN,]) [<metadata>]
* ([data] shows that this 'data' is optional).
* - N in function parameters must increase when number of parameters increases
* (Possible: a0, a1,..., aN. Impossible: a1, a10, a13).
* - Each function has its own label table.
*/
PANDA_PUBLIC_API Expected<Program, Error> Parse(TokenSet &vectors_tokens, const std::string &file_name = "");
/*
* The main function of parsing, which takes a string with source and a name of the source file.
* Returns a program or an error value: Expected<Program, Error>
*/
PANDA_PUBLIC_API Expected<Program, Error> Parse(const std::string &source, const std::string &file_name = "");
/*
* Returns a set error
*/
Error ShowError() const
{
return err_;
}
ErrorList ShowWarnings() const
{
return war_;
}
private:
panda::pandasm::Program program_;
std::unordered_map<std::string, panda::pandasm::Label> *label_table_ = nullptr;
Metadata *metadata_ = nullptr;
Context context_; /* token iterator */
panda::pandasm::Record *curr_record_ = nullptr;
panda::pandasm::LiteralArray *curr_array_ = nullptr;
bool is_const_array_ = false;
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;
size_t line_stric_ = 0;
panda::pandasm::Error err_;
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
{
return Error(mess, line_stric_, err, add_mess,
context_.tokens[static_cast<int>(context_.number) + token_shift - 1].bound_left + shift,
context_.tokens[static_cast<int>(context_.number) + token_shift - 1].bound_right,
context_.tokens[static_cast<int>(context_.number) + token_shift - 1].whole_line);
}
inline void GetWarning(const std::string &mess = "", Error::ErrorType err = Error::ErrorType::ERR_NONE,
int8_t shift = 0, const std::string &add_mess = "")
{
war_.emplace_back(mess, line_stric_, err, add_mess,
context_.tokens[context_.number - 1].bound_left + static_cast<size_t>(shift),
context_.tokens[context_.number - 1].bound_right,
context_.tokens[context_.number - 1].whole_line, Error::ErrorClass::WARNING);
}
SourcePosition GetCurrentPosition(bool left_bound) const
{
if (left_bound) {
return SourcePosition {line_stric_, context_.tokens[context_.number - 1].bound_left};
}
return SourcePosition {line_stric_, context_.tokens[context_.number - 1].bound_right};
}
bool LabelValidName();
bool TypeValidName();
bool RegValidName();
bool ParamValidName();
bool FunctionValidName();
bool ParseFunctionName();
bool ParseLabel();
bool ParseOperation();
bool ParseOperands();
bool ParseFunctionCode();
bool ParseFunctionInstruction();
bool ParseFunctionFullSign();
bool UpdateFunctionName();
bool ParseFunctionReturn();
bool ParseFunctionArg();
bool ParseFunctionArgComma(bool &comma);
bool ParseFunctionArgs();
bool ParseType(Type *type);
bool PrefixedValidName(bool allow_brackets = false);
bool ParseMetaListComma(bool &comma, bool eq);
bool MeetExpMetaList(bool eq);
bool BuildMetaListAttr(bool &eq, std::string &attribute_name, std::string &attribute_value);
bool ParseMetaList(bool flag);
bool ParseMetaDef();
bool ParseRecordFullSign();
bool ParseRecordFields();
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();
uint8_t ParseMultiArrayHallmark();
bool ParseInteger(int64_t *value);
bool ParseFloat(double *value, bool is_64bit);
bool ParseOperandVreg();
bool ParseOperandComma();
bool ParseOperandInteger();
bool ParseOperandFloat(bool is_64bit);
bool ParseOperandId();
bool ParseOperandLabel();
bool ParseOperandField();
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();
bool ParseOperandInitobj();
void SetFunctionInformation();
void SetRecordInformation(const std::string &record_name);
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 ParseAsUnionField(const std::vector<Token> &tokens);
void ParseAsBraceRight(const std::vector<Token> &tokens);
bool ParseAfterLine(bool &is_first_statement);
Expected<Program, Error> ParseAfterMainLoop(const std::string &file_name);
void ParseResetFunctionLabelsAndParams();
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);
void ParseAsCatchDirective();
void SetError();
void SetMetadataContextError(const Metadata::Error &err, bool has_value);
Expected<char, Error> ParseOctalEscapeSequence(std::string_view s, size_t *i);
Expected<char, Error> ParseHexEscapeSequence(std::string_view s, size_t *i);
Expected<char, Error> ParseEscapeSequence(std::string_view s, size_t *i);
template <class T>
auto TryEmplaceInTable(bool flag, T &item, const std::string &cid)
{
return item.try_emplace(cid, cid, program_.lang, context_.tokens[context_.number - 1].bound_left,
context_.tokens[context_.number - 1].bound_right,
context_.tokens[context_.number - 1].whole_line, flag, line_stric_);
}
template <class T>
bool AddObjectInTable(bool flag, T &item, const std::string &cid = "")
{
std::string elem = !cid.empty() ? cid : std::string(context_.GiveToken().data(), context_.GiveToken().length());
auto [iter, is_inserted] = TryEmplaceInTable(flag, item, elem);
if (is_inserted) {
return true;
}
if (iter->second.file_location->is_defined && flag) {
return false;
}
if (!iter->second.file_location->is_defined && flag) {
iter->second.file_location->is_defined = true;
return true;
}
if (!iter->second.file_location->is_defined) {
iter->second.file_location->bound_left = context_.tokens[context_.number - 1].bound_left;
iter->second.file_location->bound_right = context_.tokens[context_.number - 1].bound_right;
iter->second.file_location->whole_line = context_.tokens[context_.number - 1].whole_line;
iter->second.file_location->line_number = line_stric_;
}
return true;
}
};
template <>
inline auto Parser::TryEmplaceInTable(bool flag, std::unordered_map<std::string, panda::pandasm::Label> &item,
[[maybe_unused]] const std::string &cid)
{
return item.try_emplace(std::string(context_.GiveToken().data(), context_.GiveToken().length()),
std::string(context_.GiveToken().data(), context_.GiveToken().length()),
context_.tokens[context_.number - 1].bound_left,
context_.tokens[context_.number - 1].bound_right,
context_.tokens[context_.number - 1].whole_line, flag, line_stric_);
}
} // namespace panda::pandasm
#endif // !_PANDA_ASSEMBLER_PARSER_HPP

View File

@ -0,0 +1,34 @@
/**
* 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 <sstream>
#include "assembly-program.h"
#include "ide_helpers.h"
namespace panda::pandasm {
std::string Program::JsonDump() const
{
std::stringstream ss;
ss << "{ \"functions\": ";
ss << JsonSerializeProgramItems(function_table);
ss << ", \"records\": ";
ss << JsonSerializeProgramItems(record_table);
ss << " }";
return ss.str();
}
} // namespace panda::pandasm

View File

@ -0,0 +1,51 @@
/**
* 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_PROGRAM_HPP
#define _PANDA_ASSEMBLER_PROGRAM_HPP
#include <string>
#include <unordered_set>
#include "assembly-function.h"
#include "assembly-record.h"
#include "assembly-type.h"
#include "assembly-methodhandle.h"
#include "assembly-literals.h"
#include "extensions/extensions.h"
#include "macros.h"
namespace panda::pandasm {
// NOLINTBEGIN(misc-non-private-member-variables-in-classes)
struct Program {
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;
/*
* Returns a JSON string with the program structure and scopes locations
*/
PANDA_PUBLIC_API std::string JsonDump() const;
};
// NOLINTEND(misc-non-private-member-variables-in-classes)
} // namespace panda::pandasm
#endif // !_PANDA_ASSEMBLER_PROGRAM_HPP

View File

@ -0,0 +1,66 @@
/**
* Copyright (c) 2021-2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _PANDA_ASSEMBLER_RECORD_HPP
#define _PANDA_ASSEMBLER_RECORD_HPP
#include <memory>
#include <optional>
#include <string>
#include <vector>
#include "assembly-field.h"
#include "extensions/extensions.h"
#include "ide_helpers.h"
namespace panda::pandasm {
// NOLINTBEGIN(misc-non-private-member-variables-in-classes)
struct Record {
std::string name;
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;
bool body_presence = false;
SourceLocation body_location;
std::string source_file; /* The file in which the record is defined or empty */
std::optional<FileLocation> file_location;
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)),
file_location({f_c, b_l, b_r, l_n, d})
{
}
Record(std::string s, panda::panda_file::SourceLang lang)
: name(std::move(s)), language(lang), metadata(extensions::MetadataExtension::CreateRecordMetadata(lang))
{
}
bool HasImplementation() const
{
return !metadata->IsForeign();
}
};
// NOLINTEND(misc-non-private-member-variables-in-classes)
} // namespace panda::pandasm
#endif // !_PANDA_ASSEMBLER_RECORD_HPP

View File

@ -0,0 +1,132 @@
/**
* 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 <unordered_map>
#include "assembly-type.h"
namespace panda::pandasm {
// NOLINTNEXTLINE(fuchsia-statically-constructed-objects)
static std::unordered_map<std::string_view, std::string_view> PRIMITIVE_TYPES = {
{"u1", "Z"}, {"i8", "B"}, {"u8", "H"}, {"i16", "S"}, {"u16", "C"}, {"i32", "I"}, {"u32", "U"},
{"f32", "F"}, {"f64", "D"}, {"i64", "J"}, {"u64", "Q"}, {"void", "V"}, {"any", "A"}};
std::string Type::GetDescriptor(bool ignore_primitive) const
{
if (!ignore_primitive) {
auto it = PRIMITIVE_TYPES.find(component_name_);
if (it != PRIMITIVE_TYPES.cend()) {
return std::string(rank_, '[') + it->second.data();
}
}
std::string res = std::string(rank_, '[') + "L" + component_name_ + ";";
std::replace(res.begin(), res.end(), '.', '/');
return res;
}
/* static */
panda_file::Type::TypeId Type::GetId(std::string_view name, bool ignore_primitive)
{
static std::unordered_map<std::string_view, panda_file::Type::TypeId> panda_types = {
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define PANDATYPE(name, inst_code) {std::string_view(name), panda_file::Type::TypeId::inst_code},
PANDA_ASSEMBLER_TYPES(PANDATYPE)
#undef PANDATYPE
};
if (!ignore_primitive) {
auto iter = panda_types.find(name);
if (iter == panda_types.end()) {
return panda_file::Type::TypeId::REFERENCE;
}
return iter->second;
}
return panda_file::Type::TypeId::REFERENCE;
}
/* static */
std::string Type::GetName(std::string_view component_name, size_t rank)
{
std::string name(component_name);
while (rank-- > 0) {
name += "[]";
}
return name;
}
/* static */
Type Type::FromDescriptor(std::string_view descriptor)
{
static std::unordered_map<std::string_view, std::string_view> reverse_primitive_types = {
{"Z", "u1"}, {"B", "i8"}, {"H", "u8"}, {"S", "i16"}, {"C", "u16"}, {"I", "i32"}, {"U", "u32"},
{"F", "f32"}, {"D", "f64"}, {"J", "i64"}, {"Q", "u64"}, {"V", "void"}, {"A", "any"}};
size_t i = 0;
while (descriptor[i] == '[') {
++i;
}
size_t rank = i;
bool is_ref_type = descriptor[i] == 'L';
if (is_ref_type) {
descriptor.remove_suffix(1); /* Remove semicolon */
++i;
}
descriptor.remove_prefix(i);
if (is_ref_type) {
return Type(descriptor, rank);
}
return Type(reverse_primitive_types[descriptor], rank);
}
/* static */
Type Type::FromName(std::string_view name, bool ignore_primitive)
{
constexpr size_t STEP = 2;
size_t size = name.size();
size_t i = 0;
while (name[size - i - 1] == ']') {
i += STEP;
}
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));
return name == string_type.GetName();
}
/* static */
bool Type::IsPandaPrimitiveType(const std::string &name)
{
auto it = PRIMITIVE_TYPES.find(name);
return it != PRIMITIVE_TYPES.cend();
}
} // namespace panda::pandasm

View File

@ -0,0 +1,197 @@
/**
* 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_TYPE_H
#define _PANDA_ASSEMBLER_TYPE_H
#include "define.h"
#include "file_items.h"
#include "isa.h"
#include "libpandabase/macros.h"
namespace panda::pandasm {
class Type {
public:
enum VerificationType {
TYPE_ID_OBJECT,
TYPE_ID_ARRAY,
TYPE_ID_CLASS,
TYPE_ID_ANY_OBJECT,
};
Type() = default;
DEFAULT_MOVE_SEMANTIC(Type);
DEFAULT_COPY_SEMANTIC(Type);
~Type() = default;
Type(std::string_view component_name, size_t rank, bool ignore_primitive = false)
: component_name_(component_name), rank_(rank)
{
name_ = GetName(component_name_, rank_);
type_id_ = GetId(name_, ignore_primitive);
}
Type(const Type &component_type, size_t rank)
: Type(component_type.GetComponentName(), component_type.GetRank() + rank)
{
}
PANDA_PUBLIC_API std::string GetDescriptor(bool ignore_primitive = false) const;
PANDA_PUBLIC_API std::string GetName() const
{
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_;
}
size_t GetRank() const
{
return rank_;
}
Type GetComponentType() const
{
return Type(component_name_, rank_ > 0 ? rank_ - 1 : 0);
}
panda_file::Type::TypeId GetId() const
{
return type_id_;
}
bool IsArrayContainsPrimTypes() const
{
auto elem = GetId(component_name_);
return elem != panda_file::Type::TypeId::REFERENCE;
}
bool IsValid() const
{
return !component_name_.empty();
}
bool IsArray() const
{
return rank_ > 0;
}
bool IsObject() const
{
return type_id_ == panda_file::Type::TypeId::REFERENCE;
}
bool IsTagged() const
{
return type_id_ == panda_file::Type::TypeId::TAGGED;
}
bool IsIntegral() const
{
return type_id_ == panda_file::Type::TypeId::U1 || type_id_ == panda_file::Type::TypeId::U8 ||
type_id_ == panda_file::Type::TypeId::I8 || type_id_ == panda_file::Type::TypeId::U16 ||
type_id_ == panda_file::Type::TypeId::I16 || type_id_ == panda_file::Type::TypeId::U32 ||
type_id_ == panda_file::Type::TypeId::I32 || type_id_ == panda_file::Type::TypeId::U64 ||
type_id_ == panda_file::Type::TypeId::I64;
}
bool FitsInto32() const
{
return type_id_ == panda_file::Type::TypeId::U1 || type_id_ == panda_file::Type::TypeId::U8 ||
type_id_ == panda_file::Type::TypeId::I8 || type_id_ == panda_file::Type::TypeId::U16 ||
type_id_ == panda_file::Type::TypeId::I16 || type_id_ == panda_file::Type::TypeId::U32 ||
type_id_ == panda_file::Type::TypeId::I32;
}
bool IsFloat32() const
{
return type_id_ == panda_file::Type::TypeId::F32;
}
bool IsFloat64() const
{
return type_id_ == panda_file::Type::TypeId::F64;
}
bool IsPrim32() const
{
return (IsIntegral() && FitsInto32()) || IsFloat32();
}
bool IsPrim64() const
{
return (IsIntegral() && !FitsInto32()) || IsFloat64();
}
bool IsPrimitive() const
{
return IsPrim64() || IsPrim32();
}
bool IsVoid() const
{
return type_id_ == panda_file::Type::TypeId::VOID;
}
PANDA_PUBLIC_API static panda_file::Type::TypeId GetId(std::string_view name, bool ignore_primitive = false);
bool operator==(const Type &type) const
{
return name_ == type.name_;
}
static PANDA_PUBLIC_API Type FromDescriptor(std::string_view descriptor);
static PANDA_PUBLIC_API Type FromName(std::string_view name, bool ignore_primitive = false);
static PANDA_PUBLIC_API bool IsPandaPrimitiveType(const std::string &name);
static bool IsStringType(const std::string &name, panda::panda_file::SourceLang lang);
private:
static PANDA_PUBLIC_API std::string GetName(std::string_view component_name, size_t rank);
std::string component_name_;
size_t rank_ {0};
std::string name_;
panda_file::Type::TypeId type_id_ {panda_file::Type::TypeId::VOID};
};
} // namespace panda::pandasm
namespace std {
template <>
class hash<panda::pandasm::Type> {
public:
size_t operator()(const panda::pandasm::Type &type) const
{
return std::hash<std::string>()(type.GetName());
}
};
} // namespace std
#endif // !_PANDA_ASSEMBLER_TYPE_H

View File

@ -0,0 +1,210 @@
/**
* 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 <string_view>
#include "assembly-parser.h"
#include "utils/number-utils.h"
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),
tokens[number - 1].bound_right - tokens[number - 1].bound_left);
id = this->tokens[number - 1].type;
}
size_t Context::Len() const
{
return token.size();
}
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;
}
if (c != 'a') {
for (const auto &ch : p) {
if (std::isdigit(ch) == 0) {
return false;
}
}
} else {
if (ToNumber(p) > n) {
return false;
}
}
return true;
}
return false;
}
bool Context::ValidateParameterName(size_t number_of_params_already_is) const
{
if (number_of_params_already_is >= MAX_DWORD) {
return false;
}
if (token[0] == 'a') {
std::string_view p = token;
p.remove_prefix(1);
if (ToNumber(p) == number_of_params_already_is) {
return true;
}
}
return false;
}
std::string_view Context::GiveToken()
{
return token;
}
Token::Type Context::Next()
{
if (this->tokens.size() > number) {
return this->tokens[number].type;
}
return this->tokens[number - 1].type;
}
void Context::UpSignOperation()
{
signop = id;
}
Token::Type Context::WaitFor()
{
return signop;
}
bool Context::Mask()
{
return end;
}
bool Context::NextMask()
{
if (end) {
return true;
}
return this->tokens.size() < number + 1;
}
// NOLINTNEXTLINE(cert-dcl21-cpp)
Token::Type Context::operator++(int)
{
Token::Type last_id = id;
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),
tokens[number - 1].bound_right - tokens[number - 1].bound_left);
} else {
end = true;
}
return last_id;
}
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),
tokens[number - 1].bound_right - tokens[number - 1].bound_left);
} else {
end = true;
}
return id;
}
// NOLINTNEXTLINE(cert-dcl21-cpp)
Token::Type Context::operator--(int)
{
Token::Type last_id = id;
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),
tokens[number - 1].bound_right - tokens[number - 1].bound_left);
} else {
end = false;
}
return last_id;
}
Token::Type Context::operator--()
{
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),
tokens[number - 1].bound_right - tokens[number - 1].bound_left);
} else {
end = false;
}
return id;
}
Token::Type Context::operator*()
{
return id;
}
} // namespace panda::pandasm

View File

@ -0,0 +1,52 @@
/**
* 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_DEFINE_HPP
#define _PANDA_ASSEMBLER_DEFINE_HPP
/* Implementation-specific defines */
constexpr char PARSE_COMMENT_MARKER = '#';
constexpr char PARSE_AREA_MARKER = '.';
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define PANDA_ASSEMBLER_TYPES(_) \
_("void", VOID) \
_("u1", U1) \
_("u8", U8) \
_("i8", I8) \
_("u16", U16) \
_("i16", I16) \
_("u32", U32) \
_("i32", I32) \
_("u64", U64) \
_("i64", I64) \
_("f32", F32) \
_("f64", F64) \
_("any", TAGGED)
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define KEYWORDS_LIST(_) \
_(".catch", CATCH) \
_(".catchall", CATCHALL) \
_(".language", LANG) \
_(".function", FUN) \
_(".union_field", UNION) \
_(".record", REC) \
_(".array", ARR) \
_(".field", FLD)
#endif /* !_PANDA_ASSEMBLER_DEFINE_HPP */

View File

@ -0,0 +1,136 @@
/**
* 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_ERROR_HPP
#define _PANDA_ASSEMBLER_ERROR_HPP
#include <string>
namespace panda::pandasm {
// NOLINTBEGIN(misc-non-private-member-variables-in-classes)
struct Error {
enum class ErrorClass { WARNING = 0, ERROR };
enum class ErrorType {
ERR_NONE = 0,
// Lexer
ERR_STRING_MISSING_TERMINATING_CHARACTER,
// Parser
ERR_BAD_LABEL,
ERR_BAD_LABEL_EXT,
ERR_BAD_NAME_ID,
ERR_BAD_NAME_REG,
ERR_BAD_INTEGER_NAME,
ERR_BAD_INTEGER_WIDTH,
ERR_BAD_FLOAT_NAME,
ERR_BAD_FLOAT_WIDTH,
ERR_BAD_NUMBER_OPERANDS,
ERR_BAD_OPERAND,
ERR_BAD_OPERATION_NAME,
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,
ERR_BAD_METADATA_BOUND,
ERR_BAD_METADATA_UNKNOWN_ATTRIBUTE,
ERR_BAD_METADATA_INVALID_VALUE,
ERR_BAD_METADATA_MISSING_ATTRIBUTE,
ERR_BAD_METADATA_MISSING_VALUE,
ERR_BAD_METADATA_UNEXPECTED_ATTRIBUTE,
ERR_BAD_METADATA_UNEXPECTED_VALUE,
ERR_BAD_METADATA_MULTIPLE_ATTRIBUTE,
ERR_BAD_FUNCTION_PARAMETERS,
ERR_BAD_FUNCTION_RETURN_VALUE,
ERR_FUNCTION_ARGUMENT_MISMATCH,
ERR_BAD_FIELD_MISSING_NAME,
ERR_BAD_FIELD_VALUE_TYPE,
ERR_BAD_FIELD_METADATA,
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,
ERR_BAD_BOUND,
ERR_BAD_END,
ERR_BAD_CLOSE,
ERR_BAD_ARGS_BOUND,
ERR_BAD_TYPE,
ERR_BAD_PARAM_NAME,
ERR_BAD_NOEXP_DELIM,
ERR_BAD_STRING_INVALID_HEX_ESCAPE_SEQUENCE,
ERR_BAD_STRING_UNKNOWN_ESCAPE_SEQUENCE,
ERR_BAD_ARRAY_TYPE_BOUND,
ERR_UNDEFINED_TYPE,
ERR_MULTIPLE_DIRECTIVES,
ERR_INCORRECT_DIRECTIVE_LOCATION,
ERR_BAD_DIRECTIVE_DECLARATION,
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,
WAR_UNEXPECTED_TYPE_ID,
};
ErrorClass type;
std::string whole_line;
size_t pos; /* positions to highlight the word */
size_t end;
ErrorType err;
std::string message;
std::string verbose;
size_t line_number;
inline Error() : Error("No messages", 0, ErrorType::ERR_NONE, "", 0, 0, "") {}
inline Error(std::string s, size_t line, ErrorType error_type, std::string overinfo, size_t p, size_t e,
std::string buff, ErrorClass class_type = ErrorClass::ERROR)
: type(class_type),
whole_line(std::move(buff)),
pos(p),
end(e),
err(error_type),
message(std::move(s)),
verbose(std::move(overinfo)),
line_number(line)
{
}
};
// NOLINTEND(misc-non-private-member-variables-in-classes)
using ErrorList = std::vector<Error>;
} // namespace panda::pandasm
#endif // !_PANDA_ASSEMBLER_ERROR_HPP

View File

@ -0,0 +1,30 @@
# Copyright (c) 2021-2022 Huawei Device Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
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_obj assembler_extensions)
add_dependencies(panda_gen_files assembler_extensions)

View File

@ -0,0 +1,22 @@
/**
* 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 "extensions.h"
#include "macros.h"
#include "register_extensions.h"
namespace panda::pandasm::extensions {
} // namespace panda::pandasm::extensions

View File

@ -0,0 +1,45 @@
/**
* Copyright (c) 2021-2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef PANDA_ASSEMBLER_EXTENSIONS_H_
#define PANDA_ASSEMBLER_EXTENSIONS_H_
#include <memory>
#include <optional>
#include "meta.h"
#include "libpandafile/file_items.h"
#include "libpandabase/macros.h"
namespace panda::pandasm::extensions {
// Workaround for ts2abc. Should be removed by our colleagues.
using Language = panda::panda_file::SourceLang;
class MetadataExtension {
public:
static PANDA_PUBLIC_API std::unique_ptr<RecordMetadata> CreateRecordMetadata(panda::panda_file::SourceLang lang);
static PANDA_PUBLIC_API std::unique_ptr<FieldMetadata> CreateFieldMetadata(panda::panda_file::SourceLang lang);
static PANDA_PUBLIC_API std::unique_ptr<FunctionMetadata> CreateFunctionMetadata(
panda::panda_file::SourceLang lang);
static PANDA_PUBLIC_API std::unique_ptr<ParamMetadata> CreateParamMetadata(panda::panda_file::SourceLang lang);
};
} // namespace panda::pandasm::extensions
#endif // PANDA_ASSEMBLER_EXTENSIONS_H_

View File

@ -0,0 +1,54 @@
/**
* 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"
% Common::plugins.each_value do |plugin_opts|
% next unless plugin_opts["Metadatas"]
% plugin_opts["Metadatas"].each_value do |new_class_opts|
// NOLINTNEXTLINE(readability-duplicate-include)
#include "<%= new_class_opts["header_path"] %>"
% end
% end
namespace panda::pandasm::extensions {
// NOLINTBEGIN(misc-definitions-in-headers)
%["RecordMetadata", "FieldMetadata", "FunctionMetadata", "ParamMetadata"].each do |metadata|
/* static */
std::unique_ptr<<%= metadata %>> MetadataExtension::Create<%= metadata %>([[maybe_unused]] panda::panda_file::SourceLang lang)
{
% Common::plugins.each_value do |plugin_opts|
% next unless plugin_opts["Metadatas"]
% next unless plugin_opts["Metadatas"][metadata]
if (lang == <%= plugin_opts["lang_enum"] %>) {
return std::make_unique <<%= plugin_opts["Metadatas"][metadata]["new_class_name"] %>>();
}
% end
return std::make_unique<<%= metadata %>>();
}
% end
// NOLINTEND(misc-definitions-in-headers)
} // namespace panda::pandasm::extensions
#endif // PANDA_ASSEMBLER_EXTENSIONS_REGISTER_EXTENSIONS_H

View File

@ -0,0 +1,88 @@
/**
* 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_IDE_HELPERS_H
#define PANDA_ASSEMBLER_IDE_HELPERS_H
#include <sstream>
namespace panda::pandasm {
// NOLINTBEGIN(misc-non-private-member-variables-in-classes)
struct SourcePosition {
size_t line = 0;
size_t column = 0;
std::string JsonSerialize() const
{
std::stringstream ss;
ss << "{ "
<< "\"line\": " << line << ", "
<< "\"column\": " << column << " }";
return ss.str();
}
};
struct SourceLocation {
SourcePosition begin;
SourcePosition end;
std::string JsonSerialize() const
{
std::stringstream ss;
ss << "{ "
<< "\"begin\": " << begin.JsonSerialize() << ", "
<< "\"end\": " << end.JsonSerialize() << " }";
return ss.str();
}
};
// NOLINTEND(misc-non-private-member-variables-in-classes)
template <typename T>
std::string JsonSerializeItemBody(const T &item)
{
std::stringstream ss;
std::string quoted_name = "\"" + item.name + "\"";
ss << "{ "
<< "\"name\": " << quoted_name;
if (item.file_location->is_defined) {
ss << ", "
<< "\"bodyLocation\": " << item.body_location.JsonSerialize() << " }";
} else {
ss << " }";
}
return ss.str();
}
template <typename T>
std::string JsonSerializeProgramItems(const T &item_table)
{
std::stringstream ss;
ss << "[ ";
auto it = item_table.begin();
if (it != item_table.end()) {
ss << JsonSerializeItemBody(it->second);
++it;
}
while (it != item_table.end()) {
ss << ", " << JsonSerializeItemBody(it->second);
++it;
}
ss << " ]";
return ss.str();
}
} // namespace panda::pandasm
#endif // PANDA_ASSEMBLER_IDE_HELPERS_H

View File

@ -0,0 +1,405 @@
/**
* 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 "lexer.h"
namespace panda::pandasm {
/*-------------------------------*/
/* Is this a delimiter ? */
Token::Type FindDelim(char c)
{
/* 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},
{'}', Token::Type::DEL_BRACE_R},
{'(', Token::Type::DEL_BRACKET_L},
{')', Token::Type::DEL_BRACKET_R},
{'<', Token::Type::DEL_LT},
{'>', Token::Type::DEL_GT},
{'=', Token::Type::DEL_EQ},
{'[', Token::Type::DEL_SQUARE_BRACKET_L},
{']', Token::Type::DEL_SQUARE_BRACKET_R}};
auto iter = DELIM.find(c);
if (iter == DELIM.end()) {
return Token::Type::ID_BAD;
}
return DELIM.at(c);
}
Token::Type FindOperation(std::string_view s)
{
/* 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, prof_size) \
{std::string_view(name), Token::Type::ID_OP_##inst_code},
PANDA_INSTRUCTION_LIST(OPLIST)
#undef OPLIST
};
auto iter = OPERATIONS.find(s);
if (iter == OPERATIONS.end()) {
return Token::Type::ID_BAD;
}
return OPERATIONS.at(s);
}
Token::Type Findkeyword(std::string_view s)
{
/* 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},
KEYWORDS_LIST(KEYWORDS)
#undef KEYWORDS
};
auto iter = KEYWORDS.find(s);
if (iter == KEYWORDS.end()) {
return Token::Type::ID_BAD;
}
return KEYWORDS.at(s);
}
std::string_view TokenTypeWhat(Token::Type t)
{
if (t >= Token::Type::OPERATION && t < Token::Type::KEYWORD) {
return "OPERATION";
}
if (t >= Token::Type::KEYWORD) {
return "KEYWORD";
}
switch (t) {
case Token::Type::ID_BAD: {
return "ID_BAD";
}
case Token::Type::DEL_COMMA: {
return "DEL_COMMA";
}
case Token::Type::DEL_COLON: {
return "DEL_COLON";
}
case Token::Type::DEL_BRACE_L: {
return "DEL_BRACE_L";
}
case Token::Type::DEL_BRACE_R: {
return "DEL_BRACE_R";
}
case Token::Type::DEL_BRACKET_L: {
return "DEL_BRACKET_L";
}
case Token::Type::DEL_BRACKET_R: {
return "DEL_BRACKET_R";
}
case Token::Type::DEL_SQUARE_BRACKET_L: {
return "DEL_SQUARE_BRACKET_L";
}
case Token::Type::DEL_SQUARE_BRACKET_R: {
return "DEL_SQUARE_BRACKET_R";
}
case Token::Type::DEL_GT: {
return "DEL_GT";
}
case Token::Type::DEL_LT: {
return "DEL_LT";
}
case Token::Type::DEL_EQ: {
return "DEL_EQ";
}
case Token::Type::DEL_DOT: {
return "DEL_DOT";
}
case Token::Type::ID: {
return "ID";
}
case Token::Type::ID_STRING: {
return "ID_STRING";
}
default:
return "NONE";
}
}
static bool IsQuote(char c)
{
return c == '"';
}
Lexer::Lexer()
{
LOG(DEBUG, ASSEMBLER) << "element of class Lexer initialized";
}
Lexer::~Lexer()
{
LOG(DEBUG, ASSEMBLER) << "element of class Lexer destructed";
}
Tokens Lexer::TokenizeString(const std::string &source_str)
{
LOG(DEBUG, ASSEMBLER) << "started tokenizing of line " << lines_.size() + 1 << ": ";
lines_.emplace_back(source_str);
curr_line_ = &lines_.back();
LOG(DEBUG, ASSEMBLER) << std::string_view(&*(curr_line_->buffer.begin() + curr_line_->pos),
curr_line_->end - curr_line_->pos);
AnalyzeLine();
LOG(DEBUG, ASSEMBLER) << "tokenization of line " << lines_.size() << " is successful";
LOG(DEBUG, ASSEMBLER) << " tokens identified: ";
for (const auto &f_i : lines_.back().tokens) {
LOG(DEBUG, ASSEMBLER) << "\n "
<< std::string_view(&*(f_i.whole_line.begin() + f_i.bound_left),
f_i.bound_right - f_i.bound_left)
<< " (type: " << TokenTypeWhat(f_i.type) << ")";
LOG(DEBUG, ASSEMBLER);
LOG(DEBUG, ASSEMBLER);
}
return std::pair<std::vector<Token>, Error>(lines_.back().tokens, err_);
}
/* End of line? */
bool Lexer::Eol() const
{
return curr_line_->pos == curr_line_->end;
}
/* 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 */
return FindDelim(curr_line_->buffer[beg]);
}
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;
}
if (IsQuote(curr_line_->buffer[beg])) {
return Token::Type::ID_STRING;
}
return Token::Type::ID; /* other */
}
/* Handle string literal */
bool Lexer::LexString()
{
bool is_escape_seq = false;
char quote = curr_line_->buffer[curr_line_->pos];
size_t begin = curr_line_->pos;
while (!Eol()) {
++(curr_line_->pos);
char c = curr_line_->buffer[curr_line_->pos];
if (is_escape_seq) {
is_escape_seq = false;
continue;
}
if (c == '\\') {
is_escape_seq = true;
}
if (c == quote) {
break;
}
}
if (curr_line_->buffer[curr_line_->pos] != quote) {
err_ = Error(std::string("Missing terminating ") + quote + " character", 0,
Error::ErrorType::ERR_STRING_MISSING_TERMINATING_CHARACTER, "", begin, curr_line_->pos,
curr_line_->buffer);
return false;
}
++(curr_line_->pos);
return true;
}
/*
* 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.
*
* bound_r1 bound_r2 bound_r3
* | | |
* v v v
* token1 token2 token3 ... token1 token2 token3 ...
* => ^ ^ ^
* | | |
* bound1 bound2 bound3 ... bound_l1 bound_l2 bound_l3 ...
*
*/
void Lexer::LexTokens()
{
if (Eol()) {
return;
}
LOG(DEBUG, ASSEMBLER) << "token search started (line " << lines_.size() << "): "
<< std::string_view(&*(curr_line_->buffer.begin() + curr_line_->pos),
curr_line_->end - curr_line_->pos);
while (curr_line_->end > curr_line_->pos && isspace(curr_line_->buffer[curr_line_->end - 1]) != 0) {
--(curr_line_->end);
}
while (isspace(curr_line_->buffer[curr_line_->pos]) != 0 && !Eol()) {
++(curr_line_->pos);
}
size_t bound_right;
size_t bound_left;
while (!Eol()) {
bound_left = curr_line_->pos;
if (FindDelim(curr_line_->buffer[curr_line_->pos]) != Token::Type::ID_BAD) {
++(curr_line_->pos);
} else if (IsQuote(curr_line_->buffer[curr_line_->pos])) {
if (!LexString()) {
return;
}
} else {
while (!Eol() && FindDelim(curr_line_->buffer[curr_line_->pos]) == Token::Type::ID_BAD &&
isspace(curr_line_->buffer[curr_line_->pos]) == 0) {
++(curr_line_->pos);
size_t position = curr_line_->pos;
while (FindDelim(curr_line_->buffer[position]) == Token::Type::DEL_SQUARE_BRACKET_L ||
FindDelim(curr_line_->buffer[position]) == Token::Type::DEL_SQUARE_BRACKET_R) {
position++;
}
if (isspace(curr_line_->buffer[position]) == 0 && (position != curr_line_->end)) {
curr_line_->pos = position;
}
}
}
bound_right = curr_line_->pos;
LOG(DEBUG, ASSEMBLER) << "token identified (line " << lines_.size() << ", "
<< "token " << curr_line_->tokens.size() + 1 << "): "
<< std::string_view(&*(curr_line_->buffer.begin() + bound_left), bound_right - bound_left)
<< " ("
<< "type: " << TokenTypeWhat(LexGetType(bound_left, bound_right)) << ")";
curr_line_->tokens.emplace_back(bound_left, bound_right, LexGetType(bound_left, bound_right),
curr_line_->buffer);
while (isspace(curr_line_->buffer[curr_line_->pos]) != 0 && !Eol()) {
++(curr_line_->pos);
}
}
LOG(DEBUG, ASSEMBLER) << "all tokens identified (line " << lines_.size() << ")";
}
/*
* Ignore comments:
* 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]
*
* L1: mov v0, v1 # moving! L1: mov v0, v1 # moving!
* ^ => ^
* | |
* end end
*/
void Lexer::LexPreprocess()
{
LOG(DEBUG, ASSEMBLER) << "started removing comments (line " << lines_.size() << "): "
<< std::string_view(&*(curr_line_->buffer.begin() + curr_line_->pos),
curr_line_->end - curr_line_->pos);
// Searching for comment marker located outside of string literals.
bool inside_str_lit = !curr_line_->buffer.empty() && curr_line_->buffer[0] == '\"';
size_t cmt_pos = curr_line_->buffer.find_first_of("\"#", 0);
if (cmt_pos != std::string::npos) {
do {
if (cmt_pos != 0 && curr_line_->buffer[cmt_pos - 1] != '\\' && curr_line_->buffer[cmt_pos] == '\"') {
inside_str_lit = !inside_str_lit;
} else if (curr_line_->buffer[cmt_pos] == PARSE_COMMENT_MARKER && !inside_str_lit) {
break;
}
} while ((cmt_pos = curr_line_->buffer.find_first_of("\"#", cmt_pos + 1)) != std::string::npos);
}
if (cmt_pos != std::string::npos) {
curr_line_->end = cmt_pos;
}
while (curr_line_->end > curr_line_->pos && isspace(curr_line_->buffer[curr_line_->end - 1]) != 0) {
--(curr_line_->end);
}
LOG(DEBUG, ASSEMBLER) << "comments removed (line " << lines_.size() << "): "
<< std::string_view(&*(curr_line_->buffer.begin() + curr_line_->pos),
curr_line_->end - curr_line_->pos);
}
void Lexer::SkipSpace()
{
while (!Eol() && isspace(curr_line_->buffer[curr_line_->pos]) != 0) {
++(curr_line_->pos);
}
}
void Lexer::AnalyzeLine()
{
LexPreprocess();
SkipSpace();
LexTokens();
}
/*-------------------------------*/
} // namespace panda::pandasm

View File

@ -0,0 +1,128 @@
/**
* 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_LEXER_HPP
#define _PANDA_ASSEMBLER_LEXER_HPP
#include <array>
#include <iostream>
#include <string>
#include <string_view>
#include <unordered_map>
#include <vector>
#include "define.h"
#include "error.h"
#include "isa.h"
#include "utils/logger.h"
namespace panda::pandasm {
// NOLINTBEGIN(misc-non-private-member-variables-in-classes)
struct Token {
enum class Type {
ID_BAD = 0,
/* delimiters */
DEL_COMMA, /* , */
DEL_COLON, /* : */
DEL_BRACE_L, /* { */
DEL_BRACE_R, /* } */
DEL_BRACKET_L, /* ( */
DEL_BRACKET_R, /* ) */
DEL_SQUARE_BRACKET_L, /* [ */
DEL_SQUARE_BRACKET_R, /* ] */
DEL_GT, /* > */
DEL_LT, /* < */
DEL_EQ, /* = */
DEL_DOT, /* . */
ID, /* other */
ID_STRING, /* string literal */
OPERATION, /* special */
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define OPLIST(inst_code, name, optype, width, flags, dst_idx, src_idxs, prof_size) \
ID_OP_##inst_code, /* command type list */
PANDA_INSTRUCTION_LIST(OPLIST)
#undef OPLIST
KEYWORD, /* special */
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define KEYWORDS(name, inst_code) ID_##inst_code, /* keyword type List */
KEYWORDS_LIST(KEYWORDS)
#undef KEYWORDS
};
std::string whole_line;
size_t bound_left; /* right and left bounds of tokens */
size_t bound_right;
Type type;
Token() : Token(0, 0, Type::ID_BAD, "") {}
Token(size_t b_l, size_t b_r, Type t, std::string beg_of_line)
: whole_line(std::move(beg_of_line)), bound_left(b_l), bound_right(b_r), type(t)
{
}
};
using Tokens = std::pair<std::vector<Token>, Error>;
using TokenSet = const std::vector<std::vector<Token>>;
struct Line {
std::vector<Token> tokens;
std::string buffer; /* Raw line, as read from the file */
size_t pos {0}; /* current line position */
size_t end;
explicit Line(std::string str) : buffer(std::move(str)), end(buffer.size()) {}
};
// NOLINTEND(misc-non-private-member-variables-in-classes)
class Lexer {
public:
PANDA_PUBLIC_API Lexer();
PANDA_PUBLIC_API ~Lexer();
NO_MOVE_SEMANTIC(Lexer);
NO_COPY_SEMANTIC(Lexer);
/*
* The main function of Tokenizing, which takes a string.
* Returns a vector of tokens.
*/
PANDA_PUBLIC_API Tokens TokenizeString(const std::string &source_str);
private:
std::vector<Line> lines_;
Line *curr_line_ {nullptr};
Error err_;
bool Eol() const; /* End of line */
bool LexString();
void LexTokens();
void LexPreprocess();
void SkipSpace();
void AnalyzeLine();
Token::Type LexGetType(size_t beg, size_t end) const;
};
/*
* Returns a string representation of a token type.
*/
std::string_view TokenTypeWhat(Token::Type t);
} // namespace panda::pandasm
#endif // !_PANDA_ASSEMBLER_LEXER_HPP

View File

@ -0,0 +1,93 @@
/**
* Copyright (c) 2021-2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _PANDA_ASSEMBLER_MANGLING_H
#define _PANDA_ASSEMBLER_MANGLING_H
#include "assembly-function.h"
#include <string>
#include <vector>
namespace panda::pandasm {
constexpr const std::string_view MANGLE_BEGIN = ":";
constexpr const std::string_view MANGLE_SEPARATOR = ";";
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)
{
auto iter = name.find_first_of(MANGLE_BEGIN);
if (iter != std::string::npos) {
return name.substr(0, name.find_first_of(MANGLE_BEGIN));
}
return name;
}
inline std::string MangleFunctionName(const std::string &name, const std::vector<pandasm::Function::Parameter> &params,
const pandasm::Type &return_type)
{
std::string mangle_name {name};
mangle_name += MANGLE_BEGIN;
for (const auto &p : params) {
mangle_name += p.type.GetName() + std::string(MANGLE_SEPARATOR);
}
mangle_name += return_type.GetName() + std::string(MANGLE_SEPARATOR);
return mangle_name;
}
inline std::string MangleFieldName(const std::string &name, const pandasm::Type &type)
{
std::string mangle_name {name};
mangle_name += MANGLE_BEGIN;
mangle_name += type.GetName() + std::string(MANGLE_SEPARATOR);
return mangle_name;
}
inline std::string GetFunctionSignatureFromName(std::string name,
const std::vector<pandasm::Function::Parameter> &params)
{
name += SIGNATURE_BEGIN;
name += SIGNATURE_TYPES_SEPARATOR_L;
bool first = true;
for (const auto &p : params) {
name += (first) ? "" : SIGNATURE_TYPES_SEPARATOR;
first = false;
name += p.type.GetPandasmName();
}
name += SIGNATURE_TYPES_SEPARATOR_R;
return name;
}
inline std::string GetFunctionNameFromSignature(const std::string &sig)
{
return DeMangleName(sig);
}
inline bool IsSignatureOrMangled(const std::string &str)
{
return str.find(SIGNATURE_BEGIN) != std::string::npos;
}
} // namespace panda::pandasm
#endif // !_PANDA_ASSEMBLER_MANGLING_HPP

View File

@ -0,0 +1,518 @@
/**
* 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 "meta.h"
#include <cstdlib>
#include <algorithm>
#include <limits>
#include "utils/expected.h"
namespace panda::pandasm {
std::optional<Metadata::Error> Metadata::ValidateSize(std::string_view value) const
{
constexpr size_t SIZE = 10;
if (!std::all_of(value.cbegin(), value.cend(), ::isdigit)) {
return Error("Unsigned integer value expected", Error::Type::INVALID_VALUE);
}
strtoul(value.data(), nullptr, SIZE);
if (errno == ERANGE) {
return Error("Value is out of range", Error::Type::INVALID_VALUE);
}
return {};
}
bool ItemMetadata::IsForeign() const
{
return GetAttribute("external");
}
static panda::pandasm::Value::Type GetType(std::string_view value)
{
using VType = panda::pandasm::Value::Type;
static std::unordered_map<std::string_view, VType> types {
{"u1", VType::U1}, {"i8", VType::I8}, {"u8", VType::U8},
{"i16", VType::I16}, {"u16", VType::U16}, {"i32", VType::I32},
{"u32", VType::U32}, {"i64", VType::I64}, {"u64", VType::U64},
{"f32", VType::F32}, {"f64", VType::F64}, {"string", VType::STRING},
{"record", VType::RECORD}, {"enum", VType::ENUM}, {"annotation", VType::ANNOTATION},
{"array", VType::ARRAY}, {"method", VType::METHOD}};
return types[value];
}
template <class T>
static T ConvertFromString(std::string_view value, char **end)
{
static_assert(std::is_integral_v<T>, "T must be integral type");
constexpr T MIN = std::numeric_limits<T>::min();
constexpr T MAX = std::numeric_limits<T>::max();
if constexpr (std::is_signed_v<T>) {
auto v = ConvertFromString<int64_t>(value, end);
if (v < MIN || v > MAX) {
errno = ERANGE;
}
return static_cast<T>(v);
}
if constexpr (!std::is_signed_v<T>) {
auto v = ConvertFromString<uint64_t>(value, end);
if (v < MIN || v > MAX) {
errno = ERANGE;
}
return static_cast<T>(v);
}
}
template <>
int64_t ConvertFromString(std::string_view value, char **end)
{
return static_cast<int64_t>(strtoll(value.data(), end, 0));
}
template <>
uint64_t ConvertFromString(std::string_view value, char **end)
{
return static_cast<uint64_t>(strtoull(value.data(), end, 0));
}
template <>
float ConvertFromString(std::string_view value, char **end)
{
return strtof(value.data(), end);
}
template <>
double ConvertFromString(std::string_view value, char **end)
{
return strtod(value.data(), end);
}
template <class T>
static Expected<T, Metadata::Error> ConvertFromString(std::string_view value)
{
static_assert(std::is_arithmetic_v<T>, "T must be arithmetic type");
char *end = nullptr;
auto v = ConvertFromString<T>(value, &end);
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
if (end != value.data() + value.length()) {
return Unexpected(Metadata::Error("Excepted integer literal", Metadata::Error::Type::INVALID_VALUE));
}
if (errno == ERANGE) {
errno = 0;
return Unexpected(Metadata::Error("Value is out of range", Metadata::Error::Type::INVALID_VALUE));
}
return static_cast<T>(v);
}
template <Value::Type TYPE, class T = ValueTypeHelperT<TYPE>>
static Expected<ScalarValue, Metadata::Error> CreatePrimitiveValue(std::string_view value,
T max_value = std::numeric_limits<T>::max())
{
auto res = ConvertFromString<T>(value);
if (!res) {
return Unexpected(res.Error());
}
auto converted = res.Value();
if (converted > max_value) {
return Unexpected(Metadata::Error("Value is out of range", Metadata::Error::Type::INVALID_VALUE));
}
return ScalarValue::Create<TYPE>(converted);
}
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 = {})
{
switch (type) {
case Value::Type::U1: {
return CreatePrimitiveValue<Value::Type::U1>(value, 1);
}
case Value::Type::I8: {
return CreatePrimitiveValue<Value::Type::I8>(value);
}
case Value::Type::U8: {
return CreatePrimitiveValue<Value::Type::U8>(value);
}
case Value::Type::I16: {
return CreatePrimitiveValue<Value::Type::I16>(value);
}
case Value::Type::U16: {
return CreatePrimitiveValue<Value::Type::U16>(value);
}
case Value::Type::I32: {
return CreatePrimitiveValue<Value::Type::I32>(value);
}
case Value::Type::U32: {
return CreatePrimitiveValue<Value::Type::U32>(value);
}
case Value::Type::I64: {
return CreatePrimitiveValue<Value::Type::I64>(value);
}
case Value::Type::U64: {
return CreatePrimitiveValue<Value::Type::U64>(value);
}
case Value::Type::F32: {
return CreatePrimitiveValue<Value::Type::F32>(value);
}
case Value::Type::F64: {
return CreatePrimitiveValue<Value::Type::F64>(value);
}
case Value::Type::STRING: {
return ScalarValue::Create<Value::Type::STRING>(value);
}
case Value::Type::RECORD: {
return ScalarValue::Create<Value::Type::RECORD>(Type::FromName(value));
}
case Value::Type::METHOD: {
return ScalarValue::Create<Value::Type::METHOD>(value);
}
case Value::Type::ENUM: {
return ScalarValue::Create<Value::Type::ENUM>(value);
}
case Value::Type::ANNOTATION: {
auto it = annotation_id_map.find(std::string(value));
if (it == annotation_id_map.cend()) {
return Unexpected(Metadata::Error("Unknown annotation id", Metadata::Error::Type::INVALID_VALUE));
}
auto annotation_value = it->second.get();
return ScalarValue::Create<Value::Type::ANNOTATION>(*annotation_value);
}
default: {
break;
}
}
UNREACHABLE();
}
std::optional<Metadata::Error> AnnotationMetadata::AnnotationElementBuilder::AddValue(
std::string_view value, const std::unordered_map<std::string, std::unique_ptr<AnnotationData>> &annotation_id_map)
{
ASSERT(type_.has_value());
auto type = type_.value();
if (type == Value::Type::ARRAY) {
ASSERT(component_type_.has_value());
type = component_type_.value();
}
auto res = CreateValue(type, value, annotation_id_map);
if (!res) {
return res.Error();
}
values_.push_back(res.Value());
return {};
}
std::optional<Metadata::Error> AnnotationMetadata::Store(std::string_view attribute)
{
if (IsParseAnnotationElement() && !annotation_element_builder_.IsCompleted()) {
return Error(std::string("Unexpected attribute '").append(attribute) +
"'. Annotation element isn't completely defined",
Error::Type::UNEXPECTED_ATTRIBUTE);
}
if (IsParseAnnotation()) {
ResetAnnotationBuilder();
}
return Metadata::Store(attribute);
}
std::optional<Metadata::Error> AnnotationMetadata::MeetExpRecordAttribute(std::string_view attribute,
std::string_view value)
{
if (IsParseAnnotationElement() && !annotation_element_builder_.IsCompleted()) {
return Error(std::string("Unexpected attribute '").append(attribute) +
"'. Annotation element isn't completely defined",
Error::Type::UNEXPECTED_ATTRIBUTE);
}
InitializeAnnotationBuilder(value);
return {};
}
std::optional<Metadata::Error> AnnotationMetadata::MeetExpIdAttribute(std::string_view attribute,
std::string_view value)
{
if (!IsParseAnnotation() || IsParseAnnotationElement()) {
return Error(std::string("Unexpected attribute '").append(attribute) +
"'. Annotation record attribute must be defined first",
Error::Type::UNEXPECTED_ATTRIBUTE);
}
if (annotation_builder_.HasId()) {
return Error(std::string("Unexpected attribute '").append(attribute) +
"'. Annotation id attribute already defined",
Error::Type::UNEXPECTED_ATTRIBUTE);
}
annotation_builder_.SetId(value);
return {};
}
std::optional<Metadata::Error> AnnotationMetadata::MeetExpElementNameAttribute(std::string_view attribute,
std::string_view value)
{
if (!IsParseAnnotation()) {
return Error(std::string("Unexpected attribute '").append(attribute) +
"'. Annotation record attribute must be defined first",
Error::Type::UNEXPECTED_ATTRIBUTE);
}
if (IsParseAnnotationElement() && !annotation_element_builder_.IsCompleted()) {
return Error(std::string("Unexpected attribute '").append(attribute) +
"'. Previous annotation element isn't defined completely",
Error::Type::UNEXPECTED_ATTRIBUTE);
}
InitializeAnnotationElementBuilder(value);
return {};
}
std::optional<Metadata::Error> AnnotationMetadata::MeetExpElementTypeAttribute(std::string_view attribute,
std::string_view value)
{
if (!IsParseAnnotationElement()) {
return Error(std::string("Unexpected attribute '").append(attribute) +
"'. Annotation element name attribute must be defined first",
Error::Type::UNEXPECTED_ATTRIBUTE);
}
if (annotation_element_builder_.IsTypeSet()) {
return Error(std::string("Unexpected attribute '").append(attribute) +
"'. Annotation element type attribute already defined",
Error::Type::UNEXPECTED_ATTRIBUTE);
}
annotation_element_builder_.SetType(GetType(value));
return {};
}
std::optional<Metadata::Error> AnnotationMetadata::MeetExpElementArrayComponentTypeAttribute(std::string_view attribute,
std::string_view value)
{
if (!IsParseAnnotationElement()) {
return Error(std::string("Unexpected attribute '").append(attribute) +
"'. Annotation element name attribute must be defined first",
Error::Type::UNEXPECTED_ATTRIBUTE);
}
if (!annotation_element_builder_.IsArray()) {
return Error(std::string("Unexpected attribute '").append(attribute) + "'. Annotation element type isn't array",
Error::Type::UNEXPECTED_ATTRIBUTE);
}
if (annotation_element_builder_.IsComponentTypeSet()) {
return Error(std::string("Unexpected attribute '").append(attribute) +
"'. Annotation element array component type attribute already defined",
Error::Type::UNEXPECTED_ATTRIBUTE);
}
annotation_element_builder_.SetComponentType(GetType(value));
return {};
}
std::optional<Metadata::Error> AnnotationMetadata::MeetExpElementValueAttribute(std::string_view attribute,
std::string_view value)
{
if (!IsParseAnnotationElement()) {
return Error(std::string("Unexpected attribute '").append(attribute) +
"'. Annotation element name attribute must be defined first",
Error::Type::UNEXPECTED_ATTRIBUTE);
}
if (!annotation_element_builder_.IsTypeSet()) {
return Error(std::string("Unexpected attribute '").append(attribute) +
"'. Annotation element type attribute isn't defined",
Error::Type::UNEXPECTED_ATTRIBUTE);
}
if (annotation_element_builder_.IsArray() && !annotation_element_builder_.IsComponentTypeSet()) {
return Error(std::string("Unexpected attribute '").append(attribute) +
"'. Annotation element array component type attribute isn't defined",
Error::Type::UNEXPECTED_ATTRIBUTE);
}
if (!annotation_element_builder_.IsArray() && annotation_element_builder_.IsCompleted()) {
return Error(std::string("Unexpected attribute '").append(attribute) +
"'. Annotation element is completely defined",
Error::Type::UNEXPECTED_ATTRIBUTE);
}
return annotation_element_builder_.AddValue(value, id_map_);
}
std::optional<Metadata::Error> AnnotationMetadata::StoreValue(std::string_view attribute, std::string_view value)
{
auto err = Metadata::StoreValue(attribute, value);
if (err) {
return err;
}
if (IsAnnotationRecordAttribute(attribute)) {
return MeetExpRecordAttribute(attribute, value);
}
if (IsAnnotationIdAttribute(attribute)) {
return MeetExpIdAttribute(attribute, value);
}
if (IsAnnotationElementNameAttribute(attribute)) {
return MeetExpElementNameAttribute(attribute, value);
}
if (IsAnnotationElementTypeAttribute(attribute)) {
return MeetExpElementTypeAttribute(attribute, value);
}
if (IsAnnotationElementArrayComponentTypeAttribute(attribute)) {
return MeetExpElementArrayComponentTypeAttribute(attribute, value);
}
if (IsAnnotationElementValueAttribute(attribute)) {
return MeetExpElementValueAttribute(attribute, value);
}
if (IsParseAnnotationElement() && !annotation_element_builder_.IsCompleted()) {
return Error(std::string("Unexpected attribute '").append(attribute) +
"'. Annotation element isn't completely defined",
Error::Type::UNEXPECTED_ATTRIBUTE);
}
if (IsParseAnnotation()) {
ResetAnnotationBuilder();
}
return {};
}
std::optional<Metadata::Error> AnnotationMetadata::ValidateData()
{
if (IsParseAnnotationElement() && !annotation_element_builder_.IsCompleted()) {
return Error("Annotation element isn't completely defined", Error::Type::MISSING_ATTRIBUTE);
}
if (IsParseAnnotation()) {
ResetAnnotationBuilder();
}
return Metadata::ValidateData();
}
std::string RecordMetadata::GetBase() const
{
auto base = GetAttributeValue("extends");
if (base) {
return base.value();
}
return "";
}
std::vector<std::string> RecordMetadata::GetInterfaces() const
{
return {};
}
bool RecordMetadata::IsAnnotation() const
{
return false;
}
bool RecordMetadata::IsRuntimeAnnotation() const
{
return false;
}
bool RecordMetadata::IsTypeAnnotation() const
{
return false;
}
bool RecordMetadata::IsRuntimeTypeAnnotation() const
{
return false;
}
bool FunctionMetadata::HasImplementation() const
{
return ((ACC_ABSTRACT & GetAccessFlags()) == 0) && ((ACC_NATIVE & GetAccessFlags()) == 0);
}
bool FunctionMetadata::IsCtor() const
{
return GetAttribute("ctor");
}
bool FunctionMetadata::IsCctor() const
{
return GetAttribute("cctor");
}
std::optional<Metadata::Error> FieldMetadata::StoreValue(std::string_view attribute, std::string_view value)
{
auto err = ItemMetadata::StoreValue(attribute, value);
if (err) {
return err;
}
if (IsValueAttribute(attribute)) {
Value::Type value_type;
if (!field_type_.IsObject()) {
value_type = GetType(field_type_.GetName());
} else {
value_type = Value::Type::STRING;
}
auto res = CreateValue(value_type, value);
if (!res) {
return res.Error();
}
value_ = res.Value();
}
return {};
}
#include <meta_gen.h>
} // namespace panda::pandasm

View File

@ -0,0 +1,596 @@
/**
* 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_META_H_
#define PANDA_ASSEMBLER_META_H_
#include <memory>
#include <stack>
#include <tuple>
#include <unordered_map>
#include <unordered_set>
#include <variant>
#include <vector>
#include "annotation.h"
#include "modifiers.h"
#include "assembly-type.h"
namespace panda::pandasm {
class Metadata {
public:
class Error {
public:
enum class Type {
INVALID_VALUE,
MISSING_ATTRIBUTE,
MISSING_VALUE,
MULTIPLE_ATTRIBUTE,
UNEXPECTED_ATTRIBUTE,
UNEXPECTED_VALUE,
UNKNOWN_ATTRIBUTE
};
Error(std::string msg, Type type) : msg_(std::move(msg)), type_(type) {}
~Error() = default;
DEFAULT_MOVE_SEMANTIC(Error);
DEFAULT_COPY_SEMANTIC(Error);
std::string GetMessage() const
{
return msg_;
}
Type GetType() const
{
return type_;
}
private:
std::string msg_;
Type type_;
};
Metadata() = default;
virtual ~Metadata() = default;
std::optional<Error> SetAttribute(std::string_view attribute)
{
auto err = Validate(attribute);
if (err) {
return err;
}
SetFlags(attribute);
return Store(attribute);
}
void RemoveAttribute(const std::string &attribute)
{
RemoveFlags(attribute);
set_attributes_.erase(attribute);
}
bool GetAttribute(const std::string &attribute) const
{
return set_attributes_.find(attribute) != set_attributes_.cend();
}
std::optional<Error> SetAttributeValue(std::string_view attribute, std::string_view value)
{
auto err = Validate(attribute, value);
if (err) {
return err;
}
SetFlags(attribute, value);
return StoreValue(attribute, value);
}
std::vector<std::string> GetAttributeValues(const std::string &attribute) const
{
auto it = attributes_.find(attribute);
if (it == attributes_.cend()) {
return {};
}
return it->second;
}
std::optional<std::string> GetAttributeValue(const std::string &attribute) const
{
auto values = GetAttributeValues(attribute);
if (!values.empty()) {
return values.front();
}
return {};
}
const std::unordered_set<std::string> &GetBoolAttributes() const
{
return set_attributes_;
}
const std::unordered_map<std::string, std::vector<std::string>> &GetAttributes() const
{
return attributes_;
}
virtual std::optional<Error> ValidateData()
{
return {};
}
DEFAULT_COPY_SEMANTIC(Metadata);
DEFAULT_MOVE_SEMANTIC(Metadata);
protected:
virtual std::optional<Error> Validate(std::string_view attribute) const = 0;
virtual std::optional<Error> Validate(std::string_view attribute, std::string_view value) const = 0;
virtual std::optional<Error> StoreValue(std::string_view attribute, std::string_view value)
{
std::string key(attribute);
auto it = attributes_.find(key);
if (it == attributes_.cend()) {
std::tie(it, std::ignore) = attributes_.try_emplace(key);
}
it->second.emplace_back(value);
return {};
}
virtual std::optional<Error> Store(std::string_view attribute)
{
set_attributes_.emplace(attribute);
return {};
}
virtual void SetFlags(std::string_view attribute) = 0;
virtual void SetFlags(std::string_view attribute, std::string_view value) = 0;
virtual void RemoveFlags(std::string_view attribute) = 0;
virtual void RemoveFlags(std::string_view attribute, std::string_view value) = 0;
bool HasAttribute(std::string_view attribute) const
{
std::string key(attribute);
return GetAttribute(key) || GetAttributeValue(key);
}
std::optional<Error> ValidateSize(std::string_view value) const;
private:
std::unordered_set<std::string> set_attributes_;
std::unordered_map<std::string, std::vector<std::string>> attributes_;
};
class AnnotationMetadata : public Metadata {
public:
const std::vector<AnnotationData> &GetAnnotations() const
{
return annotations_;
}
void SetAnnotations(std::vector<AnnotationData> &&annotations)
{
annotations_ = std::forward<std::vector<AnnotationData>>(annotations);
}
void AddAnnotations(const std::vector<AnnotationData> &annotations)
{
annotations_.insert(annotations_.end(), annotations.begin(), annotations.end());
}
std::optional<Error> ValidateData() override;
protected:
std::optional<Error> Store(std::string_view attribute) override;
std::optional<Error> StoreValue(std::string_view attribute, std::string_view value) override;
virtual bool IsAnnotationRecordAttribute([[maybe_unused]] std::string_view attribute) const
{
return false;
}
virtual bool IsAnnotationIdAttribute([[maybe_unused]] std::string_view attribute) const
{
return false;
}
virtual bool IsAnnotationElementTypeAttribute([[maybe_unused]] std::string_view attribute) const
{
return false;
}
virtual bool IsAnnotationElementArrayComponentTypeAttribute([[maybe_unused]] std::string_view attribute) const
{
return false;
}
virtual bool IsAnnotationElementNameAttribute([[maybe_unused]] std::string_view attribute) const
{
return false;
}
virtual bool IsAnnotationElementValueAttribute([[maybe_unused]] std::string_view attribute) const
{
return false;
}
private:
class AnnotationElementBuilder {
public:
void Initialize(std::string_view name)
{
name_ = name;
is_initialized_ = true;
}
void Reset()
{
name_.clear();
values_.clear();
type_ = {};
component_type_ = {};
is_initialized_ = false;
}
void SetType(Value::Type type)
{
type_ = type;
}
void SetComponentType(Value::Type type)
{
ASSERT(type != Value::Type::ARRAY);
component_type_ = type;
}
std::optional<Error> AddValue(
std::string_view value,
const std::unordered_map<std::string, std::unique_ptr<AnnotationData>> &annotation_id_map);
AnnotationElement CreateAnnotationElement()
{
if (IsArray()) {
return AnnotationElement(name_,
std::make_unique<ArrayValue>(component_type_.value(), std::move(values_)));
}
return AnnotationElement(name_, std::make_unique<ScalarValue>(values_.front()));
}
bool IsArray() const
{
return type_.value() == Value::Type::ARRAY;
}
bool IsTypeSet() const
{
return type_.has_value();
}
bool IsComponentTypeSet() const
{
return component_type_.has_value();
}
bool IsInitialized() const
{
return is_initialized_;
}
bool IsCompleted() const
{
if (!IsTypeSet()) {
return false;
}
if (IsArray() && !IsComponentTypeSet()) {
return false;
}
if (!IsArray() && values_.empty()) {
return false;
}
return true;
}
private:
bool is_initialized_ {false};
std::string name_;
std::optional<Value::Type> type_;
std::optional<Value::Type> component_type_;
std::vector<ScalarValue> values_;
};
class AnnotationBuilder {
public:
void Initialize(std::string_view name)
{
name_ = name;
is_initialized_ = true;
}
void Reset()
{
name_.clear();
elements_.clear();
id_ = {};
is_initialized_ = false;
}
void SetId(std::string_view id)
{
id_ = id;
}
std::string GetId() const
{
ASSERT(HasId());
return id_.value();
}
void AddElement(AnnotationElement &&element)
{
elements_.push_back(std::forward<AnnotationElement>(element));
}
std::unique_ptr<AnnotationData> CreateAnnotationData()
{
return std::make_unique<AnnotationData>(name_, std::move(elements_));
};
void AddAnnnotationDataToVector(std::vector<AnnotationData> *annotations)
{
annotations->emplace_back(name_, std::move(elements_));
}
bool HasId() const
{
return id_.has_value();
}
bool IsInitialized() const
{
return is_initialized_;
}
private:
std::string name_;
std::optional<std::string> id_;
std::vector<AnnotationElement> elements_;
bool is_initialized_ {false};
};
std::optional<Metadata::Error> MeetExpRecordAttribute(std::string_view attribute, std::string_view value);
std::optional<Metadata::Error> MeetExpIdAttribute(std::string_view attribute, std::string_view value);
std::optional<Metadata::Error> MeetExpElementNameAttribute(std::string_view attribute, std::string_view value);
std::optional<Metadata::Error> MeetExpElementTypeAttribute(std::string_view attribute, std::string_view value);
std::optional<Metadata::Error> MeetExpElementArrayComponentTypeAttribute(std::string_view attribute,
std::string_view value);
std::optional<Metadata::Error> MeetExpElementValueAttribute(std::string_view attribute, std::string_view value);
void InitializeAnnotationBuilder(std::string_view name)
{
if (IsParseAnnotation()) {
ResetAnnotationBuilder();
}
annotation_builder_.Initialize(name);
}
void ResetAnnotationBuilder()
{
ASSERT(IsParseAnnotation());
if (IsParseAnnotationElement() && annotation_element_builder_.IsCompleted()) {
ResetAnnotationElementBuilder();
}
if (annotation_builder_.HasId()) {
id_map_.insert({annotation_builder_.GetId(), annotation_builder_.CreateAnnotationData()});
} else {
annotation_builder_.AddAnnnotationDataToVector(&annotations_);
}
annotation_builder_.Reset();
}
bool IsParseAnnotation() const
{
return annotation_builder_.IsInitialized();
}
void InitializeAnnotationElementBuilder(std::string_view name)
{
if (IsParseAnnotationElement() && annotation_element_builder_.IsCompleted()) {
ResetAnnotationElementBuilder();
}
annotation_element_builder_.Initialize(name);
}
void ResetAnnotationElementBuilder()
{
ASSERT(IsParseAnnotationElement());
ASSERT(annotation_element_builder_.IsCompleted());
annotation_builder_.AddElement(annotation_element_builder_.CreateAnnotationElement());
annotation_element_builder_.Reset();
}
bool IsParseAnnotationElement() const
{
return annotation_element_builder_.IsInitialized();
}
AnnotationBuilder annotation_builder_;
AnnotationElementBuilder annotation_element_builder_;
std::vector<AnnotationData> annotations_;
std::unordered_map<std::string, std::unique_ptr<AnnotationData>> id_map_;
};
class ItemMetadata : public AnnotationMetadata {
public:
uint32_t GetAccessFlags() const
{
return access_flags_;
}
void SetAccessFlags(uint32_t access_flags)
{
access_flags_ = access_flags;
}
PANDA_PUBLIC_API bool IsForeign() const;
private:
uint32_t access_flags_ {0};
};
class RecordMetadata : public ItemMetadata {
public:
virtual std::string GetBase() const;
virtual std::vector<std::string> GetInterfaces() const;
virtual bool IsAnnotation() const;
virtual bool IsRuntimeAnnotation() const;
virtual bool IsTypeAnnotation() const;
virtual bool IsRuntimeTypeAnnotation() const;
protected:
std::optional<Error> Validate(std::string_view attribute) const override;
std::optional<Error> Validate(std::string_view attribute, std::string_view value) const override;
void SetFlags(std::string_view attribute) override;
void SetFlags(std::string_view attribute, std::string_view value) override;
void RemoveFlags(std::string_view attribute) override;
void RemoveFlags(std::string_view attribute, std::string_view value) override;
};
class FieldMetadata : public ItemMetadata {
public:
void SetFieldType(const Type &type)
{
field_type_ = type;
}
Type GetFieldType() const
{
return field_type_;
}
void SetValue(const ScalarValue &value)
{
value_ = value;
}
std::optional<ScalarValue> GetValue() const
{
return value_;
}
protected:
std::optional<Error> StoreValue(std::string_view attribute, std::string_view value) override;
std::optional<Error> Validate(std::string_view attribute) const override;
std::optional<Error> Validate(std::string_view attribute, std::string_view value) const override;
void SetFlags(std::string_view attribute) override;
void SetFlags(std::string_view attribute, std::string_view value) override;
void RemoveFlags(std::string_view attribute) override;
void RemoveFlags(std::string_view attribute, std::string_view value) override;
virtual bool IsValueAttribute(std::string_view attribute)
{
return attribute == "value";
}
private:
Type field_type_;
std::optional<ScalarValue> value_;
};
class FunctionMetadata : public ItemMetadata {
public:
virtual bool HasImplementation() const;
virtual bool IsCtor() const;
virtual bool IsCctor() const;
protected:
std::optional<Error> Validate(std::string_view attribute) const override;
std::optional<Error> Validate(std::string_view attribute, std::string_view value) const override;
void SetFlags(std::string_view attribute) override;
void SetFlags(std::string_view attribute, std::string_view value) override;
void RemoveFlags(std::string_view attribute) override;
void RemoveFlags(std::string_view attribute, std::string_view value) override;
};
class ParamMetadata : public AnnotationMetadata {
protected:
std::optional<Error> Validate(std::string_view attribute) const override;
std::optional<Error> Validate(std::string_view attribute, std::string_view value) const override;
void SetFlags(std::string_view attribute) override;
void SetFlags(std::string_view attribute, std::string_view value) override;
void RemoveFlags(std::string_view attribute) override;
void RemoveFlags(std::string_view attribute, std::string_view value) override;
};
} // namespace panda::pandasm
#endif // PANDA_ASSEMBLER_META_H_

View File

@ -0,0 +1,115 @@
# 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.
attributes:
- name: access.field
type: enum
multiple: false
values:
- value: public
flags: [ACC_PUBLIC]
- value: protected
flags: [ACC_PROTECTED]
- value: private
flags: [ACC_PRIVATE]
applicable_to:
- field
- name: access.function
type: enum
multiple: false
values:
- value: public
flags: [ACC_PUBLIC]
- value: protected
flags: [ACC_PROTECTED]
- value: private
flags: [ACC_PRIVATE]
applicable_to:
- function
- name: access.record
type: enum
multiple: false
values:
- value: public
flags: [ACC_PUBLIC]
- value: protected
flags: [ACC_PROTECTED]
- value: private
flags: [ACC_PRIVATE]
applicable_to:
- record
- name: extends
type: record
multiple: false
applicable_to:
- record
- name: external
type: bool
applicable_to:
- record
- field
- function
- name: native
type: bool
flags: [ACC_NATIVE]
applicable_to:
- function
- name: noimpl
type: bool
flags: [ACC_ABSTRACT]
applicable_to:
- function
- name: static
type: bool
flags: [ACC_STATIC]
applicable_to:
- field
- function
- name: final
type: bool
flags: [ACC_FINAL]
applicable_to:
- record
- field
- function
- name: volatile
type: bool
flags: [ACC_VOLATILE]
multiple: false
applicable_to:
- field
- name: ctor
type: bool
applicable_to:
- function
- name: cctor
type: bool
applicable_to:
- function
- name: value
type: any
multiple: false
applicable_to:
- field

View File

@ -0,0 +1,289 @@
/**
* 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 <fstream>
#include <iomanip>
#include <iostream>
#include <map>
#include <sstream>
#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 "pandasm.h"
#include "utils/expected.h"
#include "utils/logger.h"
#include "utils/pandargs.h"
namespace panda::pandasm {
void PrintError(const panda::pandasm::Error &e, const std::string &msg)
{
std::stringstream sos;
std::cerr << msg << ": " << e.message << std::endl;
sos << " Line " << e.line_number << ", Column " << e.pos + 1 << ": ";
std::cerr << sos.str() << e.whole_line << std::endl;
std::cerr << std::setw(static_cast<int>(e.pos + sos.str().size()) + 1) << "^" << std::endl;
}
void PrintErrors(const panda::pandasm::ErrorList &warnings, const std::string &msg)
{
for (const auto &iter : warnings) {
PrintError(iter, msg);
}
}
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,
const panda::PandArg<bool> &version, std::ifstream &inputfile, int argc, const char **argv)
{
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;
}
if (verbose.GetValue()) {
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);
}
}
inputfile.open(input_file.GetValue(), std::ifstream::in);
if (!inputfile) {
std::cerr << "The input file does not exist." << std::endl;
return false;
}
return true;
}
bool Tokenize(panda::pandasm::Lexer &lexer, std::vector<std::vector<panda::pandasm::Token>> &tokens,
std::ifstream &inputfile)
{
std::string s;
while (getline(inputfile, s)) {
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");
return false;
}
tokens.push_back(q.first);
}
return true;
}
bool ParseProgram(panda::pandasm::Parser &parser, std::vector<std::vector<panda::pandasm::Token>> &tokens,
const panda::PandArg<std::string> &input_file,
panda::Expected<panda::pandasm::Program, panda::pandasm::Error> &res)
{
res = parser.Parse(tokens, input_file.GetValue());
if (!res) {
PrintError(res.Error(), "ERROR");
return false;
}
return true;
}
bool DumpProgramInJson(panda::pandasm::Program &program, const panda::PandArg<std::string> &scopes_file)
{
if (!scopes_file.GetValue().empty()) {
std::ofstream dump_file;
dump_file.open(scopes_file.GetValue());
if (!dump_file) {
std::cerr << "Cannot write scopes into the given file." << std::endl;
return false;
}
dump_file << program.JsonDump();
}
return true;
}
bool EmitProgramInBinary(panda::pandasm::Program &program, panda::PandArgParser &pa_parser,
const panda::PandArg<std::string> &output_file, panda::PandArg<bool> &optimize,
panda::PandArg<bool> &size_stat)
{
auto emit_debug_info = !optimize.GetValue();
std::map<std::string, size_t> stat;
std::map<std::string, size_t> *statp = size_stat.GetValue() ? &stat : nullptr;
panda::pandasm::AsmEmitter::PandaFileToPandaAsmMaps maps {};
panda::pandasm::AsmEmitter::PandaFileToPandaAsmMaps *mapsp = optimize.GetValue() ? &maps : nullptr;
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;
}
#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;
for (auto [name, size] : stat) {
std::cout << name << " section: " << size << std::endl;
total_size += size;
}
std::cout << "total: " << total_size << std::endl;
}
pa_parser.DisableTail();
return true;
}
bool BuildFiles(panda::pandasm::Program &program, panda::PandArgParser &pa_parser,
const panda::PandArg<std::string> &output_file, panda::PandArg<bool> &optimize,
panda::PandArg<bool> &size_stat, panda::PandArg<std::string> &scopes_file)
{
if (!DumpProgramInJson(program, scopes_file)) {
return false;
}
if (!EmitProgramInBinary(program, pa_parser, output_file, optimize, size_stat)) {
return false;
}
return true;
}
} // namespace panda::pandasm
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");
panda::PandArg<std::string> scopes_file("dump-scopes", "",
"(--dump-scopes FILENAME) Enable dump of scopes to file");
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");
panda::PandArgParser pa_parser;
pa_parser.Add(&verbose);
pa_parser.Add(&help);
pa_parser.Add(&log_file);
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, version, inputfile,
argc, argv)) {
return 1;
}
LOG(DEBUG, ASSEMBLER) << "Lexical analysis:";
panda::pandasm::Lexer lexer;
std::vector<std::vector<panda::pandasm::Token>> tokens;
if (!Tokenize(lexer, tokens, inputfile)) {
return 1;
}
LOG(DEBUG, ASSEMBLER) << "parsing:";
panda::pandasm::Parser parser;
panda::Expected<panda::pandasm::Program, panda::pandasm::Error> res;
if (!panda::pandasm::ParseProgram(parser, tokens, input_file, res)) {
return 1;
}
auto &program = res.Value();
auto w = parser.ShowWarnings();
if (!w.empty()) {
panda::pandasm::PrintErrors(w, "WARNING");
}
if (!panda::pandasm::BuildFiles(program, pa_parser, output_file, optimize, size_stat, scopes_file)) {
return 1;
}
return 0;
}

View File

@ -0,0 +1,51 @@
/**
* 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_PANDASM_H
#define PANDA_PANDASM_H
#include "utils/pandargs.h"
namespace panda::pandasm {
void PrintError(const panda::pandasm::Error &e, const std::string &msg);
void PrintErrors(const panda::pandasm::ErrorList &warnings, const std::string &msg);
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);
bool Tokenize(panda::pandasm::Lexer &lexer, std::vector<std::vector<panda::pandasm::Token>> &tokens,
std::ifstream &inputfile);
bool ParseProgram(panda::pandasm::Parser &parser, std::vector<std::vector<panda::pandasm::Token>> &tokens,
const panda::PandArg<std::string> &input_file,
panda::Expected<panda::pandasm::Program, panda::pandasm::Error> &res);
bool DumpProgramInJson(panda::pandasm::Program &program, const panda::PandArg<std::string> &scopes_file);
bool EmitProgramInBinary(panda::pandasm::Program &program, panda::PandArgParser &pa_parser,
const panda::PandArg<std::string> &output_file, panda::PandArg<bool> &optimize,
panda::PandArg<bool> &size_stat);
bool BuildFiles(panda::pandasm::Program &program, panda::PandArgParser &pa_parser,
const panda::PandArg<std::string> &output_file, panda::PandArg<bool> &optimize,
panda::PandArg<bool> &size_stat, panda::PandArg<std::string> &scopes_file);
} // namespace panda::pandasm
#endif // PANDA_PANDASM_H

View File

@ -0,0 +1,83 @@
# Copyright (c) 2021-2022 Huawei Device Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
.function i64 main(){
ldai 30000
sta v1
newarr v1, i64[]
sta.obj v0
call.short init, v0, v1
call.short bubblesort, v0, v0
lda v1
subi 1
ldarr.64 v0
return.64
}
.function void init(i64[] a0, i32 a1){
movi v2, 0
loop:
lda a1
jeqz init_exit
subi 1
sta a1
starr.64 a0, v2
inci v2, 1
lda v2
jmp loop
init_exit:
return.void
}
.function void bubblesort(i64[] a0){
lenarr a0
sta v1
outer:
ldai 1
sta v2
ldai 1
inner:
jge v1, inner_exit
sta v3
ldarr.64 a0
sta v4
lda v3
subi 1
sta v5
ldarr.64 a0
jlt v4, next
call swap, a0, v5, v3, v0
movi v2, 0
next:
lda v3
addi 1
jmp inner
inner_exit:
inci v1, -1
lda v2
jeqz outer
return.void
}
.function void swap(i64[] a0, i32 a1, i32 a2){
lda a1
ldarr.64 a0
sta v3
lda a2
ldarr.64 a0
starr.64 a0, a1
lda v3
starr.64 a0, a2
return.void
}

View File

@ -0,0 +1,37 @@
# 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.
.function i32 factorial (i32 a0) <> { #Calculation of factorial from a value in register a0
ldai.64 1
sta.64 v8
while_1:
lda.64 a0
movi.64 v0, 1
jle v0, endwhile_3
body_2:
mul v8, a0
sta.64 v8
lda.64 a0
subi 1
sta.64 a0
jmp while_1
endwhile_3:
lda.64 v8
return
}
.function i32 entry () <> {
movi.64 v0, 7
call.short factorial, v0
return
}

View File

@ -0,0 +1,52 @@
# 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.
.function i64 main(){
movi v1, 10
newarr v0, v1, i64[]
ldai 0
main_loop:
jge v1, main_ret
sta v2
call.short fibonacci, v2, v2
starr.64 v0, v2
lda v2
addi 1
jmp main_loop
main_ret:
lda v1
subi 1
ldarr.64 v0
return.64
}
.function i64 fibonacci(i64 a0){
lda a0
jnez non_zero
return.64
non_zero:
ldai 1
jne a0, non_one
return.64
non_one:
lda a0
subi 1
sta a0
subi 1
sta v1
call.short fibonacci, a0, a0
sta a0
call.short fibonacci, v1, a0
add2.64 a0
return.64
}

View File

@ -0,0 +1,62 @@
/*
* 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!
#include <initializer_list>
#include "assembly-ins.h"
namespace panda::pandasm {
% Panda::instructions.group_by(&:mnemonic).each do |mnemonic, group|
% insn = group.first
% signature = assembler_signature(group, insn.jump?)
% signature_str = signature.map { |o| "#{o.type} #{o.name}" }.join(', ')
// NOLINTNEXTLINE(readability-identifier-naming)
inline Ins Create_<%= insn.asm_token %>(<%= signature_str %>)
{
// NOLINTNEXTLINE(readability-identifier-naming)
Ins <%=insn.emitter_name%>_;
<%=group.first.emitter_name%>_.opcode = Opcode::<%= insn.asm_token %>;
% format = format_ops(insn.format).select { |o| o.name != 'prof' }
% format.each { |o| o.width = storage_width(o.width) }
% count_reg = 0
% format.each_with_index do |o, i|
% if o.name.start_with?('v')
% count_reg += 1
% end
% end
% if count_reg > 0 then
<%=group.first.emitter_name%>_.regs.reserve(<%= count_reg %>);
% end
% format.each_with_index do |o, i|
% if o.name.start_with?('imm')
% if insn.jump?
<%=group.first.emitter_name%>_.ids.push_back(label);
% else
<%=group.first.emitter_name%>_.imms.emplace_back(<%= o.name %>);
% end
% elsif o.name.start_with?('id')
<%=group.first.emitter_name%>_.ids.push_back(<%= o.name %>);
% else
<%=group.first.emitter_name%>_.regs.push_back(<%= o.name %>);
% end
% end
return <%=insn.emitter_name%>_;
}
% end
} // namespace panda::pandasm

View File

@ -0,0 +1,186 @@
/*
* 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 "assembly-ins.h"
#include "mangling.h"
namespace panda::pandasm {
// NOLINTBEGIN(readability-container-size-empty)
// NOLINTNEXTLINE(misc-definitions-in-headers,readability-function-size)
bool Ins::Emit(BytecodeEmitter& emitter, panda_file::MethodItem *method,
const std::unordered_map<std::string, panda_file::BaseMethodItem *> &methods,
const std::unordered_map<std::string, panda_file::BaseFieldItem *> &fields,
const std::unordered_map<std::string, panda_file::BaseClassItem *> &classes,
const std::unordered_map<std::string_view, panda_file::StringItem *> &strings,
const std::unordered_map<std::string, panda_file::LiteralArrayItem *> &literalarrays,
const std::unordered_map<std::string_view, panda::Label>& labels) const {
if (!IsValidToEmit()) {
std::cerr << "Invalid instruction: " << ToString() << std::endl;
LOG(FATAL, ASSEMBLER);
}
switch(opcode) {
% 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))]
%
% def type(insn)
% src_acc_ops = insn.acc_and_operands.select(&:src?)
% src_acc_ops.size != 0 ? src_acc_ops.first.type : 'none'
% end
%
% def op_size(insn)
% type(insn)[1..-1].to_i
% end
%
% def operands(insn, regs = "regs")
% ops = []
% nr = 0
% ni = 0
% insn.operands.each do |op|
% if op.reg?
% ops << "#{regs}[#{nr}]"
% nr += 1
% elsif op.imm?
% if insn.jump?
% ops << 'labels.find(ids[0])->second'
% else
% from_type, to_type = op_size(insn) == 64 ? ['double', 'int64_t'] : ['float', 'int32_t']
% if insn.float?
% ops << bit_cast("imms[#{ni}]", to_type, from_type)
% elsif type(insn).start_with? 'u' # can hold both float and integer literals
% ops << "std::holds_alternative<double>(imms[#{ni}]) ? #{bit_cast("imms[#{ni}]", to_type, from_type)} : std::get<int64_t>(imms[#{ni}])"
% else
% ops << "std::get<int64_t>(imms[#{ni}])"
% end
% end
% ni += 1
% elsif op.id?
% if insn.properties.include?('method_id')
% ops << 'methods.find(ids[0])->second->GetIndex(method)'
% elsif insn.properties.include?('field_id')
% ops << 'fields.find(ids[0])->second->GetIndex(method)'
% elsif insn.properties.include?('type_id')
% ops << 'classes.find(ids[0])->second->GetIndex(method)'
% elsif insn.properties.include?('string_id')
% ops << 'strings.find(ids[0])->second->GetOffset()'
% elsif insn.properties.include?('literalarray_id')
% ops << 'static_cast<uint32_t>(literalarrays.find(ids[0])->second->GetIndex())'
% else
% raise "Unexpected ID type"
% end
% else
% raise "Unexpected operand type"
% end
% end
% ops << "profile_id" if insn.profiled?
% if insn.properties.include?('short_long_range')
% ops << ops.shift
% end
% ops
% end
case Opcode::<%= insn.asm_token %>: {
% if insn.properties.include?('method_id')
if (ids.empty() || (methods.find(ids[0]) == methods.cend())) {
return false;
}
% elsif insn.properties.include?('field_id')
if (ids.empty() || (fields.find(ids[0]) == fields.cend())) {
return false;
}
% elsif insn.properties.include?('type_id')
if (ids.empty() || (classes.find(ids[0]) == classes.cend())) {
return false;
}
% elsif insn.properties.include?('string_id')
if (ids.empty() || (strings.find(ids[0]) == strings.cend())) {
return false;
}
% elsif insn.jump?
if (ids.empty() || (labels.find(ids[0]) == labels.cend())) {
return false;
}
% end
% regs_num = 0
% imms_num = 0
% insn.operands.each do |op|
% if op.reg?
% regs_num += 1
% elsif op.imm?
% imms_num += 1
% end
% end
% if insn.properties.include?('short_long_range') && !insn.range?
% if imms_num > 0
if (imms.size() < <%= imms_num %>) {
return false;
}
% end
% elsif insn.jump?
% if regs_num > 0
if (regs.size() < <%= regs_num %>) {
return false;
}
% end
% else
% if regs_num > 0 and imms_num > 0
if (regs.size() < <%= regs_num %> || imms.size() < <%= imms_num %>) {
return false;
}
% elsif regs_num > 0
if (regs.size() < <%= regs_num %>) {
return false;
}
% elsif imms_num > 0
if (imms.size() < <%= imms_num %>) {
return false;
}
% end
% end
% if insn.properties.include?('short_long_range') && !insn.mnemonic.include?('range')
auto registers = regs;
% 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 %>) {
registers.push_back(0);
}
emitter.<%= i.emitter_name %>(<%= operands(i, "registers").join(", ") %>);
break;
}
% end
% else
emitter.<%= insn.emitter_name %>(<%= operands(insn).join(", ") %>);
% end
break;
}
% end
default:
assert(0);
}
return true;
}
// NOLINTEND(readability-container-size-empty)
} // namespace panda::pandasm

View File

@ -0,0 +1,77 @@
/*
* 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 <iostream>
#include "assembly-ins.h"
namespace panda::pandasm {
// NOLINTNEXTLINE(readability-function-size)
std::string panda::pandasm::Ins::ToString(const std::string &endline, bool print_args /* = false */,
size_t first_arg_idx /* = 0*/) const {
std::string full_operation {};
std::string reg_case {};
if (this->set_label) {
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%>";
% 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
% if insn.profiled?
full_operation += " # [prof=";
full_operation += std::to_string(profile_id);
full_operation += ']';
% end
} break;
% end
% Panda::pseudo_instructions.each do |insn|
case panda::pandasm::Opcode::<%= insn.opcode %>: {
full_operation += "<%= insn.opcode %>";
full_operation += this->OperandsToString(print_args, first_arg_idx);
} break;
% end
case panda::pandasm::Opcode::INVALID: {
full_operation += "";
} break;
}
return full_operation + endline;
}
} // namespace panda::pandasm

View File

@ -0,0 +1,60 @@
/*
* 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.
*/
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define PANDA_INSTRUCTION_LIST(_) \
% Panda::instructions.group_by(&:mnemonic).each do |mnemonic, group|
% insn = group.first
% formats = group.map(&:format)
% pretty_format = insn.format.pretty.upcase.gsub(/[0-9]/, '')
% pretty_format.gsub!('IMM', 'ID') if insn.jump?
% pretty_format.gsub!('ID', 'TYPE') if insn.properties.include?('type_id')
%
% flags = ["InstFlags::NONE"]
% flags << "InstFlags::CALL" if insn.simple_call?
% flags << "InstFlags::JUMP" if insn.jump?
% flags << "InstFlags::COND" if insn.conditional?
% flags << "InstFlags::RETURN" if insn.return?
% 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|
% i.operands.select(&:reg?).map(&:width).max
% end.max
% max_width ||= 0
%
% profile_size = insn.profiled? ? Panda::profiles[insn.profile.name].size : 0
%
% regs = insn.operands.select(&:reg?)
% dst_idx = regs.index(&:dst?) || 'INVALID_REG_IDX'
% use_idxs = regs.size.times.select { |idx| regs[idx].src? } || []
% use_idxs += (['INVALID_REG_IDX'] * (max_number_of_src_regs - use_idxs.size))
% use_idxs = "(std::array<int, #{ max_number_of_src_regs }>{{#{use_idxs.join(', ')}}})"
%
_(<%= insn.asm_token %>, "<%= insn.mnemonic %>", <%= pretty_format %>, <%= max_width %>, <%= flags %>, <%= dst_idx %>, <%= use_idxs %>, <%= profile_size %>) \
% end
% Panda::pseudo_instructions.each do |insn|
% use_idxs = insn.use_idxs + ['INVALID_REG_IDX'] * (max_number_of_src_regs - insn.use_idxs.size)
% use_idxs = "(std::array<int, #{max_number_of_src_regs}>{{#{use_idxs.join(', ')}}})"
_(<%= insn.opcode %> , "<%= insn.opcode %>", NONE, 0, (<%= insn.flags.join(" | ") %>), <%= insn.dst_idx %>, <%= use_idxs %>, 0) \
% end

View File

@ -0,0 +1,50 @@
/*
* 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.
*/
// NOLINTBEGIN(readability-function-size, misc-definitions-in-headers)
% 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 {
<%= MetadataGen::validate_body(item_type, true).join("\n") %>
}
std::optional<Metadata::Error>
<%= MetadataGen::class_name(item_type) %>::Validate(<%= MetadataGen::validate_arg_list(item_type, false).join(', ') %>) const {
<%= MetadataGen::validate_body(item_type, false).join("\n") %>
}
% end
%
% Metadata::item_types.each do |item_type|
void <%= MetadataGen::class_name(item_type) %>::SetFlags(<%= MetadataGen::flags_arg_list(item_type, true).join(', ') %>) {
<%= MetadataGen::set_flags_body(item_type, true).join("\n") %>
}
void <%= MetadataGen::class_name(item_type) %>::SetFlags(<%= MetadataGen::flags_arg_list(item_type, false).join(', ') %>) {
<%= MetadataGen::set_flags_body(item_type, false).join("\n") %>
}
void <%= MetadataGen::class_name(item_type) %>::RemoveFlags(<%= MetadataGen::flags_arg_list(item_type, true).join(', ') %>) {
<%= MetadataGen::remove_flags_body(item_type, true).join("\n") %>
}
void <%= MetadataGen::class_name(item_type) %>::RemoveFlags(<%= MetadataGen::flags_arg_list(item_type, false).join(', ') %>) {
<%= MetadataGen::remove_flags_body(item_type, false).join("\n") %>
}
% end
// NOLINTEND(readability-function-size, misc-definitions-in-headers)

View File

@ -0,0 +1,143 @@
/*
* 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 "assembly-parser.h"
#include "utils/number-utils.h"
namespace panda::pandasm {
// NOLINTNEXTLINE(misc-definitions-in-headers,readability-function-size)
bool Parser::ParseOperands()
{
std::string_view p;
uint64_t number;
LOG(DEBUG, ASSEMBLER) << "operand search started (line " << line_stric_ << "): " << context_.tokens[0].whole_line;
switch(context_.WaitFor())
{
% Panda::instructions.group_by(&:mnemonic).each do |mnemonic, group|
% insn = group.first
% formats = group.map(&:format)
% operands = insn.operands
% properties = insn.properties
% verification = insn.verification
case Token::Type::ID_OP_<%= insn.asm_token%>: {
% if insn.return? && !insn.properties.include?('dynamic')
% if insn.return_obj?
if (!curr_func_->return_type.IsObject()) {
% elsif insn.return32?
if (!curr_func_->return_type.IsPrim32()) {
% elsif insn.return64?
if (!curr_func_->return_type.IsPrim64()) {
% elsif insn.return_void?
if (!curr_func_->return_type.IsVoid()) {
% end
GetWarning("Unexpected return type. Look at the return type in the function definition: " +
curr_func_->return_type.GetName(),
Error::ErrorType::WAR_UNEXPECTED_RETURN_TYPE);
}
% end
% required_args = operands.size
% required_args = 1 if insn.call?
% required_args = 2 if insn.stripped_mnemonic == 'calli'
% operands.each_with_index do |op, j|
%
% if (j != 0)
% if j >= required_args
if (!context_.Mask()) {
ParseOperandComma();
}
% else
if (!context_.Mask()) {
ParseOperandComma();
} else {
context_.err = GetError("Expected comma.", Error::ErrorType::ERR_BAD_NUMBER_OPERANDS);
}
% end
% end
%
if (!context_.Mask()) {
% if op.reg?
if (RegValidName()) {
p = context_.GiveToken();
p.remove_prefix(1);
number = ToNumber(p);
size_t reg_width = curr_func_->ins.back().MaxRegEncodingWidth();
if ((1ULL << reg_width) <= number) {
context_.err = GetError("Register width mismatch.", Error::ErrorType::ERR_BAD_OPERAND);
}
} else if (context_.err.err == Error::ErrorType::ERR_NONE) {
context_.err = GetError("Invalid name of register.", Error::ErrorType::ERR_BAD_NAME_REG);
}
ParseOperandVreg();
% elsif op.imm?
% if properties.include?("jump")
ParseOperandLabel();
% else
% if properties.include?("float")
ParseOperandFloat(<%= op.size == 64 ? "true" : "false" %>);
% else
ParseOperandInteger();
% end
% end
% elsif op.id?
% if properties.include?("type_id")
% if (verification.include?("type_id_array"))
ParseOperandType(Type::VerificationType::TYPE_ID_ARRAY);
% elsif (verification.include?("type_id_object"))
ParseOperandType(Type::VerificationType::TYPE_ID_OBJECT);
% elsif (verification.include?("type_id_class"))
ParseOperandType(Type::VerificationType::TYPE_ID_CLASS);
% elsif (verification.include?("type_id_any_object"))
ParseOperandType(Type::VerificationType::TYPE_ID_ANY_OBJECT);
% end
% elsif properties.include?("string_id")
ParseOperandString();
% elsif properties.include?("method_id")
% if insn.opcode[0..6] == "initobj"
ParseOperandInitobj();
% else
ParseOperandCall();
% end
% elsif properties.include?("literalarray_id") # NOTE:
% # this check must stay below check for
% # properties.include?("method_id"), because we have opcodes
% # with multiple id's, and their parsing is order-sensitive
ParseOperandLiteralArray();
% elsif properties.include?("field_id")
ParseOperandField();
% else
ParseOperandId();
% end
% end
%
% if (j >= required_args) && (operands.size > 0)
}
% elsif operands.size > 0
} else {
context_.err = GetError("Expected more arguments.", Error::ErrorType::ERR_BAD_NUMBER_OPERANDS);
}
% end
% end
ParseOperandNone();
} break;
%end
default:
context_.err = GetError("No such operation.", Error::ErrorType::ERR_BAD_NONEXISTING_OPERATION);
return false;
}
return context_.Mask();
}
} // namespace panda::pandasm

View File

@ -0,0 +1,52 @@
/*
* 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 "assembly-parser.h"
namespace panda::pandasm {
// NOLINTNEXTLINE(readability-function-size)
inline std::string OperandTypePrint(panda::pandasm::Opcode op) {
switch (op) {
% Panda::instructions.group_by(&:mnemonic).each do |mnemonic, group|
% insn = group.first
% operands = insn.operands
% properties = insn.properties
case Opcode::<%= group.first.asm_token%>: {
% operands_list = operands.map do |op|
% if op.reg?
% 'reg'
% elsif op.imm?
% properties.include?('jump') ? 'label' : 'imm'
% elsif op.id?
% if properties.include?('type_id')
% 'type'
% elsif properties.include?('string_id')
% 'string'
% elsif properties.include?('method_id')
% 'call'
% else
% 'id'
% end
% end
% end.join('_')
% operands_list = 'none' if operands_list == ''
return "<%= operands_list%>";
};
%end
default:
return "undefined";
}
}
} // namespace panda::pandasm

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,227 @@
/**
* 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 <iostream>
#include <string>
#include <gtest/gtest.h>
#include "../define.h"
#include "../lexer.h"
// NOLINTNEXTLINE(google-build-using-namespace)
using namespace panda::pandasm;
TEST(lexertests, test1)
{
Lexer l;
std::string s = "mov v1, v2";
Tokens tok = l.TokenizeString(s);
ASSERT_EQ(TokenTypeWhat(tok.first[0].type), "OPERATION") << "OPERATION expected";
ASSERT_EQ(TokenTypeWhat(tok.first[1].type), "ID") << "ID expected";
ASSERT_EQ(TokenTypeWhat(tok.first[2].type), "DEL_COMMA") << "DEL_COMMA expected";
ASSERT_EQ(TokenTypeWhat(tok.first[3].type), "ID") << "ID expected";
ASSERT_EQ(tok.second.err, Error::ErrorType::ERR_NONE) << "ERR_NONE expected";
}
TEST(lexertests, test2)
{
Lexer l;
std::string s = "ldai 1";
Tokens tok = l.TokenizeString(s);
ASSERT_EQ(TokenTypeWhat(tok.first[0].type), "OPERATION") << "OPERATION expected";
ASSERT_EQ(TokenTypeWhat(tok.first[1].type), "ID") << "ID expected";
ASSERT_EQ(tok.second.err, Error::ErrorType::ERR_NONE) << "ERR_NONE expected";
}
TEST(lexertests, test3)
{
Lexer l;
std::string s = "movi\nlda v2 v10 mov v2";
Tokens tok = l.TokenizeString(s);
ASSERT_EQ(TokenTypeWhat(tok.first[0].type), "OPERATION") << "OPERATION expected";
ASSERT_EQ(TokenTypeWhat(tok.first[1].type), "OPERATION") << "OPERATION expected";
ASSERT_EQ(TokenTypeWhat(tok.first[2].type), "ID") << "ID expected";
ASSERT_EQ(TokenTypeWhat(tok.first[3].type), "ID") << "ID expected";
ASSERT_EQ(TokenTypeWhat(tok.first[4].type), "OPERATION") << "OPERATION expected";
ASSERT_EQ(TokenTypeWhat(tok.first[5].type), "ID") << "ID expected";
ASSERT_EQ(tok.second.err, Error::ErrorType::ERR_NONE) << "ERR_NONE expected";
}
TEST(lexertests, test4)
{
Lexer l;
std::string s = "jmp Iasdfsadkfjhasifhsaiuhdacoisjdaociewhasdasdfkjasdfhjksadhfkhsakdfjhksajhdkfjhskhdfkjahhjdskaj";
Tokens tok = l.TokenizeString(s);
ASSERT_EQ(TokenTypeWhat(tok.first[0].type), "OPERATION") << "OPERATION expected";
ASSERT_EQ(TokenTypeWhat(tok.first[1].type), "ID") << "ID expected";
ASSERT_EQ(tok.second.err, Error::ErrorType::ERR_NONE) << "ERR_NONE expected";
}
TEST(lexertests, test5)
{
Lexer l;
std::string s = "call.short 1111, 1";
Tokens tok = l.TokenizeString(s);
ASSERT_EQ(TokenTypeWhat(tok.first[0].type), "OPERATION") << "OPERATION expected";
ASSERT_EQ(TokenTypeWhat(tok.first[1].type), "ID") << "ID expected";
ASSERT_EQ(TokenTypeWhat(tok.first[2].type), "DEL_COMMA") << "DEL_COMMA expected";
ASSERT_EQ(TokenTypeWhat(tok.first[3].type), "ID") << "ID expected";
ASSERT_EQ(tok.second.err, Error::ErrorType::ERR_NONE) << "ERR_NONE expected";
}
TEST(lexertests, test6)
{
Lexer l;
std::string s = "jle v1 met";
Tokens tok = l.TokenizeString(s);
ASSERT_EQ(TokenTypeWhat(tok.first[0].type), "OPERATION") << "OPERATION expected";
ASSERT_EQ(TokenTypeWhat(tok.first[1].type), "ID") << "ID expected";
ASSERT_EQ(TokenTypeWhat(tok.first[2].type), "ID") << "ID expected";
ASSERT_EQ(tok.second.err, Error::ErrorType::ERR_NONE) << "ERR_NONE expected";
}
TEST(lexertests, test7)
{
Lexer l;
std::string s = "label:";
Tokens tok = l.TokenizeString(s);
ASSERT_EQ(TokenTypeWhat(tok.first[0].type), "ID") << "ID expected";
ASSERT_EQ(tok.second.err, Error::ErrorType::ERR_NONE) << "ERR_NONE expected";
}
TEST(lexertests, test8)
{
Lexer l;
std::string s = ",";
Tokens tok = l.TokenizeString(s);
ASSERT_EQ(TokenTypeWhat(tok.first[0].type), "DEL_COMMA") << "DEL_COMMA expected";
ASSERT_EQ(tok.second.err, Error::ErrorType::ERR_NONE) << "ERR_NONE expected";
}
TEST(lexertests, test9)
{
Lexer l;
std::string s = ",:{}()<>=";
Tokens tok = l.TokenizeString(s);
ASSERT_EQ(TokenTypeWhat(tok.first[0].type), "DEL_COMMA") << "DEL_COMMA expected";
ASSERT_EQ(TokenTypeWhat(tok.first[1].type), "DEL_COLON") << "DEL_COMMA expected";
ASSERT_EQ(TokenTypeWhat(tok.first[2].type), "DEL_BRACE_L") << "DEL_COMMA expected";
ASSERT_EQ(TokenTypeWhat(tok.first[3].type), "DEL_BRACE_R") << "DEL_COMMA expected";
ASSERT_EQ(TokenTypeWhat(tok.first[4].type), "DEL_BRACKET_L") << "DEL_BRACKET_L expected";
ASSERT_EQ(TokenTypeWhat(tok.first[5].type), "DEL_BRACKET_R") << "DEL_BRACKET_R expected";
ASSERT_EQ(TokenTypeWhat(tok.first[6].type), "DEL_LT") << "DEL_LT expected";
ASSERT_EQ(TokenTypeWhat(tok.first[7].type), "DEL_GT") << "DEL_GT expected";
ASSERT_EQ(TokenTypeWhat(tok.first[8].type), "DEL_EQ") << "DEL_EQ expected";
ASSERT_EQ(tok.second.err, Error::ErrorType::ERR_NONE) << "ERR_NONE expected";
}
TEST(lexertests, test11)
{
Lexer l;
std::string s =
"i64.to.f32 alsdhashdjskhfka "
"shdkfhkasdhfkhsakdhfkshkfhskahlfkjsdfkjadskhfkshadkhfsdakhfksahdkfaksdfkhaskldhkfashdlfkjhasdkjfhklasjhdfklhsa"
"fhska";
Tokens tok = l.TokenizeString(s);
ASSERT_EQ(tok.second.err, Error::ErrorType::ERR_NONE) << "ERR_NONE expected";
}
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";
}
TEST(lexertests, string_literal)
{
{
Lexer l;
std::string s = "\"123";
Tokens tok = l.TokenizeString(s);
Error e = tok.second;
ASSERT_EQ(e.err, Error::ErrorType::ERR_STRING_MISSING_TERMINATING_CHARACTER);
}
{
Lexer l;
std::string s = R"("123\")";
Tokens tok = l.TokenizeString(s);
Error e = tok.second;
ASSERT_EQ(e.err, Error::ErrorType::ERR_STRING_MISSING_TERMINATING_CHARACTER);
}
{
Lexer l;
std::string s = R"(" a b \ c d ")";
Tokens tok = l.TokenizeString(s);
Error e = tok.second;
ASSERT_EQ(e.err, Error::ErrorType::ERR_NONE);
ASSERT_EQ(tok.first.size(), 1U);
ASSERT_EQ(tok.first[0].type, Token::Type::ID_STRING);
ASSERT_EQ(tok.first[0].bound_left, 0U);
ASSERT_EQ(tok.first[0].bound_right, s.length());
}
{
Lexer l;
std::string s = "\"abcd\"1234";
Tokens tok = l.TokenizeString(s);
Error e = tok.second;
ASSERT_EQ(e.err, Error::ErrorType::ERR_NONE);
ASSERT_EQ(tok.first.size(), 2U);
ASSERT_EQ(tok.first[0].type, Token::Type::ID_STRING);
ASSERT_EQ(tok.first[0].bound_left, 0U);
ASSERT_EQ(tok.first[0].bound_right, s.find('1'));
}
}
TEST(lexertests, array_type)
{
Lexer l;
std::string s = "i32[]";
Tokens tok = l.TokenizeString(s);
Error e = tok.second;
ASSERT_EQ(e.err, Error::ErrorType::ERR_NONE);
ASSERT_EQ(tok.first.size(), 3U);
ASSERT_EQ(tok.first[0].type, Token::Type::ID);
ASSERT_EQ(tok.first[1].type, Token::Type::DEL_SQUARE_BRACKET_L);
ASSERT_EQ(tok.first[2].type, Token::Type::DEL_SQUARE_BRACKET_R);
}
TEST(lexertests, record_type)
{
Lexer l;
std::string s = "FunctionalInterface-u16-i32-u16[]-u1-0";
Tokens tok = l.TokenizeString(s);
Error e = tok.second;
ASSERT_EQ(e.err, Error::ErrorType::ERR_NONE);
ASSERT_EQ(tok.first.size(), 1U);
ASSERT_EQ(tok.first[0].type, Token::Type::ID);
}

View File

@ -0,0 +1,60 @@
/**
* 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 <gtest/gtest.h>
#include "mangling.h"
#include "assembly-function.h"
#include "extensions/extensions.h"
// NOLINTNEXTLINE(google-build-using-namespace)
using namespace panda::pandasm;
TEST(ManglingTests, MangleFunctionName)
{
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);
auto return_type = Type("type4", 0);
std::string name = "Asm.main";
ASSERT_EQ(MangleFunctionName(name, params, return_type), "Asm.main:type1;type2;type3;type4;");
}
TEST(ManglingTests, DeMangleFunctionName)
{
std::string name = "Asm.main:type1;type2;type3;type4;";
ASSERT_EQ(DeMangleName(name), "Asm.main");
}
TEST(ManglingTests, 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, params), "Asm.main:(type1,type2,type3)");
}
TEST(ManglingTests, GetFunctionNameFromSignature)
{
std::string name = "Asm.main:(type1,type2,type3,type4)";
ASSERT_EQ(GetFunctionNameFromSignature(name), "Asm.main");
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,194 @@
/**
* 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_NUMBERS_UTILS_H
#define _PANDA_ASSEMBLER_NUMBERS_UTILS_H
namespace panda::pandasm {
constexpr size_t HEX_BASE = 16;
constexpr size_t DEC_BASE = 10;
constexpr size_t OCT_BASE = 8;
constexpr size_t BIN_BASE = 2;
constexpr size_t MAX_DWORD = 65536;
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 == ".") {
return false;
}
if (token[0] == '-' || token[0] == '+') {
token.remove_prefix(1);
}
if (token[0] == '0' && token.size() > 1 && token.find('.') == std::string::npos) {
if (token[1] == 'x') {
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') {
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) {
token.remove_prefix(1);
for (auto i : token) {
if (!(i >= '0' && i <= '7')) {
return false;
}
}
return true;
}
}
for (auto i : token) {
if (!(i >= '0' && i <= '9')) {
return false;
}
}
return true;
}
inline int64_t IntegerNumber(std::string_view p)
{
constexpr size_t GENERAL_SHIFT = 2;
// expects a valid number
if (p.size() == 1) {
return p[0] - '0';
}
size_t minus_shift = 0;
if (p[0] == '-') {
minus_shift++;
}
if (p[minus_shift + 1] == 'b') {
p.remove_prefix(GENERAL_SHIFT + minus_shift);
return std::strtoull(p.data(), nullptr, BIN_BASE) * (minus_shift == 0 ? 1 : -1);
}
if (p[minus_shift + 1] == 'x') {
return std::strtoull(p.data(), nullptr, HEX_BASE);
}
if (p[minus_shift] == '0') {
return std::strtoull(p.data(), nullptr, OCT_BASE);
}
return std::strtoull(p.data(), nullptr, DEC_BASE);
}
inline bool ValidateFloat(std::string_view p)
{
std::string_view token = p;
if (ValidateInteger(token)) {
return true;
}
if (token[0] == '-' || token[0] == '+') {
token.remove_prefix(1);
}
bool dot = false;
bool exp = false;
bool nowexp = false;
for (auto i : token) {
if (nowexp && (i == '-' || i == '+')) {
nowexp = false;
continue;
}
if (nowexp) {
nowexp = false;
}
if (i == '.' && !exp && !dot) {
dot = true;
} else if (!exp && i == 'e') {
nowexp = true;
exp = true;
} else if (!(i >= '0' && i <= '9')) {
return false;
}
}
return !nowexp;
}
inline double FloatNumber(std::string_view p, bool is_64bit)
{
constexpr size_t GENERAL_SHIFT = 2;
// expects a valid number
if (p.size() > GENERAL_SHIFT && p.substr(0, GENERAL_SHIFT) == "0x") { // hex literal
char *end = nullptr;
if (is_64bit) {
return bit_cast<double>(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);
}
inline size_t ToNumber(std::string_view p)
{
size_t sum = 0;
for (char i : p) {
if (isdigit(i) != 0) {
sum = sum * DEC_BASE + static_cast<size_t>(i - '0');
} else {
return MAX_DWORD;
}
}
return sum;
}
} // namespace panda::pandasm
#endif // !_PANDA_ASSEMBLER_NUMBERS_UTILS_H

View File

@ -0,0 +1,148 @@
# 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/static_core/ark_config.gni")
import("//build/ohos.gni")
import("$ark_root/plugins/plugins.gni")
config("bytecodeopt_public_config") {
include_dirs = [
"$target_gen_dir",
"$ark_root/bytecode_optimizer",
]
if (enable_bytecode_optimizer && plugin_enable_bytecode_optimizer) {
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_sources += plugin_libarkbytecodeopt_sources
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("libarktsbytecodeopt") {
sources = libarkbytecodeopt_sources
configs = libarkbytecodeopt_configs
deps = [
":bytecodeopt_options_gen_h",
":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",
":codegen_intrinsics_cpp",
"$ark_root/assembler:libarktsassembler",
"$ark_root/compiler:libarktscompiler",
"$ark_root/libpandabase:libarktsbase",
"$ark_root/libpandafile:libarktsfile",
]
deps += plugin_bytecodeopt_deps
output_extension = "so"
part_name = ark_part_name
subsystem_name = "$ark_subsystem_name"
}
ohos_static_library("libarktsbytecodeopt_frontend_static") {
sources = libarkbytecodeopt_sources
configs = libarkbytecodeopt_configs
deps = [
":bytecodeopt_options_gen_h",
":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",
":codegen_intrinsics_cpp",
"$ark_root/assembler:libarktsassembler_frontend_static",
"$ark_root/compiler:libarktscompiler_frontend_static",
"$ark_root/libpandabase:libarktsbase_frontend_static",
"$ark_root/libpandafile:libarktsfile_frontend_static",
]
deps += plugin_bytecodeopt_deps
part_name = ark_part_name
subsystem_name = ark_subsystem_name
}
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 = "$ark_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 = "$ark_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 = "$ark_plugin_options_yaml"
requires = [ "$ark_root/templates/plugin_options.rb" ]
output_file = "$target_gen_dir/generated/codegen_intrinsics.cpp"
}

View File

@ -0,0 +1,119 @@
# 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}"
)
panda_add_library(arkbytecodeopt_obj OBJECT ${SOURCES})
add_dependencies(arkbytecodeopt_obj isa_gen_bytecode_optimizer)
panda_gen_options(TARGET arkbytecodeopt_obj YAML_FILE options.yaml GENERATED_HEADER bytecodeopt_options_gen.h)
set_target_properties(arkbytecodeopt_obj PROPERTIES
POSITION_INDEPENDENT_CODE ON
CXX_VISIBILITY_PRESET hidden
VISIBILITY_INLINES_HIDDEN ON
)
panda_target_include_directories(arkbytecodeopt_obj
PUBLIC ${PANDA_ROOT}
PUBLIC ${CMAKE_CURRENT_BINARY_DIR}
PUBLIC ${GENERATED_DIR}
)
panda_target_link_libraries(arkbytecodeopt_obj arkcompiler arkassembler arkfile arkbase)
panda_add_library(arkbytecodeopt_static STATIC $<TARGET_OBJECTS:arkbytecodeopt_obj>)
panda_target_link_libraries(arkbytecodeopt_static arkbytecodeopt_obj)
panda_add_library(arkbytecodeopt ${PANDA_DEFAULT_LIB_TYPE} $<TARGET_OBJECTS:arkbytecodeopt_obj>)
panda_target_link_libraries(arkbytecodeopt arkbytecodeopt_obj)
set(PANDA_BYTECODE_OPT_TESTS_LIBRARIES arkbytecodeopt_static)
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
tests/irbuilder_test.cpp
)
panda_add_gtest(
NAME bytecodeopt_unit_tests
SOURCES
${BYTECODE_OPT_TEST_SOURCES}
INCLUDE_DIRS
${CMAKE_CURRENT_SOURCE_DIR}
${GENERATED_DIR}/..
${GENERATED_DIR}/../panda_gen_options
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_sanitizers(TARGET arkbytecodeopt SANITIZERS ${PANDA_SANITIZERS_LIST})

View File

@ -0,0 +1,71 @@
/**
* Copyright (c) 2021-2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef PANDA_BYTECODE_OPTIMIZER_BYTECODE_ENCODER_H
#define PANDA_BYTECODE_OPTIMIZER_BYTECODE_ENCODER_H
#include <cstdint>
#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 /* label */) override
{
return 0;
}
bool LabelHasLinks(compiler::LabelHolder::LabelId /* label */) override
{
return false;
}
}; // BytecodeEncoder
} // namespace panda::bytecodeopt
#endif // PANDA_BYTECODE_OPTIMIZER_BYTECODE_ENCODER_H

View File

@ -0,0 +1,565 @@
# 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, :cpp, :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
If = Struct.new(:condition, :true_expr, :false_expr) do
def proxy(method)
res = "if (#{condition}) {\n"
res << true_expr.send(method)
res << "} else {\n"
res << false_expr.send(method)
res << "}\n"
res
end
def encode
proxy(:encode)
end
def check_width
proxy(:check_width)
end
end
Empty = Struct.new(:dummy) do
def encode; end
def check_width; end
end
# Sugar for bytecode description
def visit(ir_op, cpp = nil)
@table ||= []
@table << Visitor.new(ir_op, cpp, 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 if_(condition, true_expr, false_expr)
If.new(condition, true_expr, false_expr)
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_value(value, node)
Case.new([value], node)
end
def case_true(opcode, *args)
case_value('1', plain(opcode, *args))
end
def case_true_node(node)
case_value('1', node)
end
def case_false(opcode, *args)
case_value('0', plain(opcode, *args))
end
def case_false_node(node)
case_value('0', node)
end
def generate_arith_op(op)
switch("static_cast<int>(#{dst_r} != #{r(0)} && #{dst_r} != #{r(1)})",
[case_true(op, r(0), r(1)),
case_false_node(is_commutative?(op) ?
if_("#{dst_r} == #{r(0)}",
plain("#{op}v", r(0), r(1)),
plain("#{op}v", r(1), r(0))) :
plain(op, r(0), r(1))
)
]
)
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 is_acc?(reg)
"#{reg} == compiler::ACC_REG_ID"
end
def is_not_acc?(reg)
"#{reg} != compiler::ACC_REG_ID"
end
def is_compact?(reg)
"#{reg} < NUM_COMPACTLY_ENCODED_REGS"
end
def is_not_compact?(reg)
"#{reg} >= NUM_COMPACTLY_ENCODED_REGS"
end
def is_fcmpg?
'static_cast<int>(inst->IsFcmpg())'
end
def is_inci?
"static_cast<int>(CanConvertToIncI(inst))"
end
def is_commutative?(op)
["add", "mul", "and", "or", "xor"].include?(op)
end
def is_arith_iv?
"#{is_not_acc?(r(0))} && #{is_not_acc?(dst_r)} && (#{is_compact?(r(0))} || #{is_compact?(dst_r)})"
end
def cast_to_int(val)
"static_cast<int>(#{val})"
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 AShr].each do |op|
visit(op) do
op = op.downcase
switch(type,
[case_switch(i32_types, cast_to_int(is_not_compact?(r(0))),
[case_true_node(if_(is_acc?(dst_r),
plain("#{op}2", r(1)),
plain("#{op}2v", dst_r, r(1)))),
case_false_node(if_("#{is_compact?(dst_r)}",
generate_arith_op(op),
plain(op, r(0), r(1))))]),
case_switch(i64_types, cast_to_int(is_acc?(dst_r)),
[case_true("#{op}2.64", r(1)),
case_false("#{op}2v.64", dst_r, r(1))])]
)
end
end
%w[add sub mul div mod].each do |op|
visit(op.capitalize) do
switch(type,
[case_switch(i32_types, cast_to_int(is_not_compact?(r(0))),
[case_true_node(if_(is_acc?(dst_r),
plain("#{op}2", r(1)),
plain("#{op}2v", dst_r, r(1)))),
case_false_node(if_("#{is_compact?(dst_r)}",
generate_arith_op(op),
plain(op, r(0), r(1))))]),
case_switch(i64_types, cast_to_int(is_acc?(dst_r)),
[case_true("#{op}2.64", r(1)),
case_false("#{op}2v.64", dst_r, r(1))]),
case_switch(f32_types, cast_to_int(is_acc?(dst_r)),
[case_true("f#{op}2", r(1)),
case_false("f#{op}2v", dst_r, r(1))]),
case_switch(f64_types, cast_to_int(is_acc?(dst_r)),
[case_true("f#{op}2.64", r(1)),
case_false("f#{op}2v.64", dst_r, r(1))])]
)
end
end
visit('AddI') do
switch(type,
[case_switch(i32_types, is_inci?,
[case_true('inci', r(0), imm),
case_false_node(if_(is_arith_iv?,
plain("addiv", dst_r, r(0), imm),
plain("addi", imm)))])]
)
end
visit('SubI') do
switch(type,
[case_switch(i32_types, is_inci?,
[case_true('inci', r(0), "-(#{imm})"),
case_false_node(if_(is_arith_iv?,
plain("subiv", dst_r, r(0), imm),
plain("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'], is_fcmpg?,
[case_true('fcmpg', r(1)),
case_false('fcmpl', r(1))]),
case_switch(['FLOAT64'], is_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(cast_to_int(is_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('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', 'if (inst->GetTypeId() == compiler::TypeIdMixin::MEM_PROMISE_CLASS_ID) { return; }') 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 LoadRuntimeClass].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_switch(i32_types, cast_to_int(is_arith_iv?),
[case_true("#{op.downcase}v", dst_r, r(0), imm),
case_false("#{op.downcase}", imm)])]
)
end
end
end

View File

@ -0,0 +1,24 @@
/**
* Copyright (c) 2021-2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef BYTECODEOPT_OPTIONS_H
#define BYTECODEOPT_OPTIONS_H
#include "generated/bytecodeopt_options_gen.h"
namespace panda::bytecodeopt {
PANDA_PUBLIC_API extern panda::bytecodeopt::Options OPTIONS;
} // namespace panda::bytecodeopt
#endif // BYTECODEOPT_OPTIONS_H

View File

@ -0,0 +1,159 @@
/**
* 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)
{
auto *graph = new_object->GetBasicBlock()->GetGraph();
auto *adapter = graph->GetRuntime();
for (auto &user : new_object->GetUsers()) {
auto *inst = user.GetInst();
if (inst->GetOpcode() == Opcode::NullCheck) {
return FindCtorCall(inst);
}
if (inst->GetOpcode() != Opcode::CallStatic) {
continue;
}
auto call = inst->CastToCallStatic();
auto call_first_arg = call->GetInput(0).GetInst();
if (call_first_arg->GetOpcode() == Opcode::NewObject) {
auto new_object_as_arg = call_first_arg->CastToNewObject();
if (new_object_as_arg != new_object) {
continue;
}
}
if (adapter->IsConstructor(call->GetCallMethod(), graph->GetLanguage())) {
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)
{
CallInst *call_init = FindCtorCall(inst);
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 load = static_cast<compiler::ClassInst *>(inst->GetInput(0).GetInst());
auto *init_object = CreateInitObject(v, load, call_init);
call_init->InsertBefore(init_object);
init_object->SetPc(call_init->GetPc());
ReplaceNewObjectUsers(inst, null_check, init_object);
inst->ClearFlag(compiler::inst_flags::NO_DCE);
if (null_check != nullptr) {
null_check->ReplaceUsers(init_object);
null_check->ClearFlag(compiler::inst_flags::NO_DCE);
null_check->RemoveInputs();
null_check->GetBasicBlock()->ReplaceInst(null_check,
static_cast<BytecodeOptPeepholes *>(v)->GetGraph()->CreateInstNOP());
}
ASSERT(!call_init->HasUsers());
call_init->ClearFlag(compiler::inst_flags::NO_DCE);
static_cast<BytecodeOptPeepholes *>(v)->SetIsApplied();
}
} // namespace panda::bytecodeopt

View File

@ -0,0 +1,92 @@
/**
* 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;
// NOLINTNEXTLINE(fuchsia-multiple-inheritance)
class BytecodeOptPeepholes : public compiler::Optimization, public compiler::GraphVisitor {
public:
explicit BytecodeOptPeepholes(compiler::Graph *graph) : compiler::Optimization(graph) {}
~BytecodeOptPeepholes() override = default;
NO_COPY_SEMANTIC(BytecodeOptPeepholes);
NO_MOVE_SEMANTIC(BytecodeOptPeepholes);
bool RunImpl() override;
const char *GetPassName() const override
{
return "BytecodeOptPeepholes";
}
bool IsEnable() const override
{
return OPTIONS.IsBytecodeOptPeepholes();
}
/* This visitor replaces combination of NewObject, SaveState,
* NullCheck and CallStatic with InitObject. It is used in order to replace newobj, sta and call
* in some ark bytecode with one instruction initobj.
*/
const ArenaVector<BasicBlock *> &GetBlocksToVisit() const override
{
return GetGraph()->GetBlocksRPO();
}
static void VisitNewObject(GraphVisitor *v, Inst *inst);
public:
bool IsApplied()
{
return is_applied_;
}
#include "compiler/optimizer/ir/visitor.inc"
private:
void SetIsApplied()
{
is_applied_ = true;
}
bool is_applied_ {false};
};
} // namespace panda::bytecodeopt
#endif // PANDA_BYTECODE_OPTIMIZER_BYTECODEOPT_PEEPHOLES_H_

View File

@ -0,0 +1,130 @@
/**
* Copyright (c) 2021-2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "canonicalization.h"
#include "compiler/optimizer/ir/basicblock.h"
#include "compiler/optimizer/ir/inst.h"
namespace panda::bytecodeopt {
bool Canonicalization::RunImpl()
{
Canonicalization visitor(GetGraph());
for (auto bb : GetGraph()->GetBlocksRPO()) {
for (auto inst : bb->AllInsts()) {
if (inst->IsCommutative()) {
visitor.VisitCommutative(inst);
} else {
visitor.VisitInstruction(inst);
}
}
}
return visitor.GetStatus();
}
static bool IsDominateReverseInputs(const compiler::Inst *inst)
{
auto input0 = inst->GetInput(0).GetInst();
auto input1 = inst->GetInput(1).GetInst();
return input0->IsDominate(input1);
}
static bool ConstantFitsCompareImm(const Inst *cst, uint32_t size)
{
ASSERT(cst->GetOpcode() == Opcode::Constant);
if (compiler::DataType::IsFloatType(cst->GetType())) {
return false;
}
auto val = cst->CastToConstant()->GetIntValue();
return (size == compiler::HALF_SIZE) && (val == 0);
}
static bool BetterToSwapCompareInputs(const compiler::Inst *inst, const compiler::Inst *input0,
const compiler::Inst *input1)
{
if (!input0->IsConst()) {
return false;
}
if (!input1->IsConst()) {
return true;
}
compiler::DataType::Type type = inst->CastToCompare()->GetOperandsType();
uint32_t size = (type == compiler::DataType::UINT64 || type == compiler::DataType::INT64) ? compiler::WORD_SIZE
: compiler::HALF_SIZE;
return ConstantFitsCompareImm(input0, size) && !ConstantFitsCompareImm(input1, size);
}
static bool SwapInputsIfNecessary(compiler::Inst *inst, const bool necessary)
{
if (!necessary) {
return false;
}
auto input0 = inst->GetInput(0).GetInst();
auto input1 = inst->GetInput(1).GetInst();
if ((inst->GetOpcode() == compiler::Opcode::Compare) && !BetterToSwapCompareInputs(inst, input0, input1)) {
return false;
}
inst->SwapInputs();
return true;
}
bool Canonicalization::TrySwapConstantInput(Inst *inst)
{
return SwapInputsIfNecessary(inst, inst->GetInput(0).GetInst()->IsConst());
}
bool Canonicalization::TrySwapReverseInput(Inst *inst)
{
return SwapInputsIfNecessary(inst, IsDominateReverseInputs(inst));
}
void Canonicalization::VisitCommutative(Inst *inst)
{
ASSERT(inst->IsCommutative());
ASSERT(inst->GetInputsCount() == 2); // 2 is COMMUTATIVE_INPUT_COUNT
if (OPTIONS.GetOptLevel() > 1) {
result_ = TrySwapReverseInput(inst);
}
result_ = TrySwapConstantInput(inst) || result_;
}
// It is not allowed to move a constant input1 with a single user (it's processed Compare instruction).
// This is necessary for further merging of the constant and the If instrution in the Lowering pass
bool AllowSwap(const compiler::Inst *inst)
{
auto input1 = inst->GetInput(1).GetInst();
if (!input1->IsConst()) {
return true;
}
for (const auto &user : input1->GetUsers()) {
if (user.GetInst() != inst) {
return true;
}
}
return false;
}
void Canonicalization::VisitCompare([[maybe_unused]] GraphVisitor *v, Inst *inst_base)
{
auto inst = inst_base->CastToCompare();
if (AllowSwap(inst) && SwapInputsIfNecessary(inst, IsDominateReverseInputs(inst))) {
auto revert_cc = SwapOperandsConditionCode(inst->GetCc());
inst->SetCc(revert_cc);
}
}
} // namespace panda::bytecodeopt

View File

@ -0,0 +1,69 @@
/**
* 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;
// NOLINTNEXTLINE(fuchsia-multiple-inheritance)
class Canonicalization : public compiler::Optimization, public compiler::GraphVisitor {
public:
explicit Canonicalization(compiler::Graph *graph) : compiler::Optimization(graph) {}
~Canonicalization() override = default;
NO_COPY_SEMANTIC(Canonicalization);
NO_MOVE_SEMANTIC(Canonicalization);
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);
void static VisitCompare([[maybe_unused]] GraphVisitor *v, Inst *inst);
static bool TrySwapReverseInput(Inst *inst);
static bool TrySwapConstantInput(Inst *inst);
#include "optimizer/ir/visitor.inc"
private:
bool result_ {false};
};
} // namespace panda::bytecodeopt
#endif // PANDA_BYTECODE_OPT_CANONICALIZATION_H

View File

@ -0,0 +1,96 @@
/**
* 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();
for (auto &user : inst->GetUsers()) {
user.GetInst()->SetFlag(compiler::inst_flags::CAN_THROW);
}
inst->ReplaceUsers(input);
inst->ClearFlag(compiler::inst_flags::NO_DCE); // DCE will remove the check inst
applied = true;
}
// set NO_DCE for Div, Mod, LoadArray, LoadStatic and LoadObject
if (NeedSetNoDCE(inst)) {
inst->SetFlag(compiler::inst_flags::NO_DCE);
applied = true;
}
// mark LenArray whose users are not all check as no_dce
// mark LenArray as no_hoist in all cases
if (inst->GetOpcode() == compiler::Opcode::LenArray) {
bool no_dce = !inst->HasUsers();
for (const auto &usr : inst->GetUsers()) {
if (!IsCheck(usr.GetInst())) {
no_dce = true;
break;
}
}
if (no_dce) {
inst->SetFlag(compiler::inst_flags::NO_DCE);
}
inst->SetFlag(compiler::inst_flags::NO_HOIST);
}
}
}
return applied;
}
bool CheckResolver::NeedSetNoDCE(const compiler::Inst *inst)
{
switch (inst->GetOpcode()) {
case compiler::Opcode::Div:
case compiler::Opcode::Mod:
case compiler::Opcode::LoadArray:
case compiler::Opcode::LoadStatic:
case compiler::Opcode::LoadObject:
return true;
default:
return false;
}
}
bool CheckResolver::IsCheck(const compiler::Inst *inst)
{
switch (inst->GetOpcode()) {
case compiler::Opcode::BoundsCheck:
case compiler::Opcode::NullCheck:
case compiler::Opcode::NegativeCheck:
case compiler::Opcode::ZeroCheck:
case compiler::Opcode::RefTypeCheck:
case compiler::Opcode::BoundsCheckI:
return true;
default:
return false;
}
}
} // namespace panda::bytecodeopt

View File

@ -0,0 +1,67 @@
/**
* 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;
NO_COPY_SEMANTIC(CheckResolver);
NO_MOVE_SEMANTIC(CheckResolver);
bool RunImpl() override;
static bool IsCheck(const compiler::Inst *inst);
static bool NeedSetNoDCE(const compiler::Inst *inst);
const char *GetPassName() const override
{
return "CheckResolver";
}
};
} // namespace panda::bytecodeopt
#endif // PANDA_CHECK_RESOLVER_H

View File

@ -0,0 +1,32 @@
# 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.
option(ENABLE_BYTECODE_OPTIMIZER_COVERAGE "Enable coverage calculation for the bytecode optimizer" false)
include(${PANDA_ROOT}/cmake/toolchain/coverage/unit_tests_lcov.cmake)
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}
)
if(ENABLE_BYTECODE_OPTIMIZER_COVERAGE)
collect_coverage_for_target(
TARGET_NAME bytecode_optimizer_coverage
INCLUDE_DIR_PATTERN '*/bytecode_optimizer/*'
)
else()
message(STATUS "Coverage will not be calculated (may be enabled by -DENABLE_BYTECODE_OPTIMIZER_COVERAGE=true ).")
endif(ENABLE_BYTECODE_OPTIMIZER_COVERAGE)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,169 @@
/**
* 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"
#include "common.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);
// NOLINTNEXTLINE(fuchsia-multiple-inheritance)
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;
NO_COPY_SEMANTIC(BytecodeGen);
NO_MOVE_SEMANTIC(BytecodeGen);
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, size_t idx);
void AddColumnNumber(const Inst *inst, 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 VisitStoreStatic(GraphVisitor *v, Inst *inst_base);
static void VisitLoadObject(GraphVisitor *v, Inst *inst_base);
static void VisitLoadStatic(GraphVisitor *v, Inst *inst_base);
static void VisitLoadString(GraphVisitor *v, Inst *inst_base);
static void VisitReturn(GraphVisitor *v, Inst *inst_base);
static void VisitCastValueToAnyType(GraphVisitor *v, Inst *inst_base);
static void VisitEcma(GraphVisitor *v, Inst *inst_base);
static void IfEcma(GraphVisitor *v, compiler::IfInst *inst);
#include "generated/codegen_visitors.inc"
#include "generated/insn_selection.h"
void VisitDefault(Inst *inst) override
{
LOG(ERROR, BYTECODE_OPTIMIZER) << "Opcode " << compiler::GetOpcodeString(inst->GetOpcode())
<< " not yet implemented in codegen";
success_ = false;
}
#include "compiler/optimizer/ir/visitor.inc"
private:
void AppendCatchBlock(uint32_t type_id, const compiler::BasicBlock *try_begin, const compiler::BasicBlock *try_end,
const compiler::BasicBlock *catch_begin, const compiler::BasicBlock *catch_end = nullptr);
void VisitTryBegin(const compiler::BasicBlock *bb);
private:
pandasm::Function *function_;
const BytecodeOptIrInterface *ir_interface_;
std::vector<pandasm::Ins> res_;
std::vector<pandasm::Function::CatchBlock> catch_blocks_;
bool success_ {true};
std::vector<pandasm::Ins> result_;
};
} // namespace panda::bytecodeopt
#endif // PANDA_BYTECODE_OPT_CODEGEN_H

View File

@ -0,0 +1,95 @@
/**
* 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.
// NOLINTNEXTLINE(readability-identifier-naming)
constexpr int32_t min = -8;
// NOLINTNEXTLINE(readability-identifier-naming)
constexpr int32_t max = 7;
int32_t imm = binop->GetImm() & BITMASK;
// Note: subi 3 is the same as inci v2, -3.
if (binop->GetOpcode() == compiler::Opcode::SubI) {
imm = -imm;
}
// IncI works only with 4 bits immediates.
return imm >= min && imm <= max;
}
} // namespace panda::bytecodeopt

View File

@ -0,0 +1,40 @@
/**
* 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;
[[maybe_unused]] static constexpr compiler::Register MAX_8_BIT_REG = 255 - 1U; // exclude INVALID_REG
// Get the position where accumulator read happens.
uint8_t AccReadIndex(const compiler::Inst *inst);
bool CanConvertToIncI(const compiler::BinaryImmOperation *binop);
} // namespace panda::bytecodeopt
#endif // PANDA_BYTECODE_OPTIMIZER_COMMON_H

View File

@ -0,0 +1,354 @@
/**
* 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 "const_array_resolver.h"
#include "compiler/optimizer/ir/basicblock.h"
#include "compiler/optimizer/optimizations/peepholes.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;
}
}
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);
if (constant_inst != nullptr) {
return constant_inst;
}
}
if (inst->IsConst()) {
return inst->CastToConstant();
}
return std::nullopt;
}
std::optional<std::vector<pandasm::LiteralArray::Literal>> ConstArrayResolver::FillLiteralArray(Inst *inst, size_t size)
{
std::vector<pandasm::LiteralArray::Literal> literals {size};
std::vector<Inst *> store_insts;
auto next = inst->GetNext();
size_t real_size = 0;
// are looking for instructions for uninterrupted filling the array
while (next != nullptr) {
// 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 == nullptr || store_array_inst->GetArray() != inst) {
break;
}
// get an index of the inserted element if possible
auto index_inst = store_array_inst->GetIndex();
auto index_const_inst = GetConstantIfPossible(index_inst);
if (!index_const_inst) {
return std::nullopt;
}
auto index = static_cast<size_t>((*index_const_inst)->GetIntValue());
if (index >= size) {
return std::nullopt;
}
pandasm::LiteralArray::Literal literal {};
// create a literal from the array element, if possible
if (!FillLiteral(store_array_inst, &literal)) {
// if not, then we can't create a constant literal array
return std::nullopt;
}
// checks if there is free space on the [index] position in the vector
pandasm::LiteralArray::Literal default_literal {};
if (literals[index] == default_literal) {
real_size++;
}
literals[index] = literal;
store_insts.push_back(next);
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 (real_size != 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;
}
bool ConstArrayResolver::FindConstantArrays()
{
size_t init_size = ir_interface_->GetLiteralArrayTableSize();
for (auto bb : GetGraph()->GetBlocksRPO()) {
// go through the instructions of the basic block in reverse order
// until we meet the instruction for storing an array element
auto inst = bb->GetLastInst();
while ((inst != nullptr) && IsSameBB(inst, bb)) {
if (inst->GetOpcode() != Opcode::StoreArray) {
inst = inst->GetPrev();
continue;
}
// the patch for creating and filling an array should start with the NewArray instruction
auto array_inst = inst->CastToStoreArray()->GetArray();
if (array_inst->GetOpcode() != Opcode::NewArray) {
inst = inst->GetPrev();
continue;
}
auto new_array_inst = array_inst->CastToNewArray();
// the instructions included in the patch must be in one basic block
if (!IsSameBB(inst, new_array_inst)) {
inst = inst->GetPrev();
continue;
}
// TODO(aantipina): add the ability to save multidimensional arrays
if (IsMultidimensionalArray(new_array_inst)) {
if (IsSameBB(inst, new_array_inst)) {
inst = new_array_inst->GetPrev();
} else {
inst = inst->GetPrev();
}
continue;
}
auto array_size_inst =
GetConstantIfPossible(new_array_inst->GetInput(compiler::NewArrayInst::INDEX_SIZE).GetInst());
if (array_size_inst == std::nullopt) {
inst = new_array_inst->GetPrev();
continue;
}
auto array_size = (*array_size_inst)->CastToConstant()->GetIntValue();
if (array_size < MIN_ARRAY_ELEMENTS_AMOUNT) {
inst = new_array_inst->GetPrev();
continue;
}
// creating a literal array, if possible
auto raw_literal_array = FillLiteralArray(new_array_inst, array_size);
if (raw_literal_array == std::nullopt) {
inst = new_array_inst->GetPrev();
continue;
}
pandasm::LiteralArray literal_array(*raw_literal_array);
// save the type and length of the array in the first two elements
AddIntroLiterals(&literal_array);
auto id = ir_interface_->GetLiteralArrayTableSize();
ir_interface_->StoreLiteralArray(std::to_string(id), std::move(literal_array));
// save the NewArray instructions for replacing them with LoadConst instructions later
const_arrays_init_.emplace(id, new_array_inst);
inst = new_array_inst->GetPrev();
}
}
// the pass worked if the size of the literal array table increased
return init_size < ir_interface_->GetLiteralArrayTableSize();
}
void ConstArrayResolver::RemoveArraysFill()
{
for (const auto &it : const_arrays_fill_) {
for (const auto &store_inst : it.second) {
store_inst->GetBasicBlock()->RemoveInst(store_inst);
}
}
}
void ConstArrayResolver::InsertLoadConstArrayInsts()
{
for (const auto &[id, start_inst] : const_arrays_init_) {
auto method = GetGraph()->GetMethod();
compiler::LoadConstArrayInst *new_inst = GetGraph()->CreateInstLoadConstArray(REFERENCE, start_inst->GetPc());
new_inst->SetTypeId(id);
new_inst->SetMethod(method);
start_inst->ReplaceUsers(new_inst);
start_inst->RemoveInputs();
compiler::SaveStateInst *save_state = GetGraph()->CreateInstSaveState();
save_state->SetPc(start_inst->GetPc());
save_state->SetMethod(method);
save_state->ReserveInputs(0);
new_inst->SetInput(0, save_state);
start_inst->InsertBefore(save_state);
start_inst->GetBasicBlock()->ReplaceInst(start_inst, new_inst);
}
}
static bool FillPrimitiveLiteral(pandasm::LiteralArray::Literal *literal, panda_file::Type::TypeId type,
compiler::ConstantInst *value_inst)
{
auto tag = pandasm::LiteralArray::GetArrayTagFromComponentType(type);
literal->tag = tag;
switch (tag) {
case panda_file::LiteralTag::ARRAY_U1:
literal->value = static_cast<bool>(value_inst->GetInt32Value());
return true;
case panda_file::LiteralTag::ARRAY_U8:
case panda_file::LiteralTag::ARRAY_I8:
literal->value = static_cast<uint8_t>(value_inst->GetInt32Value());
return true;
case panda_file::LiteralTag::ARRAY_U16:
case panda_file::LiteralTag::ARRAY_I16:
literal->value = static_cast<uint16_t>(value_inst->GetInt32Value());
return true;
case panda_file::LiteralTag::ARRAY_U32:
case panda_file::LiteralTag::ARRAY_I32:
literal->value = value_inst->GetInt32Value();
return true;
case panda_file::LiteralTag::ARRAY_U64:
case panda_file::LiteralTag::ARRAY_I64:
literal->value = value_inst->GetInt64Value();
return true;
case panda_file::LiteralTag::ARRAY_F32:
literal->value = value_inst->GetFloatValue();
return true;
case panda_file::LiteralTag::ARRAY_F64:
literal->value = value_inst->GetDoubleValue();
return true;
default:
UNREACHABLE();
}
return false;
}
bool ConstArrayResolver::FillLiteral(compiler::StoreInst *store_array_inst, pandasm::LiteralArray::Literal *literal)
{
if (store_array_inst->GetInputsCount() > STOREARRAY_INPUTS_NUM) {
return false;
}
auto raw_elem_inst = store_array_inst->GetStoredValue();
auto new_array_inst = store_array_inst->GetArray();
auto array_type =
pandasm::Type::FromName(ir_interface_->GetTypeIdByOffset(new_array_inst->CastToNewArray()->GetTypeId()));
auto component_type = array_type.GetComponentType();
auto component_type_name = array_type.GetComponentName();
if (pandasm::Type::IsPandaPrimitiveType(component_type_name)) {
auto value_inst = GetConstantIfPossible(raw_elem_inst);
if (value_inst == std::nullopt) {
return false;
}
return FillPrimitiveLiteral(literal, component_type.GetId(), *value_inst);
}
auto string_type =
pandasm::Type::FromDescriptor(panda::panda_file::GetStringClassDescriptor(ir_interface_->GetSourceLang()));
if ((raw_elem_inst->GetOpcode() == Opcode::LoadString) && (component_type_name == string_type.GetName())) {
literal->tag = panda_file::LiteralTag::ARRAY_STRING;
std::string string_value = ir_interface_->GetStringIdByOffset(raw_elem_inst->CastToLoadString()->GetTypeId());
literal->value = string_value;
return true;
}
return false;
}
} // namespace panda::bytecodeopt

View File

@ -0,0 +1,75 @@
/**
* 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;
// NOLINTNEXTLINE(google-build-using-namespace)
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;
NO_COPY_SEMANTIC(ConstArrayResolver);
NO_MOVE_SEMANTIC(ConstArrayResolver);
bool RunImpl() override;
const char *GetPassName() const override
{
return "ConstArrayResolver";
}
bool IsEnable() const override
{
return OPTIONS.IsConstArrayResolver();
}
private:
bool FindConstantArrays();
void RemoveArraysFill();
void InsertLoadConstArrayInsts();
std::optional<std::vector<pandasm::LiteralArray::Literal>> FillLiteralArray(Inst *inst, size_t size);
bool FillLiteral(compiler::StoreInst *store_array_inst, pandasm::LiteralArray::Literal *literal);
void AddIntroLiterals(pandasm::LiteralArray *lt_ar);
bool IsMultidimensionalArray(compiler::NewArrayInst *inst);
private:
BytecodeOptIrInterface *ir_interface_ {nullptr};
ArenaMap<uint32_t, compiler::NewArrayInst *> const_arrays_init_; // const_arrays_[literalarray_id] = new_array_inst
ArenaMap<Inst *, std::vector<Inst *>>
const_arrays_fill_; // const_arrays_[new_array_inst] = {store_array_inst_1, ... , store_array_inst_n}
};
} // namespace panda::bytecodeopt
#endif // PANDA_CONST_ARRAY_RESOLVER_H

View File

@ -0,0 +1,152 @@
/**
* 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;
NO_COPY_SEMANTIC(BytecodeOptIrInterface);
NO_MOVE_SEMANTIC(BytecodeOptIrInterface);
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_.empty()) {
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_.empty()) {
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(const std::string &id, pandasm::LiteralArray &&literalarray)
{
ASSERT(prog_ != nullptr);
if (prog_ == nullptr) {
return;
}
prog_->literalarray_table.emplace(id, std::move(literalarray));
}
size_t GetLiteralArrayTableSize() const
{
ASSERT(prog_ != nullptr);
if (prog_ == nullptr) {
return 0;
}
return prog_->literalarray_table.size();
}
bool IsMapsSet() const
{
return maps_ != nullptr;
}
panda_file::SourceLang GetSourceLang()
{
return prog_ != nullptr ? prog_->lang : panda_file::SourceLang::PANDA_ASSEMBLY;
}
private:
pandasm::Program *prog_ {nullptr};
const pandasm::AsmEmitter::PandaFileToPandaAsmMaps *maps_ {nullptr};
std::unordered_map<size_t, pandasm::Ins *> pc_ins_map_;
};
} // namespace panda::bytecodeopt
#endif // PANDA_IR_INTERFACE_H

View File

@ -0,0 +1,369 @@
/**
* Copyright (c) 2021-2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "optimize_bytecode.h"
#include "assembler/assembly-emitter.h"
#include "assembler/extensions/extensions.h"
#include "bytecode_instruction.h"
#include "bytecodeopt_options.h"
#include "bytecodeopt_peepholes.h"
#include "canonicalization.h"
#include "check_resolver.h"
#include "codegen.h"
#include "common.h"
#include "const_array_resolver.h"
#include "compiler/optimizer/ir/constants.h"
#include "compiler/optimizer/ir_builder/ir_builder.h"
#include "compiler/optimizer/ir_builder/pbc_iterator.h"
#include "compiler/optimizer/optimizations/branch_elimination.h"
#include "compiler/optimizer/optimizations/code_sink.h"
#include "compiler/optimizer/optimizations/cse.h"
#include "compiler/optimizer/optimizations/cleanup.h"
#include "compiler/optimizer/optimizations/if_merging.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>(false);
#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, 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>(false);
// 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::IfMerging, 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>(false);
graph->RunPass<RegAccAlloc>();
graph->RunPass<compiler::Cleanup>(false);
if (!RegAlloc(graph)) {
LOG(ERROR, BYTECODE_OPTIMIZER) << "Failed compiler::RegAlloc";
return false;
}
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, SourceLanguage lang)
{
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);
graph->SetLanguage(lang);
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};
auto lang = cda.GetSourceLang().value_or(SourceLanguage::PANDA_ASSEMBLY);
cda.EnumerateMethods([prog, maps, is_dynamic, lang, &result](panda_file::MethodDataAccessor &mda) {
if (!mda.IsExternal() && !mda.IsAbstract() && !mda.IsNative()) {
result = OptimizeFunction(prog, maps, mda, is_dynamic, lang) && result;
}
});
}
return result;
}
bool OptimizeBytecode(pandasm::Program *prog, const pandasm::AsmEmitter::PandaFileToPandaAsmMaps *maps,
const std::string &pandafile_name, bool is_dynamic, bool has_memory_pool)
{
ASSERT(prog != nullptr);
ASSERT(maps != nullptr);
if (!has_memory_pool) {
PoolManager::Initialize(PoolType::MALLOC);
}
auto res = OptimizePandaFile(prog, maps, pandafile_name, is_dynamic);
if (!has_memory_pool) {
PoolManager::Finalize();
}
return res;
}
} // namespace panda::bytecodeopt

View File

@ -0,0 +1,32 @@
/**
* 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"
#include "libpandabase/macros.h"
namespace panda::bytecodeopt {
PANDA_PUBLIC_API bool RunOptimizations(compiler::Graph *graph, BytecodeOptIrInterface *iface = nullptr);
PANDA_PUBLIC_API bool OptimizeBytecode(pandasm::Program *prog, const pandasm::AsmEmitter::PandaFileToPandaAsmMaps *maps,
const std::string &pandafile_name, bool is_dynamic = false,
bool has_memory_pool = false);
} // namespace panda::bytecodeopt
#endif // PANDA_BYTECODE_OPTIMIZER_OPTIMIZE_BYTECODE_H_

View File

@ -0,0 +1,58 @@
# Copyright (c) 2021-2022 Huawei Device Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
module:
name: bytecodeopt
namespace: panda::bytecodeopt
options:
- name: opt-level
type: int
default: 2
possible_values:
- 0
- 1
- 2
description: |
Optimization level for bytecode optimizer. N=0: No optimizations. N=1: New optimizer is turned on, only DCE is applied. N=2: (default): New optimizer is turned on, all compiler's optimizations are turned on.
- name: canonicalization
type: bool
default: true
description: Enable Canonicalization Pass
- name: const-resolver
type: bool
default: true
description: Enable Constant Resolver Pass
- name: method-regex
type: std::string
default: ""
description: A regular expression that specifies methods to optimize
- name: skip-methods-with-eh
type: bool
default: false
description: Disable optimizer for methods with exceptions handlers
- name: bytecode-opt-peepholes
type: bool
default: true
description: Enable BytecodeOptPeepholes Pass
- name: const-array-resolver
type: bool
default: true
description: Enable ConstArray Resolver Pass

View File

@ -0,0 +1,355 @@
/**
* 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()) {
if (!inst->IsConst()) {
return true;
}
if (inst->GetDstReg() == compiler::ACC_REG_ID) {
return true;
}
}
if (inst->IsAccRead()) {
compiler::Inst *input = inst->GetInput(AccReadIndex(inst)).GetInst();
if (input != src_inst && 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();
}
static bool IsCommutative(compiler::Inst *inst)
{
if (inst->GetOpcode() == compiler::Opcode::If) {
auto cc = inst->CastToIf()->GetCc();
return cc == compiler::ConditionCode::CC_EQ || cc == compiler::ConditionCode::CC_NE;
}
return inst->IsCommutative();
}
bool UserNeedSwapInputs(compiler::Inst *inst, compiler::Inst *user)
{
if (!IsCommutative(user)) {
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;
}
}
for (auto &input : user->GetInputs()) {
auto input_inst = input.GetInst();
if (input_inst != user && input_inst->GetDstReg() == compiler::ACC_REG_ID) {
return false;
}
if (inst->IsPhi() && input_inst->IsConst() &&
(input_inst->GetBasicBlock()->GetId() != user->GetBasicBlock()->GetId())) {
return false;
}
}
if (user->IsLaunchCall()) {
return false;
}
if (user->IsCall()) {
return user->GetInputsCount() <= (MAX_NUM_NON_RANGE_ARGS + 1); // +1 for SaveState
}
return user->GetInput(AccReadIndex(user)).GetInst() == inst || IsCommutative(user);
}
/**
* 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);
}
static inline bool MaybeRegDst(compiler::Inst *inst)
{
compiler::Opcode opcode = inst->GetOpcode();
return inst->IsConst() || inst->IsBinaryInst() || inst->IsBinaryImmInst() || opcode == compiler::Opcode::LoadObject;
}
/**
* 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 (MaybeRegDst(inst)) {
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 (MaybeRegDst(inst)) {
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 (MaybeRegDst(input)) {
input->ClearFlag(compiler::inst_flags::ACC_WRITE);
for (auto &user : input->GetUsers()) {
compiler::Inst *uinst = user.GetInst();
SetNeedLda(uinst, true);
}
}
}
}
}
#ifndef NDEBUG
GetGraph()->SetRegAccAllocApplied();
#endif // NDEBUG
return true;
}
} // namespace panda::bytecodeopt

View File

@ -0,0 +1,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.
*/
#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;
NO_COPY_SEMANTIC(RegAccAlloc);
NO_MOVE_SEMANTIC(RegAccAlloc);
bool IsEnable() const override
{
return compiler::OPTIONS.IsCompilerRegAccAlloc();
}
const char *GetPassName() const override
{
return "RegAccAlloc";
}
bool RunImpl() override;
private:
bool IsPhiOptimizable(compiler::Inst *phi) const;
bool IsAccRead(compiler::Inst *inst) const;
bool IsAccWrite(compiler::Inst *inst) const;
bool CanUserReadAcc(compiler::Inst *inst, compiler::Inst *user) const;
bool IsPhiAccReady(compiler::Inst *phi) const;
void SetNeedLda(compiler::Inst *inst, bool need);
compiler::Marker acc_marker_ {0};
};
} // namespace panda::bytecodeopt
#endif // PANDA_REG_ACC_ALLOC_H_

View File

@ -0,0 +1,735 @@
/**
* 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::CallLaunchStatic:
case compiler::Opcode::CallLaunchVirtual:
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 (inst->GetOpcode() == compiler::Opcode::CallLaunchVirtual ||
inst->GetOpcode() == compiler::Opcode::CallLaunchStatic) {
ASSERT(nargs > 0);
nargs -= 1; // exclude NewObject
}
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) {
break;
}
}
num_temps_ = num_temps_ > 0 ? num_temps_ : num_changed_width_;
if (num_temps_ > 0 || num_max_range_input_ > 0) {
state_ = RegEncoderState::INSERT_SPILLS;
InsertSpills();
auto usage_mask = GetGraph()->GetUsedRegs<compiler::DataType::INT64>();
for (compiler::Register r = 0; r < num_regs; r++) {
usage_mask->at(num_regs + num_temps_ - r - 1) = usage_mask->at(num_regs - r - 1);
}
std::fill(usage_mask->begin(), usage_mask->begin() + num_temps_, true);
}
SaveNumLocalsToGraph(GetNumLocalsFromGraph() + num_temps_);
state_ = RegEncoderState::IDLE;
return true;
}
static panda::compiler::DataType::Type GetRegType(panda::compiler::DataType::Type type)
{
if (type == panda::compiler::DataType::REFERENCE) {
return type;
}
if (panda::compiler::DataType::Is32Bits(type, Arch::NONE)) {
return panda::compiler::DataType::UINT32;
}
return panda::compiler::DataType::UINT64;
}
static bool RegNeedsRenumbering(panda::compiler::Register r)
{
return r != panda::compiler::ACC_REG_ID && r != panda::compiler::INVALID_REG;
}
static panda::compiler::Register RenumberReg(const panda::compiler::Register r, const panda::compiler::Register delta)
{
if (r == panda::compiler::ACC_REG_ID) {
return r;
}
return r + delta;
}
static void RenumberSpillFillRegs(panda::compiler::SpillFillInst *inst, const panda::compiler::Register min_reg,
const panda::compiler::Register delta)
{
for (auto &sf : inst->GetSpillFills()) {
if (sf.SrcType() == compiler::LocationType::REGISTER && sf.SrcValue() >= min_reg) {
sf.SetSrc(compiler::Location::MakeRegister(RenumberReg(sf.SrcValue(), delta)));
}
if (sf.DstType() == compiler::LocationType::REGISTER && sf.DstValue() >= min_reg) {
sf.SetDst(compiler::Location::MakeRegister(RenumberReg(sf.DstValue(), delta)));
}
}
}
void RegEncoder::RenumberRegs(const compiler::Register min_reg, const compiler::Register delta)
{
// Renumbering always advances register number `delta` positions forward,
// wrapping around on overflows with well-defined behavour.
// Hence the requirement to keep delta unsigned.
static_assert(std::is_unsigned<compiler::Register>::value, "compiler::Register must be unsigned");
ASSERT(delta > 0);
for (auto *bb : GetGraph()->GetBlocksRPO()) {
for (auto inst : bb->AllInsts()) {
// Renumber output of any instruction, if applicable:
if (RegNeedsRenumbering(inst->GetDstReg()) && inst->GetDstReg() >= min_reg) {
inst->SetDstReg(RenumberReg(inst->GetDstReg(), delta));
}
if (inst->IsPhi() || inst->IsCatchPhi()) {
continue;
}
// Renumber inputs and outputs of SpillFill instructions:
if (inst->IsSpillFill()) {
RenumberSpillFillRegs(inst->CastToSpillFill(), min_reg, delta);
continue;
}
// Fix inputs of common instructions:
for (size_t i = 0; i < inst->GetInputsCount(); i++) {
if (RegNeedsRenumbering(inst->GetSrcReg(i)) && inst->GetSrcReg(i) >= min_reg) {
inst->SetSrcReg(i, RenumberReg(inst->GetSrcReg(i), delta));
}
}
}
}
}
bool RegEncoder::RenumberArgRegs()
{
const auto usage_mask = GetGraph()->GetUsedRegs<compiler::DataType::INT64>();
ASSERT(usage_mask->size() == compiler::VIRTUAL_FRAME_SIZE);
auto frame_size = static_cast<compiler::Register>(usage_mask->size());
const auto num_args = GetNumArgsFromGraph();
ASSERT(frame_size >= num_args);
auto num_non_args = static_cast<compiler::Register>(frame_size - num_args);
if (num_max_range_input_ > num_non_args) {
LOG(DEBUG, BYTECODE_OPTIMIZER) << "RegEncoder: The free regs for range call are not enough";
return false;
}
compiler::Register num_locals = 0;
if (num_non_args != 0) {
while (num_locals != num_non_args && usage_mask->at(num_locals)) {
++num_locals;
}
}
compiler::Register num_temps = 0;
if (num_locals != num_non_args) {
compiler::Register r = num_non_args - 1;
while (r < num_non_args && usage_mask->at(r)) {
++num_temps;
--r;
}
}
if (num_locals + num_temps > num_non_args - num_max_range_input_) {
LOG(DEBUG, BYTECODE_OPTIMIZER) << "RegEncoder: The free regs for range call are not enough";
return false;
}
range_temps_start_ = num_locals;
bool do_renumber = true;
if (num_non_args == 0 && num_max_range_input_ == 0) { // all registers are arguments: no need to renumber
do_renumber = false;
}
// All free regs will be just enough to encode call.rang: no need to renumber
if (num_locals + num_temps + num_max_range_input_ == num_non_args) {
do_renumber = false;
}
if (num_temps + num_args == 0) { // no temps and no args: nothing to renumber
do_renumber = false;
}
if (do_renumber) {
const auto min_reg = static_cast<compiler::Register>(num_non_args - num_temps);
ASSERT(min_reg > MIN_REGISTER_NUMBER);
// Assert that if temps are present, they are marked allocated in the mask:
for (compiler::Register r = min_reg; r < min_reg + num_temps; r++) {
ASSERT(usage_mask->at(r));
}
// Assert that there are no used regs between locals and temps + arguments:
for (compiler::Register r = num_locals; r < min_reg; r++) {
ASSERT(!usage_mask->at(r));
}
auto delta = static_cast<compiler::Register>(num_locals + num_temps + num_max_range_input_ - num_non_args);
RenumberRegs(min_reg, delta);
for (compiler::Register r = min_reg; r < frame_size; r++) {
usage_mask->at(RenumberReg(r, delta)) = usage_mask->at(r);
usage_mask->at(r) = false;
}
}
SaveNumLocalsToGraph(num_locals + num_temps + num_max_range_input_);
return true;
}
void RegEncoder::InsertSpills()
{
ASSERT(num_max_range_input_ > 0 || (num_temps_ > 0 && num_temps_ <= MAX_NUM_INPUTS));
for (auto *bb : GetGraph()->GetBlocksRPO()) {
for (auto inst : bb->AllInstsSafe()) {
if (inst->GetInputsCount() == 0) {
continue;
}
VisitInstruction(inst);
// TODO(aantipina): Enable assert here for GetStatus() as soon code generation is fully supported
}
}
}
void RegEncoder::CalculateNumNeededTemps()
{
num_temps_ = 0;
for (auto bb : GetGraph()->GetBlocksRPO()) {
for (auto inst : bb->AllInstsSafe()) {
if (inst->GetInputsCount() == 0) {
continue;
}
VisitInstruction(inst);
// TODO(aantipina): Enable here for GetStatus() as soon code generation is fully supported
}
}
LOG(DEBUG, BYTECODE_OPTIMIZER) << GetGraph()->GetRuntime()->GetMethodFullName(GetGraph()->GetMethod())
<< ": num_temps_ = " << std::to_string(num_temps_);
}
template <typename T>
static void AddMoveBefore(Inst *inst, const T &sp_container)
{
if (sp_container.empty()) {
return;
}
auto sf_inst = inst->GetBasicBlock()->GetGraph()->CreateInstSpillFill();
for (auto const &[src, dst] : sp_container) {
ASSERT(src != compiler::ACC_REG_ID);
sf_inst->AddMove(src, dst.reg, GetRegType(dst.type));
LOG(DEBUG, BYTECODE_OPTIMIZER) << "RegEncoder: Move v" << static_cast<int>(dst.reg) << " <- v"
<< static_cast<int>(src) << " was added";
}
inst->GetBasicBlock()->InsertBefore(sf_inst, inst);
}
static bool IsAccReadPosition(compiler::Inst *inst, size_t pos)
{
// Calls can have accumulator at any position, return false for them
return !inst->IsCall() && inst->IsAccRead() && pos == AccReadIndex(inst);
}
void RegEncoder::InsertSpillsForDynInputsInst(compiler::Inst *inst)
{
ASSERT(state_ == RegEncoderState::INSERT_SPILLS);
ASSERT(inst->IsStaticCall() || inst->IsVirtualCall() || inst->IsInitObject() || inst->IsIntrinsic());
RegContentMap spill_map(GetGraph()->GetLocalAllocator()->Adapter()); // src -> (dst, src_type), non-callrange
RegContentVec spill_vec(GetGraph()->GetLocalAllocator()->Adapter()); // spill_vec is used to handle callrange
auto nargs = inst->GetInputsCount() - (inst->RequireState() ? 1 : 0);
auto start = GetStartInputIndex(inst);
bool range = IsIntrinsicRange(inst) || (nargs - start > MAX_NUM_NON_RANGE_ARGS && CanHoldRange(inst));
compiler::Register temp = range ? range_temps_start_ : 0;
for (size_t i = start; i < nargs; ++i) {
auto src_reg = inst->GetSrcReg(i);
auto type = inst->GetInputType(i);
// do not spillfill for acc-read position. For example, Intrinsic.FSTARR32
if (IsAccReadPosition(inst, i)) {
continue;
}
if (!range) {
if (!RegNeedsRenumbering(src_reg) || src_reg < NUM_COMPACTLY_ENCODED_REGS) {
continue;
}
auto res = spill_map.emplace(src_reg, RegContent(temp, type));
if (res.second) {
inst->SetSrcReg(i, temp++);
} else {
// Such register is already in map.
// It can be ok for cases like: CallStatic v49, v49
// Such instructions can be generated by optimizer too.
const RegContent &reg_cont = res.first->second;
inst->SetSrcReg(i, reg_cont.reg);
}
} else {
spill_vec.emplace_back(src_reg, RegContent(temp, type));
inst->SetSrcReg(i, temp++);
}
}
AddMoveBefore(inst, spill_map);
AddMoveBefore(inst, spill_vec);
}
size_t RegEncoder::GetStartInputIndex(compiler::Inst *inst)
{
return (inst->GetOpcode() == compiler::Opcode::InitObject ||
inst->GetOpcode() == compiler::Opcode::CallLaunchStatic ||
inst->GetOpcode() == compiler::Opcode::CallLaunchVirtual)
? 1
: 0; // exclude LoadAndInitClass and NewObject
}
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 IsBoundDstSrc(const compiler::Inst *inst)
{
if (!inst->IsBinaryInst()) {
return false;
}
auto src0 = inst->GetSrcReg(0);
auto src1 = inst->GetSrcReg(1);
auto dst = inst->GetDstReg();
if (inst->IsCommutative()) {
return src0 == dst || src1 == dst;
}
return src0 == dst;
}
static bool IsMoveAfter(const compiler::Inst *inst)
{
auto writes_to_dest = inst->GetDstReg() != compiler::ACC_REG_ID;
if (inst->IsBinaryImmInst()) {
return writes_to_dest;
}
if (inst->IsBinaryInst()) {
return writes_to_dest && IsBoundDstSrc(inst);
}
switch (inst->GetOpcode()) {
case compiler::Opcode::LoadObject:
// Special case for LoadObject, because it can be register instruction.
return writes_to_dest;
case compiler::Opcode::NewArray:
return true;
default:
return false;
}
}
void RegEncoder::InsertSpillsForInst(compiler::Inst *inst)
{
ASSERT(state_ == RegEncoderState::INSERT_SPILLS);
RegContentMap spill_map(GetGraph()->GetLocalAllocator()->Adapter()); // src -> (dst, src_type)
if (inst->IsOperandsDynamic()) {
InsertSpillsForDynInputsInst(inst);
return;
}
compiler::Register temp = 0;
for (size_t i = 0; i < inst->GetInputsCount(); i++) {
// TODO(mbolshov): make a better solution to skip instructions, that are not relevant to bytecode_opt
if (inst->GetInput(i).GetInst()->GetOpcode() == Opcode::LoadAndInitClass) {
continue;
}
auto reg = inst->GetSrcReg(i);
if (RegNeedsRenumbering(reg) && reg >= NUM_COMPACTLY_ENCODED_REGS) {
auto res = spill_map.emplace(reg, RegContent(temp, GetRegType(inst->GetInputType(i))));
if (res.second) {
inst->SetSrcReg(i, temp++);
} else {
// Such register is already in map.
// It can be ok for cases like: and v49, v49
// Such instructions can be generated by optimizer too.
const RegContent &reg_cont = res.first->second;
inst->SetSrcReg(i, reg_cont.reg);
}
}
}
AddMoveBefore(inst, spill_map);
if (IsMoveAfter(inst)) {
auto reg = inst->GetDstReg();
if (RegNeedsRenumbering(reg) && reg >= NUM_COMPACTLY_ENCODED_REGS) {
inst->SetDstReg(temp);
AddMoveAfter(inst, temp, RegContent(reg, GetRegType(inst->GetType())));
}
}
}
static void IncTempsIfNeeded(compiler::Inst *inst, const compiler::Register reg, compiler::Register &num_temps,
compiler::Register &num_changed_width)
{
if (RegNeedsRenumbering(reg) && reg >= NUM_COMPACTLY_ENCODED_REGS) {
num_temps++;
if (inst->IsBinaryInst()) {
num_changed_width++;
}
}
}
void RegEncoder::CalculateNumNeededTempsForInst(compiler::Inst *inst)
{
ASSERT(state_ == RegEncoderState::RESERVE_TEMPS);
compiler::Register num_temps = 0;
compiler::Register num_changed_width = 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++;
if (inst->IsBinaryInst()) {
num_changed_width++;
}
}
}
} 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, inst->GetSrcReg(i), num_temps, num_changed_width);
}
if (IsMoveAfter(inst) && !IsBoundDstSrc(inst)) {
IncTempsIfNeeded(inst, inst->GetDstReg(), num_temps, num_changed_width);
}
}
ASSERT(num_temps <= MAX_NUM_INPUTS);
num_temps_ = std::max(num_temps, num_temps_);
num_changed_width_ = std::max(num_changed_width, num_changed_width_);
}
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::VisitLoadStatic(GraphVisitor *v, Inst *inst_base)
{
auto re = static_cast<RegEncoder *>(v);
auto inst = inst_base->CastToLoadStatic();
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:
return;
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::VisitStoreStatic(GraphVisitor *v, Inst *inst_base)
{
auto re = static_cast<RegEncoder *>(v);
auto inst = inst_base->CastToStoreStatic();
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:
return;
default:
LOG(ERROR, BYTECODE_OPTIMIZER)
<< "Wrong DataType for " << compiler::GetOpcodeString(inst->GetOpcode()) << " failed";
re->success_ = false;
}
}
void RegEncoder::VisitSpillFill([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst) {}
void RegEncoder::VisitConstant([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst) {}
void RegEncoder::VisitLoadString([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst) {}
void RegEncoder::VisitReturn([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst) {}
void RegEncoder::VisitCatchPhi([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst) {}
void RegEncoder::VisitCastValueToAnyType([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst) {}
#include "generated/check_width.cpp"
} // namespace panda::bytecodeopt

View File

@ -0,0 +1,216 @@
/**
* 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 {
// NOLINTNEXTLINE(misc-non-private-member-variables-in-classes)
compiler::Register reg;
// NOLINTNEXTLINE(misc-non-private-member-variables-in-classes)
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;
// NOLINTNEXTLINE(fuchsia-multiple-inheritance)
class RegEncoder : public compiler::Optimization, public compiler::GraphVisitor {
public:
explicit RegEncoder(compiler::Graph *graph) : compiler::Optimization(graph) {}
~RegEncoder() override = default;
NO_COPY_SEMANTIC(RegEncoder);
NO_MOVE_SEMANTIC(RegEncoder);
// 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 VisitLoadStatic(GraphVisitor *v, Inst *inst_base);
static void VisitStoreObject(GraphVisitor *v, Inst *inst_base);
static void VisitStoreStatic(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(compiler::Register min_reg, 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_ {0};
RegEncoderState state_ {RegEncoderState::IDLE};
compiler::Register num_max_range_input_ {0};
compiler::Register range_temps_start_ {0};
compiler::Register num_changed_width_ {0};
bool success_ {true};
};
} // namespace panda::bytecodeopt
#endif // PANDA_REG_ENCODER_H

View File

@ -0,0 +1,459 @@
/**
* 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-inl.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;
NO_COPY_SEMANTIC(BytecodeOptimizerRuntimeAdapter);
NO_MOVE_SEMANTIC(BytecodeOptimizerRuntimeAdapter);
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()));
}
IdType GetMethodReturnTypeId(MethodPtr method) const override
{
panda_file::MethodDataAccessor mda(panda_file_, MethodCast(method));
panda_file::ProtoDataAccessor pda(panda_file_, mda.GetProtoId());
return pda.GetReferenceType(0).GetOffset();
}
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();
}
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<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 Native with exception
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, SourceLanguage lang) override
{
return GetMethodName(method) == panda_file::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()));
}
IdType GetFieldValueTypeId([[maybe_unused]] MethodPtr method, IdType id) const override
{
auto type_id = panda_file::FieldDataAccessor::GetTypeId(panda_file_, panda_file::File::EntityId(id));
return type_id.GetOffset();
}
bool IsFieldVolatile(FieldPtr field) const override
{
panda_file::FieldDataAccessor fda(panda_file_, FieldCast(field));
if (!fda.IsExternal()) {
return fda.IsVolatile();
}
auto field_id = panda_file::File::EntityId();
if (panda_file_.IsExternal(fda.GetClassId())) {
// If the field is external and class of the field is also external
// assume that field is volatile
return true;
}
auto class_id = panda_file::ClassDataAccessor(panda_file_, fda.GetClassId()).GetSuperClassId();
#ifndef NDEBUG
auto visited_classes = std::unordered_set<panda_file::File::EntityId> {class_id};
#endif
while (class_id.IsValid() && !panda_file_.IsExternal(class_id)) {
auto cda = panda_file::ClassDataAccessor(panda_file_, class_id);
cda.EnumerateFields([&field_id, &fda](panda_file::FieldDataAccessor &field_data_accessor) {
auto &pf = fda.GetPandaFile();
auto field_type = panda_file::Type::GetTypeFromFieldEncoding(fda.GetType());
if (fda.GetType() != field_data_accessor.GetType()) {
return;
}
if (pf.GetStringData(fda.GetNameId()) != pf.GetStringData(field_data_accessor.GetNameId())) {
return;
}
if (field_type.IsReference()) {
if (pf.GetStringData(panda_file::File::EntityId(fda.GetType())) !=
pf.GetStringData(panda_file::File::EntityId(field_data_accessor.GetType()))) {
return;
}
}
field_id = field_data_accessor.GetFieldId();
});
class_id = cda.GetSuperClassId();
#ifndef NDEBUG
ASSERT_PRINT(visited_classes.count(class_id) == 0, "Class hierarchy is incorrect");
visited_classes.insert(class_id);
#endif
}
if (!field_id.IsValid()) {
// If we cannot find field (for example it's in the class that located in other panda file)
// assume that field is volatile
return true;
}
ASSERT(field_id.IsValid());
panda_file::FieldDataAccessor field_da(panda_file_, field_id);
return field_da.IsVolatile();
}
ClassPtr ResolveType([[maybe_unused]] MethodPtr method, size_t id) const override
{
return reinterpret_cast<ClassPtr>(id);
}
std::string GetFieldName(FieldPtr field) const override
{
panda_file::FieldDataAccessor fda(panda_file_, FieldCast(field));
auto string_data = panda_file_.GetStringData(fda.GetNameId());
return utf::Mutf8AsCString(string_data.data);
}
private:
static compiler::DataType::Type ToCompilerType(panda_file::Type type)
{
switch (type.GetId()) {
case panda_file::Type::TypeId::VOID:
return compiler::DataType::VOID;
case panda_file::Type::TypeId::U1:
return compiler::DataType::BOOL;
case panda_file::Type::TypeId::I8:
return compiler::DataType::INT8;
case panda_file::Type::TypeId::U8:
return compiler::DataType::UINT8;
case panda_file::Type::TypeId::I16:
return compiler::DataType::INT16;
case panda_file::Type::TypeId::U16:
return compiler::DataType::UINT16;
case panda_file::Type::TypeId::I32:
return compiler::DataType::INT32;
case panda_file::Type::TypeId::U32:
return compiler::DataType::UINT32;
case panda_file::Type::TypeId::I64:
return compiler::DataType::INT64;
case panda_file::Type::TypeId::U64:
return compiler::DataType::UINT64;
case panda_file::Type::TypeId::F32:
return compiler::DataType::FLOAT32;
case panda_file::Type::TypeId::F64:
return compiler::DataType::FLOAT64;
case panda_file::Type::TypeId::REFERENCE:
return compiler::DataType::REFERENCE;
case panda_file::Type::TypeId::TAGGED:
case panda_file::Type::TypeId::INVALID:
return compiler::DataType::ANY;
default:
break;
}
UNREACHABLE();
}
static panda_file::File::EntityId MethodCast(RuntimeInterface::MethodPtr method)
{
return panda_file::File::EntityId(reinterpret_cast<uintptr_t>(method));
}
static panda_file::File::EntityId ClassCast(RuntimeInterface::ClassPtr cls)
{
return panda_file::File::EntityId(reinterpret_cast<uintptr_t>(cls));
}
static panda_file::File::EntityId FieldCast(RuntimeInterface::FieldPtr field)
{
return panda_file::File::EntityId(reinterpret_cast<uintptr_t>(field));
}
const panda_file::File &panda_file_;
};
} // namespace panda
#endif // PANDA_BYTECODE_OPTIMIZER_RUNTIME_ADAPTER_H_

View File

@ -0,0 +1,67 @@
# 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_obj
bytecode_optimizer_codegen_visitors_inc
bytecode_optimizer_reg_encoder_visitors_inc
bytecode_optimizer_codegen_intrinsics_cpp
)
add_dependencies(panda_gen_files
bytecode_optimizer_codegen_visitors_inc
bytecode_optimizer_reg_encoder_visitors_inc
bytecode_optimizer_codegen_intrinsics_cpp
)

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