diff --git a/fusion/CMakeLists.txt b/fusion/CMakeLists.txt new file mode 100644 index 00000000..98766a18 --- /dev/null +++ b/fusion/CMakeLists.txt @@ -0,0 +1,25 @@ +cmake_minimum_required(VERSION 3.17) +project(FusionProject) + +set(CMAKE_POSITION_INDEPENDENT_CODE ON) +set(FUSION_TOP ${CMAKE_SOURCE_DIR}) + +add_subdirectory(fusion) +add_subdirectory(test) + +find_package(Doxygen) +if(DOXYGEN_FOUND) + set(DOXYGEN_IN ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile) + set(DOXYGEN_OUT ${CMAKE_CURRENT_BINARY_DIR}/Doxygen) + + configure_file(${DOXYGEN_IN} ${DOXYGEN_OUT} @ONLY) + + add_custom_target(docs + COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_OUT} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMENT "Generating API documentation with Doxygen" + VERBATIM) +else() + message("Doxygen needs to be installed to generate documentation") +endif() + diff --git a/fusion/Doxyfile b/fusion/Doxyfile new file mode 100644 index 00000000..a39946ba --- /dev/null +++ b/fusion/Doxyfile @@ -0,0 +1,401 @@ +# Doxyfile 1.9.1 + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- +DOXYFILE_ENCODING = UTF-8 +PROJECT_NAME = "CAM Fusion API" +PROJECT_NUMBER = +PROJECT_BRIEF = +PROJECT_LOGO = +OUTPUT_DIRECTORY = +CREATE_SUBDIRS = NO +ALLOW_UNICODE_NAMES = NO +OUTPUT_LANGUAGE = English +#OUTPUT_TEXT_DIRECTION = None +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = YES +STRIP_FROM_PATH = +STRIP_FROM_INC_PATH = +SHORT_NAMES = NO +JAVADOC_AUTOBRIEF = NO +JAVADOC_BANNER = NO +QT_AUTOBRIEF = NO +MULTILINE_CPP_IS_BRIEF = NO +PYTHON_DOCSTRING = YES +INHERIT_DOCS = YES +SEPARATE_MEMBER_PAGES = NO +TAB_SIZE = 4 +ALIASES = +OPTIMIZE_OUTPUT_FOR_C = NO +OPTIMIZE_OUTPUT_JAVA = NO +OPTIMIZE_FOR_FORTRAN = NO +OPTIMIZE_OUTPUT_VHDL = NO +OPTIMIZE_OUTPUT_SLICE = NO +EXTENSION_MAPPING = +MARKDOWN_SUPPORT = YES +TOC_INCLUDE_HEADINGS = 5 +AUTOLINK_SUPPORT = YES +BUILTIN_STL_SUPPORT = NO +CPP_CLI_SUPPORT = NO +SIP_SUPPORT = NO +IDL_PROPERTY_SUPPORT = YES +DISTRIBUTE_GROUP_DOC = YES +GROUP_NESTED_COMPOUNDS = NO +SUBGROUPING = YES +INLINE_GROUPED_CLASSES = NO +INLINE_SIMPLE_STRUCTS = NO +TYPEDEF_HIDES_STRUCT = NO +LOOKUP_CACHE_SIZE = 0 +NUM_PROC_THREADS = 1 +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- +EXTRACT_ALL = NO +EXTRACT_PRIVATE = NO +EXTRACT_PRIV_VIRTUAL = NO +EXTRACT_PACKAGE = NO +EXTRACT_STATIC = NO +EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_METHODS = NO +EXTRACT_ANON_NSPACES = NO +RESOLVE_UNNAMED_PARAMS = YES +HIDE_UNDOC_MEMBERS = NO +HIDE_UNDOC_CLASSES = NO +HIDE_FRIEND_COMPOUNDS = NO +HIDE_IN_BODY_DOCS = NO +INTERNAL_DOCS = NO +CASE_SENSE_NAMES = YES +HIDE_SCOPE_NAMES = NO +HIDE_COMPOUND_REFERENCE= NO +SHOW_INCLUDE_FILES = YES +SHOW_GROUPED_MEMB_INC = NO +FORCE_LOCAL_INCLUDES = NO +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +SORT_BRIEF_DOCS = NO +SORT_MEMBERS_CTORS_1ST = NO +SORT_GROUP_NAMES = NO +SORT_BY_SCOPE_NAME = NO +STRICT_PROTO_MATCHING = NO +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +SHOW_USED_FILES = YES +SHOW_FILES = YES +SHOW_NAMESPACES = YES +FILE_VERSION_FILTER = +LAYOUT_FILE = +CITE_BIB_FILES = +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +QUIET = NO +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_IF_DOC_ERROR = YES +WARN_NO_PARAMDOC = NO +WARN_AS_ERROR = NO +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- +INPUT = \ + ./fusion/FieldExtractor.hpp \ + ./fusion/Fusion.hpp \ + ./fusion/FusionContext.hpp \ + ./fusion/FusionExceptions.hpp \ + ./fusion/FusionGroup.hpp \ + ./fusion/FusionTypes.hpp \ + ./fusion/MachineInfo.hpp \ + ./fusion/RadixTrie.hpp \ + ./test/Msg.hpp \ + ./test/Options.hpp \ + ./test/TestBench.hpp \ + README.md + +INPUT_ENCODING = UTF-8 +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.idl \ + *.ddl \ + *.odl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.cs \ + *.d \ + *.php \ + *.php4 \ + *.php5 \ + *.phtml \ + *.inc \ + *.m \ + *.markdown \ + *.md \ + *.mm \ + *.dox \ + *.py \ + *.pyw \ + *.f90 \ + *.f95 \ + *.f03 \ + *.f08 \ + *.f18 \ + *.f \ + *.for \ + *.vhd \ + *.vhdl \ + *.ucf \ + *.qsf \ + *.ice +RECURSIVE = NO +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = +EXCLUDE_SYMBOLS = +EXAMPLE_PATH = +EXAMPLE_PATTERNS = * +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = +INPUT_FILTER = +FILTER_PATTERNS = +FILTER_SOURCE_FILES = NO +FILTER_SOURCE_PATTERNS = +USE_MDFILE_AS_MAINPAGE = README.md +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- +SOURCE_BROWSER = NO +INLINE_SOURCES = NO +STRIP_CODE_COMMENTS = YES +REFERENCED_BY_RELATION = NO +REFERENCES_RELATION = NO +REFERENCES_LINK_SOURCE = YES +SOURCE_TOOLTIPS = YES +USE_HTAGS = NO +VERBATIM_HEADERS = YES +#CLANG_ASSISTED_PARSING = NO +#CLANG_ADD_INC_PATHS = YES +#CLANG_OPTIONS = +#CLANG_DATABASE_PATH = +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- +ALPHABETICAL_INDEX = YES +IGNORE_PREFIX = +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- +GENERATE_HTML = YES +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_EXTRA_STYLESHEET = +HTML_EXTRA_FILES = +HTML_COLORSTYLE_HUE = 220 +HTML_COLORSTYLE_SAT = 100 +HTML_COLORSTYLE_GAMMA = 80 +#HTML_TIMESTAMP = NO +HTML_DYNAMIC_MENUS = YES +HTML_DYNAMIC_SECTIONS = NO +HTML_INDEX_NUM_ENTRIES = 100 +GENERATE_DOCSET = NO +DOCSET_FEEDNAME = "Doxygen generated docs" +DOCSET_BUNDLE_ID = org.doxygen.Project +DOCSET_PUBLISHER_ID = org.doxygen.Publisher +DOCSET_PUBLISHER_NAME = Publisher +GENERATE_HTMLHELP = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +CHM_INDEX_ENCODING = +BINARY_TOC = NO +TOC_EXPAND = NO +GENERATE_QHP = NO +QCH_FILE = +QHP_NAMESPACE = org.doxygen.Project +QHP_VIRTUAL_FOLDER = doc +QHP_CUST_FILTER_NAME = +QHP_CUST_FILTER_ATTRS = +QHP_SECT_FILTER_ATTRS = +QHG_LOCATION = +GENERATE_ECLIPSEHELP = NO +ECLIPSE_DOC_ID = org.doxygen.Project +DISABLE_INDEX = NO +GENERATE_TREEVIEW = NO +ENUM_VALUES_PER_LINE = 4 +TREEVIEW_WIDTH = 250 +EXT_LINKS_IN_WINDOW = NO +HTML_FORMULA_FORMAT = png +FORMULA_FONTSIZE = 10 +#FORMULA_TRANSPARENT = YES +FORMULA_MACROFILE = +USE_MATHJAX = NO +MATHJAX_FORMAT = HTML-CSS +MATHJAX_RELPATH = https://cdn.jsdelivr.net/npm/mathjax@2 +MATHJAX_EXTENSIONS = +MATHJAX_CODEFILE = +SEARCHENGINE = YES +SERVER_BASED_SEARCH = NO +EXTERNAL_SEARCH = NO +SEARCHENGINE_URL = +SEARCHDATA_FILE = searchdata.xml +EXTERNAL_SEARCH_ID = +EXTRA_SEARCH_MAPPINGS = +#--------------------------------------------------------------------------- +# Configuration options related to the LaTeX output +#--------------------------------------------------------------------------- +GENERATE_LATEX = YES +LATEX_OUTPUT = latex +LATEX_CMD_NAME = +MAKEINDEX_CMD_NAME = makeindex +LATEX_MAKEINDEX_CMD = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = a4 +EXTRA_PACKAGES = +LATEX_HEADER = +LATEX_FOOTER = +LATEX_EXTRA_STYLESHEET = +LATEX_EXTRA_FILES = +PDF_HYPERLINKS = YES +USE_PDFLATEX = YES +LATEX_BATCHMODE = NO +LATEX_HIDE_INDICES = NO +#LATEX_SOURCE_CODE = NO +LATEX_BIB_STYLE = plain +#LATEX_TIMESTAMP = NO +LATEX_EMOJI_DIRECTORY = +#--------------------------------------------------------------------------- +# Configuration options related to the RTF output +#--------------------------------------------------------------------------- +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = +#RTF_SOURCE_CODE = NO +#--------------------------------------------------------------------------- +# Configuration options related to the man page output +#--------------------------------------------------------------------------- +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_SUBDIR = +MAN_LINKS = NO +#--------------------------------------------------------------------------- +# Configuration options related to the XML output +#--------------------------------------------------------------------------- +GENERATE_XML = NO +XML_OUTPUT = xml +XML_PROGRAMLISTING = YES +XML_NS_MEMB_FILE_SCOPE = NO +#--------------------------------------------------------------------------- +# Configuration options related to the DOCBOOK output +#--------------------------------------------------------------------------- +GENERATE_DOCBOOK = NO +DOCBOOK_OUTPUT = docbook +#DOCBOOK_PROGRAMLISTING = NO +#--------------------------------------------------------------------------- +# Configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- +GENERATE_AUTOGEN_DEF = NO +#--------------------------------------------------------------------------- +# Configuration options related to the Perl module output +#--------------------------------------------------------------------------- +GENERATE_PERLMOD = NO +PERLMOD_LATEX = NO +PERLMOD_PRETTY = YES +PERLMOD_MAKEVAR_PREFIX = +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = NO +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES +#--------------------------------------------------------------------------- +# Configuration options related to external references +#--------------------------------------------------------------------------- +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +EXTERNAL_PAGES = YES +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- +#CLASS_DIAGRAMS = YES +DIA_PATH = +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = YES +DOT_NUM_THREADS = 0 +#DOT_FONTNAME = Helvetica +#DOT_FONTSIZE = 10 +DOT_FONTPATH = +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +GROUP_GRAPHS = YES +UML_LOOK = NO +UML_LIMIT_NUM_FIELDS = 10 +DOT_UML_DETAILS = NO +DOT_WRAP_THRESHOLD = 17 +TEMPLATE_RELATIONS = NO +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +CALL_GRAPH = NO +CALLER_GRAPH = NO +GRAPHICAL_HIERARCHY = YES +DIRECTORY_GRAPH = YES +DOT_IMAGE_FORMAT = png +INTERACTIVE_SVG = NO +DOT_PATH = +DOTFILE_DIRS = +MSCFILE_DIRS = +DIAFILE_DIRS = +PLANTUML_JAR_PATH = +PLANTUML_CFG_FILE = +PLANTUML_INCLUDE_PATH = +DOT_GRAPH_MAX_NODES = 50 +MAX_DOT_GRAPH_DEPTH = 0 +#DOT_TRANSPARENT = NO +DOT_MULTI_TARGETS = NO +GENERATE_LEGEND = YES +DOT_CLEANUP = YES diff --git a/fusion/README.md b/fusion/README.md new file mode 100644 index 00000000..93588243 --- /dev/null +++ b/fusion/README.md @@ -0,0 +1,150 @@ +# Fusion API draft + +``` +Contact: Jeff Nye + Condor Computing + 2023.12.12 +``` + +There are (will be) two interfaces provided to support implementation of fusion and fracture transforms in Map/Sparta based models, the examples below will be from the CAM/Olympia implementations. + +# Changes + +A summary of some recent changes is below. This is an overview. + +## From FusionSequence to FusionGroup + +\note As the requirements on ordering are relaxed the term 'sequence' +became less accurate. I am switching to 'fusion group' or just group, +but there may be some text/code that still uses the term sequence. + +## From Init/Search/ConstraintsTransform to just Transform + +\note previously the transform process had multiple functors for +each expected phase of the fusion process. This was restrictive for an +API. There is now a single functor for transform specified within +a FusionGroup and a single functor for the top level fusion operator, see below. + +### Similar functor methods in top level + +\note The top level fusion operation is wrapped into a single +functor called Fusion::fusionOperator. This encompasses the complete +fusion operation for a list of Inst::PtrTypes. The default fusion operation +is a simple append. The intent is fusionOperator is overridden with a +user defined method that matches the functor signature, declared in +Fusion. + + +``` + + using FusionFuncType = std::function; + + + static void defaultFusionOpr(Fusion & inst, InstPtrListType & in, + InstPtrListType & out) + { + out.insert(out.end(), in.begin(), in.end()); + in.clear(); + } + +``` + +# Usage of the draft PR + +Pull the DPR, build and run the local testbench, create the doxygen docs. + +The dpr is on branch jeffnye-gh/fusion_dpr + +``` + +cd riscv-perf-model +mkdir release; cd release; cmake .. -DCMAKE_BUILD_TYPE=Release + + +cd ../fusion +mkdir -p release; cd release; cmake .. +make -j32 regress +make docs +``` + +# C++ API and testbench + +A C++ API is defined and a baseline set of implementations are provided. +The implementations are linked to the performance model using the conventional +callback mechanism. The C++ API is intended for compiled execution, regressions, etc. + +The testbench is contained in TestBench, test/testbench.h. + +## C++ API summary + + + The primary classes in the API are: + +``` + Fusion + FusionGroup (FusionGroupCfg) + MachineInfo + FieldExtractor +``` + +The top level class is Fusion, fusion/fusion.h. This is the primary +interface to Sparta based decoders. + +FusionGroup holds the definition of a fusable group of instructions. FusionGroup +provides functor handles for user defined methods. FusionGroupCfg is a +ctor helper struct/class for FusionGroup. + +MachineInfo is a placeholder (currently) for machine microarchitecture information pertinent to fusion and fusion performance modeling. + +FieldExtractor is a shim between FusionGroup functors and Mavis. It provides +boolean and field extraction methods. I expect this to change, either remove +the shim or increase the number of FieldExtractor setter/getter methods. + +There are multiple input mechanisms intended to simplify implementing +the fusion operation. Currently I am using UID. There will be support +for opcodes, assembly text, a DSL syntax and possibly a Json input format. + +UID based fusion is the example in this draft. The others are planned. + +## Other classes + +The support classes for Fusion are: + +``` + FusionContext fusioncontext.h + FusionExceptionBase fusionexceptions.h + using and typedefs fusiontypes.h + RadixTrie radixtrie.h +``` + +There is a single context supported in this version of the implementation but +generalization to multiple contexts has placeholders in the source. + +FusionExceptionBase is the parent for a a number of fusion/dsl/json/run-time exceptions specific to fusion. + +RadixTrie is experimental. It is implemented and provided for performance analysis during development. The currently used container is an unordered\_map +used for functionality and test development. + +## Usage within a Sparta unit + +The currently considered procedure for Fusion is an implementation in a +Sparta unit typically in the Decode. The primary input to the +fusionOperator() is a form of the InstQueue, sparta::Queue. +(Other forms such as InstGroup are likely). An output buffer of similar type is also provided to fusionOperator(). The fusionOperator() performs the +search across the current FusionGroup context and executes the transform +functor in the FusionGroup and places the results as appropriate in the output +queue and modifies the input queue as needed. + +The format of the input/output queues will likely be adjusted or additional +functor signatures will be provided. + +Both the top level and FusionGroup functors can be overridden by the user. The +API provides the functor signature and handle but otherwise the API does +not constrain how the functors operate. + +This gives the necessary generality and extensibility while keeping the +API definition unobtrusive. + +The remainder of the code and support classes are template based. + diff --git a/fusion/fusion/CMakeLists.txt b/fusion/fusion/CMakeLists.txt new file mode 100644 index 00000000..0e150675 --- /dev/null +++ b/fusion/fusion/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.17) +project(FusionLibrary) + +add_library(FusionLib INTERFACE) + +target_include_directories(FusionLib INTERFACE + $ + $ # for install + ${CMAKE_CURRENT_SOURCE_DIR}/../../mavis + ${CMAKE_CURRENT_SOURCE_DIR}/../../mavis/mavis +) diff --git a/fusion/fusion/FieldExtractor.hpp b/fusion/fusion/FieldExtractor.hpp new file mode 100644 index 00000000..c89d76e2 --- /dev/null +++ b/fusion/fusion/FieldExtractor.hpp @@ -0,0 +1,270 @@ +// HEADER PLACEHOLDER +// contact Jeff Nye, jeffnye-gh +// +//! \file FieldExtractor.hpp mavis shim, extract fields from encodings +#pragma once +#include "FusionExceptions.hpp" +#include "uArchInfo.hpp" +#include "Instruction.hpp" +#include "Mavis.h" +#include +#include +#include +#include +using namespace std; + +//! \class FieldExtractor +//! +//! \brief example class for extracting field values from mavis encodings +//! +//! This implementation provides a shim between operations typically +//! used during fusion contraint checking and transformation and mavis. +//! +//! The intent is any alternative to FieldExtractor could be created +//! if compliant with the interface. This has not been tested. +//! +//! It would also be interesting to determine if more of this could be +//! delegated to existing methods in mavis. +class FieldExtractor +{ + public: + //! \brief Part of mechanism to extract named fields from encodings + //! + //! OperandFieldID has no entry for immediates + //! \note I am overloading RS_MAX when referencing immediate fields + using FieldName = mavis::InstMetaData::OperandFieldID; + //! \brief alias used to extract Special fields by enum + using SFieldName = mavis::ExtractorIF::SpecialField; + + //! \brief ... + using InstPtrType = Instruction::PtrType; + //! \brief ... + using InstPtrListType = std::vector::PtrType>; + //! \brief type for getXRegs operations from mavis + using MavisBitMaskType = mavis::DecodedInstructionInfo::BitMask; + //! \brief type for getXRegs operations from mavis + using RegsGetter = MavisBitMaskType (Instruction::*)() const; + //! \brief duplicated arguments do not need to be explicitly expressed + using OptArg = std::optional; + + //! \brief compairson function primitives + enum FUNC + { + EQ, + LT + }; + + //! \brief extract value of named encoding field + //! + //! handles field name and immediate checking + uint32_t getField(InstPtrType inst, FieldName field) const + { + bool isDest = false; + if (!checkInstHasField(inst, field, isDest)) + { + throw fusion::FieldExtUnknownField((uint32_t)field, inst->dasmString()); + } + + return getFieldById(inst, field, isDest); + } + + //! \brief get the encoded value of a named special field from an + //! instptr + //! + //! This is simpler than getField, a return value of 0 from mavis + //! indicates the field does not exist so checkInstHasSField() + //! is redundant. getSFieldById() is also not necessary since + //! SpecialFields are not distinguished as a source or destination + //! operand and can be accessed from the same method. + uint32_t getSField(InstPtrType inst, SFieldName field) const + { + uint32_t value = inst->getSpecialField(field); + if (value == 0) + { + throw fusion::FieldExtUnknownSpecialField((uint32_t)field, inst->dasmString()); + } + return value; + // return getSFieldById(inst, field); + } + + //! \brief helper function for getField, src/dst switch + //! + //! isDest is set previously by checkInstHasField() + uint32_t getFieldById(InstPtrType inst, FieldName field, bool isDest) const + { + if (isDest) + { + return inst->getDestOpInfo().getFieldValue(field); + } + return inst->getSourceOpInfo().getFieldValue(field); + } + + //! \brief Use mavis to determine if the FieldName exists in this inst + //! obj + //! + //! Special case check for immediates; + //! srcOp and dstOps have to be checked separately. + //! + //! This is legally marked const, it does not modify *this, + //! but it does modifies the argument isDest. + bool checkInstHasField(InstPtrType inst, FieldName field, bool & isDest) const + { + if (inst->hasImmediate() && field == FieldName::RS_MAX) + { + return true; + } + + if (inst->getSourceOpInfo().hasFieldID(field) && field != FieldName::RS_MAX) + { + return true; + } + + // FIXME: I do not see the reason but this if statement will fail + // with + // terminate called after throwing an instance of + // 'mavis::OperandInfoInvalidFieldID' + // what(): OperandInfo field ID '' is invalid + // + // if(inst->getDestOpInfo().hasFieldID(field) && field != + // FieldName::RS_MAX) + // { + // isDest = true; + // return true; + // } + // + // But doing this is fine... + bool idok = inst->getDestOpInfo().hasFieldID(field); + bool fok = field != FieldName::RS_MAX; + if (idok && fok) + { + isDest = true; + return true; + } + + // catch fall through + throw fusion::FieldExtUnknownField((uint32_t)field, inst->dasmString()); + return false; ///... + } + + //! \brief equality + bool eq(const InstPtrListType & input, size_t a, size_t b, const FieldName f1, + const OptArg f2 = std::nullopt) const + { + return compare(input[a], input[b], f1, f2, EQ); + } + + //! \brief less than + bool lt(const InstPtrListType & input, size_t a, size_t b, const FieldName f1, + const OptArg f2 = std::nullopt) const + { + return compare(input[a], input[b], f1, f2, LT); + } + + //! \brief not equal + bool noteq(const InstPtrListType & input, size_t a, size_t b, const FieldName f1, + const OptArg f2 = std::nullopt) const + { + return !compare(input[a], input[b], f1, f2, EQ); + } + + //! \brief greater than + bool gt(const InstPtrListType & input, size_t a, size_t b, const FieldName f1, + const OptArg f2 = std::nullopt) const + { + return compare(input[b], input[a], f1, f2, LT); + } + + //! \brief less than or equal + bool lteq(const InstPtrListType & input, size_t a, size_t b, const FieldName f1, + const OptArg f2 = std::nullopt) const + { + return !compare(input[b], input[a], f1, f2, LT); + } + + //! \brief greater than or equal + bool gteq(const InstPtrListType & input, size_t a, size_t b, const FieldName f1, + const OptArg f2 = std::nullopt) const + { + return !compare(input[a], input[b], f1, f2, LT); + } + + //! \brief return the integer read ports used by the input fusion group + uint32_t getIntRdPorts(const InstPtrListType & input) const + { + return countPorts(input, &Instruction::getIntSourceRegs); + } + + //! \brief return the integer write ports used by the input fusion + //! group + uint32_t getIntWrPorts(const InstPtrListType & input) const + { + return countPorts(input, &Instruction::getIntDestRegs); + } + + //! \brief return the float read ports used by the input fusion group + uint32_t getFloatRdPorts(const InstPtrListType & input) const + { + return countPorts(input, &Instruction::getFloatSourceRegs); + } + + //! \brief return the float write ports used by the input fusion group + uint32_t getFloatWrPorts(const InstPtrListType & input) const + { + return countPorts(input, &Instruction::getFloatDestRegs); + } + + //! \brief return the vector read ports used by the input fusion group + uint32_t getVecRdPorts(const InstPtrListType & input) const + { + return countPorts(input, &Instruction::getVectorSourceRegs); + } + + //! \brief return the vector write ports used by the input fusion group + uint32_t getVecWrPorts(const InstPtrListType & input) const + { + return countPorts(input, &Instruction::getVectorDestRegs); + } + + private: + //! \brief compare common method + bool compare(const InstPtrType LHS, const InstPtrType RHS, const FieldName f1, const OptArg _f2, + FUNC func) const + { + FieldName f2 = _f2.value_or(f1); + + auto lhs = getField(LHS, f1); + auto rhs = getField(RHS, f2); + + // clang-format off + switch (func) + { + case FUNC::LT: + { + return lhs < rhs; + } + case FUNC::EQ: + { + return lhs == rhs; + } + default: + { + throw fusion::FieldExtUnknownFunction((uint32_t)func); + } + } + // clang-format on + + return false; + } + + //! \brief count the number of read or write ports required by the + //! group + uint32_t countPorts(const InstPtrListType & input, RegsGetter getRegs) const + { + mavis::DecodedInstructionInfo::BitMask mask; + for (const auto & i : input) + { + mask |= (i.get()->*getRegs)(); + } + return mask.count(); + } +}; diff --git a/fusion/fusion/Fusion.hpp b/fusion/fusion/Fusion.hpp new file mode 100644 index 00000000..a3885acd --- /dev/null +++ b/fusion/fusion/Fusion.hpp @@ -0,0 +1,171 @@ +// HEADER PLACEHOLDER +// contact Jeff Nye, jeffnye-gh, Condor Computing Corp. +// +//! \file Fusion.hpp top level fusion API +#pragma once +#include "FieldExtractor.hpp" +//#include "FslParser.hpp" +#include "FusionContext.hpp" +#include "FusionExceptions.hpp" +#include "FusionGroup.hpp" +#include "FusionTypes.hpp" +#include "MachineInfo.hpp" + +#include +#include +#include +#include + +namespace fusion +{ + //! \class Fusion + //! + //! \brief top level fusion class + //! + //! In this implementation the allocators are placeholder for + //! more complex use cases. They are provided for future + //! extensions. + //! + //! Input needed to create a fusion 'context' can come + //! from explicity construction of FusionGroups, construction + //! from the helper class FusionGroupCfg, and eventually + //! from the DSL or from Json. + //! + //! Both the DSL and JSON are future features. With the DSL + //! having a parser and a defined syntax. The JSON form has + //! no definition at the moment, the JSON form could be a simple + //! syntax variation of the DSL form. The linkage to the + //! transform function object needs to be defined for JSON. + //! + //! There is a single context assumed although there are + //! stubs for multiple context support. At the moment it + //! is not clear if multiple context are actually a + //! useful feature for fusion enabled instruction + //! decoders in the existing performance models. + //! + //! For usage see the testbench.cpp in fusion/test. For the + //! final PR there will more usage examples. + template , + typename MachineInfoTypeAlloc = fusion::ShrPtrAlloc, + typename FieldExtractorTypeAlloc = fusion::ShrPtrAlloc> + struct Fusion + { + //! \brief ... + using FusionGroupListType = std::vector; + //! \brief ... + using FusionGroupCfgType = fusion::FusionGroupCfg; + //! \brief ... + using FusionGroupCfgListType = std::vector; + //! \brief ... + using TransformFuncType = bool (*)(FusionGroupType &, InstPtrListType &, InstPtrListType &); + //! \brief ... + using FusionContextType = fusion::FusionContext; + + //! \brief ... + using FusionFuncType = std::function; + + //! \brief main ctor + Fusion( + const FusionGroupListType & fusiongroup_list, + const FusionGroupCfgListType & fusiongroupcfg_list, + const FusionGroupTypeAlloc fusiongroup_alloc = fusion::ShrPtrAlloc(), + const MachineInfoTypeAlloc machine_info_alloc = fusion::ShrPtrAlloc(), + const FieldExtractorType field_extractor_alloc = + fusion::ShrPtrAlloc()) : + fusiongroup_alloc_(fusiongroup_alloc), + machine_info_alloc_(machine_info_alloc), + fusionOpr(defaultFusionOpr) + { + if (fusiongroup_list.size() > 0) + initialize(fusiongroup_list); + else if (fusiongroupcfg_list.size() > 0) + initialize(fusiongroupcfg_list); + context_.makeContext("fbase", fusiongroup_list); + } + + //! \brief ctor from group list + Fusion(const FusionGroupListType & fusiongroup_list) : + Fusion(fusiongroup_list, {}, FusionGroupTypeAlloc(), MachineInfoTypeAlloc(), + FieldExtractorType()) + { + } + + //! \brief ctor from cfg group list + Fusion(const FusionGroupCfgListType & fusiongroupcfg_list) : + Fusion({}, fusiongroupcfg_list, FusionGroupTypeAlloc(), MachineInfoTypeAlloc(), + FieldExtractorType()) + { + } + + //! \brief initialize state from a group list + void initialize(const FusionGroupListType & fusiongroup_list) + { + for (auto grp : fusiongroup_list) + { + registerGroup(grp); + } + } + + //! \brief initialize from a group cfg list + void initialize(const FusionGroupCfgListType & grp_list) + { + for (auto cfg : grp_list) + { + FusionGroupType f(cfg); + registerGroup(f); + } + } + + //! \brief alias for context_.insertGroup() + void registerGroup(FusionGroupType & grp) { context_.insertGroup(grp); } + + //! \brief create a single context from a list of fusiongroups + //! + //! This is here to support generality but I have + //! not seen an immediate need for dynamic switching + //! between multiple fusion contexts in a simulation. + //! Something to consider for the future. + void makeContext(const std::string & name, const FusionGroupListType & fusiongroup_list) + { + context_.makeContext(name, fusiongroup_list); + } + + //! \brief interface to the fusion operation + //! + //! This is the principle interface to the fusion operation. + //! The operator can modify both input and output as needed. + //! The default operator appends in to out and clears in. + //! + //! fusionOpr can be assigned with a user function. + void fusionOperator(InstPtrListType & in, InstPtrListType & out) + { + fusionOpr(*this, in, out); + } + + //! \brief assign the functor handle with a custom operator + void setFusionOpr(FusionFuncType customOpr) { fusionOpr = customOpr; } + + //! \brief default fusion operator appends in to out and clears + //! out. + static void defaultFusionOpr(Fusion & inst, InstPtrListType & in, InstPtrListType & out) + { + out.insert(out.end(), in.begin(), in.end()); + in.clear(); + } + + //! \brief future feature + FusionGroupTypeAlloc fusiongroup_alloc_; + //! \brief future feature + MachineInfoTypeAlloc machine_info_alloc_; + //! \brief the current fusion state + //! + //! There is a single context in this version of the + //! code. This could expand to support multiple + //! simultaneous contexts if there is a use case. + FusionContextType context_; + //! \brief the fusion operation handle + FusionFuncType fusionOpr; + }; + +} // namespace fusion diff --git a/fusion/fusion/FusionContext.hpp b/fusion/fusion/FusionContext.hpp new file mode 100644 index 00000000..fbd90dff --- /dev/null +++ b/fusion/fusion/FusionContext.hpp @@ -0,0 +1,89 @@ +// HEADER PLACEHOLDER +// contact Jeff Nye, jeffnye-gh +// +//! \file FusionContext.hpp FusionGroup set context +#pragma once +#include "FusionExceptions.hpp" +#include "FusionTypes.hpp" + +#include +#include +#include +#include + +namespace fusion +{ + // ----------------------------------------------------------------------- + //! \class FusionContext + //! + //! + //! \brief Holds an searchable list of the current FusionGroups + //! + //! In this implementation the groups are held and searched + //! within a uo/map. There is a trie implementation but that + //! is not used currently. Before adding the trie if will be + //! useful to spend more time with large fusion group definitions + //! and see how the map performs vs the trie (or alternatives). + //! + //! Future abstration : typename container or typename tree: + //! typename FusionGroupContainerType = + //! std::unordered_map, + //! typename LookupType = fusion::ShrPtrAlloc + // ----------------------------------------------------------------------- + template class FusionContext + { + //! \brief for now this is a uo/map with uint key + using FusionGroupListType = std::vector; + //! \brief for now this is a uo/map with uint key + using FusionGroupContainerType = std::unordered_map; + // Future : using LookupType = RadixTrie<4>; + + public: + FusionContext() = default; + + //! \brief basic ctor + //! + //! name_ member is assigned in makeContext + FusionContext(const std::string & name, const FusionGroupListType & groups) + { + makeContext(name, groups); + } + + //! \brief insert each group into the (only/current) context + void makeContext(const std::string & n, const FusionGroupListType & groups) + { + name_ = n; + for (const auto & grp : groups) + { + insertGroup(grp); + } + } + + //! \brief insert each group in current context w/ catch + void insertGroup(const FusionGroupType & group) + { + fusion::HashType hash = group.hash(); + if (hash == 0) + { + throw fusion::HashIllegalValueError(group.name(), group.hash()); + } + + if (container_.find(hash) != container_.end()) + { + throw fusion::HashDuplicateError(group.name(), group.hash()); + } + container_.insert(std::make_pair(hash, group)); + } + + private: + //! \brief context name + //! + //! When this is used in (future) multi-context implementations + //! the name must be unique + std::string name_{"base_ctx"}; + //! \brief this is an alias for map in this version + FusionGroupContainerType container_; + // Future: LookupType lookup_; + }; + +} // namespace fusion diff --git a/fusion/fusion/FusionExceptions.hpp b/fusion/fusion/FusionExceptions.hpp new file mode 100644 index 00000000..25281364 --- /dev/null +++ b/fusion/fusion/FusionExceptions.hpp @@ -0,0 +1,118 @@ +// HEADER PLACEHOLDER +// contact Jeff Nye, jeffnye-gh +// +//! \file FusionExceptions.hpp fusion defined exceptions +#pragma once +#include "FusionTypes.hpp" + +#include +#include +#include +#include + +//! \class FusionExceptionBase +//! \class HashDuplicateError +//! \class HashIllegalValueError +//! \class ContextDuplicateError +//! \class FieldExtUnknownField +//! \class FieldExtUnknownSpecialField +//! \class FieldExtUnknownFunction + +namespace fusion +{ + + //! \brief exception base class for fusion operation exceptions + //! + //! There are a number of forward looking struct definitions. + //! In the final implementation I expect this set to be modified + class FusionExceptionBase : public std::exception + { + public: + //! \brief ... + const char* what() const noexcept override { return why_.c_str(); } + + protected: + //! \brief ... + std::string why_; + //! \brief ... + std::stringstream ss; + }; + + //! \brief hashes within a context can not overlap + //! + //! Each fusion group has a hash formed from the uids of the + //! in the group. The hash is the key into a uo/map. These hashes + //! are checked for uniqueness on construction + struct HashDuplicateError : FusionExceptionBase + { + //! \brief ... + explicit HashDuplicateError(const std::string & name, const fusion::HashType & hash) + { + ss << "Duplicated hash detected, '" << name << "'" + << " 0x" << std::hex << hash; + why_ = ss.str(); + } + }; + + //! \brief illegal hash value cause this to be thrown + //! + //! At the moment 0 is a reserved/illegal hash value + struct HashIllegalValueError : FusionExceptionBase + { + //! \brief ... + explicit HashIllegalValueError(const std::string & name, const fusion::HashType & hash) + { + ss << "Illegal hash value detected, '" << name << "'" + << " 0x" << std::hex << hash; + why_ = ss.str(); + } + }; + + //! \brief context name duplication was detected + //! + //! This supports (future feature) of multiple + //! contexts keyed off the context name string + struct ContextDuplicateError : FusionExceptionBase + { + //! \brief ... + explicit ContextDuplicateError(const std::string & name) + { + ss << "Duplicated context detected, '" << name << "'"; + why_ = ss.str(); + } + }; + + //! \brief field extractor unknown field name + struct FieldExtUnknownField : FusionExceptionBase + { + //! \brief ... + explicit FieldExtUnknownField(uint32_t fn, std::string dasm) + { + ss << "Unknown field: " << fn << " in " << dasm; + why_ = ss.str(); + } + }; + + //! \brief field extractor unknown special field name + struct FieldExtUnknownSpecialField : FusionExceptionBase + { + //! \brief ... + explicit FieldExtUnknownSpecialField(uint32_t sfn, std::string dasm) + { + ss << "Unknown special field: " << sfn << " in " << dasm; + why_ = ss.str(); + } + }; + + //! \brief field extractor unknown function + struct FieldExtUnknownFunction : FusionExceptionBase + { + //! \brief ... + explicit FieldExtUnknownFunction(uint32_t func) + { + ss << "Unknown function selection: " << func; + why_ = ss.str(); + } + }; + +} // namespace fusion diff --git a/fusion/fusion/FusionGroup.hpp b/fusion/fusion/FusionGroup.hpp new file mode 100644 index 00000000..931b8b21 --- /dev/null +++ b/fusion/fusion/FusionGroup.hpp @@ -0,0 +1,314 @@ +// HEADER PLACEHOLDER +// contact Jeff Nye, jeffnye-gh +// +//! \file FusionGroup.hpp holds fusion definitions and transforms +#pragma once +#include "FusionTypes.hpp" +#include "Instruction.hpp" +#include "Mavis.h" + +#include +#include +#include +#include +#include +#include +#include + +//! \class FusionGroupCfg + +namespace fusion +{ + //! \brief forward decl + template class FusionGroup; + + // --------------------------------------------------------------- + //! \brief FusionGroup ctor helper + //! + //! FusionGroupCfg helps construct FusionGroups from combinations + //! of ctor arguments. There is more work to do here. + //! + //! MachineInfoType provides access to implementation details of the machine + //! + //! FieldExtractor provides an interface to mavis and support functions + //! for boolean operations. + //! + //! (will) support: + //! UIDs implemented + //! opcodes not implemented, future feature + //! asm text not implemented, future feature + // --------------------------------------------------------------- + template struct FusionGroupCfg + { + //! \brief convenient group type + using FusionGroupType = FusionGroup; + + //! \brief transform functor signature + using TransformFuncType = bool (*)(FusionGroup &, + InstPtrListType &, InstPtrListType &); + + // Future feature + // uids can be derived from opcs, but not the other way + // if conversion from opcs to uids is required so is Mavis + // std::optional opcs; + // std::optional asms; + // fusion::MavisType *mavis{nullptr}; + + //! \brief default transform functor + //! + //! The group is not fused, input is appended to out. input is cleared. + static bool default_transform(FusionGroupType &, InstPtrListType & in, + InstPtrListType & out) + { + out.insert(std::end(out), std::begin(in), std::end(in)); + in.clear(); + return true; + } + + //! \brief convenient name string + const std::string name; + //! \brief list of UIDs representing the group + std::optional uids; + //! \brief handle for the transform function + //! + //! In previous implementations constraints checking and + //! transformation were enforced as split operations. This is + //! no longer required. + std::optional transformFunc = default_transform; + }; + + // --------------------------------------------------------------- + //! \brief FusionGroup parent + //! + //! opcs & asm statements are not supported yet, future + // --------------------------------------------------------------- + class FusionGroupBase + { + public: + //! \brief save typing + using UidType = fusion::UidType; + //! \brief save typing + using HashType = fusion::HashType; + //! \brief save typing + using InstPtrListType = fusion::InstPtrListType; + //! \brief save typing + using InstUidListType = fusion::InstUidListType; + + //! \brief base ctor + FusionGroupBase(std::string n = "", InstUidListType u = InstUidListType(), HashType h = 0) : + name_(n), + uids_(u), + // Future feature + // opcs_(fusion::OpcodeListType()), + // asms_(fusion::AsmStmtListType()), + hash_(h) + { + } + + //! \brief capture the UIDs and create the hash key + virtual void setUids(InstUidListType & u) + { + uids_ = u; + initHash(); + } + + //! \brief uids accessor + virtual InstUidListType & uids() { return uids_; } + + // virtual OpcodeListType& opcs() { return opcs_; } + // virtual AsmStmtListType& asms() { return asms_; } + + //! \brief hash setter + virtual void setHash(HashType hash) { hash_ = hash; } + + //! \brief refresh the hash from uids_ + virtual void initHash() { hash_ = jenkins_1aat(uids_); } + + //! \brief hash accessor + virtual HashType hash() const { return hash_; } + + //! \brief convenience function + virtual void setName(std::string n) { name_ = n; } + + //! \brief name accessor + virtual std::string name() const { return name_; } + + //! \brief report fgroup state to stream + //! + //! I prefer this instead of overloading << + virtual void info(std::ostream & os = std::cout) const + { + std::cout << "Name: " << name() << std::endl; + std::cout << " HASH: " << std::hex << " 0x" << hash() << std::endl; + std::cout << " UIDS: "; + for (auto u : uids_) + std::cout << std::hex << " 0x" << u; + std::cout << std::endl; + // Future feature + // std::cout<<" OPCs: "; + // for(auto o : opcs_) std::cout< & v) + { + HashType hash = 0; + + for (auto i : v) + { + hash += i; + hash += (hash << 10); + hash ^= (hash >> 6); + } + + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + + return hash; + } + + private: + //! \brief convenient name string + std::string name_{""}; + //! \brief the instruction UIDs known to mavis + InstUidListType uids_; + // Future feature + // OpcodeListType opcs_; + // AsmStmtListType asms_; + //! \brief uint32_t hash key + HashType hash_{0}; + }; + + // --------------------------------------------------------------- + //! \brief A fusion group is the basis for fusion detection and transformation + //! + //! A fusion group is a list of UIDs that represent data useful for + //! matching a group against incoming Inst::PtrTypes as well as + //! constraints checking. + //! + //! TransformFuncType transform_ is the functor handle. The default + //! is expected to be overridden externally. + // --------------------------------------------------------------- + template + class FusionGroup : public FusionGroupBase + { + //! \brief convenient typedef + using FusionGroupType = FusionGroup; + + //! \brief conform to convention used else where + using MavisType = Mavis, uArchInfo>; + + //! \brief convenient typedef + using FusionGroupCfgType = FusionGroupCfg; + + //! \brief functor signature and typedef + using TransformFuncType = bool (*)(FusionGroup &, + InstPtrListType &, InstPtrListType &); + + public: + //! \brief conform to convention used elsewhere + typedef typename std::shared_ptr> PtrType; + + // -------------------------------------------------------------------- + //! \brief default ctor + FusionGroup(std::string n = "", InstUidListType u = InstUidListType(), + TransformFuncType t = nullptr) : + FusionGroupBase(n, u), + transform_(t) + { + initHash(); + } + + // -------------------------------------------------------------------- + //! \brief groupcfg ctor + FusionGroup(const FusionGroupCfgType & cfg) : + FusionGroupBase(cfg.name, + cfg.uids ? *cfg.uids : InstUidListType() //, + // cfg.opcs ? *cfg.opcs : OpcodeListType() + ), + transform_(cfg.transformFunc ? *cfg.transformFunc : nullptr) + { + if (uids().size() == 0) + { + throw std::invalid_argument("For " + cfg.name + + " uids are required in this implementation"); + } + initHash(); + } + + //! \brief transform elements of input to append to output + //! + //! transform() is called when a fusiongroup is selected by + //! Fusion (fusion.h). The return value returns true if + //! the transform function met the criterion. False if not. + //! On false Fusion continues to search the context. + //! + //! The transform operation is expected to modify in if fusion + //! occurs and also to append to out with the transformation + //! results. All combinations of true/false, modifying/not modifying + //! input and output are valid. + bool transform(InstPtrListType & in, InstPtrListType & out) + { + if (transform_) + return transform_(*this, in, out); + return false; + } + + //! \brief default transform functor + //! + //! The group is not fused, input is appended to out. input is cleared. + static bool default_transform(FusionGroupType &, InstPtrListType & in, + InstPtrListType & out) + { + out.insert(std::end(out), std::begin(in), std::end(in)); + in.clear(); + return true; + } + + //! \brief user method for changing the default transform functor + void setTransform(TransformFuncType func) { transform_ = func; } + + //! \brief tranform handle accessor + TransformFuncType getTransform() { return transform_; } + + //! \brief machine info handle accessor + MachineInfoType & mi() { return mi_; } + + //! \brief machine info handle accessor + MachineInfoType & machineInfo() { return mi(); } + + //! \brief field extractor handle accessor + FieldExtractorType & fe() { return fe_; } + + //! \brief field extractor handle accessor + FieldExtractorType & fieldExtractor() { return fe(); } + + private: + //! \brief MachineInfo provides access to uarch details + MachineInfoType mi_; + //! \brief FieldExtractor provides field access methods + FieldExtractorType fe_; + //! \brief handle to transform functor + TransformFuncType transform_ = default_transform; + }; + +} // namespace fusion diff --git a/fusion/fusion/FusionTypes.hpp b/fusion/fusion/FusionTypes.hpp new file mode 100644 index 00000000..c224e513 --- /dev/null +++ b/fusion/fusion/FusionTypes.hpp @@ -0,0 +1,57 @@ +// HEADER PLACEHOLDER +// contact Jeff Nye, jeffnye-gh +// +//! \file FusionTypes.hpp marshalled types used by fusion +#pragma once +#include "Mavis.h" +#include "mavis/DecoderTypes.h" +#include "Instruction.hpp" +#include "uArchInfo.hpp" +#include +#include +#include + +namespace fusion +{ + //! \brief ... + using MavisType = Mavis, uArchInfo>; + + //! \brief ... + using FileNameListType = std::vector; + + //! \brief ... + using UidType = mavis::InstructionUniqueID; + //! \brief ... + using InstUidListType = std::vector; + + //! \brief ... + using FieldName = mavis::InstMetaData::OperandFieldID; + //! \brief ... + using SFieldName = mavis::ExtractorIF::SpecialField; + //! \brief ... + using Opcode = mavis::Opcode; + //! \brief ... + using OpcodeListType = std::vector; + + //! \brief ... + using AsmStmtListType = std::vector; + + //! \brief ... + using InstPtrListType = std::vector::PtrType>; + //! \brief ... + using HashType = uint32_t; + + //! \brief This is provided but has limited use currently + template struct ShrPtrAlloc + { + //! \brief ... + using Tptr = std::shared_ptr; + + //! \brief ... + template Tptr operator()(Args &&... args) + { + return std::make_shared(std::forward(args)...); + } + }; + +} // namespace fusion diff --git a/fusion/fusion/Instruction.hpp b/fusion/fusion/Instruction.hpp new file mode 100644 index 00000000..accab0bf --- /dev/null +++ b/fusion/fusion/Instruction.hpp @@ -0,0 +1,152 @@ +// +// Created by David Murrell on 11/7/19. +// +// JN: original file name was Inst.h + +#pragma once + +#include +#include +#include "mavis/OpcodeInfo.h" +#include "mavis/Extractor.h" + +/** + * EXAMPLE Instruction + */ +template class Instruction +{ + public: + typedef typename std::shared_ptr> PtrType; + + public: + // Instruction(const mavis::DecodedInstructionInfo::PtrType& dii, const + // uint64_t icode, const mavis::ExtractorIF::PtrType& extractor, const + // typename AnnotationType::PtrType& ui) : + Instruction(const mavis::OpcodeInfo::PtrType & dinfo, + const typename AnnotationType::PtrType & ui, uint32_t dummy) : + dinfo_(dinfo), + uinfo_(ui) + { + } + + // Copy construction (needed for cloning) + Instruction(const Instruction &) = default; + + // Morph into a different instruction (new mavis info) + void morph(const typename mavis::OpcodeInfo::PtrType & new_dinfo, + const typename AnnotationType::PtrType & new_ui) + { + // dinfo_.reset(new_dinfo); + dinfo_ = new_dinfo; + // uinfo_.reset(new_ui); + uinfo_ = new_ui; + + // TBD: Instruction user may need to reset any information cached with this + // instruction + } + + /** + * User code to "recycle" an instruction (which DTable has cached and is + * attempting to reuse) + */ + void recycle() {} + + typename mavis::OpcodeInfo::PtrType getOpInfo() const { return dinfo_; } + + std::string getMnemonic() const { return dinfo_->getMnemonic(); } + + std::string dasmString() const { return dinfo_->dasmString(); } + + bool isInstType(mavis::InstMetaData::InstructionTypes itype) const + { + return dinfo_->isInstType(itype); + } + + bool isExtInstType(mavis::DecodedInstructionInfo::ExtractedInstTypes itype) const + { + return dinfo_->isExtractedInstType(itype); + } + + int64_t getSignedOffset() const { return dinfo_->getSignedOffset(); } + + mavis::DecodedInstructionInfo::BitMask getSourceAddressRegs() const + { + return dinfo_->getSourceAddressRegs(); + } + + mavis::DecodedInstructionInfo::BitMask getSourceDataRegs() const + { + return dinfo_->getSourceDataRegs(); + } + + mavis::DecodedInstructionInfo::BitMask getIntSourceRegs() const + { + return dinfo_->getIntSourceRegs(); + } + + mavis::DecodedInstructionInfo::BitMask getFloatSourceRegs() const + { + return dinfo_->getFloatSourceRegs(); + } + + mavis::DecodedInstructionInfo::BitMask getVectorSourceRegs() const + { + return dinfo_->getVectorSourceRegs(); + } + + mavis::DecodedInstructionInfo::BitMask getIntDestRegs() const + { + return dinfo_->getIntDestRegs(); + } + + mavis::DecodedInstructionInfo::BitMask getFloatDestRegs() const + { + return dinfo_->getFloatDestRegs(); + } + + mavis::DecodedInstructionInfo::BitMask getVectorDestRegs() const + { + return dinfo_->getVectorDestRegs(); + } + + uint64_t getSpecialField(mavis::OpcodeInfo::SpecialField sfid) const + { + return dinfo_->getSpecialField(sfid); + } + + const mavis::OperandInfo & getSourceOpInfo() const { return dinfo_->getSourceOpInfo(); } + + const mavis::OperandInfo & getDestOpInfo() const { return dinfo_->getDestOpInfo(); } + + mavis::InstructionUniqueID getUID() const { return dinfo_->getInstructionUniqueID(); } + + bool hasImmediate() const { return dinfo_->hasImmediate(); } + + const typename AnnotationType::PtrType & getuArchInfo() const { return uinfo_; } + + // private: + typename mavis::OpcodeInfo::PtrType dinfo_; + typename AnnotationType::PtrType uinfo_; + + void print(std::ostream & os) const + { + std::ios_base::fmtflags os_state(os.flags()); + os << dinfo_->getMnemonic() << ", src[" << dinfo_->numSourceRegs() + << "]: " << dinfo_->getSourceRegs() << " (addr: " << dinfo_->getSourceAddressRegs() + << ")" + << ", dst: " << dinfo_->getDestRegs() << ", data size: " << std::dec + << dinfo_->getDataSize(); + if (uinfo_ != nullptr) + { + os << ", uInfo: " << *uinfo_; + } + os.flags(os_state); + } + + public: + friend std::ostream & operator<<(std::ostream & os, const Instruction & i) + { + i.print(os); + return os; + } +}; diff --git a/fusion/fusion/MachineInfo.hpp b/fusion/fusion/MachineInfo.hpp new file mode 100644 index 00000000..4c018592 --- /dev/null +++ b/fusion/fusion/MachineInfo.hpp @@ -0,0 +1,77 @@ +// HEADER PLACEHOLDER +// contact Jeff Nye, jeffnye-gh +// +//! \file MachineInfo.hpp processor implementation details +#pragma once +#include +#include +#include +#include + +//! \brief Placeholder for uarch and implementation details +//! +//! I have not followed up on the discussion on using +//! extension.core_extensions from the yaml for this information. +//! This is future work. +struct MachineInfo +{ + //! \brief there is only a default constructor provided + MachineInfo() {} + + //! \brief access number of implemented integer write ports + uint32_t maxIntWrPorts() const { return maxIntWrPorts_; } + + //! \brief access number of implemented float write ports + uint32_t maxFpWrPorts() const { return maxFpWrPorts_; } + + //! \brief access number of implemented vector write ports + uint32_t maxVecWrPorts() const { return maxVecWrPorts_; } + + //! \brief access number of implemented integer read ports + uint32_t maxIntRdPorts() const { return maxIntRdPorts_; } + + //! \brief access number of implemented float read ports + uint32_t maxFpRdPorts() const { return maxFpRdPorts_; } + + //! \brief access number of implemented vector read ports + uint32_t maxVecRdPorts() const { return maxVecRdPorts_; } + + //! \brief Is there a location available to execute a fused operator? + //! + //! Obviously, a placeholder. This would be a debug function + //! trap dispatch to execute pipes that do not implement the fused opr. + bool compSiteImplemented(uint32_t magic) const { return true; } + + //! \brief how many cycles to wait for the InstrQueue to fill + //! + //! Since fusion operates on 1 or more opcodes there are cases + //! where the InstrQueue may not have enough opcodes to validate + //! fusion groups. This is more of an issue for N-tuples > 2. + //! At some point the cycles saved by fusion are spent waiting + //! for fusable opportunities. This limits the impact. + uint32_t allowedLatency() const { return allowedLatency_; } + + //! \brief ... + void setName(std::string n) { name_ = n; } + + //! \brief ... + std::string name() const { return name_; } + + private: + //! \brief arbitrary name + std::string name_{"noname"}; + //! \brief ... + uint32_t maxIntWrPorts_{2}; + //! \brief ... + uint32_t maxFpWrPorts_{2}; + //! \brief ... + uint32_t maxVecWrPorts_{2}; + //! \brief ... + uint32_t maxIntRdPorts_{4}; + //! \brief ... + uint32_t maxFpRdPorts_{4}; + //! \brief ... + uint32_t maxVecRdPorts_{4}; + //! \brief ... + uint32_t allowedLatency_{1}; +}; diff --git a/fusion/fusion/RadixTrie.hpp b/fusion/fusion/RadixTrie.hpp new file mode 100644 index 00000000..6f7b738f --- /dev/null +++ b/fusion/fusion/RadixTrie.hpp @@ -0,0 +1,113 @@ +// HEADER PLACEHOLDER +// contact Jeff Nye, jeffnye-gh +// +//! \file RadixTrie.hpp space optimized search tree(trie) +#pragma once +#include +#include +#include +#include +#include +#include +#include + +//! \brief Node for uint32_t radix trie +//! +template class RadixTrieNode +{ + public: + //! \brief ... + bool isEndOfWord; + //! \brief ... + std::vector> children; + + //! \brief ... + RadixTrieNode() : isEndOfWord(false) + { + children.resize(1 << BIT_WIDTH); + for (auto & child : children) + { + child = nullptr; + } + } +}; + +//! \brief RadixTrie with element width as template parameter +//! +//! This is self explanatory. 4 bits seems to be the most performant, +//! for state sizes of 1024*1024. +//! +//! This is not used in the current implementation. It is provided +//! in the DPR for comment on future use and because I am comparing +//! performance against real sets of fusion groups. There is little +//! to no protection against 'bad' input. That will come later. +//! +//! 1 1024*1024 +//! Time taken for insertion: 7.31112 seconds +//! Time taken for searching: 1.26558 seconds +//! 2 1024*1024 +//! Time taken for insertion: 4.85911 seconds +//! Time taken for searching: 0.691898 seconds +//! 4 1024*1024 +//! Time taken for insertion: 4.77562 seconds <=== 4 bits +//! Time taken for searching: 0.43249 seconds +//! 8 1024*1024 +//! Time taken for insertion: 25.8369 seconds +//! Time taken for searching: 0.319623 seconds +template class RadixTrie +{ + public: + //! \brief per convention used elsewhere + typedef std::shared_ptr PtrType; + + //! \brief default ctor + RadixTrie() : root(std::make_unique>()) {} + + //! \brief insert... + void insert(uint32_t key) { insertRecursive(root, key, 0); } + + //! \brief find... + bool search(uint32_t key) const { return searchRecursive(root, key, 0); } + + private: + //! \brief ... + void insertRecursive(std::unique_ptr> & node, uint32_t key, + uint32_t depth) + { + if (!node) + { + node = std::make_unique>(); + } + + if (depth == MAX_DEPTH) + { + node->isEndOfWord = true; + return; + } + + uint32_t index = (key >> (BIT_WIDTH * (MAX_DEPTH - depth - 1))) & ((1 << BIT_WIDTH) - 1); + insertRecursive(node->children[index], key, depth + 1); + } + + //! \brief ... + bool searchRecursive(const std::unique_ptr> & node, uint32_t key, + uint32_t depth) const + { + if (!node) + { + return false; + } + if (depth == MAX_DEPTH) + { + return node->isEndOfWord; + } + uint32_t index = (key >> (BIT_WIDTH * (MAX_DEPTH - depth - 1))) & ((1 << BIT_WIDTH) - 1); + return searchRecursive(node->children[index], key, depth + 1); + } + + //! \brief top of the trie + std::unique_ptr> root; + + //! \brief keep a limit on depth based on the template parameter + static constexpr uint32_t MAX_DEPTH = std::numeric_limits::digits / BIT_WIDTH; +}; diff --git a/fusion/fusion/uArchInfo.hpp b/fusion/fusion/uArchInfo.hpp new file mode 100644 index 00000000..74415582 --- /dev/null +++ b/fusion/fusion/uArchInfo.hpp @@ -0,0 +1,214 @@ +// +// Created by David Murrell on 12/2/19. +// +#pragma once +#include "json.hpp" +#include "mavis/DecoderExceptions.h" +#include "mavis/DecoderTypes.h" +#include "uArchInfoExceptions.hpp" + +#include +#include +#include +#include + +/** + * uArchInfo: encapsulates "static" u-arch specific information + */ +class uArchInfo +{ + public: + typedef std::shared_ptr PtrType; + + // TODO: flesh this out + enum class UnitSet : uint64_t + { + AGU = 1ull << 0, + INT = 1ull << 1, + FLOAT = 1ull << 2, + MULTIPLY = 1ull << 3, + DIVIDE = 1ull << 4, + BRANCH = 1ull << 5, + LOAD = 1ull << 6, + STORE = 1ull << 7, + SYSTEM = 1ull << 8, + VECTOR = 1ull << 9, + }; + + // BEGIN: Stuff imported from the old StaticInstructionData object + enum RegFile + { + INTEGER, + FLOAT, + INVALID, + N_REGFILES = INVALID + }; + + static inline const char* const regfile_names[] = {"integer", "float"}; + + // TODO: resolve this against the UnitSet + enum IssueTarget : std::uint16_t + { + IEX, + FEX, + BR, + LSU, + ROB, // Instructions that go right to retire + N_ISSUE_TARGETS + }; + + static constexpr uint32_t MAX_ARCH_REGS = 5; + // END: Stuff imported from the old StaticInstructionData object + + private: + // Map unit names to unit ID's + static inline std::map umap_ = { + {"agu", UnitSet::AGU}, {"int", UnitSet::INT}, {"float", UnitSet::FLOAT}, + {"mul", UnitSet::MULTIPLY}, {"div", UnitSet::DIVIDE}, {"branch", UnitSet::BRANCH}, + {"load", UnitSet::LOAD}, {"store", UnitSet::STORE}, {"system", UnitSet::SYSTEM}, + {"vector", UnitSet::VECTOR}, + }; + + // TEMPORARY: map unit names to TargetUnit for back-level compatibility + static inline std::map issue_target_map_ = { + {"int", IssueTarget::IEX}, {"float", IssueTarget::FEX}, + {"branch", IssueTarget::BR}, {"load", IssueTarget::LSU}, + {"store", IssueTarget::LSU}, {"system", IssueTarget::ROB}, // TEMPORARY! + {"vector", IssueTarget::FEX}, // TEMPORARY! + {"rob", IssueTarget::ROB}, // TEMPORARY! + }; + + public: + /** + * \brief This object encapsulates all the micro-architectural information + * that depends on the instruction type. It is "static" and cached by Mavis in + * the instruction factories. Mavis will pass the nlohmann::json object to + * this constructor so that the user can parse any of the desired fields from + * the u-arch JSON file supplied to Mavis \param jobj nlohmann::json object + * for the given instruction + */ + explicit uArchInfo(const nlohmann::json & jobj) { parse_(jobj); } + + uArchInfo() = default; + uArchInfo(const uArchInfo &) = delete; + + void update(const nlohmann::json & jobj) + { + // TODO: identical to constructor(jobj) for now, but we may want to provide + // update restrictions + parse_(jobj); + } + + bool isUnit(UnitSet u) const { return (static_cast(u) & units_) != 0; } + + IssueTarget getIssueTarget() const { return issue_target_; } + + uint32_t getLatency() const { return latency_; } + + bool isPipelined() const { return pipelined_; } + + bool isSerialized() const { return serialize_; } + + bool isROBGrpStart() const { return rob_grp_start_; } + + bool isROBGrpEnd() const { return rob_grp_end_; } + + private: + uint64_t units_ = 0; ///< Bit mask of target execution units (from UnitSet) + IssueTarget issue_target_ = IssueTarget::N_ISSUE_TARGETS; ///< Issue target + uint32_t latency_ = 0; ///< Execution latency + bool pipelined_ = true; ///< Pipelined execution (non-blocking)? + bool serialize_ = false; ///< Serializes execution? + bool rob_grp_start_ = false; ///< Starts a new ROB group? + bool rob_grp_end_ = false; ///< Ends a ROB group? + + private: + friend std::ostream & operator<<(std::ostream & os, const uArchInfo & ui); + + void print(std::ostream & os) const + { + os << "{units: 0x" << std::hex << units_ << ", lat: " << std::dec << latency_ + << ", piped: " << pipelined_ << ", serialize: " << serialize_ + << ", ROB group begin: " << rob_grp_start_ << ", ROB group end: " << rob_grp_end_ << "}"; + } + + /** + * \brief Parse the JSON file + * \param jobj + */ + void parse_(const nlohmann::json & jobj) + { + // Issue target (from IssueTarget) + if (jobj.find("issue") != jobj.end()) + { + const auto itr = issue_target_map_.find(jobj["issue"]); + if (itr == issue_target_map_.end()) + { + throw uArchInfoUnknownIssueTarget(jobj["mnemonic"], jobj["issue"]); + } + issue_target_ = itr->second; + } + + // Target execution unit (from UnitSet) -- bit mask allows multiple targets + if (jobj.find("unit") != jobj.end()) + { + mavis::UnitNameListType ulist = jobj["unit"].get(); + for (const auto & u : ulist) + { + const auto itr = umap_.find(u); + if (itr == umap_.end()) + { + throw uArchInfoUnknownUnit(jobj["mnemonic"], u); + } + units_ |= static_cast(itr->second); + } + } + + // Instruction latency + if (jobj.find("latency") != jobj.end()) + { + latency_ = jobj["latency"]; + } + + // Whether the instruction is piplined (non-blocking) + if (jobj.find("pipelined") != jobj.end()) + { + pipelined_ = jobj["pipelined"]; + } + + // Whether the instruction serializes execution + if (jobj.find("serialize") != jobj.end()) + { + serialize_ = jobj["serialize"]; + } + + // Whether the instruction starts a new ROB group + if (jobj.find("rob_group") != jobj.end()) + { + mavis::StringListType slist = jobj["rob_group"].get(); + for (const auto & str : slist) + { + if (str == "begin") + { + rob_grp_start_ = true; + } + else if (str == "end") + { + rob_grp_end_ = true; + } + else + { + throw uArchInfoROBGroupParseError(jobj["mnemonic"], str); + } + } + } + + std::cout << "uArchInfo: " << jobj["mnemonic"] << std::endl; + } +}; + +inline std::ostream & operator<<(std::ostream & os, const uArchInfo & ui) +{ + ui.print(os); + return os; +} diff --git a/fusion/fusion/uArchInfoExceptions.hpp b/fusion/fusion/uArchInfoExceptions.hpp new file mode 100644 index 00000000..625f33c5 --- /dev/null +++ b/fusion/fusion/uArchInfoExceptions.hpp @@ -0,0 +1,89 @@ +// +// Created by David Murrell on 2/19/20. +// +#pragma once + +#include +#include +#include + +/** + * Decoder exceptions base class + */ +class uArchInfoBaseException : public std::exception +{ + public: + virtual const char* what() const noexcept { return why_.c_str(); } + + protected: + std::string why_; +}; + +/** + * uArchInfo parse error: the given unit for the instruction was not found by + * the uArchInfo object. + * + * This can happen from a mis-spelling of the unit in the JSON, or from a need + * to update the uArchInfo's list of supported units + */ +class uArchInfoUnknownUnit : public uArchInfoBaseException +{ + public: + uArchInfoUnknownUnit(const std::string & mnemonic, const std::string & unit_name) : + uArchInfoBaseException() + { + std::stringstream ss; + ss << "Instruction '" << mnemonic << "': " + << " unit '" << unit_name << "' " + << "is not known (uArchInfo object). Could be a mis-spelling in the " + "JSON file"; + why_ = ss.str(); + } +}; + +/** + * uArchInfo parse error: the given issue target for the instruction was not + * found by the uArchInfo object. + * + * This can happen from a mis-spelling of the issue target in the JSON, or from + * a need to update the uArchInfo's list of supported units + */ +class uArchInfoUnknownIssueTarget : public uArchInfoBaseException +{ + public: + uArchInfoUnknownIssueTarget(const std::string & mnemonic, const std::string & target_name) : + uArchInfoBaseException() + { + std::stringstream ss; + ss << "Instruction '" << mnemonic << "': " + << " issue target '" << target_name << "' " + << "is not known (uArchInfo object). Could be a mis-spelling in the " + "JSON file"; + why_ = ss.str(); + } +}; + +/** + * uArchInfo parsing error: the given ROB group begin/end stanza in the JSON is + * malformed + * + * This can happen from a mis-spelling of the rob_group key in the JSON. The + * form should be one of the following: + * + * "rob_group" : ["begin"] + * "rob_group" : ["end"] + * "rob_group" : ["begin", "end"] + */ +class uArchInfoROBGroupParseError : public uArchInfoBaseException +{ + public: + uArchInfoROBGroupParseError(const std::string & mnemonic, const std::string & bad_string) : + uArchInfoBaseException() + { + std::stringstream ss; + ss << "Instruction '" << mnemonic << "': " + << " rob_group element '" << bad_string << "'" + << " is not valid. Needs to be one of 'begin' or 'end'"; + why_ = ss.str(); + } +}; diff --git a/fusion/test/CMakeLists.txt b/fusion/test/CMakeLists.txt new file mode 100644 index 00000000..6e6524f1 --- /dev/null +++ b/fusion/test/CMakeLists.txt @@ -0,0 +1,55 @@ +cmake_minimum_required(VERSION 3.17) +project(FusionTest) + +set(CMAKE_POSITION_INDEPENDENT_CODE ON) + +find_library(MAVIS_LIBRARY NAMES mavis + HINTS "${CMAKE_SOURCE_DIR}/../release/mavis" + "${CMAKE_SOURCE_DIR}/../debug/mavis" + "${CMAKE_SOURCE_DIR}/../fastdebug/mavis" + "${CMAKE_SOURCE_DIR}/../build/mavis") + +if(NOT MAVIS_LIBRARY) + message(FATAL_ERROR "libmavis.a not found") +endif() + +add_library(mavis STATIC IMPORTED) + +set_target_properties(mavis PROPERTIES IMPORTED_LOCATION ${MAVIS_LIBRARY}) + +add_executable(fusiontest + TestBench.cpp + TestData.cpp + TestFieldExtractor.cpp + main.cpp + Options.cpp ) + +target_compile_options(fusiontest PRIVATE -Wall -Wextra + -Wno-unused-parameter # mavis + ) + +find_package(Boost REQUIRED COMPONENTS program_options) +if(Boost_FOUND) + target_include_directories(fusiontest PRIVATE ${Boost_INCLUDE_DIRS}) + target_link_libraries(fusiontest + PRIVATE FusionLib mavis + PRIVATE ${Boost_LIBRARIES} pthread ) +else() + message(SEND_ERROR "Boost is a required package for the testbench") +endif() + +set(JSON_BASE "${FUSION_TOP}/test/json") + +set(TB_OPTS --tb_verbose) + +# There is a mavis required dependency for c after g +set(ISA_FILES + --isa_file ${JSON_BASE}/isa_rv64g.json + --isa_file ${JSON_BASE}/isa_rv64c.json) + +list(APPEND OPTS ${ISA_FILES} + ${TB_OPTS}) + +add_custom_target(regress + COMMAND fusiontest ${OPTS} +) diff --git a/fusion/test/Msg.hpp b/fusion/test/Msg.hpp new file mode 100644 index 00000000..53533b55 --- /dev/null +++ b/fusion/test/Msg.hpp @@ -0,0 +1,118 @@ +// HEADER PLACEHOLDER +// contact Jeff Nye, jeffnye-gh +// +//! \file Msg.hpp header for simple uniform messages +#pragma once +#include +#include +#include +#include + +//! \brief singleton for standardized messages +//! +//! I use this to standardize local output. +//! +//! I'm using an ad hoc test bench, a compliant testbench +//! would be part of a fusion capable Sparta unit. In that +//! case this would be replaced with the mechanism found +//! in the unit benches. +struct Msg +{ + //! \brief get the singleton instance + static Msg* getInstance() + { + if (!instance) + instance = new Msg; + return instance; + } + + ~Msg() {} // dtor + + //! \brief this adds an identifier prefix to messages + //! + //! Example -I:MYUNIT: {message} + void setWho(std::string _w) { w = _w + ": "; } + + //! \brief shared message method + void mmsg(std::string p, std::string m) const { std::cout << p << w << m << std::endl; } + + //! \brief debug messages + void dmsg(std::string m = "", int v = 4) const + { + if (v <= verbose) + mmsg("-D: ", m); + } + + //! \brief error messages + void emsg(std::string m = "", int v = 1) const + { + if (v <= verbose) + mmsg("-E: ", m); + } + + //! \brief info messages + void imsg(std::string m = "", int v = 3) const + { + if (v <= verbose) + mmsg("-I: ", m); + } + + //! \brief warining messages + void wmsg(std::string m = "", int v = 2) const + { + if (v <= verbose) + mmsg("-W: ", m); + } + + //! \brief ... + void msg(std::string m) const { std::cout << m << std::endl; } + + //! \brief helper to show potentially empty strings + std::string tq(std::string s) const { return "'" + s + "'"; } + + // ---------------------------------------------------------------- + //! \brief ... + std::string w; + /** + * \brief verbosity setting + * + * \verbatim + * verbose 0 - silent + * 1 - errors + * 2 - errors,warnings + * 3 - errors,warnings,info + * >= 4 - errors,warnings,info,debug4 + * - debug messages can be a various levels, debugN + * \endverbatim + */ + + //! \brief ... + int verbose{3}; + + //! \brief ... + static Msg* instance; + + private: + //! \brief ... + Msg() + { + w = ""; + verbose = 3; + } + + // ------------------------------------------------------------------- + // Msg(std::string _who="",int _verbose=3) + // : w(_who+": "), + // verbose(_verbose) + // {} + // ------------------------------------------------------------------- + //! \brief ...copy + Msg(const Msg &) = delete; + //! \brief ...move + Msg(Msg &&) = delete; + //! \brief ...assign + Msg & operator=(const Msg &) = delete; +}; + +//! \brief ... +extern std::unique_ptr msg; diff --git a/fusion/test/Options.cpp b/fusion/test/Options.cpp new file mode 100644 index 00000000..b52e7b77 --- /dev/null +++ b/fusion/test/Options.cpp @@ -0,0 +1,99 @@ +// HEADER PLACEHOLDER +// contact Jeff Nye, jeffnye-gh, Condor Computing Corp. +// +#include "Msg.hpp" +#include "Options.hpp" +#include +using namespace std; + +// -------------------------------------------------------------------- +// Build the option set and check the options +// -------------------------------------------------------------------- +void Options::setupOptions(int ac, char** av) +{ + notify_error = false; + + po::options_description visibleOpts("\nFusion API test\n " + "Usage:: test [--help|-h|--version|-v] { options }"); + + po::options_description stdOpts("Standard options"); + buildOptions(stdOpts); + + try + { + po::store(po::command_line_parser(ac, av).options(stdOpts).run(), vm); + + // Without positional option po::parse_command_line can be used + // po::store(po::parse_command_line(ac, av, allOpts), vm); + } + catch (boost::program_options::error & e) + { + msg->msg(""); + msg->emsg("1st pass command line option parsing failed"); + msg->emsg("What: " + string(e.what())); + usage(stdOpts); + exit(1); + } + + po::notify(vm); + if (!checkOptions(vm, stdOpts, true)) + exit(1); +} + +// -------------------------------------------------------------------- +// Construct the std, hidden and positional option descriptions +// -------------------------------------------------------------------- +// clang-format off +void Options::buildOptions(po::options_description & stdOpts) +{ + stdOpts.add_options() + + ("help,h", "...") + + ("version,v", "report version and exit") + + ("stf", po::value(&stf_file), "STF input file") + + ("output,o", po::value(&output_file), + "Log output file") + + ("isa_file", po::value>(&isa_files), + "Multiple --isa_file accepted") + + ("tb_verbose", po::bool_switch(&tb_verbose) ->default_value(false), + "Test bench message control"); +} + +// clang-format on +// -------------------------------------------------------------------- +// Check sanity on the options, handle --help, --version +// -------------------------------------------------------------------- +bool Options::checkOptions(po::variables_map & vm, po::options_description & stdOpts, + bool firstPass) +{ + if (firstPass) + { + if (vm.count("help")) + { + usage(stdOpts); + return false; + } + if (vm.count("version")) + { + version(); + return false; + } + } + + return true; +} + +// -------------------------------------------------------------------- +// -------------------------------------------------------------------- +void Options::version() const +{ + msg->imsg(""); + msg->imsg("Fusion api tester"); + msg->imsg("Slack jeff w/any questions"); + msg->imsg(""); +} diff --git a/fusion/test/Options.hpp b/fusion/test/Options.hpp new file mode 100644 index 00000000..697749cf --- /dev/null +++ b/fusion/test/Options.hpp @@ -0,0 +1,76 @@ +// HEADER PLACEHOLDER +// contact Jeff Nye, jeffnye-gh +// +//! \file Options.hpp testbench options +#pragma once +#include +#include +#include +#include +namespace po = boost::program_options; + +//! \brief container for boost program option settings +struct Options +{ + // ---------------------------------------------------------------- + //! \brief singleton + // ---------------------------------------------------------------- + static Options* getInstance() + { + if (!instance) + instance = new Options(); + return instance; + } + + // ---------------------------------------------------------------- + // support methods + // ---------------------------------------------------------------- + //! \brief ... + void buildOptions(po::options_description &); + + //! \brief ... + bool checkOptions(po::variables_map &, po::options_description &, bool); + + //! \brief ... + void setupOptions(int, char**); + + //! \brief ... + void usage(po::options_description & o) const { std::cout << o << std::endl; } + + //! \brief ... + void version() const; + + // ---------------------------------------------------------------- + //! \brief future STF file support in test bench + std::string stf_file{""}; + //! \brief ... + std::string output_file{""}; + //! \brief ... + std::vector isa_files; + //! \brief enable extra messages from the tb + bool tb_verbose{false}; + // ---------------------------------------------------------------- + // ---------------------------------------------------------------- + //! \brief ... + bool notify_error{false}; + //! \brief placeholder + bool _query_options{false}; + //! \brief ... + po::variables_map vm; + //! \brief ... + static Options* instance; + + private: + //! \brief ... + Options() {} + + //! \brief ... + Options(const Options &) = delete; + //! \brief ... + Options(Options &&) = delete; + //! \brief ... + Options & operator=(const Options &) = delete; +}; + +//! \brief ... +extern std::shared_ptr opts; diff --git a/fusion/test/TestBench.cpp b/fusion/test/TestBench.cpp new file mode 100644 index 00000000..c5ed2ce4 --- /dev/null +++ b/fusion/test/TestBench.cpp @@ -0,0 +1,665 @@ +// HEADER PLACEHOLDER +// contact Jeff Nye, jeffnye-gh, Condor Computing Corp. +// +#include "FieldExtractor.hpp" +#include "Fusion.hpp" +#include "FusionContext.hpp" +#include "FusionGroup.hpp" +#include "FusionTypes.hpp" +#include "MachineInfo.hpp" +#include "Msg.hpp" +#include "Options.hpp" +#include "RadixTrie.hpp" +#include "TestBench.hpp" + +#include + +#include +#include +#include +#include +#include +#include +using namespace std; + +// ==================================================================== +// ==================================================================== +TestBench::TestBench(int ac, char** av) +{ + msg->setWho("TestBench"); + opts->setupOptions(ac, av); + verbose = opts->tb_verbose; +} + +// -------------------------------------------------------------------- +// -------------------------------------------------------------------- +bool TestBench::run() +{ + // sanity check files + if (!sanityTest()) + return false; + if (!basicMavisTest()) + return false; + if (!basicConstraintsTest()) + return false; + if (!fusionGroupAltCtorTest()) + return false; + if (!fusionGroupCfgCtorTest()) + return false; + if (!fusionContextTest(true)) + return false; + + if (!fusionCtorCompileTest(true)) + return false; + if (!fusionSearchTest(true)) + return false; + + // FieldExtractor method tests + if (!fieldExtractorTests(true)) + return false; + + return true; +} + +// -------------------------------------------------------------------- +// -------------------------------------------------------------------- +bool TestBench::fusionContextTest(bool debug) +{ + if (verbose) + msg->imsg("fusionContextTest BEGIN"); + + using FusionGroupType = fusion::FusionGroup; + using FusionGroupListType = std::vector; + + fusion::FusionContext context_; + + FusionGroupType grp1("uf1", uf1, cbProxy::uf1_func); + FusionGroupType grp2("uf2", uf2, cbProxy::uf2_func); + FusionGroupType grp3("uf3", uf3, cbProxy::uf3_func); + + FusionGroupListType grouplist = {grp1, grp2, grp3}; + + try + { + context_.makeContext("fusionContextTest", grouplist); + } + catch (const fusion::ContextDuplicateError & e) + { + std::cerr << "Caught ContextDuplicateError: " << e.what() << endl; + } + catch (const std::exception & e) + { + std::cerr << "Caught unclassified exception: " << e.what() << endl; + return false; + } + + if (verbose) + msg->imsg("fusionContextTest END"); + return true; +} + +// -------------------------------------------------------------------- +// -------------------------------------------------------------------- +bool TestBench::fusionSearchTest(bool debug) +{ + if (verbose) + msg->imsg("fusionSearchTest BEGIN"); + bool ok = true; + + using FusionGroupType = fusion::FusionGroup; + using FusionGroupCfgType = fusion::FusionGroupCfg; + using FusionType = fusion::Fusion; + + FusionType::FusionGroupCfgListType testCases = { + FusionGroupCfgType{.name = {"UF1"}, + .uids = uf1, + .transformFunc = &TestBench::cbProxy::uf1_func}, + FusionGroupCfgType{.name = {"UF1_1"}, + .uids = uf1_1, + .transformFunc = &TestBench::cbProxy::uf1_1_func}, + FusionGroupCfgType{.name = {"UF1_2"}, + .uids = uf1_2, + .transformFunc = &TestBench::cbProxy::uf1_2_func}, + FusionGroupCfgType{.name = {"UF1_3"}, + .uids = uf1_3, + .transformFunc = &TestBench::cbProxy::uf1_3_func}, + FusionGroupCfgType{.name = {"UF2"}, + .uids = uf2, + .transformFunc = &TestBench::cbProxy::uf2_func}, + FusionGroupCfgType{.name = {"UF3"}, + .uids = uf3, + .transformFunc = &TestBench::cbProxy::uf3_func}}; + + // Future add specific tests for hash creation + // std::unordered_map expHashes; + // generateExpectHashes(expHashes,testCases); + + InstPtrListType in, out; + + assign(in, of1, opts->isa_files); + assign(out, of1, opts->isa_files); + + size_t outSize = out.size(); + size_t inSize = in.size(); + + FusionType f(testCases); + f.fusionOperator(in, out); + + // The default operator appends in to out and clears in + if (in.size() != 0) + { + msg->emsg("fusionOperator failed to clean input vector"); + ok = false; + } + + if (out.size() != (outSize + inSize)) + { + msg->emsg("fusionOperator failed to properly append to output vector"); + ok = false; + } + + // Test the custom operator as lambda + auto customLambda = + [](FusionType & inst, fusion::InstPtrListType & in, fusion::InstPtrListType & out) + { + out = in; // in is not cleared + }; + + // Restore in/out + in.clear(); + out.clear(); + InstPtrListType tmp; + assign(in, of1, opts->isa_files); + inSize = in.size(); + + f.setFusionOpr(customLambda); + f.fusionOperator(in, out); + + // Did the lambda clear the in vector + if (in.size() == 0) + { + msg->emsg("the custom fusionOperator incorrectly cleared the " + "input vector"); + ok = false; + } + + // The resulting out vector should be the same size as in and tmp + bool sameSize = inSize == out.size() && inSize == in.size(); + if (!sameSize) + { + msg->emsg("with the custom fusionOperator the vector sizes are " + "mismatched"); + ok = false; + } + + if (verbose) + msg->imsg("fusionSearchTest END"); + return ok; +} + +// -------------------------------------------------------------------- +// -------------------------------------------------------------------- +void TestBench::generateExpectHashes(unordered_map & exp, + const FusionType::FusionGroupCfgListType & in) +{ + for (size_t i = 0; i < in.size(); ++i) + { + FusionGroupCfgType cfg = in[i]; + fusion::HashType expHash = jenkinsOneAtATime(cfg.uids.value()); + exp.insert(make_pair(cfg.name, expHash)); + } +} + +// -------------------------------------------------------------------- +// -------------------------------------------------------------------- +bool TestBench::fusionCtorCompileTest(bool debug) +{ + if (verbose) + msg->imsg("fusionCtorCompileTest BEGIN"); + bool ok = true; + + using FusionGroupType = fusion::FusionGroup; + using FusionType = fusion::Fusion; + + const FusionType::FusionGroupListType fusionGroup_list = {}; + const FusionType::FusionGroupCfgListType fusionGroupCfg_list = {}; + + FusionType f4(fusionGroup_list); + FusionType f5(fusionGroupCfg_list); + + if (verbose) + msg->imsg("fusionCtorCompileTest END"); + return ok; +} + +// -------------------------------------------------------------------- +// -------------------------------------------------------------------- +bool TestBench::basicMavisTest(bool debug) +{ + if (verbose) + msg->imsg("basicMavisTest BEGIN"); + InstUidListType goldenUid = uf1; // { 0xb, 0xd, 0x1c, 0xf, 0x13 }; + OpcodeListType goldenOpc = of1; // { 0x76e9,0x685,0x8d35,0x1542,0x9141 }; + + Instruction::PtrType inst = nullptr; + + using MavisType = Mavis, uArchInfo>; + MavisType mavis_facade(opts->isa_files, {}); + + // Make sure the opcodes convert correctly + InstPtrListType instrs; + for (const auto opc : goldenOpc) + { + try + { + instrs.emplace_back(mavis_facade.makeInst(opc, 0)); + } + catch (const mavis::IllegalOpcode & ex) + { + cout << ex.what() << endl; + msg->emsg("basicMavisTest failed to convert opcode"); + return false; + } + } + + InstUidListType uids; + for (const auto & inst : instrs) + { + uids.emplace_back(inst->getUID()); + } + + if (instrs.size() != goldenUid.size() || instrs.size() != goldenOpc.size() + || instrs.size() != uids.size()) + { + msg->emsg("basicMavisTest size mismatch in inst vector"); + return false; + } + + if (debug) + info(goldenUid, uids, instrs); + + if (verbose) + msg->imsg("basicMavisTest END"); + return true; +} + +// -------------------------------------------------------------------- +// -------------------------------------------------------------------- +bool TestBench::fusionGroupAltCtorTest() +{ + if (verbose) + msg->imsg("fusionGroupAltCtorTest BEGIN"); + + // fusion ctor compile checks + fusion::FusionGroup f1; + fusion::FusionGroup f2{}; + fusion::FusionGroup f3 = + fusion::FusionGroup(); + + struct OtherMachine + { + OtherMachine() {} + }; + + struct OtherExtractor + { + OtherExtractor() {} + }; + + // Alternative machineinfo and extractor + using AltFusionGroupType = fusion::FusionGroup; + + InstUidListType alt_uid = {}; + + AltFusionGroupType alt1("alt1"); + AltFusionGroupType alt2("alt2", alt_uid); + + struct proxy + { + static bool alt_func(AltFusionGroupType &, InstPtrListType &, InstPtrListType &) + { + return true; + } + }; + + bool ok = true; + + AltFusionGroupType alt3("alt3", alt_uid); + alt3.setTransform(proxy::alt_func); + + InstPtrListType in, out; + + if (!alt3.transform(in, out)) + { + msg->emsg("alt3.transform() failed"); + ok = false; + } + + AltFusionGroupType alt4("alt4", alt_uid, proxy::alt_func); + if (!alt4.transform(in, out)) + { + msg->emsg("alt4.transform() failed"); + ok = false; + } + + if (verbose) + msg->imsg("fusionGroupAltCtorTest END"); + return ok; +} + +// -------------------------------------------------------------------- +// -------------------------------------------------------------------- +bool TestBench::fusionGroupCfgCtorTest() +{ + if (verbose) + msg->imsg("fusionGroupCfgCtorTest BEGIN"); + using FusionGroupCfgType = fusion::FusionGroupCfg; + + // --------------------------------------------------------------- + // Test that hash created from the F1CfgUid matches the hash from a + // base class reference instance + // --------------------------------------------------------------- + // reference version + fusion::FusionGroupBase reference; + reference.setUids(uf1); + fusion::HashType referenceHash = reference.hash(); + + // With uids, no opcs, no mavis + FusionGroupCfgType F1CfgUid{.name = {"F1CfgUid"}, + .uids = uf1, + .transformFunc = &f1_constraints}; + + bool ok = true; + + using FusionGroupType = fusion::FusionGroup; + FusionGroupType F1fromF1CfgUid(F1CfgUid); + + F1fromF1CfgUid.info(); + + if (referenceHash != F1fromF1CfgUid.hash()) + { + msg->emsg("F1fromF1CfgUid hash does not match reference hash"); + ok = false; + } + // --------------------------------------------------------------- + // Test that the F1CfgUid ctor results in a FusionGroup that can + // correctly transform this input group + InstPtrListType in, out; + // assign the input vector before transform + assign(in, of1, opts->isa_files); + + if (!F1fromF1CfgUid.transform(in, out)) + { + msg->emsg("F1fromF1CfgUid.transform() returned false"); + ok = false; + } + + if (in.size() != 0) + { + msg->emsg("F1fromF1CfgUid.f1_constraints failed to modify input"); + ok = false; + } + if (out.size() != 1) + { + msg->emsg("F1fromF1CfgUid.f1_constraints failed to modify output"); + ok = false; + } + // --------------------------------------------------------------- + // Test that a FusionGroupCfg constructed from opcodes acts like + // a FusionGroupCfg constructed from respective UIDs + // + // Future support + + // --------------------------------------------------------------- + // Test that a FusionGroupCfg constructed from assembly statements acts + // like a FusionGroupCfg constructed from respective UIDs + // + // Future support + + if (verbose) + msg->imsg("fusionGroupCfgCtorTest END"); + return ok; +} + +// -------------------------------------------------------------------- +// Fusion group transform test +// -------------------------------------------------------------------- +bool TestBench::basicConstraintsTest() +{ + if (verbose) + msg->imsg("basicConstraintsTest BEGIN"); + + FusionGroupType F1("F1", TestBench::uf1, f1_constraints); + + InstPtrListType in; + InstPtrListType out; + + // Create instr from opcodes + assign(in, of1, opts->isa_files); + + bool ok = true; + + if (!F1.transform(in, out)) + { + msg->emsg("F1.transform() returned false"); + ok = false; + } + + if (in.size() != 0) + { + msg->emsg("F1.f1_constraints failed to modify input"); + ok = false; + } + + if (out.size() != 1) + { + msg->emsg("F1.f1_constraints failed to modify output"); + ok = false; + } + + FusionGroupType F2("F2", TestBench::uf2); + + if (F2.getTransform() != nullptr) + { + msg->emsg("F2.transform() was not a nullptr as expected"); + ok = false; + } + + F2.setTransform(f1_constraints); + + if (F2.getTransform() == nullptr) + { + msg->emsg("F2.transform() was not set to handler as expected"); + ok = false; + } + + if (F2.transform(in, out)) + { + msg->emsg("F2.transform() failed to reject uf2 sequence"); + ok = false; + } + + if (verbose) + msg->imsg("basicConstraintsTest END"); + return ok; +} + +// -------------------------------------------------------------------- +// -------------------------------------------------------------------- +bool TestBench::radixTrieTest(bool) +{ + if (verbose) + msg->imsg("radixTrieTest BEGIN"); + RadixTrie<4> trie; + uint32_t num_values = 1024 * 1024; + + bool ok = true; + + std::mt19937 generator(std::random_device{}()); + std::uniform_int_distribution distribution(0, std::numeric_limits::max()); + + auto start = std::chrono::high_resolution_clock::now(); + for (uint32_t i = 0; i < num_values; ++i) + { + uint32_t randomNumber = distribution(generator); + trie.insert(randomNumber); + } + + auto end = std::chrono::high_resolution_clock::now(); + std::chrono::duration insertDuration = end - start; + std::cout << "Time taken for insertion: " << insertDuration.count() << " seconds" << std::endl; + + start = std::chrono::high_resolution_clock::now(); + for (uint32_t i = 0; i < num_values; ++i) + { + uint32_t randomNumber = distribution(generator); + trie.search(randomNumber); + } + + end = std::chrono::high_resolution_clock::now(); + std::chrono::duration searchDuration = end - start; + std::cout << "Time taken for searching: " << searchDuration.count() << " seconds" << std::endl; + + trie.insert(12345); + trie.insert(67890); + + std::cout << "Found '12345' " << (trie.search(12345) ? "Yes" : "No") << std::endl; + std::cout << "Found '67890' " << (trie.search(67890) ? "Yes" : "No") << std::endl; + std::cout << "Found '54321' " << (trie.search(54321) ? "Yes" : "No") << std::endl; + + if (trie.search(12345) && trie.search(67890) && !trie.search(54321)) + { + ok = true; + } + else + { + ok = false; + } + if (verbose) + msg->imsg("radixTrieTest END"); + return ok; +} + +// -------------------------------------------------------------------- +// -------------------------------------------------------------------- +bool TestBench::sanityTest(bool) +{ + bool ok = true; + + for (auto fn : opts->isa_files) + { + if (!std::filesystem::exists(fn)) + { + msg->emsg("Can not find isa file " + fn); + ok = false; + } + } + + return ok; +} + +// -------------------------------------------------------------------- +// -------------------------------------------------------------------- +void TestBench::assign(InstPtrListType & in, OpcodeListType & opcodes, + const FileNameListType & json_files) +{ + using MavisType = Mavis, uArchInfo>; + MavisType mavis_facade(json_files, {}); + + Instruction::PtrType inst = nullptr; + + for (const auto icode : opcodes) + { + try + { + in.emplace_back(mavis_facade.makeInst(icode, 0)); + } + catch (const mavis::IllegalOpcode & ex) + { + cout << ex.what() << endl; + } + } +} + +// ------------------------------------------------------------------------ +// zoo.F1 specific checks +// +// Operand requirements +// - rgrp[0].RD == rgrp[1].RD == rgrp[2].RS2 (note RS2 change) +// - rgrp[2].RD == rgrp[3].RD == rgrp[4].RD +// - rgrp[3].IMM == rgrp[4].IMM getField IMM not implemented +// ------------------------------------------------------------------------ +bool TestBench::f1_constraints(FusionGroupType & g, InstPtrListType & in, InstPtrListType & out) +{ + // This goup expects at least 5 instruction positions in the input + if (in.size() < 5) + return false; + + // number of wr/rd ports required by group tested against machine + // limits + if (g.fe().getIntWrPorts(in) > g.mi().maxIntWrPorts()) + return false; + if (g.fe().getIntRdPorts(in) > g.mi().maxIntRdPorts()) + return false; + + // using enum FieldExtractor::FieldName; requires c++20 + constexpr auto RD = FieldExtractor::FieldName::RD; + constexpr auto RS2 = FieldExtractor::FieldName::RS2; + + // Operand field encodings comparison against constraints + // The indexes are positions in the group, 0 = 1st instruction + if (g.fe().noteq(in, 0, 1, RD) || g.fe().noteq(in, 0, 2, RD, RS2) || g.fe().noteq(in, 2, 3, RD) + || g.fe().noteq(in, 2, 4, RD)) + { + return false; + } + + // This test only does constraints checking - fake a transform + out.emplace_back(in[0]); + in.clear(); + return true; +} + +// -------------------------------------------------------------------- +// -------------------------------------------------------------------- +fusion::HashType TestBench::jenkinsOneAtATime(const ::vector & v) +{ + fusion::HashType hash = 0; + + for (auto i : v) + { + hash += i; + hash += (hash << 10); + hash ^= (hash >> 6); + } + + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + + return hash; +} + +// -------------------------------------------------------------------- +void TestBench::info(InstUidListType & aUIDs, InstUidListType & bUIDs, InstPtrListType & instrs) +{ + cout << "aUIDs "; + for (auto uid : aUIDs) + { + cout << " 0x" << setw(8) << hex << setfill('0') << uid; + } + cout << endl; + cout << "bUIDs "; + for (auto uid : bUIDs) + { + cout << " 0x" << setw(8) << hex << setfill('0') << uid; + } + cout << endl; + + cout << "Instrs" << endl; + string pad = " "; + for (auto inst : instrs) + { + cout << pad << inst << endl; + } +} diff --git a/fusion/test/TestBench.hpp b/fusion/test/TestBench.hpp new file mode 100644 index 00000000..d9c801e3 --- /dev/null +++ b/fusion/test/TestBench.hpp @@ -0,0 +1,238 @@ +// HEADER PLACEHOLDER +// contact Jeff Nye, jeffnye-gh +// +//! \file TestBench.hpp testbench interface and utils +#pragma once +#include "Msg.hpp" +#include "FieldExtractor.hpp" +#include "Fusion.hpp" +#include "FusionGroup.hpp" +#include "FusionTypes.hpp" +#include "MachineInfo.hpp" + +//! \class TestBench +// ==================================================================== +// ==================================================================== +//! \brief local test bench for Fusion and related classes +struct TestBench +{ + //! \brief save typing + using FusionGroupType = fusion::FusionGroup; + //! \brief save typing + using FusionGroupListType = std::vector; + + //! \brief save typing + using FusionGroupCfgType = fusion::FusionGroupCfg; + + //! \brief save typing + using FusionType = fusion::Fusion; + + //! \brief save typing + using InstUidListType = fusion::InstUidListType; + + //! \brief save typing + using InstPtrListType = fusion::InstPtrListType; + + //! \brief save typing + using FileNameListType = fusion::FileNameListType; + + //! \brief save typing + using Opcode = fusion::Opcode; + + //! \brief save typing + using OpcodeListType = fusion::OpcodeListType; + + //! \brief save typing + using MavisType = fusion::MavisType; + + //! \brief ctor with boost program_option support + TestBench(int, char**); + + //! \brief hold functor proxies used by the tb + struct cbProxy; + + //! \brief run all tests + bool run(); + + //! \brief sanity check compilation of ctors + bool fusionCtorCompileTest(bool debug = false); + + //! \brief basic find fusiongroup, match to input, and transform + bool fusionSearchTest(bool debug = false); + + //! \brief sanity check the way mavis and isa files supplied are used + bool basicMavisTest(bool debug = false); + + //! \brief check constraints and perform simple transform + bool basicConstraintsTest(); + + //! \brief test using alternatives to MachineInfo and FieldExtractor + bool fusionGroupAltCtorTest(); + + //! \brief test choices in specifying FusionGroupCfg + bool fusionGroupCfgCtorTest(); + + //! \brief unit test for class::FusionContext + bool fusionContextTest(bool debug = false); + + //! \brief unit test for class::RadixTrie + bool radixTrieTest(bool debug = false); + + //! \brief catch all for start up checks + bool sanityTest(bool debug = false); + + // ------------------------------------------------------------- + // field extractor method tests and support, testfieldextractor.cpp + // ------------------------------------------------------------- + //! \brief top level extractor test runner + bool fieldExtractorTests(bool debug = false); + + //! \brief create an inst from opcode, catch conversion errors + FieldExtractor::InstPtrType makeInst(MavisType &, uint32_t); + + //! \brief helper for testing field values + bool testFieldValue(uint32_t, std::string, uint32_t act, uint32_t exp); + + //! \brief transform funcs + static bool f1_constraints(FusionGroupType &, InstPtrListType &, InstPtrListType &); + + //! \brief assign to InstPtrListType from opcodes + void assign(InstPtrListType &, std::vector &, const FileNameListType &); + //! \brief info debug function + void info(InstUidListType &, InstUidListType &, InstPtrListType &); + + //! \brief support golden reference hashes + void generateExpectHashes(std::unordered_map &, + const FusionType::FusionGroupCfgListType &); + + //! \brief duplicate of hash function found in fusion group + //! + //! duplicated to support debug + fusion::HashType jenkinsOneAtATime(const std::vector &); + + //! \brief common isa files to separate from the cmd line versions + static const std::vector stdIsaFiles; + + //! \brief extra messages + bool verbose{false}; + + //! \brief zoo.F1 example fusion group + //! + //! This is from the fusion group zoo, F1 has three variants. There are + //! three forms for each, opcode, uid, and asm text. I'm trying out + //! various methods for a user to express a fusion group when not using + //! the DSL. Note all of these will be needed. + //! @{ + static InstUidListType uf1, uf1_1, uf1_2, uf1_3; + static OpcodeListType of1, of1_1, of1_2, of1_3; + //! @} + + //! \brief zoo.F2 example fusion group + //! @{ + static InstUidListType uf2; + static OpcodeListType of2; + //! @} + + //! \brief zoo.F3 example fusion group + //! @{ + static InstUidListType uf3; + static OpcodeListType of3; + //! }@ + + //! \brief zoo.F4 example fusion group + //! @{ + static InstUidListType uf4; + static OpcodeListType of4; + //! }@ + + //! \brief zoo.F5 example fusion group and three variants + //! @{ + static InstUidListType uf5, uf5_1, uf5_2, uf5_3; + static OpcodeListType of5, of5_1, of5_2, of5_3; + //! }@ +}; + +// --------------------------------------------------------------------- +//! \brief call back proxies used in the unit test(s) +//! +//! There is a callback for each fusion group test case f1, f1.1, etc +// --------------------------------------------------------------------- +struct TestBench::cbProxy +{ + //! \brief ... + static bool uf1_func(FusionGroupType &, InstPtrListType &, InstPtrListType &) + { + msg->imsg("uf1_func called"); + return true; + } + + //! \brief ... + static bool uf1_1_func(FusionGroupType &, InstPtrListType &, InstPtrListType &) + { + msg->imsg("uf1_1_func called"); + return true; + } + + //! \brief ... + static bool uf1_2_func(FusionGroupType &, InstPtrListType &, InstPtrListType &) + { + msg->imsg("uf1_2_func called"); + return true; + } + + //! \brief ... + static bool uf1_3_func(FusionGroupType &, InstPtrListType &, InstPtrListType &) + { + msg->imsg("uf1_3_func called"); + return true; + } + + //! \brief ... + static bool uf2_func(FusionGroupType &, InstPtrListType &, InstPtrListType &) + { + msg->imsg("uf2_func called"); + return true; + } + + //! \brief ... + static bool uf3_func(FusionGroupType &, InstPtrListType &, InstPtrListType &) + { + msg->imsg("uf3_func called"); + return true; + } + + //! \brief ... + static bool uf4_func(FusionGroupType &, InstPtrListType &, InstPtrListType &) + { + msg->imsg("uf4_func called"); + return true; + } + + //! \brief ... + static bool uf5_func(FusionGroupType &, InstPtrListType &, InstPtrListType &) + { + msg->imsg("uf5_func called"); + return true; + } + + //! \brief ... + static bool uf5_1_func(FusionGroupType &, InstPtrListType &, InstPtrListType &) + { + msg->imsg("uf5_1_func called"); + return true; + } + + //! \brief ... + static bool uf5_2_func(FusionGroupType &, InstPtrListType &, InstPtrListType &) + { + msg->imsg("uf5_2_func called"); + return true; + } + + //! \brief ... + static bool uf5_3_func(FusionGroupType &, InstPtrListType &, InstPtrListType &) + { + msg->imsg("uf5_3_func called"); + return true; + } +}; diff --git a/fusion/test/TestData.cpp b/fusion/test/TestData.cpp new file mode 100644 index 00000000..951c8b8e --- /dev/null +++ b/fusion/test/TestData.cpp @@ -0,0 +1,125 @@ +// HEADER PLACEHOLDER +// contact Jeff Nye, jeffnye-gh, Condor Computing Corp. +// +#include "FieldExtractor.hpp" +#include "Fusion.hpp" +#include "FusionContext.hpp" +#include "FusionGroup.hpp" +#include "FusionTypes.hpp" +#include "MachineInfo.hpp" +#include "Msg.hpp" +#include "Options.hpp" +#include "RadixTrie.hpp" +#include "TestBench.hpp" + +#include + +#include +#include +#include +using namespace std; +// -------------------------------------------------------------------- +// Example +// -------------------------------------------------------------------- +// TestBench::OpcodeListType TestBench::of1 = { +// // INDEX DISASM FORM FIELDS UID +// 0x76e9, // 0 c.lui x13, -6 CI-TYPE RD IMM 0xb +// 0x0685, // 1 c.addi x13,0x1 I-TYPE RD RS1 IMM 0xd +// 0x8d35, // 2 c.xor x10,x13 R-TYPE RD RS1 RS2 0x1c +// 0x1542, // 3 c.slli x10,48 I-TYPE RD RS1 IMM 0xf +// 0x9141 // 4 c.srli x10,48 I-TYPE RD RS1 IMM 0x13 +// }; +// +// 0 U RD = G1 IMM = C1 +// 1 I RD = G1 RS1 = G1 IMM = C2 +// 2 R RD = G1 RS1 = G2 RD2 = G1 +// 3 I RD = G2 RS1 = G2 IMM = C3 +// 4 I RD = G2 RS1 = G2 IMM = C3 +// ---------------------------------------------------------------------- +TestBench::InstUidListType TestBench::uf1 = {0xb, 0xd, 0x1c, 0xf, 0x13}; +TestBench::OpcodeListType TestBench::of1 = { + // INDEX DISASM FORM FIELDS UID + 0x76e9, // 0 c.lui x13, -6 CI-TYPE RD IMM 0xb + 0x0685, // 1 c.addi x13,0x1 I-TYPE RD RS1 IMM 0xd + 0x8d35, // 2 c.xor x10,x13 R-TYPE RD RS1 RS2 0x1c + 0x1542, // 3 c.slli x10,48 I-TYPE RD RS1 IMM 0xf + 0x9141 // 4 c.srli x10,48 I-TYPE RD RS1 IMM 0x13 +}; +// fusion::HashType TestBench::hf1 = { 0xb,0xd,0x1c,0xf,0x13 }; +// -------------------------------------------------------------------- +// Fragment of of1 +// -------------------------------------------------------------------- +TestBench::InstUidListType TestBench::uf1_1 = {0xd, 0x1c, 0xf, 0x13}; +TestBench::OpcodeListType TestBench::of1_1 = { + 0x0685, // "c.addi x13,0x1", 0xd + 0x8d35, // "c.xor x10,x13", 0x1c + 0x1542, // "c.slli x10,48", 0xf + 0x9141 // "c.srli x10,48" 0x13 +}; +//// -------------------------------------------------------------------- +TestBench::InstUidListType TestBench::uf1_2 = {0x1c, 0xf, 0x13}; +TestBench::OpcodeListType TestBench::of1_2 = { + 0x8d35, // "c.xor x10,x13", 0xf + 0x1542, // "c.slli x10,48", 0xf + 0x9141 // "c.srli x10,48" 0x13 +}; +// -------------------------------------------------------------------- +TestBench::InstUidListType TestBench::uf1_3 = {0xf, 0x13}; +TestBench::OpcodeListType TestBench::of1_3 = { + 0x1542, // "c.slli x10,48", 0xf + 0x9141 // "c.srli x10,48" 0x13 +}; +// -------------------------------------------------------------------- +// -------------------------------------------------------------------- +TestBench::InstUidListType TestBench::uf2 = {0xd, 0x34}; +TestBench::OpcodeListType TestBench::of2 = { + 0x7159, // "c.addi16sp -112", 0xd + 0xf0a2 // "c.fswsp f8, 96" 0x34 +}; +// -------------------------------------------------------------------- +// -------------------------------------------------------------------- +TestBench::InstUidListType TestBench::uf3 = {0x17, 0x2d}; +TestBench::OpcodeListType TestBench::of3 = { + 0x843a, // "c.mv x8, x14", 0x17 + 0x6018 // "c.flw f14, 0(x8)" 0x2d +}; +// -------------------------------------------------------------------- +// -------------------------------------------------------------------- +TestBench::InstUidListType TestBench::uf4 = {0xf, 0x13}; +TestBench::OpcodeListType TestBench::of4 = { + 0xe014, // "c.fsw f13, 0(x8)", 0xf + 0x86a2 // "c.mv x13, x8"; 0x13 +}; +// -------------------------------------------------------------------- +// -------------------------------------------------------------------- +TestBench::InstUidListType TestBench::uf5 = {0x17, 0x2d, 0x34, 0x17, 0x4}; +TestBench::OpcodeListType TestBench::of5 = { + 0x843a, // "c.mv x8, x14", 0x17 + 0x6018, // "c.flw f14, 0(x8)", 0x2d + 0xe014, // "c.fsw f13, 0(x8)", 0x34 + 0x86a2, // "c.mv x13, x8", 0x17 + 0xff65 // "c.bnez x14, -8" 0x4 +}; +// -------------------------------------------------------------------- +TestBench::InstUidListType TestBench::uf5_1 = {0x17, 0x2d, 0x34, 0x17}; +TestBench::OpcodeListType TestBench::of5_1 = { + 0x843a, // "c.mv x8, x14", 0x17 + 0x6018, // "c.flw f14, 0(x8)", 0x2d + 0xe014, // "c.fsw f13, 0(x8)", 0x34 + 0x86a2 // "c.mv x13, x8" 0x17 +}; +// -------------------------------------------------------------------- +TestBench::InstUidListType TestBench::uf5_2 = {0x2d, 0x34, 0x17, 0x4}; +TestBench::OpcodeListType TestBench::of5_2 = { + 0x6018, // "c.flw f14, 0(x8)", 0x2d + 0xe014, // "c.fsw f13, 0(x8)", 0x34 + 0x86a2, // "c.mv x13, x8", 0x17 + 0xff65, // "c.bnez x14, -8" 0x4 +}; +// -------------------------------------------------------------------- +TestBench::InstUidListType TestBench::uf5_3 = {0x2d, 0x34, 0x17}; +TestBench::OpcodeListType TestBench::of5_3 = { + 0x6018, // "c.flw f14, 0(x8)", 0x2d + 0xe014, // "c.fsw f13, 0(x8)", 0x34 + 0x86a2 // "c.mv x13, x8" 0x17 +}; diff --git a/fusion/test/TestFieldExtractor.cpp b/fusion/test/TestFieldExtractor.cpp new file mode 100644 index 00000000..5dff01eb --- /dev/null +++ b/fusion/test/TestFieldExtractor.cpp @@ -0,0 +1,120 @@ +// HEADER PLACEHOLDER +// contact Jeff Nye, jeffnye-gh, Condor Computing Corp. +// +#include "Msg.hpp" +#include "Options.hpp" +#include "TestBench.hpp" +#include +#include +using namespace std; + +// -------------------------------------------------------------------- +// The intent is at least one test for the top level methods overall. +// +// Some method tests are covered as part of the Fusion Group and transform +// tests. SField and ImmField tests are less used elsewhere. +// These outliers are tested here. +// -------------------------------------------------------------------- +bool TestBench::fieldExtractorTests(bool debug) +{ + FieldExtractor fe; + using InstPtrType = FieldExtractor::InstPtrType; + + if (verbose) + msg->imsg("fieldExtractorTests BEGIN"); + + bool ok = true; + MavisType mavis(opts->isa_files, {}); + + // add x1,x2,x3 + InstPtrType inst_1 = makeInst(mavis, 0x003100b3); + + if (inst_1 == nullptr) + ok = false; + + using FN = FieldExtractor::FieldName; + using SFN = FieldExtractor::SFieldName; + + // Test for uint32_t getField(InstPtrType,FieldName) const + uint32_t rd_inst_1 = fe.getField(inst_1, FN::RD); + uint32_t rs1_inst_1 = fe.getField(inst_1, FN::RS1); + uint32_t rs2_inst_1 = fe.getField(inst_1, FN::RS2); + + if (!testFieldValue(0, "RD", rd_inst_1, 0x1)) + ok = false; + if (!testFieldValue(1, "RS1", rs1_inst_1, 0x2)) + ok = false; + if (!testFieldValue(2, "RS2", rs2_inst_1, 0x3)) + ok = false; + + bool isDest = false; + if (!fe.checkInstHasField(inst_1, FN::RD, isDest)) + { + msg->emsg("checkInstHasField() failed to detect RD"); + ok = false; + } + + if (!isDest) + { + msg->emsg("ID=3: checkInstHasField() failed to set isDest"); + ok = false; + } + + // Test for uint32_t getSField(InstPtrType,SFieldName) const + // [ funct7 | rs2 | rs1 | rm | rd | opcode ] + // 3322 2222 2222 1111 1111 11 + // 1098 7654 3210 9876 5432 1098 7654 3210 + // 0111 0010 1010 0111 1111 0101 0100 0011 + // RM should be 111 -> 0x7 + InstPtrType inst_2 = makeInst(mavis, 0x72a7f543); + if (inst_2 == nullptr) + ok = false; + uint32_t rm_inst_2 = fe.getSField(inst_2, SFN::RM); + if (!testFieldValue(4, "RM", rm_inst_2, 0x7)) + ok = false; + + // Test for uint32_t getImmField(InstPtrType inst) const + + if (!ok) + msg->emsg("fieldExtractor_tests FAILED"); + if (verbose) + msg->imsg("fieldExtractor_tests END"); + return ok; +} + +// -------------------------------------------------------------------- +// -------------------------------------------------------------------- +FieldExtractor::InstPtrType TestBench::makeInst(MavisType & m, uint32_t opc) +{ + FieldExtractor::InstPtrType inst; + + try + { + inst = m.makeInst(opc, 0); + } + catch (const std::exception &) + { + if (!inst) + { + std::ostringstream ss; + ss << "0x" << std::setw(8) << std::setfill('0') << std::hex << opc; + msg->emsg("Mavis could not create instruction from " + ss.str()); + return nullptr; + } + } + + return inst; +} + +// -------------------------------------------------------------------- +// -------------------------------------------------------------------- +bool TestBench::testFieldValue(uint32_t id, string name, uint32_t act, uint32_t exp) +{ + if (act != exp) + { + msg->emsg("ID:" + ::to_string(id) + ":FIELD:" + name + ": value mismatch"); + return false; + } + + return true; +} diff --git a/fusion/test/json/isa_rv64c.json b/fusion/test/json/isa_rv64c.json new file mode 100644 index 00000000..76e7a6f2 --- /dev/null +++ b/fusion/test/json/isa_rv64c.json @@ -0,0 +1,293 @@ +[ + { + "mnemonic" : "c.addi4spn", + "tags" : ["c"], + "expand" : "addi", + "form" : "C0", + "ignore" : ["func2A"], + "xform" : "CIW_sp", + "stencil" : "0x0" + }, + { + "mnemonic" : "c.lw", + "tags" : ["c"], + "expand" : "lw", + "form" : "C0", + "ignore" : ["func2A"], + "xform" : "C0_load_word", + "stencil" : "0x4000" + }, + { + "mnemonic" : "c.ld", + "tags" : ["c"], + "expand" : "ld", + "form" : "C0", + "ignore" : ["func2A"], + "xform" : "C0_load_double", + "stencil" : "0x6000" + }, + { + "mnemonic" : "c.sw", + "tags" : ["c"], + "expand" : "sw", + "form" : "C0", + "ignore" : ["func2A"], + "xform" : "C0_store_word", + "stencil" : "0xc000" + }, + { + "mnemonic" : "c.sd", + "tags" : ["c"], + "expand" : "sd", + "form" : "C0", + "ignore" : ["func2A"], + "xform" : "C0_store_double", + "stencil" : "0xe000" + }, + { + "mnemonic" : "c.addi", + "tags" : ["c"], + "expand" : "addi", + "form" : "C1", + "ignore" : ["func2", "func1", "func2b"], + "xform" : "CI_addi", + "stencil" : "0x1" + }, + { + "mnemonic" : "c.nop", + "tags" : ["c"], + "expand" : "nop", + "form" : "C1", + "ignore" : ["func1", "func2b"], + "fixed" : [ "rs1" ], + "xform" : "CI_addi", + "stencil" : "0x1" + }, + { + "mnemonic" : "c.addi16sp", + "tags" : ["c"], + "expand" : "addi", + "form" : "C1", + "ignore" : ["func1", "func2b"], + "fixed" : [ "rs1" ], + "xform" : "CI_sp", + "stencil" : "0x6101" + }, + { + "mnemonic" : "c.jr", + "tags" : ["c"], + "expand" : "jalr", + "form" : "C2", + "fixed" : ["rs2"], + "xform" : "CJR", + "stencil" : "0x8002" + }, + { + "mnemonic" : "c.addiw", + "tags" : ["c"], + "expand" : "addiw", + "form" : "C1", + "ignore" : ["func2", "func1", "func2b"], + "xform" : "CI_addiw", + "stencil" : "0x2001" + }, + { + "mnemonic" : "c.jalr", + "tags" : ["c"], + "expand" : "jalr", + "form" : "C2", + "fixed" : ["rs2"], + "xform" : "CJALR", + "stencil" : "0x9002" + }, + { + "mnemonic" : "c.li", + "tags" : ["c"], + "expand" : "li", + "form" : "C1", + "ignore" : ["func2", "func1", "func2b"], + "xform" : "CI_rD_only", + "stencil" : "0x4001" + }, + { + "mnemonic" : "c.lui", + "tags" : ["c"], + "expand" : "lui", + "form" : "C1", + "ignore" : ["func2", "func1", "func2b"], + "xform" : "CI_rD_shifted", + "stencil" : "0x6001" + }, + { + "mnemonic" : "c.srli", + "tags" : ["c"], + "expand" : "srli", + "form" : "C1", + "ignore" : ["func1", "func2b"], + "xform" : "CIX", + "stencil" : "0x8001" + }, + { + "mnemonic" : "c.srai", + "tags" : ["c"], + "expand" : "srai", + "form" : "C1", + "ignore" : ["func1", "func2b"], + "xform" : "CIX", + "stencil" : "0x8401" + }, + { + "mnemonic" : "c.andi", + "tags" : ["c"], + "expand" : "andi", + "form" : "C1", + "ignore" : ["func1", "func2b"], + "xform" : "CIX_andi", + "stencil" : "0x8801" + }, + { + "mnemonic" : "c.sub", + "tags" : ["c"], + "expand" : "sub", + "form" : "C1", + "xform" : "CA", + "stencil" : "0x8c01" + }, + { + "mnemonic" : "c.xor", + "tags" : ["c"], + "expand" : "xor", + "form" : "C1", + "xform" : "CA", + "stencil" : "0x8c21" + }, + { + "mnemonic" : "c.or", + "tags" : ["c"], + "expand" : "or", + "form" : "C1", + "xform" : "CA", + "stencil" : "0x8c41" + }, + { + "mnemonic" : "c.and", + "tags" : ["c"], + "expand" : "and", + "form" : "C1", + "xform" : "CA", + "stencil" : "0x8c61" + }, + { + "mnemonic" : "c.subw", + "tags" : ["c"], + "expand" : "subw", + "form" : "C1", + "xform" : "CA", + "stencil" : "0x9c01" + }, + { + "mnemonic" : "c.addw", + "tags" : ["c"], + "expand" : "addw", + "form" : "C1", + "xform" : "CA", + "stencil" : "0x9c21" + }, + { + "mnemonic" : "c.j", + "tags" : ["c"], + "expand" : "jal", + "form" : "C1", + "ignore" : ["func2", "func1", "func2b"], + "xform" : "CJ", + "stencil" : "0xa001" + }, + { + "mnemonic" : "c.beqz", + "tags" : ["c"], + "expand" : "beq", + "form" : "C1", + "ignore" : ["func2", "func1", "func2b"], + "xform" : "CB", + "stencil" : "0xc001" + }, + { + "mnemonic" : "c.bnez", + "tags" : ["c"], + "expand" : "bne", + "form" : "C1", + "ignore" : ["func2", "func1", "func2b"], + "xform" : "CB", + "stencil" : "0xe001" + }, + { + "mnemonic" : "c.slli", + "tags" : ["c"], + "expand" : "slli", + "form" : "C2", + "ignore" : ["func1"], + "xform" : "C2_slli", + "stencil" : "0x2" + }, + { + "mnemonic" : "c.lwsp", + "tags" : ["c"], + "expand" : "lw", + "form" : "C2", + "ignore" : ["func1"], + "xform" : "C2_sp_load_word", + "stencil" : "0x4002" + }, + { + "mnemonic" : "c.ldsp", + "tags" : ["c"], + "expand" : "ld", + "form" : "C2", + "ignore" : ["func1"], + "xform" : "C2_sp_load_double", + "stencil" : "0x6002" + }, + { + "mnemonic" : "c.mv", + "tags" : ["c"], + "expand" : "add", + "form" : "C2", + "type" : ["move"], + "xform" : "C2_mv", + "stencil" : "0x8002" + }, + { + "mnemonic" : "c.ebreak", + "tags" : ["c"], + "expand" : "ebreak", + "form" : "C2", + "fixed" : [ "rs1", "rs2" ], + "stencil" : "0x9002" + }, + { + "mnemonic" : "c.add", + "tags" : ["c"], + "expand" : "add", + "form" : "C2", + "xform" : "C2_add", + "stencil" : "0x9002" + }, + { + "mnemonic" : "c.swsp", + "tags" : ["c"], + "expand" : "sw", + "form" : "C2", + "ignore" : ["func1"], + "xform" : "C2_sp_store_word", + "stencil" : "0xc002" + }, + { + "mnemonic" : "c.sdsp", + "tags" : ["c"], + "expand" : "sd", + "form" : "C2", + "ignore" : ["func1"], + "xform" : "C2_sp_store_double", + "stencil" : "0xe002" + } +] diff --git a/fusion/test/json/isa_rv64g.json b/fusion/test/json/isa_rv64g.json new file mode 100644 index 00000000..69d86be1 --- /dev/null +++ b/fusion/test/json/isa_rv64g.json @@ -0,0 +1,1354 @@ +[ + { + "mnemonic" : "beq", + "form" : "B", + "stencil" : "0x63", + "type" : ["branch", "cond"], + "l-oper" : "all" + }, + { + "mnemonic" : "bne", + "form" : "B", + "stencil" : "0x1063", + "type" : ["branch", "cond"], + "l-oper" : "all" + }, + { + "mnemonic" : "blt", + "form" : "B", + "stencil" : "0x4063", + "type" : ["branch", "cond"], + "l-oper" : "all" + }, + { + "mnemonic" : "bge", + "form" : "B", + "stencil" : "0x5063", + "type" : ["branch", "cond"], + "l-oper" : "all" + }, + { + "mnemonic" : "bltu", + "form" : "B", + "stencil" : "0x6063", + "type" : ["branch", "cond"], + "l-oper" : "all" + }, + { + "mnemonic" : "bgeu", + "form" : "B", + "stencil" : "0x7063", + "type" : ["branch", "cond"], + "l-oper" : "all" + }, + { + "mnemonic" : "jalr", + "form" : "I", + "stencil" : "0x67", + "type" : ["branch","jalr"], + "l-oper" : "all" + }, + { + "mnemonic" : "jal", + "form" : "J", + "stencil" : "0x6f", + "type" : ["branch","jal"], + "l-oper" : "all" + }, + { + "mnemonic" : "lui", + "form" : "U", + "stencil" : "0x37", + "type" : ["int", "arith"], + "l-oper" : "all" + }, + { + "mnemonic" : "auipc", + "form" : "U", + "stencil" : "0x17", + "type" : ["int", "arith", "pc"], + "l-oper" : "all" + }, + { + "mnemonic" : "addi", + "form" : "ISH", + "ignore" : ["func6"], + "xform" : "I", + "stencil" : "0x13", + "type" : ["int", "arith"], + "l-oper" : "all" + }, + { + "mnemonic" : "li", + "tags" : ["i", "g"], + "form" : "ISH", + "ignore" : ["func6"], + "xform" : "I", + "stencil" : "0x13", + "fixed" : ["rs1"], + "type" : ["int", "arith"], + "l-oper" : "all" + }, + { + "mnemonic" : "nop", + "overlay" : { + "base" : "li", + "match" : ["0xFFFFFF80", "0x00000000"] + }, + "xform" : "I", + "type" : ["int", "arith"] + }, + { + "mnemonic" : "mv", + "overlay" : { + "base" : "addi", + "match" : ["0xFFF00000", "0x00000000"] + }, + "xform" : "I_mv", + "type" : ["int", "arith", "move"] + }, + { + "mnemonic" : "slli", + "form" : "ISH", + "stencil" : "0x1013", + "type" : ["int", "arith"], + "l-oper" : "all" + }, + { + "mnemonic" : "slti", + "form" : "ISH", + "ignore" : ["func6"], + "xform" : "I", + "stencil" : "0x2013", + "type" : ["int", "arith"], + "l-oper" : "all" + }, + { + "mnemonic" : "sltiu", + "form" : "ISH", + "ignore" : ["func6"], + "xform" : "I", + "stencil" : "0x3013", + "type" : ["int", "arith"], + "l-oper" : "all" + }, + { + "mnemonic" : "xori", + "form" : "ISH", + "ignore" : ["func6"], + "xform" : "I", + "stencil" : "0x4013", + "type" : ["int", "arith"], + "l-oper" : "all" + }, + { + "mnemonic" : "srli", + "form" : "ISH", + "stencil" : "0x5013", + "type" : ["int", "arith"], + "l-oper" : "all" + }, + { + "mnemonic" : "srai", + "form" : "ISH", + "stencil" : "0x40005013", + "type" : ["int", "arith"], + "l-oper" : "all" + }, + { + "mnemonic" : "ori", + "form" : "ISH", + "ignore" : ["func6"], + "xform" : "I", + "stencil" : "0x6013", + "type" : ["int", "arith"], + "l-oper" : "all" + }, + { + "mnemonic" : "andi", + "form" : "ISH", + "ignore" : ["func6"], + "xform" : "I", + "stencil" : "0x7013", + "type" : ["int", "arith"], + "l-oper" : "all" + }, + { + "mnemonic" : "add", + "form" : "R", + "stencil" : "0x33", + "type" : ["int", "arith"], + "l-oper" : "all" + }, + { + "mnemonic" : "sub", + "form" : "R", + "stencil" : "0x40000033", + "type" : ["int", "arith"], + "l-oper" : "all" + }, + { + "mnemonic" : "sll", + "form" : "R", + "stencil" : "0x1033", + "type" : ["int", "arith"], + "l-oper" : "all" + }, + { + "mnemonic" : "slt", + "form" : "R", + "stencil" : "0x2033", + "type" : ["int", "arith"], + "l-oper" : "all" + }, + { + "mnemonic" : "sltu", + "form" : "R", + "stencil" : "0x3033", + "type" : ["int", "arith"], + "l-oper" : "all" + }, + { + "mnemonic" : "xor", + "form" : "R", + "stencil" : "0x4033", + "type" : ["int", "arith"], + "l-oper" : "all" + }, + { + "mnemonic" : "srl", + "form" : "R", + "stencil" : "0x5033", + "type" : ["int", "arith"], + "l-oper" : "all" + }, + { + "mnemonic" : "sra", + "form" : "R", + "stencil" : "0x40005033", + "type" : ["int", "arith"], + "l-oper" : "all" + }, + { + "mnemonic" : "or", + "form" : "R", + "stencil" : "0x6033", + "type" : ["int", "arith"], + "l-oper" : "all" + }, + { + "mnemonic" : "and", + "form" : "R", + "stencil" : "0x7033", + "type" : ["int", "arith"], + "l-oper" : "all" + }, + { + "mnemonic" : "addiw", + "form" : "ISHW", + "ignore" : [ "func7" ], + "xform" : "I", + "stencil" : "0x1b", + "w-oper" : "all", + "type" : ["int", "arith"], + "l-oper" : "all" + }, + { + "mnemonic" : "slliw", + "form" : "ISHW", + "stencil" : "0x101b", + "type" : ["int", "arith"], + "l-oper" : "all" + }, + { + "mnemonic" : "srliw", + "form" : "ISHW", + "stencil" : "0x501b", + "type" : ["int", "arith"], + "l-oper" : "all" + }, + { + "mnemonic" : "sraiw", + "form" : "ISHW", + "stencil" : "0x4000501b", + "type" : ["int", "arith"], + "l-oper" : "all" + }, + { + "mnemonic" : "addw", + "form" : "R", + "stencil" : "0x3b", + "type" : ["int", "arith"], + "l-oper" : "all" + }, + { + "mnemonic" : "subw", + "form" : "R", + "stencil" : "0x4000003b", + "type" : ["int", "arith"], + "l-oper" : "all" + }, + { + "mnemonic" : "sllw", + "form" : "R", + "stencil" : "0x103b", + "type" : ["int", "arith"], + "l-oper" : "all" + }, + { + "mnemonic" : "srlw", + "form" : "R", + "stencil" : "0x503b", + "type" : ["int", "arith"], + "l-oper" : "all" + }, + { + "mnemonic" : "sraw", + "form" : "R", + "stencil" : "0x4000503b", + "type" : ["int", "arith"], + "l-oper" : "all" + }, + { + "mnemonic" : "lb", + "form" : "I", + "xform" : "I_load", + "stencil" : "0x3", + "type" : ["int", "load"], + "l-oper" : "all", + "data" : 8 + }, + { + "mnemonic" : "lh", + "form" : "I", + "xform" : "I_load", + "stencil" : "0x1003", + "type" : ["int", "load"], + "l-oper" : "all", + "data" : 16 + }, + { + "mnemonic" : "lw", + "form" : "I", + "xform" : "I_load", + "stencil" : "0x2003", + "type" : ["int", "load"], + "l-oper" : "all", + "data" : 32 + }, + { + "mnemonic" : "ld", + "form" : "I", + "xform" : "I_load", + "stencil" : "0x3003", + "type" : ["int", "load"], + "l-oper" : "all", + "data" : 64 + }, + { + "mnemonic" : "lbu", + "form" : "I", + "xform" : "I_load", + "stencil" : "0x4003", + "type" : ["int", "load"], + "l-oper" : "all", + "data" : 8 + }, + { + "mnemonic" : "lhu", + "form" : "I", + "xform" : "I_load", + "stencil" : "0x5003", + "type" : ["int", "load"], + "l-oper" : "all", + "data" : 16 + }, + { + "mnemonic" : "lwu", + "form" : "I", + "xform" : "I_load", + "stencil" : "0x6003", + "type" : ["int", "load"], + "l-oper" : "all", + "data" : 32 + }, + { + "mnemonic" : "sb", + "form" : "S", + "stencil" : "0x23", + "type" : ["int", "store"], + "l-oper" : "all", + "data" : 8 + }, + { + "mnemonic" : "sh", + "form" : "S", + "stencil" : "0x1023", + "type" : ["int", "store"], + "l-oper" : "all", + "data" : 16 + }, + { + "mnemonic" : "sw", + "form" : "S", + "stencil" : "0x2023", + "type" : ["int", "store"], + "l-oper" : "all", + "data" : 32 + }, + { + "mnemonic" : "sd", + "form" : "S", + "stencil" : "0x3023", + "type" : ["int", "store"], + "l-oper" : "all", + "data" : 64 + }, + { + "mnemonic" : "fence", + "form" : "FENCE", + "stencil" : "0xf", + "type" : ["fence"], + "l-oper" : "all" + }, + { + "mnemonic" : "fence.i", + "form" : "FENCE", + "xform" : "I", + "stencil" : "0x100f", + "type" : ["fence"], + "l-oper" : "all" + }, + { + "mnemonic" : "mul", + "form" : "R", + "stencil" : "0x2000033", + "type" : ["int", "mul"], + "l-oper" : "all" + }, + { + "mnemonic" : "mulh", + "form" : "R", + "stencil" : "0x2001033", + "type" : ["int", "mul"], + "l-oper" : "all" + }, + { + "mnemonic" : "mulhsu", + "form" : "R", + "stencil" : "0x2002033", + "type" : ["int", "mul"], + "l-oper" : "all" + }, + { + "mnemonic" : "mulhu", + "form" : "R", + "stencil" : "0x2003033", + "type" : ["int", "mul"], + "l-oper" : "all" + }, + { + "mnemonic" : "div", + "form" : "R", + "stencil" : "0x2004033", + "type" : ["int", "div"], + "l-oper" : "all" + }, + { + "mnemonic" : "divu", + "form" : "R", + "stencil" : "0x2005033", + "type" : ["int", "div"], + "l-oper" : "all" + }, + { + "mnemonic" : "rem", + "form" : "R", + "stencil" : "0x2006033", + "type" : ["int", "div"], + "l-oper" : "all" + }, + { + "mnemonic" : "remu", + "form" : "R", + "stencil" : "0x2007033", + "type" : ["int", "div"], + "l-oper" : "all" + }, + { + "mnemonic" : "mulw", + "form" : "R", + "stencil" : "0x200003b", + "type" : ["int", "mul"], + "l-oper" : "all" + }, + { + "mnemonic" : "divw", + "form" : "R", + "stencil" : "0x200403b", + "type" : ["int", "div"], + "l-oper" : "all" + }, + { + "mnemonic" : "divuw", + "form" : "R", + "stencil" : "0x200503b", + "type" : ["int", "div"], + "l-oper" : "all" + }, + { + "mnemonic" : "remw", + "form" : "R", + "stencil" : "0x200603b", + "type" : ["int", "div"], + "l-oper" : "all" + }, + { + "mnemonic" : "remuw", + "form" : "R", + "stencil" : "0x200703b", + "type" : ["int", "div"], + "l-oper" : "all" + }, + { + "mnemonic" : "amoadd.w", + "form" : "AMO", + "stencil" : "0x202f", + "type" : ["int", "arith", "atomic"], + "l-oper" : "all", + "data" : 32 + }, + { + "mnemonic" : "amoxor.w", + "form" : "AMO", + "stencil" : "0x2000202f", + "type" : ["int", "arith", "atomic"], + "l-oper" : "all", + "data" : 32 + }, + { + "mnemonic" : "amoor.w", + "form" : "AMO", + "stencil" : "0x4000202f", + "type" : ["int", "arith", "atomic"], + "l-oper" : "all", + "data" : 32 + }, + { + "mnemonic" : "amoand.w", + "form" : "AMO", + "stencil" : "0x6000202f", + "type" : ["int", "arith", "atomic"], + "l-oper" : "all", + "data" : 32 + }, + { + "mnemonic" : "amomin.w", + "form" : "AMO", + "stencil" : "0x8000202f", + "type" : ["int", "arith", "atomic"], + "l-oper" : "all", + "data" : 32 + }, + { + "mnemonic" : "amomax.w", + "form" : "AMO", + "stencil" : "0xa000202f", + "type" : ["int", "arith", "atomic"], + "l-oper" : "all", + "data" : 32 + }, + { + "mnemonic" : "amominu.w", + "form" : "AMO", + "stencil" : "0xc000202f", + "type" : ["int", "arith", "atomic"], + "l-oper" : "all", + "data" : 32 + }, + { + "mnemonic" : "amomaxu.w", + "form" : "AMO", + "stencil" : "0xe000202f", + "type" : ["int", "arith", "atomic"], + "l-oper" : "all", + "data" : 32 + }, + { + "mnemonic" : "amoswap.w", + "form" : "AMO", + "stencil" : "0x800202f", + "type" : ["int", "arith", "atomic"], + "l-oper" : "all", + "data" : 32 + }, + { + "mnemonic" : "lr.w", + "form" : "AMO", + "fixed" : ["rs2"], + "stencil" : "0x1000202f", + "type" : ["int", "load", "atomic"], + "l-oper" : "all", + "data" : 32 + }, + { + "mnemonic" : "sc.w", + "form" : "AMO", + "stencil" : "0x1800202f", + "type" : ["int", "store", "atomic"], + "l-oper" : "all", + "data" : 32 + }, + { + "mnemonic" : "amoadd.d", + "form" : "AMO", + "stencil" : "0x302f", + "type" : ["int", "arith", "atomic"], + "l-oper" : "all", + "data" : 64 + }, + { + "mnemonic" : "amoxor.d", + "form" : "AMO", + "stencil" : "0x2000302f", + "type" : ["int", "arith", "atomic"], + "l-oper" : "all", + "data" : 64 + }, + { + "mnemonic" : "amoor.d", + "form" : "AMO", + "stencil" : "0x4000302f", + "type" : ["int", "arith", "atomic"], + "l-oper" : "all", + "data" : 64 + }, + { + "mnemonic" : "amoand.d", + "form" : "AMO", + "stencil" : "0x6000302f", + "type" : ["int", "arith", "atomic"], + "l-oper" : "all", + "data" : 64 + }, + { + "mnemonic" : "amomin.d", + "form" : "AMO", + "stencil" : "0x8000302f", + "type" : ["int", "arith", "atomic"], + "l-oper" : "all", + "data" : 64 + }, + { + "mnemonic" : "amomax.d", + "form" : "AMO", + "stencil" : "0xa000302f", + "type" : ["int", "arith", "atomic"], + "l-oper" : "all", + "data" : 64 + }, + { + "mnemonic" : "amominu.d", + "form" : "AMO", + "stencil" : "0xc000302f", + "type" : ["int", "arith", "atomic"], + "l-oper" : "all", + "data" : 64 + }, + { + "mnemonic" : "amomaxu.d", + "form" : "AMO", + "stencil" : "0xe000302f", + "type" : ["int", "arith", "atomic"], + "l-oper" : "all", + "data" : 64 + }, + { + "mnemonic" : "amoswap.d", + "form" : "AMO", + "stencil" : "0x800302f", + "type" : ["int", "arith", "atomic"], + "l-oper" : "all", + "data" : 64 + }, + { + "mnemonic" : "lr.d", + "form" : "AMO", + "fixed" : ["rs2"], + "stencil" : "0x1000302f", + "type" : ["int", "load", "atomic"], + "l-oper" : "all", + "data" : 64 + }, + { + "mnemonic" : "sc.d", + "form" : "AMO", + "stencil" : "0x1800302f", + "type" : ["int", "store", "atomic"], + "l-oper" : "all", + "data" : 64 + }, + { + "mnemonic" : "ecall", + "form" : "R", + "fixed" : ["rs2", "rs1", "rd"], + "stencil" : "0x73", + "type" : ["int", "system"], + "l-oper" : "all" + }, + { + "mnemonic" : "ebreak", + "form" : "R", + "fixed" : ["rs2", "rs1", "rd"], + "stencil" : "0x100073", + "type" : ["int", "system"], + "l-oper" : "all" + }, + { + "mnemonic" : "uret", + "form" : "R", + "fixed" : ["rs2", "rs1", "rd"], + "stencil" : "0x200073", + "type" : ["system"] + }, + { + "mnemonic" : "sret", + "form" : "R", + "fixed" : ["rs2", "rs1", "rd"], + "stencil" : "0x10200073", + "type" : ["system"] + }, + { + "mnemonic" : "mret", + "form" : "R", + "fixed" : ["rs2", "rs1", "rd"], + "stencil" : "0x30200073", + "type" : ["system"] + }, + { + "mnemonic" : "dret", + "form" : "R", + "fixed" : ["rs2", "rs1", "rd"], + "stencil" : "0x7b200073", + "type" : ["system"] + }, + { + "mnemonic" : "sfence.vma", + "form" : "R", + "fixed" : ["rd"], + "stencil" : "0x12000073", + "type" : ["fence", "system"], + "l-oper" : "all" + }, + { + "mnemonic" : "wfi", + "form" : "R", + "fixed" : ["rs2", "rs1", "rd"], + "stencil" : "0x10500073", + "type" : ["system"] + }, + { + "mnemonic" : "csrrw", + "factory" : "csr", + "form" : "R", + "ignore" : ["func7"], + "stencil" : "0x1073", + "xform" : "CSR", + "type" : ["csr", "system"], + "l-oper" : "all" + }, + { + "mnemonic" : "csrrs", + "factory" : "csr", + "form" : "R", + "ignore" : ["func7"], + "stencil" : "0x2073", + "xform" : "CSR", + "type" : ["csr", "system"], + "l-oper" : "all" + }, + { + "mnemonic" : "csrrc", + "factory" : "csr", + "form" : "R", + "ignore" : ["func7"], + "stencil" : "0x3073", + "xform" : "CSR", + "type" : ["csr", "system"], + "l-oper" : "all" + }, + { + "mnemonic" : "csrrwi", + "factory" : "csr", + "form" : "R", + "ignore" : ["func7"], + "stencil" : "0x5073", + "xform" : "CSRI", + "type" : ["csr", "system"], + "l-oper" : "all" + }, + { + "mnemonic" : "csrrsi", + "factory" : "csr", + "form" : "R", + "ignore" : ["func7"], + "stencil" : "0x6073", + "xform" : "CSRI", + "type" : ["csr", "system"], + "l-oper" : "all" + }, + { + "mnemonic" : "csrrci", + "factory" : "csr", + "form" : "R", + "ignore" : ["func7"], + "stencil" : "0x7073", + "xform" : "CSRI", + "type" : ["csr", "system"], + "l-oper" : "all" + }, + { + "mnemonic" : "cflush.d.l1", + "form" : "R", + "fixed" : ["rd", "rs2"], + "stencil" : "0xfc000073", + "type" : ["cache", "system"], + "l-oper" : ["rs1"] + }, + { + "mnemonic" : "cdiscard.d.l1", + "form" : "R", + "fixed" : ["rd", "rs2"], + "stencil" : "0xfc200073", + "type" : ["cache", "system"] + }, + { + "mnemonic" : "cflush.i.l1", + "form" : "R", + "fixed" : ["rd", "rs2"], + "stencil" : "0xfc100073", + "type" : ["cache", "system"] + }, + { + "mnemonic" : "fadd.s", + "form" : "Rfloat", + "stencil" : "0x53", + "s-oper" : "all", + "type" : ["float", "arith"] + }, + { + "mnemonic" : "fsub.s", + "form" : "Rfloat", + "stencil" : "0x8000053", + "s-oper" : "all", + "type" : ["float", "arith"] + }, + { + "mnemonic" : "fmul.s", + "form" : "Rfloat", + "stencil" : "0x10000053", + "s-oper" : "all", + "type" : ["float", "mul"] + }, + { + "mnemonic" : "fdiv.s", + "form" : "Rfloat", + "stencil" : "0x18000053", + "s-oper" : "all", + "type" : ["float", "div"] + }, + { + "mnemonic" : "fsgnj.s", + "form" : "Rfloat", + "fixed" : [ "rm" ], + "stencil" : "0x20000053", + "s-oper" : "all", + "type" : ["float", "arith"] + }, + { + "mnemonic" : "fsgnjn.s", + "form" : "Rfloat", + "fixed" : [ "rm" ], + "stencil" : "0x20001053", + "s-oper" : "all", + "type" : ["float", "arith"] + }, + { + "mnemonic" : "fsgnjx.s", + "form" : "Rfloat", + "fixed" : [ "rm" ], + "stencil" : "0x20002053", + "s-oper" : "all", + "type" : ["float", "arith"] + }, + { + "mnemonic" : "fmin.s", + "form" : "Rfloat", + "fixed" : [ "rm" ], + "stencil" : "0x28000053", + "s-oper" : "all", + "type" : ["float", "arith"] + }, + { + "mnemonic" : "fmax.s", + "form" : "Rfloat", + "fixed" : [ "rm" ], + "stencil" : "0x28001053", + "s-oper" : "all", + "type" : ["float", "arith"] + }, + { + "mnemonic" : "fsqrt.s", + "form" : "Rfloat", + "fixed" : [ "rs2" ], + "stencil" : "0x58000053", + "s-oper" : "all", + "type" : ["float", "sqrt"] + }, + { + "mnemonic" : "fadd.d", + "form" : "Rfloat", + "stencil" : "0x2000053", + "d-oper" : "all", + "type" : ["float", "arith"] + }, + { + "mnemonic" : "fsub.d", + "form" : "Rfloat", + "stencil" : "0xa000053", + "d-oper" : "all", + "type" : ["float", "arith"] + }, + { + "mnemonic" : "fmul.d", + "form" : "Rfloat", + "stencil" : "0x12000053", + "d-oper" : "all", + "type" : ["float", "mul"] + }, + { + "mnemonic" : "fdiv.d", + "form" : "Rfloat", + "stencil" : "0x1a000053", + "d-oper" : "all", + "type" : ["float", "div"] + }, + { + "mnemonic" : "fsgnj.d", + "form" : "Rfloat", + "fixed" : [ "rm" ], + "stencil" : "0x22000053", + "d-oper" : "all", + "type" : ["float", "arith"] + }, + { + "mnemonic" : "fsgnjn.d", + "form" : "Rfloat", + "fixed" : [ "rm" ], + "stencil" : "0x22001053", + "d-oper" : "all", + "type" : ["float", "arith"] + }, + { + "mnemonic" : "fsgnjx.d", + "form" : "Rfloat", + "fixed" : [ "rm" ], + "stencil" : "0x22002053", + "d-oper" : "all", + "type" : ["float", "arith"] + }, + { + "mnemonic" : "fmin.d", + "form" : "Rfloat", + "fixed" : [ "rm" ], + "stencil" : "0x2a000053", + "d-oper" : "all", + "type" : ["float", "arith"] + }, + { + "mnemonic" : "fmax.d", + "form" : "Rfloat", + "fixed" : [ "rm" ], + "stencil" : "0x2a001053", + "d-oper" : "all", + "type" : ["float", "arith"] + }, + { + "mnemonic" : "fcvt.s.d", + "form" : "Rfloat", + "fixed" : [ "rs2" ], + "stencil" : "0x40100053", + "d-oper" : ["rs1"], + "s-oper" : ["rd"], + "type" : ["float", "convert"] + }, + { + "mnemonic" : "fcvt.d.s", + "form" : "Rfloat", + "fixed" : [ "rs2" ], + "stencil" : "0x42000053", + "d-oper" : ["rd"], + "s-oper" : ["rs1"], + "type" : ["float", "convert"] + }, + { + "mnemonic" : "fsqrt.d", + "form" : "Rfloat", + "fixed" : [ "rs2" ], + "stencil" : "0x5a000053", + "d-oper" : "all", + "type" : ["float", "sqrt"] + }, + { + "mnemonic" : "fle.s", + "form" : "Rfloat", + "fixed" : [ "rm" ], + "stencil" : "0xa0000053", + "s-oper" : ["rs1","rs2"], + "w-oper" : ["rd"], + "type" : ["float", "compare"] + }, + { + "mnemonic" : "flt.s", + "form" : "Rfloat", + "fixed" : [ "rm" ], + "stencil" : "0xa0001053", + "s-oper" : ["rs1","rs2"], + "w-oper" : ["rd"], + "type" : ["float", "compare"] + }, + { + "mnemonic" : "feq.s", + "form" : "Rfloat", + "fixed" : [ "rm" ], + "stencil" : "0xa0002053", + "s-oper" : ["rs1","rs2"], + "w-oper" : ["rd"], + "type" : ["float", "compare"] + }, + { + "mnemonic" : "fle.d", + "form" : "Rfloat", + "fixed" : [ "rm" ], + "stencil" : "0xa2000053", + "d-oper" : ["rs1","rs2"], + "w-oper" : ["rd"], + "type" : ["float", "compare"] + }, + { + "mnemonic" : "flt.d", + "form" : "Rfloat", + "fixed" : [ "rm" ], + "stencil" : "0xa2001053", + "d-oper" : ["rs1","rs2"], + "w-oper" : ["rd"], + "type" : ["float", "compare"] + }, + { + "mnemonic" : "feq.d", + "form" : "Rfloat", + "fixed" : [ "rm" ], + "stencil" : "0xa2002053", + "d-oper" : ["rs1","rs2"], + "w-oper" : ["rd"], + "type" : ["float", "compare"] + }, + { + "mnemonic" : "fcvt.w.s", + "form" : "Rfloat", + "fixed" : [ "rs2" ], + "stencil" : "0xc0000053", + "s-oper" : ["rs1"], + "w-oper" : ["rd"], + "type" : ["float", "convert"] + }, + { + "mnemonic" : "fcvt.wu.s", + "form" : "Rfloat", + "fixed" : [ "rs2" ], + "stencil" : "0xc0100053", + "s-oper" : ["rs1"], + "w-oper" : ["rd"], + "type" : ["float", "convert"] + }, + { + "mnemonic" : "fcvt.l.s", + "form" : "Rfloat", + "fixed" : [ "rs2" ], + "stencil" : "0xc0200053", + "s-oper" : ["rs1"], + "l-oper" : ["rd"], + "type" : ["float", "convert"] + }, + { + "mnemonic" : "fcvt.lu.s", + "form" : "Rfloat", + "fixed" : [ "rs2" ], + "stencil" : "0xc0300053", + "s-oper" : ["rs1"], + "l-oper" : ["rd"], + "type" : ["float", "convert"] + }, + { + "mnemonic" : "fmv.x.w", + "form" : "Rfloat", + "fixed" : [ "rm", "rs2" ], + "stencil" : "0xe0000053", + "s-oper" : ["rs1"], + "l-oper" : ["rd"], + "type" : ["float", "move"] + }, + { + "mnemonic" : "fclass.s", + "form" : "Rfloat", + "fixed" : [ "rm", "rs2" ], + "stencil" : "0xe0001053", + "s-oper" : ["rs1"], + "l-oper" : ["rd"], + "type" : ["float", "classify"] + }, + { + "mnemonic" : "fcvt.w.d", + "form" : "Rfloat", + "fixed" : [ "rs2" ], + "stencil" : "0xc2000053", + "d-oper" : ["rs1"], + "w-oper" : ["rd"], + "type" : ["float", "convert"] + }, + { + "mnemonic" : "fcvt.wu.d", + "form" : "Rfloat", + "fixed" : [ "rs2" ], + "stencil" : "0xc2100053", + "d-oper" : ["rs1"], + "w-oper" : ["rd"], + "type" : ["float", "convert"] + }, + { + "mnemonic" : "fcvt.l.d", + "form" : "Rfloat", + "fixed" : [ "rs2" ], + "stencil" : "0xc2200053", + "d-oper" : ["rs1"], + "l-oper" : ["rd"], + "type" : ["float", "convert"] + }, + { + "mnemonic" : "fcvt.lu.d", + "form" : "Rfloat", + "fixed" : [ "rs2" ], + "stencil" : "0xc2300053", + "d-oper" : ["rs1"], + "l-oper" : ["rd"], + "type" : ["float", "convert"] + }, + { + "mnemonic" : "fmv.x.d", + "form" : "Rfloat", + "fixed" : [ "rm", "rs2" ], + "stencil" : "0xe2000053", + "d-oper" : ["rs1"], + "l-oper" : ["rd"], + "type" : ["float", "move"] + }, + { + "mnemonic" : "fclass.d", + "form" : "Rfloat", + "fixed" : [ "rm", "rs2" ], + "stencil" : "0xe2001053", + "d-oper" : ["rs1"], + "l-oper" : ["rd"], + "type" : ["float", "classify"] + }, + { + "mnemonic" : "fcvt.s.w", + "form" : "Rfloat", + "fixed" : [ "rs2" ], + "stencil" : "0xd0000053", + "w-oper" : ["rs1"], + "s-oper" : ["rd"], + "type" : ["float", "convert"] + }, + { + "mnemonic" : "fcvt.s.wu", + "form" : "Rfloat", + "fixed" : [ "rs2" ], + "stencil" : "0xd0100053", + "w-oper" : ["rs1"], + "s-oper" : ["rd"], + "type" : ["float", "convert"] + }, + { + "mnemonic" : "fcvt.s.l", + "form" : "Rfloat", + "fixed" : [ "rs2" ], + "stencil" : "0xd0200053", + "l-oper" : ["rs1"], + "s-oper" : ["rd"], + "type" : ["float", "convert"] + }, + { + "mnemonic" : "fcvt.s.lu", + "form" : "Rfloat", + "fixed" : [ "rs2" ], + "stencil" : "0xd0300053", + "l-oper" : ["rs1"], + "s-oper" : ["rd"], + "type" : ["float", "convert"] + }, + { + "mnemonic" : "fmv.w.x", + "form" : "Rfloat", + "fixed" : [ "rm", "rs2" ], + "stencil" : "0xf0000053", + "l-oper" : ["rs1"], + "s-oper" : ["rd"], + "type" : ["float", "move"] + }, + { + "mnemonic" : "fcvt.d.w", + "form" : "Rfloat", + "fixed" : [ "rs2" ], + "stencil" : "0xd2000053", + "w-oper" : ["rs1"], + "d-oper" : ["rd"], + "type" : ["float", "convert"] + }, + { + "mnemonic" : "fcvt.d.wu", + "form" : "Rfloat", + "fixed" : [ "rs2" ], + "stencil" : "0xd2100053", + "w-oper" : ["rs1"], + "d-oper" : ["rd"], + "type" : ["float", "convert"] + }, + { + "mnemonic" : "fcvt.d.l", + "form" : "Rfloat", + "fixed" : [ "rs2" ], + "stencil" : "0xd2200053", + "l-oper" : ["rs1"], + "d-oper" : ["rd"], + "type" : ["float", "convert"] + }, + { + "mnemonic" : "fcvt.d.lu", + "form" : "Rfloat", + "fixed" : [ "rs2" ], + "stencil" : "0xd2300053", + "l-oper" : ["rs1"], + "d-oper" : ["rd"], + "type" : ["float", "convert"] + }, + { + "mnemonic" : "fmv.d.x", + "form" : "Rfloat", + "fixed" : [ "rm", "rs2" ], + "stencil" : "0xf2000053", + "l-oper" : ["rs1"], + "d-oper" : ["rd"], + "type" : ["float", "move"] + }, + { + "mnemonic" : "flw", + "form" : "VF_mem", + "xform" : "I_load", + "stencil" : "0x2007", + "ignore" : ["mewop"], + "type" : ["float", "load"], + "l-oper" : ["rs1"], + "s-oper" : ["rd"], + "data" : 32 + }, + { + "mnemonic" : "fld", + "form" : "VF_mem", + "xform" : "I_load", + "stencil" : "0x3007", + "ignore" : ["mewop"], + "type" : ["float", "load"], + "l-oper" : ["rs1"], + "d-oper" : ["rd"], + "data" : 64 + }, + { + "mnemonic" : "fsw", + "form" : "VF_mem", + "xform" : "S", + "stencil" : "0x2027", + "ignore" : ["mewop"], + "type" : ["float", "store"], + "l-oper" : ["rs1"], + "s-oper" : ["rs2"], + "data" : 32 + }, + { + "mnemonic" : "fsd", + "form" : "VF_mem", + "xform" : "S", + "stencil" : "0x3027", + "ignore" : ["mewop"], + "type" : ["float", "store"], + "l-oper" : ["rs1"], + "d-oper" : ["rs2"], + "data" : 64 + }, + { + "mnemonic" : "fmadd.s", + "form" : "R4", + "stencil" : "0x43", + "type" : ["float", "mac"], + "s-oper" : "all" + }, + { + "mnemonic" : "fmsub.s", + "form" : "R4", + "stencil" : "0x47", + "type" : ["float", "mac"], + "s-oper" : "all" + }, + { + "mnemonic" : "fnmsub.s", + "form" : "R4", + "stencil" : "0x4b", + "type" : ["float", "mac"], + "s-oper" : "all" + }, + { + "mnemonic" : "fnmadd.s", + "form" : "R4", + "stencil" : "0x4f", + "type" : ["float", "mac"], + "s-oper" : "all" + }, + { + "mnemonic" : "fmadd.d", + "form" : "R4", + "stencil" : "0x2000043", + "type" : ["float", "mac"], + "d-oper" : "all" + }, + { + "mnemonic" : "fmsub.d", + "form" : "R4", + "stencil" : "0x2000047", + "type" : ["float", "mac"], + "d-oper" : "all" + }, + { + "mnemonic" : "fnmsub.d", + "form" : "R4", + "stencil" : "0x200004b", + "type" : ["float", "mac"], + "d-oper" : "all" + }, + { + "mnemonic" : "fnmadd.d", + "form" : "R4", + "stencil" : "0x200004f", + "type" : ["float", "mac"], + "d-oper" : "all" + } +] diff --git a/fusion/test/main.cpp b/fusion/test/main.cpp new file mode 100644 index 00000000..6a3851d9 --- /dev/null +++ b/fusion/test/main.cpp @@ -0,0 +1,40 @@ +// HEADER PLACEHOLDER +// contact Jeff Nye, jeffnye-gh, Condor Computing Corp. +// +#include "TestBench.hpp" +#include "Msg.hpp" +#include "Options.hpp" + +#include +#include +using namespace std; + +Options* Options::instance = 0; +std::shared_ptr opts(Options::getInstance()); + +Msg* Msg::instance = 0; +std::unique_ptr msg(Msg::getInstance()); + +// ------------------------------------------------------------------------ +int main(int ac, char** av) +{ + TestBench tb(ac, av); + ofstream out("PASSFAIL"); + if (!out.is_open()) + { + msg->emsg("Could not open pass/fail status file"); + return 1; + } + + msg->imsg("Test run begin"); + bool ok = tb.run(); + if (!ok) + { + out << "FAIL" << endl; + msg->emsg("Test run end FAIL"); + return 1; + } + out << "PASS" << endl; + msg->imsg("Test run end PASS"); + return 0; +}