From d568f1d80a477d2b400e17975d7d06fd83dcd21f Mon Sep 17 00:00:00 2001 From: Mikhail Sherstennikov Date: Tue, 19 Jul 2022 17:42:43 +0300 Subject: [PATCH] Support type profiling for dynamic languages Each instruction, that supports profiling, has an additional field in an ISA description: `Profile`. This field denotes the name of the profiling type. Profiling types are described in the language plugins. Core part of Ark has no instructions with profiling, since it is statically typed. All profiling related data should be defined in the language plugins. Core part in turn, uses embedded plugins machinery for connecting with language plugin's profiling implementation. Other changes: - Add embedded plugins machinery. See plugins/CMakeLists.txt for more info. - Merge SourceLanguage enums from panda_file and compiler to the single place: pandabase. - Disassembler: - print encoding byte in 2 symbols instead of 4. - print instructions offset from the beginning of a method code instead of file. - properly indent labels when `--verbose` option is passed - Compiler: - Restrict Link Register usage only via Temp registers acquirement machinery. Otherwise we may have problems in FastPath mode, where LR is not saved in prologue. - Print relative offsets of branches in x64 disassembler. - Add flag `PushCallers` in FrameInfo to push callers to stack, instead of caller's frame, as it done for CFrame. This change is needed in FastPath mode when we call some function, since FastPath's frame has no slots for caller registers. - Support `default` element in Irtoc validation items. - Refvert changes from M.Bolshov in compiler_logger.h, thereby nowi, when compiler logs are enabled, compiler's components logs are disabled by default. Signed-off-by: Mikhail Sherstennikov --- assembler/BUILD.gn | 2 + assembler/CMakeLists.txt | 2 + assembler/asm_isapi.rb | 2 +- assembler/assembly-emitter.cpp | 44 +++++++-- assembler/assembly-emitter.h | 2 + assembler/assembly-function.h | 1 + assembler/assembly-ins.h | 17 +++- assembler/lexer.cpp | 2 +- assembler/lexer.h | 3 +- assembler/templates/ins_create_api.h.erb | 3 +- assembler/templates/ins_emit.h.erb | 1 + assembler/templates/ins_to_string.cpp.erb | 5 + assembler/templates/isa.h.erb | 6 +- bytecode_optimizer/runtime_adapter.h | 6 +- cmake/Definitions.cmake | 4 + cmake/PostPlugins.cmake | 18 ++++ cmake/RegisterPlugins.cmake | 57 +++++++++++ cmake/TemplateBasedGen.cmake | 3 +- compiler/CMakeLists.txt | 3 + compiler/aot/aot_builder/BUILD.gn | 1 + compiler/aot/aot_file.cpp | 3 +- compiler/compiler.yaml | 5 + compiler/compiler_logger.h | 13 ++- compiler/optimizer/code_generator/codegen.cpp | 32 +++--- .../optimizer/code_generator/frame_info.h | 4 + compiler/optimizer/code_generator/operands.h | 1 + .../optimizer/code_generator/slow_path.cpp | 3 +- .../code_generator/target/aarch64/encode.cpp | 19 ++-- .../code_generator/target/amd64/encode.cpp | 1 + compiler/optimizer/ir/dump.cpp | 5 +- compiler/optimizer/ir/graph.h | 2 +- compiler/optimizer/ir/inst.h | 13 ++- compiler/optimizer/ir/instructions.yaml | 7 ++ compiler/optimizer/ir/runtime_interface.h | 27 ++++- .../optimizer/ir_builder/inst_builder-inl.h | 4 +- .../optimizer/ir_builder/inst_builder.cpp | 17 ++++ compiler/optimizer/ir_builder/inst_builder.h | 8 +- .../entrypoints_bridge_asm_macro.inl.erb | 3 +- .../intrinsics/get_intrinsics.inl.erb | 28 ++++-- .../templates/ir-dyn-base-types.h.erb | 12 +-- .../templates/source_languages.h.erb | 13 +-- compiler/tools/paoc/paoc.cpp | 11 +++ compiler/tools/paoc/paoc.h | 3 + disassembler/BUILD.gn | 4 - disassembler/CMakeLists.txt | 7 +- disassembler/disasm.cpp | 9 +- disassembler/disassembler.cpp | 99 ++++++++++++++++++- disassembler/disassembler.h | 14 ++- .../templates/bc_ins_to_pandasm_ins.cpp.erb | 3 + disassembler/templates/get_ins_info.cpp.erb | 4 +- disassembler/tests/instructions_test.cpp.in | 7 +- docs/bytecode_profiling.md | 31 ++++++ docs/file_format.md | 1 + irtoc/backend/compiler/codegen_fastpath.cpp | 48 ++++++++- irtoc/backend/function.h | 6 +- irtoc/backend/irtoc_runtime.h | 2 +- irtoc/lang/cpp_function.rb | 12 ++- irtoc/lang/function.rb | 19 +++- irtoc/lang/ir_generator.rb | 3 + irtoc/lang/regmask.rb | 13 ++- irtoc/scripts/allocation.irt | 6 +- irtoc/scripts/interpreter.irt | 24 +++-- isa/combine.rb | 6 +- isa/isa.yaml | 6 ++ isa/isapi.rb | 51 +++++++++- libpandabase/BUILD.gn | 10 ++ libpandabase/LibpandabasePostPlugins.cmake | 12 ++- libpandabase/macros.h | 3 + libpandabase/templates/source_language.h.erb | 59 +++++++++++ libpandabase/utils/bit_field.h | 1 - libpandafile/bytecode_instruction.h | 5 + libpandafile/class_data_accessor.h | 50 ++++++++++ libpandafile/file_items.cpp | 10 ++ libpandafile/file_items.h | 9 +- libpandafile/method_data_accessor-inl.h | 36 ++++++- libpandafile/method_data_accessor.h | 15 ++- libpandafile/pandafile_isapi.rb | 2 +- .../templates/bytecode_emitter_def_gen.h.erb | 2 +- .../templates/bytecode_emitter_gen.h.erb | 2 +- .../bytecode_instruction-inl_gen.h.erb | 19 ++++ .../bytecode_instruction_enum_gen.h.erb | 11 +++ libpandafile/templates/source_lang_enum.h.erb | 31 +----- .../tests/bytecode_emitter_tests_gen.h.erb | 4 +- runtime/BUILD.gn | 9 ++ runtime/CMakeLists.txt | 3 + runtime/asm_defines/CMakeLists.txt | 1 + runtime/class_linker.cpp | 8 +- runtime/compiler.cpp | 13 +++ runtime/compiler.h | 8 +- runtime/deoptimization.cpp | 12 ++- runtime/entrypoints/entrypoints.cpp | 10 +- runtime/entrypoints/entrypoints.yaml | 2 +- runtime/include/method.h | 9 +- runtime/include/panda_vm.h | 1 + runtime/options.yaml | 5 + runtime/profiling/CMakeLists.txt | 18 ++++ runtime/profiling/profiling.h | 93 +++++++++++++++++ runtime/profiling/profiling_gen.h.erb | 65 ++++++++++++ runtime/runtime.cpp | 2 + templates/plugin_options.rb | 3 +- verification/gen/templates/job_fill_gen.h.erb | 32 +++--- 101 files changed, 1157 insertions(+), 209 deletions(-) create mode 100644 docs/bytecode_profiling.md create mode 100644 libpandabase/templates/source_language.h.erb create mode 100644 runtime/profiling/CMakeLists.txt create mode 100644 runtime/profiling/profiling.h create mode 100644 runtime/profiling/profiling_gen.h.erb diff --git a/assembler/BUILD.gn b/assembler/BUILD.gn index 979856a0f..5dbc48b36 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 8b5c73723..6575250f0 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 ee19cab48..e43988d71 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 e0e698f2c..6ceceb3c2 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 587cb6789..20e60ca58 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 7b88a67f9..0d5e58f07 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 95ed783e9..d5916ee60 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 148b63876..3e9c156a2 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 456f29f68..4c85e7e0c 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 4c07583b6..85b823c8e 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 c0dfb0be4..c5aa20c1d 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 7dc626f7a..ee6125ce9 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 bed0d0ee8..4c75a3ef4 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 992060a86..2b8210ce6 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 88c3a52c0..b8f9ffa32 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 426082a8e..72aadd9ad 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 21b1b490c..77c5820f7 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 a36072985..0b8cfedee 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 54a4d4340..8ad13f387 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 4f338da7c..1dc1f4702 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 ae47bff72..e5cc248de 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 78fbfb617..84550e295 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 147d9930b..d2be25244 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 25757a92d..4a254f0e4 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 2a377fb23..652c3a01a 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 949871228..903a221de 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 dd929191f..f5fbc912a 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 74cc4a673..a85a87b82 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 b6c35ac41..3f9b569ad 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 6cff4131b..89eaeec77 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 1a5bc4f4a..b10e6babd 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 ef2b3af7e..9e0c00e3f 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 60c30219f..05f627df5 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 57b31510b..0a7072e06 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 406c27f85..e30eccc66 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 81eb7acd5..d6b12133f 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 8455fda69..5fb628487 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 6292d9133..c5fd93e6f 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 f64942b85..863f40f94 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 ac2327976..ae767c42e 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 6a7075c21..04154e8ed 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 e193ad435..758879da8 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 aad873290..b05aaa65f 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 aaf9196e5..43a20d2b8 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 98ed22484..47992229c 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 ca84e011c..81bc9007c 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 0ea7d4b6d..6a4882a3e 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 6eb5e8bd2..d2c13ca7c 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 1ac9a2e1c..42afa2031 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 e11a152cd..0fa1c9fd2 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 6786dd436..41d356833 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 000000000..63f3102ff --- /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 b1be6e70f..e02f5a99a 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 99e41ca2c..5eb006c2f 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 0b03a4919..246e2a677 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 acee1ff8d..89df92f00 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 74f1b9246..6fc8f70a2 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 5829c1f02..36e2e88c2 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 5912746ac..755e7d086 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 3ca1bd41f..66e333cce 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 1ae7719b4..a72470419 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 caf34240b..44c826f51 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 d99416f93..bd7d379dd 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 1ae40342c..2e9bc3d67 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 9a3371888..5d7e6d565 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 afe52800a..49ca73d57 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 6a11c8206..959fa4a30 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 500e49150..fcdd94df3 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 000000000..fcc98af32 --- /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 8ad473281..5c799bfc5 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 d6b5c98d4..596c63171 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 6371b57f2..116d330e4 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 f93de6149..1c7b3c337 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 5beb3139c..e1a67ad19 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 f06104ccd..33fbb298a 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 3db479fc1..38f51d69e 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 011144dd9..c723d9e87 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 ebf98e456..72f2aa1b4 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 1cd46da34..b8fb95387 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 bb2d4d046..7cad68a49 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 58a9f0d03..d27641e32 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 0408428e2..7816fc4db 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 4c25d731b..816b966e5 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 ada5edacf..8aa743dd1 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 3ae93b487..52eddff89 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 642e33236..e1aed8f4a 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 852d04da4..0194f2e77 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 6c8cbc72b..c88d6c450 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 4c4bec598..17f5b292c 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 7aeea6b64..f10ec7036 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 36ccf5909..86dc5bc07 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 614ff3a8d..373419923 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 6440eaacf..c6fce67ec 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 73e34415f..aa18a984b 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 412d7dbe8..099ef46e3 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 000000000..cd21a2ae5 --- /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 000000000..e6971e2f1 --- /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 000000000..0b064ebc6 --- /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 6b0286aff..cde22b4e9 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 82049662b..6debaa064 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 765c8cbb5..dd0ee9621 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: -- Gitee