diff --git a/assembler/BUILD.gn b/assembler/BUILD.gn index 979856a0fb0b059de04ac80591f5376838724142..5dbc48b36d92e62260936752d7eb9e76d601773a 100644 --- a/assembler/BUILD.gn +++ b/assembler/BUILD.gn @@ -17,6 +17,7 @@ config("arkassembler_public_config") { "$ark_root/assembler", "$target_gen_dir", "$target_gen_dir/include", + "$root_gen_dir", "$root_gen_dir/libpandabase", "$ark_root", ] @@ -84,6 +85,7 @@ source_set("libarkassembler_static") { "$ark_root/compiler:libarkcompiler", "$ark_root/libpandabase:libarkbase", "$ark_root/libpandafile:libarkfile", + "$ark_root/runtime:profiling_gen_profiling_gen_h", sdk_libc_secshared_dep, ] diff --git a/assembler/CMakeLists.txt b/assembler/CMakeLists.txt index 8b5c73723a3b46593237a76114237344172f654c..6575250f0f37b873cda827bf98b77f27abd6a7dd 100644 --- a/assembler/CMakeLists.txt +++ b/assembler/CMakeLists.txt @@ -60,6 +60,7 @@ add_dependencies(arkassembler isa_gen_assembler arkfile meta_gen_h + profiling_gen ) target_include_directories(arkassembler PUBLIC @@ -67,6 +68,7 @@ target_include_directories(arkassembler PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_BINARY_DIR}/libpandabase ${PANDA_ROOT} + ${CMAKE_BINARY_DIR} ) target_link_libraries(arkassembler arkfile) diff --git a/assembler/asm_isapi.rb b/assembler/asm_isapi.rb index ee19cab48c9fe7d31a5bd2db6906aa700786ac9d..e43988d7173ee577822b6e54bd645f2285879752 100755 --- a/assembler/asm_isapi.rb +++ b/assembler/asm_isapi.rb @@ -87,7 +87,7 @@ end # type - type of variable in emitter code def assembler_signature(group, is_jump) insn = group.first - format_ops(insn.format).each do |o| + 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] diff --git a/assembler/assembly-emitter.cpp b/assembler/assembly-emitter.cpp index e0e698f2c88e74070ffbc1e2b9e5ebf6aecf1243..6ceceb3c222ebe63411a00e98d294e4ebbb4efd5 100644 --- a/assembler/assembly-emitter.cpp +++ b/assembler/assembly-emitter.cpp @@ -15,16 +15,14 @@ #include "assembly-emitter.h" -#include -#include -#include -#include - -#include "bytecode_instruction-inl.h" #include "file_items.h" #include "file_writer.h" #include "mangling.h" #include "os/file.h" +#include "runtime/include/profiling_gen.h" + +#include +#include namespace { @@ -1392,6 +1390,7 @@ bool AsmEmitter::MakeFunctionDebugInfoAndAnnotations(ItemContainer *items, const if (!AddMethodAndParamsAnnotations(items, program, entities, method, func)) { return false; } + method->SetProfileSize(func.profile_size); } return true; } @@ -1596,6 +1595,39 @@ std::unique_ptr AsmEmitter::Emit(const Program &program, return panda_file::File::OpenFromMemory(std::move(ptr)); } +bool AsmEmitter::AssignProfileInfo(Program *program) +{ + std::unordered_map> inst_map; + constexpr auto SIZES = profiling::GetOrderedProfileSizes(); + + /** + * Since elements in the profile vector should be grouped by its size and ordered in + * descending order, we first save instructions in map, where key is a profile size. + * Then we iterate over this map in descending order - from biggest profile element size + * to smallest. And assign profile index to all instructions with same size. + */ + for (auto &func : program->function_table) { + for (auto &inst : func.second.ins) { + auto prof_size = INST_PROFILE_SIZES[static_cast(inst.opcode)]; + if (prof_size != 0) { + inst_map[prof_size].push_back(&inst); + } + } + size_t index = 0; + for (auto it = SIZES.rbegin(); it != SIZES.rend(); ++it) { + std::vector &vec = inst_map[*it]; + for (auto inst : vec) { + inst->profile_id = index; + index += *it; + } + vec.clear(); + } + + func.second.profile_size = index; + } + return true; +} + TypeItem *AsmEmitter::GetTypeItem( ItemContainer *items, const std::unordered_map &primitive_types, const Type &type, const Program &program) diff --git a/assembler/assembly-emitter.h b/assembler/assembly-emitter.h index 587cb678919e1a6eb5a66f6d7ea0aa2cf6b48e1f..20e60ca582a5d7247769703386b3688d17b0f634 100644 --- a/assembler/assembly-emitter.h +++ b/assembler/assembly-emitter.h @@ -64,6 +64,8 @@ public: static std::unique_ptr Emit(const Program &program, PandaFileToPandaAsmMaps *maps = nullptr); + static bool AssignProfileInfo(Program *program); + static std::string GetLastError() { return last_error_; diff --git a/assembler/assembly-function.h b/assembler/assembly-function.h index 7b88a67f9509bbddec063e97d9a44a0cbfd4f17a..0d5e58f0702731c1ef3e28897d91f1d74fa50498 100644 --- a/assembler/assembly-function.h +++ b/assembler/assembly-function.h @@ -82,6 +82,7 @@ struct Function { std::vector catch_blocks; int64_t value_of_first_param = -1; size_t regs_num = 0; + size_t profile_size {0}; std::vector params; bool body_presence = false; Type return_type; diff --git a/assembler/assembly-ins.h b/assembler/assembly-ins.h index 95ed783e91b4277ca0c0b7404794174908356862..d5916ee607ee1d09a68c657d0c1bc14d00941c17 100644 --- a/assembler/assembly-ins.h +++ b/assembler/assembly-ins.h @@ -33,7 +33,7 @@ namespace panda::pandasm { enum class Opcode { // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define OPLIST(opcode, name, optype, width, flags, def_idx, use_idxs) opcode, +#define OPLIST(opcode, name, optype, width, flags, def_idx, use_idxs, prof_size) opcode, PANDA_INSTRUCTION_LIST(OPLIST) #undef OPLIST INVALID, @@ -69,30 +69,36 @@ constexpr InstFlags operator|(InstFlags a, InstFlags b) } // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define OPLIST(opcode, name, optype, width, flags, def_idx, use_idxs) flags, +#define OPLIST(opcode, name, optype, width, flags, def_idx, use_idxs, prof_size) flags, constexpr std::array(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) width, +#define OPLIST(opcode, name, optype, width, flags, def_idx, use_idxs, prof_size) width, constexpr std::array(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) def_idx, +#define OPLIST(opcode, name, optype, width, flags, def_idx, use_idxs, prof_size) def_idx, constexpr std::array(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) use_idxs, +#define OPLIST(opcode, name, optype, width, flags, def_idx, use_idxs, prof_size) use_idxs, // clang-format off constexpr std::array, static_cast(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(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; @@ -110,6 +116,7 @@ struct Ins { 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 */ std::string ToString(std::string endline = "", bool print_args = false, size_t first_arg_idx = 0) const; diff --git a/assembler/lexer.cpp b/assembler/lexer.cpp index 148b63876ef70ef788da0950641d1f266dd105b1..3e9c156a255cca7ae4cf57a772fa0e272a809122 100644 --- a/assembler/lexer.cpp +++ b/assembler/lexer.cpp @@ -49,7 +49,7 @@ Token::Type FindOperation(std::string_view s) /* Generate the map of OPERATIONS from ISA: */ static const std::unordered_map OPERATIONS = { // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define OPLIST(inst_code, name, optype, width, flags, dst_idx, use_idxs) \ +#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 diff --git a/assembler/lexer.h b/assembler/lexer.h index 456f29f68d7de1a9911fecd52b46a232c86c7467..4c85e7e0ccdcf35c7ebf55371947aecfd2f69121 100644 --- a/assembler/lexer.h +++ b/assembler/lexer.h @@ -52,7 +52,8 @@ struct Token { OPERATION, /* special */ // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define OPLIST(inst_code, name, optype, width, flags, dst_idx, src_idxs) ID_OP_##inst_code, /* command type list */ +#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 */ diff --git a/assembler/templates/ins_create_api.h.erb b/assembler/templates/ins_create_api.h.erb index 4c07583b68837a95023ab96d5d626531132ecbf6..85b823c8e6283d6515fa6cee547b695b03dd6bf7 100644 --- a/assembler/templates/ins_create_api.h.erb +++ b/assembler/templates/ins_create_api.h.erb @@ -28,7 +28,7 @@ inline Ins Create_<%= insn.asm_token %>(<%= signature_str %>) { Ins <%=insn.emitter_name%>_; <%=group.first.emitter_name%>_.opcode = Opcode::<%= insn.asm_token %>; -% format = format_ops(insn.format) +% 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| @@ -57,5 +57,4 @@ inline Ins Create_<%= insn.asm_token %>(<%= signature_str %>) % end - } // namespace panda::pandasm diff --git a/assembler/templates/ins_emit.h.erb b/assembler/templates/ins_emit.h.erb index c0dfb0be4e25384837c9643ae8fe3e7054fc576a..c5aa20c1d9918136ff3309f6527ff68a9f3904cc 100644 --- a/assembler/templates/ins_emit.h.erb +++ b/assembler/templates/ins_emit.h.erb @@ -83,6 +83,7 @@ bool Ins::Emit(BytecodeEmitter& emitter, panda_file::MethodItem *method, % raise "Unexpected operand type" % end % end +% ops << "profile_id" if insn.profiled? % if insn.call? && insn.properties.include?('method_id') % ops << ops.shift % end diff --git a/assembler/templates/ins_to_string.cpp.erb b/assembler/templates/ins_to_string.cpp.erb index 7dc626f7a41bb9f1e011abee9fbe0a78e6858e42..ee6125ce9bbcfa418f49d008ab857a29bd152317 100644 --- a/assembler/templates/ins_to_string.cpp.erb +++ b/assembler/templates/ins_to_string.cpp.erb @@ -52,6 +52,11 @@ std::string panda::pandasm::Ins::ToString(std::string endline, bool print_args / 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 diff --git a/assembler/templates/isa.h.erb b/assembler/templates/isa.h.erb index bed0d0ee8e0f7f1cded1643aeb814468d105a2d0..4c75a3ef4cd7e4d280a4473aed29b35e55a834a3 100644 --- a/assembler/templates/isa.h.erb +++ b/assembler/templates/isa.h.erb @@ -42,17 +42,19 @@ % 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{{#{use_idxs.join(', ')}}})" % -_(<%= insn.asm_token %>, "<%= insn.mnemonic %>", <%= pretty_format %>, <%= max_width %>, <%= flags %>, <%= dst_idx %>, <%= use_idxs %>) \ +_(<%= 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{{#{use_idxs.join(', ')}}})" -_(<%= insn.opcode %> , "<%= insn.opcode %>", NONE, 0, (<%= insn.flags.join(" | ") %>), <%= insn.dst_idx %>, <%= use_idxs %>) \ +_(<%= insn.opcode %> , "<%= insn.opcode %>", NONE, 0, (<%= insn.flags.join(" | ") %>), <%= insn.dst_idx %>, <%= use_idxs %>, 0) \ % end diff --git a/bytecode_optimizer/runtime_adapter.h b/bytecode_optimizer/runtime_adapter.h index 992060a86a44fe6cd2952b2d17b2f94f8d1c1afd..2b8210ce62220e22232b9b1af2cab1da533a13e8 100644 --- a/bytecode_optimizer/runtime_adapter.h +++ b/bytecode_optimizer/runtime_adapter.h @@ -152,7 +152,7 @@ public: return cda.GetCodeSize(); } - compiler::SourceLanguage GetMethodSourceLanguage(MethodPtr method) const override + SourceLanguage GetMethodSourceLanguage(MethodPtr method) const override { panda_file::MethodDataAccessor mda(panda_file_, MethodCast(method)); @@ -161,7 +161,7 @@ public: auto source_lang = mda.GetSourceLang(); ASSERT(source_lang.has_value()); - return static_cast(source_lang.value()); + return static_cast(source_lang.value()); } size_t GetClassIdForField([[maybe_unused]] MethodPtr method, size_t field_id) const override @@ -268,7 +268,7 @@ public: lang = cda.GetSourceLang().value_or(lang); } - return GetMethodName(method) == GetCtorName(lang); + return GetMethodName(method) == panda_file::GetCtorName(lang); } std::string GetMethodFullName(MethodPtr method, bool /* with_signature */) const override diff --git a/cmake/Definitions.cmake b/cmake/Definitions.cmake index 88c3a52c0cdab718857f694bbbda32e9afead022..b8f9ffa3288b69a63922f3bb56d80a8422e2bbc1 100644 --- a/cmake/Definitions.cmake +++ b/cmake/Definitions.cmake @@ -343,6 +343,8 @@ if (PANDA_TARGET_ARM64) set(PANDA_COMPILER_TARGET_AARCH64 ON) endif() +option(PANDA_ENABLE_BYTECODE_PROFILING "Enable bytecode profiling" OFF) + panda_promote_to_definitions( PANDA_COMPILER_TARGET_X86 PANDA_COMPILER_TARGET_X86_64 @@ -350,6 +352,7 @@ panda_promote_to_definitions( PANDA_COMPILER_TARGET_AARCH64 PANDA_COMPILER_ENABLE PANDA_QEMU_BUILD + PANDA_ENABLE_BYTECODE_PROFILING ) message(STATUS "PANDA_TARGET_MOBILE_WITH_MANAGED_LIBS = ${PANDA_TARGET_MOBILE_WITH_MANAGED_LIBS}") @@ -393,3 +396,4 @@ message(STATUS "PANDA_PGO_OPTIMIZE = ${PANDA_PGO_OPTIMIZE}") message(STATUS "PANDA_PRODUCT_BUILD = ${PANDA_PRODUCT_BUILD}") message(STATUS "PANDA_ENABLE_RELAYOUT_PROFILE = ${PANDA_ENABLE_RELAYOUT_PROFILE}") message(STATUS "PANDA_QEMU_BUILD = ${PANDA_QEMU_BUILD}") +message(STATUS "PANDA_ENABLE_BYTECODE_PROFILING = ${PANDA_ENABLE_BYTECODE_PROFILING}") diff --git a/cmake/PostPlugins.cmake b/cmake/PostPlugins.cmake index 426082a8e081ad44b09241d7b96a51d35cbd7694..72aadd9ad367b167ed9199a226ac7b884cea1323 100644 --- a/cmake/PostPlugins.cmake +++ b/cmake/PostPlugins.cmake @@ -21,6 +21,24 @@ add_custom_command(OUTPUT ${GEN_PLUGIN_OPTIONS_YAML} ) add_custom_target(plugin_options_merge DEPENDS ${GEN_PLUGIN_OPTIONS_YAML}) +get_target_property(MERGE_PLUGINS merge_plugins PLUGINS) +foreach(plugin_file ${MERGE_PLUGINS}) + set(PLUGINS_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/plugins) + + string(REGEX REPLACE "[/\\.]" "_" plugin_target ${plugin_file}) + set(plugin_target_files "${plugin_target}__files") + + get_target_property(PLUGIN_FILES ${plugin_target_files} PLUGIN_FILES) + + add_custom_command(OUTPUT ${PLUGINS_BINARY_DIR}/${plugin_file} + COMMAND cat ${PLUGIN_FILES} > ${PLUGINS_BINARY_DIR}/${plugin_file} + DEPENDS ${PLUGIN_FILES} + ) + + add_custom_target(${plugin_target} DEPENDS ${PLUGINS_BINARY_DIR}/${plugin_file}) + add_dependencies(merge_plugins ${plugin_target}) +endforeach() + include(assembler/extensions/AssemblerExtPostPlugins.cmake) include(bytecode_optimizer/templates/BytecodeOptPostPlugins.cmake) include(runtime/RuntimeEnableRelayoutPostPlugins.cmake) diff --git a/cmake/RegisterPlugins.cmake b/cmake/RegisterPlugins.cmake index 21b1b490c85f9fd5d925c5d961f21340ea08359d..77c5820f7d7abd84aef9d5aeb997790a9af8f77c 100644 --- a/cmake/RegisterPlugins.cmake +++ b/cmake/RegisterPlugins.cmake @@ -50,3 +50,60 @@ LISTPLUGINS(PLUGINS ${CMAKE_CURRENT_SOURCE_DIR}/plugins) foreach(plugin ${PLUGINS}) register_plugin(${plugin}) endforeach() + +# Merge Plugins machinery. Example below. +# +# ===== Code in core part ===== +# [some_component/CMakeLists.txt] +# declare_plugin_file("get_word_size.h") +# +# [some_core_file.cpp] +# void GetWordSize(Language lang) { +# switch (lang) { +# #include "plugins/get_word_size.h" +# } +# } +# +# ===== Code in some plugin ===== +# [CMakeLists.txt] +# add_merge_plugin(PLUGIN_NAME "get_word_size.h" INPUT_FILE "plugin_get_word_size.h") +# +# [plugin_get_word_size.h] +# case (Language::PLUGIN_LANGUAGE) +# return 4; +# break; +# +# After that, content of `plugins/get_word_size.h` file will be filled by all files passed via `add_to_embedded_plugin`. +# In our case it will be just one file `plugin_get_word_size.h`. + +add_custom_target(merge_plugins) +set_target_properties(merge_plugins PROPERTIES PLUGINS "") + +# Declare plugin file for merge_plugins machinery. +function(declare_plugin_file NAME) + string(REGEX REPLACE "[/\\.]" "_" plugin_files_target "${NAME}__files") + add_custom_target(${plugin_files_target}) + set_target_properties(${plugin_files_target} PROPERTIES PLUGIN_FILES "") + + get_target_property(PLUGINS merge_plugins PLUGINS) + list(APPEND PLUGINS ${NAME}) + set_target_properties(merge_plugins PROPERTIES PLUGINS "${PLUGINS}") + + add_dependencies(merge_plugins ${plugin_files_target}) +endfunction() + +# Add file to embedded plugin +# Arguments: +# [in] PLUGIN_NAME - name of the plugin, where file should be added +# [in] INPUT_FILE - path to file to be added to the plugin +function(add_merge_plugin) + set(prefix ARG) + set(singleValues PLUGIN_NAME INPUT_FILE) + cmake_parse_arguments(${prefix} "" "${singleValues}" "${multiValues}" ${ARGN}) + + string(REGEX REPLACE "[/\\.]" "_" plugin_files_target "${ARG_PLUGIN_NAME}__files") + + get_target_property(FILES ${plugin_files_target} PLUGIN_FILES) + list(APPEND FILES ${ARG_INPUT_FILE}) + set_target_properties(${plugin_files_target} PROPERTIES PLUGIN_FILES "${FILES}") +endfunction() diff --git a/cmake/TemplateBasedGen.cmake b/cmake/TemplateBasedGen.cmake index a36072985fa40d95a0df0e8100d4d62541336901..0b8cfedeeb67106a81db2cc70e879c8759323b4d 100644 --- a/cmake/TemplateBasedGen.cmake +++ b/cmake/TemplateBasedGen.cmake @@ -96,7 +96,7 @@ endfunction() # * EXTRA_DEPENDENCIES -- a list of files that should be considered as dependencies function(panda_isa_gen) - set(singlevalues SOURCE DESTINATION) + set(singlevalues SOURCE DESTINATION TARGET_NAME) set(multivalues TEMPLATES REQUIRES EXTRA_DEPENDENCIES) cmake_parse_arguments( ISA_GEN_ARG @@ -112,6 +112,7 @@ function(panda_isa_gen) panda_gen(DATA ${ISA_DATA} TEMPLATES ${ISA_GEN_ARG_TEMPLATES} SOURCE ${ISA_GEN_ARG_SOURCE} + TARGET_NAME ${ISA_GEN_ARG_TARGET_NAME} DESTINATION ${ISA_GEN_ARG_DESTINATION} REQUIRES ${ISA_GEN_ARG_REQUIRES} EXTRA_DEPENDENCIES ${ISA_GEN_ARG_EXTRA_DEPENDENCIES} diff --git a/compiler/CMakeLists.txt b/compiler/CMakeLists.txt index 54a4d43405deef9edd00e062c6d3996843988b66..8ad13f387b5a7837d78a036021acb905392d8530 100644 --- a/compiler/CMakeLists.txt +++ b/compiler/CMakeLists.txt @@ -260,6 +260,8 @@ add_dependencies(arkcompiler entrypoints_compiler_checksum_gen) add_dependencies(arkcompiler cross_values) add_dependencies(arkcompiler cpu_features_gen) add_dependencies(arkcompiler asm_defines_generator) +add_dependencies(arkcompiler profiling_gen) +add_dependencies(arkcompiler merge_plugins) if (PANDA_TARGET_MOBILE OR PANDA_TARGET_OHOS) add_dependencies(host_tools_depends arkcompiler) @@ -273,6 +275,7 @@ endif() target_include_directories(arkcompiler PUBLIC ${PANDA_ROOT} PUBLIC ${PANDA_ROOT}/runtime + PUBLIC ${PANDA_BINARY_ROOT} PUBLIC ${PANDA_BINARY_ROOT}/runtime/include PUBLIC ${PANDA_BINARY_ROOT}/cross_values PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/compiler/aot/aot_builder/BUILD.gn b/compiler/aot/aot_builder/BUILD.gn index 4f338da7cb1378af1f0f04db2e1f7b3cb0a826fa..1dc1f4702981a99a52616c4cd303d576d2b0b003 100644 --- a/compiler/aot/aot_builder/BUILD.gn +++ b/compiler/aot/aot_builder/BUILD.gn @@ -36,6 +36,7 @@ ohos_static_library("aotbuilder") { "$ark_root/compiler:arkcompiler_public_config", ":aotbuilder_public_config", sdk_libc_secshared_config, + "$ark_root/libpandabase:arkbase_public_config", "$ark_root/runtime:arkruntime_public_config", "$ark_root/libpandafile:arkfile_public_config", ] diff --git a/compiler/aot/aot_file.cpp b/compiler/aot/aot_file.cpp index ae47bff72c64e65ca894f6e8b985f6f9c6fd912d..e5cc248de434b064f4da257abe045cf75b0a741b 100644 --- a/compiler/aot/aot_file.cpp +++ b/compiler/aot/aot_file.cpp @@ -137,7 +137,8 @@ void AotFile::PatchTable(RuntimeInterface *runtime) for (size_t i = 0; i < static_cast(RuntimeInterface::IntrinsicId::COUNT); i++) { IntrinsicInst inst(Opcode::Intrinsic, static_cast(i)); // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) - table[i] = runtime->GetIntrinsicAddress(inst.IsRuntimeCall(), static_cast(i)); + table[i] = runtime->GetIntrinsicAddress(inst.IsRuntimeCall(), SourceLanguage::INVALID, + static_cast(i)); } } diff --git a/compiler/compiler.yaml b/compiler/compiler.yaml index 78fbfb61763396f9c78166faae796c81ea58f86f..84550e295d358475aac2a4d28d407d60c0e07c96 100644 --- a/compiler/compiler.yaml +++ b/compiler/compiler.yaml @@ -634,6 +634,11 @@ options: default: true description: Enable scalar replacement optimization +- name: compiler-profile + type: std::string + default: "" + description: Path to a file with profile information + events: - name: branch-elimination fields: diff --git a/compiler/compiler_logger.h b/compiler/compiler_logger.h index 147d9930be66b0903dde63477d6f854ba6cd2624..d2be25244326c51c136d1561c42594464dc1c76f 100644 --- a/compiler/compiler_logger.h +++ b/compiler/compiler_logger.h @@ -63,8 +63,19 @@ private: static std::bitset components_; }; +#ifndef NDEBUG + // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define COMPILER_LOG(level, comp) LOG(level, COMPILER) << "[" #comp "] " +#define COMPILER_LOG(level, comp) \ + CompilerLogger::IsComponentEnabled(CompilerLoggerComponents::comp) && LOG(level, COMPILER) << "[" #comp "] " + +#else + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define COMPILER_LOG(level, comp) \ + CompilerLogger::IsComponentEnabled(CompilerLoggerComponents::comp) && LOG(level, COMPILER) << "[" #comp "] " + +#endif } // namespace panda::compiler #endif // COMPILER_COMPILER_LOGGER_H_ diff --git a/compiler/optimizer/code_generator/codegen.cpp b/compiler/optimizer/code_generator/codegen.cpp index 25757a92df66d1456aa4408ca149430bc1df8bab..4a254f0e46c6fdf237667f5b580b90f9d87d1919 100644 --- a/compiler/optimizer/code_generator/codegen.cpp +++ b/compiler/optimizer/code_generator/codegen.cpp @@ -1099,8 +1099,8 @@ void Codegen::CallIntrinsic(Inst *inst, RuntimeInterface::IntrinsicId id) intptr_t offset = aot_data->GetEntrypointOffset(GetEncoder()->GetCursorOffset(), static_cast(id)); GetEncoder()->MakeCallAot(offset); } else { - GetEncoder()->MakeCall( - reinterpret_cast(GetRuntime()->GetIntrinsicAddress(inst->IsRuntimeCall(), id))); + GetEncoder()->MakeCall(reinterpret_cast(GetRuntime()->GetIntrinsicAddress( + inst->IsRuntimeCall(), GetRuntime()->GetMethodSourceLanguage(GetGraph()->GetMethod()), id))); } } @@ -2277,11 +2277,16 @@ void Codegen::SaveCallerRegisters(RegMask live_regs, VRegMask live_vregs, bool a live_regs &= GetCallerRegsMask(GetArch(), false); live_vregs &= GetCallerRegsMask(GetArch(), true); } - GetEncoder()->SaveRegisters(live_regs, false, GetFrameInfo()->GetCallersOffset(), base, - GetCallerRegsMask(GetArch(), false)); - GetEncoder()->SaveRegisters(live_vregs, true, GetFrameInfo()->GetFpCallersOffset(), base, - GetFrameInfo()->GetPositionedCallers() ? GetCallerRegsMask(GetArch(), true) - : RegMask()); + if (GetFrameInfo()->GetPushCallers()) { + GetEncoder()->PushRegisters(live_regs, false, GetTarget().SupportLinkReg()); + GetEncoder()->PushRegisters(live_vregs, true, GetTarget().SupportLinkReg()); + } else { + GetEncoder()->SaveRegisters(live_regs, false, GetFrameInfo()->GetCallersOffset(), base, + GetCallerRegsMask(GetArch(), false)); + GetEncoder()->SaveRegisters(live_vregs, true, GetFrameInfo()->GetFpCallersOffset(), base, + GetFrameInfo()->GetPositionedCallers() ? GetCallerRegsMask(GetArch(), true) + : RegMask()); + } } void Codegen::LoadCallerRegisters(RegMask live_regs, VRegMask live_vregs, bool adjust_regs) @@ -2297,10 +2302,15 @@ void Codegen::LoadCallerRegisters(RegMask live_regs, VRegMask live_vregs, bool a live_regs &= GetCallerRegsMask(GetArch(), false); live_vregs &= GetCallerRegsMask(GetArch(), true); } - GetEncoder()->LoadRegisters(live_regs, false, GetFrameInfo()->GetCallersOffset(), base, - GetCallerRegsMask(GetArch(), false)); - GetEncoder()->LoadRegisters(live_vregs, true, GetFrameInfo()->GetFpCallersOffset(), base, - GetCallerRegsMask(GetArch(), true)); + if (GetFrameInfo()->GetPushCallers()) { + GetEncoder()->PopRegisters(live_regs, false, GetTarget().SupportLinkReg()); + GetEncoder()->PopRegisters(live_vregs, true, GetTarget().SupportLinkReg()); + } else { + GetEncoder()->LoadRegisters(live_regs, false, GetFrameInfo()->GetCallersOffset(), base, + GetCallerRegsMask(GetArch(), false)); + GetEncoder()->LoadRegisters(live_vregs, true, GetFrameInfo()->GetFpCallersOffset(), base, + GetCallerRegsMask(GetArch(), true)); + } } bool Codegen::RegisterKeepCallArgument(CallInst *call_inst, Reg reg) diff --git a/compiler/optimizer/code_generator/frame_info.h b/compiler/optimizer/code_generator/frame_info.h index 2a377fb23beeec9b5049e540b51dbf06d903e425..652c3a01aa51d20888029f3e83da30e86b36371b 100644 --- a/compiler/optimizer/code_generator/frame_info.h +++ b/compiler/optimizer/code_generator/frame_info.h @@ -95,6 +95,7 @@ public: // AdjustSpReg - sub SP,#framesize in prologue and add SP,#framesize in epilogue. FRAME_INFO_FIELD(AdjustSpReg, bool); FRAME_INFO_FIELD(HasFloatRegs, bool); + FRAME_INFO_FIELD(PushCallers, bool); using PositionedCallers = BitField; using PositionedCallees = PositionedCallers::NextFlag; @@ -105,6 +106,9 @@ public: using SaveUnusedCalleeRegs = SetupFrame::NextFlag; using AdjustSpReg = SaveUnusedCalleeRegs::NextFlag; using HasFloatRegs = AdjustSpReg::NextFlag; + // Whether we need to push callers below stack during calls. Actually it is some kind of workaround, and we need + // to rework it and make better solution, e.g allocate size for callers in a frame and save them there. + using PushCallers = HasFloatRegs::NextFlag; // The following static 'constructors' are for situations // when we have to generate prologue/epilogue but there is diff --git a/compiler/optimizer/code_generator/operands.h b/compiler/optimizer/code_generator/operands.h index 94987122862de395b17bf97c2627f586893e6960..903a221de10ea521ed4c60d06ce0d449366c64b5 100644 --- a/compiler/optimizer/code_generator/operands.h +++ b/compiler/optimizer/code_generator/operands.h @@ -363,6 +363,7 @@ public: constexpr size_t GetMask() const { + CHECK_LT(id_, 32U); return (1U << id_); } diff --git a/compiler/optimizer/code_generator/slow_path.cpp b/compiler/optimizer/code_generator/slow_path.cpp index dd929191f996f02e26e88af1ea0b514a59a33be8..f5fbc912aca4fddab912b65f1bff2f0293c6ba9a 100644 --- a/compiler/optimizer/code_generator/slow_path.cpp +++ b/compiler/optimizer/code_generator/slow_path.cpp @@ -117,7 +117,8 @@ bool SlowPathEntrypoint::GenerateDeoptimize(Codegen *codegen) } else { UNREACHABLE(); } - codegen->CallRuntime(GetInst(), GetEntrypoint(), INVALID_REGISTER, {Imm(static_cast(type))}); + uintptr_t value = helpers::ToUnderlying(type) | (GetInst()->GetId() << MinimumBitsToStore(DeoptimizeType::COUNT)); + codegen->CallRuntime(GetInst(), GetEntrypoint(), INVALID_REGISTER, {Imm(value)}); return true; } diff --git a/compiler/optimizer/code_generator/target/aarch64/encode.cpp b/compiler/optimizer/code_generator/target/aarch64/encode.cpp index 74cc4a6733ed180acbdc8241799f2ad1a91dd4c5..a85a87b8295a75e81f5178a741de7dfee58b4b41 100644 --- a/compiler/optimizer/code_generator/target/aarch64/encode.cpp +++ b/compiler/optimizer/code_generator/target/aarch64/encode.cpp @@ -125,7 +125,6 @@ void Aarch64Encoder::EncodeJump(LabelHolder::LabelId id, Reg src, Imm imm, Condi return; } - ASSERT(CanEncodeImmAddSubCmp(value, src.GetSize(), false)); if (value < 0) { GetMasm()->Cmn(VixlReg(src), VixlImm(-value)); } else { // if (value > 0) @@ -254,16 +253,16 @@ void Aarch64Encoder::MakeCall([[maybe_unused]] compiler::RelocationInfo *relocat void Aarch64Encoder::MakeCall(const void *entry_point) { - auto lr_reg = GetTarget().GetLinkReg(); - EncodeMov(lr_reg, Imm(reinterpret_cast(entry_point))); - GetMasm()->Blr(VixlReg(lr_reg)); + ScopedTmpReg tmp(this, true); + EncodeMov(tmp, Imm(reinterpret_cast(entry_point))); + GetMasm()->Blr(VixlReg(tmp)); } void Aarch64Encoder::MakeCall(MemRef entry_point) { - auto lr_reg = GetTarget().GetLinkReg(); - EncodeLdr(lr_reg, false, entry_point); - GetMasm()->Blr(VixlReg(lr_reg)); + ScopedTmpReg tmp(this, true); + EncodeLdr(tmp, false, entry_point); + GetMasm()->Blr(VixlReg(tmp)); } void Aarch64Encoder::MakeCall(Reg reg) @@ -321,9 +320,9 @@ void Aarch64Encoder::LoadPcRelative(Reg reg, intptr_t offset, Reg reg_addr) void Aarch64Encoder::MakeCallAot(intptr_t offset) { - auto lr_reg = GetTarget().GetLinkReg(); - LoadPcRelative(lr_reg, offset); - GetMasm()->Blr(VixlReg(lr_reg)); + ScopedTmpReg tmp(this, true); + LoadPcRelative(tmp, offset); + GetMasm()->Blr(VixlReg(tmp)); } bool Aarch64Encoder::CanMakeCallByOffset(intptr_t offset) diff --git a/compiler/optimizer/code_generator/target/amd64/encode.cpp b/compiler/optimizer/code_generator/target/amd64/encode.cpp index b6c35ac41b00f18fe3672f6fe17bd257556e787f..3f9b569adc25e132d75e08c3e31b662ba15d3e59 100644 --- a/compiler/optimizer/code_generator/target/amd64/encode.cpp +++ b/compiler/optimizer/code_generator/target/amd64/encode.cpp @@ -2575,6 +2575,7 @@ size_t Amd64Encoder::DisasmInstr(std::ostream &stream, size_t pc, ssize_t code_o // Initialize formatter ZydisFormatter formatter; res &= ZYAN_SUCCESS(ZydisFormatterInit(&formatter, ZYDIS_FORMATTER_STYLE_ATT)); + ZydisFormatterSetProperty(&formatter, ZYDIS_FORMATTER_PROP_FORCE_RELATIVE_BRANCHES, 1); ASSERT(res); ZydisDecodedInstruction instruction; diff --git a/compiler/optimizer/ir/dump.cpp b/compiler/optimizer/ir/dump.cpp index 6cff4131bced1db5aa14d2bbc7478b8a73faef94..89eaeec776741b89c417b8feae446d8aca386d21 100644 --- a/compiler/optimizer/ir/dump.cpp +++ b/compiler/optimizer/ir/dump.cpp @@ -372,7 +372,8 @@ static void DumpOpcodeAnyTypeMixin(std::ostream &out, const Inst *inst) ArenaString space(" ", adapter); ArenaString opcode(GetOpcodeString(mixin_inst->GetOpcode()), adapter); ArenaString any_base_type(AnyTypeTypeToString(mixin_inst->GetAnyType()), adapter); - out << std::setw(INDENT_OPCODE) << opcode + space + any_base_type + space; + out << std::setw(INDENT_OPCODE) + << opcode + space + any_base_type + (mixin_inst->IsIntegerWasSeen() ? " i" : "") + space; } void CompareAnyTypeInst::DumpOpcode(std::ostream *out) const @@ -397,7 +398,7 @@ void AnyTypeCheckInst::DumpOpcode(std::ostream *out) const ArenaString space(" ", adapter); ArenaString opcode(GetOpcodeString(GetOpcode()), adapter); ArenaString any_base_type(AnyTypeTypeToString(GetAnyType()), adapter); - (*out) << std::setw(INDENT_OPCODE) << opcode + space + any_base_type + space; + (*out) << std::setw(INDENT_OPCODE) << opcode + space + any_base_type + (IsIntegerWasSeen() ? " i" : "") + space; } void ClassImmediateInst::DumpOpcode(std::ostream *out) const diff --git a/compiler/optimizer/ir/graph.h b/compiler/optimizer/ir/graph.h index 1a5bc4f4a8912b12b58e58c392c490cd162b67ad..b10e6babdf695497b49c3ba3690a2effbadca83d 100644 --- a/compiler/optimizer/ir/graph.h +++ b/compiler/optimizer/ir/graph.h @@ -317,7 +317,7 @@ public: bool IsOfflineCompilationMode() const { - return IsAotMode() || GetMode().IsInterpreter(); + return IsAotMode() || GetMode().IsInterpreter() || GetMode().IsFastPath(); } bool IsDefaultLocationsInit() const diff --git a/compiler/optimizer/ir/inst.h b/compiler/optimizer/ir/inst.h index ef2b3af7ea6b3a95ba81479964ad71bc331994fc..9e0c00e3f994c2f90a31797b09ba31c711069ec1 100644 --- a/compiler/optimizer/ir/inst.h +++ b/compiler/optimizer/ir/inst.h @@ -2786,10 +2786,21 @@ public: return T::template GetField(); } + bool IsIntegerWasSeen() const + { + return T::template GetField(); + } + + void SetIsIntegerWasSeen(bool value) + { + T::template SetField(value); + } + protected: using AnyBaseTypeField = typename T::LastField::template NextField; - using LastField = AnyBaseTypeField; + using IntegerWasSeen = typename AnyBaseTypeField::NextFlag; + using LastField = IntegerWasSeen; }; /** diff --git a/compiler/optimizer/ir/instructions.yaml b/compiler/optimizer/ir/instructions.yaml index 60c30219ff1df1cf4cea3000d52816ed9883add6..05f627df5bb2b7add5bf4f2a38bf2a234ed2fea6 100644 --- a/compiler/optimizer/ir/instructions.yaml +++ b/compiler/optimizer/ir/instructions.yaml @@ -107,6 +107,13 @@ instructions: verification: - $verify_binary + - opcode: MulOverflowCheck + base: FixedInputsInst3 + signature: [d-int, int, int, save_state] + flags: [no_dce, no_hoist, no_cse, barrier, acc_read, require_state, can_deoptimize] + modes: [jit_aot] + description: Mul two values, deoptimize if overflow occurred. + - opcode: Div base: BinaryOperation signature: [d-number, number, number-zc] diff --git a/compiler/optimizer/ir/runtime_interface.h b/compiler/optimizer/ir/runtime_interface.h index 57b31510b638b68b679a0e0bb4aac72fb8db56ff..0a7072e060391bac6b9c5455b2a88fdc1ec1cf2f 100644 --- a/compiler/optimizer/ir/runtime_interface.h +++ b/compiler/optimizer/ir/runtime_interface.h @@ -884,7 +884,8 @@ public: return static_cast(0); } - virtual uintptr_t GetIntrinsicAddress([[maybe_unused]] bool runtime_call, [[maybe_unused]] IntrinsicId unused) const + virtual uintptr_t GetIntrinsicAddress([[maybe_unused]] bool runtime_call, [[maybe_unused]] SourceLanguage lang, + [[maybe_unused]] IntrinsicId unused) const { return 0; } @@ -953,6 +954,28 @@ public: return false; } + /************************************************************************** + * Bytecode profiling + */ + using BytecodeProfile = uintptr_t; + using MethodProfile = uintptr_t; + + virtual MethodProfile GetMethodProfile([[maybe_unused]] MethodPtr method) const + { + return 0; + } + virtual BytecodeProfile GetBytecodeProfile([[maybe_unused]] MethodProfile prof, + [[maybe_unused]] const uint8_t *bc_inst, + [[maybe_unused]] size_t pc) const + { + return 0; + } + + virtual Expected AddProfile([[maybe_unused]] std::string_view fname) + { + return Unexpected("Not implemented"); + } + NO_COPY_SEMANTIC(RuntimeInterface); NO_MOVE_SEMANTIC(RuntimeInterface); @@ -1011,7 +1034,7 @@ enum class TraceId { TLAB_EVENT = 1U << 3U, }; -enum class DeoptimizeType { +enum class DeoptimizeType : uint8_t { INVALID = 0, INLINE_IC, INLINE_CHA, diff --git a/compiler/optimizer/ir_builder/inst_builder-inl.h b/compiler/optimizer/ir_builder/inst_builder-inl.h index 406c27f85669b85da7442354d3fc2a3aebde51a3..e30eccc66f4e7eb13ae9f299ec34a23e86fa2db7 100644 --- a/compiler/optimizer/ir_builder/inst_builder-inl.h +++ b/compiler/optimizer/ir_builder/inst_builder-inl.h @@ -627,12 +627,14 @@ Inst *InstBuilder::BuildLoadStaticInst(const BytecodeInstruction *bc_inst, DataT } // NOLINTNEXTLINE(misc-definitions-in-headers) -Inst *InstBuilder::BuildAnyTypeCheckInst(size_t bc_addr, Inst *input, Inst *save_state, AnyBaseType type) +Inst *InstBuilder::BuildAnyTypeCheckInst(size_t bc_addr, Inst *input, Inst *save_state, AnyBaseType type, + bool integer_was_seen) { auto any_check = graph_->CreateInstAnyTypeCheck(DataType::ANY, bc_addr); any_check->SetInput(0, input); any_check->SetInput(1, save_state); any_check->SetAnyType(type); + any_check->SetIsIntegerWasSeen(integer_was_seen); AddInstruction(any_check); return any_check; diff --git a/compiler/optimizer/ir_builder/inst_builder.cpp b/compiler/optimizer/ir_builder/inst_builder.cpp index 81eb7acd578ef57fd933c826b0c38a5a720744f8..d6b12133f7eb57c2bc360fa7cf2c0e035e073d7e 100644 --- a/compiler/optimizer/ir_builder/inst_builder.cpp +++ b/compiler/optimizer/ir_builder/inst_builder.cpp @@ -17,6 +17,7 @@ #include "phi_resolver.h" #include "optimizer/code_generator/encode.h" #include "compiler_logger.h" +#include "runtime/profiling/profiling.h" namespace panda::compiler { @@ -82,6 +83,7 @@ void InstBuilder::Prepare(bool is_inlined_graph) if (OPTIONS.IsCompilerUseSafepoint() && !is_inlined_graph) { GetGraph()->GetStartBlock()->AppendInst(CreateSafePoint(GetGraph()->GetStartBlock())); } + method_profile_ = GetRuntime()->GetMethodProfile(GetMethod()); } void InstBuilder::UpdateDefsForCatch() @@ -451,4 +453,19 @@ void InstBuilder::SyncWithGraph() current_defs_ = &defs_[current_bb_->GetId()]; } +compiler::AnyBaseType InstBuilder::GetProfilingAnyType([[maybe_unused]] RuntimeInterface::BytecodeProfile profile, + [[maybe_unused]] const BytecodeInstruction *bc_inst, + [[maybe_unused]] unsigned index, + [[maybe_unused]] bool *is_integer_seen) +{ + // NOLINTNEXTLINE(hicpp-multiway-paths-covered) + switch (GetRuntime()->GetMethodSourceLanguage(GetMethod())) { +#ifdef PANDA_ENABLE_BYTECODE_PROFILING +#include "plugins/get_profiling_any_type.h" +#endif + default: + return compiler::AnyBaseType::UNDEFINED_TYPE; + } +} + } // namespace panda::compiler diff --git a/compiler/optimizer/ir_builder/inst_builder.h b/compiler/optimizer/ir_builder/inst_builder.h index 8455fda693fdcfabe83c8bd05dcdebadf7b1083e..5fb6284877955833aa5eb5c5dd64f22e93bbec59 100644 --- a/compiler/optimizer/ir_builder/inst_builder.h +++ b/compiler/optimizer/ir_builder/inst_builder.h @@ -139,6 +139,10 @@ public: private: void SyncWithGraph(); + compiler::AnyBaseType GetProfilingAnyType(RuntimeInterface::BytecodeProfile profile, + const BytecodeInstruction *bc_inst, unsigned index, + bool *is_integer_seen); + void UpdateDefsForCatch(); void UpdateDefsForLoopHead(); @@ -347,7 +351,7 @@ private: void BuildCastToAnyString(const BytecodeInstruction *bc_inst); void BuildCastToAnyNumber(const BytecodeInstruction *bc_inst); Inst *BuildAnyTypeCheckInst(size_t bc_addr, Inst *input, Inst *save_state, - AnyBaseType type = AnyBaseType::UNDEFINED_TYPE); + AnyBaseType type = AnyBaseType::UNDEFINED_TYPE, bool integer_was_seen = false); #include "inst_builder_extensions.inl.h" Graph *GetGraph() @@ -417,6 +421,8 @@ private: RuntimeInterface *runtime_ {nullptr}; BasicBlock *current_bb_ {nullptr}; + RuntimeInterface::MethodProfile method_profile_ {}; + // Definitions vector of currently processed basic block InstVector *current_defs_ {nullptr}; // Contains definitions of the virtual registers in all basic blocks diff --git a/compiler/optimizer/templates/intrinsics/entrypoints_bridge_asm_macro.inl.erb b/compiler/optimizer/templates/intrinsics/entrypoints_bridge_asm_macro.inl.erb index 6292d9133fd8fc9606cc02987ccd6773f39e7c21..c5fd93e6f91e6d77b3e70c75e510a390d32dd212 100644 --- a/compiler/optimizer/templates/intrinsics/entrypoints_bridge_asm_macro.inl.erb +++ b/compiler/optimizer/templates/intrinsics/entrypoints_bridge_asm_macro.inl.erb @@ -31,9 +31,8 @@ BRIDGE_SELECTOR <%= implementation %>BridgeSelectorEntryPoint, <%= implementatio #ifndef NDEBUG % implementation = intrn.fast_path if intrn.respond_to?(:fast_path) % if intrn.respond_to?(:fast_path) -#if defined(PANDA_TARGET_AMD64) && !defined(PANDA_COMPILER_TARGET_X86_64) RUNTIME_CALL_CHECKER <%= intrn.impl.rpartition("::").last %>RuntimeCallChecker, <%= intrn.impl.rpartition("::").last %> -#else +#if !(defined(PANDA_TARGET_AMD64) && !defined(PANDA_COMPILER_TARGET_X86_64)) % end RUNTIME_CALL_CHECKER <%= implementation %>RuntimeCallChecker, <%= implementation %> % if intrn.respond_to?(:fast_path) diff --git a/compiler/optimizer/templates/intrinsics/get_intrinsics.inl.erb b/compiler/optimizer/templates/intrinsics/get_intrinsics.inl.erb index f64942b854dc961fb0743644ff811fdc09c7f3c7..863f40f949929298f7b84f0522fd8783c035a640 100644 --- a/compiler/optimizer/templates/intrinsics/get_intrinsics.inl.erb +++ b/compiler/optimizer/templates/intrinsics/get_intrinsics.inl.erb @@ -15,26 +15,32 @@ // Autogenerated file -- DO NOT EDIT! +#if defined(PANDA_TARGET_AMD64) && !defined(PANDA_COMPILER_TARGET_X86_64) + #define RUNTIME_HAS_NO_FASTPATH + static constexpr bool FASTPATH_ENABLED = false; +#else + static constexpr bool FASTPATH_ENABLED = true; +#endif + % Compiler::intrinsics.select(&:has_impl?).uniq{ |i| i.impl }.each do |intrinsic| % if intrinsic.private #ifndef PANDA_PRODUCT_BUILD % end % impl = intrinsic.respond_to?(:fast_path) ? intrinsic.fast_path : intrinsic.impl % if intrinsic.respond_to?(:fast_path) -#if defined(PANDA_TARGET_AMD64) && !defined(PANDA_COMPILER_TARGET_X86_64) extern "C" void <%= intrinsic.impl.rpartition('::').last %>Bridge(); extern "C" void <%= intrinsic.impl.rpartition('::').last %>RuntimeCallChecker(); -#else % end % if intrinsic.respond_to?(:fast_path) +#ifndef RUNTIME_HAS_NO_FASTPATH extern "C" void <%= impl.rpartition('::').last %>(); % else extern "C" void <%= impl.rpartition('::').last %>Bridge(); % end extern "C" void <%= impl.rpartition('::').last %>RuntimeCallChecker(); -% if intrinsic.respond_to?(:fast_path) +% if intrinsic.respond_to?(:fast_path) #endif -% end +% end % if intrinsic.private #endif // PANDA_PRODUCT_BUILD % end @@ -56,7 +62,8 @@ inline RuntimeInterface::IntrinsicId GetIntrinsicEntryPointId(intrinsics::Intrin } // NOLINTNEXTLINE(readability-function-size) -uintptr_t PandaRuntimeInterface::GetIntrinsicAddress(bool runtime_call, PandaRuntimeInterface::IntrinsicId id) const { +uintptr_t PandaRuntimeInterface::GetIntrinsicAddress(bool runtime_call, [[maybe_unused]] SourceLanguage lang, + [[maybe_unused]] PandaRuntimeInterface::IntrinsicId id) const { switch (id) { case IntrinsicId::LIB_CALL_FMOD: { using fp = double (*)(double, double); @@ -84,14 +91,15 @@ uintptr_t PandaRuntimeInterface::GetIntrinsicAddress(bool runtime_call, PandaRun #ifndef PANDA_PRODUCT_BUILD % end % if intrinsic.respond_to?(:fast_path) -#if defined(PANDA_TARGET_AMD64) && !defined(PANDA_COMPILER_TARGET_X86_64) - return runtime_call ? reinterpret_cast(<%= "#{intrinsic.impl.rpartition('::').last}Bridge" %>) : + if (!FASTPATH_ENABLED || !IsGcValidForFastPath(lang)) { + return runtime_call ? reinterpret_cast(<%= "#{intrinsic.impl.rpartition('::').last}Bridge" %>) : #ifdef NDEBUG - reinterpret_cast(<%= intrinsic.impl %>); + reinterpret_cast(<%= intrinsic.impl %>); #else - reinterpret_cast(<%= intrinsic.impl.rpartition('::').last %>RuntimeCallChecker); + reinterpret_cast(<%= intrinsic.impl.rpartition('::').last %>RuntimeCallChecker); #endif -#else + } +#ifndef RUNTIME_HAS_NO_FASTPATH % end return runtime_call ? reinterpret_cast(<%= bridge %>) : #ifdef NDEBUG diff --git a/compiler/optimizer/templates/ir-dyn-base-types.h.erb b/compiler/optimizer/templates/ir-dyn-base-types.h.erb index ac232797694053a365c284540fd0f034f15e71db..ae767c42eb978a65c6543f9bc94d2fb4ec677614 100644 --- a/compiler/optimizer/templates/ir-dyn-base-types.h.erb +++ b/compiler/optimizer/templates/ir-dyn-base-types.h.erb @@ -41,7 +41,7 @@ class AnyTypeCheckInst; class EncodeVisitor; inline AnyBaseType NumericDataTypeToAnyType([[maybe_unused]] panda::compiler::DataType::Type type, - panda::compiler::SourceLanguage language) { + panda::SourceLanguage language) { ASSERT(type == panda::compiler::DataType::Type::UINT8 || type == panda::compiler::DataType::Type::INT8 || type == panda::compiler::DataType::Type::UINT16 || type == panda::compiler::DataType::Type::INT16 || type == panda::compiler::DataType::Type::UINT32 || type == panda::compiler::DataType::Type::INT32 || @@ -53,7 +53,7 @@ inline AnyBaseType NumericDataTypeToAnyType([[maybe_unused]] panda::compiler::Da % next unless plugin_opts["language_config"]["lang_type"] == "dynamic" % next unless plugin_opts["compiler_base_types"] % next unless plugin_opts["compiler_base_types"]["func_resolve_numeric_type"] - case panda::compiler::SourceLanguage::<%= plugin_lang %>: + case panda::SourceLanguage::<%= plugin_lang %>: return <%= plugin_opts["compiler_base_types"]["func_resolve_numeric_type"] %>(type); % end default: @@ -62,13 +62,13 @@ inline AnyBaseType NumericDataTypeToAnyType([[maybe_unused]] panda::compiler::Da } } -inline AnyBaseType GetAnyStringType(panda::compiler::SourceLanguage language) { +inline AnyBaseType GetAnyStringType(panda::SourceLanguage language) { switch(language) { // NOLINT(hicpp-multiway-paths-covered) % Common::plugins.each do |plugin_lang, plugin_opts| % next unless plugin_opts["language_config"]["lang_type"] == "dynamic" % next unless plugin_opts["compiler_base_types"] % next unless plugin_opts["compiler_base_types"]["func_resolve_string_type"] - case panda::compiler::SourceLanguage::<%= plugin_lang %>: + case panda::SourceLanguage::<%= plugin_lang %>: return <%= plugin_opts["compiler_base_types"]["func_resolve_string_type"] %>(); % end default: @@ -88,13 +88,13 @@ inline AnyBaseType GetAnyStringType(panda::compiler::SourceLanguage language) { */ inline std::optional IsAnyTypeCanBeSubtypeOf([[maybe_unused]] AnyBaseType super_type, [[maybe_unused]] AnyBaseType type, - [[maybe_unused]] panda::compiler::SourceLanguage language) { + [[maybe_unused]] panda::SourceLanguage language) { switch(language) { // NOLINT(hicpp-multiway-paths-covered) % Common::plugins.each do |plugin_lang, plugin_opts| % next unless plugin_opts["language_config"]["lang_type"] == "dynamic" % next unless plugin_opts["compiler_base_types"] != nil % next unless plugin_opts["compiler_base_types"]["func_is_any_type_can_be_subtype_of"] != nil - case panda::compiler::SourceLanguage::<%= plugin_lang %>: + case panda::SourceLanguage::<%= plugin_lang %>: return <%= plugin_opts["compiler_base_types"]["func_is_any_type_can_be_subtype_of"] %>(super_type, type); % end default: diff --git a/compiler/optimizer/templates/source_languages.h.erb b/compiler/optimizer/templates/source_languages.h.erb index 6a7075c21ec69d414a1c7aa4af3e01433ddfde55..04154e8ede4481a712008f680e85a81a2406df7f 100644 --- a/compiler/optimizer/templates/source_languages.h.erb +++ b/compiler/optimizer/templates/source_languages.h.erb @@ -18,22 +18,17 @@ #ifndef COMPILER_OPTIMIZER_TEMPLATES_SOURCE_LANGUAGES_H #define COMPILER_OPTIMIZER_TEMPLATES_SOURCE_LANGUAGES_H -namespace panda::compiler { +#include "source_language.h" -enum class SourceLanguage : uint8_t { -% Common::plugins.each do |plugin_lang, plugin_opts| - <%= plugin_lang %> = <%= plugin_opts["lang_enum_id"] %>, -% end - PANDA_ASSEMBLY = 1 -}; +namespace panda::compiler { enum class AnyBaseType : uint32_t { UNDEFINED_TYPE = 0, % Common::plugins.each do |plugin_lang, plugin_opts| % next unless plugin_opts["compiler_base_types"] % next unless plugin_opts["compiler_base_types"]["list_types"] -% plugin_opts["compiler_base_types"]["list_types"].each_key do |type_name| - <%= plugin_lang %>_<%= type_name %>, +% plugin_opts["compiler_base_types"]["list_types"].each_with_index do |(type_name, _), index| + <%= plugin_lang %>_<%= type_name %>, // <%= index + 1%> % end % end COUNT diff --git a/compiler/tools/paoc/paoc.cpp b/compiler/tools/paoc/paoc.cpp index e193ad435e4fa681b5809a259410a3acddfaedcd..758879da88e099f8c7eefce97724a81c8099ef27 100644 --- a/compiler/tools/paoc/paoc.cpp +++ b/compiler/tools/paoc/paoc.cpp @@ -280,6 +280,13 @@ int Paoc::Run(const panda::Span &args) } auto allocator = Runtime::GetCurrent()->GetInternalAllocator(); runtime_ = Runtime::GetCurrent()->GetPandaVM()->GetCompilerRuntimeInterface(); + if (compiler::OPTIONS.WasSetCompilerProfile()) { + auto res = runtime_->AddProfile(compiler::OPTIONS.GetCompilerProfile()); + if (!res) { + LOG(FATAL, COMPILER) << "Cannot open profile `" << compiler::OPTIONS.GetCompilerProfile() + << "`: " << res.Error(); + } + } aot_builder_.SetRuntime(runtime_); slow_path_data_ = allocator->New(); if (!LoadPandaFiles()) { @@ -333,6 +340,8 @@ void Paoc::RunAotMode(const panda::Span &args) } aot_builder_.SetWithCha(paoc_options_->IsPaocUseCha()); aot_builder_.Write(cmdline, output_file); + LOG_PAOC(INFO) << "METHODS COMPILED (success/all): " << success_methods_ << '/' + << success_methods_ + failed_methods_; LOG_PAOC(DEBUG) << "Successfully compiled to " << output_file; } @@ -620,8 +629,10 @@ bool Paoc::Compile(Method *method, size_t method_index) if (!CompileAot(&ctx)) { EVENT_COMPILATION(method_name, false, ctx.method->GetCodeSize(), 0, 0, 0, events::CompilationStatus::FAILED); + failed_methods_++; return false; } + success_methods_++; return true; case PaocMode::JIT: return CompileJit(&ctx); diff --git a/compiler/tools/paoc/paoc.h b/compiler/tools/paoc/paoc.h index aad873290dba0f2e1b20bf0d77b01ede046898e8..b05aaa65ff5f0aabb60b6be3b73a8c175ff8fb23 100644 --- a/compiler/tools/paoc/paoc.h +++ b/compiler/tools/paoc/paoc.h @@ -86,6 +86,9 @@ private: PaocClusters clusters_info_; compiler::SharedSlowPathData *slow_path_data_ {nullptr}; + unsigned success_methods_ {0}; + unsigned failed_methods_ {0}; + std::ofstream statistics_dump_; }; diff --git a/disassembler/BUILD.gn b/disassembler/BUILD.gn index aaf9196e512b6820abcfb1069524a281a2e29c3f..43a20d2b86d2dc35b3f3c0cdf694c0bf527b7f7c 100644 --- a/disassembler/BUILD.gn +++ b/disassembler/BUILD.gn @@ -20,7 +20,6 @@ config("arkdisassembler_public_config") { arkdisassembler_sources = [ "$target_gen_dir/bc_ins_to_pandasm_ins.cpp", - "$target_gen_dir/get_ins_info.cpp", "$target_gen_dir/opcode_translator.cpp", "$target_gen_dir/type_to_pandasm_type.cpp", "disassembler.cpp", @@ -66,7 +65,6 @@ ohos_shared_library("arkdisassembler") { ":disasm_plugins_inc", ":get_language_specific_metadata_inc", ":isa_gen_ark_disam_bc_ins_to_pandasm_ins_cpp", - ":isa_gen_ark_disam_get_ins_info_cpp", ":isa_gen_ark_disam_opcode_translator_cpp", ":type_to_pandasm_type_cpp", "$ark_root/assembler:libarkassembler", @@ -97,7 +95,6 @@ ohos_static_library("arkdisassembler_frontend_static") { ":disasm_plugins_inc", ":get_language_specific_metadata_inc", ":isa_gen_ark_disam_bc_ins_to_pandasm_ins_cpp", - ":isa_gen_ark_disam_get_ins_info_cpp", ":isa_gen_ark_disam_opcode_translator_cpp", ":type_to_pandasm_type_cpp", "$ark_root/assembler:libarkassembler_frontend_static", @@ -141,7 +138,6 @@ ark_isa_gen("isa_gen_ark_disam") { template_files = [ "opcode_translator.cpp.erb", "bc_ins_to_pandasm_ins.cpp.erb", - "get_ins_info.cpp.erb", ] sources = "templates" destination = "$target_gen_dir" diff --git a/disassembler/CMakeLists.txt b/disassembler/CMakeLists.txt index 98ed22484719118470a1be7c5a2b875559aa317b..47992229c4ef12647b000820b8640cc8c931cd25 100644 --- a/disassembler/CMakeLists.txt +++ b/disassembler/CMakeLists.txt @@ -31,7 +31,6 @@ panda_isa_gen( TEMPLATES "opcode_translator.cpp.erb" "bc_ins_to_pandasm_ins.cpp.erb" - "get_ins_info.cpp.erb" REQUIRES "${PANDA_ROOT}/assembler/asm_isapi.rb" "${PANDA_ROOT}/libpandafile/pandafile_isapi.rb" @@ -41,7 +40,6 @@ add_library(arkdisassembler ${PANDA_DEFAULT_LIB_TYPE} disassembler.cpp opcode_translator.cpp bc_ins_to_pandasm_ins.cpp - get_ins_info.cpp type_to_pandasm_type.cpp ) @@ -49,10 +47,15 @@ target_include_directories(arkdisassembler PUBLIC ${CMAKE_CURRENT_BINARY_DIR} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} PUBLIC ${CMAKE_SOURCE_DIR}/libpandabase + PUBLIC ${CMAKE_SOURCE_DIR}/runtime PUBLIC ${CMAKE_BINARY_DIR}/libpandabase PUBLIC ${CMAKE_BINARY_DIR}/libpandafile/include + ${CMAKE_BINARY_DIR} ) +add_dependencies(arkdisassembler source_languages_h) +add_dependencies(arkdisassembler merge_plugins) + target_link_libraries(arkdisassembler arkfile arkbase arkassembler) target_link_libraries(ark_disasm arkdisassembler arkbase arkfile arkassembler) diff --git a/disassembler/disasm.cpp b/disassembler/disasm.cpp index ca84e011c0b0332da2cb89de473e235d91c14c3d..81bc9007c3f9577715743df5d1ceddb911dba748 100644 --- a/disassembler/disasm.cpp +++ b/disassembler/disasm.cpp @@ -29,13 +29,16 @@ void PrintHelp(panda::PandArgParser &pa_parser) } void Disassemble(const std::string &input_file, const std::string &output_file, const bool verbose, const bool quiet, - const bool skip_strings) + const bool skip_strings, const std::string &profile) { LOG(DEBUG, DISASSEMBLER) << "[initializing disassembler]\nfile: " << input_file << "\n"; panda::disasm::Disassembler disasm {}; disasm.Disassemble(input_file, quiet, skip_strings); if (verbose) { + if (!profile.empty()) { + disasm.SetProfile(profile); + } disasm.CollectInfo(); } @@ -100,6 +103,7 @@ int main(int argc, const char **argv) "(--debug-file FILENAME) set debug file name. default is std::cout"); panda::PandArg input_file("input_file", "", "Path to the source binary code"); panda::PandArg output_file("output_file", "", "Path to the generated assembly code"); + panda::PandArg profile("profile", "", "Path to the profile"); panda::PandArg version {"version", false, "Ark version, file format version and minimum supported file format version"}; @@ -114,6 +118,7 @@ int main(int argc, const char **argv) pa_parser.Add(&version); pa_parser.PushBackTail(&input_file); pa_parser.PushBackTail(&output_file); + pa_parser.Add(&profile); pa_parser.EnableTail(); if (!ProcessArgs(pa_parser, input_file, output_file, debug, debug_file, help, version, argc, argv)) { @@ -121,7 +126,7 @@ int main(int argc, const char **argv) } Disassemble(input_file.GetValue(), output_file.GetValue(), verbose.GetValue(), quiet.GetValue(), - skip_strings.GetValue()); + skip_strings.GetValue(), profile.GetValue()); pa_parser.DisableTail(); diff --git a/disassembler/disassembler.cpp b/disassembler/disassembler.cpp index 0ea7d4b6d6603bb6981e6910dab242dffd01af9f..6a4882a3eececba8b161ce3d3693d4fba335352d 100644 --- a/disassembler/disassembler.cpp +++ b/disassembler/disassembler.cpp @@ -46,7 +46,83 @@ void Disassembler::Disassemble(const std::string &filename_in, bool quiet, bool GetLanguageSpecificMetadata(); } else { - LOG(ERROR, DISASSEMBLER) << "> unable to open specified pandafile: <" << filename_in << ">"; + LOG(FATAL, DISASSEMBLER) << "> unable to open specified pandafile: <" << filename_in << ">"; + } +} + +void Disassembler::SetProfile(std::string_view fname) +{ + std::ifstream stm(fname.data(), std::ios::binary); + if (!stm.is_open()) { + LOG(FATAL, DISASSEMBLER) << "Cannot open profile file"; + } + + auto res = profiling::ReadProfile(stm, file_language_); + if (!res) { + LOG(FATAL, DISASSEMBLER) << "Failed to deserialize: " << res.Error(); + } + profile_ = res.Value(); +} + +void Disassembler::GetInsInfo(panda_file::MethodDataAccessor &mda, const panda_file::File::EntityId &code_id, + MethodInfo *method_info /* out */) const +{ + const static size_t FORMAT_WIDTH = 20; + const static size_t INSTRUCTION_WIDTH = 2; + + panda_file::CodeDataAccessor code_accessor(*file_, code_id); + + std::string method_name = mda.GetFullName(); + auto prof = profiling::INVALID_PROFILE; + if (profile_ != profiling::INVALID_PROFILE) { + prof = profiling::FindMethodInProfile(profile_, file_language_, method_name); + } + + auto ins_sz = code_accessor.GetCodeSize(); + auto ins_arr = code_accessor.GetInstructions(); + + auto bc_ins = BytecodeInstruction(ins_arr); + auto bc_ins_last = bc_ins.JumpTo(ins_sz); + + while (bc_ins.GetAddress() != bc_ins_last.GetAddress()) { + std::stringstream ss; + + uintptr_t bc = bc_ins.GetAddress() - BytecodeInstruction(ins_arr).GetAddress(); + ss << "offset: 0x" << std::setfill('0') << std::setw(4U) << std::hex << bc; + ss << ", " << std::setfill('.'); + + BytecodeInstruction::Format format = bc_ins.GetFormat(); + + auto format_str = std::string("[") + BytecodeInstruction::GetFormatString(format) + ']'; + ss << std::setw(FORMAT_WIDTH) << std::left << format_str; + + ss << "["; + + const uint8_t *pc = bc_ins.GetAddress(); + const size_t sz = bc_ins.GetSize(); + + for (size_t i = 0; i < sz; i++) { + ss << "0x" << std::setw(INSTRUCTION_WIDTH) << std::setfill('0') << std::right << std::hex + << static_cast(pc[i]); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + + if (i != sz - 1) { + ss << " "; + } + } + + ss << "]"; + + if (profile_ != profiling::INVALID_PROFILE) { + auto prof_id = bc_ins.GetProfileId(); + if (prof_id != -1) { + ss << ", Profile: "; + profiling::DumpProfile(prof, file_language_, &bc_ins, ss); + } + } + + method_info->instructions_info.push_back(ss.str()); + + bc_ins = bc_ins.GetNext(); } } @@ -936,11 +1012,16 @@ void Disassembler::GetMethodInfo(const panda_file::File::EntityId &method_id, Me ss << ", code offset: 0x" << std::setfill('0') << std::setw(DEFAULT_OFFSET_WIDTH) << std::hex << method_accessor.GetCodeId().value(); - GetInsInfo(method_accessor.GetCodeId().value(), method_info); + GetInsInfo(method_accessor, method_accessor.GetCodeId().value(), method_info); } else { ss << ", "; } + auto profile_size = method_accessor.GetProfileSize(); + if (profile_size) { + ss << ", profile size: " << profile_size.value(); + } + method_info->method_info = ss.str(); if (method_accessor.GetCodeId()) { @@ -1202,8 +1283,18 @@ void Disassembler::Serialize(const pandasm::Function &method, std::ostream &os, os << " { # " << method_info.method_info << "\n# CODE:\n"; for (size_t i = 0; i < method.ins.size(); i++) { - os << "\t" << std::setw(width) << std::left << method.ins.at(i).ToString("", true, method.regs_num) << " # " - << method_info.instructions_info.at(i) << "\n"; + if (method.ins[i].set_label) { + std::string ins = method.ins[i].ToString("", true, method.regs_num); + std::string delim = ": "; + size_t pos = ins.find(delim); + std::string label = ins.substr(0, pos); + ins.erase(0, pos + delim.length()); + os << label << ":\n\t" << std::setw(width) << ins << " # " << method_info.instructions_info.at(i) + << "\n"; + } else { + os << "\t" << std::setw(width) << std::left << method.ins.at(i).ToString("", true, method.regs_num) + << " # " << method_info.instructions_info.at(i) << "\n"; + } } } else { os << " {\n"; diff --git a/disassembler/disassembler.h b/disassembler/disassembler.h index 6eb5e8bd222742570c359a1b73d92734f166a357..d2c13ca7cb27f43129c252e2585d3a81cc281d50 100644 --- a/disassembler/disassembler.h +++ b/disassembler/disassembler.h @@ -34,6 +34,8 @@ #include "file.h" #include "os/file.h" +#include "runtime/profiling/profiling.h" + #include "assembly-program.h" #include "assembly-ins.h" @@ -53,7 +55,10 @@ public: DEFAULT_MOVE_SEMANTIC(Disassembler); Disassembler() = default; - ~Disassembler() = default; + ~Disassembler() + { + profiling::DestroyProfile(profile_, file_language_); + } void Disassemble(const std::string &filename_in, const bool quiet = false, const bool skip_strings = false); void CollectInfo(); @@ -75,6 +80,8 @@ public: return prog_info_; } + void SetProfile(std::string_view fname); + private: void GetLiteralArrays(); void GetRecords(); @@ -111,7 +118,8 @@ private: void GetRecordInfo(const panda_file::File::EntityId &record_id, RecordInfo *record_info) const; void GetMethodInfo(const panda_file::File::EntityId &method_id, MethodInfo *method_info) const; - void GetInsInfo(const panda_file::File::EntityId &code_id, MethodInfo *method_info) const; + void GetInsInfo(panda_file::MethodDataAccessor &mda, const panda_file::File::EntityId &code_id, + MethodInfo *method_info) const; void Serialize(size_t index, const pandasm::LiteralArray &lit_array, std::ostream &os) const; void SerializeValues(const pandasm::LiteralArray &lit_array, std::ostream &os) const; @@ -162,6 +170,8 @@ private: ProgInfo prog_info_; + profiling::ProfileContainer profile_ {profiling::INVALID_PROFILE}; + std::unique_ptr debug_info_extractor_; bool quiet_; diff --git a/disassembler/templates/bc_ins_to_pandasm_ins.cpp.erb b/disassembler/templates/bc_ins_to_pandasm_ins.cpp.erb index 1ac9a2e1c629cccbe5c4f1afef0d6e18b1a6ea68..42afa2031bdb5e644ee3009d7284dbf8b0a42e37 100644 --- a/disassembler/templates/bc_ins_to_pandasm_ins.cpp.erb +++ b/disassembler/templates/bc_ins_to_pandasm_ins.cpp.erb @@ -47,6 +47,9 @@ pandasm::Ins Disassembler::BytecodeInstructionToPandasmInstruction(BytecodeInstr % elsif (i.operands.count(&:id?) != 0) ins.ids.push_back(IDToString(bc_ins, method_id)); % end +% end +% if i.profiled? + ins.profile_id = bc_ins.GetProfileId(); % end break; % end diff --git a/disassembler/templates/get_ins_info.cpp.erb b/disassembler/templates/get_ins_info.cpp.erb index e11a152cd6e44f24aea2887d8a3105be0cd4cf2b..0fa1c9fd265004cb6ebe8976d5ce47c981fb90dc 100644 --- a/disassembler/templates/get_ins_info.cpp.erb +++ b/disassembler/templates/get_ins_info.cpp.erb @@ -22,7 +22,7 @@ namespace panda::disasm { void Disassembler::GetInsInfo(const panda_file::File::EntityId& code_id, MethodInfo* method_info /* out */) const { const static size_t FORMAT_WIDTH = 20; - const static size_t INSTRUCTION_WIDTH = 4; + const static size_t INSTRUCTION_WIDTH = 2; panda_file::CodeDataAccessor code_accessor(*file_, code_id); @@ -36,7 +36,7 @@ void Disassembler::GetInsInfo(const panda_file::File::EntityId& code_id, MethodI std::stringstream ss; ss << "offset: 0x" << std::setfill('0') << std::setw(4) << std::hex - << code_id.GetOffset() + bc_ins.GetAddress() - BytecodeInstruction(ins_arr).GetAddress(); + << bc_ins.GetAddress() - BytecodeInstruction(ins_arr).GetAddress(); ss << ", " << std::setfill('.'); BytecodeInstruction::Format format = bc_ins.GetFormat(); diff --git a/disassembler/tests/instructions_test.cpp.in b/disassembler/tests/instructions_test.cpp.in index 6786dd436e6d8b2d587c3491bee24df54323a5f7..41d356833e14c985e501b371cf15a1b5e9922147 100644 --- a/disassembler/tests/instructions_test.cpp.in +++ b/disassembler/tests/instructions_test.cpp.in @@ -14,6 +14,7 @@ */ #include +#include #include #include @@ -328,7 +329,11 @@ TEST(instructions_test, test_debug_info) ASSERT_LT(code_start, code_end); std::string instructions = body_g.substr(code_start + strlen("# CODE:\n"), code_end + 1 - (code_start + strlen("# CODE:\n"))); - size_t instruction_count = std::count(instructions.begin(), instructions.end(), '\n'); + + std::regex const inst_regex("# offset: "); + std::ptrdiff_t const instruction_count(std::distance( + std::sregex_iterator(instructions.begin(), instructions.end(), inst_regex), + std::sregex_iterator())); const ProgInfo &prog_info = d.GetProgInfo(); auto g_it = prog_info.methods_info.find("g:()"); diff --git a/docs/bytecode_profiling.md b/docs/bytecode_profiling.md new file mode 100644 index 0000000000000000000000000000000000000000..63f3102ff1b51f08423650ae90139c6b280e7339 --- /dev/null +++ b/docs/bytecode_profiling.md @@ -0,0 +1,31 @@ +# Bytecode profiling in Dynamic languages + +## Support in ISA + +Each bytecode instruction, that supports profiling, contains additional field - profile index. It denotes index of +a corresponding profile data in the profile vector. + +Currently, it has 2 bytes size, but we can reduce it for small methods, where size of a profiling vector will be less +than 256 bytes. + +## Support in Runtime + +For each method, that contains instructions with profiling data, Runtime should allocate profiling vector. + +Profiling vector is a plain data of bytes, which content is determined by the language of a given bytecode. Core part +of Runtime knows nothing about how to interpret the profiling vector. + +Profile elements are sorted in descending order, from largest Profiles to smallest. It avoids alignment issues. + +Each interpreter handler reads profiling field from bytecode instruction and update corresponding slot in profiling +vector. + +## Support in AOT compilation + +Profile information, gathered during run, can be saved into special profile file, that can be specified by option +`--profile-ouptut`. After VM execution is finished, all profile information will be saved into this file. + +Format of a profile file is determined by the language plugin. + +AOT compiler accesses this file via runtime interface. CoreVM doesn't implement profiling interface, it must be +implemented by all language plugins, that supports profiling. diff --git a/docs/file_format.md b/docs/file_format.md index b1be6e70fc0ae686173255075119518f4c19313a..e02f5a99ab1e0a99ff47862c45949b9288b41051 100644 --- a/docs/file_format.md +++ b/docs/file_format.md @@ -367,6 +367,7 @@ Note: Proper region index to resolve `class_idx` and `proto_idx` can be found by | `PARAM_ANNOTATION` | `0x07` | `0-1` | `uint8_t[4]` | Data represents the offset to the runtime **invisible** annotations of the method's parameters. The offset must point to the value in [ParamAnnotations](#paramannotations) format. | | `TYPE_ANNOTATION` | `0x08` | `>=0` | `uint8_t[4]` | Data represents the offset to runtime **invisible** type annotation of the method. The tag may be repeated in case the method has several annotations. The offset must point to the value in [Annotation](#annotation) format. | | `RUNTIME_TYPE_ANNOTATION` | `0x09` | `>=0` | `uint8_t[4]` | Data represents the offset to runtime **visible** type annotation of the method. The tag may be repeated in case the method has several annotations. The offset must point to the value in [Annotation](#annotation) format. | +| `PROFILE_INFO` | `0x0a` | `>=0` | `uint8_t[]` | Data represents the profile information. Format is unspecified and determined by the language, used in the file. ### ForeignClass diff --git a/irtoc/backend/compiler/codegen_fastpath.cpp b/irtoc/backend/compiler/codegen_fastpath.cpp index 99e41ca2c98f0650768421d267500837b6ff312a..5eb006c2f1d4ef0fbf2b4f42e901bfa410763408 100644 --- a/irtoc/backend/compiler/codegen_fastpath.cpp +++ b/irtoc/backend/compiler/codegen_fastpath.cpp @@ -42,6 +42,44 @@ static void RestoreCallerRegistersFromFrame(RegMask mask, Encoder *encoder, cons encoder->LoadRegisters(mask, is_fp, -start_slot, fp_reg, GetCallerRegsMask(fl.GetArch(), is_fp)); } +/* + * We determine runtime calls manually, not using MethodProperties::HasRuntimeCalls, because we need to ignore + * SLOW_PATH_ENTRY intrinsic, since it doesn't require LR to be preserved. + */ +static bool HasRuntimeCalls(const Graph &graph) +{ + bool has_runtime_calls = false; + for (auto bb : graph.GetBlocksRPO()) { + for (auto inst : bb->Insts()) { + switch (inst->GetOpcode()) { + case Opcode::StoreArray: + has_runtime_calls = inst->CastToStoreArray()->GetNeedBarrier(); + break; + case Opcode::StoreObject: + has_runtime_calls = inst->CastToStoreObject()->GetNeedBarrier(); + break; + default: + break; + } + if (has_runtime_calls) { + break; + } + if (inst->IsRuntimeCall()) { + if (inst->IsIntrinsic() && inst->CastToIntrinsic()->GetIntrinsicId() == + RuntimeInterface::IntrinsicId::INTRINSIC_SLOW_PATH_ENTRY) { + continue; + } + has_runtime_calls = true; + break; + } + } + if (has_runtime_calls) { + break; + } + } + return has_runtime_calls; +} + void CodegenFastPath::GeneratePrologue() { SCOPED_DISASM_STR(this, "FastPath Prologue"); @@ -51,7 +89,12 @@ void CodegenFastPath::GeneratePrologue() caller_regs &= GetUsedRegs() & ~GetTarget().GetParamRegsMask(args_num); SaveCallerRegistersInFrame(caller_regs, GetEncoder(), GetFrameLayout(), false); + auto has_runtime_calls = HasRuntimeCalls(*GetGraph()); + saved_registers_ = GetUsedRegs() & RegMask(GetCalleeRegsMask(GetArch(), false)); + if (GetTarget().SupportLinkReg() && has_runtime_calls) { + saved_registers_ |= GetTarget().GetLinkReg().GetMask(); + } GetEncoder()->PushRegisters(saved_registers_, false, GetTarget().SupportLinkReg()); if (GetUsedVRegs().Any()) { @@ -109,8 +152,9 @@ void CodegenFastPath::CreateFrameInfo() { auto frame = GetGraph()->GetLocalAllocator()->New( FrameInfo::PositionedCallers::Encode(true) | FrameInfo::PositionedCallees::Encode(false) | - FrameInfo::CallersRelativeFp::Encode(true) | FrameInfo::CalleesRelativeFp::Encode(false)); - frame->SetSpillsCount(GetGraph()->GetStackSlotsCount()); + FrameInfo::CallersRelativeFp::Encode(true) | FrameInfo::CalleesRelativeFp::Encode(false) | + FrameInfo::PushCallers::Encode(true)); + frame->SetSpillsCount(GetFrameLayout().GetSpillsCount()); CFrameLayout fl(GetGraph()->GetArch(), GetGraph()->GetStackSlotsCount()); frame->SetCallersOffset( diff --git a/irtoc/backend/function.h b/irtoc/backend/function.h index 0b03a4919117c7e1204ccfb50204bcb111eb57f0..246e2a677156c4eb6a6838b6140c0895713484f3 100644 --- a/irtoc/backend/function.h +++ b/irtoc/backend/function.h @@ -69,7 +69,7 @@ public: return external_functions_[index].c_str(); } - compiler::SourceLanguage GetLanguage() const + SourceLanguage GetLanguage() const { return lang_; } @@ -134,7 +134,7 @@ protected: external_functions_ = funcs; } - void SetLanguage(compiler::SourceLanguage lang) + void SetLanguage(SourceLanguage lang) { lang_ = lang; } @@ -144,7 +144,7 @@ protected: private: compiler::Graph *graph_ {nullptr}; - compiler::SourceLanguage lang_ {compiler::SourceLanguage::PANDA_ASSEMBLY}; + SourceLanguage lang_ {SourceLanguage::PANDA_ASSEMBLY}; std::vector code_; std::vector external_functions_; std::vector source_dirs_; diff --git a/irtoc/backend/irtoc_runtime.h b/irtoc/backend/irtoc_runtime.h index acee1ff8d9274dbdad97080a5a9c430e49f310ba..89df92f00ae36b31b1566c71c3adf8649f40a74d 100644 --- a/irtoc/backend/irtoc_runtime.h +++ b/irtoc/backend/irtoc_runtime.h @@ -113,7 +113,7 @@ public: return reinterpret_cast((offset << 1U) | 1U); } - compiler::SourceLanguage GetMethodSourceLanguage(MethodPtr method) const override + SourceLanguage GetMethodSourceLanguage(MethodPtr method) const override { return MethodCast(method)->GetLanguage(); } diff --git a/irtoc/lang/cpp_function.rb b/irtoc/lang/cpp_function.rb index 74f1b924639bdd1d09dbd17f2b06851bc7b90865..6fc8f70a2a5287a8f17d60c1f44b28a6c642a97f 100755 --- a/irtoc/lang/cpp_function.rb +++ b/irtoc/lang/cpp_function.rb @@ -49,14 +49,22 @@ class CppFunction def generate_ir(gen) @variants.each { |x| gen.generate_function(x.func) } - params = "IntrinsicInst *inst, #{@params.keys.map { |x| "AnyBaseType #{x}"}.join(', ')}" + params = "IntrinsicInst *inst, #{@params.keys.map { |x| "[[maybe_unused]] AnyBaseType #{x}"}.join(', ')}" Output.scoped_puts "inline #{@return_type} #{@name}(#{params}) {" do @variants.each do |variant| + next if variant.cond == :default Output.scoped_puts "if (#{variant.cond}) {" do Output << "return #{variant.name}(inst);" end end - Output << "return false;" + defaults = @variants.select { |x| x.cond == :default } + raise "Default condition can be only once" if defaults.size > 1 + if defaults.empty? + Output << "return false;" + else + Output << "return #{defaults[0].name}(inst);" + end + end end end \ No newline at end of file diff --git a/irtoc/lang/function.rb b/irtoc/lang/function.rb index 5829c1f027646363881fcbdb8948b01c29cf23e2..36e2e88c291423f3c3cb49065f7c481b3ce880fd 100755 --- a/irtoc/lang/function.rb +++ b/irtoc/lang/function.rb @@ -92,7 +92,17 @@ class Function @cf_stack = [] @labels = {} if validate - @validation = Hash[validate.map {|k, v| [k, ((v.is_a?(Hash)) ? v[Options.arch] : v)] } ] + @validation = {} + validate.each do |k, v| + if v.is_a?(Hash) + value = v[Options.arch.to_sym] + value = v[:default] unless value + else + value = v + end + @validation[k] = value + end + @validation.each { |name, v| raise "#{@name}: no target arch in validation value: #{name}" unless v } end @body = block @@ -198,7 +208,7 @@ class Function raise "Compilation mode is not specified" unless @mode Output.println("if(GetGraph()->GetArch() != #{Options.cpp_arch}) LOG(FATAL, IRTOC) << \"Arch doesn't match\";") Output.println("GetGraph()->SetRelocationHandler(this);") - Output.println("SetLanguage(compiler::SourceLanguage::#{@lang});") + Output.println("SetLanguage(SourceLanguage::#{@lang});") Output.println("[[maybe_unused]] auto *graph = GetGraph();") Output.println("[[maybe_unused]] auto *runtime = GetGraph()->GetRuntime();") if @mode @@ -209,7 +219,10 @@ class Function ss += ");" Output.println(ss) end - Output.println("GetGraph()->SetArchUsedRegs(~0x#{@regalloc_mask.value.to_s(16)});") if @regalloc_mask + if @regalloc_mask + Output.println("GetGraph()->SetArchUsedRegs(~0x#{@regalloc_mask.value.to_s(16)});") + Output << "// Regalloc mask: #{@regalloc_mask}" + end Output.println(%Q[SetExternalFunctions({"#{@external_funcs.join('", "')}"});]) if @external_funcs @source_dirs.each_with_index do |dir, index| Output.println("uint32_t DIR_#{index} = AddSourceDir(\"#{dir}\");") diff --git a/irtoc/lang/ir_generator.rb b/irtoc/lang/ir_generator.rb index 5912746ac0c5c712bbf533abe64372567231bcd8..755e7d086a74f25c378da931bf2f3b4ca17b2f33 100755 --- a/irtoc/lang/ir_generator.rb +++ b/irtoc/lang/ir_generator.rb @@ -48,6 +48,7 @@ class GeneratorIrInline end def generate_ir_with_control_flow + Output << "// NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores)" Output << "auto cur_bb = source_inst->GetBasicBlock();" Output << "[[maybe_unused]] auto end_bb = cur_bb->SplitBlockAfterInstruction(source_inst, false);" @func.basic_blocks.each do |bb| @@ -80,6 +81,7 @@ class GeneratorIrInline Output << "/********************************************************" Output << " * bb #{bb.index}" Output << ' */' + Output << "// NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores)" Output << "cur_bb = bb_#{bb.index};" unless @func.simple_control_flow? bb.instructions.each { |inst| generate_instruction(inst) } end @@ -115,6 +117,7 @@ class GeneratorIrInline Output.scoped_puts "if (#{input_name}->GetOpcode() == Opcode::AnyTypeCheck) {" do if inst.IsCastAnyTypeValue? Output << "#{input_name}->CastToAnyTypeCheck()->SetAnyType(#{var_name}->GetAnyType());" + Output << "#{var_name}->SetIsIntegerWasSeen(#{input_name}->CastToAnyTypeCheck()->IsIntegerWasSeen());" end end end diff --git a/irtoc/lang/regmask.rb b/irtoc/lang/regmask.rb index 3ca1bd41fffb632353546afa5589849b44b2c8c3..66e333cce88833a2b64ac4b6fae6f86c0700c8b2 100755 --- a/irtoc/lang/regmask.rb +++ b/irtoc/lang/regmask.rb @@ -44,6 +44,7 @@ class RegMask args.each do |value| if value.is_a?(Symbol) raise "RegMask is initialized with symbol, but Regmap wasn't specified" unless @regmap + raise "Regmap doesn't contain value for #{value}" unless @regmap.data.keys.include? value value = @regmap[value] end raise "RegMask is initialized with wrong type: #{value.class}" unless value.is_a?(Integer) @@ -129,7 +130,15 @@ class RegMask @value == other.value end - def each - (0..@size - 1).select { |x| self[x] }.each { |x| yield x } + def each(&block) + if block + (0..@size - 1).select { |x| self[x] }.each { |x| yield x } + else + (0..@size - 1).select { |x| self[x] } + end + end + + def to_s + "[#{each.join(', ')}]" end end diff --git a/irtoc/scripts/allocation.irt b/irtoc/scripts/allocation.irt index 1ae7719b4d3b0e952362692839b5104a47db9013..a724704194f774d923f928afc765917aa7a93382 100644 --- a/irtoc/scripts/allocation.irt +++ b/irtoc/scripts/allocation.irt @@ -23,14 +23,14 @@ if Options.arch == :arm64 end AllocateObjectTlabValidation = { - spills_count_max: 2, # TODO(msherstennikov): set to 0 once regalloc supports smart temps - code_size_max: { arm64: 108, x86_64: 460, arm32: 9999 } + spills_count_max: { default: 2, arm32: 9999 }, # TODO(msherstennikov): set to 0 once regalloc supports smart temps + code_size_max: { arm64: 116, x86_64: 460, arm32: 9999 } # TODO(msherstennikov): revert back code size values, once regalloc supports smart temps # code_size_max: { arm64: 100, x86_64: 125, arm32: 9999 } } AllocateArrayTlabValidation = { - spills_count_max: 2, # TODO(msherstennikov): set to 0 once regalloc supports smart temps + spills_count_max: { default: 2, arm32: 9999 }, # TODO(msherstennikov): set to 0 once regalloc supports smart temps code_size_max: { arm64: 136, x86_64: 476, arm32: 9999 } # TODO(msherstennikov): revert back code size values, once regalloc supports smart temps # code_size_max: { arm64: 128, x86_64: 145, arm32: 9999 } diff --git a/irtoc/scripts/interpreter.irt b/irtoc/scripts/interpreter.irt index caf34240b38500db41ddeba862931613b39af619..44c826f5133f2f0328375ceb99b66bf8540c3445 100644 --- a/irtoc/scripts/interpreter.irt +++ b/irtoc/scripts/interpreter.irt @@ -1064,16 +1064,16 @@ end macro(:"handle_ststatic_#{name}id16") do |id| method_ptr := LoadI(%frame).Imm(Constants::FRAME_METHOD_OFFSET).ptr update_bytecode_offset - + field := static_field(id, false) # no restore because acc holds primitive value - + If(field, 0).CC(:CC_EQ).b { move_to_exception } field_access_flags := LoadI(field).Imm("Field::GetAccessFlagsOffset()").u32 field_type_id := ShrI(AndI(field_access_flags).Imm("ACC_TYPE").u32).Imm("ACC_TYPE_SHIFT").u32 - + [[0x2, "u8"], [0x3, "i8"], [0x4, "u8"], [0x5, "i16"], [0x6, "u16"], [0x7, "i32"], [0x8, "u32"], [0x9, "u32"], [0xa, "u64"], [0xb, "i64"], [0xc, "u64"]].each do |typeid, type| offset = LoadI(field).Imm("Field::GetOffsetOffset()").u32 field_class := LoadI(field).Imm("Field::GetClassOffset()").ref @@ -1086,10 +1086,10 @@ end end } end - + end end - + macro(:handle_ststatic_obj_id16) do |id| method_ptr := LoadI(%frame).Imm(Constants::FRAME_METHOD_OFFSET).ptr update_bytecode_offset @@ -1101,7 +1101,7 @@ macro(:handle_ststatic_obj_id16) do |id| field_class := LoadI(field).Imm("Field::GetClassOffset()").ref Store(field_class, offset, acc.ref).SetNeedBarrier(true).ref end - + [['', 32], ['64_', 64]].each do |name, type_size| macro(:"handle_ldstatic_#{name}id16") do |id| method_ptr := LoadI(%frame).Imm(Constants::FRAME_METHOD_OFFSET).ptr @@ -1116,7 +1116,7 @@ end field_type_id := ShrI(AndI(field_access_flags).Imm("ACC_TYPE").u32).Imm("ACC_TYPE_SHIFT").u32 offset = LoadI(field).Imm("Field::GetOffsetOffset()").u32 field_class := LoadI(field).Imm("Field::GetClassOffset()").ref - + [[0x2, "u8"], [0x3, "i8"], [0x4, "u8"], [0x5, "i16"], [0x6, "u16"], [0x7, "i32"], [0x8, "u32"], [0x9, "u32"], [0xa, "u64"], [0xb, "i64"], [0xc, "u64"]].each do |typeid, type| value := Load(field_class, offset).send(type) If(field_type_id, typeid).CC(:CC_EQ).b { @@ -1133,7 +1133,7 @@ end acc_tag := Constants::PRIMITIVE_TAG end end - + macro(:handle_ldstatic_obj_id16) do |id| method_ptr := LoadI(%frame).Imm(Constants::FRAME_METHOD_OFFSET).ptr update_bytecode_offset @@ -1493,7 +1493,11 @@ Panda.instructions.each do |i| mode << :DynamicMethod if i.properties.include?('dynamic') lang = i.namespace == 'core' ? 'PANDA_ASSEMBLY' : i.namespace.upcase - function("HANDLE_FAST_#{i.handler_name}", + # Remove profile part from the handler name, thereby we avoid adjusting of handler names each time we add profile + # info for an instruction. + handler_name = i.handler_name.gsub(/_PROF\d+/, '') + + function("HANDLE_FAST_#{handler_name}", regmap: handler_regmap, regalloc_set: $panda_mask, mode: mode, @@ -1563,7 +1567,7 @@ Panda.instructions.each do |i| end end - case i.handler_name + case handler_name when "NOP" # mov when "MOVI_V4_IMM4", "MOVI_V8_IMM8" diff --git a/isa/combine.rb b/isa/combine.rb index d99416f93fc32261537243a89ed17563b3832dcc..bd7d379dd8b06df308148ba4a737df7de65b6f36 100755 --- a/isa/combine.rb +++ b/isa/combine.rb @@ -49,7 +49,11 @@ options.data.drop(1).each do |plugin_path| plugin_data.each_key do |attr| raise "Uknown data property: #{attr}" unless data.key?(attr) - data[attr] += plugin_data[attr] + if data[attr].is_a? Hash + data[attr] = data[attr].merge(plugin_data[attr]) + else + data[attr] += plugin_data[attr] + end end end diff --git a/isa/isa.yaml b/isa/isa.yaml index 1ae40342c6d03dbc0b2316d6d601049a2c592150..2e9bc3d674e367ac4b060f7ee7cfb5deff640fca 100644 --- a/isa/isa.yaml +++ b/isa/isa.yaml @@ -251,6 +251,12 @@ prefixes: description: IEEE-754 single precision floating-point operations. opcode_idx: 0xec +# Define elements, that allowed to be used in a profile description. +profiles_schema: {} + +# Profiles should be specified in an ISA description for a specific language. +profiles: [] + groups: - title: No operation description: Perform an operation without behavior. diff --git a/isa/isapi.rb b/isa/isapi.rb index 9a3371888eec57daa22f98c3b2f7431a9913ca41..5d7e6d56532b9a26b53a1747e71a9edba5897bbb 100755 --- a/isa/isapi.rb +++ b/isa/isapi.rb @@ -121,7 +121,7 @@ class Instruction < SimpleDelegator # Suggested handler name def handler_name - opcode.upcase + opcode.upcase.gsub(/_PROF\d+/, '') end def intrinsic_name @@ -136,6 +136,10 @@ class Instruction < SimpleDelegator properties.include? 'inlinable' end + def profiled? + dig(:profile) + end + def opcode_idx if prefix dig(:opcode_idx) << 8 | prefix.opcode_idx @@ -235,6 +239,10 @@ class Instruction < SimpleDelegator dig(:namespace) || 'core' end + cached def profile + Panda::profiles[dig(:profile)] + end + include FreezeMixin freeze_defined_methods end @@ -295,6 +303,18 @@ class Format encoding end + # Return [offset, width] for profile id element + def profile_info + raise "Format hasn't profile" unless profiled? + prof = encoding.values.select { |x| x.name == 'prof'} + raise "Profile info not found in format #{name}" if prof.size != 1 + return prof[0].offset, prof[0].width + end + + def profiled? + name.include? 'prof' + end + include FreezeMixin freeze_defined_methods end @@ -306,7 +326,7 @@ class Operand def initialize(name, srcdst, type, width = 0, offset = 0) @name = name.to_s.gsub(/[0-9]/, '').to_sym - unless %i[v acc imm method_id type_id field_id string_id literalarray_id].include?(@name) + unless %i[v acc imm method_id type_id field_id string_id literalarray_id prof].include?(@name) raise "Incorrect operand #{name}" end @@ -335,6 +355,10 @@ class Operand %i[method_id type_id field_id string_id literalarray_id].include?(@name) end + def prof? + @name == :prof + end + def dst? %i[inout out].include?(@srcdst) end @@ -456,6 +480,29 @@ module Panda OpenStruct.new(tag: 'acc_write', description: 'Use accumulator as a destination operand.')] end + def verify_schema(name, data, schema) + data.each do |item| + item.each_pair do |key, value| + element = schema[key] + raise "Schema verification failed for #{name}: no element in schema: #{key}" unless element + if element == 'string' + raise "Schema verification failed for #{name}: #{key} must be a string" unless value.is_a? String + elsif element == 'int' + raise "Schema verification failed for #{name}: #{key} must be an integer" unless value.is_a? Integer + elsif element.is_a? Array + value.each do |x| + raise "Schema verification failed for #{name}: unexpected array value: #{x}" unless element.include? x + end + end + end + end + end + + cached def profiles + verify_schema('Profiles', @data.profiles, @data.profiles_schema) + @data.profiles.map { |x| [x.name, x] }.to_h + end + # Hash with exception tag as a key and exception description as a value cached def exceptions_hash convert_to_hash(exceptions) diff --git a/libpandabase/BUILD.gn b/libpandabase/BUILD.gn index afe52800a6f9ce1784f598ecce606c6bd816a1e4..49ca73d5769881804f058ea80d1b5e52a8cb7590 100644 --- a/libpandabase/BUILD.gn +++ b/libpandabase/BUILD.gn @@ -17,6 +17,7 @@ import("//build/ohos.gni") config("arkbase_public_config") { include_dirs = [ "$ark_root/libpandabase", + "$target_gen_dir/generated", "$target_gen_dir/include", "$target_gen_dir/generated", "$target_gen_dir", @@ -194,6 +195,7 @@ libarkbase_deps = [ ":logger_enum_gen_h", ":logger_impl_gen_h", ":coherency_line_size_h", + ":source_language_gen", sdk_libc_secshared_dep, ] @@ -258,3 +260,11 @@ ark_gen_file("logger_impl_gen_h") { requires = [ "$ark_root/libpandabase/templates/logger.rb" ] output_file = "$target_gen_dir/include/logger_impl_gen.inc" } + +ark_gen_file("source_language_gen") { + extra_dependencies = [ "$ark_root:concat_plugins_yamls" ] + template_file = "templates/source_language.h.erb" + data_file = "$root_gen_dir/plugin_options.yaml" + requires = [ "$ark_root/templates/plugin_options.rb" ] + output_file = "$target_gen_dir/generated/source_language.h" +} diff --git a/libpandabase/LibpandabasePostPlugins.cmake b/libpandabase/LibpandabasePostPlugins.cmake index 6a11c8206f173ee82c507c38f920a55b9c58c1ef..959fa4a306ff8a0a2ad0d6f623971d429542b9cb 100644 --- a/libpandabase/LibpandabasePostPlugins.cmake +++ b/libpandabase/LibpandabasePostPlugins.cmake @@ -52,6 +52,16 @@ else() set(ARKBASE_TARGETS arkbase) endif() +set(SOURCE_LANGUAGE_H ${CMAKE_BINARY_DIR}/libpandabase/generated/source_language.h) +panda_gen_file( + DATAFILE ${GEN_PLUGIN_OPTIONS_YAML} + TEMPLATE ${PROJECT_SOURCE_DIR}/libpandabase/templates/source_language.h.erb + REQUIRES ${PANDA_ROOT}/templates/plugin_options.rb + EXTRA_DEPENDENCIES plugin_options_merge + OUTPUTFILE ${SOURCE_LANGUAGE_H} +) +add_custom_target(source_language_gen DEPENDS ${SOURCE_LANGUAGE_H}) + foreach(TARGET ${ARKBASE_TARGETS}) - add_dependencies(${TARGET} logger_gen) + add_dependencies(${TARGET} logger_gen source_language_gen) endforeach() diff --git a/libpandabase/macros.h b/libpandabase/macros.h index 500e49150156aa56283594e04b40c321b7612a5c..fcdd94df3df52dd00e316b5f32b7de6e239a2f01 100644 --- a/libpandabase/macros.h +++ b/libpandabase/macros.h @@ -47,6 +47,9 @@ #define FIELD_UNUSED #endif +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define UNUSED_VAR(var) (void)(var) + #ifndef PANDA_TARGET_WINDOWS // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define PANDA_PUBLIC_API __attribute__((visibility("default"))) diff --git a/libpandabase/templates/source_language.h.erb b/libpandabase/templates/source_language.h.erb new file mode 100644 index 0000000000000000000000000000000000000000..fcc98af32c619b7a4abd6fa248cb66ea190187ea --- /dev/null +++ b/libpandabase/templates/source_language.h.erb @@ -0,0 +1,59 @@ +/** + * Copyright (c) 2021-2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Autogenerated file -- DO NOT EDIT! + +#ifndef LIBPANDABASE_TEMPLATES_SOURCE_LANGUAGE_H +#define LIBPANDABASE_TEMPLATES_SOURCE_LANGUAGE_H + +namespace panda { + +enum class SourceLanguage : uint8_t { +% Common::plugins.each do |plugin_lang, plugin_opts| + <%= plugin_lang %> = <%= plugin_opts["lang_enum_id"] %>, +% if plugin_lang == "JAVA" + JAVA_8 = JAVA, +% end +% end + PANDA_ASSEMBLY = 1, + INVALID = static_cast(-1) +}; + +inline std::ostream &operator<<(std::ostream &stream, SourceLanguage lang) +{ + switch (lang) { +% Common::plugins.each do |plugin_lang, plugin_opts| + case <%= plugin_opts["lang_enum"] %>: +% name = case plugin_lang +% when "ECMASCRIPT" then "ECMAScript" +% when "JAVA_8" then "Java 8" +% when "JAVA" then "Java" +% else plugin_lang.gsub(/_/, " ").capitalize +% end + stream << "<%= name %>"; + break; +% end + case panda::SourceLanguage::PANDA_ASSEMBLY: + stream << "Panda Assembly"; + break; + default: + UNREACHABLE(); + } + return stream; +} + +} // namespace panda + +#endif // LIBPANDABASE_TEMPLATES_SOURCE_LANGUAGE_H diff --git a/libpandabase/utils/bit_field.h b/libpandabase/utils/bit_field.h index 8ad4732819b5d18d0fefaf8f06026fd91b5a2ee2..5c799bfc54027d70805200c1908f890b32354d5b 100644 --- a/libpandabase/utils/bit_field.h +++ b/libpandabase/utils/bit_field.h @@ -29,7 +29,6 @@ class BitField { static constexpr unsigned BITS_PER_BYTE = 8; static_assert(start < sizeof(uint64_t) * BITS_PER_BYTE, "Invalid position"); - static_assert(bits_num != 0U, "Invalid size"); static_assert(bits_num <= sizeof(uint64_t) * BITS_PER_BYTE, "Invalid size"); static_assert(bits_num + start <= sizeof(uint64_t) * BITS_PER_BYTE, "Invalid position + size"); diff --git a/libpandafile/bytecode_instruction.h b/libpandafile/bytecode_instruction.h index d6b5c98d4b6edf6218eb3480786270ab87235993..596c631711eb532c3757de683a88770a45f6d30c 100644 --- a/libpandafile/bytecode_instruction.h +++ b/libpandafile/bytecode_instruction.h @@ -257,6 +257,11 @@ public: // Read imm and return it as int64_t/uint64_t auto GetImm64(size_t idx = 0) const; + /** + * Return profile id if instruction supports profiling, otherwise return -1. + */ + int GetProfileId() const; + /** * Primary and Secondary Opcodes are used in interpreter/verifier instruction dispatch * while full Opcode is typically used for various instruction property query. diff --git a/libpandafile/class_data_accessor.h b/libpandafile/class_data_accessor.h index 6371b57f281e15fbf21164b32b3606426280a5cc..116d330e45cb93b9a7a339f4f84f5ce16ef67c52 100644 --- a/libpandafile/class_data_accessor.h +++ b/libpandafile/class_data_accessor.h @@ -121,6 +121,56 @@ public: return name_.data; } + std::string DemangledName() + { + const uint8_t *descriptor = GetDescriptor(); + switch (*descriptor) { + case 'V': + return "void"; + case 'Z': + return "u1"; + case 'B': + return "i8"; + case 'H': + return "u8"; + case 'S': + return "i16"; + case 'C': + return "u16"; + case 'I': + return "i32"; + case 'U': + return "u32"; + case 'J': + return "i64"; + case 'Q': + return "u64"; + case 'F': + return "f32"; + case 'D': + return "f64"; + case 'A': + return "any"; + default: { + break; + } + } + + std::string name = utf::Mutf8AsCString(descriptor); + if (name[0] == '[') { + return name; + } + + std::replace(name.begin(), name.end(), '/', '.'); + + ASSERT(name.size() > 2); // 2 - L and ; + + name.erase(0, 1); + name.pop_back(); + + return name; + } + private: void SkipSourceLang(); diff --git a/libpandafile/file_items.cpp b/libpandafile/file_items.cpp index f93de614996bb74ebfb812c4d545226bd2d9dd0f..1c7b3c33788e6d5c8029b5f96a90c590ef52ba72 100644 --- a/libpandafile/file_items.cpp +++ b/libpandafile/file_items.cpp @@ -573,6 +573,10 @@ size_t MethodItem::CalculateSize() const size += TAG_SIZE + ID_SIZE; } + if (profile_size_ != 0) { + size += TAG_SIZE + sizeof(profile_size_); + } + size += TAG_SIZE; // null tag return size; @@ -652,6 +656,12 @@ bool MethodItem::WriteTaggedData(Writer *writer) } } + if (profile_size_ != 0) { + if (!WriteTaggedValue(writer, MethodTag::PROFILE_INFO, static_cast(profile_size_))) { + return false; + } + } + return writer->WriteByte(static_cast(MethodTag::NOTHING)); } diff --git a/libpandafile/file_items.h b/libpandafile/file_items.h index 5beb3139c972214ae0361b9527b0dd29d80265d3..e1a67ad1910f0fb1826678b162287887aabf0f4d 100644 --- a/libpandafile/file_items.h +++ b/libpandafile/file_items.h @@ -58,7 +58,8 @@ enum class MethodTag : uint8_t { ANNOTATION = 0x06, PARAM_ANNOTATION = 0x07, TYPE_ANNOTATION = 0x08, - RUNTIME_TYPE_ANNOTATION = 0x09 + RUNTIME_TYPE_ANNOTATION = 0x09, + PROFILE_INFO = 0x0a }; enum class FieldTag : uint8_t { @@ -919,6 +920,11 @@ public: return &runtime_type_annotations_; } + void SetProfileSize(size_t size) + { + profile_size_ = size; + } + private: bool WriteRuntimeAnnotations(Writer *writer); @@ -937,6 +943,7 @@ private: std::vector runtime_type_annotations_; ParamAnnotationsItem *runtime_param_annotations_ {nullptr}; ParamAnnotationsItem *param_annotations_ {nullptr}; + uint16_t profile_size_ {0}; }; class BaseClassItem : public TypeItem { diff --git a/libpandafile/method_data_accessor-inl.h b/libpandafile/method_data_accessor-inl.h index f06104ccdfd504eb209a95c5d1c53549d16c96ef..33fbb298a54bea4d18bb72b4334984b6bfd7494b 100644 --- a/libpandafile/method_data_accessor-inl.h +++ b/libpandafile/method_data_accessor-inl.h @@ -18,6 +18,7 @@ #include "helpers.h" #include "method_data_accessor.h" +#include "class_data_accessor-inl.h" #include "annotation_data_accessor.h" #include "proto_data_accessor.h" @@ -246,11 +247,26 @@ inline void MethodDataAccessor::EnumerateRuntimeTypeAnnotations(Callback cb) SkipTypeAnnotation(); } + helpers::EnumerateTaggedValues( + runtime_type_annotation_sp_, MethodTag::RUNTIME_TYPE_ANNOTATION, cb, &profile_info_sp_); +} + +inline std::optional MethodDataAccessor::GetProfileSize() +{ + if (is_external_) { + // NB! This is a workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80635 + // which fails Release builds for GCC 8 and 9. + std::optional novalue; + return novalue; + } + + if (profile_info_sp_.data() == nullptr) { + SkipRuntimeTypeAnnotation(); + } Span sp {nullptr, nullptr}; - size_ = panda_file_.GetIdFromPointer(runtime_type_annotation_sp_.data()).GetOffset() - method_id_.GetOffset() + - 1; // + 1 for NOTHING tag - helpers::EnumerateTaggedValues(runtime_type_annotation_sp_, - MethodTag::RUNTIME_TYPE_ANNOTATION, cb, &sp); + auto v = helpers::GetOptionalTaggedValue(profile_info_sp_, MethodTag::PROFILE_INFO, &sp); + size_ = panda_file_.GetIdFromPointer(sp.data()).GetOffset() - method_id_.GetOffset() + 1; // + 1 for NOTHING tag + return v; } inline std::optional MethodDataAccessor::GetParamAnnotationId() @@ -352,6 +368,18 @@ inline uint32_t MethodDataAccessor::GetNumericalAnnotation(uint32_t field_id) return result; } +inline std::string MethodDataAccessor::GetFullName() const +{ + uint32_t str_offset = + const_cast(this)->GetNumericalAnnotation(AnnotationField::FUNCTION_NAME); + if (str_offset != 0) { + auto cname = panda_file::ClassDataAccessor(panda_file_, GetClassId()).DemangledName(); + auto mname = utf::Mutf8AsCString(panda_file_.GetStringData(panda_file::File::EntityId(str_offset)).data); + return cname + "::" + mname; + } + return utf::Mutf8AsCString(GetName().data); +} + } // namespace panda::panda_file #endif // LIBPANDAFILE_METHOD_DATA_ACCESSOR_INL_H_ diff --git a/libpandafile/method_data_accessor.h b/libpandafile/method_data_accessor.h index 3db479fc1e6625cabda50431834a5f3b8dae7e35..38f51d69eeccf4ac9f9878e5120753799596aef1 100644 --- a/libpandafile/method_data_accessor.h +++ b/libpandafile/method_data_accessor.h @@ -25,6 +25,14 @@ namespace panda::panda_file { // NOLINTNEXTLINE(cppcoreguidelines-special-member-functions, hicpp-special-member-functions) class MethodDataAccessor { public: + enum AnnotationField : uint32_t { + IC_SIZE = 0, + FUNCTION_LENGTH, + FUNCTION_NAME, + STRING_DATA_BEGIN = FUNCTION_NAME, + STRING_DATA_END = FUNCTION_NAME + }; + MethodDataAccessor(const File &panda_file, File::EntityId method_id); ~MethodDataAccessor() = default; @@ -35,6 +43,8 @@ public: // quick way to get method name static panda_file::File::StringData GetName(const File &panda_file, File::EntityId method_id); + std::string GetFullName() const; + // quick way to get proto id static File::EntityId GetProtoId(const File &panda_file, File::EntityId method_id); @@ -142,12 +152,14 @@ public: template void EnumerateRuntimeTypeAnnotations(Callback cb); + std::optional GetProfileSize(); + std::optional GetParamAnnotationId(); size_t GetSize() { if (size_ == 0) { - SkipRuntimeTypeAnnotation(); + GetProfileSize(); } return size_; @@ -210,6 +222,7 @@ private: Span param_annotation_sp_ {nullptr, nullptr}; Span type_annotation_sp_ {nullptr, nullptr}; Span runtime_type_annotation_sp_ {nullptr, nullptr}; + Span profile_info_sp_ {nullptr, nullptr}; size_t size_; }; diff --git a/libpandafile/pandafile_isapi.rb b/libpandafile/pandafile_isapi.rb index 011144dd9d4fb0d89e2d74ce3edb7b8bbf066249..c723d9e8793f825c6e5108241e1d1b67995f48cc 100755 --- a/libpandafile/pandafile_isapi.rb +++ b/libpandafile/pandafile_isapi.rb @@ -21,7 +21,7 @@ Instruction.class_eval do end def each_operand - getters = {:reg? => 0, :imm? => 0, :id? => 0} + getters = {:reg? => 0, :imm? => 0, :id? => 0, :prof? => 0} operands.each do |op| key = getters.keys.find { |x| op.send(x) } yield op, getters[key] diff --git a/libpandafile/templates/bytecode_emitter_def_gen.h.erb b/libpandafile/templates/bytecode_emitter_def_gen.h.erb index ebf98e45697bb8b96026fc4d068254690f6bbcaf..72f2aa1b433cff18127fa4b876932a608a49bb3c 100644 --- a/libpandafile/templates/bytecode_emitter_def_gen.h.erb +++ b/libpandafile/templates/bytecode_emitter_def_gen.h.erb @@ -18,6 +18,6 @@ % Panda::instructions.group_by(&:mnemonic).each do |mnemonic, group| % # formats = group.map(&:format) % signature = emitter_signature(group, group.first.jump?) -% signature_str = signature.map { |o| "#{o.type} #{o.name}" }.join(', ') +% signature_str = signature.map { |o| "#{o.type} #{o.name}#{o.name == 'prof' ? ' = 0' : ''}" }.join(', ') void <%= group.first.emitter_name %>(<%= signature_str %>); %end diff --git a/libpandafile/templates/bytecode_emitter_gen.h.erb b/libpandafile/templates/bytecode_emitter_gen.h.erb index 1cd46da34595648a1043fd97877a4276294d6c3e..b8fb9538764d56d9f062c369bf82a5c935c5deda 100644 --- a/libpandafile/templates/bytecode_emitter_gen.h.erb +++ b/libpandafile/templates/bytecode_emitter_gen.h.erb @@ -23,7 +23,7 @@ static size_t Emit(It out, Types... args) { // NOLINT(readability-function-size) % fmt = i.format % ops = i.operands % offsets = [0] # for opcode -% offsets += ops.map(&:offset).sort +% offsets += fmt.encoding.map { |x, y| y.offset}.sort % offsets += [fmt.size * 8] # terminating offset, used for calculating last operand encoding width % // Disable check due to clang-tidy bug https://bugs.llvm.org/show_bug.cgi?id=32203 diff --git a/libpandafile/templates/bytecode_instruction-inl_gen.h.erb b/libpandafile/templates/bytecode_instruction-inl_gen.h.erb index bb2d4d0464f24209fc7b34ea9292a60ac4ef3fd0..7cad68a490139c762fac06c955adf23e8f877ff1 100644 --- a/libpandafile/templates/bytecode_instruction-inl_gen.h.erb +++ b/libpandafile/templates/bytecode_instruction-inl_gen.h.erb @@ -239,6 +239,24 @@ ALWAYS_INLINE inline uint16_t BytecodeInst::GetVReg(size_t idx /* = 0 */) UNREACHABLE(); } +template +__attribute__ ((visibility("hidden"))) +ALWAYS_INLINE inline int BytecodeInst::GetProfileId() const { // NOLINTNEXTLINE(readability-function-size) + Format format = GetFormat(); + + switch (format) { +% insns_uniq_sort_fmts.each do |i| +% next unless i.profiled? +% profile_info = i.format.profile_info + case Format::<%= i.format.pretty.upcase %>: + // NOLINTNEXTLINE(readability-magic-numbers) + return static_cast(Read<<%= profile_info[0] %>U, <%= profile_info[1] %>U>()); +% end + default: + return -1; + } +} + template template ::Format format, size_t idx /* = 0 */> inline auto BytecodeInst::GetImm() const { // NOLINT(readability-function-size) @@ -395,6 +413,7 @@ template std::ostream& operator<<(std::ostream& os, os << "<%= inst.mnemonic %>"; % sep = " " % inst.each_operand do |op, idx| +% next if op.prof? % op_str = "\"#{sep}v\" << inst.template GetVReg::Format::#{inst.format.pretty.upcase}, #{idx}>()" if op.reg? % op_str = "\"#{sep}\" << inst.template GetImm::Format::#{inst.format.pretty.upcase}, #{idx}>()" if op.imm? % op_str = "\"#{sep}id\" << inst.template GetId::Format::#{inst.format.pretty.upcase}, #{idx}>()" if op.id? diff --git a/libpandafile/templates/bytecode_instruction_enum_gen.h.erb b/libpandafile/templates/bytecode_instruction_enum_gen.h.erb index 58a9f0d0385beaa72af18a85d2e9a09c8ace2610..d27641e32fdc652c05ed1f51c44382f60df19cd2 100644 --- a/libpandafile/templates/bytecode_instruction_enum_gen.h.erb +++ b/libpandafile/templates/bytecode_instruction_enum_gen.h.erb @@ -37,3 +37,14 @@ enum Exceptions : uint32_t { <%= f.tag.upcase %> = <%= format("0x%x", 1 << i) %>, % end }; + +static constexpr const char *GetFormatString(Format format) +{ + // NOLINTNEXTLINE(readability-magic-numbers) + std::array> FORMATS = { +% Panda::formats.each do |fmt| + "<%= fmt.pretty.upcase %>", +% end + }; + return FORMATS[static_cast(format)]; +} \ No newline at end of file diff --git a/libpandafile/templates/source_lang_enum.h.erb b/libpandafile/templates/source_lang_enum.h.erb index 0408428e29255de5ddbce2593aa8277140d9df21..7816fc4db863ff3b0744ccbafebaf8760cc40ed9 100644 --- a/libpandafile/templates/source_lang_enum.h.erb +++ b/libpandafile/templates/source_lang_enum.h.erb @@ -16,15 +16,11 @@ #ifndef LIBPANDAFILE_SOURCE_LANG_ENUM_H #define LIBPANDAFILE_SOURCE_LANG_ENUM_H +#include "source_language.h" + namespace panda::panda_file { -enum class SourceLang : uint8_t { -% Common::plugins.each do |plugin_lang, plugin_opts| -% short_plugin_lang = plugin_lang == "JAVA" ? "JAVA_8" : plugin_lang - <%= short_plugin_lang %> = <%= plugin_opts["lang_enum_id"] %>, -% end - PANDA_ASSEMBLY = 1 -}; +using SourceLang = SourceLanguage; constexpr uint8_t LANG_COUNT = <%= Common::plugins.length + 1 %>; @@ -47,27 +43,6 @@ static constexpr size_t GetLangArrIndex(panda_file::SourceLang lang) return 0; } -inline std::ostream &operator<<(std::ostream &stream, SourceLang lang) -{ - switch (lang) { -% Common::plugins.each do |plugin_lang, plugin_opts| - case <%= plugin_opts["lang_enum"] %>: -% name = case plugin_lang -% when "ECMASCRIPT" then "ECMAScript" -% when "JAVA" then "Java 8" -% else plugin_lang.gsub(/_/, " ").capitalize -% end - stream << "<%= name %>"; - break; -% end - case panda::panda_file::SourceLang::PANDA_ASSEMBLY: - stream << "Panda Assembly"; - break; - default: - UNREACHABLE(); - } - return stream; -} // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define LANG_ENUM_LIST \ diff --git a/libpandafile/templates/tests/bytecode_emitter_tests_gen.h.erb b/libpandafile/templates/tests/bytecode_emitter_tests_gen.h.erb index 4c25d731b8422f976a50ade1350a834f2b212874..816b966e5e988aacf7ed54e732f73944c164774d 100644 --- a/libpandafile/templates/tests/bytecode_emitter_tests_gen.h.erb +++ b/libpandafile/templates/tests/bytecode_emitter_tests_gen.h.erb @@ -68,7 +68,7 @@ static std::vector &operator<<(std::vector &out, Opcode op) % % i = group.first % -% if i.operands.empty? +% if i.format.encoding.empty? # i.operands.empty? TEST(BytecodeEmitter, <%= emitter_name %>) { TestNoneFormat(Opcode::<%= i.opcode.upcase %>, [](BytecodeEmitter* emitter){ emitter-><%= emitter_name %>(); @@ -107,8 +107,8 @@ TEST(BytecodeEmitter, <%= emitter_name %>_<%= pretty_format %>) { % else % group.each do |i| TEST(BytecodeEmitter, <%= emitter_name %>_<%= i.format.pretty.upcase %>) { -% num_ops = i.operands.length() % ops = format_ops(i.format) +% num_ops = ops.length # i.operands.length() % ['min', 'max'].repeated_permutation(num_ops).each do |p| % args = [] % vals = [] diff --git a/runtime/BUILD.gn b/runtime/BUILD.gn index ada5edacf4d862d50ad9b2a0ffb269e7fe928eef..8aa743dd1696576a23077f5e88ab9ef4026c7db8 100644 --- a/runtime/BUILD.gn +++ b/runtime/BUILD.gn @@ -146,6 +146,7 @@ group("arkruntime_header_deps") { ":plugins_entrypoints_gen_h", ":plugins_inc", ":plugins_interpreters_inl_h", + ":profiling_gen_profiling_gen_h", "$ark_root/libpandabase:base_options_h", "$ark_root/verification/gen:isa_gen_verification_gen_abs_int_inl_gen_h", "$ark_root/verification/gen:isa_gen_verification_gen_cflow_iterate_inl_gen_h", @@ -524,6 +525,14 @@ gen_intrinsics_yaml("arkruntime_gen_intrinsics_yaml") { gen_include_dir = "$target_gen_dir/include" +ark_isa_gen("profiling_gen") { + template_files = [ + "profiling_gen.h.erb", + ] + sources = "profiling" + destination = "$target_gen_dir/include" +} + ark_isa_gen("isa_gen_libarkruntime") { template_files = [ "interpreter-inl_gen.h.erb", diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index 3ae93b487d77d288bb1b4057ab65184f1099fc23..52eddff896bedc6aa7df264ffe081c1b8fdf4454 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -360,6 +360,7 @@ add_dependencies(arkruntime_static arkstdlib asm_defines_generator entrypoints_gen + profiling_gen ${irtoc_fastpath_target} ${irtoc_interpreter_target} ) @@ -809,6 +810,8 @@ if(PANDA_WITH_TESTS) add_subdirectory(tests/tooling) endif() +add_subdirectory(profiling) + panda_add_sanitizers(TARGET arkruntime SANITIZERS ${PANDA_SANITIZERS_LIST}) panda_add_sanitizers(TARGET arkruntime_static SANITIZERS ${PANDA_SANITIZERS_LIST}) panda_add_sanitizers(TARGET arkruntime_interpreter_impl SANITIZERS ${PANDA_SANITIZERS_LIST}) diff --git a/runtime/asm_defines/CMakeLists.txt b/runtime/asm_defines/CMakeLists.txt index 642e33236933fd549b0c827ec4400ae5fb2d3aef..e1aed8f4abba75c6d054632c6d6ece144cc932ce 100644 --- a/runtime/asm_defines/CMakeLists.txt +++ b/runtime/asm_defines/CMakeLists.txt @@ -26,6 +26,7 @@ add_dependencies(asm_defines plugins_defines_h plugins_asm_defines_def coherency_line_size_h + source_language_gen ) set(GEN_INCLUDE_DIR "${CMAKE_CURRENT_BINARY_DIR}/../include") diff --git a/runtime/class_linker.cpp b/runtime/class_linker.cpp index 852d04da4ea4f912592a521c286fe36447953b60..0194f2e7707da3769012ce724426d601921cb12c 100644 --- a/runtime/class_linker.cpp +++ b/runtime/class_linker.cpp @@ -392,10 +392,10 @@ void MaybeLinkMethodToAotCode(Method *method, const compiler::AotClass &aot_clas auto entry = aot_class.FindMethodCodeEntry(method_index); if (entry != nullptr) { method->SetCompiledEntryPoint(entry); - LOG(DEBUG, AOT) << "Found AOT entrypoint [" - << reinterpret_cast(aot_class.FindMethodCodeSpan(method_index).data()) << ":" - << reinterpret_cast(aot_class.FindMethodCodeSpan(method_index).end()) - << "] for method: " << method->GetFullName(); + LOG(INFO, AOT) << "Found AOT entrypoint [" + << reinterpret_cast(aot_class.FindMethodCodeSpan(method_index).data()) << ":" + << reinterpret_cast(aot_class.FindMethodCodeSpan(method_index).end()) + << "] for method: " << method->GetFullName(); EVENT_AOT_ENTRYPOINT_FOUND(method->GetFullName()); ASSERT(aot_class.FindMethodHeader(method_index)->method_id == method->GetFileId().GetOffset()); diff --git a/runtime/compiler.cpp b/runtime/compiler.cpp index 6c8cbc72b080a5ad3f9a66b2e26b7b186ddbf7e3..c88d6c450f9f42300737eaeaf2d817be217ac17d 100644 --- a/runtime/compiler.cpp +++ b/runtime/compiler.cpp @@ -61,6 +61,19 @@ bool CompilerProcessor::Process(CompilerTask task) return true; } +/** + * Intrinsics fast paths are supported only for G1 GC. + */ +bool PandaRuntimeInterface::IsGcValidForFastPath(SourceLanguage lang) const +{ + auto runtime = Runtime::GetCurrent(); + if (lang == SourceLanguage::INVALID) { + lang = ManagedThread::GetCurrent()->GetThreadLang(); + } + auto gc_type = runtime->GetGCType(runtime->GetOptions(), lang); + return gc_type == mem::GCType::G1_GC; +} + compiler::RuntimeInterface::MethodId PandaRuntimeInterface::ResolveMethodIndex(MethodPtr parent_method, MethodIndex index) const { diff --git a/runtime/compiler.h b/runtime/compiler.h index 4c4bec59819109981789b082d5005f73fcaecd65..17f5b292ca42def5d68c35bb09d54d40ff667de3 100644 --- a/runtime/compiler.h +++ b/runtime/compiler.h @@ -195,9 +195,9 @@ public: { return MethodCast(method)->GetCodeSize(); } - compiler::SourceLanguage GetMethodSourceLanguage(MethodPtr method) const override + SourceLanguage GetMethodSourceLanguage(MethodPtr method) const override { - return static_cast(MethodCast(method)->GetClass()->GetSourceLang()); + return static_cast(MethodCast(method)->GetClass()->GetSourceLang()); } void SetCompiledEntryPoint(MethodPtr method, void *ep) override { @@ -507,7 +507,7 @@ public: /************************************************************************** * Entrypoints */ - uintptr_t GetIntrinsicAddress(bool runtime_call, IntrinsicId id) const override; + uintptr_t GetIntrinsicAddress(bool runtime_call, SourceLanguage lang, IntrinsicId id) const override; /************************************************************************** * Dynamic object information @@ -544,6 +544,8 @@ public: return TaggedValue::TAG_SPECIAL_MASK; } + bool IsGcValidForFastPath(SourceLanguage lang) const; + private: static compiler::DataType::Type ToCompilerType(panda_file::Type type) { diff --git a/runtime/deoptimization.cpp b/runtime/deoptimization.cpp index 7aeea6b64f9ecf12cea436320ea70723e00726c0..f10ec703682b8087acc02e138f7db7be067675d8 100644 --- a/runtime/deoptimization.cpp +++ b/runtime/deoptimization.cpp @@ -120,20 +120,22 @@ void InvalidateCompiledEntryPoint(const PandaSet &methods, bool is_cha ASSERT(stack->IsCFrame()); auto &cframe = stack->GetCFrame(); UnpoisonAsanStack(cframe.GetFrameOrigin()); - - LOG(DEBUG, INTEROP) << "Deoptimize frame: " << stack->GetMethod()->GetFullName(); + auto method = stack->GetMethod(); if (pc == nullptr) { - ASSERT(cframe.GetMethod() != nullptr); + ASSERT(method != nullptr); // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) - pc = stack->GetMethod()->GetInstructions() + stack->GetBytecodePc(); + pc = method->GetInstructions() + stack->GetBytecodePc(); } + LOG(DEBUG, INTEROP) << "Deoptimize frame: " << method->GetFullName() << ", pc=" << std::hex + << pc - method->GetInstructions() << std::dec; + // We must run InvalidateCompiledEntryPoint before we convert the frame, because GC is already may be in the // collecting phase and it can move some object in the deoptimized frame. if (destroy_compiled_code) { PandaSet destroy_method; - destroy_method.insert(cframe.GetMethod()); + destroy_method.insert(method); InvalidateCompiledEntryPoint(destroy_method, false); } diff --git a/runtime/entrypoints/entrypoints.cpp b/runtime/entrypoints/entrypoints.cpp index 36ccf5909c2519174d0e48c5473710e64036f79a..86dc5bc07708ba66ba60f02c000a6b5d29c052a5 100644 --- a/runtime/entrypoints/entrypoints.cpp +++ b/runtime/entrypoints/entrypoints.cpp @@ -859,11 +859,15 @@ extern "C" NO_ADDRESS_SANITIZE void StackOverflowExceptionEntrypoint() HandlePendingException(UnwindPolicy::SKIP_INLINED); } -extern "C" NO_ADDRESS_SANITIZE void DeoptimizeEntrypoint(uint8_t deoptimize_type) +extern "C" NO_ADDRESS_SANITIZE void DeoptimizeEntrypoint(uint64_t deoptimize_type) { BEGIN_ENTRYPOINT(); - auto type = static_cast(deoptimize_type); - LOG(DEBUG, INTEROP) << "DeoptimizeEntrypoint (reason: " << panda::compiler::DeoptimizeTypeToString(type) << ")\n"; + auto type = static_cast( + deoptimize_type & ((1U << MinimumBitsToStore(panda::compiler::DeoptimizeType::COUNT)) - 1)); + [[maybe_unused]] auto inst_id = deoptimize_type >> MinimumBitsToStore(panda::compiler::DeoptimizeType::COUNT); + LOG(DEBUG, INTEROP) << "DeoptimizeEntrypoint (reason: " << panda::compiler::DeoptimizeTypeToString(type) + << ", inst_id: " << inst_id << ")\n"; + ASSERT(!ManagedThread::GetCurrent()->HasPendingException()); bool destroy = (type == panda::compiler::DeoptimizeType::INLINE_IC); auto stack = StackWalker::Create(ManagedThread::GetCurrent()); diff --git a/runtime/entrypoints/entrypoints.yaml b/runtime/entrypoints/entrypoints.yaml index 614ff3a8dca8f1de86092084018769f90dc32060..373419923d04616be2066eb9a723835a22115a77 100644 --- a/runtime/entrypoints/entrypoints.yaml +++ b/runtime/entrypoints/entrypoints.yaml @@ -28,7 +28,7 @@ entrypoints: properties: [no_return] signature: - void - - uint8_t + - uint64_t - name: AbstractMethodError entrypoint: AbstractMethodErrorEntrypoint diff --git a/runtime/include/method.h b/runtime/include/method.h index 6440eaacf333e783ab2ede4049d7480286e3b3b6..c6fce67ec8906de37271870a5448126422ee1eaa 100644 --- a/runtime/include/method.h +++ b/runtime/include/method.h @@ -26,6 +26,7 @@ #include "libpandafile/code_data_accessor-inl.h" #include "libpandafile/file.h" #include "libpandafile/file_items.h" +#include "libpandafile/method_data_accessor.h" #include "libpandafile/modifiers.h" #include "runtime/bridge/bridge.h" #include "runtime/include/compiler_interface.h" @@ -95,13 +96,7 @@ public: WAITING = 4, }; - enum AnnotationField : uint32_t { - IC_SIZE = 0, - FUNCTION_LENGTH, - FUNCTION_NAME, - STRING_DATA_BEGIN = FUNCTION_NAME, - STRING_DATA_END = FUNCTION_NAME - }; + using AnnotationField = panda_file::MethodDataAccessor::AnnotationField; class Proto { public: diff --git a/runtime/include/panda_vm.h b/runtime/include/panda_vm.h index 73e34415fe0d70a450aec7c08e501d949c794262..aa18a984b4419cc118f562ab1dc50e4e66f10394 100644 --- a/runtime/include/panda_vm.h +++ b/runtime/include/panda_vm.h @@ -75,6 +75,7 @@ public: virtual void VisitVmRoots(const GCRootVisitor &visitor) = 0; virtual void UpdateVmRefs() = 0; virtual void UninitializeThreads() = 0; + virtual void SaveProfileInfo() {} virtual Expected InvokeEntrypoint(Method *entrypoint, const std::vector &args); diff --git a/runtime/options.yaml b/runtime/options.yaml index 412d7dbe8e7e70e88f6a48d46ba708cea9d8f20a..099ef46e357b12056784b5f57069ea2570810f59 100644 --- a/runtime/options.yaml +++ b/runtime/options.yaml @@ -722,3 +722,8 @@ options: type: uint32_t default: 0 description: Numerical identifier of an intrusive test + +- name: profile-output + type: std::string + default: "profile.bin" + description: Specify the location the collected profile information diff --git a/runtime/profiling/CMakeLists.txt b/runtime/profiling/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..cd21a2ae5f379dfe034d62ceb1c068b0333ceddb --- /dev/null +++ b/runtime/profiling/CMakeLists.txt @@ -0,0 +1,18 @@ + +set(GENERATED_DIR "${CMAKE_CURRENT_BINARY_DIR}") +if (NOT GEN_INCLUDE_DIR) + message(FATAL_ERROR "GEN_INCLUDE_DIR variable is not defined") +endif() + +panda_isa_gen(TEMPLATES profiling_gen.h.erb + TARGET_NAME profiling_gen + SOURCE ${CMAKE_CURRENT_LIST_DIR} + DESTINATION ${GEN_INCLUDE_DIR} +) + +declare_plugin_file("read_profile.h") +declare_plugin_file("destroy_profile.h") +declare_plugin_file("find_method_in_profile.h") +declare_plugin_file("dump_profile.h") +declare_plugin_file("profiling_includes.h") +declare_plugin_file("get_profiling_any_type.h") \ No newline at end of file diff --git a/runtime/profiling/profiling.h b/runtime/profiling/profiling.h new file mode 100644 index 0000000000000000000000000000000000000000..e6971e2f1ed4deea5288f2ed5ba9c2389e557cd9 --- /dev/null +++ b/runtime/profiling/profiling.h @@ -0,0 +1,93 @@ +/** + * 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. + */ + +#ifndef PANDA_PROFILING_H +#define PANDA_PROFILING_H + +#include "libpandafile/bytecode_instruction.h" +#include "source_lang_enum.h" +#include "libpandabase/utils/expected.h" +#ifdef PANDA_ENABLE_BYTECODE_PROFILING +#include "plugins/profiling_includes.h" +#include "runtime/include/profiling_gen.h" +#endif +#include +#include + +namespace panda::profiling { + +using ProfileContainer = uintptr_t; +using ProfileType = uintptr_t; + +static constexpr ProfileContainer INVALID_PROFILE = 0; + +inline Expected ReadProfile([[maybe_unused]] std::istream &stm, + [[maybe_unused]] panda::panda_file::SourceLang lang) +{ + // NOLINTNEXTLINE(hicpp-multiway-paths-covered) + switch (lang) { +#ifdef PANDA_ENABLE_BYTECODE_PROFILING +#include "plugins/read_profile.h" +#endif + default: + break; + } + return Unexpected("ReadProfile: No plugin found for the given language"); +} + +inline void DestroyProfile([[maybe_unused]] ProfileContainer profile, + [[maybe_unused]] panda::panda_file::SourceLang lang) +{ + // NOLINTNEXTLINE(hicpp-multiway-paths-covered) + switch (lang) { +#ifdef PANDA_ENABLE_BYTECODE_PROFILING +#include "plugins/destroy_profile.h" +#endif + default: + break; + } +} + +inline ProfileType FindMethodInProfile([[maybe_unused]] ProfileContainer profile, + [[maybe_unused]] panda::panda_file::SourceLang lang, + [[maybe_unused]] const std::string &method_name) +{ + // NOLINTNEXTLINE(hicpp-multiway-paths-covered) + switch (lang) { +#ifdef PANDA_ENABLE_BYTECODE_PROFILING +#include "plugins/find_method_in_profile.h" +#endif + default: + LOG(FATAL, COMMON) << "FindMethodInProfile: No plugin found for the given language"; + } + return INVALID_PROFILE; +} + +inline void DumpProfile([[maybe_unused]] ProfileType profile, [[maybe_unused]] panda::panda_file::SourceLang lang, + [[maybe_unused]] BytecodeInstruction *inst, [[maybe_unused]] std::ostream &stm) +{ + // NOLINTNEXTLINE(hicpp-multiway-paths-covered) + switch (lang) { +#ifdef PANDA_ENABLE_BYTECODE_PROFILING +#include "plugins/dump_profile.h" +#endif + default: + break; + } +} + +} // namespace panda::profiling + +#endif // PANDA_PROFILING_H diff --git a/runtime/profiling/profiling_gen.h.erb b/runtime/profiling/profiling_gen.h.erb new file mode 100644 index 0000000000000000000000000000000000000000..0b064ebc6bf9b5993d73c5c7f7b366eea2914abe --- /dev/null +++ b/runtime/profiling/profiling_gen.h.erb @@ -0,0 +1,65 @@ +#ifndef PANDA_PROFILING_GEN_H +#define PANDA_PROFILING_GEN_H + +#include "libpandafile/bytecode_instruction-inl.h" +#include + +namespace panda::profiling { + +% require 'set' +% require_relative '../../templates/common.rb' + +enum class ProfilingKind { + NONE, +% Panda::profiles.each do |name, _| + <%= name.snakecase.upcase %>, +% end +}; + +inline constexpr const char* GetProfilingKindName(ProfilingKind kind) +{ + // NOLINTNEXTLINE(hicpp-multiway-paths-covered) + switch (kind) { +% Panda::profiles.each do |prof, _| + case ProfilingKind::<%= prof.snakecase.upcase %>: + return "<%= prof.snakecase.upcase %>"; +% end + default: + return "INVALID"; + } +} + +% sizes = Panda::profiles.values.map(&:size).uniq.sort +inline constexpr std::array> GetOrderedProfileSizes() { + return {<%= sizes.join(', ') %>}; +} + +inline size_t GetProfileSizeInBytes(BytecodeInstruction::Opcode opcode) +{ + // NOLINTNEXTLINE(hicpp-multiway-paths-covered) + switch (opcode) { +% Panda::instructions.select { |x| x.profiled? }.each do |inst| + case BytecodeInstruction::Opcode::<%= inst.opcode.upcase %>: + return <%= inst.profile.size %>; +% end + default: + return 0; + } +} + +inline ProfilingKind GetProfileKind(BytecodeInstruction::Opcode opcode) +{ + // NOLINTNEXTLINE(hicpp-multiway-paths-covered) + switch (opcode) { +% Panda::instructions.select { |x| x.profiled? }.each do |inst| + case BytecodeInstruction::Opcode::<%= inst.opcode.upcase %>: + return ProfilingKind::<%= inst.profile.name.snakecase.upcase %>; +% end + default: + return ProfilingKind::NONE; + } +} + +} // namespace panda::profiling + +#endif // PANDA_PROFILING_GEN_H \ No newline at end of file diff --git a/runtime/runtime.cpp b/runtime/runtime.cpp index 6b0286aff745cf6d1403e3a82f6750cf9cccb0c5..cde22b4e9c00b59a60c8c4b23758e15ef69b9870 100644 --- a/runtime/runtime.cpp +++ b/runtime/runtime.cpp @@ -402,6 +402,8 @@ bool Runtime::Destroy() Trace::StopTracing(); } + instance->GetPandaVM()->SaveProfileInfo(); + instance->GetNotificationManager()->VmDeathEvent(); // Stop compiler first to make sure compile memleak doesn't occur diff --git a/templates/plugin_options.rb b/templates/plugin_options.rb index 82049662bbd3e04ef22fbbdf33e61b519f5a99d6..6debaa0647ce67281bda7b0f68275a4a9dc7a511 100755 --- a/templates/plugin_options.rb +++ b/templates/plugin_options.rb @@ -74,8 +74,7 @@ module Common data.plugins.each do |plugin| h_plugin = plugin.to_h h_plugin_lang = h_plugin.keys.first.to_s - lang_enum = h_plugin_lang == "JAVA" ? "JAVA_8" : h_plugin_lang - lang_enum = "panda::panda_file::SourceLang::" + lang_enum + lang_enum = "panda::SourceLanguage::" + h_plugin_lang @plugins[h_plugin_lang] = Hash.new() @plugins[h_plugin_lang]["lang_enum"] = lang_enum assign_data_level(@plugins, h_plugin_lang, h_plugin.values.first) diff --git a/verification/gen/templates/job_fill_gen.h.erb b/verification/gen/templates/job_fill_gen.h.erb index 765c8cbb5bf2b8719de52f6d48156350f03ce81b..dd0ee9621556565da65a9b760d6f25797474af89 100644 --- a/verification/gen/templates/job_fill_gen.h.erb +++ b/verification/gen/templates/job_fill_gen.h.erb @@ -65,31 +65,31 @@ bool Job::ResolveIdentifiers(LibCache &cache) { % Panda::instructions.each do |i| % combination_flags = "" % mnemonic = i.mnemonic.split('.').map { |p| p == '64' ? 'Wide' : p.capitalize }.join -% value_dispatch = "HANDLE_" + i.opcode.upcase +% value_dispatch = "HANDLE_" + i.handler_name % if i.properties.any? { |prop| ['method_id', 'field_id', 'type_id', 'literalarray_id'].include?(prop)} -% combination_flags += "Prop_" +% combination_flags += "Prop_" % end % if (['method_id', 'field_id', 'type_id', 'string_id', 'literalarray_id'] & i.properties).size > 1 % cache_api = "cache_api" -% combination_flags += "CacheApi_" +% combination_flags += "CacheApi_" % else % cache_api = "cache.FastAPI()" -% combination_flags += "CacheFastApi_" +% combination_flags += "CacheFastApi_" % end % if i.properties.include?('literalarray_id') -% combination_flags += "LiteralliId_" +% combination_flags += "LiteralliId_" % end % if i.properties.include?('method_id') -% combination_flags += "MethodId_" +% combination_flags += "MethodId_" % end % if i.properties.include?('field_id') -% combination_flags += "FieldId_" +% combination_flags += "FieldId_" % end % if i.properties.include?('type_id') -% combination_flags += "TypeId_" +% combination_flags += "TypeId_" % end % if i.properties.include?('string_id') -% combination_flags += "StringId_" +% combination_flags += "StringId_" % end % combination_flags += "GetNext_" % flag = dispatch_table_hash.include?(combination_flags) @@ -100,7 +100,7 @@ bool Job::ResolveIdentifiers(LibCache &cache) { % end % body_gen_parts = Hash.new() % body_gen_parts = { -% "Prop_" => %( +% "Prop_" => %( % LOG_INST(); % auto id = inst.GetId(); % ), @@ -209,7 +209,7 @@ bool Job::ResolveIdentifiers(LibCache &cache) { % if (inst.IsLast()) { % return true; % } -% +% % auto next_inst = inst.GetNext(); % if (!inst.IsPrimaryOpcodeValid()) { % LOG(DEBUG, VERIFIER) << "Opcode value is out of range. " @@ -226,9 +226,9 @@ bool Job::ResolveIdentifiers(LibCache &cache) { % return false; % } % inst = next_inst; -% } +% } % goto* dispatch_table[inst.GetPrimaryOpcode()]; -% ) +% ) %} % % full_code_hash = Hash.new("") @@ -244,10 +244,10 @@ bool Job::ResolveIdentifiers(LibCache &cache) { % % full_code_hash.each { |key_full, code| % dispatch_table_hash[key_full].each { |label| -<%= label%>:; +<%= label%>:; % } - { -<%= code %> + { +<%= code %> % } % HANDLE_INVALID: