diff --git a/.github/actions/build.sh b/.github/actions/build.sh new file mode 100755 index 00000000..2a8a8a5c --- /dev/null +++ b/.github/actions/build.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +cd ${GITHUB_WORKSPACE} +mkdir $MAVIS_BUILD_TYPE +cd $MAVIS_BUILD_TYPE + +CXX_COMPILER=${COMPILER/clang/clang++} +CXX_COMPILER=${CXX_COMPILER/gcc/g++} + +cmake .. -DCMAKE_C_COMPILER=$COMPILER -DCMAKE_CXX_COMPILER=$CXX_COMPILER -DCMAKE_BUILD_TYPE=$MAVIS_BUILD_TYPE -DMAVIS_CXX_STANDARD=$MAVIS_CXX_STANDARD + +if [ $? -ne 0 ]; then + echo "ERROR: CMake for mavis failed" + exit 1 +fi + +make -j$(nproc --all) + +if [ $? -ne 0 ]; then + echo "ERROR: build of mavis failed" + exit 1 +fi diff --git a/.github/actions/regress.sh b/.github/actions/regress.sh new file mode 100755 index 00000000..c0411f85 --- /dev/null +++ b/.github/actions/regress.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +cd ${GITHUB_WORKSPACE} +cd $MAVIS_BUILD_TYPE +make -j$(nproc --all) regress + +if [ $? -ne 0 ]; then + echo "ERROR: regress of mavis failed" + exit 1 +fi diff --git a/.github/workflows/build-regress.yaml b/.github/workflows/build-regress.yaml new file mode 100644 index 00000000..8e9381f9 --- /dev/null +++ b/.github/workflows/build-regress.yaml @@ -0,0 +1,75 @@ +on: + pull_request: + branches: + - main + +name: Run Mavis regression on Ubuntu +jobs: + check_json_job: + name: Ubuntu-check-json + runs-on: ubuntu-latest + + steps: + # Install jq + - name: Install jq + run: sudo apt-get update && sudo apt-get install -y jq + + # Get Mavis + - name: Clone Mavis + uses: actions/checkout@v4 + with: + submodules: recursive + + # Run JSON check script + - name: Check JSONs + run: ./scripts/check_jsons + + regress: + strategy: + # Strategy is a matrix of debug and release builds/regression + matrix: + BUILD_TYPE: [Debug,Release] + COMPILER: [gcc,clang] + MAVIS_CXX_STANDARD: [20, 23] + + name: Ubuntu-regress + runs-on: ubuntu-latest + + env: + MAVIS_BUILD_TYPE: ${{ matrix.BUILD_TYPE }} + COMPILER: ${{ matrix.COMPILER }} + MAVIS_CXX_STANDARD: ${{ matrix.MAVIS_CXX_STANDARD }} + + steps: + - name: Setup Git Identity + run: | + git config --global user.email "runner@github.com" + git config --global user.name "Github Runner" + + # Install packages + - name: Install packages + run: | + sudo apt-get update --fix-missing + sudo apt-get install libboost-all-dev llvm lld + + # Get Mavis + - name: Clone Mavis + uses: actions/checkout@v4 + with: + submodules: recursive + + # Build + - name: Build + run: ./.github/actions/build.sh + + # Regress + - name: Regress + run: ./.github/actions/regress.sh + + # Save error logs, etc + - name: Save artifacts + if: failure() + uses: actions/upload-artifact@main + with: + name: ErrorLogs-${{matrix.BUILD_TYPE}}-${{matrix.COMPILER}} + path: ${{matrix.BUILD_TYPE}}/ diff --git a/.github/workflows/check-json.yml b/.github/workflows/check-json.yml deleted file mode 100644 index 1c22546e..00000000 --- a/.github/workflows/check-json.yml +++ /dev/null @@ -1,25 +0,0 @@ -on: - pull_request: - branches: - - main - -name: Check Mavis JSON validity on Ubuntu -jobs: - check_json_job: - name: Ubuntu-check-json - runs-on: ubuntu-latest - - steps: - # Install jq - - name: Install jq - run: sudo apt-get update && sudo apt-get install -y jq - - # Get Mavis - - name: Clone Mavis - uses: actions/checkout@v4 - with: - submodules: recursive - - # Run JSON check script - - name: Check JSONs - run: ./scripts/check_jsons diff --git a/CMakeLists.txt b/CMakeLists.txt index f990e31a..97021496 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,6 +34,12 @@ endif() add_subdirectory(elfio) +if (CMAKE_CXX_COMPILER_ID MATCHES "GNU") + if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 14.0) + add_compile_options(-Wno-template-id-cdtor) + endif() +endif() + add_library(mavis impl/ExtractorRegistry.cpp impl/FormRegistry.cpp @@ -72,6 +78,35 @@ if(NOT COMPILER_SUPPORTS_CONSTEXPR_BITSET) target_include_directories(mavis PUBLIC "${bitset2_SOURCE_DIR}") endif() +# Set up test infrastructure +# Only enable tests if Mavis is the top-level project +if (PROJECT_IS_TOP_LEVEL) + include(CTest) + include(ProcessorCount) + ProcessorCount(NUM_CORES) + + if (NUM_CORES GREATER 8) # Max of 8 cores + set(NUM_CORE, 8) + endif() + if (NOT NUM_CORES EQUAL 0) + set(CTEST_BUILD_FLAGS -j${NUM_CORES}) + set(ctest_test_args ${ctest_test_args} PARALLEL_LEVEL ${NUM_CORES}) + endif() + + add_custom_target(regress) + add_custom_command(TARGET regress POST_BUILD COMMAND ${CMAKE_CTEST_COMMAND}) + set(ENABLE_MAVIS_TESTS True) +else() + set(ENABLE_MAVIS_TESTS False) +endif() + +function (mavis_test name target) + if (ENABLE_MAVIS_TESTS) + add_dependencies(regress ${target}) + add_test(NAME ${name} COMMAND ${ARGN}) + endif() +endfunction (mavis_test) + # Setup common compiler options for the tests and examples add_library(mavis_test_lib INTERFACE) target_link_libraries (mavis_test_lib INTERFACE mavis) @@ -100,6 +135,8 @@ target_compile_options(mavis PUBLIC -fPIC) set(CMAKE_CXX_FLAGS_DEBUG "-U_FORTIFY_SOURCE -O0 -g") set(CMAKE_CXX_FLAGS_FASTDEBUG "-O3 -g") +include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/internal.cmake OPTIONAL) + if(NOT CLANGFORMAT_EXECUTABLE) set(CLANGFORMAT_EXECUTABLE clang-format) endif() diff --git a/README.md b/README.md index d9c53704..b75566c1 100644 --- a/README.md +++ b/README.md @@ -197,15 +197,9 @@ cmake .. cmake .. -DMAVIS_CXX_STANDARD=23 make -j8 ``` -* Build the basic tester; output program is `Mavis` +* Run regression ``` -cd build/test/basic -make -j8 -``` -* Execute the basic tester and compare golden `out` -``` -./Mavis > test.out -diff -s test.out golden.out +make -j8 regress ``` * Build and test with a directed program; output program is diff --git a/cmake/internal.cmake.example b/cmake/internal.cmake.example new file mode 100644 index 00000000..188eec9e --- /dev/null +++ b/cmake/internal.cmake.example @@ -0,0 +1,22 @@ +# This is an example CMake config for including internal-only instruction definitions +# Edit this file as needed and rename it to cmake/internal.cmake + +add_library(internal_instructions + internal/InternalForms.cpp +) + +get_target_property(MAVIS_INCLUDES mavis INCLUDE_DIRECTORIES) +target_include_directories(internal_instructions PRIVATE ${MAVIS_INCLUDES}) + +get_target_property(MAVIS_LIBRARIES mavis LINK_LIBRARIES) +target_link_libraries(internal_instructions PRIVATE ${MAVIS_LIBRARIES}) + +target_compile_definitions(mavis PRIVATE + INTERNAL_EXTRACTOR_REGISTRY=InternalExtractorRegistry + INTERNAL_EXTRACTOR_REGISTRY_HEADER="internal/InternalExtractorRegistry.h" + INTERNAL_FORM_REGISTRY=InternalFormRegistry + INTERNAL_FORM_REGISTRY_HEADER="internal/InternalFormRegistry.h" +) +target_link_libraries(mavis PRIVATE internal_instructions) + +add_subdirectory(internal/test EXCLUDE_FROM_ALL) diff --git a/impl/DTable.tcc b/impl/DTable.tcc index 28184a3a..917dce01 100644 --- a/impl/DTable.tcc +++ b/impl/DTable.tcc @@ -49,7 +49,7 @@ namespace mavis if (const auto it = inst.find("xform"); it != inst.end()) { override_extractor = - extractors_.getExtractor(boost::json::value_to(it->value())); + ExtractorRegistry::getExtractor(boost::json::value_to(it->value())); } // Parse the factory name override, if present @@ -88,36 +88,39 @@ namespace mavis else { const std::string form = boost::json::value_to(inst.at("form")); - const FormBase* form_wrap = forms_.findFormWrapper(form); - if (form_wrap == nullptr) - { - throw BuildErrorUnknownForm(jfile, mnemonic, form); - } - - InstMetaData::PtrType meta = - builder_->makeInstMetaData(mnemonic, inst, !xpand_name.empty(), tags); try { - typename IFactoryIF::PtrType ifact = - build_(form_wrap, mnemonic, istencil, flist, ignore_set, factory_name, - xpand_name, override_extractor, meta); + const auto & form_wrap = FormRegistry::findFormWrapper(form); - // Check for encoding aliases for this factory and add them to the tree - StringListType alias_stencils; - if (const auto alias_it = inst.find("alias"); alias_it != inst.end()) + InstMetaData::PtrType meta = + builder_->makeInstMetaData(mnemonic, inst, !xpand_name.empty(), tags); + try { - alias_stencils = boost::json::value_to(alias_it->value()); - for (const auto & astencil : alias_stencils) + typename IFactoryIF::PtrType ifact = + build_(form_wrap, mnemonic, istencil, flist, ignore_set, factory_name, + xpand_name, override_extractor, meta); + + // Check for encoding aliases for this factory and add them to the tree + StringListType alias_stencils; + if (const auto alias_it = inst.find("alias"); alias_it != inst.end()) { - Opcode opc = stoll(astencil, nullptr, 16); - build_(form_wrap, mnemonic, opc, flist, ignore_set, factory_name, - xpand_name, override_extractor, meta, ifact); + alias_stencils = boost::json::value_to(alias_it->value()); + for (const auto & astencil : alias_stencils) + { + Opcode opc = stoll(astencil, nullptr, 16); + build_(form_wrap, mnemonic, opc, flist, ignore_set, factory_name, + xpand_name, override_extractor, meta, ifact); + } } } + catch (const BuildErrorInstructionAlias & ex) + { + std::cerr << ex.what() << std::endl; + } } - catch (const BuildErrorInstructionAlias & ex) + catch (const RegistryNotFoundException &) { - std::cerr << ex.what() << std::endl; + throw BuildErrorUnknownForm(jfile, mnemonic, form); } } } @@ -256,7 +259,7 @@ namespace mavis template typename IFactoryIF::PtrType DTable::buildLeaf_( - const FormBase* form, + const FormBase::PtrType & form, const typename IFactoryIF::PtrType & curr_node, const std::string & mnemonic, const Opcode istencil, const FieldNameListType & flist, const std::string & factory_name, const std::string & xpand_name, @@ -279,7 +282,7 @@ namespace mavis // the form specified by the FormType template parameter if (override_extractor == nullptr) { - override_extractor = extractors_.getExtractor(form->getName()); + override_extractor = ExtractorRegistry::getExtractor(form->getName()); } // IFactorySpecialCaseComposite *parent = // just for now... @@ -349,7 +352,7 @@ namespace mavis template typename IFactoryIF::PtrType DTable::build_( - const FormBase* form, const std::string & mnemonic, const Opcode istencil, + const FormBase::PtrType & form, const std::string & mnemonic, const Opcode istencil, const FieldNameListType & flist, const FieldNameSetType & ignore_set, const std::string & factory_name, const std::string & xpand_name, const ExtractorIF::PtrType & override_extractor, InstMetaData::PtrType & einfo, @@ -370,7 +373,7 @@ namespace mavis { curr_node->addIFactory( istencil, typename IFactoryIF::PtrType( - new IFactoryDenseComposite(fields[0]))); + new IFactoryDenseComposite(fields[0]))); } curr_node = curr_node->getNode(istencil); // Advance... if (!mavis::utils::notNull(curr_node->getField())->isEquivalent(fields[0])) @@ -420,8 +423,9 @@ namespace mavis { if (curr_node->getDefault() == nullptr) { - curr_node->addDefaultIFactory(typename IFactoryIF::PtrType( - new IFactorySpecialCaseComposite())); + curr_node->addDefaultIFactory( + typename IFactoryIF::PtrType( + new IFactorySpecialCaseComposite())); } curr_node = curr_node->getDefault(); // Advance... } @@ -431,7 +435,7 @@ namespace mavis { curr_node->addIFactory( istencil, typename IFactoryIF::PtrType( - new IFactorySpecialCaseComposite())); + new IFactorySpecialCaseComposite())); } curr_node = curr_node->getNode(istencil); // Advance... } diff --git a/impl/ExtractorRegistry.cpp b/impl/ExtractorRegistry.cpp index 75ac2c57..e38ecb75 100644 --- a/impl/ExtractorRegistry.cpp +++ b/impl/ExtractorRegistry.cpp @@ -1,123 +1,131 @@ #include "mavis/Extractor.h" #include "mavis/ExtractorRegistry.h" +#include "mavis/GenericRegistry.h" +#include "mavis/Utils.h" #include "impl/forms/ExtractorForms.h" #include "impl/forms/ExtractorDerived.h" +#ifdef INTERNAL_EXTRACTOR_REGISTRY +#include INTERNAL_EXTRACTOR_REGISTRY_HEADER +#else +#define INTERNAL_EXTRACTOR_REGISTRY void +#endif + namespace mavis { - const ExtractorIF::PtrType & ExtractorRegistry::getExtractor(const std::string & fname) { - static const std::map EXTRACTOR_REGISTRY = { - {Form_AMO::name, std::make_shared>() }, - {Form_AMO_pair::name, std::make_shared>() }, - {Form_B::name, std::make_shared>() }, - {Form_C0::name, std::make_shared>() }, - {Form_C0_load_double::name, std::make_shared>() }, - {Form_C0_load_word::name, std::make_shared>() }, - {Form_C0_load_word_pair::name, std::make_shared>() }, - {Form_C0_load_byte::name, std::make_shared>() }, - {Form_C0_load_half::name, std::make_shared>() }, - //{Form_C0_store::name, std::make_shared>()}, - {Form_C0_store_double::name, std::make_shared>() }, - {Form_C0_store_word::name, std::make_shared>() }, - {Form_C0_store_word_pair::name, std::make_shared>()}, - {Form_C0_store_byte::name, std::make_shared>() }, - {Form_C0_store_half::name, std::make_shared>() }, - {Form_C1_rsd::name, std::make_shared>() }, - {Form_C1_rsd_I0::name, std::make_shared>() }, - {Form_C1_rsd_zext_I0::name, std::make_shared>() }, - {Form_C1_rsd_Ineg1::name, std::make_shared>() }, - {Form_C1_rsd_I0xFF::name, std::make_shared>() }, - {Form_C2::name, std::make_shared>() }, - {Form_C2_add::name, std::make_shared>() }, - {Form_C2_mv::name, std::make_shared>() }, - {Form_C2_slli::name, std::make_shared>() }, - {Form_C2_sp::name, std::make_shared>() }, - {Form_C2_sp_load_double::name, std::make_shared>() }, - {Form_C2_sp_load_float_single::name, - std::make_shared>() }, - {Form_C2_sp_load_float_double::name, - std::make_shared>() }, - {Form_C2_sp_load_word::name, std::make_shared>() }, - {Form_C2_sp_load_word_pair::name, - std::make_shared>() }, - {Form_C2_sp_store_double::name, std::make_shared>()}, - {Form_C2_sp_store_word::name, std::make_shared>() }, - {Form_C2_sp_store_word_pair::name, - std::make_shared>() }, - {Form_CA::name, std::make_shared>() }, - {Form_CB::name, std::make_shared>() }, - {Form_CI::name, std::make_shared>() }, - {Form_CI_addi::name, std::make_shared>() }, - {Form_CI_addiw::name, std::make_shared>() }, - {Form_CI_rD_only::name, std::make_shared>() }, - {Form_CI_rD_shifted::name, std::make_shared>() }, - {Form_CI_sp::name, std::make_shared>() }, - {Form_CIW::name, std::make_shared>() }, - {Form_CIW_sp::name, std::make_shared>() }, - {Form_CIX::name, std::make_shared>() }, - {Form_CIX_andi::name, std::make_shared>() }, - {Form_CJ::name, std::make_shared>() }, - {Form_CJAL::name, std::make_shared>() }, - {Form_CJR::name, std::make_shared>() }, - {Form_CJALR::name, std::make_shared>() }, - {Form_CMPP::name, std::make_shared>() }, - {Form_CMPP_push::name, std::make_shared>() }, - {Form_CMPP_popretz::name, std::make_shared>() }, - {Form_CMMV_mva01s::name, std::make_shared>() }, - {Form_CMMV_mvsa01::name, std::make_shared>() }, - {Form_CMJT::name, std::make_shared>() }, - {Form_CSR::name, std::make_shared>() }, - {Form_CSRI::name, std::make_shared>() }, - {Form_FENCE::name, std::make_shared>() }, - {Form_HV_load::name, std::make_shared>() }, - {Form_HV_store::name, std::make_shared>() }, - {Form_I::name, std::make_shared>() }, - {Form_I_load::name, std::make_shared>() }, - {Form_I_load_pair::name, std::make_shared>() }, - {Form_I_mv::name, std::make_shared>() }, - {Form_ISH::name, std::make_shared>() }, - {Form_ISHW::name, std::make_shared>() }, - {Form_J::name, std::make_shared>() }, - {Form_MOP::name, std::make_shared>() }, - {Form_MOPC::name, std::make_shared>() }, - {Form_NTL_hint::name, std::make_shared>() }, - {Form_PF_hint::name, std::make_shared>() }, - {Form_R::name, std::make_shared>() }, - {Form_Rfloat::name, std::make_shared>() }, - {Form_Rfloat_fli_h::name, std::make_shared>() }, - {Form_Rfloat_fli_s::name, std::make_shared>() }, - {Form_Rfloat_fli_d::name, std::make_shared>() }, - {Form_Rfloat_fli_q::name, std::make_shared>() }, - {Form_R4::name, std::make_shared>() }, - {Form_S::name, std::make_shared>() }, - {Form_S_Pair::name, std::make_shared>() }, - {Form_Shadow::name, std::make_shared>() }, - {Form_U::name, std::make_shared>() }, - {Form_V::name, std::make_shared>() }, - {Form_VF_mem::name, std::make_shared>() }, - {Form_V_load::name, std::make_shared>() }, - {Form_V_store::name, std::make_shared>() }, - {Form_V_vsetvl::name, std::make_shared>() }, - {Form_V_vsetvli::name, std::make_shared>() }, - {Form_V_vsetivli::name, std::make_shared>() }, - {Form_V_simm::name, std::make_shared>() }, - {Form_V_uimm::name, std::make_shared>() }, - {Form_V_uimm6::name, std::make_shared>() }, - {Form_V_op::name, std::make_shared>() }, - {Form_V_implied::name, std::make_shared>() }, - {Form_V_op_implied::name, std::make_shared>() }, - {Form_V_uimm_implied::name, std::make_shared>() }, - {Form_AES64KSI::name, std::make_shared>() } - }; + // clang-format off + using extractor_registry = GenericRegistry< + INTERNAL_EXTRACTOR_REGISTRY, + Extractor, + Form_AMO, + Form_AMO_pair, + Form_B, + Form_C0, + Form_C0_load_double, + Form_C0_load_word, + Form_C0_load_word_pair, + Form_C0_load_byte, + Form_C0_load_half, + //Form_C0_store, + Form_C0_store_double, + Form_C0_store_word, + Form_C0_store_word_pair, + Form_C0_store_byte, + Form_C0_store_half, + Form_C1_rsd, + Form_C1_rsd_I0, + Form_C1_rsd_zext_I0, + Form_C1_rsd_Ineg1, + Form_C1_rsd_I0xFF, + Form_C2, + Form_C2_add, + Form_C2_mv, + Form_C2_slli, + Form_C2_sp, + Form_C2_sp_load_double, + Form_C2_sp_load_float_single, + Form_C2_sp_load_float_double, + Form_C2_sp_load_word, + Form_C2_sp_load_word_pair, + Form_C2_sp_store_double, + Form_C2_sp_store_word, + Form_C2_sp_store_word_pair, + Form_CA, + Form_CB, + Form_CI, + Form_CI_addi, + Form_CI_addiw, + Form_CI_rD_only, + Form_CI_rD_shifted, + Form_CI_sp, + Form_CIW, + Form_CIW_sp, + Form_CIX, + Form_CIX_andi, + Form_CJ, + Form_CJAL, + Form_CJR, + Form_CJALR, + Form_CMPP, + Form_CMPP_push, + Form_CMPP_popretz, + Form_CMMV_mva01s, + Form_CMMV_mvsa01, + Form_CMJT, + Form_CSR, + Form_CSRI, + Form_FENCE, + Form_HV_load, + Form_HV_store, + Form_I, + Form_I_load, + Form_I_load_pair, + Form_I_mv, + Form_ISH, + Form_ISHW, + Form_J, + Form_MOP, + Form_MOPC, + Form_NTL_hint, + Form_PF_hint, + Form_R, + Form_Rfloat, + Form_Rfloat_fli_h, + Form_Rfloat_fli_s, + Form_Rfloat_fli_d, + Form_Rfloat_fli_q, + Form_R4, + Form_S, + Form_S_Pair, + Form_Shadow, + Form_U, + Form_V, + Form_VF_mem, + Form_V_load, + Form_V_store, + Form_V_vsetvl, + Form_V_vsetvli, + Form_V_vsetivli, + Form_V_simm, + Form_V_uimm, + Form_V_uimm6, + Form_V_op, + Form_V_implied, + Form_V_op_implied, + Form_V_uimm_implied, + Form_AES64KSI + >; + // clang-format on - const auto itr = EXTRACTOR_REGISTRY.find(fname); - if (itr == EXTRACTOR_REGISTRY.end()) + try + { + return extractor_registry::get(fname); + } + catch (const RegistryNotFoundException &) { throw BuildErrorUnknownForm(fname); } - return itr->second; } - } // namespace mavis diff --git a/impl/FormRegistry.cpp b/impl/FormRegistry.cpp index c8b6a880..24b9e957 100644 --- a/impl/FormRegistry.cpp +++ b/impl/FormRegistry.cpp @@ -1,72 +1,61 @@ #include "mavis/FormRegistry.h" +#include "mavis/GenericRegistry.h" #include "impl/forms/CommonForms.h" #include "impl/forms/CompressedForms.h" #include "impl/forms/VectorForms.h" -#include + +#ifdef INTERNAL_FORM_REGISTRY +#include INTERNAL_FORM_REGISTRY_HEADER +#else +#define INTERNAL_FORM_REGISTRY void +#endif namespace mavis { - - static const std::map form_registry_ = { - {Form_AMO::name, new Form() }, - {Form_B::name, new Form() }, - {Form_C0::name, new Form() }, - {Form_C1::name, new Form() }, - {Form_C2::name, new Form() }, - {Form_CA::name, new Form() }, - {Form_CB::name, new Form() }, - {Form_CI::name, new Form() }, - {Form_CI_rD_only::name, new Form()}, - {Form_CIW::name, new Form() }, - {Form_CIX::name, new Form() }, - {Form_CJ::name, new Form() }, - {Form_CJR::name, new Form() }, - // {Form_CM::name, new Form() }, - {Form_CMPP::name, new Form() }, - {Form_CMJT::name, new Form() }, - {Form_CSR::name, new Form() }, - {Form_CSRI::name, new Form() }, - {Form_FENCE::name, new Form() }, - {Form_I::name, new Form() }, - {Form_ISH::name, new Form() }, - {Form_ISHW::name, new Form() }, - {Form_J::name, new Form() }, - {Form_R::name, new Form() }, - {Form_Rfloat::name, new Form() }, - {Form_R4::name, new Form() }, - {Form_S::name, new Form() }, - {Form_U::name, new Form() }, - {Form_V::name, new Form() }, - {Form_VF_mem::name, new Form() }, - {Form_V_vsetvl::name, new Form() }, - {Form_V_vsetvli::name, new Form() }, - {Form_V_vsetivli::name, new Form()}, - {Form_V_uimm6::name, new Form() }, - {Form_AES64KSI::name, new Form() } - }; - - const FormBase* FormRegistry::findFormWrapper(const std::string & fname) + const FormBase::PtrType & FormRegistry::findFormWrapper(const std::string & fname) { - const auto itr = form_registry_.find(fname); - if (itr != form_registry_.end()) - { - return itr->second; - } - else - { - return nullptr; - } - } + // clang-format off + using form_registry = GenericRegistry< + INTERNAL_FORM_REGISTRY, + Form, + Form_AMO, + Form_B, + Form_C0, + Form_C1, + Form_C2, + Form_CA, + Form_CB, + Form_CI, + Form_CI_rD_only, + Form_CIW, + Form_CIX, + Form_CJ, + Form_CJR, + Form_CMPP, + Form_CMJT, + Form_CSR, + Form_CSRI, + Form_FENCE, + Form_I, + Form_ISH, + Form_ISHW, + Form_J, + Form_R, + Form_Rfloat, + Form_R4, + Form_S, + Form_U, + Form_V, + Form_VF_mem, + Form_V_vsetvl, + Form_V_vsetvli, + Form_V_vsetivli, + Form_V_uimm6, + Form_AES64KSI + >; + // clang-format on - const FormBase* FormRegistry::getFormWrapper(const std::string & fname) - { - const auto itr = form_registry_.find(fname); - if (itr == form_registry_.end()) - { - throw BuildErrorUnknownForm(fname); - } - return itr->second; + return form_registry::get(fname); } - } // namespace mavis diff --git a/mavis/DTable.h b/mavis/DTable.h index a5aff911..e9094c93 100644 --- a/mavis/DTable.h +++ b/mavis/DTable.h @@ -398,8 +398,6 @@ namespace mavis typename IFactoryIF::PtrType root_ = nullptr; typename IFactoryBuilder::PtrType builder_; - ExtractorRegistry extractors_; - FormRegistry forms_; // Toplevel caches. Both are "tagged" by the opcode: // 1. icache_: Cache of instruction object prototypes (for makeInst) @@ -411,7 +409,7 @@ namespace mavis const std::string & mnemonic, const MatchSet & tags); typename IFactoryIF::PtrType - buildLeaf_(const FormBase* form, + buildLeaf_(const FormBase::PtrType & form, const typename IFactoryIF::PtrType & curr_node, const std::string & mnemonic, Opcode istencil, const FieldNameListType & flist, const std::string & factory_name, const std::string & xpand_name, @@ -419,7 +417,7 @@ namespace mavis const typename IFactoryIF::PtrType & shared_ifact); typename IFactoryIF::PtrType - build_(const FormBase* form, const std::string & mnemonic, Opcode istencil, + build_(const FormBase::PtrType & form, const std::string & mnemonic, Opcode istencil, const FieldNameListType & flist, const FieldNameSetType & ignore_set, const std::string & factory_name, const std::string & xpand_name, const ExtractorIF::PtrType & override_extractor, InstMetaData::PtrType & einfo, diff --git a/mavis/DecoderExceptions.h b/mavis/DecoderExceptions.h index 8ef36190..9f4ddf5d 100644 --- a/mavis/DecoderExceptions.h +++ b/mavis/DecoderExceptions.h @@ -101,6 +101,10 @@ namespace mavis } }; + class RegistryNotFoundException : public BaseException + { + }; + /** * DTable build error: Occurs when building the TRIE branches (non-leaves) * and we're trying to add an instruction whose field is incompatible with diff --git a/mavis/ExtensionManager.hpp b/mavis/ExtensionManager.hpp index c11c4773..699bdddf 100644 --- a/mavis/ExtensionManager.hpp +++ b/mavis/ExtensionManager.hpp @@ -20,7 +20,7 @@ #endif // Clang versions prior to 16 had a bug that broke std::views -#if(__clang__ && __clang_major__ < 16) +#if (__clang__ && __clang_major__ < 16) #define STD_VIEWS_BUG 1 #endif @@ -444,94 +444,81 @@ namespace mavis::extension_manager // Mimics the behavior of std::views::filter class FilteredView { - public: - class iterator - { - private: - const bool include_meta_; - const EnabledMap::const_iterator end_it_; - EnabledMap::const_iterator it_; - - void advance_() - { - while(it_ != end_it_ && !filterExt_(*it_, include_meta_)) - { - ++it_; - } - } - - public: - iterator(const EnabledMap::const_iterator& it, const EnabledMap::const_iterator& end_it, const bool include_meta) : - include_meta_(include_meta), - end_it_(end_it), - it_(it) - { - advance_(); - } - - explicit iterator(const EnabledMap::const_iterator& end_it) : - include_meta_(false), - end_it_(end_it), - it_(end_it) - { - } - - iterator& operator++() - { - ++it_; - advance_(); - return *this; - } - - iterator operator++(int) - { - const auto temp = *this; - ++it_; - advance_(); - return temp; - } - - const EnabledMap::value_type& operator*() const - { - return *it_; - } - - const EnabledMap::value_type* operator->() const - { - return it_.operator->(); - } - - bool operator==(const iterator& rhs) const - { - return it_ == rhs.it_; - } - - bool operator!=(const iterator& rhs) const - { - return it_ != rhs.it_; - } - }; - - private: - const EnabledMap& enabled_extensions_; + public: + class iterator + { + private: const bool include_meta_; + const EnabledMap::const_iterator end_it_; + EnabledMap::const_iterator it_; - public: - explicit FilteredView(const EnabledMap& enabled_extensions, const bool include_meta) : - enabled_extensions_(enabled_extensions), - include_meta_(include_meta) + void advance_() { + while (it_ != end_it_ && !filterExt_(*it_, include_meta_)) + { + ++it_; + } } - iterator begin() const + public: + iterator(const EnabledMap::const_iterator & it, + const EnabledMap::const_iterator & end_it, const bool include_meta) : + include_meta_(include_meta), + end_it_(end_it), + it_(it) { - return iterator(enabled_extensions_.begin(), enabled_extensions_.end(), include_meta_); + advance_(); } - iterator end() const + explicit iterator(const EnabledMap::const_iterator & end_it) : + include_meta_(false), + end_it_(end_it), + it_(end_it) { - return iterator(enabled_extensions_.end()); } + + iterator & operator++() + { + ++it_; + advance_(); + return *this; + } + + iterator operator++(int) + { + const auto temp = *this; + ++it_; + advance_(); + return temp; + } + + const EnabledMap::value_type & operator*() const { return *it_; } + + const EnabledMap::value_type* operator->() const { return it_.operator->(); } + + bool operator==(const iterator & rhs) const { return it_ == rhs.it_; } + + bool operator!=(const iterator & rhs) const { return it_ != rhs.it_; } + }; + + private: + const EnabledMap & enabled_extensions_; + const bool include_meta_; + + public: + explicit FilteredView(const EnabledMap & enabled_extensions, const bool include_meta) : + enabled_extensions_(enabled_extensions), + include_meta_(include_meta) + { + } + + iterator begin() const + { + return iterator(enabled_extensions_.begin(), enabled_extensions_.end(), + include_meta_); + } + + iterator end() const { return iterator(enabled_extensions_.end()); } }; #endif @@ -1034,7 +1021,6 @@ namespace mavis::extension_manager #else (void)dep; #endif - } template @@ -1501,7 +1487,59 @@ namespace mavis::extension_manager virtual std::string getISAFromELF_(const std::string & elf) const = 0; - virtual void setISASpecJSONImpl_(const std::string &) {}; + void setISASpecJSON_(const std::string & jfile) + { + const boost::json::value json = parseJSONWithException(jfile); + + try + { + const auto & jobj = json.as_object(); + + if (auto meta_extensions_it = jobj.find("meta_extensions"); + meta_extensions_it != jobj.end()) + { + for (const auto & meta_ext_obj : meta_extensions_it->value().as_array()) + { + processExtension_(meta_ext_obj.as_object()); + } + } + + if (auto config_extensions_it = jobj.find("config_extensions"); + config_extensions_it != jobj.end()) + { + for (const auto & config_ext_obj : config_extensions_it->value().as_array()) + { + processExtension_(config_ext_obj.as_object()); + } + } + + if (auto extensions_it = jobj.find("extensions"); extensions_it != jobj.end()) + { + for (const auto & ext_obj : extensions_it->value().as_array()) + { + processExtension_(ext_obj.as_object()); + } + } + + setISASpecJSONImpl_(jobj); + + if (auto includes_it = jobj.find("includes"); includes_it != jobj.end()) + { + for (const auto & include : includes_it->value().as_array()) + { + setISASpecJSON_(mavis_json_dir_ + "/" + + std::string(include.as_string().c_str())); + } + } + } + catch (const ExtensionManagerException &) + { + std::cerr << "Error parsing file " << jfile << std::endl; + throw; + } + } + + virtual void setISASpecJSONImpl_(const boost::json::object &) {}; virtual void setISAImpl_(const std::string & isa) = 0; @@ -1798,7 +1836,7 @@ namespace mavis::extension_manager ExtensionManager(ExtensionManager &&) = default; virtual ~ExtensionManager() = default; - const std::unordered_set& getUnknownExtensions() const + const std::unordered_set & getUnknownExtensions() const { return unknown_extensions_; } @@ -1806,46 +1844,7 @@ namespace mavis::extension_manager void setISASpecJSON(const std::string & jfile, const std::string & mavis_json_dir) { mavis_json_dir_ = mavis_json_dir; - - const boost::json::value json = parseJSONWithException(jfile); - - try - { - const auto & jobj = json.as_object(); - - if (auto meta_extensions_it = jobj.find("meta_extensions"); - meta_extensions_it != jobj.end()) - { - for (const auto & meta_ext_obj : meta_extensions_it->value().as_array()) - { - processExtension_(meta_ext_obj.as_object()); - } - } - - if (auto config_extensions_it = jobj.find("config_extensions"); - config_extensions_it != jobj.end()) - { - for (const auto & config_ext_obj : config_extensions_it->value().as_array()) - { - processExtension_(config_ext_obj.as_object()); - } - } - - if (auto extensions_it = jobj.find("extensions"); extensions_it != jobj.end()) - { - for (const auto & ext_obj : extensions_it->value().as_array()) - { - processExtension_(ext_obj.as_object()); - } - } - } - catch (const ExtensionManagerException &) - { - std::cerr << "Error parsing file " << jfile << std::endl; - throw; - } - - setISASpecJSONImpl_(jfile); + setISASpecJSON_(jfile); } void setISAFromELF(const std::string & elf) { setISA(getISAFromELF_(elf)); } @@ -1882,7 +1881,8 @@ namespace mavis::extension_manager return ExtensionMapView(enabled_extensions_, include_meta_extensions); } - void registerExtensionChangeCallback(std::function &, bool)> cb) + void registerExtensionChangeCallback( + std::function &, bool)> cb) { extensions_changed_callback_ = cb; } diff --git a/mavis/Extractor.h b/mavis/Extractor.h index 590424e5..80f3d555 100644 --- a/mavis/Extractor.h +++ b/mavis/Extractor.h @@ -4,6 +4,7 @@ #include "OperandInfo.hpp" #include "DecoderConsts.h" #include "Swizzler.hpp" +#include "GenericRegistryTraits.h" #include #include @@ -455,6 +456,13 @@ namespace mavis // Only specialized Forms will compile template class Extractor; + template<> + struct GenericRegistryTraits + { + using BaseType = ExtractorIF; + using PtrType = ExtractorIF::PtrType; + }; + /** * ExtractorIF ostream insertion operator * @param os diff --git a/mavis/Form.h b/mavis/Form.h index 9c767a4c..60d775c2 100644 --- a/mavis/Form.h +++ b/mavis/Form.h @@ -4,6 +4,7 @@ #include "Field.h" #include "mavis/DecoderExceptions.h" #include "Float.h" +#include "GenericRegistryTraits.h" namespace mavis { @@ -64,6 +65,9 @@ namespace mavis class FormBase { public: + using PtrType = std::unique_ptr; + + virtual ~FormBase() = default; virtual std::string getName() const = 0; virtual const Field & getField(const uint32_t fid) const = 0; virtual const Field & getField(const std::string & fname) const = 0; @@ -139,6 +143,13 @@ namespace mavis } }; + template<> + struct GenericRegistryTraits
+ { + using BaseType = FormBase; + using PtrType = FormBase::PtrType; + }; + inline std::ostream & operator<<(std::ostream & os, const FormBase & form) { form.print(os); diff --git a/mavis/FormRegistry.h b/mavis/FormRegistry.h index 015bbdbb..44990c7e 100644 --- a/mavis/FormRegistry.h +++ b/mavis/FormRegistry.h @@ -12,8 +12,7 @@ namespace mavis class FormRegistry { public: - static const FormBase* findFormWrapper(const std::string & fname); - static const FormBase* getFormWrapper(const std::string & fname); + static const FormBase::PtrType & findFormWrapper(const std::string & fname); }; } // namespace mavis diff --git a/mavis/GenericRegistry.h b/mavis/GenericRegistry.h new file mode 100644 index 00000000..ed7e9cd1 --- /dev/null +++ b/mavis/GenericRegistry.h @@ -0,0 +1,75 @@ +#pragma once + +#include +#include "DecoderExceptions.h" +#include "GenericRegistryTraits.h" +#include "Utils.h" + +namespace mavis +{ + // For each KeyType in KeyTypes..., maps KeyType::name to a smart pointer managing a + // WrapperType The wrapper type must have a non-templated base type A specialization + // must be defined for GenericRegistryTraits as follows: + // template<> struct + // GenericRegistryTraits + // { + // using BaseType = base type; + // using PtrType = smart pointer; + // }; + // ChildRegistry can be set to a non-void GenericRegistry specialization in order to add + // additional registrations that might be defined in a different header + template typename WrapperType, + typename... KeyTypes> + class GenericRegistry + { + private: + using registry_traits = GenericRegistryTraits; + using base_type = typename registry_traits::BaseType; + using ptr_type = typename registry_traits::PtrType; + using ptr_traits = utils::smart_ptr_traits; + + static_assert(std::conjunction_v>...>, + "Every specialization of WrapperType... must derive from " + "ptr_type::element_type"); + + GenericRegistry() = default; + + public: + static const ptr_type & get(const std::string & name) + { + static const std::unordered_map registry = [] + { + std::unordered_map registry; + + // Calls emplace for every type in KeyTypes + (registry.emplace(KeyTypes::name, + ptr_traits::template construct>()), + ...); + + return registry; + }(); + + const auto it = registry.find(name); + + // If there is no child registry, throw an exception + if constexpr (std::is_void_v) + { + if (it == registry.end()) [[unlikely]] + { + throw RegistryNotFoundException(); + } + } + // Otherwise, check the child registry + else + { + if (it == registry.end()) + { + return ChildRegistry::get(name); + } + } + + return it->second; + } + }; +} // namespace mavis diff --git a/mavis/GenericRegistryTraits.h b/mavis/GenericRegistryTraits.h new file mode 100644 index 00000000..4fad6d18 --- /dev/null +++ b/mavis/GenericRegistryTraits.h @@ -0,0 +1,6 @@ +#pragma once + +namespace mavis +{ + template