Move dead submodules in-tree

Signed-off-by: swurl <swurl@swurl.xyz>
This commit is contained in:
swurl 2025-05-31 02:33:02 -04:00
parent c0cceff365
commit 6c655321e6
No known key found for this signature in database
GPG key ID: A5A7629F109C8FD1
4081 changed files with 1185566 additions and 45 deletions

1
externals/dynarmic vendored

@ -1 +0,0 @@
Subproject commit c8389f4860bb41266a5b3aba197d54719c23bd64

215
externals/dynarmic/.clang-format vendored Normal file
View file

@ -0,0 +1,215 @@
---
Language: Cpp
AccessModifierOffset: -4
AlignAfterOpenBracket: Align
AlignConsecutiveMacros: None
AlignConsecutiveAssignments: None
AlignConsecutiveBitFields: None
AlignConsecutiveDeclarations: None
AlignEscapedNewlines: Right
AlignOperands: AlignAfterOperator
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: true
AllowAllConstructorInitializersOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortEnumsOnASingleLine: true
AllowShortBlocksOnASingleLine: Empty
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Inline
AllowShortLambdasOnASingleLine: All
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: true
AlwaysBreakTemplateDeclarations: Yes
AttributeMacros:
- __capability
BinPackArguments: true
BinPackParameters: false
BitFieldColonSpacing: Both
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: Never
AfterEnum: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
AfterExternBlock: false
BeforeCatch: false
BeforeElse: false
BeforeLambdaBody: false
BeforeWhile: false
IndentBraces: false
SplitEmptyFunction: false
SplitEmptyRecord: false
SplitEmptyNamespace: false
BreakBeforeBinaryOperators: All
BreakBeforeBraces: Custom
BreakBeforeConceptDeclarations: true
BreakBeforeTernaryOperators: true
BreakBeforeInheritanceComma: false
BreakConstructorInitializersBeforeComma: true
BreakConstructorInitializers: BeforeComma
BreakInheritanceList: BeforeComma
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 0
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 8
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DeriveLineEnding: true
DerivePointerAlignment: false
DisableFormat: false
# EmptyLineAfterAccessModifier: Leave
EmptyLineBeforeAccessModifier: Always
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IncludeBlocks: Regroup
IncludeCategories:
- Regex: '^<mach/'
Priority: 1
SortPriority: 0
CaseSensitive: false
- Regex: '^<windows.h>'
Priority: 1
SortPriority: 0
CaseSensitive: false
- Regex: '(^<signal.h>)|(^<sys/ucontext.h>)|(^<ucontext.h>)'
Priority: 1
SortPriority: 0
CaseSensitive: false
- Regex: '^<([^\.])*>$'
Priority: 2
SortPriority: 0
CaseSensitive: false
- Regex: '^<.*\.'
Priority: 3
SortPriority: 0
CaseSensitive: false
- Regex: '.*'
Priority: 4
SortPriority: 0
CaseSensitive: false
IncludeIsMainRegex: '([-_](test|unittest))?$'
IncludeIsMainSourceRegex: ''
# IndentAccessModifiers: false
IndentCaseBlocks: false
IndentCaseLabels: false
IndentExternBlock: NoIndent
IndentGotoLabels: false
IndentPPDirectives: AfterHash
IndentRequires: false
IndentWidth: 4
IndentWrappedFunctionNames: false
# InsertTrailingCommas: None
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: false
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
NamespaceMacros:
ObjCBinPackProtocolList: Never
ObjCBlockIndentWidth: 2
ObjCBreakBeforeNestedBlockParam: true
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 1
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 200
PenaltyIndentedWhitespace: 0
PointerAlignment: Left
RawStringFormats:
- Language: Cpp
Delimiters:
- cc
- CC
- cpp
- Cpp
- CPP
- 'c++'
- 'C++'
CanonicalDelimiter: ''
BasedOnStyle: google
- Language: TextProto
Delimiters:
- pb
- PB
- proto
- PROTO
EnclosingFunctions:
- EqualsProto
- EquivToProto
- PARSE_PARTIAL_TEXT_PROTO
- PARSE_TEST_PROTO
- PARSE_TEXT_PROTO
- ParseTextOrDie
- ParseTextProtoOrDie
- ParseTestProto
- ParsePartialTestProto
CanonicalDelimiter: ''
BasedOnStyle: google
ReflowComments: true
# ShortNamespaceLines: 5
SortIncludes: true
SortJavaStaticImport: Before
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: false
SpaceAroundPointerQualifiers: Default
SpaceBeforeAssignmentOperators: true
SpaceBeforeCaseColon: false
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceBeforeSquareBrackets: false
SpaceInEmptyBlock: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 2
SpacesInAngles: false
SpacesInConditionalStatement: false
SpacesInCStyleCastParentheses: false
SpacesInContainerLiterals: false
# SpacesInLineCommentPrefix: -1
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Latest
StatementAttributeLikeMacros:
- Q_EMIT
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
TabWidth: 4
TypenameMacros:
UseCRLF: false
UseTab: Never
WhitespaceSensitiveMacros:
- STRINGIZE
- PP_STRINGIZE
- BOOST_PP_STRINGIZE
- NS_SWIFT_NAME
- CF_SWIFT_NAME
- FCODE
- ICODE
...

13
externals/dynarmic/.gitignore vendored Normal file
View file

@ -0,0 +1,13 @@
# Built files
build/
build-*/
cmake-build-*/
.idea/
docs/Doxygen/
# Generated files
src/dynarmic/backend/arm64/mig/
src/dynarmic/backend/x64/mig/
# System files
.DS_Store
.vscode
.cache/

212
externals/dynarmic/CMakeLists.txt vendored Normal file
View file

@ -0,0 +1,212 @@
cmake_minimum_required(VERSION 3.12)
project(dynarmic LANGUAGES C CXX ASM VERSION 6.7.0)
# Determine if we're built as a subproject (using add_subdirectory)
# or if this is the master project.
set(MASTER_PROJECT OFF)
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
set(MASTER_PROJECT ON)
endif()
if (MASTER_PROJECT)
include(CTest)
endif()
# Dynarmic project options
option(DYNARMIC_ENABLE_CPU_FEATURE_DETECTION "Turning this off causes dynarmic to assume the host CPU doesn't support anything later than SSE3" ON)
option(DYNARMIC_ENABLE_NO_EXECUTE_SUPPORT "Enables support for systems that require W^X" OFF)
option(DYNARMIC_FATAL_ERRORS "Errors are fatal" OFF)
option(DYNARMIC_IGNORE_ASSERTS "Ignore asserts" OFF)
option(DYNARMIC_TESTS "Build tests" ${BUILD_TESTING})
option(DYNARMIC_TESTS_USE_UNICORN "Enable fuzzing tests against unicorn" OFF)
option(DYNARMIC_USE_LLVM "Support disassembly of jitted x86_64 code using LLVM" OFF)
option(DYNARMIC_USE_PRECOMPILED_HEADERS "Use precompiled headers" ON)
option(DYNARMIC_USE_BUNDLED_EXTERNALS "Use all bundled externals (useful when e.g. cross-compiling)" OFF)
option(DYNARMIC_WARNINGS_AS_ERRORS "Warnings as errors" ${MASTER_PROJECT})
if (NOT DEFINED DYNARMIC_FRONTENDS)
set(DYNARMIC_FRONTENDS "A32;A64" CACHE STRING "Selects which frontends to enable")
endif()
# Default to a Release build
if (NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE)
message(STATUS "Defaulting to a Release build")
endif()
# Set hard requirements for C++
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
# Disable in-source builds
set(CMAKE_DISABLE_SOURCE_CHANGES ON)
set(CMAKE_DISABLE_IN_SOURCE_BUILD ON)
if ("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}")
message(SEND_ERROR "In-source builds are not allowed.")
endif()
# Add the module directory to the list of paths
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/CMakeModules")
# Arch detection
include(DetectArchitecture)
if (NOT DEFINED ARCHITECTURE)
message(FATAL_ERROR "Unsupported architecture encountered. Ending CMake generation.")
endif()
message(STATUS "Target architecture: ${ARCHITECTURE}")
# Compiler flags
if (MSVC)
set(DYNARMIC_CXX_FLAGS
/experimental:external
/external:W0
/external:anglebrackets
/W4
/w44263 # Non-virtual member function hides base class virtual function
/w44265 # Class has virtual functions, but destructor is not virtual
/w44456 # Declaration of 'var' hides previous local declaration
/w44457 # Declaration of 'var' hides function parameter
/w44458 # Declaration of 'var' hides class member
/w44459 # Declaration of 'var' hides global definition
/w44946 # Reinterpret-cast between related types
/wd4592 # Symbol will be dynamically initialized (implementation limitation)
/permissive- # Stricter C++ standards conformance
/MP
/Zi
/Zo
/EHsc
/Zc:externConstexpr # Allows external linkage for variables declared "extern constexpr", as the standard permits.
/Zc:inline # Omits inline functions from object-file output.
/Zc:throwingNew # Assumes new (without std::nothrow) never returns null.
/volatile:iso # Use strict standard-abiding volatile semantics
/bigobj # Increase number of sections in .obj files
/DNOMINMAX)
if (DYNARMIC_WARNINGS_AS_ERRORS)
list(APPEND DYNARMIC_CXX_FLAGS
/WX)
endif()
if (${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang")
list(APPEND DYNARMIC_CXX_FLAGS
-Qunused-arguments
-Wno-missing-braces)
endif()
else()
set(DYNARMIC_CXX_FLAGS
-Wall
-Wextra
-Wcast-qual
-pedantic
-Wno-missing-braces
-Wstack-usage=4096)
if (ARCHITECTURE STREQUAL "x86_64")
list(APPEND DYNARMIC_CXX_FLAGS -mtune=core2)
endif()
if (DYNARMIC_WARNINGS_AS_ERRORS)
list(APPEND DYNARMIC_CXX_FLAGS
-pedantic-errors
-Werror)
endif()
if (DYNARMIC_FATAL_ERRORS)
list(APPEND DYNARMIC_CXX_FLAGS
-Wfatal-errors)
endif()
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
# GCC produces bogus -Warray-bounds warnings from xbyak headers for code paths that are not
# actually reachable. Specifically, it happens in cases where some code casts an Operand&
# to Address& after first checking isMEM(), and that code is inlined in a situation where
# GCC knows that the variable is actually a Reg64. isMEM() will never return true for a
# Reg64, but GCC doesn't know that.
list(APPEND DYNARMIC_CXX_FLAGS -Wno-array-bounds)
endif()
if (CMAKE_CXX_COMPILER_ID MATCHES "[Cc]lang")
# Bracket depth determines maximum size of a fold expression in Clang since 9c9974c3ccb6.
# And this in turns limits the size of a std::array.
list(APPEND DYNARMIC_CXX_FLAGS -fbracket-depth=1024)
endif()
endif()
# Forced use of individual bundled libraries for non-REQUIRED library is possible with e.g. cmake -DCMAKE_DISABLE_FIND_PACKAGE_fmt=ON ...
if (DYNARMIC_USE_BUNDLED_EXTERNALS)
set(CMAKE_DISABLE_FIND_PACKAGE_biscuit ON)
set(CMAKE_DISABLE_FIND_PACKAGE_Catch2 ON)
set(CMAKE_DISABLE_FIND_PACKAGE_fmt ON)
set(CMAKE_DISABLE_FIND_PACKAGE_mcl ON)
set(CMAKE_DISABLE_FIND_PACKAGE_oaknut ON)
set(CMAKE_DISABLE_FIND_PACKAGE_unordered_dense ON)
set(CMAKE_DISABLE_FIND_PACKAGE_xbyak ON)
set(CMAKE_DISABLE_FIND_PACKAGE_Zydis ON)
endif()
find_package(Boost 1.57 REQUIRED)
find_package(fmt 9 CONFIG)
find_package(mcl 0.1.12 EXACT CONFIG)
find_package(unordered_dense CONFIG)
if ("arm64" IN_LIST ARCHITECTURE OR DYNARMIC_TESTS)
find_package(oaknut 2.0.1 CONFIG)
endif()
if ("riscv" IN_LIST ARCHITECTURE)
find_package(biscuit 0.9.1 QUIET)
endif()
if ("x86_64" IN_LIST ARCHITECTURE)
find_package(xbyak 7 CONFIG)
find_package(Zydis 4 CONFIG)
endif()
if (DYNARMIC_USE_LLVM)
find_package(LLVM REQUIRED)
separate_arguments(LLVM_DEFINITIONS)
endif()
if (DYNARMIC_TESTS)
find_package(Catch2 3 CONFIG)
if (DYNARMIC_TESTS_USE_UNICORN)
find_package(Unicorn REQUIRED)
endif()
endif()
# Pull in externals CMakeLists for libs where available
add_subdirectory(externals)
# Dynarmic project files
add_subdirectory(src/dynarmic)
if (DYNARMIC_TESTS)
add_subdirectory(tests)
endif()
#
# Install
#
include(GNUInstallDirs)
include(CMakePackageConfigHelpers)
install(TARGETS dynarmic EXPORT dynarmicTargets)
install(EXPORT dynarmicTargets
NAMESPACE dynarmic::
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/dynarmic"
)
configure_package_config_file(CMakeModules/dynarmicConfig.cmake.in
dynarmicConfig.cmake
INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/dynarmic"
)
write_basic_package_version_file(dynarmicConfigVersion.cmake
COMPATIBILITY SameMajorVersion
)
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/dynarmicConfig.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/dynarmicConfigVersion.cmake"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/dynarmic"
)
install(DIRECTORY src/dynarmic TYPE INCLUDE FILES_MATCHING PATTERN "*.h")

View file

@ -0,0 +1,17 @@
# This function should be passed a name of an existing target. It will automatically generate
# file groups following the directory hierarchy, so that the layout of the files in IDEs matches the
# one in the filesystem.
function(create_target_directory_groups target_name)
# Place any files that aren't in the source list in a separate group so that they don't get in
# the way.
source_group("Other Files" REGULAR_EXPRESSION ".")
get_target_property(target_sources "${target_name}" SOURCES)
foreach(file_name IN LISTS target_sources)
get_filename_component(dir_name "${file_name}" PATH)
# Group names use '\' as a separator even though the entire rest of CMake uses '/'...
string(REPLACE "/" "\\" group_name "${dir_name}")
source_group("${group_name}" FILES "${file_name}")
endforeach()
endfunction()

View file

@ -0,0 +1,62 @@
include(CheckSymbolExists)
if (CMAKE_OSX_ARCHITECTURES)
set(DYNARMIC_MULTIARCH_BUILD 1)
set(ARCHITECTURE "${CMAKE_OSX_ARCHITECTURES}")
return()
endif()
function(detect_architecture symbol arch)
if (NOT DEFINED ARCHITECTURE)
set(CMAKE_REQUIRED_QUIET YES)
check_symbol_exists("${symbol}" "" DETECT_ARCHITECTURE_${arch})
unset(CMAKE_REQUIRED_QUIET)
if (DETECT_ARCHITECTURE_${arch})
set(ARCHITECTURE "${arch}" PARENT_SCOPE)
endif()
unset(DETECT_ARCHITECTURE_${arch} CACHE)
endif()
endfunction()
detect_architecture("__ARM64__" arm64)
detect_architecture("__aarch64__" arm64)
detect_architecture("_M_ARM64" arm64)
detect_architecture("__arm__" arm)
detect_architecture("__TARGET_ARCH_ARM" arm)
detect_architecture("_M_ARM" arm)
detect_architecture("__x86_64" x86_64)
detect_architecture("__x86_64__" x86_64)
detect_architecture("__amd64" x86_64)
detect_architecture("_M_X64" x86_64)
detect_architecture("__i386" x86)
detect_architecture("__i386__" x86)
detect_architecture("_M_IX86" x86)
detect_architecture("__ia64" ia64)
detect_architecture("__ia64__" ia64)
detect_architecture("_M_IA64" ia64)
detect_architecture("__mips" mips)
detect_architecture("__mips__" mips)
detect_architecture("_M_MRX000" mips)
detect_architecture("__ppc64__" ppc64)
detect_architecture("__powerpc64__" ppc64)
detect_architecture("__ppc__" ppc)
detect_architecture("__ppc" ppc)
detect_architecture("__powerpc__" ppc)
detect_architecture("_ARCH_COM" ppc)
detect_architecture("_ARCH_PWR" ppc)
detect_architecture("_ARCH_PPC" ppc)
detect_architecture("_M_MPPC" ppc)
detect_architecture("_M_PPC" ppc)
detect_architecture("__riscv" riscv)
detect_architecture("__EMSCRIPTEN__" wasm)

View file

@ -0,0 +1,37 @@
# Exports:
#
# Variables:
# LIBUNICORN_FOUND
# LIBUNICORN_INCLUDE_DIR
# LIBUNICORN_LIBRARY
#
# Target:
# Unicorn::Unicorn
#
find_path(LIBUNICORN_INCLUDE_DIR
unicorn/unicorn.h
HINTS $ENV{UNICORNDIR}
PATH_SUFFIXES include)
find_library(LIBUNICORN_LIBRARY
NAMES unicorn
HINTS $ENV{UNICORNDIR})
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Unicorn DEFAULT_MSG LIBUNICORN_LIBRARY LIBUNICORN_INCLUDE_DIR)
if (UNICORN_FOUND)
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
unset(THREADS_PREFER_PTHREAD_FLAG)
add_library(Unicorn::Unicorn UNKNOWN IMPORTED)
set_target_properties(Unicorn::Unicorn PROPERTIES
IMPORTED_LOCATION ${LIBUNICORN_LIBRARY}
INTERFACE_INCLUDE_DIRECTORIES ${LIBUNICORN_INCLUDE_DIR}
INTERFACE_LINK_LIBRARIES Threads::Threads
)
endif()
mark_as_advanced(LIBUNICORN_INCLUDE_DIR LIBUNICORN_LIBRARY)

View file

@ -0,0 +1,26 @@
function(target_architecture_specific_sources project arch)
if (NOT DYNARMIC_MULTIARCH_BUILD)
target_sources("${project}" PRIVATE ${ARGN})
return()
endif()
foreach(input_file IN LISTS ARGN)
if(input_file MATCHES ".cpp$")
if(NOT IS_ABSOLUTE ${input_file})
set(input_file "${CMAKE_CURRENT_SOURCE_DIR}/${input_file}")
endif()
set(output_file "${CMAKE_CURRENT_BINARY_DIR}/arch_gen/${input_file}")
add_custom_command(
OUTPUT "${output_file}"
COMMAND ${CMAKE_COMMAND} "-Darch=${arch}"
"-Dinput_file=${input_file}"
"-Doutput_file=${output_file}"
-P "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/impl/TargetArchitectureSpecificSourcesWrapFile.cmake"
DEPENDS "${input_file}"
VERBATIM
)
target_sources(${project} PRIVATE "${output_file}")
endif()
endforeach()
endfunction()

View file

@ -0,0 +1,33 @@
@PACKAGE_INIT@
include(CMakeFindDependencyMacro)
set(ARCHITECTURE "@ARCHITECTURE@")
if (NOT @BUILD_SHARED_LIBS@)
find_dependency(Boost 1.57)
find_dependency(fmt 9)
find_dependency(mcl 0.1.12 EXACT)
find_dependency(unordered_dense)
if ("arm64" IN_LIST ARCHITECTURE)
find_dependency(oaknut 2.0.1)
endif()
if ("riscv" IN_LIST ARCHITECTURE)
find_dependency(biscuit 0.9.1)
endif()
if ("x86_64" IN_LIST ARCHITECTURE)
find_dependency(xbyak 7)
find_dependency(Zydis 4)
endif()
if (@DYNARMIC_USE_LLVM@)
find_dependency(LLVM)
endif()
endif()
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
check_required_components(@PROJECT_NAME@)

View file

@ -0,0 +1,3 @@
string(TOUPPER "${arch}" arch)
file(READ "${input_file}" f_contents)
file(WRITE "${output_file}" "#include <mcl/macro/architecture.hpp>\n#if defined(MCL_ARCHITECTURE_${arch})\n${f_contents}\n#endif\n")

12
externals/dynarmic/LICENSE.txt vendored Normal file
View file

@ -0,0 +1,12 @@
Copyright (C) 2017 merryhime <git@mary.rs>
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

420
externals/dynarmic/README.md vendored Normal file
View file

@ -0,0 +1,420 @@
Dynarmic
========
[![Github Actions Build Status (x86-64)](https://github.com/yuzu-mirror/dynarmic/actions/workflows/x86-64.yml/badge.svg)](https://github.com/yuzu-mirror/dynarmic/actions/workflows/x86-64.yml) [![Github Actions Build Status (AArch64)](https://github.com/yuzu-mirror/dynarmic/actions/workflows/aarch64.yml/badge.svg)](https://github.com/yuzu-mirror/dynarmic/actions/workflows/AArch64.yml)
A dynamic recompiler for ARM.
Highlight features:
- Fast dynamic binary translation via Just-in-Time compilation
- Clean API
- Implemented in modern C++20
- Hooks exposed for easy code instrumentation
- Code injection support for very fine-grained instrumentation
- Support for unusual address space setups (bring-your-own memory system)
- Native support for most popular operating systems (Windows, macOS, Linux, FreeBSD, OpenBSD, NetBSD, Android)
*Please note that an adversarial guest program [can determine if it is being run under dynarmic](#disadvantages-of-dynarmic). Preventing this is not a goal of this project.*
### Supported guest architectures
* v3
* v4
* v4T
* v5TE
* v6K
* v6T2
* v7A
* 32-bit v8
* 64-bit v8
You can specify the specific guest version using [ArchVersion](src/dynarmic/interface/A32/arch_version.h).
There are no plans to support v1 or v2.
### Supported host architectures
* x86-64
* AArch64
There are no plans to support any 32-bit architecture.
Important API Changes in v6.x Series
------------------------------------
* **v6.7.0**
* To support use cases where one wants to have the guest to have the same address space as the host, `nullptr` is now a valid value for `fastmem_pointer`.
**This change is not backwards-compatible.** If you were previously using `nullptr` to represent an invalid fastmem arena, you will now have to use `std::nullopt`.
Documentation
-------------
Design documentation can be found at [docs/Design.md](docs/Design.md).
Usage Example
-------------
The below is a minimal example. Bring-your-own memory system.
```cpp
#include <array>
#include <cstdint>
#include <cstdio>
#include <exception>
#include "dynarmic/interface/A32/a32.h"
#include "dynarmic/interface/A32/config.h"
using u8 = std::uint8_t;
using u16 = std::uint16_t;
using u32 = std::uint32_t;
using u64 = std::uint64_t;
class MyEnvironment final : public Dynarmic::A32::UserCallbacks {
public:
u64 ticks_left = 0;
std::array<u8, 2048> memory{};
u8 MemoryRead8(u32 vaddr) override {
if (vaddr >= memory.size()) {
return 0;
}
return memory[vaddr];
}
u16 MemoryRead16(u32 vaddr) override {
return u16(MemoryRead8(vaddr)) | u16(MemoryRead8(vaddr + 1)) << 8;
}
u32 MemoryRead32(u32 vaddr) override {
return u32(MemoryRead16(vaddr)) | u32(MemoryRead16(vaddr + 2)) << 16;
}
u64 MemoryRead64(u32 vaddr) override {
return u64(MemoryRead32(vaddr)) | u64(MemoryRead32(vaddr + 4)) << 32;
}
void MemoryWrite8(u32 vaddr, u8 value) override {
if (vaddr >= memory.size()) {
return;
}
memory[vaddr] = value;
}
void MemoryWrite16(u32 vaddr, u16 value) override {
MemoryWrite8(vaddr, u8(value));
MemoryWrite8(vaddr + 1, u8(value >> 8));
}
void MemoryWrite32(u32 vaddr, u32 value) override {
MemoryWrite16(vaddr, u16(value));
MemoryWrite16(vaddr + 2, u16(value >> 16));
}
void MemoryWrite64(u32 vaddr, u64 value) override {
MemoryWrite32(vaddr, u32(value));
MemoryWrite32(vaddr + 4, u32(value >> 32));
}
void InterpreterFallback(u32 pc, size_t num_instructions) override {
// This is never called in practice.
std::terminate();
}
void CallSVC(u32 swi) override {
// Do something.
}
void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override {
// Do something.
}
void AddTicks(u64 ticks) override {
if (ticks > ticks_left) {
ticks_left = 0;
return;
}
ticks_left -= ticks;
}
u64 GetTicksRemaining() override {
return ticks_left;
}
};
int main(int argc, char** argv) {
MyEnvironment env;
Dynarmic::A32::UserConfig user_config;
user_config.callbacks = &env;
Dynarmic::A32::Jit cpu{user_config};
// Execute at least 1 instruction.
// (Note: More than one instruction may be executed.)
env.ticks_left = 1;
// Write some code to memory.
env.MemoryWrite16(0, 0x0088); // lsls r0, r1, #2
env.MemoryWrite16(2, 0xE7FE); // b +#0 (infinite loop)
// Setup registers.
cpu.Regs()[0] = 1;
cpu.Regs()[1] = 2;
cpu.Regs()[15] = 0; // PC = 0
cpu.SetCpsr(0x00000030); // Thumb mode
// Execute!
cpu.Run();
// Here we would expect cpu.Regs()[0] == 8
printf("R0: %u\n", cpu.Regs()[0]);
return 0;
}
```
Alternatives to Dynarmic
------------------------
Here are some projects with the same goals as dynarmic:
* [Unicorn](https://www.unicorn-engine.org/) - Recompiling multi-architecture CPU emulator, based on QEMU
* [SkyEye](http://skyeye.sourceforge.net) - Cached interpreter for ARM
More general alternatives:
* [tARMac](https://davidsharp.com/tarmac/) - Tarmac's use of armlets was initial inspiration for us to use an intermediate representation
* [QEMU](https://www.qemu.org/) - Recompiling multi-architecture system emulator
* [VisUAL](https://salmanarif.bitbucket.io/visual/index.html) - Visual ARM UAL emulator intended for education
* A wide variety of other recompilers, interpreters and emulators can be found embedded in other projects, here are some we would recommend looking at:
* [firebird's recompiler](https://github.com/nspire-emus/firebird) - Takes more of a call-threaded approach to recompilation
* [higan's arm7tdmi emulator](https://github.com/higan-emu/higan/tree/master/higan/component/processor/arm7tdmi) - Very clean code-style
* [arm-js by ozaki-r](https://github.com/ozaki-r/arm-js) - Emulates ARMv7A and some peripherals of Versatile Express, in the browser
Disadvantages of Dynarmic
-------------------------
In the pursuit of speed, some behavior not commonly depended upon is elided. Therefore this emulator does not match spec.
Please note that this would mean that a guest application can easily determine if it is being run under instrumentation.
Known examples:
* Only user-mode is emulated, there is no emulation of any other privilege levels.
* FPSR state is approximate.
* Misaligned loads/stores are not appropriately trapped in certain cases.
* Exclusive monitor behavior may not match any known physical processor.
No formal verification has been done, and no security assessment has been made.
Use this code base at your own risk.
Legal
-----
dynarmic is under a 0BSD license. See LICENSE.txt for more details.
dynarmic uses several other libraries, whose licenses are included below:
### biscuit
```
Copyright 2021 Lioncash/Lioncache
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
```
### catch
```
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
```
### fmt
```
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
```
### mcl & oaknut
```
MIT License
Copyright (c) 2022 merryhime <https://mary.rs>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
```
### unordered_dense
```
MIT License
Copyright (c) 2022 Martin Leitner-Ankerl
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
```
### xbyak
```
Copyright (c) 2007 MITSUNARI Shigeo
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
Neither the name of the copyright owner nor the names of its contributors may
be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
THE POSSIBILITY OF SUCH DAMAGE.
-----------------------------------------------------------------------------
ソースコード形式かバイナリ形式か、変更するかしないかを問わず、以下の条件を満た
す場合に限り、再頒布および使用が許可されます。
ソースコードを再頒布する場合、上記の著作権表示、本条件一覧、および下記免責条項
を含めること。
バイナリ形式で再頒布する場合、頒布物に付属のドキュメント等の資料に、上記の著作
権表示、本条件一覧、および下記免責条項を含めること。
書面による特別の許可なしに、本ソフトウェアから派生した製品の宣伝または販売促進
に、著作権者の名前またはコントリビューターの名前を使用してはならない。
本ソフトウェアは、著作権者およびコントリビューターによって「現状のまま」提供さ
れており、明示黙示を問わず、商業的な使用可能性、および特定の目的に対する適合性
に関する暗黙の保証も含め、またそれに限定されない、いかなる保証もありません。
著作権者もコントリビューターも、事由のいかんを問わず、 損害発生の原因いかんを
問わず、かつ責任の根拠が契約であるか厳格責任であるか(過失その他の)不法行為で
あるかを問わず、仮にそのような損害が発生する可能性を知らされていたとしても、
本ソフトウェアの使用によって発生した(代替品または代用サービスの調達、使用の
喪失、データの喪失、利益の喪失、業務の中断も含め、またそれに限定されない)直接
損害、間接損害、偶発的な損害、特別損害、懲罰的損害、または結果損害について、
一切責任を負わないものとします。
```
### zydis
```
The MIT License (MIT)
Copyright (c) 2014-2020 Florian Bernd
Copyright (c) 2014-2020 Joel Höner
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
```

324
externals/dynarmic/docs/Design.md vendored Normal file
View file

@ -0,0 +1,324 @@
# Dynarmic Design Documentation
Dynarmic is a dynamic recompiler for the ARMv6K architecture. Future plans for dynarmic include
support for other versions of the ARM architecture, having a interpreter mode, and adding support
for other architectures.
Users of this library interact with it primarily through the interface provided in
[`src/dynarmic/interface`](../src/dynarmic/interface). Users specify how dynarmic's CPU core interacts with
the rest of their system providing an implementation of the relevant `UserCallbacks` interface.
Users setup the CPU state using member functions of `Jit`, then call `Jit::Execute` to start CPU
execution. The callbacks defined on `UserCallbacks` may be called from dynamically generated code,
so users of the library should not depend on the stack being in a walkable state for unwinding.
* A32: [`Jit`](../src/dynarmic/interface/A32/a32.h), [`UserCallbacks`](../src/dynarmic/interface/A32/config.h)
* A64: [`Jit`](../src/dynarmic/interface/A64/a64.h), [`UserCallbacks`](../src/dynarmic/interface/A64/config.h)
Dynarmic reads instructions from memory by calling `UserCallbacks::MemoryReadCode`. These
instructions then pass through several stages:
1. Decoding (Identifying what type of instruction it is and breaking it up into fields)
2. Translation (Generation of high-level IR from the instruction)
3. Optimization (Eliminiation of redundant microinstructions, other speed improvements)
4. Emission (Generation of host-executable code into memory)
5. Execution (Host CPU jumps to the start of emitted code and runs it)
Using the A32 frontend with the x64 backend as an example:
* Decoding is done by [double dispatch](https://en.wikipedia.org/wiki/Visitor_pattern) in
[`src/frontend/A32/decoder/{arm.h,thumb16.h,thumb32.h}`](../src/dynarmic/frontend/A32/decoder/).
* Translation is done by the visitors in [`src/dynarmic/frontend/A32/translate/translate_{arm,thumb}.cpp`](../src/dynarmic/frontend/A32/translate/).
The function [`Translate`](../src/dynarmic/frontend/A32/translate/translate.h) takes a starting memory location,
some CPU state, and memory reader callback and returns a basic block of IR.
* The IR can be found under [`src/frontend/ir/`](../src/dynarmic/ir/).
* Optimizations can be found under [`src/ir_opt/`](../src/dynarmic/ir/opt/).
* Emission is done by `EmitX64` which can be found in [`src/dynarmic/backend/x64/emit_x64.{h,cpp}`](../src/dynarmic/backend/x64/).
* Execution is performed by calling `BlockOfCode::RunCode` in [`src/dynarmic/backend/x64/block_of_code.{h,cpp}`](../src/dynarmic/backend/x64/).
## Decoder
The decoder is a double dispatch decoder. Each instruction is represented by a line in the relevant
instruction table. Here is an example line from [`arm.h`](../src/dynarmic/frontend/A32/decoder/arm.h):
INST(&V::arm_ADC_imm, "ADC (imm)", "cccc0010101Snnnnddddrrrrvvvvvvvv")
(Details on this instruction can be found in section A8.8.1 of the ARMv7-A manual. This is encoding A1.)
The first argument to INST is the member function to call on the visitor. The second argument is a user-readable
instruction name. The third argument is a bit-representation of the instruction.
### Instruction Bit-Representation
Each character in the bitstring represents a bit. A `0` means that that bitposition **must** contain a zero. A `1`
means that that bitposition **must** contain a one. A `-` means we don't care about the value at that bitposition.
A string of the same character represents a field. In the above example, the first four bits `cccc` represent the
four-bit-long cond field of the ARM Add with Carry (immediate) instruction.
The visitor would have to have a function named `arm_ADC_imm` with 6 arguments, one for each field (`cccc`, `S`,
`nnnn`, `dddd`, `rrrr`, `vvvvvvvv`). If there is a mismatch of field number with argument number, a compile-time
error results.
## Translator
The translator is a visitor that uses the decoder to decode instructions. The translator generates IR code with the
help of the [`IREmitter` class](../src/dynarmic/ir/ir_emitter.h). An example of a translation function follows:
bool ArmTranslatorVisitor::arm_ADC_imm(Cond cond, bool S, Reg n, Reg d, int rotate, Imm8 imm8) {
u32 imm32 = ArmExpandImm(rotate, imm8);
// ADC{S}<c> <Rd>, <Rn>, #<imm>
if (ConditionPassed(cond)) {
auto result = ir.AddWithCarry(ir.GetRegister(n), ir.Imm32(imm32), ir.GetCFlag());
if (d == Reg::PC) {
ASSERT(!S);
ir.ALUWritePC(result.result);
ir.SetTerm(IR::Term::ReturnToDispatch{});
return false;
}
ir.SetRegister(d, result.result);
if (S) {
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
ir.SetVFlag(result.overflow);
}
}
return true;
}
where `ir` is an instance of the `IRBuilder` class. Each member function of the `IRBuilder` class constructs
an IR microinstruction.
## Intermediate Representation
Dynarmic uses an ordered SSA intermediate representation. It is very vaguely similar to those found in other
similar projects like redream, nucleus, and xenia. Major differences are: (1) the abundance of context
microinstructions whereas those projects generally only have two (`load_context`/`store_context`), (2) the
explicit handling of flags as their own values, and (3) very different basic block edge handling.
The intention of the context microinstructions and explicit flag handling is to allow for future optimizations. The
differences in the way edges are handled are a quirk of the current implementation and dynarmic will likely add a
function analyser in the medium-term future.
Dynarmic's intermediate representation is typed. Each microinstruction may take zero or more arguments and may
return zero or more arguments. A subset of the microinstructions available is documented below.
A complete list of microinstructions can be found in [src/dynarmic/ir/opcodes.inc](../src/dynarmic/ir/opcodes.inc).
The below lists some commonly used microinstructions.
### Immediate: Imm{U1,U8,U32,RegRef}
<u1> ImmU1(u1 value)
<u8> ImmU8(u8 value)
<u32> ImmU32(u32 value)
<RegRef> ImmRegRef(Arm::Reg gpr)
These instructions take a `bool`, `u8` or `u32` value and wraps it up in an IR node so that they can be used
by the IR.
### Context: {Get,Set}Register
<u32> GetRegister(<RegRef> reg)
<void> SetRegister(<RegRef> reg, <u32> value)
Gets and sets `JitState::Reg[reg]`. Note that `SetRegister(Arm::Reg::R15, _)` is disallowed by IRBuilder.
Use `{ALU,BX}WritePC` instead.
Note that sequences like `SetRegister(R4, _)` followed by `GetRegister(R4)` are
optimized away.
### Context: {Get,Set}{N,Z,C,V}Flag
<u1> GetNFlag()
<void> SetNFlag(<u1> value)
<u1> GetZFlag()
<void> SetZFlag(<u1> value)
<u1> GetCFlag()
<void> SetCFlag(<u1> value)
<u1> GetVFlag()
<void> SetVFlag(<u1> value)
Gets and sets bits in `JitState::Cpsr`. Similarly to registers redundant get/sets are optimized away.
### Context: BXWritePC
<void> BXWritePC(<u32> value)
This should probably be the last instruction in a translation block unless you're doing something fancy.
This microinstruction sets R15 and CPSR.T as appropriate.
### Callback: CallSupervisor
<void> CallSupervisor(<u32> svc_imm32)
This should probably be the last instruction in a translation block unless you're doing something fancy.
### Calculation: LastSignificant{Half,Byte}
<u16> LeastSignificantHalf(<u32> value)
<u8> LeastSignificantByte(<u32> value)
Extract a u16 and u8 respectively from a u32.
### Calculation: MostSignificantBit, IsZero
<u1> MostSignificantBit(<u32> value)
<u1> IsZero(<u32> value)
These are used to implement ARM flags N and Z. These can often be optimized away by the backend into a host flag read.
### Calculation: LogicalShiftLeft
(<u32> result, <u1> carry_out) LogicalShiftLeft(<u32> operand, <u8> shift_amount, <u1> carry_in)
Pseudocode:
if shift_amount == 0:
return (operand, carry_in)
x = operand * (2 ** shift_amount)
result = Bits<31,0>(x)
carry_out = Bit<32>(x)
return (result, carry_out)
This follows ARM semantics. Note `shift_amount` is not masked to 5 bits (like `SHL` does on x64).
### Calculation: LogicalShiftRight
(<u32> result, <u1> carry_out) LogicalShiftLeft(<u32> operand, <u8> shift_amount, <u1> carry_in)
Pseudocode:
if shift_amount == 0:
return (operand, carry_in)
x = ZeroExtend(operand, from_size: 32, to_size: shift_amount+32)
result = Bits<shift_amount+31,shift_amount>(x)
carry_out = Bit<shift_amount-1>(x)
return (result, carry_out)
This follows ARM semantics. Note `shift_amount` is not masked to 5 bits (like `SHR` does on x64).
### Calculation: ArithmeticShiftRight
(<u32> result, <u1> carry_out) ArithmeticShiftRight(<u32> operand, <u8> shift_amount, <u1> carry_in)
Pseudocode:
if shift_amount == 0:
return (operand, carry_in)
x = SignExtend(operand, from_size: 32, to_size: shift_amount+32)
result = Bits<shift_amount+31,shift_amount>(x)
carry_out = Bit<shift_amount-1>(x)
return (result, carry_out)
This follows ARM semantics. Note `shift_amount` is not masked to 5 bits (like `SAR` does on x64).
### Calcuation: RotateRight
(<u32> result, <u1> carry_out) RotateRight(<u32> operand, <u8> shift_amount, <u1> carry_in)
Pseudocode:
if shift_amount == 0:
return (operand, carry_in)
shift_amount %= 32
result = (operand << shift_amount) | (operand >> (32 - shift_amount))
carry_out = Bit<31>(result)
return (result, carry_out)
### Calculation: AddWithCarry
(<u32> result, <u1> carry_out, <u1> overflow) AddWithCarry(<u32> a, <u32> b, <u1> carry_in)
a + b + carry_in
### Calculation: SubWithCarry
(<u32> result, <u1> carry_out, <u1> overflow) SubWithCarry(<u32> a, <u32> b, <u1> carry_in)
This has equivalent semantics to `AddWithCarry(a, Not(b), carry_in)`.
a - b - !carry_in
### Calculation: And
<u32> And(<u32> a, <u32> b)
### Calculation: Eor
<u32> Eor(<u32> a, <u32> b)
Exclusive OR (i.e.: XOR)
### Calculation: Or
<u32> Or(<u32> a, <u32> b)
### Calculation: Not
<u32> Not(<u32> value)
### Callback: {Read,Write}Memory{8,16,32,64}
<u8> ReadMemory8(<u32> vaddr)
<u8> ReadMemory16(<u32> vaddr)
<u8> ReadMemory32(<u32> vaddr)
<u8> ReadMemory64(<u32> vaddr)
<void> WriteMemory8(<u32> vaddr, <u8> value_to_store)
<void> WriteMemory16(<u32> vaddr, <u16> value_to_store)
<void> WriteMemory32(<u32> vaddr, <u32> value_to_store)
<void> WriteMemory64(<u32> vaddr, <u64> value_to_store)
Memory access.
### Terminal: Interpret
SetTerm(IR::Term::Interpret{next})
This terminal instruction calls the interpreter, starting at `next`.
The interpreter must interpret exactly one instruction.
### Terminal: ReturnToDispatch
SetTerm(IR::Term::ReturnToDispatch{})
This terminal instruction returns control to the dispatcher.
The dispatcher will use the value in R15 to determine what comes next.
### Terminal: LinkBlock
SetTerm(IR::Term::LinkBlock{next})
This terminal instruction jumps to the basic block described by `next` if we have enough
cycles remaining. If we do not have enough cycles remaining, we return to the
dispatcher, which will return control to the host.
### Terminal: PopRSBHint
SetTerm(IR::Term::PopRSBHint{})
This terminal instruction checks the top of the Return Stack Buffer against R15.
If RSB lookup fails, control is returned to the dispatcher.
This is an optimization for faster function calls. A backend that doesn't support
this optimization or doesn't have a RSB may choose to implement this exactly as
ReturnToDispatch.
### Terminal: If
SetTerm(IR::Term::If{cond, term_then, term_else})
This terminal instruction conditionally executes one terminal or another depending
on the run-time state of the ARM flags.

2474
externals/dynarmic/docs/Doxyfile vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,76 @@
# Register Allocation (x64 Backend)
`HostLoc`s contain values. A `HostLoc` ("host value location") is either a host CPU register or a host spill location.
Values once set cannot be changed. Values can however be moved by the register allocator between `HostLoc`s. This is
handled by the register allocator itself and code that uses the register allocator need not and should not move values
between registers.
The register allocator is based on three concepts: `Use`, `Def` and `Scratch`.
* `Use`: The use of a value.
* `Define`: The definition of a value, this is the only time when a value is set.
* `Scratch`: Allocate a register that can be freely modified as one wishes.
Note that `Use`ing a value decrements its `use_count` by one. When the `use_count` reaches zero the value is discarded and no longer exists.
The member functions on `RegAlloc` are just a combination of the above concepts.
### `Scratch`
Xbyak::Reg64 ScratchGpr(HostLocList desired_locations = any_gpr)
Xbyak::Xmm ScratchXmm(HostLocList desired_locations = any_xmm)
At runtime, allocate one of the registers in `desired_locations`. You are free to modify the register. The register is discarded at the end of the allocation scope.
### Pure `Use`
Xbyak::Reg64 UseGpr(Argument& arg);
Xbyak::Xmm UseXmm(Argument& arg);
OpArg UseOpArg(Argument& arg);
void Use(Argument& arg, HostLoc host_loc);
At runtime, the value corresponding to `arg` will be placed a register. The actual register is determined by
which one of the above functions is called. `UseGpr` places it in an unused GPR, `UseXmm` places it
in an unused XMM register, `UseOpArg` might be in a register or might be a memory location, and `Use` allows
you to specify a specific register (GPR or XMM) to use.
This register **must not** have it's value changed.
### `UseScratch`
Xbyak::Reg64 UseScratchGpr(Argument& arg);
Xbyak::Xmm UseScratchXmm(Argument& arg);
void UseScratch(Argument& arg, HostLoc host_loc);
At runtime, the value corresponding to `arg` will be placed a register. The actual register is determined by
which one of the above functions is called. `UseScratchGpr` places it in an unused GPR, `UseScratchXmm` places it
in an unused XMM register, and `UseScratch` allows you to specify a specific register (GPR or XMM) to use.
The return value is the register allocated to you.
You are free to modify the value in the register. The register is discarded at the end of the allocation scope.
### `Define` as register
A `Define` is the defintion of a value. This is the only time when a value may be set.
void DefineValue(IR::Inst* inst, const Xbyak::Reg& reg);
By calling `DefineValue`, you are stating that you wish to define the value for `inst`, and you have written the
value to the specified register `reg`.
### `Define`ing as an alias of a different value
Adding a `Define` to an existing value.
void DefineValue(IR::Inst* inst, Argument& arg);
You are declaring that the value for `inst` is the same as the value for `arg`. No host machine instructions are
emitted.
## When to use each?
* Prefer `Use` to `UseScratch` where possible.
* Prefer the `OpArg` variants where possible.
* Prefer to **not** use the specific `HostLoc` variants where possible.

View file

@ -0,0 +1,145 @@
# Return Stack Buffer Optimization (x64 Backend)
One of the optimizations that dynarmic does is block-linking. Block-linking is done when
the destination address of a jump is available at JIT-time. Instead of returning to the
dispatcher at the end of a block we can perform block-linking: just jump directly to the
next block. This is beneficial because returning to the dispatcher can often be quite
expensive.
What should we do in cases when we can't predict the destination address? The eponymous
example is when executing a return statement at the end of a function; the return address
is not statically known at compile time.
We deal with this by using a return stack buffer: When we execute a call instruction,
we push our prediction onto the RSB. When we execute a return instruction, we pop a
prediction off the RSB. If the prediction is a hit, we immediately jump to the relevant
compiled block. Otherwise, we return to the dispatcher.
This is the essential idea behind this optimization.
## `UniqueHash`
One complication dynarmic has is that a compiled block is not uniquely identifiable by
the PC alone, but bits in the FPSCR and CPSR are also relevant. We resolve this by
computing a 64-bit `UniqueHash` that is guaranteed to uniquely identify a block.
u64 LocationDescriptor::UniqueHash() const {
// This value MUST BE UNIQUE.
// This calculation has to match up with EmitX64::EmitTerminalPopRSBHint
u64 pc_u64 = u64(arm_pc) << 32;
u64 fpscr_u64 = u64(fpscr.Value());
u64 t_u64 = cpsr.T() ? 1 : 0;
u64 e_u64 = cpsr.E() ? 2 : 0;
return pc_u64 | fpscr_u64 | t_u64 | e_u64;
}
## Our implementation isn't actually a stack
Dynarmic's RSB isn't actually a stack. It was implemented as a ring buffer because
that showed better performance in tests.
### RSB Structure
The RSB is implemented as a ring buffer. `rsb_ptr` is the index of the insertion
point. Each element in `rsb_location_descriptors` is a `UniqueHash` and they
each correspond to an element in `rsb_codeptrs`. `rsb_codeptrs` contains the
host addresses for the corresponding the compiled blocks.
`RSBSize` was chosen by performance testing. Note that this is bigger than the
size of the real RSB in hardware (which has 3 entries). Larger RSBs than 8
showed degraded performance.
struct JitState {
// ...
static constexpr size_t RSBSize = 8; // MUST be a power of 2.
u32 rsb_ptr = 0;
std::array<u64, RSBSize> rsb_location_descriptors;
std::array<u64, RSBSize> rsb_codeptrs;
void ResetRSB();
// ...
};
### RSB Push
We insert our prediction at the insertion point iff the RSB doesn't already
contain a prediction with the same `UniqueHash`.
void EmitX64::EmitPushRSB(IR::Block&, IR::Inst* inst) {
using namespace Xbyak::util;
ASSERT(inst->GetArg(0).IsImmediate());
u64 imm64 = inst->GetArg(0).GetU64();
Xbyak::Reg64 code_ptr_reg = reg_alloc.ScratchGpr({HostLoc::RCX});
Xbyak::Reg64 loc_desc_reg = reg_alloc.ScratchGpr();
Xbyak::Reg32 index_reg = reg_alloc.ScratchGpr().cvt32();
u64 code_ptr = unique_hash_to_code_ptr.find(imm64) != unique_hash_to_code_ptr.end()
? u64(unique_hash_to_code_ptr[imm64])
: u64(code->GetReturnFromRunCodeAddress());
code->mov(index_reg, dword[r15 + offsetof(JitState, rsb_ptr)]);
code->add(index_reg, 1);
code->and_(index_reg, u32(JitState::RSBSize - 1));
code->mov(loc_desc_reg, u64(imm64));
CodePtr patch_location = code->getCurr<CodePtr>();
patch_unique_hash_locations[imm64].emplace_back(patch_location);
code->mov(code_ptr_reg, u64(code_ptr)); // This line has to match up with EmitX64::Patch.
code->EnsurePatchLocationSize(patch_location, 10);
Xbyak::Label label;
for (size_t i = 0; i < JitState::RSBSize; ++i) {
code->cmp(loc_desc_reg, qword[r15 + offsetof(JitState, rsb_location_descriptors) + i * sizeof(u64)]);
code->je(label, code->T_SHORT);
}
code->mov(dword[r15 + offsetof(JitState, rsb_ptr)], index_reg);
code->mov(qword[r15 + index_reg.cvt64() * 8 + offsetof(JitState, rsb_location_descriptors)], loc_desc_reg);
code->mov(qword[r15 + index_reg.cvt64() * 8 + offsetof(JitState, rsb_codeptrs)], code_ptr_reg);
code->L(label);
}
In pseudocode:
for (i := 0 .. RSBSize-1)
if (rsb_location_descriptors[i] == imm64)
goto label;
rsb_ptr++;
rsb_ptr %= RSBSize;
rsb_location_desciptors[rsb_ptr] = imm64; //< The UniqueHash
rsb_codeptr[rsb_ptr] = /* codeptr corresponding to the UniqueHash */;
label:
## RSB Pop
To check if a predicition is in the RSB, we linearly scan the RSB.
void EmitX64::EmitTerminalPopRSBHint(IR::Term::PopRSBHint, IR::LocationDescriptor initial_location) {
using namespace Xbyak::util;
// This calculation has to match up with IREmitter::PushRSB
code->mov(ecx, MJitStateReg(Arm::Reg::PC));
code->shl(rcx, 32);
code->mov(ebx, dword[r15 + offsetof(JitState, FPSCR_mode)]);
code->or_(ebx, dword[r15 + offsetof(JitState, CPSR_et)]);
code->or_(rbx, rcx);
code->mov(rax, u64(code->GetReturnFromRunCodeAddress()));
for (size_t i = 0; i < JitState::RSBSize; ++i) {
code->cmp(rbx, qword[r15 + offsetof(JitState, rsb_location_descriptors) + i * sizeof(u64)]);
code->cmove(rax, qword[r15 + offsetof(JitState, rsb_codeptrs) + i * sizeof(u64)]);
}
code->jmp(rax);
}
In pseudocode:
rbx := ComputeUniqueHash()
rax := ReturnToDispatch
for (i := 0 .. RSBSize-1)
if (rbx == rsb_location_descriptors[i])
rax = rsb_codeptrs[i]
goto rax

View file

@ -0,0 +1,87 @@
# Always build externals as static libraries, even when dynarmic is built as shared
if (BUILD_SHARED_LIBS)
set(BUILD_SHARED_LIBS OFF)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set_property(DIRECTORY PROPERTY EXCLUDE_FROM_ALL ON)
endif()
# Allow options shadowing with normal variables when subproject use old cmake policy
set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)
# Disable tests in all externals supporting the standard option name
set(BUILD_TESTING OFF)
# For libraries that already come with a CMakeLists file,
# simply add the directory to that file as a subdirectory
# to have CMake automatically recognize them.
# biscuit
if (NOT TARGET biscuit::biscuit)
if ("riscv" IN_LIST ARCHITECTURE)
add_subdirectory(biscuit)
endif()
endif()
# catch
if (NOT TARGET Catch2::Catch2WithMain)
if (DYNARMIC_TESTS)
add_subdirectory(catch EXCLUDE_FROM_ALL)
endif()
endif()
# fmt
if (NOT TARGET fmt::fmt)
# fmtlib formatting library
set(FMT_INSTALL ON)
add_subdirectory(fmt)
endif()
# mcl
if (NOT TARGET merry::mcl)
set(MCL_INSTALL ON)
add_subdirectory(mcl)
endif()
# oaknut
if (NOT TARGET merry::oaknut)
if ("arm64" IN_LIST ARCHITECTURE)
add_subdirectory(oaknut)
elseif (DYNARMIC_TESTS)
add_subdirectory(oaknut EXCLUDE_FROM_ALL)
endif()
endif()
# unordered_dense
if (NOT TARGET ankerl::unordered_dense)
set(UNORDERED_DENSE_INSTALL ON)
add_subdirectory(unordered_dense)
endif()
# xbyak
if (NOT TARGET xbyak::xbyak)
if ("x86_64" IN_LIST ARCHITECTURE)
add_subdirectory(xbyak)
endif()
endif()
# zydis
if (NOT TARGET Zydis::Zydis)
if ("x86_64" IN_LIST ARCHITECTURE)
set(ZYDIS_BUILD_TOOLS OFF)
set(ZYDIS_BUILD_EXAMPLES OFF)
set(ZYDIS_BUILD_DOXYGEN OFF)
set(ZYAN_ZYCORE_PATH "${CMAKE_CURRENT_LIST_DIR}/zycore-c" CACHE PATH "")
set(CMAKE_DISABLE_FIND_PACKAGE_Doxygen ON)
if (NOT TARGET Zydis)
add_subdirectory(zydis)
endif()
endif()
endif()

View file

@ -0,0 +1,45 @@
name: Build and Test
on: [push, pull_request]
env:
BUILD_TYPE: Release
jobs:
build:
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
cpu_detection: [0, 1]
fail-fast: false
runs-on: ${{matrix.os}}
steps:
- name: Install build dependencies
if: ${{matrix.os == 'ubuntu-latest'}}
run: sudo apt-get install llvm ninja-build
- name: Install build dependencies
if: ${{matrix.os == 'macos-latest'}}
run: |
brew install llvm ninja
echo "/usr/local/opt/llvm/bin" >> $GITHUB_PATH
- name: Checkout biscuit repo
uses: actions/checkout@v2
- name: Configure CMake
run: >
cmake
-B ${{github.workspace}}/build
-G Ninja
- name: Build
working-directory: ${{github.workspace}}/build
run: ninja
- name: Test
working-directory: ${{github.workspace}}/build
run: ctest --extra-verbose -C ${{env.BUILD_TYPE}}

View file

@ -0,0 +1,3 @@
# Built files
build/
build-*/

View file

@ -0,0 +1,17 @@
cmake_minimum_required(VERSION 3.15)
project(biscuit VERSION 0.16.0)
include(CTest)
option(BISCUIT_CODE_BUFFER_MMAP "Use mmap for handling code buffers instead of new" OFF)
# Source directories
add_subdirectory(src)
if (BUILD_TESTING)
add_subdirectory(tests)
endif()
if (BUILD_EXAMPLES)
add_subdirectory(examples)
endif()

View file

@ -0,0 +1,12 @@
Copyright 2021 Lioncash/Lioncache
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.

View file

@ -0,0 +1,165 @@
# Biscuit: RISC-V Runtime Code Generation Library
*RISC it for the biscuit*
## About
An experimental runtime code generator for RISC-V.
This allows for runtime code generation of RISC-V instructions. Similar
to how [Xbyak](https://github.com/herumi/xbyak) allows for runtime code generation of x86 instructions.
## Implemented ISA Features
Includes both 32-bit and 64-bit instructions in the following:
| Feature | Version |
|:--------------|:-------:|
| A | 2.1 |
| B | 1.0 |
| C | 2.0 |
| D | 2.2 |
| F | 2.2 |
| H | 1.0 |
| K | 1.0.1 |
| M | 2.0 |
| N | 1.1 |
| Q | 2.2 |
| RV32I | 2.1 |
| RV64I | 2.1 |
| S | 1.12 |
| V | 1.0 |
| Ssctr | 1.0 rc6 |
| Sstc | 0.5.4 |
| XTheadBa | 1.0 |
| XTheadCondMov | 1.0 |
| Zabha | 1.0 |
| Zacas | 1.0 |
| Zawrs | 1.01 |
| Zcb | 1.0.4 |
| Zclsd | 0.10 |
| Zcmp | 1.0.4 |
| Zcmt | 1.0.4 |
| Zfa | 1.0 |
| Zfbfmin | 1.0 |
| Zfh | 1.0 |
| Zfhmin | 1.0 |
| Zicbom | 1.0 |
| Zicbop | 1.0 |
| Zicboz | 1.0 |
| Zicfilp | 1.0 |
| Zicfiss | 1.0 |
| Zicond | 1.0.1 |
| Zicsr | 2.0 |
| Zifencei | 2.0 |
| Zihintntl | 1.0 |
| Zilsd | 0.10 |
| Zvbb | 1.0 |
| Zvbc | 1.0 |
| Zvfbfmin | 1.0 |
| Zvfbfwma | 1.0 |
| Zvkn | 1.0 |
Note that usually only extensions considered ratified will be implemented
as non-ratified documents are considerably more likely to have
large changes made to them, which makes maintaining instruction
APIs a little annoying.
## Dependencies
Biscuit requires no external dependencies for its library other than the C++ standard library.
The tests, however, use the Catch2 testing library. This is included in tree so there's no need
to worry about installing it yourself if you wish to run said tests.
## Building Biscuit
1. Generate the build files for the project with CMake
2. Hit the build button in your IDE of choice, or run the relevant console command to build for the CMake generator you've chosen.
3. Done.
## Running Tests
1. Generate the build files for the project with CMake
2. Build the tests
3. Run the test executable directly, or enter `ctest` into your terminal.
## License
The library is licensed under the MIT license.
While it's not a requirement whatsoever, it'd be pretty neat if you told me that you found the library useful :-)
## Example
The following is an adapted equivalent of the `strlen` implementation within the RISC-V bit manipulation extension specification.
For brevity, it has been condensed to only handle little-endian platforms.
```cpp
// We prepare some contiguous buffer and give the pointer to the beginning
// of the data and the total size of the buffer in bytes to the assembler.
void strlen_example(uint8_t* buffer, size_t buffer_size) {
using namespace biscuit;
constexpr int ptrlog = 3;
constexpr int szreg = 8;
Assembler as(buffer, buffer_size);
Label done;
Label loop;
as.ANDI(a3, a0, szreg - 1); // Offset
as.ANDI(a1, a0, 0xFF8); // Align pointer
as.LI(a4, szreg);
as.SUB(a4, a4, a3); // XLEN - offset
as.SLLI(a3, a3, ptrlog); // offset * 8
as.LD(a2, 0, a1); // Chunk
//
// Shift the partial/unaligned chunk we loaded to remove the bytes
// from before the start of the string, adding NUL bytes at the end.
//
as.SRL(a2, a2, a3); // chunk >> (offset * 8)
as.ORCB(a2, a2);
as.NOT(a2, a2);
// Non-NUL bytes in the string have been expanded to 0x00, while
// NUL bytes have become 0xff. Search for the first set bit
// (corresponding to a NUL byte in the original chunk).
as.CTZ(a2, a2);
// The first chunk is special: compare against the number of valid
// bytes in this chunk.
as.SRLI(a0, a2, 3);
as.BGTU(a4, a0, &done);
as.ADDI(a3, a1, szreg);
as.LI(a4, -1);
// Our critical loop is 4 instructions and processes data in 4 byte
// or 8 byte chunks.
as.Bind(&loop);
as.LD(a2, szreg, a1);
as.ADDI(a1, a1, szreg);
as.ORCB(a2, a2);
as.BEQ(a2, a4, &loop);
as.NOT(a2, a2);
as.CTZ(a2, a2);
as.SUB(a1, a1, a3);
as.ADD(a0, a0, a1);
as.SRLI(a2, a2, 3);
as.ADD(a0, a0, a2);
as.Bind(&done);
as.RET();
}
```

View file

@ -0,0 +1,88 @@
---
Language: Cpp
# BasedOnStyle: LLVM
AccessModifierOffset: -4
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlinesLeft: false
AlignOperands: true
AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: true
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterClass: false
AfterControlStatement: false
AfterEnum: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
BeforeCatch: false
BeforeElse: false
IndentBraces: false
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Attach
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
ColumnLimit: 100
CommentPragmas: '^ IWYU pragma:'
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ]
IncludeCategories:
- Regex: '^\<[^Q][^/.>]*\>'
Priority: -2
- Regex: '^\<'
Priority: -1
- Regex: '^\"'
Priority: 0
IndentCaseLabels: false
IndentWidth: 4
IndentWrappedFunctionNames: false
KeepEmptyLinesAtTheStartOfBlocks: true
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBlockIndentWidth: 2
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 150
PointerAlignment: Left
ReflowComments: true
SortIncludes: true
SpaceAfterCStyleCast: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Cpp11
TabWidth: 4
UseTab: Never
...

View file

@ -0,0 +1,5 @@
@PACKAGE_INIT@
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@-targets.cmake")
check_required_components(@PROJECT_NAME@)

View file

@ -0,0 +1,2 @@
add_subdirectory(cpuinfo)
add_subdirectory(literal)

View file

@ -0,0 +1,3 @@
add_executable(cpuinfo cpuinfo.cpp)
target_link_libraries(cpuinfo biscuit)
set_property(TARGET cpuinfo PROPERTY CXX_STANDARD 20)

View file

@ -0,0 +1,31 @@
// Copyright (c), 2022, KNS Group LLC (YADRO)
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
#include <biscuit/assembler.hpp>
#include <biscuit/cpuinfo.hpp>
#include <iostream>
using namespace biscuit;
int main()
{
CPUInfo cpu;
std::cout << "Has I:" << cpu.Has(RISCVExtension::I) << std::endl;
std::cout << "Has M:" << cpu.Has(RISCVExtension::M) << std::endl;
std::cout << "Has A:" << cpu.Has(RISCVExtension::A) << std::endl;
std::cout << "Has F:" << cpu.Has(RISCVExtension::F) << std::endl;
std::cout << "Has D:" << cpu.Has(RISCVExtension::D) << std::endl;
std::cout << "Has C:" << cpu.Has(RISCVExtension::C) << std::endl;
std::cout << "Has V:" << cpu.Has(RISCVExtension::V) << std::endl;
if (cpu.Has(RISCVExtension::V)) {
std::cout << "VLENB:" << cpu.GetVlenb() << std::endl;
}
return 0;
}

View file

@ -0,0 +1,3 @@
add_executable(literal literal.cpp)
target_link_libraries(literal biscuit)
set_property(TARGET literal PROPERTY CXX_STANDARD 20)

View file

@ -0,0 +1,96 @@
#include <biscuit/assert.hpp>
#include <biscuit/assembler.hpp>
#include <iostream>
using namespace biscuit;
struct BigData {
uint64_t data_low;
uint64_t data_high;
};
constexpr uint64_t literal1_value = 0x1234567890ABCDEF;
constexpr uint64_t literal2_value = 0x1122334455667788;
constexpr uint64_t literal3_value = 0xFEDCBA0987654321;
constexpr uint64_t literal4_value = 0xAABBCCDDEEFF0011;
constexpr std::array<uint64_t, 4> big_array = {0xDEADBEEFDEADBEEF, 0x1111111111111111, 0x2222222222222222, 0x3333333333333333};
constexpr BigData big_data = {0xCAFECAFECAFECAFE, 0x1111111111111111};
void print_literals(uint64_t literal1, uint64_t literal2, uint64_t literal3, uint64_t literal4, uint64_t literal5, uint64_t literal6) {
std::cout << "Literal 1: " << std::hex << literal1 << std::endl;
std::cout << "Literal 2: " << std::hex << literal2 << std::endl;
std::cout << "Literal 3: " << std::hex << literal3 << std::endl;
std::cout << "Literal 4: " << std::hex << literal4 << std::endl;
std::cout << "Literal 5: " << std::hex << literal5 << std::endl;
std::cout << "Literal 6: " << std::hex << literal6 << std::endl;
BISCUIT_ASSERT(literal1 == literal1_value);
BISCUIT_ASSERT(literal2 == literal2_value);
BISCUIT_ASSERT(literal3 == literal3_value);
BISCUIT_ASSERT(literal4 == literal4_value);
BISCUIT_ASSERT(literal5 == big_array[0]);
BISCUIT_ASSERT(literal6 == big_data.data_low);
}
int main() {
Assembler as(0x5000);
Literal literal1(literal1_value);
Literal literal2(literal2_value);
Literal literal3(literal3_value);
Literal literal4(literal4_value);
Literal literal5(big_array);
Literal literal6(big_data);
// Literal placed before the code, more than 0x1000 bytes away
as.Place(&literal1);
as.AdvanceBuffer(as.GetCodeBuffer().GetCursorOffset() + 0x1000);
// Literal placed before the code, less than 0x1000 bytes away
as.Place(&literal2);
// Place a Literal that is an array of 4 uint64_t values
as.Place(&literal5);
// Place a Literal that is a custom POD type
as.Place(&literal6);
void (*code)() = reinterpret_cast<void(*)()>(as.GetCursorPointer());
as.ADDI(sp, sp, -8);
as.SD(ra, 0, sp);
as.LD(a0, &literal1);
as.LD(a1, &literal2);
as.LD(a2, &literal3);
as.LD(a3, &literal4);
as.LD(a4, &literal5);
as.LD(a5, &literal6);
as.LI(t0, (uint64_t)print_literals);
as.JALR(t0);
as.LD(ra, 0, sp);
as.ADDI(sp, sp, 8);
as.RET();
// Literal placed after the code, less than 0x1000 bytes away
as.Place(&literal3);
as.AdvanceBuffer(as.GetCodeBuffer().GetCursorOffset() + 0x1000);
// Literal placed after the code, more than 0x1000 bytes away
as.Place(&literal4);
as.GetCodeBuffer().SetExecutable();
code();
return 0;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,14 @@
#pragma once
#include <cstdio>
#include <cstdlib>
#define BISCUIT_ASSERT(condition) \
do { \
if (!(condition)) { \
std::printf("Assertion failed (%s)\nin %s, function %s line %i\n", \
#condition, \
__FILE__, __func__, __LINE__); \
std::abort(); \
} \
} while (false)

View file

@ -0,0 +1,233 @@
#pragma once
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <type_traits>
#include <biscuit/assert.hpp>
namespace biscuit {
/**
* An arbitrarily sized buffer that code is written into.
*
* Also contains other member functions for manipulating
* the data within the code buffer.
*/
class CodeBuffer {
public:
// Default capacity of 4KB.
static constexpr size_t default_capacity = 4096;
/**
* Constructor
*
* @param capacity The initial capacity of the code buffer in bytes.
*/
explicit CodeBuffer(size_t capacity = default_capacity);
/**
* Constructor
*
* @param buffer A non-null pointer to an allocated buffer of size `capacity`.
* @param capacity The capacity of the memory pointed to by `buffer`.
*
* @pre The given memory buffer must not be null.
* @pre The given memory buffer must be at minimum `capacity` bytes in size.
*
* @note The caller is responsible for managing the lifetime of the given memory.
* CodeBuffer will *not* free the memory once it goes out of scope.
*/
explicit CodeBuffer(uint8_t* buffer, size_t capacity);
// Copy constructor and assignment is deleted in order to prevent unintentional memory leaks.
CodeBuffer(const CodeBuffer&) = delete;
CodeBuffer& operator=(const CodeBuffer&) = delete;
// Move constructing or moving the buffer in general is allowed, as it's a transfer of control.
CodeBuffer(CodeBuffer&& other) noexcept;
CodeBuffer& operator=(CodeBuffer&& other) noexcept;
/**
* Destructor
*
* If a custom memory buffer is not given to the code buffer,
* then the code buffer will automatically free any memory
* it had allocated in order to be able to emit code.
*/
~CodeBuffer() noexcept;
/// Returns whether or not the memory is managed by the code buffer.
[[nodiscard]] bool IsManaged() const noexcept { return m_is_managed; }
/// Retrieves the current cursor position within the buffer.
[[nodiscard]] ptrdiff_t GetCursorOffset() const noexcept {
return m_cursor - m_buffer;
}
/// Retrieves the current address of the cursor within the buffer.
[[nodiscard]] uintptr_t GetCursorAddress() const noexcept {
return GetOffsetAddress(GetCursorOffset());
}
/// Retrieves the cursor pointer
[[nodiscard]] uint8_t* GetCursorPointer() noexcept {
return GetOffsetPointer(GetCursorOffset());
}
/// Retrieves the cursor pointer
[[nodiscard]] const uint8_t* GetCursorPointer() const noexcept {
return GetOffsetPointer(GetCursorOffset());
}
/// Sets the cursor pointer
void SetCursorPointer(uint8_t* ptr) noexcept {
BISCUIT_ASSERT(ptr >= m_buffer && ptr < m_buffer + m_capacity);
m_cursor = ptr;
}
/// Retrieves the address of an arbitrary offset within the buffer.
[[nodiscard]] uintptr_t GetOffsetAddress(ptrdiff_t offset) const noexcept {
return reinterpret_cast<uintptr_t>(GetOffsetPointer(offset));
}
/// Retrieves the pointer to an arbitrary location within the buffer.
[[nodiscard]] uint8_t* GetOffsetPointer(ptrdiff_t offset) noexcept {
auto pointer = m_buffer + offset;
BISCUIT_ASSERT(pointer >= m_buffer && pointer < m_buffer + m_capacity);
return pointer;
}
/// Retrieves the pointer to an arbitrary location within the buffer.
[[nodiscard]] const uint8_t* GetOffsetPointer(ptrdiff_t offset) const noexcept {
auto pointer = m_buffer + offset;
BISCUIT_ASSERT(pointer >= m_buffer && pointer < m_buffer + m_capacity);
return pointer;
}
/**
* Allows rewinding of the code buffer cursor.
*
* @param offset The offset to rewind the cursor by.
*
* @note If no offset is provided, then this function rewinds the
* cursor to the beginning of the buffer.
*
* @note The offset may not be larger than the current cursor offset
* and may not be less than the current buffer starting address.
*/
void RewindCursor(ptrdiff_t offset = 0) noexcept {
auto* rewound = m_buffer + offset;
BISCUIT_ASSERT(m_buffer <= rewound && rewound <= m_cursor);
m_cursor = rewound;
}
/**
* Allows advancing of the code buffer cursor.
*
* @param offset The offset to advance the cursor by.
*
* @note The offset may not be smaller than the current cursor offset
* and may not be larger than the current buffer capacity.
*/
void AdvanceCursor(ptrdiff_t offset) noexcept {
auto* forward = m_buffer + offset;
BISCUIT_ASSERT(m_cursor <= forward && forward < m_buffer + m_capacity);
m_cursor = forward;
}
/**
* Whether or not the underlying buffer has enough room for the
* given number of bytes.
*
* @param num_bytes The number of bytes to store in the buffer.
*/
[[nodiscard]] bool HasSpaceFor(size_t num_bytes) const noexcept {
return GetRemainingBytes() >= num_bytes;
}
/// Returns the size of the data written to the buffer in bytes.
[[nodiscard]] size_t GetSizeInBytes() const noexcept {
EnsureBufferRange();
return static_cast<size_t>(m_cursor - m_buffer);
}
/// Returns the total number of remaining bytes in the buffer.
[[nodiscard]] size_t GetRemainingBytes() const noexcept {
EnsureBufferRange();
return static_cast<size_t>((m_buffer + m_capacity) - m_cursor);
}
/**
* Grows the underlying memory of the code buffer
*
* @param new_capacity The new capacity of the code buffer in bytes.
*
* @pre The underlying memory of the code buffer *must* be managed
* by the code buffer itself. Attempts to grow the buffer
* with memory that is not managed by it will result in
* an assertion being hit.
*
* @note Calling this with a new capacity that is less than or equal
* to the current capacity of the buffer will result in
* this function doing nothing.
*/
void Grow(size_t new_capacity);
/**
* Emits a given value into the code buffer.
*
* @param value The value to emit into the code buffer.
* @tparam T A trivially-copyable type.
*/
template <typename T>
void Emit(T value) noexcept {
static_assert(std::is_trivially_copyable_v<T>,
"It's undefined behavior to memcpy a non-trivially-copyable type.");
BISCUIT_ASSERT(HasSpaceFor(sizeof(T)));
std::memcpy(m_cursor, &value, sizeof(T));
m_cursor += sizeof(T);
}
/// Emits a 16-bit value into the code buffer.
void Emit16(uint32_t value) noexcept {
Emit(static_cast<uint16_t>(value));
}
/// Emits a 32-bit value into the code buffer.
void Emit32(uint32_t value) noexcept {
Emit(value);
}
/**
* Sets the internal code buffer to be executable.
*
* @note This will make the contained region of memory non-writable
* to satisfy operating under W^X contexts. To make the
* region writable again, use SetWritable().
*/
void SetExecutable();
/**
* Sets the internal code buffer to be writable
*
* @note This will make the contained region of memory non-executable
* to satisfy operating under W^X contexts. To make the region
* executable again, use SetExecutable().
*/
void SetWritable();
private:
void EnsureBufferRange() const noexcept {
BISCUIT_ASSERT(m_cursor >= m_buffer && m_cursor <= m_buffer + m_capacity);
}
uint8_t* m_buffer = nullptr;
uint8_t* m_cursor = nullptr;
size_t m_capacity = 0;
bool m_is_managed = false;
};
} // namespace biscuit

View file

@ -0,0 +1,113 @@
// Copyright (c), 2022, KNS Group LLC (YADRO)
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
#pragma once
#include <biscuit/assembler.hpp>
#include <biscuit/registers.hpp>
#include <cstddef>
#include <cstdint>
namespace biscuit {
enum class RISCVExtension : uint64_t {
I,
M,
A,
F,
D,
C,
V,
Zba,
Zbb,
Zbs,
Zicboz,
Zbc,
Zbkb,
Zbkc,
Zbkx,
Zknd,
Zkne,
Zknh,
Zksed,
Zksh,
Zkt,
Zvbb,
Zvbc,
Zvkb,
Zvkg,
Zvkned,
Zvknha,
Zvknhb,
Zvksed,
Zvksh,
Zvkt,
Zfh,
Zfhmin,
Zihintntl,
Zvfh,
Zvfhmin,
Zfa,
Ztso,
Zacas,
Zicond,
Zihintpause,
Zve32x,
Zve32f,
Zve64x,
Zve64f,
Zve64d,
Zimop,
Zca,
Zcb,
Zcd,
Zcf,
Zcmop,
Zawrs
};
template <CSR csr>
struct CSRReader : public biscuit::Assembler {
// Buffer capacity exactly for 2 instructions.
static constexpr size_t capacity = 8;
CSRReader() : biscuit::Assembler{CSRReader::capacity} {
CSRR(a0, csr);
RET();
}
// Copy constructor and assignment.
CSRReader(const CSRReader&) = delete;
CSRReader& operator=(const CSRReader&) = delete;
// Move constructor and assignment.
CSRReader(CSRReader&&) = default;
CSRReader& operator=(CSRReader&&) = default;
template <typename CSRReaderFunc>
CSRReaderFunc GetCode() {
this->GetCodeBuffer().SetExecutable();
return reinterpret_cast<CSRReaderFunc>(this->GetBufferPointer(0));
}
};
/**
* Class that detects information about a RISC-V CPU.
*/
class CPUInfo {
public:
/**
* Checks if a particular RISC-V extension is available.
*
* @param extension The extension to check.
*/
bool Has(RISCVExtension extension) const;
/// Returns the vector register length in bytes.
uint32_t GetVlenb() const;
};
} // namespace biscuit

View file

@ -0,0 +1,465 @@
#pragma once
#include <cstdint>
namespace biscuit {
// Control and Status Register
enum class CSR : uint32_t {
// clang-format off
// User-level CSRs
UStatus = 0x000, // User status register
UIE = 0x004, // User interrupt-enable register
UTVEC = 0x005, // User trap handler base address
UScratch = 0x040, // Scratch register for user trap handlers
UEPC = 0x041, // User exception program counter
UCause = 0x042, // User trap cause
UTVal = 0x043, // User bad address or instruction
UIP = 0x044, // User interrupt pending
FFlags = 0x001, // Floating-point Accrued Exceptions
FRM = 0x002, // Floating-point Dynamic Rounding Mode
FCSR = 0x003, // Floating-point Control and Status Register (frm + fflags)
SSP = 0x011, // Shadow stack pointer
JVT = 0x017, // Table jump base vector and control register
Cycle = 0xC00, // Cycle counter for RDCYCLE instruction.
Time = 0xC01, // Timer for RDTIME instruction.
InstRet = 0xC02, // Instructions retired counter for RDINSTRET instruction.
HPMCounter3 = 0xC03, // Performance-monitoring counter.
HPMCounter4 = 0xC04, // Performance-monitoring counter.
HPMCounter5 = 0xC05, // Performance-monitoring counter.
HPMCounter6 = 0xC06, // Performance-monitoring counter.
HPMCounter7 = 0xC07, // Performance-monitoring counter.
HPMCounter8 = 0xC08, // Performance-monitoring counter.
HPMCounter9 = 0xC09, // Performance-monitoring counter.
HPMCounter10 = 0xC0A, // Performance-monitoring counter.
HPMCounter11 = 0xC0B, // Performance-monitoring counter.
HPMCounter12 = 0xC0C, // Performance-monitoring counter.
HPMCounter13 = 0xC0D, // Performance-monitoring counter.
HPMCounter14 = 0xC0E, // Performance-monitoring counter.
HPMCounter15 = 0xC0F, // Performance-monitoring counter.
HPMCounter16 = 0xC10, // Performance-monitoring counter.
HPMCounter17 = 0xC11, // Performance-monitoring counter.
HPMCounter18 = 0xC12, // Performance-monitoring counter.
HPMCounter19 = 0xC13, // Performance-monitoring counter.
HPMCounter20 = 0xC14, // Performance-monitoring counter.
HPMCounter21 = 0xC15, // Performance-monitoring counter.
HPMCounter22 = 0xC16, // Performance-monitoring counter.
HPMCounter23 = 0xC17, // Performance-monitoring counter.
HPMCounter24 = 0xC18, // Performance-monitoring counter.
HPMCounter25 = 0xC19, // Performance-monitoring counter.
HPMCounter26 = 0xC1A, // Performance-monitoring counter.
HPMCounter27 = 0xC1B, // Performance-monitoring counter.
HPMCounter28 = 0xC1C, // Performance-monitoring counter.
HPMCounter29 = 0xC1D, // Performance-monitoring counter.
HPMCounter30 = 0xC1E, // Performance-monitoring counter.
HPMCounter31 = 0xC1F, // Performance-monitoring counter.
CycleH = 0xC80, // Upper 32 bits of cycle, RV32I only.
TimeH = 0xC81, // Upper 32 bits of time, RV32I only.
InstRetH = 0xC82, // Upper 32 bits of instret, RV32I only.
HPMCounter3H = 0xC83, // Upper 32 bits of HPMCounter3, RV32I only.
HPMCounter4H = 0xC84, // Upper 32 bits of HPMCounter4, RV32I only.
HPMCounter5H = 0xC85, // Upper 32 bits of HPMCounter5, RV32I only.
HPMCounter6H = 0xC86, // Upper 32 bits of HPMCounter6, RV32I only.
HPMCounter7H = 0xC87, // Upper 32 bits of HPMCounter7, RV32I only.
HPMCounter8H = 0xC88, // Upper 32 bits of HPMCounter8, RV32I only.
HPMCounter9H = 0xC89, // Upper 32 bits of HPMCounter9, RV32I only.
HPMCounter10H = 0xC8A, // Upper 32 bits of HPMCounter10, RV32I only.
HPMCounter11H = 0xC8B, // Upper 32 bits of HPMCounter11, RV32I only.
HPMCounter12H = 0xC8C, // Upper 32 bits of HPMCounter12, RV32I only.
HPMCounter13H = 0xC8D, // Upper 32 bits of HPMCounter13, RV32I only.
HPMCounter14H = 0xC8E, // Upper 32 bits of HPMCounter14, RV32I only.
HPMCounter15H = 0xC8F, // Upper 32 bits of HPMCounter15, RV32I only.
HPMCounter16H = 0xC90, // Upper 32 bits of HPMCounter16, RV32I only.
HPMCounter17H = 0xC91, // Upper 32 bits of HPMCounter17, RV32I only.
HPMCounter18H = 0xC92, // Upper 32 bits of HPMCounter18, RV32I only.
HPMCounter19H = 0xC93, // Upper 32 bits of HPMCounter19, RV32I only.
HPMCounter20H = 0xC94, // Upper 32 bits of HPMCounter20, RV32I only.
HPMCounter21H = 0xC95, // Upper 32 bits of HPMCounter21, RV32I only.
HPMCounter22H = 0xC96, // Upper 32 bits of HPMCounter22, RV32I only.
HPMCounter23H = 0xC97, // Upper 32 bits of HPMCounter23, RV32I only.
HPMCounter24H = 0xC98, // Upper 32 bits of HPMCounter24, RV32I only.
HPMCounter25H = 0xC99, // Upper 32 bits of HPMCounter25, RV32I only.
HPMCounter26H = 0xC9A, // Upper 32 bits of HPMCounter26, RV32I only.
HPMCounter27H = 0xC9B, // Upper 32 bits of HPMCounter27, RV32I only.
HPMCounter28H = 0xC9C, // Upper 32 bits of HPMCounter28, RV32I only.
HPMCounter29H = 0xC9D, // Upper 32 bits of HPMCounter29, RV32I only.
HPMCounter30H = 0xC9E, // Upper 32 bits of HPMCounter30, RV32I only.
HPMCounter31H = 0xC9F, // Upper 32 bits of HPMCounter31, RV32I only.
// Supervisor-level CSRs
SStatus = 0x100, // Supervisor status register
SEDeleg = 0x102, // Supervisor exception delegation register
SIDeleg = 0x103, // Supervisor interrupt delegation register
SIE = 0x104, // Supervisor interrupt-enable register
STVec = 0x105, // Supervisor trap handler base address
SCounterEn = 0x106, // Supervisor counter enable
SEnvCfg = 0x10A, // Supervisor environment configuration register
SScratch = 0x140, // Scratch register for supervisor trap handlers
SEPC = 0x141, // Supervisor exception program counter
SCause = 0x142, // Supervisor trap cause
STVal = 0x143, // Supervisor bad address or instruction
SIP = 0x144, // Supervisor interrupt pending.
SCTRCTL = 0x14E, // Supervisor control transfer records control register
SCTRStatus = 0x14F, // Supervisor control transfer records status register
SCTRDepth = 0x15F, // Supervisor control transfer records depth register
SISelect = 0x150, // Supervisor indirect register select
SIReg = 0x151, // Supervisor indirect register alias
StopEI = 0x15C, // Supervisor top external interrupt (only with an IMSIC)
StopI = 0xDB0, // Supervisor top interrupt
SIEH = 0x114, // Upper 32 bits of sie
SIPH = 0x154, // Upper 32 bits of sip
STimeCmp = 0x14D, // Supervisor timer register
STimeCmpH = 0x15D, // Supervisor timer register, RV32 only
SATP = 0x180, // Supervisor address translation and protection
SContext = 0x5A8, // Supervisor-mode context register
// Hypervisor-level CSRs
HStatus = 0x600, // Hypervisor status register
HEDeleg = 0x602, // Hypervisor exception delegation register
HIDeleg = 0x603, // Hypervisor interrupt delegation register
HIE = 0x604, // Hypervisor interrupt-enable register
HCounterEn = 0x606, // Hypervisor counter enable
HGEIE = 0x607, // Hypervisor guest external interrupt-enable register
HVIEN = 0x608, // Hypervisor virtual interrupt enables
HVICTL = 0x609, // Hypervisor virtual interrupt control
HIDelegH = 0x613, // Upper 32 bits of hideleg
HVIENH = 0x618, // Upper 32 bits of hvien
HVIPH = 0x655, // Upper 32 bits of hvip
HVIPrio1H = 0x656, // Upper 32 bits of hviprio1
HVIPrio2H = 0x657, // Upper 32 bits of hviprio2
VSIEH = 0x214, // Upper 32 bits of vsie
VSIPH = 0x254, // Upper 32 bits of vsiph
HTVal = 0x643, // Hypervisor bad guest physical address
HIP = 0x644, // Hypervisor interrupt pending
HVIP = 0x645, // Hypervisor virtual interrupt pending
HVIPrio1 = 0x646, // Hypervisor VS-level interrupt priorities
HVIPrio2 = 0x647, // Hypervisor VS-level interrupt priorities
HTInst = 0x64A, // Hypervisor trap instruction (transformed)
HGEIP = 0xE12, // Hypervisor guest external interrupt pending
HEnvCfg = 0x60A, // Hypervisor environment configuration register
HEnvCfgH = 0x61A, // Additional hypervisor environment configuration register, RV32 only
HGATP = 0x680, // Hypervisor guest address translation and protection
HContext = 0x6A8, // Hypervisor-mode context register
HTimeDelta = 0x605, // Delta for VS/VU-mode timer
HTimeDeltaH = 0x615, // Upper 32 bits of HTimeDelta, HSXLEN=32 only
VSStatus = 0x200, // Virtual supervisor status register
VSIE = 0x204, // Virtual supervisor interrupt-enable register
VSTVec = 0x205, // Virtual supervisor trap handler base address
VSScratch = 0x240, // Virtual supervisor scratch register
VSEPC = 0x241, // Virtual supervisor exception program register
VSCause = 0x242, // Virtual supervisor trap cause
VSTVal = 0x243, // Virtual supervisor bad address or instruction
VSIP = 0x244, // Virtual supervisor interrupt pending
VSCTRCTL = 0x24E, // Virtual supervisor control transfer records control register
VSISelect = 0x250, // Virtual supervisor indirect register select
VSIReg = 0x251, // Virtual supervisor indirect register alias
VStopEI = 0x25C, // Virtual supervisor top external interrupt (only with an IMSIC)
VStopI = 0xEB0, // Virtual supervisor top interrupt
VSTimeCmp = 0x24D, // Virtual supervisor timer register
VSTimeCmpH = 0x25D, // Virtual supervisor timer register, RV32 only
VSATP = 0x280, // Virtual supervisor address translation and protection
// Machine-level CSRs
MVendorID = 0xF11, // Vendor ID
MArchID = 0xF12, // Architecture ID
MImpID = 0xF13, // Implementation ID
MHartID = 0xF14, // Hardware Thread ID
MConfigPtr = 0xF15, // Pointer to configuration data structure
MStatus = 0x300, // Machine status register
MISA = 0x301, // ISA and extensions
MEDeleg = 0x302, // Machine exception delegation register
MIDeleg = 0x303, // Machine interrupt delegation register
MIE = 0x304, // Machine interrupt-enable register
MRVec = 0x305, // Machine trap-handler base address
MCounterEn = 0x306, // Machine counter enable
MVIEN = 0x308, // Machine virtual interrupt enables
MVIP = 0x309, // Machine virtual interrupt-pending bits
MStatusH = 0x310, // Additional machine status register, RV32 only
MIDelegH = 0x313, // Upper 32 bits of of mideleg (only with S-mode)
MIEH = 0x314, // Upper 32 bits of mie
MVIENH = 0x318, // Upper 32 bits of mvien (only with S-mode)
MVIPH = 0x319, // Upper 32 bits of mvip (only with S-mode)
MIPH = 0x354, // Upper 32 bits of mip
MScratch = 0x340, // Scratch register for machine trap handlers
MEPC = 0x341, // Machine exception program counter
MCause = 0x342, // Machine trap cause
MTVal = 0x343, // Machine bad address or instruction
MIP = 0x344, // Machine interrupt pending
MTInst = 0x34A, // Machine trap instruction (transformed)
MTVal2 = 0x34B, // Machine bad guest physical address
MCTRCTL = 0x34E, // Machine control transfer records control register
MISelect = 0x350, // Machine indirect register select
MIReg = 0x351, // Machine indirect register alias
MTopEI = 0x35C, // Machine top external interrupt (only with an IMSIC)
MTopI = 0xFB0, // Machine top interrupt
MEnvCfg = 0x30A, // Machine environment configuration register
MEnvCfgH = 0x31A, // Additional machine environment configuration register, RV32 only
MSecCfg = 0x747, // Machine security configuration register
MSecCfgH = 0x757, // Additional machine security configuration register, RV32 only
PMPCfg0 = 0x3A0, // Physical memory protection configuration
PMPCfg1 = 0x3A1, // Physical memory protection configuration, RV32 only
PMPCfg2 = 0x3A2, // Physical memory protection configuration
PMPCfg3 = 0x3A3, // Physical memory protection configuration, RV32 only
PMPCfg4 = 0x3A4, // Physical memory protection configuration
PMPCfg5 = 0x3A5, // Physical memory protection configuration, RV32 only
PMPCfg6 = 0x3A6, // Physical memory protection configuration
PMPCfg7 = 0x3A7, // Physical memory protection configuration, RV32 only
PMPCfg8 = 0x3A8, // Physical memory protection configuration
PMPCfg9 = 0x3A9, // Physical memory protection configuration, RV32 only
PMPCfg10 = 0x3AA, // Physical memory protection configuration
PMPCfg11 = 0x3AB, // Physical memory protection configuration, RV32 only
PMPCfg12 = 0x3AC, // Physical memory protection configuration
PMPCfg13 = 0x3AD, // Physical memory protection configuration, RV32 only
PMPCfg14 = 0x3AE, // Physical memory protection configuration
PMPCfg15 = 0x3AF, // Physical memory protection configuration, RV32 only
PMPAddr0 = 0x3B0, // Physical memory protection address register
PMPAddr1 = 0x3B1, // Physical memory protection address register
PMPAddr2 = 0x3B2, // Physical memory protection address register
PMPAddr3 = 0x3B3, // Physical memory protection address register
PMPAddr4 = 0x3B4, // Physical memory protection address register
PMPAddr5 = 0x3B5, // Physical memory protection address register
PMPAddr6 = 0x3B6, // Physical memory protection address register
PMPAddr7 = 0x3B7, // Physical memory protection address register
PMPAddr8 = 0x3B8, // Physical memory protection address register
PMPAddr9 = 0x3B9, // Physical memory protection address register
PMPAddr10 = 0x3BA, // Physical memory protection address register
PMPAddr11 = 0x3BB, // Physical memory protection address register
PMPAddr12 = 0x3BC, // Physical memory protection address register
PMPAddr13 = 0x3BD, // Physical memory protection address register
PMPAddr14 = 0x3BE, // Physical memory protection address register
PMPAddr15 = 0x3BF, // Physical memory protection address register
PMPAddr16 = 0x3C0, // Physical memory protection address register
PMPAddr17 = 0x3C1, // Physical memory protection address register
PMPAddr18 = 0x3C2, // Physical memory protection address register
PMPAddr19 = 0x3C3, // Physical memory protection address register
PMPAddr20 = 0x3C4, // Physical memory protection address register
PMPAddr21 = 0x3C5, // Physical memory protection address register
PMPAddr22 = 0x3C6, // Physical memory protection address register
PMPAddr23 = 0x3C7, // Physical memory protection address register
PMPAddr24 = 0x3C8, // Physical memory protection address register
PMPAddr25 = 0x3C9, // Physical memory protection address register
PMPAddr26 = 0x3CA, // Physical memory protection address register
PMPAddr27 = 0x3CB, // Physical memory protection address register
PMPAddr28 = 0x3CC, // Physical memory protection address register
PMPAddr29 = 0x3CD, // Physical memory protection address register
PMPAddr30 = 0x3CE, // Physical memory protection address register
PMPAddr31 = 0x3CF, // Physical memory protection address register
PMPAddr32 = 0x3D0, // Physical memory protection address register
PMPAddr33 = 0x3D1, // Physical memory protection address register
PMPAddr34 = 0x3D2, // Physical memory protection address register
PMPAddr35 = 0x3D3, // Physical memory protection address register
PMPAddr36 = 0x3D4, // Physical memory protection address register
PMPAddr37 = 0x3D5, // Physical memory protection address register
PMPAddr38 = 0x3D6, // Physical memory protection address register
PMPAddr39 = 0x3D7, // Physical memory protection address register
PMPAddr40 = 0x3D8, // Physical memory protection address register
PMPAddr41 = 0x3D9, // Physical memory protection address register
PMPAddr42 = 0x3DA, // Physical memory protection address register
PMPAddr43 = 0x3DB, // Physical memory protection address register
PMPAddr44 = 0x3DC, // Physical memory protection address register
PMPAddr45 = 0x3DD, // Physical memory protection address register
PMPAddr46 = 0x3DE, // Physical memory protection address register
PMPAddr47 = 0x3DF, // Physical memory protection address register
PMPAddr48 = 0x3E0, // Physical memory protection address register
PMPAddr49 = 0x3E1, // Physical memory protection address register
PMPAddr50 = 0x3E2, // Physical memory protection address register
PMPAddr51 = 0x3E3, // Physical memory protection address register
PMPAddr52 = 0x3E4, // Physical memory protection address register
PMPAddr53 = 0x3E5, // Physical memory protection address register
PMPAddr54 = 0x3E6, // Physical memory protection address register
PMPAddr55 = 0x3E7, // Physical memory protection address register
PMPAddr56 = 0x3E8, // Physical memory protection address register
PMPAddr57 = 0x3E9, // Physical memory protection address register
PMPAddr58 = 0x3EA, // Physical memory protection address register
PMPAddr59 = 0x3EB, // Physical memory protection address register
PMPAddr60 = 0x3EC, // Physical memory protection address register
PMPAddr61 = 0x3ED, // Physical memory protection address register
PMPAddr62 = 0x3EE, // Physical memory protection address register
PMPAddr63 = 0x3EF, // Physical memory protection address register
MNScratch = 0x740, // Resumable NMI scratch register
MNEPC = 0x741, // Resumable NMI program counter
MNCause = 0x742, // Resumable NMI cause
MNStatus = 0x744, // Resumable NMI status
MCycle = 0xB00, // Machine cycle counter
MInstRet = 0xB02, // Machine instructions-retired counter
MHPMCounter3 = 0xB03, // Machine performance-monitoring counter
MHPMCounter4 = 0xB04, // Machine performance-monitoring counter
MHPMCounter5 = 0xB05, // Machine performance-monitoring counter
MHPMCounter6 = 0xB06, // Machine performance-monitoring counter
MHPMCounter7 = 0xB07, // Machine performance-monitoring counter
MHPMCounter8 = 0xB08, // Machine performance-monitoring counter
MHPMCounter9 = 0xB09, // Machine performance-monitoring counter
MHPMCounter10 = 0xB0A, // Machine performance-monitoring counter
MHPMCounter11 = 0xB0B, // Machine performance-monitoring counter
MHPMCounter12 = 0xB0C, // Machine performance-monitoring counter
MHPMCounter13 = 0xB0D, // Machine performance-monitoring counter
MHPMCounter14 = 0xB0E, // Machine performance-monitoring counter
MHPMCounter15 = 0xB0F, // Machine performance-monitoring counter
MHPMCounter16 = 0xB10, // Machine performance-monitoring counter
MHPMCounter17 = 0xB11, // Machine performance-monitoring counter
MHPMCounter18 = 0xB12, // Machine performance-monitoring counter
MHPMCounter19 = 0xB13, // Machine performance-monitoring counter
MHPMCounter20 = 0xB14, // Machine performance-monitoring counter
MHPMCounter21 = 0xB15, // Machine performance-monitoring counter
MHPMCounter22 = 0xB16, // Machine performance-monitoring counter
MHPMCounter23 = 0xB17, // Machine performance-monitoring counter
MHPMCounter24 = 0xB18, // Machine performance-monitoring counter
MHPMCounter25 = 0xB19, // Machine performance-monitoring counter
MHPMCounter26 = 0xB1A, // Machine performance-monitoring counter
MHPMCounter27 = 0xB1B, // Machine performance-monitoring counter
MHPMCounter28 = 0xB1C, // Machine performance-monitoring counter
MHPMCounter29 = 0xB1D, // Machine performance-monitoring counter
MHPMCounter30 = 0xB1E, // Machine performance-monitoring counter
MHPMCounter31 = 0xB1F, // Machine performance-monitoring counter
MCycleH = 0xB80, // Upper 32 bits ofmcycle, RV32I only
MInstRetH = 0xB82, // Upper 32 bits ofminstret, RV32I only
MHPMCounter3H = 0xB83, // Upper 32 bits of MHPMCounter3, RV32I only
MHPMCounter4H = 0xB84, // Upper 32 bits of MHPMCounter4, RV32I only
MHPMCounter5H = 0xB85, // Upper 32 bits of MHPMCounter5, RV32I only
MHPMCounter6H = 0xB86, // Upper 32 bits of MHPMCounter6, RV32I only
MHPMCounter7H = 0xB87, // Upper 32 bits of MHPMCounter7, RV32I only
MHPMCounter8H = 0xB88, // Upper 32 bits of MHPMCounter8, RV32I only
MHPMCounter9H = 0xB89, // Upper 32 bits of MHPMCounter9, RV32I only
MHPMCounter10H = 0xB8A, // Upper 32 bits of MHPMCounter10, RV32I only
MHPMCounter11H = 0xB8B, // Upper 32 bits of MHPMCounter11, RV32I only
MHPMCounter12H = 0xB8C, // Upper 32 bits of MHPMCounter12, RV32I only
MHPMCounter13H = 0xB8D, // Upper 32 bits of MHPMCounter13, RV32I only
MHPMCounter14H = 0xB8E, // Upper 32 bits of MHPMCounter14, RV32I only
MHPMCounter15H = 0xB8F, // Upper 32 bits of MHPMCounter15, RV32I only
MHPMCounter16H = 0xB90, // Upper 32 bits of MHPMCounter16, RV32I only
MHPMCounter17H = 0xB91, // Upper 32 bits of MHPMCounter17, RV32I only
MHPMCounter18H = 0xB92, // Upper 32 bits of MHPMCounter18, RV32I only
MHPMCounter19H = 0xB93, // Upper 32 bits of MHPMCounter19, RV32I only
MHPMCounter20H = 0xB94, // Upper 32 bits of MHPMCounter20, RV32I only
MHPMCounter21H = 0xB95, // Upper 32 bits of MHPMCounter21, RV32I only
MHPMCounter22H = 0xB96, // Upper 32 bits of MHPMCounter22, RV32I only
MHPMCounter23H = 0xB97, // Upper 32 bits of MHPMCounter23, RV32I only
MHPMCounter24H = 0xB98, // Upper 32 bits of MHPMCounter24, RV32I only
MHPMCounter25H = 0xB99, // Upper 32 bits of MHPMCounter25, RV32I only
MHPMCounter26H = 0xB9A, // Upper 32 bits of MHPMCounter26, RV32I only
MHPMCounter27H = 0xB9B, // Upper 32 bits of MHPMCounter27, RV32I only
MHPMCounter28H = 0xB9C, // Upper 32 bits of MHPMCounter28, RV32I only
MHPMCounter29H = 0xB9D, // Upper 32 bits of MHPMCounter29, RV32I only
MHPMCounter30H = 0xB9E, // Upper 32 bits of MHPMCounter30, RV32I only
MHPMCounter31H = 0xB9F, // Upper 32 bits of MHPMCounter31, RV32I only
MCountInhibit = 0x320, // Machine counter-inhibit register
MCycleCfg = 0x321, // Privilege mode filtering for cycle counter
MCycleCfgH = 0x721, // Privilege mode filtering for cycle counter (RV32)
MInstRetCfg = 0x322, // Privilege mode filtering for instret counters
MInstRetCfgH = 0x722, // Privilege mode filtering for instret counters (RV32)
MHPMEvent3 = 0x323, // Machine performance-monitoring event selector
MHPMEvent4 = 0x324, // Machine performance-monitoring event selector
MHPMEvent5 = 0x325, // Machine performance-monitoring event selector
MHPMEvent6 = 0x326, // Machine performance-monitoring event selector
MHPMEvent7 = 0x327, // Machine performance-monitoring event selector
MHPMEvent8 = 0x328, // Machine performance-monitoring event selector
MHPMEvent9 = 0x329, // Machine performance-monitoring event selector
MHPMEvent10 = 0x32A, // Machine performance-monitoring event selector
MHPMEvent11 = 0x32B, // Machine performance-monitoring event selector
MHPMEvent12 = 0x32C, // Machine performance-monitoring event selector
MHPMEvent13 = 0x32D, // Machine performance-monitoring event selector
MHPMEvent14 = 0x32E, // Machine performance-monitoring event selector
MHPMEvent15 = 0x32F, // Machine performance-monitoring event selector
MHPMEvent16 = 0x330, // Machine performance-monitoring event selector
MHPMEvent17 = 0x331, // Machine performance-monitoring event selector
MHPMEvent18 = 0x332, // Machine performance-monitoring event selector
MHPMEvent19 = 0x333, // Machine performance-monitoring event selector
MHPMEvent20 = 0x334, // Machine performance-monitoring event selector
MHPMEvent21 = 0x335, // Machine performance-monitoring event selector
MHPMEvent22 = 0x336, // Machine performance-monitoring event selector
MHPMEvent23 = 0x337, // Machine performance-monitoring event selector
MHPMEvent24 = 0x338, // Machine performance-monitoring event selector
MHPMEvent25 = 0x339, // Machine performance-monitoring event selector
MHPMEvent26 = 0x33A, // Machine performance-monitoring event selector
MHPMEvent27 = 0x33B, // Machine performance-monitoring event selector
MHPMEvent28 = 0x33C, // Machine performance-monitoring event selector
MHPMEvent29 = 0x33D, // Machine performance-monitoring event selector
MHPMEvent30 = 0x33E, // Machine performance-monitoring event selector
MHPMEvent31 = 0x33F, // Machine performance-monitoring event selector
TSelect = 0x7A0, // Debug/Trace trigger register select
TData1 = 0x7A1, // First Debug/Trace trigger data register
TData2 = 0x7A2, // Second Debug/Trace trigger data register
TData3 = 0x7A3, // Third Debug/Trace trigger data register
TInfo = 0x7A4, // Trigger info
TControl = 0x7A5, // Trigger control
MContext = 0x7A8, // Machine-mode context register
MSContext = 0x7AA, // Machine supervisor context
// TData Aliases
MControl = 0x7A1, // Match control
MControl6 = 0x7A1, // Match control type 6
ICount = 0x7A1, // Instruction count
ITrigger = 0x7A1, // Interrupt trigger
ETrigger = 0x7A1, // Exception trigger
TMEXTrigger = 0x7A1, // External trigger
TExtra32 = 0x7A3, // Trigger extra (RV32)
TExtra64 = 0x7A3, // Trigger extra (RV64)
DCSR = 0x7B0, // Debug control and status register
DPC = 0x7B1, // Debug PC
DScratch0 = 0x7B2, // Debug scratch register 0
DScratch1 = 0x7B3, // Debug scratch register 1
// Scalar Cryptography Entropy Source Extension CSRs
Seed = 0x015, // Entropy bit provider (up to 16 bits)
// Vector Extension CSRs
VStart = 0x008, // Vector start position
VXSat = 0x009, // Fixed-Point Saturate Flag
VXRM = 0x00A, // Fixed-Point Rounding Mode
VCSR = 0x00F, // Vector control and status register
VL = 0xC20, // Vector length
VType = 0xC21, // Vector data type register
VLenb = 0xC22, // Vector register length in bytes
// clang-format on
};
} // namespace biscuit

View file

@ -0,0 +1,49 @@
#pragma once
#include <cstdint>
// Source file for general values and data structures
// that don't fit a particular criteria related to the ISA.
namespace biscuit {
enum class FenceOrder : uint32_t {
W = 1, // Write
R = 2, // Read
O = 4, // Device Output
I = 8, // Device Input
RW = R | W,
IO = I | O,
IR = I | R,
IW = I | W,
IRW = I | R | W,
OI = O | I,
OR = O | R,
OW = O | W,
ORW = O | R | W,
IORW = I | O | R | W,
};
// Atomic ordering
enum class Ordering : uint32_t {
None = 0, // None
RL = 1, // Release
AQ = 2, // Acquire
AQRL = AQ | RL, // Acquire-Release
};
// Floating-point Rounding Mode
enum class RMode : uint32_t {
RNE = 0b000, // Round to Nearest, ties to Even
RTZ = 0b001, // Round towards Zero
RDN = 0b010, // Round Down (towards negative infinity)
RUP = 0b011, // Round Up (towards positive infinity)
RMM = 0b100, // Round to Nearest, ties to Max Magnitude
DYN = 0b111, // Dynamic Rounding Mode
};
} // namespace biscuit

View file

@ -0,0 +1,173 @@
#pragma once
#include <cstddef>
#include <optional>
#include <set>
#include <biscuit/assert.hpp>
namespace biscuit {
/**
* A label is a representation of an address that can be used with branch and jump instructions.
*
* Labels do not need to be bound to a location immediately. A label can be created
* to provide branches with a tentative, undecided location that is then bound
* at a later point in time.
*
* @note Any label that is created, is used with a branch instruction,
* but is *not* bound to a location (via Bind() in the assembler)
* will result in an assertion being invoked when the label instance's
* destructor is executed.
*
* @note A label may only be bound to one location. Any attempt to rebind
* a label that is already bound will result in an assertion being
* invoked.
*
* @par
* An example of binding a label:
*
* @code{.cpp}
* Assembler as{...};
* Label label;
*
* as.BNE(x2, x3, &label); // Use the label
* as.ADD(x7, x8, x9);
* as.XOR(x7, x10, x12);
* as.Bind(&label); // Bind the label to a location
* @endcode
*/
class Label {
public:
using Location = std::optional<ptrdiff_t>;
using LocationOffset = Location::value_type;
/**
* Default constructor.
*
* This constructor results in a label being constructed that is not
* bound to a particular location yet.
*/
explicit Label() = default;
/// Destructor
~Label() noexcept {
// It's a logic bug if something references a label and hasn't been handled.
//
// This is usually indicative of a scenario where a label is referenced but
// hasn't been bound to a location.
//
BISCUIT_ASSERT(IsResolved());
}
// We disable copying of labels, as this doesn't really make sense to do.
// It also presents a problem. When labels are being resolved, if we have
// two labels pointing to the same place, resolving the links to this address
// are going to clobber each other N times for however many copies of the label
// exist.
//
// This isn't a particularly major problem, since the resolving will still result
// in the same end result, but it does make it annoying to think about label interactions
// moving forward. Thus, I choose to simply not think about it at all!
//
Label(const Label&) = delete;
Label& operator=(const Label&) = delete;
// Moving labels on the other hand is totally fine, this is just pushing data around
// to another label while invalidating the label having it's data "stolen".
Label(Label&&) noexcept = default;
Label& operator=(Label&&) noexcept = default;
/**
* Determines whether or not this label instance has a location assigned to it.
*
* A label is considered bound if it has an assigned location.
*/
[[nodiscard]] bool IsBound() const noexcept {
return m_location.has_value();
}
/**
* Determines whether or not this label is resolved.
*
* A label is considered resolved when all referencing offsets have been handled.
*/
[[nodiscard]] bool IsResolved() const noexcept {
return m_offsets.empty();
}
/**
* Determines whether or not this label is unresolved.
*
* A label is considered unresolved if it still has any unhandled referencing offsets.
*/
[[nodiscard]] bool IsUnresolved() const noexcept {
return !IsResolved();
}
/**
* Retrieves the location for this label.
*
* @note If the returned location is empty, then this label has not been assigned
* a location yet.
*/
[[nodiscard]] Location GetLocation() const noexcept {
return m_location;
}
private:
// A label instance is inherently bound to the assembler it's
// used with, as the offsets within the label set depend on
// said assemblers code buffer.
friend class Assembler;
/**
* Binds a label to the given location.
*
* @param offset The instruction offset to bind this label to.
*
* @pre The label must not have already been bound to a previous location.
* Attempting to rebind a label is typically, in almost all scenarios,
* the source of bugs.
* Attempting to rebind an already bound label will result in an assertion
* being triggered.
*/
void Bind(LocationOffset offset) noexcept {
BISCUIT_ASSERT(!IsBound());
m_location = offset;
}
/**
* Marks the given address as dependent on this label.
*
* This is used in scenarios where a label exists, but has not yet been
* bound to a location yet. It's important to track these addresses,
* as we'll need to patch the dependent branch instructions with the
* proper offset once the label is finally bound by the assembler.
*
* During label binding, the offset will be calculated and inserted
* into dependent instructions.
*/
void AddOffset(LocationOffset offset) {
// If a label is already bound to a location, then offset tracking
// isn't necessary. Tripping this assert means we have a bug somewhere.
BISCUIT_ASSERT(!IsBound());
BISCUIT_ASSERT(IsNewOffset(offset));
m_offsets.insert(offset);
}
// Clears all the underlying offsets for this label.
void ClearOffsets() noexcept {
m_offsets.clear();
}
// Determines whether or not this address has already been added before.
[[nodiscard]] bool IsNewOffset(LocationOffset offset) const noexcept {
return m_offsets.find(offset) == m_offsets.cend();
}
std::set<LocationOffset> m_offsets;
Location m_location;
};
} // namespace biscuit

View file

@ -0,0 +1,176 @@
#pragma once
#include <biscuit/assert.hpp>
#include <cstddef>
#include <optional>
#include <set>
#include <type_traits>
namespace biscuit {
/**
* A Literal is a representation of a constant value that can be loaded into a register.
* This is useful for avoiding multiple instructions for loading big constants.
*
* Literals, like Labels, don't need to be placed immediately. They can be created
* and used with loads that require a Literal, and placed in the buffer at a later point.
*
* @note Any literal that is created, is used with a load instruction,
* but is *not* placed to a location (via Place() in the assembler)
* will result in an assertion being invoked when the literal instance's
* destructor is executed.
*
* @note A literal may only be placed to one location. Any attempt to place
* a literal that is already placed will result in an assertion being
* invoked.
*
* @par
* An example of placing a literal:
* @code{.cpp}
* Assembler as{...};
* Literal literal(0x1234567890ABCDEF);
*
* as.LD(x2, &literal); // Load the literal (emits a AUIPC+LD sequence)
* as.JR(x2); // Execution continues elsewhere
* as.Place(&literal); // Place the literal at this location in the buffer
* @endcode
*/
template<class T>
class Literal {
public:
using Location = std::optional<ptrdiff_t>;
using LocationOffset = Location::value_type;
/**
* This constructor results in a literal being constructed that is not
* placed at a particular location yet.
*
* @param value The value that this literal represents.
*/
explicit Literal(T value) : m_value{value} {}
/// Destructor
~Literal() noexcept {
// It's a logic bug if something references a literal and hasn't been handled.
//
// This is usually indicative of a scenario where a literal is referenced but
// hasn't been placed at a location.
//
BISCUIT_ASSERT(IsResolved());
}
// Copying disabled for the same reasons as Labels.
Literal(const Literal&) = delete;
Literal& operator=(const Literal&) = delete;
Literal(Literal&&) noexcept = default;
Literal& operator=(Literal&&) noexcept = default;
/**
* Determines whether or not this literal instance has a location assigned to it.
*
* A literal is considered placed if it has an assigned location.
*/
[[nodiscard]] bool IsPlaced() const noexcept {
return m_location.has_value();
}
/**
* Determines whether or not this literal is resolved.
*
* A literal is considered resolved when all referencing offsets have been handled.
*/
[[nodiscard]] bool IsResolved() const noexcept {
return m_offsets.empty();
}
/**
* Determines whether or not this literal is unresolved.
*
* A literal is considered unresolved if it still has any unhandled referencing offsets.
*/
[[nodiscard]] bool IsUnresolved() const noexcept {
return !IsResolved();
}
/**
* Retrieves the location for this literal.
*
* @note If the returned location is empty, then this literal has not been assigned
* a location yet.
*/
[[nodiscard]] Location GetLocation() const noexcept {
return m_location;
}
private:
// A literal instance is inherently bound to the assembler it's
// used with, as the offsets within the literal set depend on
// said assemblers code buffer.
friend class Assembler;
/**
* Places a literal to the given location.
*
* @param offset The offset to place this literal at.
*
* @returns The literal value so it can be copied to memory by the assembler.
*
* @pre The literal must not have already been placed at a previous location.
* Attempting to place a literal multiple times is typically, in almost all scenarios,
* the source of bugs.
* Attempting to place an already placed literal will result in an assertion
* being triggered.
*/
[[nodiscard]] const T& Place(LocationOffset offset) noexcept {
BISCUIT_ASSERT(!IsPlaced());
m_location = offset;
return m_value;
}
/**
* Marks the given address as dependent on this literal.
*
* This is used in scenarios where a literal exists, but has not yet been
* placed at a location yet. It's important to track these addresses,
* as we'll need to patch the dependent load instructions with the
* proper offset once the literal is finally placed by the assembler.
*
* During literal placement, the offset will be calculated and inserted
* into dependent instructions.
*/
void AddOffset(LocationOffset offset) {
// If a literal is already placed at a location, then offset tracking
// isn't necessary. Tripping this assert means we have a bug somewhere.
BISCUIT_ASSERT(!IsPlaced());
BISCUIT_ASSERT(IsNewOffset(offset));
m_offsets.insert(offset);
}
// Clears all the underlying offsets for this literal.
void ClearOffsets() noexcept {
m_offsets.clear();
}
// Determines whether or not this address has already been added before.
[[nodiscard]] bool IsNewOffset(LocationOffset offset) const noexcept {
return m_offsets.find(offset) == m_offsets.cend();
}
std::set<LocationOffset> m_offsets;
Location m_location;
const T m_value;
// Literals are provided as a way to avoid long instruction sequences for loading
// immediates to registers. As such, the Literal type is not useful for
// types <= uint32_t, as those can be loaded directly with at most a couple instructions.
// (e.g. via the LI() function in the assembler)
static_assert(sizeof(T) >= 8, "Literal type must be at least 64 bits wide.");
// For the assembler to be able to emit the literal value, it must be trivially copyable.
static_assert(std::is_trivially_copyable_v<T>, "Literal type must be trivially copyable.");
};
} // namespace biscuit

View file

@ -0,0 +1,315 @@
#pragma once
#include <biscuit/assert.hpp>
#include <compare>
#include <cstdint>
namespace biscuit {
/**
* Generic abstraction around a register.
*
* This is less bug-prone than using raw primitive sizes
* in opcode emitter functions, since it provides stronger typing.
*/
class Register {
public:
constexpr Register() noexcept = default;
/// Gets the index for this register.
[[nodiscard]] constexpr uint32_t Index() const noexcept {
return m_index;
}
friend constexpr bool operator==(Register, Register) = default;
friend constexpr auto operator<=>(Register, Register) = default;
protected:
constexpr explicit Register(uint32_t index) noexcept
: m_index{index} {}
private:
uint32_t m_index{};
};
/// General purpose register.
class GPR final : public Register {
public:
constexpr GPR() noexcept : Register{0} {}
constexpr explicit GPR(uint32_t index) noexcept : Register{index} {}
friend constexpr bool operator==(GPR, GPR) = default;
friend constexpr auto operator<=>(GPR, GPR) = default;
};
/// Floating point register.
class FPR final : public Register {
public:
constexpr FPR() noexcept : Register{0} {}
constexpr explicit FPR(uint32_t index) noexcept : Register{index} {}
friend constexpr bool operator==(FPR, FPR) = default;
friend constexpr auto operator<=>(FPR, FPR) = default;
};
/// Vector register.
class Vec final : public Register {
public:
constexpr Vec() noexcept : Register{0} {}
constexpr explicit Vec(uint32_t index) noexcept : Register{index} {}
friend constexpr bool operator==(Vec, Vec) = default;
friend constexpr auto operator<=>(Vec, Vec) = default;
};
// General-purpose Registers
constexpr GPR x0{0};
constexpr GPR x1{1};
constexpr GPR x2{2};
constexpr GPR x3{3};
constexpr GPR x4{4};
constexpr GPR x5{5};
constexpr GPR x6{6};
constexpr GPR x7{7};
constexpr GPR x8{8};
constexpr GPR x9{9};
constexpr GPR x10{10};
constexpr GPR x11{11};
constexpr GPR x12{12};
constexpr GPR x13{13};
constexpr GPR x14{14};
constexpr GPR x15{15};
constexpr GPR x16{16};
constexpr GPR x17{17};
constexpr GPR x18{18};
constexpr GPR x19{19};
constexpr GPR x20{20};
constexpr GPR x21{21};
constexpr GPR x22{22};
constexpr GPR x23{23};
constexpr GPR x24{24};
constexpr GPR x25{25};
constexpr GPR x26{26};
constexpr GPR x27{27};
constexpr GPR x28{28};
constexpr GPR x29{29};
constexpr GPR x30{30};
constexpr GPR x31{31};
// Symbolic General-purpose Register Names
constexpr GPR zero{x0};
constexpr GPR ra{x1};
constexpr GPR sp{x2};
constexpr GPR gp{x3};
constexpr GPR tp{x4};
constexpr GPR fp{x8};
constexpr GPR a0{x10};
constexpr GPR a1{x11};
constexpr GPR a2{x12};
constexpr GPR a3{x13};
constexpr GPR a4{x14};
constexpr GPR a5{x15};
constexpr GPR a6{x16};
constexpr GPR a7{x17};
constexpr GPR s0{x8};
constexpr GPR s1{x9};
constexpr GPR s2{x18};
constexpr GPR s3{x19};
constexpr GPR s4{x20};
constexpr GPR s5{x21};
constexpr GPR s6{x22};
constexpr GPR s7{x23};
constexpr GPR s8{x24};
constexpr GPR s9{x25};
constexpr GPR s10{x26};
constexpr GPR s11{x27};
constexpr GPR t0{x5};
constexpr GPR t1{x6};
constexpr GPR t2{x7};
constexpr GPR t3{x28};
constexpr GPR t4{x29};
constexpr GPR t5{x30};
constexpr GPR t6{x31};
// Floating-point registers
constexpr FPR f0{0};
constexpr FPR f1{1};
constexpr FPR f2{2};
constexpr FPR f3{3};
constexpr FPR f4{4};
constexpr FPR f5{5};
constexpr FPR f6{6};
constexpr FPR f7{7};
constexpr FPR f8{8};
constexpr FPR f9{9};
constexpr FPR f10{10};
constexpr FPR f11{11};
constexpr FPR f12{12};
constexpr FPR f13{13};
constexpr FPR f14{14};
constexpr FPR f15{15};
constexpr FPR f16{16};
constexpr FPR f17{17};
constexpr FPR f18{18};
constexpr FPR f19{19};
constexpr FPR f20{20};
constexpr FPR f21{21};
constexpr FPR f22{22};
constexpr FPR f23{23};
constexpr FPR f24{24};
constexpr FPR f25{25};
constexpr FPR f26{26};
constexpr FPR f27{27};
constexpr FPR f28{28};
constexpr FPR f29{29};
constexpr FPR f30{30};
constexpr FPR f31{31};
// Symbolic Floating-point Register Names
constexpr FPR fa0{f10};
constexpr FPR fa1{f11};
constexpr FPR fa2{f12};
constexpr FPR fa3{f13};
constexpr FPR fa4{f14};
constexpr FPR fa5{f15};
constexpr FPR fa6{f16};
constexpr FPR fa7{f17};
constexpr FPR ft0{f0};
constexpr FPR ft1{f1};
constexpr FPR ft2{f2};
constexpr FPR ft3{f3};
constexpr FPR ft4{f4};
constexpr FPR ft5{f5};
constexpr FPR ft6{f6};
constexpr FPR ft7{f7};
constexpr FPR ft8{f28};
constexpr FPR ft9{f29};
constexpr FPR ft10{f30};
constexpr FPR ft11{f31};
constexpr FPR fs0{f8};
constexpr FPR fs1{f9};
constexpr FPR fs2{f18};
constexpr FPR fs3{f19};
constexpr FPR fs4{f20};
constexpr FPR fs5{f21};
constexpr FPR fs6{f22};
constexpr FPR fs7{f23};
constexpr FPR fs8{f24};
constexpr FPR fs9{f25};
constexpr FPR fs10{f26};
constexpr FPR fs11{f27};
// Vector registers (V extension)
constexpr Vec v0{0};
constexpr Vec v1{1};
constexpr Vec v2{2};
constexpr Vec v3{3};
constexpr Vec v4{4};
constexpr Vec v5{5};
constexpr Vec v6{6};
constexpr Vec v7{7};
constexpr Vec v8{8};
constexpr Vec v9{9};
constexpr Vec v10{10};
constexpr Vec v11{11};
constexpr Vec v12{12};
constexpr Vec v13{13};
constexpr Vec v14{14};
constexpr Vec v15{15};
constexpr Vec v16{16};
constexpr Vec v17{17};
constexpr Vec v18{18};
constexpr Vec v19{19};
constexpr Vec v20{20};
constexpr Vec v21{21};
constexpr Vec v22{22};
constexpr Vec v23{23};
constexpr Vec v24{24};
constexpr Vec v25{25};
constexpr Vec v26{26};
constexpr Vec v27{27};
constexpr Vec v28{28};
constexpr Vec v29{29};
constexpr Vec v30{30};
constexpr Vec v31{31};
// Register utilities
// Used with compressed stack management instructions
// (cm.push, cm.pop, etc) for building up a register list to encode.
//
// Also enforces that only valid registers are used in the lists.
class PushPopList final {
public:
// Represents an inclusive range ([start, end]) of registers.
struct Range final {
// Signifies an empty range. Normally this doesn't need to explicitly
// be created. Default parameters will usually take care of it.
constexpr Range() : start{UINT32_MAX}, end{UINT32_MAX} {}
// This particular constructor is used for the case of rlist=5
// where only ra and s0 get stored.
constexpr Range(GPR start_end) noexcept : start{start_end}, end{start_end} {
BISCUIT_ASSERT(start_end == s0);
}
constexpr Range(GPR start_, GPR end_) noexcept : start{start_}, end{end_} {
BISCUIT_ASSERT(start_ == s0);
BISCUIT_ASSERT(IsSRegister(end_));
// See the Zc spec. The only way for s10 to be used is to also include s11.
BISCUIT_ASSERT(end_ != s10);
}
GPR start;
GPR end;
};
// Deliberately non-explicit to allow for convenient instantiation at usage sites.
// e.g. Rather than CM.POP(PushPopList{ra, {s0, s2}}, 16), we can just have the
// usage be transparent like CM.POP({ra, {s0, s2}}, 16). Nice and compact!
constexpr PushPopList(GPR ra_reg, const Range& range = {}) noexcept
: m_bitmask{BuildBitmask(range)} {
BISCUIT_ASSERT(ra_reg == ra);
}
// Gets the built-up bitmask of passed in registers
[[nodiscard]] constexpr uint32_t GetBitmask() const noexcept {
return m_bitmask;
}
private:
[[nodiscard]] static constexpr uint32_t BuildBitmask(const Range& range) noexcept {
if (range.end.Index() == UINT32_MAX) {
return 4U;
}
if (range.end == s11) {
return 15U;
}
if (range.end == s0 || range.end == s1) {
return range.end.Index() - 3U;
}
return range.end.Index() - 11U;
}
// Aside from ra, it's only valid for s0-s11 to show up the register list ranges.
[[nodiscard]] static constexpr bool IsSRegister(const GPR gpr) noexcept {
return gpr == s0 || gpr == s1 || (gpr >= s2 && gpr <= s11);
}
uint32_t m_bitmask = 0;
};
} // namespace biscuit

View file

@ -0,0 +1,88 @@
#pragma once
#include <cstdint>
// Source file for anything specific to the RISC-V vector extension.
namespace biscuit {
/// Describes whether or not an instruction should make use of the mask vector.
enum class VecMask : uint32_t {
Yes = 0,
No = 1,
};
/// Describes the selected element width.
enum class SEW : uint32_t {
E8 = 0b000, // 8-bit vector elements
E16 = 0b001, // 16-bit vector elements
E32 = 0b010, // 32-bit vector elements
E64 = 0b011, // 64-bit vector elements
E128 = 0b100, // 128-bit vector elements
E256 = 0b101, // 256-bit vector elements
E512 = 0b110, // 512-bit vector elements
E1024 = 0b111, // 1024-bit vector elements
};
/// Describes the selected register group multiplier.
enum class LMUL : uint32_t {
M1 = 0b000, // Group of one vector
M2 = 0b001, // Groups of two vectors
M4 = 0b010, // Groups of four vectors
M8 = 0b011, // Groups of eight vectors
MF8 = 0b101, // Fractional vector group (1/8)
MF4 = 0b110, // Fractional vector group (1/4)
MF2 = 0b111, // Fractional vector group (1/2)
};
/**
* Describes whether or not vector masks are agnostic.
*
* From the RVV spec:
*
* When a set is marked undisturbed, the corresponding set of
* destination elements in a vector register group retain the
* value they previously held.
*
* When a set is marked agnostic, the corresponding set of destination
* elements in any vector destination operand can either retain the value
* they previously held, or are overwritten with 1s.
*
* Within a single vector instruction, each destination element can be either
* left undisturbed or overwritten with 1s, in any combination, and the pattern
* of undisturbed or overwritten with 1s is not required to be deterministic when
* the instruction is executed with the same inputs. In addition, except for
* mask load instructions, any element in the tail of a mask result can also be
* written with the value the mask-producing operation would have calculated with vl=VLMAX
*/
enum class VMA : uint32_t {
No, // Undisturbed
Yes, // Agnostic
};
/**
* Describes whether or not vector tail elements are agnostic.
*
* From the RVV spec:
*
* When a set is marked undisturbed, the corresponding set of
* destination elements in a vector register group retain the
* value they previously held.
*
* When a set is marked agnostic, the corresponding set of destination
* elements in any vector destination operand can either retain the value
* they previously held, or are overwritten with 1s.
*
* Within a single vector instruction, each destination element can be either
* left undisturbed or overwritten with 1s, in any combination, and the pattern
* of undisturbed or overwritten with 1s is not required to be deterministic when
* the instruction is executed with the same inputs. In addition, except for
* mask load instructions, any element in the tail of a mask result can also be
* written with the value the mask-producing operation would have calculated with vl=VLMAX
*/
enum class VTA : uint32_t {
No, // Undisturbed
Yes, // Agnostic
};
} // namespace biscuit

View file

@ -0,0 +1,156 @@
# Main library
add_library(biscuit
# Source files
assembler.cpp
assembler_compressed.cpp
assembler_crypto.cpp
assembler_floating_point.cpp
assembler_vector.cpp
code_buffer.cpp
cpuinfo.cpp
# Headers
assembler_util.hpp
"${PROJECT_SOURCE_DIR}/include/biscuit/assembler.hpp"
"${PROJECT_SOURCE_DIR}/include/biscuit/assert.hpp"
"${PROJECT_SOURCE_DIR}/include/biscuit/code_buffer.hpp"
"${PROJECT_SOURCE_DIR}/include/biscuit/csr.hpp"
"${PROJECT_SOURCE_DIR}/include/biscuit/isa.hpp"
"${PROJECT_SOURCE_DIR}/include/biscuit/label.hpp"
"${PROJECT_SOURCE_DIR}/include/biscuit/registers.hpp"
"${PROJECT_SOURCE_DIR}/include/biscuit/vector.hpp"
"${PROJECT_SOURCE_DIR}/include/biscuit/cpuinfo.hpp"
)
add_library(biscuit::biscuit ALIAS biscuit)
target_include_directories(biscuit
PUBLIC
$<INSTALL_INTERFACE:include>
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
)
target_compile_features(biscuit
PRIVATE
cxx_std_20
)
if (MSVC)
target_compile_options(biscuit
PRIVATE
/MP
/Zi
/Zo
/permissive-
/EHsc
/utf-8
/volatile:iso
/Zc:externConstexpr
/Zc:inline
/Zc:throwingNew
# Warnings
/W4
/we4062 # enumerator 'identifier' in a switch of enum 'enumeration' is not handled
/we4101 # 'identifier': unreferenced local variable
/we4265 # 'class': class has virtual functions, but destructor is not virtual
/we4287 # 'operator' : unsigned/negative constant mismatch
/we4365 # 'action' : conversion from 'type_1' to 'type_2', signed/unsigned mismatch
/we4388 # signed/unsigned mismatch
/we4547 # 'operator' : operator before comma has no effect; expected operator with side-effect
/we4549 # 'operator1': operator before comma has no effect; did you intend 'operator2'?
/we4555 # Expression has no effect; expected expression with side-effect
/we4715 # 'function': not all control paths return a value
/we4834 # Discarding return value of function with 'nodiscard' attribute
/we5038 # data member 'member1' will be initialized after data member 'member2'
)
elseif (("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU"))
target_compile_options(biscuit
PRIVATE
-Wall
-Wextra
-Wconversion
-Wsign-conversion
-Werror=array-bounds
-Werror=cast-qual
-Werror=ignored-qualifiers
-Werror=implicit-fallthrough
-Werror=sign-compare
-Werror=reorder
-Werror=uninitialized
-Werror=unused-function
-Werror=unused-result
-Werror=unused-variable
)
endif()
if (BISCUIT_CODE_BUFFER_MMAP)
target_compile_definitions(biscuit
PRIVATE
-DBISCUIT_CODE_BUFFER_MMAP
)
endif()
# Install target
include(GNUInstallDirs)
set(BISCUIT_INSTALL_CONFIGDIR "${CMAKE_INSTALL_LIBDIR}/cmake/biscuit")
# Set install target and relevant includes.
install(TARGETS biscuit
EXPORT biscuit-targets
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
)
install(
DIRECTORY "${PROJECT_SOURCE_DIR}/include/"
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
)
# Export targets to a script
install(EXPORT biscuit-targets
FILE
biscuit-targets.cmake
NAMESPACE
biscuit::
DESTINATION
"${BISCUIT_INSTALL_CONFIGDIR}"
)
# Now create the config version script
include(CMakePackageConfigHelpers)
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/biscuit-config-version.cmake"
VERSION
${PROJECT_VERSION}
COMPATIBILITY
SameMajorVersion
)
configure_package_config_file(
"${PROJECT_SOURCE_DIR}/cmake/biscuit-config.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/biscuit-config.cmake"
INSTALL_DESTINATION "${BISCUIT_INSTALL_CONFIGDIR}"
)
# Now install the config and version files.
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/biscuit-config.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/biscuit-config-version.cmake"
DESTINATION "${BISCUIT_INSTALL_CONFIGDIR}"
)
# Export library from the build tree.
export(EXPORT biscuit-targets
FILE
"${CMAKE_CURRENT_BINARY_DIR}/biscuit-targets.cmake"
NAMESPACE
biscuit::
)
export(PACKAGE biscuit)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,701 @@
#include <biscuit/assert.hpp>
#include <biscuit/assembler.hpp>
#include <array>
#include <cmath>
#include "assembler_util.hpp"
// RVC Extension Instructions
namespace biscuit {
namespace {
// Emits a compressed branch instruction. These consist of:
// funct3 | imm[8|4:3] | rs | imm[7:6|2:1|5] | op
void EmitCompressedBranch(CodeBuffer& buffer, uint32_t funct3, int32_t offset, GPR rs, uint32_t op) {
BISCUIT_ASSERT(IsValidCBTypeImm(offset));
BISCUIT_ASSERT(IsValid3BitCompressedReg(rs));
const auto transformed_imm = TransformToCBTypeImm(static_cast<uint32_t>(offset));
const auto rs_san = CompressedRegTo3BitEncoding(rs);
buffer.Emit16(((funct3 & 0b111) << 13) | transformed_imm | (rs_san << 7) | (op & 0b11));
}
// Emits a compressed jump instruction. These consist of:
// funct3 | imm | op
void EmitCompressedJump(CodeBuffer& buffer, uint32_t funct3, int32_t offset, uint32_t op) {
BISCUIT_ASSERT(IsValidCJTypeImm(offset));
BISCUIT_ASSERT((offset % 2) == 0);
buffer.Emit16(TransformToCJTypeImm(static_cast<uint32_t>(offset)) |
((funct3 & 0b111) << 13) | (op & 0b11));
}
// Emits a compress immediate instruction. These consist of:
// funct3 | imm | rd | imm | op
void EmitCompressedImmediate(CodeBuffer& buffer, uint32_t funct3, uint32_t imm, GPR rd, uint32_t op) {
BISCUIT_ASSERT(rd != x0);
const auto new_imm = ((imm & 0b11111) << 2) | ((imm & 0b100000) << 7);
buffer.Emit16(((funct3 & 0b111) << 13) | new_imm | (rd.Index() << 7) | (op & 0b11));
}
// Emits a compressed load instruction. These consist of:
// funct3 | imm | rs1 | imm | rd | op
void EmitCompressedLoad(CodeBuffer& buffer, uint32_t funct3, uint32_t imm, GPR rs,
Register rd, uint32_t op) {
BISCUIT_ASSERT(IsValid3BitCompressedReg(rs));
BISCUIT_ASSERT(IsValid3BitCompressedReg(rd));
imm &= 0xF8;
const auto imm_enc = ((imm & 0x38) << 7) | ((imm & 0xC0) >> 1);
const auto rd_san = CompressedRegTo3BitEncoding(rd);
const auto rs_san = CompressedRegTo3BitEncoding(rs);
buffer.Emit16(((funct3 & 0b111) << 13) | imm_enc | (rs_san << 7) | (rd_san << 2) | (op & 0b11));
}
// Emits a compressed register arithmetic instruction. These consist of:
// funct6 | rd | funct2 | rs | op
void EmitCompressedRegArith(CodeBuffer& buffer, uint32_t funct6, GPR rd, uint32_t funct2,
GPR rs, uint32_t op) {
BISCUIT_ASSERT(IsValid3BitCompressedReg(rs));
BISCUIT_ASSERT(IsValid3BitCompressedReg(rd));
const auto rd_san = CompressedRegTo3BitEncoding(rd);
const auto rs_san = CompressedRegTo3BitEncoding(rs);
buffer.Emit16(((funct6 & 0b111111) << 10) | (rd_san << 7) | ((funct2 & 0b11) << 5) |
(rs_san << 2) | (op & 0b11));
}
// Emits a compressed store instruction. These consist of:
// funct3 | imm | rs1 | imm | rs2 | op
void EmitCompressedStore(CodeBuffer& buffer, uint32_t funct3, uint32_t imm, GPR rs1,
Register rs2, uint32_t op) {
// This has the same format as a compressed load, with rs2 taking the place of rd.
// We can reuse the code we've already written to handle this.
EmitCompressedLoad(buffer, funct3, imm, rs1, rs2, op);
}
// Emits a compressed wide immediate instruction. These consist of:
// funct3 | imm | rd | opcode
void EmitCompressedWideImmediate(CodeBuffer& buffer, uint32_t funct3, uint32_t imm,
GPR rd, uint32_t op) {
BISCUIT_ASSERT(IsValid3BitCompressedReg(rd));
const auto rd_sanitized = CompressedRegTo3BitEncoding(rd);
buffer.Emit16(((funct3 & 0b111) << 13) | ((imm & 0xFF) << 5) |
(rd_sanitized << 2) | (op & 0b11));
}
void EmitCLBType(CodeBuffer& buffer, uint32_t funct6, GPR rs, uint32_t uimm, GPR rd,
uint32_t op, uint32_t b6) {
BISCUIT_ASSERT(IsValid3BitCompressedReg(rs));
BISCUIT_ASSERT(IsValid3BitCompressedReg(rd));
BISCUIT_ASSERT(uimm <= 3);
const auto rd_san = CompressedRegTo3BitEncoding(rd);
const auto rs_san = CompressedRegTo3BitEncoding(rs);
buffer.Emit16((funct6 << 10) | (rs_san << 7) | (b6 << 6) | (uimm << 5) | (rd_san << 2) | op);
}
void EmitCLHType(CodeBuffer& buffer, uint32_t funct6, GPR rs, uint32_t uimm, GPR rd,
uint32_t op, uint32_t b6) {
BISCUIT_ASSERT((uimm % 2) == 0);
BISCUIT_ASSERT(uimm <= 2);
// Only have 1 bit of encoding space for the immediate.
const uint32_t uimm_fixed = uimm >> 1;
EmitCLBType(buffer, funct6, rs, uimm_fixed, rd, op, b6);
}
// These have the same layout as the equivalent loads, we just essentially alias
// the name of those to provide better intent at the call site.
void EmitCSBType(CodeBuffer& buffer, uint32_t funct6, GPR rs, uint32_t uimm, GPR rd, uint32_t op) {
EmitCLBType(buffer, funct6, rs, uimm, rd, op, 0);
}
void EmitCSHType(CodeBuffer& buffer, uint32_t funct6, GPR rs, uint32_t uimm, GPR rd, uint32_t op) {
EmitCLHType(buffer, funct6, rs, uimm, rd, op, 0);
}
void EmitCUType(CodeBuffer& buffer, uint32_t funct6, GPR rd, uint32_t funct5, uint32_t op) {
BISCUIT_ASSERT(IsValid3BitCompressedReg(rd));
const auto rd_san = CompressedRegTo3BitEncoding(rd);
buffer.Emit16((funct6 << 10) | (rd_san << 7) | (funct5 << 2) | op);
}
void EmitCMJTType(CodeBuffer& buffer, uint32_t funct6, uint32_t index, uint32_t op) {
buffer.Emit16((funct6 << 10) | (index << 2) | op);
}
void EmitCMMVType(CodeBuffer& buffer, uint32_t funct6, GPR r1s, uint32_t funct2, GPR r2s, uint32_t op) {
const auto is_valid_s_register = [](GPR reg) {
return reg == s0 || reg == s1 || (reg >= s2 && reg <= s7);
};
BISCUIT_ASSERT(r1s != r2s);
BISCUIT_ASSERT(is_valid_s_register(r1s));
BISCUIT_ASSERT(is_valid_s_register(r2s));
const auto r1s_san = r1s.Index() & 0b111;
const auto r2s_san = r2s.Index() & 0b111;
buffer.Emit16((funct6 << 10) | (r1s_san << 7) | (funct2 << 5) | (r2s_san << 2) | op);
}
void EmitCMPPType(CodeBuffer& buffer, uint32_t funct6, uint32_t funct2, PushPopList reglist,
int32_t stack_adj, uint32_t op, ArchFeature feature) {
BISCUIT_ASSERT(stack_adj % 16 == 0);
static constexpr std::array stack_adj_bases_rv32{
0U, 0U, 0U, 0U, 16U, 16U, 16U, 16U,
32U, 32U, 32U, 32U, 48U, 48U, 48U, 64U,
};
static constexpr std::array stack_adj_bases_rv64{
0U, 0U, 0U, 0U, 16U, 16U, 32U, 32U,
48U, 48U, 64U, 64U, 80U, 80U, 96U, 112U
};
const auto bitmask = reglist.GetBitmask();
const auto stack_adj_base = IsRV64(feature) ? stack_adj_bases_rv64[bitmask]
: stack_adj_bases_rv32[bitmask];
const auto stack_adj_u = static_cast<uint32_t>(std::abs(stack_adj));
const auto spimm = (stack_adj_u - stack_adj_base) / 16U;
// We can only encode up to three differenct values as the upper spimm bits.
// Ensure we catch any cases where we end up going outside of them.
BISCUIT_ASSERT(stack_adj_u == stack_adj_base ||
stack_adj_u == stack_adj_base + 16 ||
stack_adj_u == stack_adj_base + 32 ||
stack_adj_u == stack_adj_base + 48);
buffer.Emit16((funct6 << 10) | (funct2 << 8) | (bitmask << 4) | (spimm << 2) | op);
}
} // Anonymous namespace
void Assembler::C_ADD(GPR rd, GPR rs) noexcept {
BISCUIT_ASSERT(rs != x0);
m_buffer.Emit16(0x9002 | (rd.Index() << 7) | (rs.Index() << 2));
}
void Assembler::C_ADDI(GPR rd, int32_t imm) noexcept {
BISCUIT_ASSERT(imm != 0);
BISCUIT_ASSERT(IsValidSigned6BitImm(imm));
EmitCompressedImmediate(m_buffer, 0b000, static_cast<uint32_t>(imm), rd, 0b01);
}
void Assembler::C_ADDIW(GPR rd, int32_t imm) noexcept {
BISCUIT_ASSERT(IsRV64OrRV128(m_features));
BISCUIT_ASSERT(IsValidSigned6BitImm(imm));
EmitCompressedImmediate(m_buffer, 0b001, static_cast<uint32_t>(imm), rd, 0b01);
}
void Assembler::C_ADDI4SPN(GPR rd, uint32_t imm) noexcept {
BISCUIT_ASSERT(imm != 0);
BISCUIT_ASSERT(imm <= 1020);
BISCUIT_ASSERT(imm % 4 == 0);
// clang-format off
const auto new_imm = ((imm & 0x030) << 2) |
((imm & 0x3C0) >> 4) |
((imm & 0x004) >> 1) |
((imm & 0x008) >> 3);
// clang-format on
EmitCompressedWideImmediate(m_buffer, 0b000, new_imm, rd, 0b00);
}
void Assembler::C_ADDW(GPR rd, GPR rs) noexcept {
BISCUIT_ASSERT(IsRV64OrRV128(m_features));
EmitCompressedRegArith(m_buffer, 0b100111, rd, 0b01, rs, 0b01);
}
void Assembler::C_ADDI16SP(int32_t imm) noexcept {
BISCUIT_ASSERT(imm != 0);
BISCUIT_ASSERT(imm >= -512 && imm <= 496);
BISCUIT_ASSERT(imm % 16 == 0);
// clang-format off
const auto uimm = static_cast<uint32_t>(imm);
const auto new_imm = ((uimm & 0x020) >> 3) |
((uimm & 0x180) >> 4) |
((uimm & 0x040) >> 1) |
((uimm & 0x010) << 2) |
((uimm & 0x200) << 3);
// clang-format on
m_buffer.Emit16(0x6000U | new_imm | (x2.Index() << 7) | 0b01U);
}
void Assembler::C_AND(GPR rd, GPR rs) noexcept {
EmitCompressedRegArith(m_buffer, 0b100011, rd, 0b11, rs, 0b01);
}
void Assembler::C_ANDI(GPR rd, uint32_t imm) noexcept {
BISCUIT_ASSERT(IsValid3BitCompressedReg(rd));
constexpr auto base = 0x8801U;
const auto shift_enc = ((imm & 0b11111) << 2) | ((imm & 0b100000) << 7);
const auto reg = CompressedRegTo3BitEncoding(rd);
m_buffer.Emit16(base | shift_enc | (reg << 7));
}
void Assembler::C_BEQZ(GPR rs, int32_t offset) noexcept {
EmitCompressedBranch(m_buffer, 0b110, offset, rs, 0b01);
}
void Assembler::C_BEQZ(GPR rs, Label* label) noexcept {
const auto address = LinkAndGetOffset(label);
C_BEQZ(rs, static_cast<int32_t>(address));
}
void Assembler::C_BNEZ(GPR rs, int32_t offset) noexcept {
EmitCompressedBranch(m_buffer, 0b111, offset, rs, 0b01);
}
void Assembler::C_BNEZ(GPR rs, Label* label) noexcept {
const auto address = LinkAndGetOffset(label);
C_BNEZ(rs, static_cast<int32_t>(address));
}
void Assembler::C_EBREAK() noexcept {
m_buffer.Emit16(0x9002);
}
void Assembler::C_FLD(FPR rd, uint32_t imm, GPR rs) noexcept {
BISCUIT_ASSERT(IsRV32OrRV64(m_features));
BISCUIT_ASSERT(imm <= 248);
BISCUIT_ASSERT(imm % 8 == 0);
EmitCompressedLoad(m_buffer, 0b001, imm, rs, rd, 0b00);
}
void Assembler::C_FLDSP(FPR rd, uint32_t imm) noexcept {
BISCUIT_ASSERT(IsRV32OrRV64(m_features));
BISCUIT_ASSERT(imm <= 504);
BISCUIT_ASSERT(imm % 8 == 0);
// clang-format off
const auto new_imm = ((imm & 0x018) << 2) |
((imm & 0x1C0) >> 4) |
((imm & 0x020) << 7);
// clang-format on
m_buffer.Emit16(0x2002U | (rd.Index() << 7) | new_imm);
}
void Assembler::C_FLW(FPR rd, uint32_t imm, GPR rs) noexcept {
BISCUIT_ASSERT(IsRV32(m_features));
BISCUIT_ASSERT(imm <= 124);
BISCUIT_ASSERT(imm % 4 == 0);
imm &= 0x7C;
const auto new_imm = ((imm & 0b0100) << 5) | (imm & 0x78);
EmitCompressedLoad(m_buffer, 0b011, new_imm, rs, rd, 0b00);
}
void Assembler::C_FLWSP(FPR rd, uint32_t imm) noexcept {
BISCUIT_ASSERT(IsRV32(m_features));
BISCUIT_ASSERT(imm <= 252);
BISCUIT_ASSERT(imm % 4 == 0);
// clang-format off
const auto new_imm = ((imm & 0x020) << 7) |
((imm & 0x0C0) >> 4) |
((imm & 0x01C) << 2);
// clang-format on
m_buffer.Emit16(0x6002U | (rd.Index() << 7) | new_imm);
}
void Assembler::C_FSD(FPR rs2, uint32_t imm, GPR rs1) noexcept {
BISCUIT_ASSERT(IsRV32OrRV64(m_features));
BISCUIT_ASSERT(imm <= 248);
BISCUIT_ASSERT(imm % 8 == 0);
EmitCompressedStore(m_buffer, 0b101, imm, rs1, rs2, 0b00);
}
void Assembler::C_FSDSP(FPR rs, uint32_t imm) noexcept {
BISCUIT_ASSERT(IsRV32OrRV64(m_features));
BISCUIT_ASSERT(imm <= 504);
BISCUIT_ASSERT(imm % 8 == 0);
// clang-format off
const auto new_imm = ((imm & 0x038) << 7) |
((imm & 0x1C0) << 1);
// clang-format on
m_buffer.Emit16(0xA002U | (rs.Index() << 2) | new_imm);
}
void Assembler::C_J(Label* label) noexcept {
const auto address = LinkAndGetOffset(label);
C_J(static_cast<int32_t>(address));
}
void Assembler::C_J(int32_t offset) noexcept {
EmitCompressedJump(m_buffer, 0b101, offset, 0b01);
}
void Assembler::C_JAL(Label* label) noexcept {
const auto address = LinkAndGetOffset(label);
C_JAL(static_cast<int32_t>(address));
}
void Assembler::C_JAL(int32_t offset) noexcept {
BISCUIT_ASSERT(IsRV32(m_features));
EmitCompressedJump(m_buffer, 0b001, offset, 0b01);
}
void Assembler::C_FSW(FPR rs2, uint32_t imm, GPR rs1) noexcept {
BISCUIT_ASSERT(IsRV32(m_features));
BISCUIT_ASSERT(imm <= 124);
BISCUIT_ASSERT(imm % 4 == 0);
imm &= 0x7C;
const auto new_imm = ((imm & 0b0100) << 5) | (imm & 0x78);
EmitCompressedStore(m_buffer, 0b111, new_imm, rs1, rs2, 0b00);
}
void Assembler::C_FSWSP(FPR rs, uint32_t imm) noexcept {
BISCUIT_ASSERT(IsRV32(m_features));
BISCUIT_ASSERT(imm <= 252);
BISCUIT_ASSERT(imm % 4 == 0);
// clang-format off
const auto new_imm = ((imm & 0x0C0) << 1) |
((imm & 0x03C) << 7);
// clang-format on
m_buffer.Emit16(0xE002U | (rs.Index() << 2) | new_imm);
}
void Assembler::C_JALR(GPR rs) noexcept {
BISCUIT_ASSERT(rs != x0);
m_buffer.Emit16(0x9002 | (rs.Index() << 7));
}
void Assembler::C_JR(GPR rs) noexcept {
BISCUIT_ASSERT(rs != x0);
m_buffer.Emit16(0x8002 | (rs.Index() << 7));
}
void Assembler::C_LD(GPR rd, uint32_t imm, GPR rs) noexcept {
BISCUIT_ASSERT(imm <= 248);
BISCUIT_ASSERT(imm % 8 == 0);
EmitCompressedLoad(m_buffer, 0b011, imm, rs, rd, 0b00);
}
void Assembler::C_LDSP(GPR rd, uint32_t imm) noexcept {
BISCUIT_ASSERT(rd != x0);
BISCUIT_ASSERT(imm <= 504);
BISCUIT_ASSERT(imm % 8 == 0);
// clang-format off
const auto new_imm = ((imm & 0x018) << 2) |
((imm & 0x1C0) >> 4) |
((imm & 0x020) << 7);
// clang-format on
m_buffer.Emit16(0x6002U | (rd.Index() << 7) | new_imm);
}
void Assembler::C_LI(GPR rd, int32_t imm) noexcept {
BISCUIT_ASSERT(IsValidSigned6BitImm(imm));
EmitCompressedImmediate(m_buffer, 0b010, static_cast<uint32_t>(imm), rd, 0b01);
}
void Assembler::C_LQ(GPR rd, uint32_t imm, GPR rs) noexcept {
BISCUIT_ASSERT(IsRV128(m_features));
BISCUIT_ASSERT(imm <= 496);
BISCUIT_ASSERT(imm % 16 == 0);
imm &= 0x1F0;
const auto new_imm = ((imm & 0x100) >> 5) | (imm & 0xF0);
EmitCompressedLoad(m_buffer, 0b001, new_imm, rs, rd, 0b00);
}
void Assembler::C_LQSP(GPR rd, uint32_t imm) noexcept {
BISCUIT_ASSERT(IsRV128(m_features));
BISCUIT_ASSERT(rd != x0);
BISCUIT_ASSERT(imm <= 1008);
BISCUIT_ASSERT(imm % 16 == 0);
// clang-format off
const auto new_imm = ((imm & 0x020) << 7) |
((imm & 0x010) << 2) |
((imm & 0x3C0) >> 4);
// clang-format on
m_buffer.Emit16(0x2002U | (rd.Index() << 7) | new_imm);
}
void Assembler::C_LUI(GPR rd, uint32_t imm) noexcept {
BISCUIT_ASSERT(imm != 0);
BISCUIT_ASSERT(rd != x0 && rd != x2);
const auto new_imm = (imm & 0x3F000) >> 12;
EmitCompressedImmediate(m_buffer, 0b011, new_imm, rd, 0b01);
}
void Assembler::C_LW(GPR rd, uint32_t imm, GPR rs) noexcept {
BISCUIT_ASSERT(imm <= 124);
BISCUIT_ASSERT(imm % 4 == 0);
imm &= 0x7C;
const auto new_imm = ((imm & 0b0100) << 5) | (imm & 0x78);
EmitCompressedLoad(m_buffer, 0b010, new_imm, rs, rd, 0b00);
}
void Assembler::C_LWSP(GPR rd, uint32_t imm) noexcept {
BISCUIT_ASSERT(rd != x0);
BISCUIT_ASSERT(imm <= 252);
BISCUIT_ASSERT(imm % 4 == 0);
// clang-format off
const auto new_imm = ((imm & 0x020) << 7) |
((imm & 0x0C0) >> 4) |
((imm & 0x01C) << 2);
// clang-format on
m_buffer.Emit16(0x4002U | (rd.Index() << 7) | new_imm);
}
void Assembler::C_MV(GPR rd, GPR rs) noexcept {
BISCUIT_ASSERT(rd != x0);
BISCUIT_ASSERT(rs != x0);
m_buffer.Emit16(0x8002 | (rd.Index() << 7) | (rs.Index() << 2));
}
void Assembler::C_NOP() noexcept {
m_buffer.Emit16(1);
}
void Assembler::C_OR(GPR rd, GPR rs) noexcept {
EmitCompressedRegArith(m_buffer, 0b100011, rd, 0b10, rs, 0b01);
}
void Assembler::C_SD(GPR rs2, uint32_t imm, GPR rs1) noexcept {
BISCUIT_ASSERT(imm <= 248);
BISCUIT_ASSERT(imm % 8 == 0);
EmitCompressedLoad(m_buffer, 0b111, imm, rs1, rs2, 0b00);
}
void Assembler::C_SDSP(GPR rs, uint32_t imm) noexcept {
BISCUIT_ASSERT(imm <= 504);
BISCUIT_ASSERT(imm % 8 == 0);
// clang-format off
const auto new_imm = ((imm & 0x038) << 7) |
((imm & 0x1C0) << 1);
// clang-format on
m_buffer.Emit16(0xE002U | (rs.Index() << 2) | new_imm);
}
void Assembler::C_SLLI(GPR rd, uint32_t shift) noexcept {
BISCUIT_ASSERT(rd != x0);
BISCUIT_ASSERT(IsValidCompressedShiftAmount(shift));
// RV128C encodes a 64-bit shift with an encoding of 0.
if (shift == 64) {
BISCUIT_ASSERT(IsRV128(m_features));
shift = 0;
}
const auto shift_enc = ((shift & 0b11111) << 2) | ((shift & 0b100000) << 7);
m_buffer.Emit16(0x0002U | shift_enc | (rd.Index() << 7));
}
void Assembler::C_SQ(GPR rs2, uint32_t imm, GPR rs1) noexcept {
BISCUIT_ASSERT(IsRV128(m_features));
BISCUIT_ASSERT(imm <= 496);
BISCUIT_ASSERT(imm % 16 == 0);
imm &= 0x1F0;
const auto new_imm = ((imm & 0x100) >> 5) | (imm & 0xF0);
EmitCompressedStore(m_buffer, 0b101, new_imm, rs1, rs2, 0b00);
}
void Assembler::C_SQSP(GPR rs, uint32_t imm) noexcept {
BISCUIT_ASSERT(IsRV128(m_features));
BISCUIT_ASSERT(imm <= 1008);
BISCUIT_ASSERT(imm % 16 == 0);
// clang-format off
const auto new_imm = ((imm & 0x3C0) << 1) |
((imm & 0x030) << 7);
// clang-format on
m_buffer.Emit16(0xA002U | (rs.Index() << 2) | new_imm);
}
void Assembler::C_SRAI(GPR rd, uint32_t shift) noexcept {
BISCUIT_ASSERT(IsValid3BitCompressedReg(rd));
BISCUIT_ASSERT(IsValidCompressedShiftAmount(shift));
// RV128C encodes a 64-bit shift with an encoding of 0.
if (shift == 64) {
BISCUIT_ASSERT(IsRV128(m_features));
shift = 0;
}
constexpr auto base = 0x8401U;
const auto shift_enc = ((shift & 0b11111) << 2) | ((shift & 0b100000) << 7);
const auto reg = CompressedRegTo3BitEncoding(rd);
m_buffer.Emit16(base | shift_enc | (reg << 7));
}
void Assembler::C_SRLI(GPR rd, uint32_t shift) noexcept {
BISCUIT_ASSERT(IsValid3BitCompressedReg(rd));
BISCUIT_ASSERT(IsValidCompressedShiftAmount(shift));
// RV128C encodes a 64-bit shift with an encoding of 0.
if (shift == 64) {
BISCUIT_ASSERT(IsRV128(m_features));
shift = 0;
}
constexpr auto base = 0x8001U;
const auto shift_enc = ((shift & 0b11111) << 2) | ((shift & 0b100000) << 7);
const auto reg = CompressedRegTo3BitEncoding(rd);
m_buffer.Emit16(base | shift_enc | (reg << 7));
}
void Assembler::C_SUB(GPR rd, GPR rs) noexcept {
EmitCompressedRegArith(m_buffer, 0b100011, rd, 0b00, rs, 0b01);
}
void Assembler::C_SUBW(GPR rd, GPR rs) noexcept {
BISCUIT_ASSERT(IsRV64OrRV128(m_features));
EmitCompressedRegArith(m_buffer, 0b100111, rd, 0b00, rs, 0b01);
}
void Assembler::C_SW(GPR rs2, uint32_t imm, GPR rs1) noexcept {
BISCUIT_ASSERT(imm <= 124);
BISCUIT_ASSERT(imm % 4 == 0);
imm &= 0x7C;
const auto new_imm = ((imm & 0b0100) << 5) | (imm & 0x78);
EmitCompressedStore(m_buffer, 0b110, new_imm, rs1, rs2, 0b00);
}
void Assembler::C_SWSP(GPR rs, uint32_t imm) noexcept {
BISCUIT_ASSERT(imm <= 252);
BISCUIT_ASSERT(imm % 4 == 0);
// clang-format off
const auto new_imm = ((imm & 0x0C0) << 1) |
((imm & 0x03C) << 7);
// clang-format on
m_buffer.Emit16(0xC002U | (rs.Index() << 2) | new_imm);
}
void Assembler::C_UNDEF() noexcept {
m_buffer.Emit16(0);
}
void Assembler::C_XOR(GPR rd, GPR rs) noexcept {
EmitCompressedRegArith(m_buffer, 0b100011, rd, 0b01, rs, 0b01);
}
// Zc Extension Instructions
void Assembler::C_LBU(GPR rd, uint32_t uimm, GPR rs) noexcept {
// C.LBU swaps the ordering of the immediate.
const auto uimm_fixed = ((uimm & 0b01) << 1) | ((uimm & 0b10) >> 1);
EmitCLBType(m_buffer, 0b100000, rs, uimm_fixed, rd, 0b00, 0);
}
void Assembler::C_LH(GPR rd, uint32_t uimm, GPR rs) noexcept {
EmitCLHType(m_buffer, 0b100001, rs, uimm, rd, 0b00, 1);
}
void Assembler::C_LHU(GPR rd, uint32_t uimm, GPR rs) noexcept {
EmitCLHType(m_buffer, 0b100001, rs, uimm, rd, 0b00, 0);
}
void Assembler::C_SB(GPR rs2, uint32_t uimm, GPR rs1) noexcept {
// C.SB swaps the ordering of the immediate.
const auto uimm_fixed = ((uimm & 0b01) << 1) | ((uimm & 0b10) >> 1);
EmitCSBType(m_buffer, 0b100010, rs1, uimm_fixed, rs2, 0b00);
}
void Assembler::C_SH(GPR rs2, uint32_t uimm, GPR rs1) noexcept {
EmitCSHType(m_buffer, 0b100011, rs1, uimm, rs2, 0b00);
}
void Assembler::C_SEXT_B(GPR rd) noexcept {
EmitCUType(m_buffer, 0b100111, rd, 0b11001, 0b01);
}
void Assembler::C_SEXT_H(GPR rd) noexcept {
EmitCUType(m_buffer, 0b100111, rd, 0b11011, 0b01);
}
void Assembler::C_ZEXT_B(GPR rd) noexcept {
EmitCUType(m_buffer, 0b100111, rd, 0b11000, 0b01);
}
void Assembler::C_ZEXT_H(GPR rd) noexcept {
EmitCUType(m_buffer, 0b100111, rd, 0b11010, 0b01);
}
void Assembler::C_ZEXT_W(GPR rd) noexcept {
BISCUIT_ASSERT(IsRV64(m_features));
EmitCUType(m_buffer, 0b100111, rd, 0b11100, 0b01);
}
void Assembler::C_MUL(GPR rsd, GPR rs2) noexcept {
EmitCompressedRegArith(m_buffer, 0b100111, rsd, 0b10, rs2, 0b01);
}
void Assembler::C_NOT(GPR rd) noexcept {
EmitCUType(m_buffer, 0b100111, rd, 0b11101, 0b01);
}
void Assembler::CM_JALT(uint32_t index) noexcept {
BISCUIT_ASSERT(index >= 32 && index <= 255);
EmitCMJTType(m_buffer, 0b101000, index, 0b10);
}
void Assembler::CM_JT(uint32_t index) noexcept {
BISCUIT_ASSERT(index <= 31);
EmitCMJTType(m_buffer, 0b101000, index, 0b10);
}
void Assembler::CM_MVA01S(GPR r1s, GPR r2s) noexcept {
EmitCMMVType(m_buffer, 0b101011, r1s, 0b11, r2s, 0b10);
}
void Assembler::CM_MVSA01(GPR r1s, GPR r2s) noexcept {
EmitCMMVType(m_buffer, 0b101011, r1s, 0b01, r2s, 0b10);
}
void Assembler::CM_POP(PushPopList reg_list, int32_t stack_adj) noexcept {
BISCUIT_ASSERT(stack_adj > 0);
EmitCMPPType(m_buffer, 0b101110, 0b10, reg_list, stack_adj, 0b10, m_features);
}
void Assembler::CM_POPRET(PushPopList reg_list, int32_t stack_adj) noexcept {
BISCUIT_ASSERT(stack_adj > 0);
EmitCMPPType(m_buffer, 0b101111, 0b10, reg_list, stack_adj, 0b10, m_features);
}
void Assembler::CM_POPRETZ(PushPopList reg_list, int32_t stack_adj) noexcept {
BISCUIT_ASSERT(stack_adj > 0);
EmitCMPPType(m_buffer, 0b101111, 0b00, reg_list, stack_adj, 0b10, m_features);
}
void Assembler::CM_PUSH(PushPopList reg_list, int32_t stack_adj) noexcept {
BISCUIT_ASSERT(stack_adj < 0);
EmitCMPPType(m_buffer, 0b101110, 0b00, reg_list, stack_adj, 0b10, m_features);
}
// Control Flow Integrity Extension Instructions
void Assembler::C_SSPOPCHK() noexcept {
EmitCMOP(m_buffer, 5);
}
void Assembler::C_SSPUSH() noexcept {
EmitCMOP(m_buffer, 1);
}
} // namespace biscuit

View file

@ -0,0 +1,172 @@
#include <biscuit/assert.hpp>
#include <biscuit/assembler.hpp>
#include "assembler_util.hpp"
namespace biscuit {
namespace {
void EmitAES32Instruction(CodeBuffer& buffer, uint32_t op, GPR rd, GPR rs1, GPR rs2, uint32_t bs) noexcept {
BISCUIT_ASSERT(bs <= 0b11);
buffer.Emit32(op | (bs << 30) | (rs2.Index() << 20) |
(rs1.Index() << 15) | (rd.Index() << 7));
}
void EmitSM4Instruction(CodeBuffer& buffer, uint32_t op, GPR rd, GPR rs1, GPR rs2, uint32_t bs) noexcept {
// Same behavior, function exists for a better contextual name.
EmitAES32Instruction(buffer, op, rd, rs1, rs2, bs);
}
void EmitAES64Instruction(CodeBuffer& buffer, uint32_t op, GPR rd, GPR rs1, GPR rs2) noexcept {
buffer.Emit32(op | (rs2.Index() << 20) | (rs1.Index() << 15) | (rd.Index() << 7));
}
void EmitSHAInstruction(CodeBuffer& buffer, uint32_t op, GPR rd, GPR rs1, GPR rs2) noexcept {
// Same behavior, function exists for a better contextual name.
EmitAES64Instruction(buffer, op, rd, rs1, rs2);
}
void EmitSM3Instruction(CodeBuffer& buffer, uint32_t op, GPR rd, GPR rs) noexcept {
// Same behavior, function exists for a better contextual name.
EmitAES64Instruction(buffer, op, rd, rs, x0);
}
} // Anonymous namespace
void Assembler::AES32DSI(GPR rd, GPR rs1, GPR rs2, uint32_t bs) noexcept {
BISCUIT_ASSERT(IsRV32(m_features));
EmitAES32Instruction(m_buffer, 0x2A000033, rd, rs1, rs2, bs);
}
void Assembler::AES32DSMI(GPR rd, GPR rs1, GPR rs2, uint32_t bs) noexcept {
BISCUIT_ASSERT(IsRV32(m_features));
EmitAES32Instruction(m_buffer, 0x2E000033, rd, rs1, rs2, bs);
}
void Assembler::AES32ESI(GPR rd, GPR rs1, GPR rs2, uint32_t bs) noexcept {
BISCUIT_ASSERT(IsRV32(m_features));
EmitAES32Instruction(m_buffer, 0x22000033, rd, rs1, rs2, bs);
}
void Assembler::AES32ESMI(GPR rd, GPR rs1, GPR rs2, uint32_t bs) noexcept {
BISCUIT_ASSERT(IsRV32(m_features));
EmitAES32Instruction(m_buffer, 0x26000033, rd, rs1, rs2, bs);
}
void Assembler::AES64DS(GPR rd, GPR rs1, GPR rs2) noexcept {
BISCUIT_ASSERT(IsRV64(m_features));
EmitAES64Instruction(m_buffer, 0x3A000033, rd, rs1, rs2);
}
void Assembler::AES64DSM(GPR rd, GPR rs1, GPR rs2) noexcept {
BISCUIT_ASSERT(IsRV64(m_features));
EmitAES64Instruction(m_buffer, 0x3E000033, rd, rs1, rs2);
}
void Assembler::AES64ES(GPR rd, GPR rs1, GPR rs2) noexcept {
BISCUIT_ASSERT(IsRV64(m_features));
EmitAES64Instruction(m_buffer, 0x32000033, rd, rs1, rs2);
}
void Assembler::AES64ESM(GPR rd, GPR rs1, GPR rs2) noexcept {
BISCUIT_ASSERT(IsRV64(m_features));
EmitAES64Instruction(m_buffer, 0x36000033, rd, rs1, rs2);
}
void Assembler::AES64IM(GPR rd, GPR rs) noexcept {
BISCUIT_ASSERT(IsRV64(m_features));
EmitAES64Instruction(m_buffer, 0x30001013, rd, rs, x0);
}
void Assembler::AES64KS1I(GPR rd, GPR rs, uint32_t rnum) noexcept {
// RVK spec states that rnums 0xB to 0xF are reserved.
BISCUIT_ASSERT(IsRV64(m_features));
BISCUIT_ASSERT(rnum <= 0xA);
EmitAES64Instruction(m_buffer, 0x31001013, rd, rs, GPR{rnum});
}
void Assembler::AES64KS2(GPR rd, GPR rs1, GPR rs2) noexcept {
BISCUIT_ASSERT(IsRV64(m_features));
EmitAES64Instruction(m_buffer, 0x7E000033, rd, rs1, rs2);
}
void Assembler::SHA256SIG0(GPR rd, GPR rs) noexcept {
EmitSHAInstruction(m_buffer, 0x10201013, rd, rs, x0);
}
void Assembler::SHA256SIG1(GPR rd, GPR rs) noexcept {
EmitSHAInstruction(m_buffer, 0x10301013, rd, rs, x0);
}
void Assembler::SHA256SUM0(GPR rd, GPR rs) noexcept {
EmitSHAInstruction(m_buffer, 0x10001013, rd, rs, x0);
}
void Assembler::SHA256SUM1(GPR rd, GPR rs) noexcept {
EmitSHAInstruction(m_buffer, 0x10101013, rd, rs, x0);
}
void Assembler::SHA512SIG0(GPR rd, GPR rs) noexcept {
BISCUIT_ASSERT(IsRV64(m_features));
EmitSHAInstruction(m_buffer, 0x10601013, rd, rs, x0);
}
void Assembler::SHA512SIG0H(GPR rd, GPR rs1, GPR rs2) noexcept {
BISCUIT_ASSERT(IsRV32(m_features));
EmitSHAInstruction(m_buffer, 0x5C000033, rd, rs1, rs2);
}
void Assembler::SHA512SIG0L(GPR rd, GPR rs1, GPR rs2) noexcept {
BISCUIT_ASSERT(IsRV32(m_features));
EmitSHAInstruction(m_buffer, 0x54000033, rd, rs1, rs2);
}
void Assembler::SHA512SIG1(GPR rd, GPR rs) noexcept {
BISCUIT_ASSERT(IsRV64(m_features));
EmitSHAInstruction(m_buffer, 0x10701013, rd, rs, x0);
}
void Assembler::SHA512SIG1H(GPR rd, GPR rs1, GPR rs2) noexcept {
BISCUIT_ASSERT(IsRV32(m_features));
EmitSHAInstruction(m_buffer, 0x5E000033, rd, rs1, rs2);
}
void Assembler::SHA512SIG1L(GPR rd, GPR rs1, GPR rs2) noexcept {
BISCUIT_ASSERT(IsRV32(m_features));
EmitSHAInstruction(m_buffer, 0x56000033, rd, rs1, rs2);
}
void Assembler::SHA512SUM0(GPR rd, GPR rs) noexcept {
BISCUIT_ASSERT(IsRV64(m_features));
EmitSHAInstruction(m_buffer, 0x10401013, rd, rs, x0);
}
void Assembler::SHA512SUM0R(GPR rd, GPR rs1, GPR rs2) noexcept {
BISCUIT_ASSERT(IsRV32(m_features));
EmitSHAInstruction(m_buffer, 0x50000033, rd, rs1, rs2);
}
void Assembler::SHA512SUM1(GPR rd, GPR rs) noexcept {
BISCUIT_ASSERT(IsRV64(m_features));
EmitSHAInstruction(m_buffer, 0x10501013, rd, rs, x0);
}
void Assembler::SHA512SUM1R(GPR rd, GPR rs1, GPR rs2) noexcept {
BISCUIT_ASSERT(IsRV32(m_features));
EmitSHAInstruction(m_buffer, 0x52000033, rd, rs1, rs2);
}
void Assembler::SM3P0(GPR rd, GPR rs) noexcept {
EmitSM3Instruction(m_buffer, 0x10801013, rd, rs);
}
void Assembler::SM3P1(GPR rd, GPR rs) noexcept {
EmitSM3Instruction(m_buffer, 0x10901013, rd, rs);
}
void Assembler::SM4ED(GPR rd, GPR rs1, GPR rs2, uint32_t bs) noexcept {
EmitSM4Instruction(m_buffer, 0x30000033, rd, rs1, rs2, bs);
}
void Assembler::SM4KS(GPR rd, GPR rs1, GPR rs2, uint32_t bs) noexcept {
EmitSM4Instruction(m_buffer, 0x34000033, rd, rs1, rs2, bs);
}
} // namespace biscuit

View file

@ -0,0 +1,648 @@
#include <biscuit/assert.hpp>
#include <biscuit/assembler.hpp>
#include <algorithm>
#include <array>
#include <cstring>
#include <iterator>
#include "assembler_util.hpp"
// Various floating-point-based extension instructions.
namespace biscuit {
// RV32F Extension Instructions
void Assembler::FADD_S(FPR rd, FPR rs1, FPR rs2, RMode rmode) noexcept {
EmitRType(m_buffer, 0b0000000, rs2, rs1, rmode, rd, 0b1010011);
}
void Assembler::FCLASS_S(GPR rd, FPR rs1) noexcept {
EmitRType(m_buffer, 0b1110000, f0, rs1, 0b001, rd, 0b1010011);
}
void Assembler::FCVT_S_W(FPR rd, GPR rs1, RMode rmode) noexcept {
EmitRType(m_buffer, 0b1101000, f0, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011);
}
void Assembler::FCVT_S_WU(FPR rd, GPR rs1, RMode rmode) noexcept {
EmitRType(m_buffer, 0b1101000, f1, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011);
}
void Assembler::FCVT_W_S(GPR rd, FPR rs1, RMode rmode) noexcept {
EmitRType(m_buffer, 0b1100000, f0, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011);
}
void Assembler::FCVT_WU_S(GPR rd, FPR rs1, RMode rmode) noexcept {
EmitRType(m_buffer, 0b1100000, f1, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011);
}
void Assembler::FDIV_S(FPR rd, FPR rs1, FPR rs2, RMode rmode) noexcept {
EmitRType(m_buffer, 0b0001100, rs2, rs1, rmode, rd, 0b1010011);
}
void Assembler::FEQ_S(GPR rd, FPR rs1, FPR rs2) noexcept {
EmitRType(m_buffer, 0b1010000, rs2, rs1, 0b010, rd, 0b1010011);
}
void Assembler::FLE_S(GPR rd, FPR rs1, FPR rs2) noexcept {
EmitRType(m_buffer, 0b1010000, rs2, rs1, 0b000, rd, 0b1010011);
}
void Assembler::FLT_S(GPR rd, FPR rs1, FPR rs2) noexcept {
EmitRType(m_buffer, 0b1010000, rs2, rs1, 0b001, rd, 0b1010011);
}
void Assembler::FLW(FPR rd, int32_t offset, GPR rs) noexcept {
BISCUIT_ASSERT(IsValidSigned12BitImm(offset));
EmitIType(m_buffer, static_cast<uint32_t>(offset), rs, 0b010, rd, 0b0000111);
}
void Assembler::FMADD_S(FPR rd, FPR rs1, FPR rs2, FPR rs3, RMode rmode) noexcept {
EmitR4Type(m_buffer, rs3, 0b00, rs2, rs1, rmode, rd, 0b1000011);
}
void Assembler::FMAX_S(FPR rd, FPR rs1, FPR rs2) noexcept {
EmitRType(m_buffer, 0b0010100, rs2, rs1, 0b001, rd, 0b1010011);
}
void Assembler::FMIN_S(FPR rd, FPR rs1, FPR rs2) noexcept {
EmitRType(m_buffer, 0b0010100, rs2, rs1, 0b000, rd, 0b1010011);
}
void Assembler::FMSUB_S(FPR rd, FPR rs1, FPR rs2, FPR rs3, RMode rmode) noexcept {
EmitR4Type(m_buffer, rs3, 0b00, rs2, rs1, rmode, rd, 0b1000111);
}
void Assembler::FMUL_S(FPR rd, FPR rs1, FPR rs2, RMode rmode) noexcept {
EmitRType(m_buffer, 0b0001000, rs2, rs1, rmode, rd, 0b1010011);
}
void Assembler::FMV_W_X(FPR rd, GPR rs1) noexcept {
EmitRType(m_buffer, 0b1111000, f0, rs1, 0b000, rd, 0b1010011);
}
void Assembler::FMV_X_W(GPR rd, FPR rs1) noexcept {
EmitRType(m_buffer, 0b1110000, f0, rs1, 0b000, rd, 0b1010011);
}
void Assembler::FNMADD_S(FPR rd, FPR rs1, FPR rs2, FPR rs3, RMode rmode) noexcept {
EmitR4Type(m_buffer, rs3, 0b00, rs2, rs1, rmode, rd, 0b1001111);
}
void Assembler::FNMSUB_S(FPR rd, FPR rs1, FPR rs2, FPR rs3, RMode rmode) noexcept {
EmitR4Type(m_buffer, rs3, 0b00, rs2, rs1, rmode, rd, 0b1001011);
}
void Assembler::FSGNJ_S(FPR rd, FPR rs1, FPR rs2) noexcept {
EmitRType(m_buffer, 0b0010000, rs2, rs1, 0b000, rd, 0b1010011);
}
void Assembler::FSGNJN_S(FPR rd, FPR rs1, FPR rs2) noexcept {
EmitRType(m_buffer, 0b0010000, rs2, rs1, 0b001, rd, 0b1010011);
}
void Assembler::FSGNJX_S(FPR rd, FPR rs1, FPR rs2) noexcept {
EmitRType(m_buffer, 0b0010000, rs2, rs1, 0b010, rd, 0b1010011);
}
void Assembler::FSQRT_S(FPR rd, FPR rs1, RMode rmode) noexcept {
EmitRType(m_buffer, 0b0101100, f0, rs1, rmode, rd, 0b1010011);
}
void Assembler::FSUB_S(FPR rd, FPR rs1, FPR rs2, RMode rmode) noexcept {
EmitRType(m_buffer, 0b0000100, rs2, rs1, rmode, rd, 0b1010011);
}
void Assembler::FSW(FPR rs2, int32_t offset, GPR rs1) noexcept {
BISCUIT_ASSERT(IsValidSigned12BitImm(offset));
EmitSType(m_buffer, static_cast<uint32_t>(offset), rs2, rs1, 0b010, 0b0100111);
}
void Assembler::FABS_S(FPR rd, FPR rs) noexcept {
FSGNJX_S(rd, rs, rs);
}
void Assembler::FMV_S(FPR rd, FPR rs) noexcept {
FSGNJ_S(rd, rs, rs);
}
void Assembler::FNEG_S(FPR rd, FPR rs) noexcept {
FSGNJN_S(rd, rs, rs);
}
// RV64F Extension Instructions
void Assembler::FCVT_L_S(GPR rd, FPR rs1, RMode rmode) noexcept {
BISCUIT_ASSERT(IsRV64(m_features));
EmitRType(m_buffer, 0b1100000, f2, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011);
}
void Assembler::FCVT_LU_S(GPR rd, FPR rs1, RMode rmode) noexcept {
BISCUIT_ASSERT(IsRV64(m_features));
EmitRType(m_buffer, 0b1100000, f3, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011);
}
void Assembler::FCVT_S_L(FPR rd, GPR rs1, RMode rmode) noexcept {
BISCUIT_ASSERT(IsRV64(m_features));
EmitRType(m_buffer, 0b1101000, f2, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011);
}
void Assembler::FCVT_S_LU(FPR rd, GPR rs1, RMode rmode) noexcept {
BISCUIT_ASSERT(IsRV64(m_features));
EmitRType(m_buffer, 0b1101000, f3, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011);
}
// RV32D Extension Instructions
void Assembler::FADD_D(FPR rd, FPR rs1, FPR rs2, RMode rmode) noexcept {
EmitRType(m_buffer, 0b0000001, rs2, rs1, rmode, rd, 0b1010011);
}
void Assembler::FCLASS_D(GPR rd, FPR rs1) noexcept {
EmitRType(m_buffer, 0b1110001, f0, rs1, 0b001, rd, 0b1010011);
}
void Assembler::FCVT_D_W(FPR rd, GPR rs1, RMode rmode) noexcept {
EmitRType(m_buffer, 0b1101001, f0, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011);
}
void Assembler::FCVT_D_WU(FPR rd, GPR rs1, RMode rmode) noexcept {
EmitRType(m_buffer, 0b1101001, f1, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011);
}
void Assembler::FCVT_W_D(GPR rd, FPR rs1, RMode rmode) noexcept {
EmitRType(m_buffer, 0b1100001, f0, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011);
}
void Assembler::FCVT_WU_D(GPR rd, FPR rs1, RMode rmode) noexcept {
EmitRType(m_buffer, 0b1100001, f1, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011);
}
void Assembler::FCVT_D_S(FPR rd, FPR rs1, RMode rmode) noexcept {
EmitRType(m_buffer, 0b0100001, f0, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011);
}
void Assembler::FCVT_S_D(FPR rd, FPR rs1, RMode rmode) noexcept {
EmitRType(m_buffer, 0b0100000, f1, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011);
}
void Assembler::FDIV_D(FPR rd, FPR rs1, FPR rs2, RMode rmode) noexcept {
EmitRType(m_buffer, 0b0001101, rs2, rs1, rmode, rd, 0b1010011);
}
void Assembler::FEQ_D(GPR rd, FPR rs1, FPR rs2) noexcept {
EmitRType(m_buffer, 0b1010001, rs2, rs1, 0b010, rd, 0b1010011);
}
void Assembler::FLE_D(GPR rd, FPR rs1, FPR rs2) noexcept {
EmitRType(m_buffer, 0b1010001, rs2, rs1, 0b000, rd, 0b1010011);
}
void Assembler::FLT_D(GPR rd, FPR rs1, FPR rs2) noexcept {
EmitRType(m_buffer, 0b1010001, rs2, rs1, 0b001, rd, 0b1010011);
}
void Assembler::FLD(FPR rd, int32_t offset, GPR rs) noexcept {
BISCUIT_ASSERT(IsValidSigned12BitImm(offset));
EmitIType(m_buffer, static_cast<uint32_t>(offset), rs, 0b011, rd, 0b0000111);
}
void Assembler::FMADD_D(FPR rd, FPR rs1, FPR rs2, FPR rs3, RMode rmode) noexcept {
EmitR4Type(m_buffer, rs3, 0b01, rs2, rs1, rmode, rd, 0b1000011);
}
void Assembler::FMAX_D(FPR rd, FPR rs1, FPR rs2) noexcept {
EmitRType(m_buffer, 0b0010101, rs2, rs1, 0b001, rd, 0b1010011);
}
void Assembler::FMIN_D(FPR rd, FPR rs1, FPR rs2) noexcept {
EmitRType(m_buffer, 0b0010101, rs2, rs1, 0b000, rd, 0b1010011);
}
void Assembler::FMSUB_D(FPR rd, FPR rs1, FPR rs2, FPR rs3, RMode rmode) noexcept {
EmitR4Type(m_buffer, rs3, 0b01, rs2, rs1, rmode, rd, 0b1000111);
}
void Assembler::FMUL_D(FPR rd, FPR rs1, FPR rs2, RMode rmode) noexcept {
EmitRType(m_buffer, 0b0001001, rs2, rs1, rmode, rd, 0b1010011);
}
void Assembler::FNMADD_D(FPR rd, FPR rs1, FPR rs2, FPR rs3, RMode rmode) noexcept {
EmitR4Type(m_buffer, rs3, 0b01, rs2, rs1, rmode, rd, 0b1001111);
}
void Assembler::FNMSUB_D(FPR rd, FPR rs1, FPR rs2, FPR rs3, RMode rmode) noexcept {
EmitR4Type(m_buffer, rs3, 0b01, rs2, rs1, rmode, rd, 0b1001011);
}
void Assembler::FSGNJ_D(FPR rd, FPR rs1, FPR rs2) noexcept {
EmitRType(m_buffer, 0b0010001, rs2, rs1, 0b000, rd, 0b1010011);
}
void Assembler::FSGNJN_D(FPR rd, FPR rs1, FPR rs2) noexcept {
EmitRType(m_buffer, 0b0010001, rs2, rs1, 0b001, rd, 0b1010011);
}
void Assembler::FSGNJX_D(FPR rd, FPR rs1, FPR rs2) noexcept {
EmitRType(m_buffer, 0b0010001, rs2, rs1, 0b010, rd, 0b1010011);
}
void Assembler::FSQRT_D(FPR rd, FPR rs1, RMode rmode) noexcept {
EmitRType(m_buffer, 0b0101101, f0, rs1, rmode, rd, 0b1010011);
}
void Assembler::FSUB_D(FPR rd, FPR rs1, FPR rs2, RMode rmode) noexcept {
EmitRType(m_buffer, 0b0000101, rs2, rs1, rmode, rd, 0b1010011);
}
void Assembler::FSD(FPR rs2, int32_t offset, GPR rs1) noexcept {
BISCUIT_ASSERT(IsValidSigned12BitImm(offset));
EmitSType(m_buffer, static_cast<uint32_t>(offset), rs2, rs1, 0b011, 0b0100111);
}
void Assembler::FABS_D(FPR rd, FPR rs) noexcept {
FSGNJX_D(rd, rs, rs);
}
void Assembler::FMV_D(FPR rd, FPR rs) noexcept {
FSGNJ_D(rd, rs, rs);
}
void Assembler::FNEG_D(FPR rd, FPR rs) noexcept {
FSGNJN_D(rd, rs, rs);
}
// RV64D Extension Instructions
void Assembler::FCVT_L_D(GPR rd, FPR rs1, RMode rmode) noexcept {
BISCUIT_ASSERT(IsRV64(m_features));
EmitRType(m_buffer, 0b1100001, f2, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011);
}
void Assembler::FCVT_LU_D(GPR rd, FPR rs1, RMode rmode) noexcept {
BISCUIT_ASSERT(IsRV64(m_features));
EmitRType(m_buffer, 0b1100001, f3, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011);
}
void Assembler::FCVT_D_L(FPR rd, GPR rs1, RMode rmode) noexcept {
BISCUIT_ASSERT(IsRV64(m_features));
EmitRType(m_buffer, 0b1101001, f2, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011);
}
void Assembler::FCVT_D_LU(FPR rd, GPR rs1, RMode rmode) noexcept {
BISCUIT_ASSERT(IsRV64(m_features));
EmitRType(m_buffer, 0b1101001, f3, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011);
}
void Assembler::FMV_D_X(FPR rd, GPR rs1) noexcept {
BISCUIT_ASSERT(IsRV64OrRV128(m_features));
EmitRType(m_buffer, 0b1111001, f0, rs1, 0b000, rd, 0b1010011);
}
void Assembler::FMV_X_D(GPR rd, FPR rs1) noexcept {
BISCUIT_ASSERT(IsRV64OrRV128(m_features));
EmitRType(m_buffer, 0b1110001, f0, rs1, 0b000, rd, 0b1010011);
}
// RV32Q Extension Instructions
void Assembler::FADD_Q(FPR rd, FPR rs1, FPR rs2, RMode rmode) noexcept {
EmitRType(m_buffer, 0b0000011, rs2, rs1, rmode, rd, 0b1010011);
}
void Assembler::FCLASS_Q(GPR rd, FPR rs1) noexcept {
EmitRType(m_buffer, 0b1110011, f0, rs1, 0b001, rd, 0b1010011);
}
void Assembler::FCVT_Q_W(FPR rd, GPR rs1, RMode rmode) noexcept {
EmitRType(m_buffer, 0b1101011, f0, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011);
}
void Assembler::FCVT_Q_WU(FPR rd, GPR rs1, RMode rmode) noexcept {
EmitRType(m_buffer, 0b1101011, f1, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011);
}
void Assembler::FCVT_W_Q(GPR rd, FPR rs1, RMode rmode) noexcept {
EmitRType(m_buffer, 0b1100011, f0, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011);
}
void Assembler::FCVT_WU_Q(GPR rd, FPR rs1, RMode rmode) noexcept {
EmitRType(m_buffer, 0b1100011, f1, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011);
}
void Assembler::FCVT_Q_D(FPR rd, FPR rs1, RMode rmode) noexcept {
EmitRType(m_buffer, 0b0100011, f1, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011);
}
void Assembler::FCVT_D_Q(FPR rd, FPR rs1, RMode rmode) noexcept {
EmitRType(m_buffer, 0b0100001, f3, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011);
}
void Assembler::FCVT_Q_S(FPR rd, FPR rs1, RMode rmode) noexcept {
EmitRType(m_buffer, 0b0100011, f0, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011);
}
void Assembler::FCVT_S_Q(FPR rd, FPR rs1, RMode rmode) noexcept {
EmitRType(m_buffer, 0b0100000, f3, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011);
}
void Assembler::FDIV_Q(FPR rd, FPR rs1, FPR rs2, RMode rmode) noexcept {
EmitRType(m_buffer, 0b0001111, rs2, rs1, rmode, rd, 0b1010011);
}
void Assembler::FEQ_Q(GPR rd, FPR rs1, FPR rs2) noexcept {
EmitRType(m_buffer, 0b1010011, rs2, rs1, 0b010, rd, 0b1010011);
}
void Assembler::FLE_Q(GPR rd, FPR rs1, FPR rs2) noexcept {
EmitRType(m_buffer, 0b1010011, rs2, rs1, 0b000, rd, 0b1010011);
}
void Assembler::FLT_Q(GPR rd, FPR rs1, FPR rs2) noexcept {
EmitRType(m_buffer, 0b1010011, rs2, rs1, 0b001, rd, 0b1010011);
}
void Assembler::FLQ(FPR rd, int32_t offset, GPR rs) noexcept {
BISCUIT_ASSERT(IsValidSigned12BitImm(offset));
EmitIType(m_buffer, static_cast<uint32_t>(offset), rs, 0b100, rd, 0b0000111);
}
void Assembler::FMADD_Q(FPR rd, FPR rs1, FPR rs2, FPR rs3, RMode rmode) noexcept {
EmitR4Type(m_buffer, rs3, 0b11, rs2, rs1, rmode, rd, 0b1000011);
}
void Assembler::FMAX_Q(FPR rd, FPR rs1, FPR rs2) noexcept {
EmitRType(m_buffer, 0b0010111, rs2, rs1, 0b001, rd, 0b1010011);
}
void Assembler::FMIN_Q(FPR rd, FPR rs1, FPR rs2) noexcept {
EmitRType(m_buffer, 0b0010111, rs2, rs1, 0b000, rd, 0b1010011);
}
void Assembler::FMSUB_Q(FPR rd, FPR rs1, FPR rs2, FPR rs3, RMode rmode) noexcept {
EmitR4Type(m_buffer, rs3, 0b11, rs2, rs1, rmode, rd, 0b1000111);
}
void Assembler::FMUL_Q(FPR rd, FPR rs1, FPR rs2, RMode rmode) noexcept {
EmitRType(m_buffer, 0b0001011, rs2, rs1, rmode, rd, 0b1010011);
}
void Assembler::FNMADD_Q(FPR rd, FPR rs1, FPR rs2, FPR rs3, RMode rmode) noexcept {
EmitR4Type(m_buffer, rs3, 0b11, rs2, rs1, rmode, rd, 0b1001111);
}
void Assembler::FNMSUB_Q(FPR rd, FPR rs1, FPR rs2, FPR rs3, RMode rmode) noexcept {
EmitR4Type(m_buffer, rs3, 0b11, rs2, rs1, rmode, rd, 0b1001011);
}
void Assembler::FSGNJ_Q(FPR rd, FPR rs1, FPR rs2) noexcept {
EmitRType(m_buffer, 0b0010011, rs2, rs1, 0b000, rd, 0b1010011);
}
void Assembler::FSGNJN_Q(FPR rd, FPR rs1, FPR rs2) noexcept {
EmitRType(m_buffer, 0b0010011, rs2, rs1, 0b001, rd, 0b1010011);
}
void Assembler::FSGNJX_Q(FPR rd, FPR rs1, FPR rs2) noexcept {
EmitRType(m_buffer, 0b0010011, rs2, rs1, 0b010, rd, 0b1010011);
}
void Assembler::FSQRT_Q(FPR rd, FPR rs1, RMode rmode) noexcept {
EmitRType(m_buffer, 0b0101111, f0, rs1, rmode, rd, 0b1010011);
}
void Assembler::FSUB_Q(FPR rd, FPR rs1, FPR rs2, RMode rmode) noexcept {
EmitRType(m_buffer, 0b0000111, rs2, rs1, rmode, rd, 0b1010011);
}
void Assembler::FSQ(FPR rs2, int32_t offset, GPR rs1) noexcept {
BISCUIT_ASSERT(IsValidSigned12BitImm(offset));
EmitSType(m_buffer, static_cast<uint32_t>(offset), rs2, rs1, 0b100, 0b0100111);
}
void Assembler::FABS_Q(FPR rd, FPR rs) noexcept {
FSGNJX_Q(rd, rs, rs);
}
void Assembler::FMV_Q(FPR rd, FPR rs) noexcept {
FSGNJ_Q(rd, rs, rs);
}
void Assembler::FNEG_Q(FPR rd, FPR rs) noexcept {
FSGNJN_Q(rd, rs, rs);
}
// RV64Q Extension Instructions
void Assembler::FCVT_L_Q(GPR rd, FPR rs1, RMode rmode) noexcept {
BISCUIT_ASSERT(IsRV64(m_features));
EmitRType(m_buffer, 0b1100011, f2, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011);
}
void Assembler::FCVT_LU_Q(GPR rd, FPR rs1, RMode rmode) noexcept {
BISCUIT_ASSERT(IsRV64(m_features));
EmitRType(m_buffer, 0b1100011, f3, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011);
}
void Assembler::FCVT_Q_L(FPR rd, GPR rs1, RMode rmode) noexcept {
BISCUIT_ASSERT(IsRV64(m_features));
EmitRType(m_buffer, 0b1101011, f2, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011);
}
void Assembler::FCVT_Q_LU(FPR rd, GPR rs1, RMode rmode) noexcept {
BISCUIT_ASSERT(IsRV64(m_features));
EmitRType(m_buffer, 0b1101011, f3, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011);
}
// RV32Zfh Extension Instructions
void Assembler::FADD_H(FPR rd, FPR rs1, FPR rs2, RMode rmode) noexcept {
EmitRType(m_buffer, 0b0000010, rs2, rs1, rmode, rd, 0b1010011);
}
void Assembler::FCLASS_H(GPR rd, FPR rs1) noexcept {
EmitRType(m_buffer, 0b1110010, f0, rs1, 0b001, rd, 0b1010011);
}
void Assembler::FCVT_D_H(FPR rd, FPR rs1, RMode rmode) noexcept {
EmitRType(m_buffer, 0b0100001, f2, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011);
}
void Assembler::FCVT_H_D(FPR rd, FPR rs1, RMode rmode) noexcept {
EmitRType(m_buffer, 0b0100010, f1, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011);
}
void Assembler::FCVT_H_Q(FPR rd, FPR rs1, RMode rmode) noexcept {
EmitRType(m_buffer, 0b0100010, f3, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011);
}
void Assembler::FCVT_H_S(FPR rd, FPR rs1, RMode rmode) noexcept {
EmitRType(m_buffer, 0b0100010, f0, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011);
}
void Assembler::FCVT_H_W(FPR rd, GPR rs1, RMode rmode) noexcept {
EmitRType(m_buffer, 0b1101010, f0, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011);
}
void Assembler::FCVT_H_WU(FPR rd, GPR rs1, RMode rmode) noexcept {
EmitRType(m_buffer, 0b1101010, f1, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011);
}
void Assembler::FCVT_Q_H(FPR rd, FPR rs1, RMode rmode) noexcept {
EmitRType(m_buffer, 0b0100011, f2, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011);
}
void Assembler::FCVT_S_H(FPR rd, FPR rs1, RMode rmode) noexcept {
EmitRType(m_buffer, 0b0100000, f2, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011);
}
void Assembler::FCVT_W_H(GPR rd, FPR rs1, RMode rmode) noexcept {
EmitRType(m_buffer, 0b1100010, f0, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011);
}
void Assembler::FCVT_WU_H(GPR rd, FPR rs1, RMode rmode) noexcept {
EmitRType(m_buffer, 0b1100010, f1, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011);
}
void Assembler::FDIV_H(FPR rd, FPR rs1, FPR rs2, RMode rmode) noexcept {
EmitRType(m_buffer, 0b0001110, rs2, rs1, rmode, rd, 0b1010011);
}
void Assembler::FEQ_H(GPR rd, FPR rs1, FPR rs2) noexcept {
EmitRType(m_buffer, 0b1010010, rs2, rs1, 0b010, rd, 0b1010011);
}
void Assembler::FLE_H(GPR rd, FPR rs1, FPR rs2) noexcept {
EmitRType(m_buffer, 0b1010010, rs2, rs1, 0b000, rd, 0b1010011);
}
void Assembler::FLH(FPR rd, int32_t offset, GPR rs) noexcept {
BISCUIT_ASSERT(IsValidSigned12BitImm(offset));
EmitIType(m_buffer, static_cast<uint32_t>(offset), rs, 0b001, rd, 0b0000111);
}
void Assembler::FLT_H(GPR rd, FPR rs1, FPR rs2) noexcept {
EmitRType(m_buffer, 0b1010010, rs2, rs1, 0b001, rd, 0b1010011);
}
void Assembler::FMADD_H(FPR rd, FPR rs1, FPR rs2, FPR rs3, RMode rmode) noexcept {
EmitR4Type(m_buffer, rs3, 0b10, rs2, rs1, rmode, rd, 0b1000011);
}
void Assembler::FMAX_H(FPR rd, FPR rs1, FPR rs2) noexcept {
EmitRType(m_buffer, 0b0010110, rs2, rs1, 0b001, rd, 0b1010011);
}
void Assembler::FMIN_H(FPR rd, FPR rs1, FPR rs2) noexcept {
EmitRType(m_buffer, 0b0010110, rs2, rs1, 0b000, rd, 0b1010011);
}
void Assembler::FMSUB_H(FPR rd, FPR rs1, FPR rs2, FPR rs3, RMode rmode) noexcept {
EmitR4Type(m_buffer, rs3, 0b10, rs2, rs1, rmode, rd, 0b1000111);
}
void Assembler::FMUL_H(FPR rd, FPR rs1, FPR rs2, RMode rmode) noexcept {
EmitRType(m_buffer, 0b0001010, rs2, rs1, rmode, rd, 0b1010011);
}
void Assembler::FMV_H_X(FPR rd, GPR rs1) noexcept {
EmitRType(m_buffer, 0b1111010, f0, rs1, 0b000, rd, 0b1010011);
}
void Assembler::FMV_X_H(GPR rd, FPR rs1) noexcept {
EmitRType(m_buffer, 0b1110010, f0, rs1, 0b000, rd, 0b1010011);
}
void Assembler::FNMADD_H(FPR rd, FPR rs1, FPR rs2, FPR rs3, RMode rmode) noexcept {
EmitR4Type(m_buffer, rs3, 0b10, rs2, rs1, rmode, rd, 0b1001111);
}
void Assembler::FNMSUB_H(FPR rd, FPR rs1, FPR rs2, FPR rs3, RMode rmode) noexcept {
EmitR4Type(m_buffer, rs3, 0b10, rs2, rs1, rmode, rd, 0b1001011);
}
void Assembler::FSGNJ_H(FPR rd, FPR rs1, FPR rs2) noexcept {
EmitRType(m_buffer, 0b0010010, rs2, rs1, 0b000, rd, 0b1010011);
}
void Assembler::FSGNJN_H(FPR rd, FPR rs1, FPR rs2) noexcept {
EmitRType(m_buffer, 0b0010010, rs2, rs1, 0b001, rd, 0b1010011);
}
void Assembler::FSGNJX_H(FPR rd, FPR rs1, FPR rs2) noexcept {
EmitRType(m_buffer, 0b0010010, rs2, rs1, 0b010, rd, 0b1010011);
}
void Assembler::FSH(FPR rs2, int32_t offset, GPR rs1) noexcept {
BISCUIT_ASSERT(IsValidSigned12BitImm(offset));
EmitSType(m_buffer, static_cast<uint32_t>(offset), rs2, rs1, 0b001, 0b0100111);
}
void Assembler::FSQRT_H(FPR rd, FPR rs1, RMode rmode) noexcept {
EmitRType(m_buffer, 0b0101110, f0, rs1, rmode, rd, 0b1010011);
}
void Assembler::FSUB_H(FPR rd, FPR rs1, FPR rs2, RMode rmode) noexcept {
EmitRType(m_buffer, 0b0000110, rs2, rs1, rmode, rd, 0b1010011);
}
// RV64Zfh Extension Instructions
void Assembler::FCVT_L_H(GPR rd, FPR rs1, RMode rmode) noexcept {
EmitRType(m_buffer, 0b1100010, f2, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011);
}
void Assembler::FCVT_LU_H(GPR rd, FPR rs1, RMode rmode) noexcept {
EmitRType(m_buffer, 0b1100010, f3, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011);
}
void Assembler::FCVT_H_L(FPR rd, GPR rs1, RMode rmode) noexcept {
EmitRType(m_buffer, 0b1101010, f2, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011);
}
void Assembler::FCVT_H_LU(FPR rd, GPR rs1, RMode rmode) noexcept {
EmitRType(m_buffer, 0b1101010, f3, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011);
}
// Zfa Extension Instructions
static void FLIImpl(CodeBuffer& buffer, uint32_t funct7, FPR rd, double value) noexcept {
static constexpr std::array fli_table{
0xBFF0000000000000ULL, // -1.0
0x0010000000000000ULL, // Minimum positive normal
0x3EF0000000000000ULL, // 1.0 * 2^-16
0x3F00000000000000ULL, // 1.0 * 2^-15
0x3F70000000000000ULL, // 1.0 * 2^-8
0x3F80000000000000ULL, // 1.0 * 2^-7
0x3FB0000000000000ULL, // 1.0 * 2^-4
0x3FC0000000000000ULL, // 1.0 * 2^-3
0x3FD0000000000000ULL, // 0.25
0x3FD4000000000000ULL, // 0.3125
0x3FD8000000000000ULL, // 0.375
0x3FDC000000000000ULL, // 0.4375
0x3FE0000000000000ULL, // 0.5
0x3FE4000000000000ULL, // 0.625
0x3FE8000000000000ULL, // 0.75
0x3FEC000000000000ULL, // 0.875
0x3FF0000000000000ULL, // 1.0
0x3FF4000000000000ULL, // 1.25
0x3FF8000000000000ULL, // 1.5
0x3FFC000000000000ULL, // 1.75
0x4000000000000000ULL, // 2.0
0x4004000000000000ULL, // 2.5
0x4008000000000000ULL, // 3
0x4010000000000000ULL, // 4
0x4020000000000000ULL, // 8
0x4030000000000000ULL, // 16
0x4060000000000000ULL, // 2^7
0x4070000000000000ULL, // 2^8
0x40E0000000000000ULL, // 2^15
0x40F0000000000000ULL, // 2^16
0x7FF0000000000000ULL, // +inf
0x7FF8000000000000ULL, // Canonical NaN
};
uint64_t ivalue{};
std::memcpy(&ivalue, &value, sizeof(uint64_t));
const auto iter = std::find_if(fli_table.cbegin(), fli_table.cend(), [ivalue](uint64_t entry) {
return entry == ivalue;
});
BISCUIT_ASSERT(iter != fli_table.cend());
const auto index = static_cast<uint32_t>(std::distance(fli_table.cbegin(), iter));
EmitRType(buffer, funct7, f1, GPR{index}, 0b000, rd, 0b1010011);
}
void Assembler::FLI_D(FPR rd, double value) noexcept {
FLIImpl(m_buffer, 0b1111001, rd, value);
}
void Assembler::FLI_H(FPR rd, double value) noexcept {
FLIImpl(m_buffer, 0b1111010, rd, value);
}
void Assembler::FLI_S(FPR rd, double value) noexcept {
FLIImpl(m_buffer, 0b1111000, rd, value);
}
void Assembler::FMINM_D(FPR rd, FPR rs1, FPR rs2) noexcept {
EmitRType(m_buffer, 0b0010101, rs2, rs1, 0b010, rd, 0b1010011);
}
void Assembler::FMINM_H(FPR rd, FPR rs1, FPR rs2) noexcept {
EmitRType(m_buffer, 0b0010110, rs2, rs1, 0b010, rd, 0b1010011);
}
void Assembler::FMINM_Q(FPR rd, FPR rs1, FPR rs2) noexcept {
EmitRType(m_buffer, 0b0010111, rs2, rs1, 0b010, rd, 0b1010011);
}
void Assembler::FMINM_S(FPR rd, FPR rs1, FPR rs2) noexcept {
EmitRType(m_buffer, 0b0010100, rs2, rs1, 0b010, rd, 0b1010011);
}
void Assembler::FMAXM_D(FPR rd, FPR rs1, FPR rs2) noexcept {
EmitRType(m_buffer, 0b0010101, rs2, rs1, 0b011, rd, 0b1010011);
}
void Assembler::FMAXM_H(FPR rd, FPR rs1, FPR rs2) noexcept {
EmitRType(m_buffer, 0b0010110, rs2, rs1, 0b011, rd, 0b1010011);
}
void Assembler::FMAXM_Q(FPR rd, FPR rs1, FPR rs2) noexcept {
EmitRType(m_buffer, 0b0010111, rs2, rs1, 0b011, rd, 0b1010011);
}
void Assembler::FMAXM_S(FPR rd, FPR rs1, FPR rs2) noexcept {
EmitRType(m_buffer, 0b0010100, rs2, rs1, 0b011, rd, 0b1010011);
}
void Assembler::FROUND_D(FPR rd, FPR rs1, RMode rmode) noexcept {
EmitRType(m_buffer, 0b0100001, f4, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011);
}
void Assembler::FROUND_H(FPR rd, FPR rs1, RMode rmode) noexcept {
EmitRType(m_buffer, 0b0100010, f4, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011);
}
void Assembler::FROUND_Q(FPR rd, FPR rs1, RMode rmode) noexcept {
EmitRType(m_buffer, 0b0100011, f4, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011);
}
void Assembler::FROUND_S(FPR rd, FPR rs1, RMode rmode) noexcept {
EmitRType(m_buffer, 0b0100000, f4, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011);
}
void Assembler::FROUNDNX_D(FPR rd, FPR rs1, RMode rmode) noexcept {
EmitRType(m_buffer, 0b0100001, f5, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011);
}
void Assembler::FROUNDNX_H(FPR rd, FPR rs1, RMode rmode) noexcept {
EmitRType(m_buffer, 0b0100010, f5, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011);
}
void Assembler::FROUNDNX_Q(FPR rd, FPR rs1, RMode rmode) noexcept {
EmitRType(m_buffer, 0b0100011, f5, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011);
}
void Assembler::FROUNDNX_S(FPR rd, FPR rs1, RMode rmode) noexcept {
EmitRType(m_buffer, 0b0100000, f5, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011);
}
void Assembler::FCVTMOD_W_D(GPR rd, FPR rs1) noexcept {
EmitRType(m_buffer, 0b1100001, f8, rs1, static_cast<uint32_t>(RMode::RTZ), rd, 0b1010011);
}
void Assembler::FMVH_X_D(GPR rd, FPR rs1) noexcept {
EmitRType(m_buffer, 0b1110001, f1, rs1, 0b000, rd, 0b1010011);
}
void Assembler::FMVH_X_Q(GPR rd, FPR rs1) noexcept {
EmitRType(m_buffer, 0b1110011, f1, rs1, 0b000, rd, 0b1010011);
}
void Assembler::FMVP_D_X(FPR rd, GPR rs1, GPR rs2) noexcept {
EmitRType(m_buffer, 0b1011001, rs2, rs1, 0b000, rd, 0b1010011);
}
void Assembler::FMVP_Q_X(FPR rd, GPR rs1, GPR rs2) noexcept {
EmitRType(m_buffer, 0b1011011, rs2, rs1, 0b000, rd, 0b1010011);
}
void Assembler::FLEQ_D(GPR rd, FPR rs1, FPR rs2) noexcept {
EmitRType(m_buffer, 0b1010001, rs2, rs1, 0b100, rd, 0b1010011);
}
void Assembler::FLTQ_D(GPR rd, FPR rs1, FPR rs2) noexcept {
EmitRType(m_buffer, 0b1010001, rs2, rs1, 0b101, rd, 0b1010011);
}
void Assembler::FLEQ_H(GPR rd, FPR rs1, FPR rs2) noexcept {
EmitRType(m_buffer, 0b1010010, rs2, rs1, 0b100, rd, 0b1010011);
}
void Assembler::FLTQ_H(GPR rd, FPR rs1, FPR rs2) noexcept {
EmitRType(m_buffer, 0b1010010, rs2, rs1, 0b101, rd, 0b1010011);
}
void Assembler::FLEQ_Q(GPR rd, FPR rs1, FPR rs2) noexcept {
EmitRType(m_buffer, 0b1010011, rs2, rs1, 0b100, rd, 0b1010011);
}
void Assembler::FLTQ_Q(GPR rd, FPR rs1, FPR rs2) noexcept {
EmitRType(m_buffer, 0b1010011, rs2, rs1, 0b101, rd, 0b1010011);
}
void Assembler::FLEQ_S(GPR rd, FPR rs1, FPR rs2) noexcept {
EmitRType(m_buffer, 0b1010000, rs2, rs1, 0b100, rd, 0b1010011);
}
void Assembler::FLTQ_S(GPR rd, FPR rs1, FPR rs2) noexcept {
EmitRType(m_buffer, 0b1010000, rs2, rs1, 0b101, rd, 0b1010011);
}
// Zfbfmin, Zvfbfmin, Zvfbfwma Extension Instructions
void Assembler::FCVT_BF16_S(FPR rd, FPR rs, RMode rmode) noexcept {
EmitRType(m_buffer, 0b0100010, f8, rs, static_cast<uint32_t>(rmode), rd, 0b1010011);
}
void Assembler::FCVT_S_BF16(FPR rd, FPR rs, RMode rmode) noexcept {
EmitRType(m_buffer, 0b0100000, f6, rs, static_cast<uint32_t>(rmode), rd, 0b1010011);
}
} // namespace biscuit

View file

@ -0,0 +1,244 @@
#pragma once
#include <biscuit/assert.hpp>
#include <biscuit/code_buffer.hpp>
#include <biscuit/registers.hpp>
#include <cstddef>
#include <cstdint>
// Generic internal utility header for various helper functions related
// to encoding instructions.
namespace biscuit {
// Determines if a value lies within the range of a 6-bit immediate.
[[nodiscard]] constexpr bool IsValidSigned6BitImm(ptrdiff_t value) {
return value >= -32 && value <= 31;
}
// S-type and I-type immediates are 12 bits in size
[[nodiscard]] constexpr bool IsValidSigned12BitImm(ptrdiff_t value) {
return value >= -2048 && value <= 2047;
}
// B-type immediates only provide -4KiB to +4KiB range branches.
[[nodiscard]] constexpr bool IsValidBTypeImm(ptrdiff_t value) {
return value >= -4096 && value <= 4095;
}
// J-type immediates only provide -1MiB to +1MiB range branches.
[[nodiscard]] constexpr bool IsValidJTypeImm(ptrdiff_t value) {
return value >= -0x80000 && value <= 0x7FFFF;
}
// CB-type immediates only provide -256B to +256B range branches.
[[nodiscard]] constexpr bool IsValidCBTypeImm(ptrdiff_t value) {
return value >= -256 && value <= 255;
}
// CJ-type immediates only provide -2KiB to +2KiB range branches.
[[nodiscard]] constexpr bool IsValidCJTypeImm(ptrdiff_t value) {
return IsValidSigned12BitImm(value);
}
// Determines whether or not the register fits in 3-bit compressed encoding.
[[nodiscard]] constexpr bool IsValid3BitCompressedReg(Register reg) {
const auto index = reg.Index();
return index >= 8 && index <= 15;
}
// Determines whether or not the given shift amount is valid for a compressed shift instruction
[[nodiscard]] constexpr bool IsValidCompressedShiftAmount(uint32_t shift) {
return shift > 0 && shift <= 64;
}
// Turns a compressed register into its encoding.
[[nodiscard]] constexpr uint32_t CompressedRegTo3BitEncoding(Register reg) {
return reg.Index() - 8;
}
// Transforms a regular value into an immediate encoded in a B-type instruction.
[[nodiscard]] constexpr uint32_t TransformToBTypeImm(uint32_t imm) {
// clang-format off
return ((imm & 0x07E0) << 20) |
((imm & 0x1000) << 19) |
((imm & 0x001E) << 7) |
((imm & 0x0800) >> 4);
// clang-format on
}
// Transforms a regular value into an immediate encoded in a J-type instruction.
[[nodiscard]] constexpr uint32_t TransformToJTypeImm(uint32_t imm) {
// clang-format off
return ((imm & 0x0FF000) >> 0) |
((imm & 0x000800) << 9) |
((imm & 0x0007FE) << 20) |
((imm & 0x100000) << 11);
// clang-format on
}
// Transforms a regular value into an immediate encoded in a CB-type instruction.
[[nodiscard]] constexpr uint32_t TransformToCBTypeImm(uint32_t imm) {
// clang-format off
return ((imm & 0x0C0) >> 1) |
((imm & 0x006) << 2) |
((imm & 0x020) >> 3) |
((imm & 0x018) << 7) |
((imm & 0x100) << 4);
// clang-format on
}
// Transforms a regular value into an immediate encoded in a CJ-type instruction.
[[nodiscard]] constexpr uint32_t TransformToCJTypeImm(uint32_t imm) {
// clang-format off
return ((imm & 0x800) << 1) |
((imm & 0x010) << 7) |
((imm & 0x300) << 1) |
((imm & 0x400) >> 2) |
((imm & 0x040) << 1) |
((imm & 0x080) >> 1) |
((imm & 0x00E) << 4) |
((imm & 0x020) >> 3);
// clang-format on
}
// Emits a B type RISC-V instruction. These consist of:
// imm[12|10:5] | rs2 | rs1 | funct3 | imm[4:1] | imm[11] | opcode
inline void EmitBType(CodeBuffer& buffer, uint32_t imm, GPR rs2, GPR rs1,
uint32_t funct3, uint32_t opcode) {
imm &= 0x1FFE;
buffer.Emit32(TransformToBTypeImm(imm) | (rs2.Index() << 20) | (rs1.Index() << 15) |
((funct3 & 0b111) << 12) | (opcode & 0x7F));
}
// Emits a I type RISC-V instruction. These consist of:
// imm[11:0] | rs1 | funct3 | rd | opcode
inline void EmitIType(CodeBuffer& buffer, uint32_t imm, Register rs1, uint32_t funct3,
Register rd, uint32_t opcode) {
imm &= 0xFFF;
buffer.Emit32((imm << 20) | (rs1.Index() << 15) | ((funct3 & 0b111) << 12) |
(rd.Index() << 7) | (opcode & 0x7F));
}
// Emits a J type RISC-V instruction. These consist of:
// imm[20|10:1|11|19:12] | rd | opcode
inline void EmitJType(CodeBuffer& buffer, uint32_t imm, GPR rd, uint32_t opcode) {
imm &= 0x1FFFFE;
buffer.Emit32(TransformToJTypeImm(imm) | rd.Index() << 7 | (opcode & 0x7F));
}
// Emits a R type RISC instruction. These consist of:
// funct7 | rs2 | rs1 | funct3 | rd | opcode
inline void EmitRType(CodeBuffer& buffer, uint32_t funct7, Register rs2, Register rs1,
uint32_t funct3, Register rd, uint32_t opcode) {
// clang-format off
const auto value = ((funct7 & 0xFF) << 25) |
(rs2.Index() << 20) |
(rs1.Index() << 15) |
((funct3 & 0b111) << 12) |
(rd.Index() << 7) |
(opcode & 0x7F);
// clang-format off
buffer.Emit32(value);
}
// Emits a R type RISC instruction. These consist of:
// funct7 | rs2 | rs1 | funct3 | rd | opcode
inline void EmitRType(CodeBuffer& buffer, uint32_t funct7, FPR rs2, FPR rs1, RMode funct3,
FPR rd, uint32_t opcode) {
EmitRType(buffer, funct7, rs2, rs1, static_cast<uint32_t>(funct3), rd, opcode);
}
// Emits a R4 type RISC instruction. These consist of:
// rs3 | funct2 | rs2 | rs1 | funct3 | rd | opcode
inline void EmitR4Type(CodeBuffer& buffer, FPR rs3, uint32_t funct2, FPR rs2, FPR rs1,
RMode funct3, FPR rd, uint32_t opcode) {
const auto reg_bits = (rs3.Index() << 27) | (rs2.Index() << 20) | (rs1.Index() << 15) | (rd.Index() << 7);
const auto funct_bits = ((funct2 & 0b11) << 25) | (static_cast<uint32_t>(funct3) << 12);
buffer.Emit32(reg_bits | funct_bits | (opcode & 0x7F));
}
// Emits a S type RISC-V instruction. These consist of:
// imm[11:5] | rs2 | rs1 | funct3 | imm[4:0] | opcode
inline void EmitSType(CodeBuffer& buffer, uint32_t imm, Register rs2, GPR rs1,
uint32_t funct3, uint32_t opcode) {
imm &= 0xFFF;
// clang-format off
const auto new_imm = ((imm & 0x01F) << 7) |
((imm & 0xFE0) << 20);
// clang-format on
buffer.Emit32(new_imm | (rs2.Index() << 20) | (rs1.Index() << 15) |
((funct3 & 0b111) << 12) | (opcode & 0x7F));
}
// Emits a U type RISC-V instruction. These consist of:
// imm[31:12] | rd | opcode
inline void EmitUType(CodeBuffer& buffer, uint32_t imm, GPR rd, uint32_t opcode) {
buffer.Emit32((imm & 0x000FFFFF) << 12 | rd.Index() << 7 | (opcode & 0x7F));
}
// Emits an atomic instruction.
inline void EmitAtomic(CodeBuffer& buffer, uint32_t funct5, Ordering ordering, GPR rs2, GPR rs1,
uint32_t funct3, GPR rd, uint32_t opcode) noexcept {
const auto funct7 = (funct5 << 2) | static_cast<uint32_t>(ordering);
EmitRType(buffer, funct7, rs2, rs1, funct3, rd, opcode);
}
// Emits a fence instruction
inline void EmitFENCE(CodeBuffer& buffer, uint32_t fm, FenceOrder pred, FenceOrder succ,
GPR rs, uint32_t funct3, GPR rd, uint32_t opcode) noexcept {
// clang-format off
buffer.Emit32(((fm & 0b1111) << 28) |
(static_cast<uint32_t>(pred) << 24) |
(static_cast<uint32_t>(succ) << 20) |
(rs.Index() << 15) |
((funct3 & 0b111) << 12) |
(rd.Index() << 7) |
(opcode & 0x7F));
// clang-format on
}
// Maybe OPs (see Zcmop/Zimop extension)
inline void EmitMOP_R(CodeBuffer& buffer, uint32_t op, GPR rd, GPR rs) {
constexpr auto base = 0x81C04073U;
const auto encoded_op = ((op & 0b11) << 20) | ((op & 0b1100) << 24) | (op & 0b10000) << 26;
buffer.Emit32(base | encoded_op | (rd.Index() << 7) | (rs.Index() << 15));
}
inline void EmitMOP_RR(CodeBuffer& buffer, uint32_t op, GPR rd, GPR rs1, GPR rs2) {
constexpr auto base = 0x82004073U;
const auto encoded_op = ((op & 0b11) << 26) | ((op & 0b100) << 28);
buffer.Emit32(base | encoded_op | (rd.Index() << 7) | (rs1.Index() << 15) | (rs2.Index() << 20));
}
inline void EmitCMOP(CodeBuffer& buffer, uint32_t op) {
constexpr auto base = 0x6081U;
const auto encoded_op = (op & 0b1110) << 7;
buffer.Emit16(base | encoded_op);
}
// Internal helpers for siloing away particular comparisons for behavior.
constexpr bool IsRV32(ArchFeature feature) {
return feature == ArchFeature::RV32;
}
constexpr bool IsRV64(ArchFeature feature) {
return feature == ArchFeature::RV64;
}
constexpr bool IsRV128(ArchFeature feature) {
return feature == ArchFeature::RV128;
}
constexpr bool IsRV32OrRV64(ArchFeature feature) {
return IsRV32(feature) || IsRV64(feature);
}
constexpr bool IsRV64OrRV128(ArchFeature feature) {
return IsRV64(feature) || IsRV128(feature);
}
} // namespace biscuit

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,111 @@
#include <biscuit/assert.hpp>
#include <biscuit/code_buffer.hpp>
#include <cstring>
#include <utility>
#ifdef BISCUIT_CODE_BUFFER_MMAP
#include <sys/mman.h>
#endif
namespace biscuit {
CodeBuffer::CodeBuffer(size_t capacity)
: m_capacity{capacity}, m_is_managed{true} {
if (capacity == 0) {
return;
}
#ifdef BISCUIT_CODE_BUFFER_MMAP
m_buffer = static_cast<uint8_t*>(mmap(nullptr, capacity,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS,
-1, 0));
BISCUIT_ASSERT(m_buffer != nullptr);
#else
m_buffer = new uint8_t[capacity]();
#endif
m_cursor = m_buffer;
}
CodeBuffer::CodeBuffer(uint8_t* buffer, size_t capacity)
: m_buffer{buffer}, m_cursor{buffer}, m_capacity{capacity} {
BISCUIT_ASSERT(buffer != nullptr);
}
CodeBuffer::CodeBuffer(CodeBuffer&& other) noexcept
: m_buffer{std::exchange(other.m_buffer, nullptr)}
, m_cursor{std::exchange(other.m_cursor, nullptr)}
, m_capacity{std::exchange(other.m_capacity, size_t{0})}
, m_is_managed{std::exchange(other.m_is_managed, false)} {}
CodeBuffer& CodeBuffer::operator=(CodeBuffer&& other) noexcept {
if (this == &other) {
return *this;
}
std::swap(m_buffer, other.m_buffer);
std::swap(m_cursor, other.m_cursor);
std::swap(m_capacity, other.m_capacity);
std::swap(m_is_managed, other.m_is_managed);
return *this;
}
CodeBuffer::~CodeBuffer() noexcept {
if (!m_is_managed) {
return;
}
#ifdef BISCUIT_CODE_BUFFER_MMAP
munmap(m_buffer, m_capacity);
#else
delete[] m_buffer;
#endif
}
void CodeBuffer::Grow(size_t new_capacity) {
BISCUIT_ASSERT(IsManaged());
// No-op, just return.
if (new_capacity <= m_capacity) {
return;
}
const auto cursor_offset = GetCursorOffset();
#ifdef BISCUIT_CODE_BUFFER_MMAP
auto* new_buffer = static_cast<uint8_t*>(mremap(m_buffer, m_capacity, new_capacity, MREMAP_MAYMOVE));
BISCUIT_ASSERT(new_buffer != nullptr);
#else
auto* new_buffer = new uint8_t[new_capacity]();
std::memcpy(new_buffer, m_buffer, m_capacity);
delete[] m_buffer;
#endif
m_buffer = new_buffer;
m_capacity = new_capacity;
m_cursor = m_buffer + cursor_offset;
}
void CodeBuffer::SetExecutable() {
#ifdef BISCUIT_CODE_BUFFER_MMAP
const auto result = mprotect(m_buffer, m_capacity, PROT_READ | PROT_EXEC);
BISCUIT_ASSERT(result == 0);
#else
// Unimplemented/Unnecessary for new
BISCUIT_ASSERT(false);
#endif
}
void CodeBuffer::SetWritable() {
#ifdef BISCUIT_CODE_BUFFER_MMAP
const auto result = mprotect(m_buffer, m_capacity, PROT_READ | PROT_WRITE);
BISCUIT_ASSERT(result == 0);
#else
// Unimplemented/Unnecessary for new
BISCUIT_ASSERT(false);
#endif
}
} // namespace biscuit

View file

@ -0,0 +1,428 @@
// Copyright (c), 2022, KNS Group LLC (YADRO)
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
#include <biscuit/assert.hpp>
#include <biscuit/cpuinfo.hpp>
#if defined(__linux__) && defined(__riscv)
#include <asm/hwcap.h>
#include <sys/auxv.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <utility>
#ifdef SYS_riscv_hwprobe
#include <asm/hwprobe.h>
#endif
#endif
#ifndef RISCV_HWPROBE_BASE_BEHAVIOR_IMA
#define RISCV_HWPROBE_BASE_BEHAVIOR_IMA (1ULL << 0)
#endif
#ifndef RISCV_HWPROBE_IMA_FD
#define RISCV_HWPROBE_IMA_FD (1ULL << 0)
#endif
#ifndef RISCV_HWPROBE_IMA_C
#define RISCV_HWPROBE_IMA_C (1ULL << 1)
#endif
#ifndef RISCV_HWPROBE_IMA_V
#define RISCV_HWPROBE_IMA_V (1ULL << 2)
#endif
#ifndef RISCV_HWPROBE_EXT_ZBA
#define RISCV_HWPROBE_EXT_ZBA (1ULL << 3)
#endif
#ifndef RISCV_HWPROBE_EXT_ZBB
#define RISCV_HWPROBE_EXT_ZBB (1ULL << 4)
#endif
#ifndef RISCV_HWPROBE_EXT_ZBS
#define RISCV_HWPROBE_EXT_ZBS (1ULL << 5)
#endif
#ifndef RISCV_HWPROBE_EXT_ZICBOZ
#define RISCV_HWPROBE_EXT_ZICBOZ (1ULL << 6)
#endif
#ifndef RISCV_HWPROBE_EXT_ZBC
#define RISCV_HWPROBE_EXT_ZBC (1ULL << 7)
#endif
#ifndef RISCV_HWPROBE_EXT_ZBKB
#define RISCV_HWPROBE_EXT_ZBKB (1ULL << 8)
#endif
#ifndef RISCV_HWPROBE_EXT_ZBKC
#define RISCV_HWPROBE_EXT_ZBKC (1ULL << 9)
#endif
#ifndef RISCV_HWPROBE_EXT_ZBKX
#define RISCV_HWPROBE_EXT_ZBKX (1ULL << 10)
#endif
#ifndef RISCV_HWPROBE_EXT_ZKND
#define RISCV_HWPROBE_EXT_ZKND (1ULL << 11)
#endif
#ifndef RISCV_HWPROBE_EXT_ZKNE
#define RISCV_HWPROBE_EXT_ZKNE (1ULL << 12)
#endif
#ifndef RISCV_HWPROBE_EXT_ZKNH
#define RISCV_HWPROBE_EXT_ZKNH (1ULL << 13)
#endif
#ifndef RISCV_HWPROBE_EXT_ZKSED
#define RISCV_HWPROBE_EXT_ZKSED (1ULL << 14)
#endif
#ifndef RISCV_HWPROBE_EXT_ZKSH
#define RISCV_HWPROBE_EXT_ZKSH (1ULL << 15)
#endif
#ifndef RISCV_HWPROBE_EXT_ZKT
#define RISCV_HWPROBE_EXT_ZKT (1ULL << 16)
#endif
#ifndef RISCV_HWPROBE_EXT_ZVBB
#define RISCV_HWPROBE_EXT_ZVBB (1ULL << 17)
#endif
#ifndef RISCV_HWPROBE_EXT_ZVBC
#define RISCV_HWPROBE_EXT_ZVBC (1ULL << 18)
#endif
#ifndef RISCV_HWPROBE_EXT_ZVKB
#define RISCV_HWPROBE_EXT_ZVKB (1ULL << 19)
#endif
#ifndef RISCV_HWPROBE_EXT_ZVKG
#define RISCV_HWPROBE_EXT_ZVKG (1ULL << 20)
#endif
#ifndef RISCV_HWPROBE_EXT_ZVKNED
#define RISCV_HWPROBE_EXT_ZVKNED (1ULL << 21)
#endif
#ifndef RISCV_HWPROBE_EXT_ZVKNHA
#define RISCV_HWPROBE_EXT_ZVKNHA (1ULL << 22)
#endif
#ifndef RISCV_HWPROBE_EXT_ZVKNHB
#define RISCV_HWPROBE_EXT_ZVKNHB (1ULL << 23)
#endif
#ifndef RISCV_HWPROBE_EXT_ZVKSED
#define RISCV_HWPROBE_EXT_ZVKSED (1ULL << 24)
#endif
#ifndef RISCV_HWPROBE_EXT_ZVKSH
#define RISCV_HWPROBE_EXT_ZVKSH (1ULL << 25)
#endif
#ifndef RISCV_HWPROBE_EXT_ZVKT
#define RISCV_HWPROBE_EXT_ZVKT (1ULL << 26)
#endif
#ifndef RISCV_HWPROBE_EXT_ZFH
#define RISCV_HWPROBE_EXT_ZFH (1ULL << 27)
#endif
#ifndef RISCV_HWPROBE_EXT_ZFHMIN
#define RISCV_HWPROBE_EXT_ZFHMIN (1ULL << 28)
#endif
#ifndef RISCV_HWPROBE_EXT_ZIHINTNTL
#define RISCV_HWPROBE_EXT_ZIHINTNTL (1ULL << 29)
#endif
#ifndef RISCV_HWPROBE_EXT_ZVFH
#define RISCV_HWPROBE_EXT_ZVFH (1ULL << 30)
#endif
#ifndef RISCV_HWPROBE_EXT_ZVFHMIN
#define RISCV_HWPROBE_EXT_ZVFHMIN (1ULL << 31)
#endif
#ifndef RISCV_HWPROBE_EXT_ZFA
#define RISCV_HWPROBE_EXT_ZFA (1ULL << 32)
#endif
#ifndef RISCV_HWPROBE_EXT_ZTSO
#define RISCV_HWPROBE_EXT_ZTSO (1ULL << 33)
#endif
#ifndef RISCV_HWPROBE_EXT_ZACAS
#define RISCV_HWPROBE_EXT_ZACAS (1ULL << 34)
#endif
#ifndef RISCV_HWPROBE_EXT_ZICOND
#define RISCV_HWPROBE_EXT_ZICOND (1ULL << 35)
#endif
#ifndef RISCV_HWPROBE_EXT_ZIHINTPAUSE
#define RISCV_HWPROBE_EXT_ZIHINTPAUSE (1ULL << 36)
#endif
#ifndef RISCV_HWPROBE_EXT_ZVE32X
#define RISCV_HWPROBE_EXT_ZVE32X (1ULL << 37)
#endif
#ifndef RISCV_HWPROBE_EXT_ZVE32F
#define RISCV_HWPROBE_EXT_ZVE32F (1ULL << 38)
#endif
#ifndef RISCV_HWPROBE_EXT_ZVE64X
#define RISCV_HWPROBE_EXT_ZVE64X (1ULL << 39)
#endif
#ifndef RISCV_HWPROBE_EXT_ZVE64F
#define RISCV_HWPROBE_EXT_ZVE64F (1ULL << 40)
#endif
#ifndef RISCV_HWPROBE_EXT_ZVE64D
#define RISCV_HWPROBE_EXT_ZVE64D (1ULL << 41)
#endif
#ifndef RISCV_HWPROBE_EXT_ZIMOP
#define RISCV_HWPROBE_EXT_ZIMOP (1ULL << 42)
#endif
#ifndef RISCV_HWPROBE_EXT_ZCA
#define RISCV_HWPROBE_EXT_ZCA (1ULL << 43)
#endif
#ifndef RISCV_HWPROBE_EXT_ZCB
#define RISCV_HWPROBE_EXT_ZCB (1ULL << 44)
#endif
#ifndef RISCV_HWPROBE_EXT_ZCD
#define RISCV_HWPROBE_EXT_ZCD (1ULL << 45)
#endif
#ifndef RISCV_HWPROBE_EXT_ZCF
#define RISCV_HWPROBE_EXT_ZCF (1ULL << 46)
#endif
#ifndef RISCV_HWPROBE_EXT_ZCMOP
#define RISCV_HWPROBE_EXT_ZCMOP (1ULL << 47)
#endif
#ifndef RISCV_HWPROBE_EXT_ZAWRS
#define RISCV_HWPROBE_EXT_ZAWRS (1ULL << 48)
#endif
#ifndef COMPAT_HWCAP_ISA_I
#define COMPAT_HWCAP_ISA_I (1U << ('I' - 'A'))
#endif
#ifndef COMPAT_HWCAP_ISA_M
#define COMPAT_HWCAP_ISA_M (1U << ('M' - 'A'))
#endif
#ifndef COMPAT_HWCAP_ISA_A
#define COMPAT_HWCAP_ISA_A (1U << ('A' - 'A'))
#endif
#ifndef COMPAT_HWCAP_ISA_F
#define COMPAT_HWCAP_ISA_F (1U << ('F' - 'A'))
#endif
#ifndef COMPAT_HWCAP_ISA_D
#define COMPAT_HWCAP_ISA_D (1U << ('D' - 'A'))
#endif
#ifndef COMPAT_HWCAP_ISA_C
#define COMPAT_HWCAP_ISA_C (1U << ('C' - 'A'))
#endif
#ifndef COMPAT_HWCAP_ISA_V
#define COMPAT_HWCAP_ISA_V (1U << ('V' - 'A'))
#endif
namespace biscuit {
bool CPUInfo::Has(RISCVExtension extension) const {
#if defined(__linux__) && defined(__riscv)
static const auto [ima, features0] = []() {
#ifdef SYS_riscv_hwprobe
riscv_hwprobe pairs[] = {
{RISCV_HWPROBE_KEY_BASE_BEHAVIOR, 0},
{RISCV_HWPROBE_KEY_IMA_EXT_0, 0},
};
long result = syscall(SYS_riscv_hwprobe, pairs, std::size(pairs), 0, nullptr, 0);
uint64_t ima = pairs[0].value;
uint64_t features0 = pairs[1].value;
#else
long result = -1;
uint64_t ima = 0;
uint64_t features0 = 0;
#endif
if (result < 0) {
// Older kernel versions don't support this syscall.
// Fallback to an older implementation
static const uint64_t features = getauxval(AT_HWCAP) & (
COMPAT_HWCAP_ISA_I |
COMPAT_HWCAP_ISA_M |
COMPAT_HWCAP_ISA_A |
COMPAT_HWCAP_ISA_F |
COMPAT_HWCAP_ISA_D |
COMPAT_HWCAP_ISA_C |
COMPAT_HWCAP_ISA_V
);
if ((features & (COMPAT_HWCAP_ISA_I | COMPAT_HWCAP_ISA_M | COMPAT_HWCAP_ISA_A)) != 0) {
ima = RISCV_HWPROBE_BASE_BEHAVIOR_IMA;
}
if ((features & (COMPAT_HWCAP_ISA_F | COMPAT_HWCAP_ISA_D)) != 0) {
features0 |= RISCV_HWPROBE_IMA_FD;
}
if ((features & COMPAT_HWCAP_ISA_C) != 0) {
features0 |= RISCV_HWPROBE_IMA_C;
}
if ((features & COMPAT_HWCAP_ISA_V) != 0) {
features0 |= RISCV_HWPROBE_IMA_V;
}
}
return std::make_pair(ima, features0);
}();
#else
static const uint64_t ima = 0;
static const uint64_t features0 = 0;
#endif
switch (extension) {
case RISCVExtension::I:
case RISCVExtension::M:
case RISCVExtension::A: {
return ima == RISCV_HWPROBE_BASE_BEHAVIOR_IMA;
}
case RISCVExtension::F:
case RISCVExtension::D:
return (features0 & RISCV_HWPROBE_IMA_FD) != 0;
case RISCVExtension::C:
return (features0 & RISCV_HWPROBE_IMA_C) != 0;
case RISCVExtension::V:
return (features0 & RISCV_HWPROBE_IMA_V) != 0;
case RISCVExtension::Zba:
return (features0 & RISCV_HWPROBE_EXT_ZBA) != 0;
case RISCVExtension::Zbb:
return (features0 & RISCV_HWPROBE_EXT_ZBB) != 0;
case RISCVExtension::Zbs:
return (features0 & RISCV_HWPROBE_EXT_ZBS) != 0;
case RISCVExtension::Zicboz:
return (features0 & RISCV_HWPROBE_EXT_ZICBOZ) != 0;
case RISCVExtension::Zbc:
return (features0 & RISCV_HWPROBE_EXT_ZBC) != 0;
case RISCVExtension::Zbkb:
return (features0 & RISCV_HWPROBE_EXT_ZBKB) != 0;
case RISCVExtension::Zbkc:
return (features0 & RISCV_HWPROBE_EXT_ZBKC) != 0;
case RISCVExtension::Zbkx:
return (features0 & RISCV_HWPROBE_EXT_ZBKX) != 0;
case RISCVExtension::Zknd:
return (features0 & RISCV_HWPROBE_EXT_ZKND) != 0;
case RISCVExtension::Zkne:
return (features0 & RISCV_HWPROBE_EXT_ZKNE) != 0;
case RISCVExtension::Zknh:
return (features0 & RISCV_HWPROBE_EXT_ZKNH) != 0;
case RISCVExtension::Zksed:
return (features0 & RISCV_HWPROBE_EXT_ZKSED) != 0;
case RISCVExtension::Zksh:
return (features0 & RISCV_HWPROBE_EXT_ZKSH) != 0;
case RISCVExtension::Zkt:
return (features0 & RISCV_HWPROBE_EXT_ZKT) != 0;
case RISCVExtension::Zvbb:
return (features0 & RISCV_HWPROBE_EXT_ZVBB) != 0;
case RISCVExtension::Zvbc:
return (features0 & RISCV_HWPROBE_EXT_ZVBC) != 0;
case RISCVExtension::Zvkb:
return (features0 & RISCV_HWPROBE_EXT_ZVKB) != 0;
case RISCVExtension::Zvkg:
return (features0 & RISCV_HWPROBE_EXT_ZVKG) != 0;
case RISCVExtension::Zvkned:
return (features0 & RISCV_HWPROBE_EXT_ZVKNED) != 0;
case RISCVExtension::Zvknha:
return (features0 & RISCV_HWPROBE_EXT_ZVKNHA) != 0;
case RISCVExtension::Zvknhb:
return (features0 & RISCV_HWPROBE_EXT_ZVKNHB) != 0;
case RISCVExtension::Zvksed:
return (features0 & RISCV_HWPROBE_EXT_ZVKSED) != 0;
case RISCVExtension::Zvksh:
return (features0 & RISCV_HWPROBE_EXT_ZVKSH) != 0;
case RISCVExtension::Zvkt:
return (features0 & RISCV_HWPROBE_EXT_ZVKT) != 0;
case RISCVExtension::Zfh:
return (features0 & RISCV_HWPROBE_EXT_ZFH) != 0;
case RISCVExtension::Zfhmin:
return (features0 & RISCV_HWPROBE_EXT_ZFHMIN) != 0;
case RISCVExtension::Zihintntl:
return (features0 & RISCV_HWPROBE_EXT_ZIHINTNTL) != 0;
case RISCVExtension::Zvfh:
return (features0 & RISCV_HWPROBE_EXT_ZVFH) != 0;
case RISCVExtension::Zvfhmin:
return (features0 & RISCV_HWPROBE_EXT_ZVFHMIN) != 0;
case RISCVExtension::Zfa:
return (features0 & RISCV_HWPROBE_EXT_ZFA) != 0;
case RISCVExtension::Ztso:
return (features0 & RISCV_HWPROBE_EXT_ZTSO) != 0;
case RISCVExtension::Zacas:
return (features0 & RISCV_HWPROBE_EXT_ZACAS) != 0;
case RISCVExtension::Zicond:
return (features0 & RISCV_HWPROBE_EXT_ZICOND) != 0;
case RISCVExtension::Zihintpause:
return (features0 & RISCV_HWPROBE_EXT_ZIHINTPAUSE) != 0;
case RISCVExtension::Zve32f:
return (features0 & RISCV_HWPROBE_EXT_ZVE32F) != 0;
case RISCVExtension::Zve32x:
return (features0 & RISCV_HWPROBE_EXT_ZVE32X) != 0;
case RISCVExtension::Zve64d:
return (features0 & RISCV_HWPROBE_EXT_ZVE64D) != 0;
case RISCVExtension::Zve64f:
return (features0 & RISCV_HWPROBE_EXT_ZVE64F) != 0;
case RISCVExtension::Zve64x:
return (features0 & RISCV_HWPROBE_EXT_ZVE64X) != 0;
case RISCVExtension::Zimop:
return (features0 & RISCV_HWPROBE_EXT_ZIMOP) != 0;
case RISCVExtension::Zca:
return (features0 & RISCV_HWPROBE_EXT_ZCA) != 0;
case RISCVExtension::Zcb:
return (features0 & RISCV_HWPROBE_EXT_ZCB) != 0;
case RISCVExtension::Zcd:
return (features0 & RISCV_HWPROBE_EXT_ZCD) != 0;
case RISCVExtension::Zcf:
return (features0 & RISCV_HWPROBE_EXT_ZCF) != 0;
case RISCVExtension::Zcmop:
return (features0 & RISCV_HWPROBE_EXT_ZCMOP) != 0;
case RISCVExtension::Zawrs:
return (features0 & RISCV_HWPROBE_EXT_ZAWRS) != 0;
}
return false;
}
uint32_t CPUInfo::GetVlenb() const {
if(Has(RISCVExtension::V)) {
static CSRReader<CSR::VLenb> csrReader;
const static auto getVLEN = csrReader.GetCode<uint32_t (*)()>();
return getVLEN();
}
return 0;
}
} // namespace biscuit

View file

@ -0,0 +1,79 @@
project(biscuit_tests)
add_executable(${PROJECT_NAME}
src/assembler_bfloat_tests.cpp
src/assembler_branch_tests.cpp
src/assembler_cfi_tests.cpp
src/assembler_cmo_tests.cpp
src/assembler_privileged_tests.cpp
src/assembler_rv32i_tests.cpp
src/assembler_rv64i_tests.cpp
src/assembler_rva_tests.cpp
src/assembler_rvb_tests.cpp
src/assembler_rvc_tests.cpp
src/assembler_rvd_tests.cpp
src/assembler_rvf_tests.cpp
src/assembler_rvk_tests.cpp
src/assembler_rvm_tests.cpp
src/assembler_rvq_tests.cpp
src/assembler_rvv_tests.cpp
src/assembler_vector_crypto_tests.cpp
src/assembler_xthead_tests.cpp
src/assembler_zabha_tests.cpp
src/assembler_zacas_tests.cpp
src/assembler_zawrs_tests.cpp
src/assembler_zc_tests.cpp
src/assembler_zfa_tests.cpp
src/assembler_zicond_tests.cpp
src/assembler_zicsr_tests.cpp
src/assembler_zihintntl_tests.cpp
src/main.cpp
src/assembler_test_utils.hpp
)
target_include_directories(${PROJECT_NAME}
PRIVATE
externals/
)
target_link_libraries(${PROJECT_NAME}
PRIVATE
biscuit
)
target_compile_features(${PROJECT_NAME}
PRIVATE
cxx_std_20
)
if (MSVC)
target_compile_options(${PROJECT_NAME}
PRIVATE
/MP
/Zi
/Zo
/permissive-
/EHsc
/utf-8
/volatile:iso
/Zc:externConstexpr
/Zc:inline
/Zc:throwingNew
# Warnings
/W4
/we4062 # enumerator 'identifier' in a switch of enum 'enumeration' is not handled
/we4101 # 'identifier': unreferenced local variable
/we4265 # 'class': class has virtual functions, but destructor is not virtual
/we4388 # signed/unsigned mismatch
/we4547 # 'operator' : operator before comma has no effect; expected operator with side-effect
/we4549 # 'operator1': operator before comma has no effect; did you intend 'operator2'?
/we4555 # Expression has no effect; expected expression with side-effect
/we4715 # 'function': not all control paths return a value
/we4834 # Discarding return value of function with 'nodiscard' attribute
/we5038 # data member 'member1' will be initialized after data member 'member2'
)
endif()
add_test(biscuit_tests_ctest ${PROJECT_NAME})

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,95 @@
#include <catch/catch.hpp>
#include <biscuit/assembler.hpp>
#include "assembler_test_utils.hpp"
using namespace biscuit;
TEST_CASE("FCVT.BF16.S", "[Zfbfmin]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FCVT_BF16_S(f31, f7, RMode::RNE);
REQUIRE(value == 0x44838FD3);
as.RewindBuffer();
as.FCVT_BF16_S(f31, f7, RMode::RMM);
REQUIRE(value == 0x4483CFD3);
as.RewindBuffer();
as.FCVT_BF16_S(f31, f7, RMode::DYN);
REQUIRE(value == 0x4483FFD3);
}
TEST_CASE("FCVT.S.BF16", "[Zfbfmin]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FCVT_S_BF16(f31, f7, RMode::RNE);
REQUIRE(value == 0x40638FD3);
as.RewindBuffer();
as.FCVT_S_BF16(f31, f7, RMode::RMM);
REQUIRE(value == 0x4063CFD3);
as.RewindBuffer();
as.FCVT_S_BF16(f31, f7, RMode::DYN);
REQUIRE(value == 0x4063FFD3);
}
TEST_CASE("VFNCVTBF16.F.F.W", "[Zvfbfmin]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.VFNCVTBF16_F_F_W(v31, v7, VecMask::Yes);
REQUIRE(value == 0x487E9FD7);
as.RewindBuffer();
as.VFNCVTBF16_F_F_W(v31, v7, VecMask::No);
REQUIRE(value == 0x4A7E9FD7);
}
TEST_CASE("VFWCVTBF16.F.F.V", "[Zvfbfmin]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.VFWCVTBF16_F_F_V(v31, v7, VecMask::Yes);
REQUIRE(value == 0x48769FD7);
as.RewindBuffer();
as.VFWCVTBF16_F_F_V(v31, v7, VecMask::No);
REQUIRE(value == 0x4A769FD7);
}
TEST_CASE("VFWMACCBF16.VF", "[Zvfbfwma]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.VFWMACCBF16(v31, f7, v20, VecMask::Yes);
REQUIRE(value == 0xED43DFD7);
as.RewindBuffer();
as.VFWMACCBF16(v31, f7, v20, VecMask::No);
REQUIRE(value == 0xEF43DFD7);
}
TEST_CASE("VFWMACCBF16.VV", "[Zvfbfwma]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.VFWMACCBF16(v31, v7, v20, VecMask::Yes);
REQUIRE(value == 0xED439FD7);
as.RewindBuffer();
as.VFWMACCBF16(v31, v7, v20, VecMask::No);
REQUIRE(value == 0xEF439FD7);
}

View file

@ -0,0 +1,105 @@
#include <catch/catch.hpp>
#include <array>
#include <biscuit/assembler.hpp>
#include "assembler_test_utils.hpp"
using namespace biscuit;
TEST_CASE("Branch to Self", "[branch]") {
uint32_t data;
auto as = MakeAssembler32(data);
// Simple branch to self with a jump instruction.
{
Label label;
as.Bind(&label);
as.J(&label);
REQUIRE(data == 0x0000006F);
}
as.RewindBuffer();
// Simple branch to self with a compressed jump instruction.
{
Label label;
as.Bind(&label);
as.C_J(&label);
REQUIRE((data & 0xFFFF) == 0xA001);
}
as.RewindBuffer();
// Simple branch to self with a conditional branch instruction.
{
Label label;
as.Bind(&label);
as.BNE(x3, x4, &label);
REQUIRE(data == 0x00419063);
}
as.RewindBuffer();
// Simple branch to self with a compressed branch instruction.
{
Label label;
as.Bind(&label);
as.C_BNEZ(x15, &label);
REQUIRE((data & 0xFFFF) == 0xE381);
}
}
TEST_CASE("Branch with Instructions Between", "[branch]") {
std::array<uint32_t, 20> data{};
auto as = MakeAssembler32(data);
// Simple branch backward
{
Label label;
as.Bind(&label);
as.ADD(x1, x2, x3);
as.SUB(x2, x4, x3);
as.J(&label);
REQUIRE(data[2] == 0xFF9FF06F);
}
as.RewindBuffer();
data.fill(0);
// Simple branch forward
{
Label label;
as.J(&label);
as.ADD(x1, x2, x3);
as.SUB(x2, x4, x3);
as.Bind(&label);
REQUIRE(data[0] == 0x00C0006F);
}
as.RewindBuffer();
data.fill(0);
// Simple branch backward (compressed)
{
Label label;
as.Bind(&label);
as.ADD(x1, x2, x3);
as.SUB(x2, x4, x3);
as.C_J(&label);
REQUIRE((data[2] & 0xFFFF) == 0xBFC5);
}
as.RewindBuffer();
data.fill(0);
// Simple branch forward (compressed)
{
Label label;
as.C_J(&label);
as.ADD(x1, x2, x3);
as.SUB(x2, x4, x3);
as.Bind(&label);
REQUIRE((data[0] & 0xFFFF) == 0xA0A1);
}
}

View file

@ -0,0 +1,125 @@
#include <catch/catch.hpp>
#include <biscuit/assembler.hpp>
#include "assembler_test_utils.hpp"
using namespace biscuit;
TEST_CASE("SSAMOSWAP.D", "[Zicfiss]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.SSAMOSWAP_D(Ordering::None, x31, x7, x15);
REQUIRE(value == 0x4877BFAF);
as.RewindBuffer();
as.SSAMOSWAP_D(Ordering::AQ, x31, x7, x15);
REQUIRE(value == 0x4C77BFAF);
as.RewindBuffer();
as.SSAMOSWAP_D(Ordering::RL, x31, x7, x15);
REQUIRE(value == 0x4A77BFAF);
as.RewindBuffer();
as.SSAMOSWAP_D(Ordering::AQRL, x31, x7, x15);
REQUIRE(value == 0x4E77BFAF);
}
TEST_CASE("SSAMOSWAP.W", "[Zicfiss]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.SSAMOSWAP_W(Ordering::None, x31, x7, x15);
REQUIRE(value == 0x4877AFAF);
as.RewindBuffer();
as.SSAMOSWAP_W(Ordering::AQ, x31, x7, x15);
REQUIRE(value == 0x4C77AFAF);
as.RewindBuffer();
as.SSAMOSWAP_W(Ordering::RL, x31, x7, x15);
REQUIRE(value == 0x4A77AFAF);
as.RewindBuffer();
as.SSAMOSWAP_W(Ordering::AQRL, x31, x7, x15);
REQUIRE(value == 0x4E77AFAF);
}
TEST_CASE("SSRDP", "[Zicfiss]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
for (uint32_t i = 1; i <= 31; i++) {
as.SSRDP(GPR{i});
REQUIRE(value == (0xCDC04073U | (i << 7)));
as.RewindBuffer();
}
}
TEST_CASE("SSPOPCHK", "[Zicfiss]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.SSPOPCHK(x1);
REQUIRE(value == 0xCDC0C073);
as.RewindBuffer();
as.SSPOPCHK(x5);
REQUIRE(value == 0xCDC2C073);
}
TEST_CASE("C.SSPOPCHK", "[Zicfiss]") {
uint16_t value = 0;
auto as = MakeAssembler32(value);
as.C_SSPOPCHK();
REQUIRE(value == 0x6281U);
}
TEST_CASE("SSPUSH", "[Zicfiss]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.SSPUSH(x1);
REQUIRE(value == 0xCE104073);
as.RewindBuffer();
as.SSPUSH(x5);
REQUIRE(value == 0xCE504073);
}
TEST_CASE("C.SSPUSH", "[Zicfiss]") {
uint16_t value = 0;
auto as = MakeAssembler32(value);
as.C_SSPUSH();
REQUIRE(value == 0x6081U);
}
TEST_CASE("LPAD", "[Zicfilp]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.LPAD(-1);
REQUIRE(value == 0xFFFFF017);
as.RewindBuffer();
as.LPAD(0);
REQUIRE(value == 0x00000017);
as.RewindBuffer();
as.LPAD(0x00FF00FF);
REQUIRE(value == 0xF00FF017);
}

View file

@ -0,0 +1,113 @@
#include <catch/catch.hpp>
#include <biscuit/assembler.hpp>
#include "assembler_test_utils.hpp"
using namespace biscuit;
TEST_CASE("CBO.CLEAN", "[cmo]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.CBO_CLEAN(x0);
REQUIRE(value == 0x0010200F);
as.RewindBuffer();
as.CBO_CLEAN(x31);
REQUIRE(value == 0x001FA00F);
}
TEST_CASE("CBO.FLUSH", "[cmo]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.CBO_FLUSH(x0);
REQUIRE(value == 0x0020200F);
as.RewindBuffer();
as.CBO_FLUSH(x31);
REQUIRE(value == 0x002FA00F);
}
TEST_CASE("CBO.INVAL", "[cmo]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.CBO_INVAL(x0);
REQUIRE(value == 0x0000200F);
as.RewindBuffer();
as.CBO_INVAL(x31);
REQUIRE(value == 0x000FA00F);
}
TEST_CASE("CBO.ZERO", "[cmo]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.CBO_ZERO(x0);
REQUIRE(value == 0x0040200F);
as.RewindBuffer();
as.CBO_ZERO(x31);
REQUIRE(value == 0x004FA00F);
}
TEST_CASE("PREFETCH.I", "[cmo]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.PREFETCH_I(x0);
REQUIRE(value == 0x00006013);
as.RewindBuffer();
as.PREFETCH_I(x31, 2016);
REQUIRE(value == 0x7E0FE013);
as.RewindBuffer();
as.PREFETCH_I(x31, -2016);
REQUIRE(value == 0x820FE013);
}
TEST_CASE("PREFETCH.R", "[cmo]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.PREFETCH_R(x0);
REQUIRE(value == 0x00106013);
as.RewindBuffer();
as.PREFETCH_R(x31, 2016);
REQUIRE(value == 0x7E1FE013);
as.RewindBuffer();
as.PREFETCH_R(x31, -2016);
REQUIRE(value == 0x821FE013);
}
TEST_CASE("PREFETCH.W", "[cmo]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.PREFETCH_W(x0);
REQUIRE(value == 0x00306013);
as.RewindBuffer();
as.PREFETCH_W(x31, 2016);
REQUIRE(value == 0x7E3FE013);
as.RewindBuffer();
as.PREFETCH_W(x31, -2016);
REQUIRE(value == 0x823FE013);
}

View file

@ -0,0 +1,310 @@
#include <catch/catch.hpp>
#include <biscuit/assembler.hpp>
#include "assembler_test_utils.hpp"
using namespace biscuit;
TEST_CASE("HFENCE.VVMA", "[rvpriv]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.HFENCE_VVMA(x0, x0);
REQUIRE(value == 0x22000073);
as.RewindBuffer();
as.HFENCE_VVMA(x15, x15);
REQUIRE(value == 0x22F78073);
}
TEST_CASE("HFENCE.GVMA", "[rvpriv]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.HFENCE_GVMA(x0, x0);
REQUIRE(value == 0x62000073);
as.RewindBuffer();
as.HFENCE_GVMA(x15, x15);
REQUIRE(value == 0x62F78073);
}
TEST_CASE("HINVAL.VVMA", "[rvpriv]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.HINVAL_VVMA(x0, x0);
REQUIRE(value == 0x26000073);
as.RewindBuffer();
as.HINVAL_VVMA(x15, x15);
REQUIRE(value == 0x26F78073);
}
TEST_CASE("HINVAL.GVMA", "[rvpriv]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.HINVAL_GVMA(x0, x0);
REQUIRE(value == 0x66000073);
as.RewindBuffer();
as.HINVAL_GVMA(x15, x15);
REQUIRE(value == 0x66F78073);
}
TEST_CASE("HLV.B", "[rvpriv]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.HLV_B(x0, x0);
REQUIRE(value == 0x60004073);
as.RewindBuffer();
as.HLV_B(x15, x14);
REQUIRE(value == 0x600747F3);
}
TEST_CASE("HLV.BU", "[rvpriv]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.HLV_BU(x0, x0);
REQUIRE(value == 0x60104073);
as.RewindBuffer();
as.HLV_BU(x15, x14);
REQUIRE(value == 0x601747F3);
}
TEST_CASE("HLV.D", "[rvpriv]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.HLV_D(x0, x0);
REQUIRE(value == 0x6C004073);
as.RewindBuffer();
as.HLV_D(x15, x14);
REQUIRE(value == 0x6C0747F3);
}
TEST_CASE("HLV.H", "[rvpriv]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.HLV_H(x0, x0);
REQUIRE(value == 0x64004073);
as.RewindBuffer();
as.HLV_H(x15, x14);
REQUIRE(value == 0x640747F3);
}
TEST_CASE("HLV.HU", "[rvpriv]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.HLV_HU(x0, x0);
REQUIRE(value == 0x64104073);
as.RewindBuffer();
as.HLV_HU(x15, x14);
REQUIRE(value == 0x641747F3);
}
TEST_CASE("HLV.W", "[rvpriv]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.HLV_W(x0, x0);
REQUIRE(value == 0x68004073);
as.RewindBuffer();
as.HLV_W(x15, x14);
REQUIRE(value == 0x680747F3);
}
TEST_CASE("HLV.WU", "[rvpriv]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.HLV_WU(x0, x0);
REQUIRE(value == 0x68104073);
as.RewindBuffer();
as.HLV_WU(x15, x14);
REQUIRE(value == 0x681747F3);
}
TEST_CASE("HLVX.HU", "[rvpriv]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.HLVX_HU(x0, x0);
REQUIRE(value == 0x64304073);
as.RewindBuffer();
as.HLVX_HU(x15, x14);
REQUIRE(value == 0x643747F3);
}
TEST_CASE("HLVX.WU", "[rvpriv]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.HLVX_WU(x0, x0);
REQUIRE(value == 0x68304073);
as.RewindBuffer();
as.HLVX_WU(x15, x14);
REQUIRE(value == 0x683747F3);
}
TEST_CASE("HSV.B", "[rvpriv]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.HSV_B(x0, x0);
REQUIRE(value == 0x62004073);
as.RewindBuffer();
as.HSV_B(x15, x14);
REQUIRE(value == 0x62F74073);
}
TEST_CASE("HSV.D", "[rvpriv]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.HSV_D(x0, x0);
REQUIRE(value == 0x6E004073);
as.RewindBuffer();
as.HSV_D(x15, x14);
REQUIRE(value == 0x6EF74073);
}
TEST_CASE("HSV.H", "[rvpriv]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.HSV_H(x0, x0);
REQUIRE(value == 0x66004073);
as.RewindBuffer();
as.HSV_H(x15, x14);
REQUIRE(value == 0x66F74073);
}
TEST_CASE("HSV.W", "[rvpriv]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.HSV_W(x0, x0);
REQUIRE(value == 0x6A004073);
as.RewindBuffer();
as.HSV_W(x15, x14);
REQUIRE(value == 0x6AF74073);
}
TEST_CASE("MRET", "[rvpriv]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.MRET();
REQUIRE(value == 0x30200073);
}
TEST_CASE("SCTRCLR", "[rvpriv]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.SCTRCLR();
REQUIRE(value == 0x10400073);
}
TEST_CASE("SFENCE.INVAL.IR", "[rvpriv]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.SFENCE_INVAL_IR();
REQUIRE(value == 0x18100073);
}
TEST_CASE("SFENCE.VMA", "[rvpriv]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.SFENCE_VMA(x0, x0);
REQUIRE(value == 0x12000073);
as.RewindBuffer();
as.SFENCE_VMA(x15, x15);
REQUIRE(value == 0x12F78073);
}
TEST_CASE("SFENCE.W.INVAL", "[rvpriv]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.SFENCE_W_INVAL();
REQUIRE(value == 0x18000073);
}
TEST_CASE("SINVAL.VMA", "[rvpriv]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.SINVAL_VMA(x0, x0);
REQUIRE(value == 0x16000073);
as.RewindBuffer();
as.SINVAL_VMA(x15, x15);
REQUIRE(value == 0x16F78073);
}
TEST_CASE("SRET", "[rvpriv]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.SRET();
REQUIRE(value == 0x10200073);
}
TEST_CASE("URET", "[rvpriv]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.URET();
REQUIRE(value == 0x00200073);
}
TEST_CASE("WFI", "[rvpriv]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.WFI();
REQUIRE(value == 0x10500073);
}

View file

@ -0,0 +1,769 @@
#include <catch/catch.hpp>
#include <array>
#include <biscuit/assembler.hpp>
#include "assembler_test_utils.hpp"
using namespace biscuit;
TEST_CASE("ADD", "[rv32i]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.ADD(x7, x15, x31);
REQUIRE(value == 0x01F783B3);
as.RewindBuffer();
as.ADD(x31, x31, x31);
REQUIRE(value == 0x01FF8FB3);
as.RewindBuffer();
as.ADD(x0, x0, x0);
REQUIRE(value == 0x00000033);
}
TEST_CASE("ADDI", "[rv32i]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.ADDI(x15, x31, 1024);
REQUIRE(value == 0x400F8793);
as.RewindBuffer();
as.ADDI(x15, x31, 2048);
REQUIRE(value == 0x800F8793);
as.RewindBuffer();
as.ADDI(x15, x31, 4095);
REQUIRE(value == 0xFFFF8793);
}
TEST_CASE("AND", "[rv32i]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.AND(x7, x15, x31);
REQUIRE(value == 0x01F7F3B3);
as.RewindBuffer();
as.AND(x31, x31, x31);
REQUIRE(value == 0x01FFFFB3);
as.RewindBuffer();
as.AND(x0, x0, x0);
REQUIRE(value == 0x00007033);
}
TEST_CASE("ANDI", "[rv32i]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.ANDI(x15, x31, 1024);
REQUIRE(value == 0x400FF793);
as.RewindBuffer();
as.ANDI(x15, x31, 2048);
REQUIRE(value == 0x800FF793);
as.RewindBuffer();
as.ANDI(x15, x31, 4095);
REQUIRE(value == 0xFFFFF793);
}
TEST_CASE("AUIPC", "[rv32i]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.AUIPC(x31, -1);
REQUIRE(value == 0xFFFFFF97);
as.RewindBuffer();
as.AUIPC(x31, 0);
REQUIRE(value == 0x00000F97);
as.RewindBuffer();
as.AUIPC(x31, 0x00FF00FF);
REQUIRE(value == 0xF00FFF97);
}
TEST_CASE("BEQ", "[rv32i]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.BEQ(x15, x31, 2000);
REQUIRE(value == 0x7DF78863);
as.RewindBuffer();
as.BEQ(x15, x31, -2);
REQUIRE(value == 0xFFF78FE3);
}
TEST_CASE("BGE", "[rv32i]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.BGE(x15, x31, 2000);
REQUIRE(value == 0x7DF7D863);
as.RewindBuffer();
as.BGE(x15, x31, -2);
REQUIRE(value == 0xFFF7DFE3);
}
TEST_CASE("BGEU", "[rv32i]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.BGEU(x15, x31, 2000);
REQUIRE(value == 0x7DF7F863);
as.RewindBuffer();
as.BGEU(x15, x31, -2);
REQUIRE(value == 0xFFF7FFE3);
}
TEST_CASE("BNE", "[rv32i]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.BNE(x15, x31, 2000);
REQUIRE(value == 0x7DF79863);
as.RewindBuffer();
as.BNE(x15, x31, -2);
REQUIRE(value == 0xFFF79FE3);
}
TEST_CASE("BLT", "[rv32i]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.BLT(x15, x31, 2000);
REQUIRE(value == 0x7DF7C863);
as.RewindBuffer();
as.BLT(x15, x31, -2);
REQUIRE(value == 0xFFF7CFE3);
}
TEST_CASE("BLTU", "[rv32i]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.BLTU(x15, x31, 2000);
REQUIRE(value == 0x7DF7E863);
as.RewindBuffer();
as.BLTU(x15, x31, -2);
REQUIRE(value == 0xFFF7EFE3);
}
TEST_CASE("CALL", "[rv32i]") {
std::array<uint32_t, 2> vals{};
auto as = MakeAssembler32(vals);
const auto compare_vals = [&vals](uint32_t val_1, uint32_t val_2) {
REQUIRE(vals[0] == val_1);
REQUIRE(vals[1] == val_2);
};
as.CALL(-1);
compare_vals(0x00000097, 0xFFF080E7);
}
TEST_CASE("EBREAK", "[rv32i]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.EBREAK();
REQUIRE(value == 0x00100073);
}
TEST_CASE("ECALL", "[rv32i]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.ECALL();
REQUIRE(value == 0x00000073);
}
TEST_CASE("FENCE", "[rv32i]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FENCE(FenceOrder::IORW, FenceOrder::IORW);
REQUIRE(value == 0x0FF0000F);
as.RewindBuffer();
as.FENCETSO();
REQUIRE(value == 0x8330000F);
as.RewindBuffer();
as.FENCEI();
REQUIRE(value == 0x0000100F);
}
TEST_CASE("JAL", "[rv32i]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.JAL(x31, 0xFFFFFFFF);
REQUIRE(value == 0xFFFFFFEF);
as.RewindBuffer();
as.JAL(x31, 2000);
REQUIRE(value == 0x7D000FEF);
as.RewindBuffer();
as.JAL(x31, 100000);
REQUIRE(value == 0x6A018FEF);
}
TEST_CASE("JALR", "[rv32i]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.JALR(x15, 1024, x31);
REQUIRE(value == 0x400F87E7);
as.RewindBuffer();
as.JALR(x15, 1536, x31);
REQUIRE(value == 0x600F87E7);
as.RewindBuffer();
as.JALR(x15, -1, x31);
REQUIRE(value == 0xFFFF87E7);
}
TEST_CASE("LB", "[rv32i]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.LB(x15, 1024, x31);
REQUIRE(value == 0x400F8783);
as.RewindBuffer();
as.LB(x15, 1536, x31);
REQUIRE(value == 0x600F8783);
as.RewindBuffer();
as.LB(x15, -1, x31);
REQUIRE(value == 0xFFFF8783);
}
TEST_CASE("LBU", "[rv32i]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.LBU(x15, 1024, x31);
REQUIRE(value == 0x400FC783);
as.RewindBuffer();
as.LBU(x15, 1536, x31);
REQUIRE(value == 0x600FC783);
as.RewindBuffer();
as.LBU(x15, -1, x31);
REQUIRE(value == 0xFFFFC783);
}
TEST_CASE("LH", "[rv32i]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.LH(x15, 1024, x31);
REQUIRE(value == 0x400F9783);
as.RewindBuffer();
as.LH(x15, 1536, x31);
REQUIRE(value == 0x600F9783);
as.RewindBuffer();
as.LH(x15, -1, x31);
REQUIRE(value == 0xFFFF9783);
}
TEST_CASE("LHU", "[rv32i]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.LHU(x15, 1024, x31);
REQUIRE(value == 0x400FD783);
as.RewindBuffer();
as.LHU(x15, 1536, x31);
REQUIRE(value == 0x600FD783);
as.RewindBuffer();
as.LHU(x15, -1, x31);
REQUIRE(value == 0xFFFFD783);
}
TEST_CASE("LI", "[rv32i]") {
std::array<uint32_t, 2> vals{};
auto as = MakeAssembler32(vals);
const auto compare_vals = [&vals](uint32_t val_1, uint32_t val_2) {
REQUIRE(vals[0] == val_1);
REQUIRE(vals[1] == val_2);
};
///////// Single ADDI cases
as.LI(x1, 0);
// addi x1, x0, 0
compare_vals(0x00000093, 0x00000000);
as.RewindBuffer();
vals = {};
as.LI(x1, uint64_t(-1));
// addi x1, x0, -1
compare_vals(0xFFF00093, 0x00000000);
as.RewindBuffer();
vals = {};
as.LI(x1, 42);
// addi x1, x0, 42
compare_vals(0x02A00093, 0x000000000);
as.RewindBuffer();
vals = {};
as.LI(x1, 0x7ff);
// addi x1, x0, 2047
compare_vals(0x7FF00093, 0x00000000);
as.RewindBuffer();
vals = {};
///////// Single LUI cases
as.LI(x1, 0x2A000);
// lui x1, 42
compare_vals(0x0002A0B7, 0x00000000);
as.RewindBuffer();
vals = {};
as.LI(x1, ~0xFFFULL);
// lui x1, -1
compare_vals(0xFFFFF0B7, 0x00000000);
as.RewindBuffer();
vals = {};
as.LI(x1, uint64_t(INT32_MIN));
// lui x1, -524288
compare_vals(0x800000B7, 0x00000000);
as.RewindBuffer();
vals = {};
///////// Full LUI+ADDI cases
as.LI(x1, 0x11111111);
// lui x1, 69905
// addi x1, x1, 273
compare_vals(0x111110B7, 0x11108093);
as.RewindBuffer();
vals = {};
as.LI(x1, INT32_MAX);
// lui x1, -524288
// addi x1, x1, -1
compare_vals(0x800000B7, 0xFFF08093);
}
TEST_CASE("LUI", "[rv32i]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.LUI(x10, 0xFFFFFFFF);
REQUIRE(value == 0xFFFFF537);
as.RewindBuffer();
as.LUI(x10, 0xFFF7FFFF);
REQUIRE(value == 0x7FFFF537);
as.RewindBuffer();
as.LUI(x31, 0xFFFFFFFF);
REQUIRE(value == 0xFFFFFFB7);
}
TEST_CASE("LW", "[rv32i]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.LW(x15, 1024, x31);
REQUIRE(value == 0x400FA783);
as.RewindBuffer();
as.LW(x15, 1536, x31);
REQUIRE(value == 0x600FA783);
as.RewindBuffer();
as.LW(x15, -1, x31);
REQUIRE(value == 0xFFFFA783);
}
TEST_CASE("OR", "[rv32i]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.OR(x7, x15, x31);
REQUIRE(value == 0x01F7E3B3);
as.RewindBuffer();
as.OR(x31, x31, x31);
REQUIRE(value == 0x01FFEFB3);
as.RewindBuffer();
as.OR(x0, x0, x0);
REQUIRE(value == 0x00006033);
}
TEST_CASE("ORI", "[rv32i]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.ORI(x15, x31, 1024);
REQUIRE(value == 0x400FE793);
as.RewindBuffer();
as.ORI(x15, x31, 2048);
REQUIRE(value == 0x800FE793);
as.RewindBuffer();
as.ORI(x15, x31, 4095);
REQUIRE(value == 0xFFFFE793);
}
TEST_CASE("PAUSE", "[rv32i]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.PAUSE();
REQUIRE(value == 0x0100000F);
}
TEST_CASE("SB", "[rv32i]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.SB(x31, 1024, x15);
REQUIRE(value == 0x41F78023);
as.RewindBuffer();
as.SB(x31, 1536, x15);
REQUIRE(value == 0x61F78023);
as.RewindBuffer();
as.SB(x31, -1, x15);
REQUIRE(value == 0xFFF78FA3);
}
TEST_CASE("SH", "[rv32i]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.SH(x31, 1024, x15);
REQUIRE(value == 0x41F79023);
as.RewindBuffer();
as.SH(x31, 1536, x15);
REQUIRE(value == 0x61F79023);
as.RewindBuffer();
as.SH(x31, -1, x15);
REQUIRE(value == 0xFFF79FA3);
}
TEST_CASE("SLL", "[rv32i]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.SLL(x7, x15, x31);
REQUIRE(value == 0x01F793B3);
as.RewindBuffer();
as.SLL(x31, x31, x31);
REQUIRE(value == 0x01FF9FB3);
as.RewindBuffer();
as.SLL(x0, x0, x0);
REQUIRE(value == 0x00001033);
}
TEST_CASE("SLLI", "[rv32i]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.SLLI(x31, x15, 10);
REQUIRE(value == 0x00A79F93);
as.RewindBuffer();
as.SLLI(x31, x15, 20);
REQUIRE(value == 0x01479F93);
as.RewindBuffer();
as.SLLI(x31, x15, 31);
REQUIRE(value == 0x01F79F93);
}
TEST_CASE("SLT", "[rv32i]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.SLT(x7, x15, x31);
REQUIRE(value == 0x01F7A3B3);
as.RewindBuffer();
as.SLT(x31, x31, x31);
REQUIRE(value == 0x01FFAFB3);
as.RewindBuffer();
as.SLT(x0, x0, x0);
REQUIRE(value == 0x00002033);
}
TEST_CASE("SLTI", "[rv32i]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.SLTI(x15, x31, 1024);
REQUIRE(value == 0x400FA793);
as.RewindBuffer();
as.SLTI(x15, x31, -2048);
REQUIRE(value == 0x800FA793);
as.RewindBuffer();
as.SLTI(x15, x31, -1);
REQUIRE(value == 0xFFFFA793);
}
TEST_CASE("SLTIU", "[rv32i]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.SLTIU(x15, x31, 1024);
REQUIRE(value == 0x400FB793);
as.RewindBuffer();
as.SLTIU(x15, x31, -2048);
REQUIRE(value == 0x800FB793);
as.RewindBuffer();
as.SLTIU(x15, x31, -1);
REQUIRE(value == 0xFFFFB793);
}
TEST_CASE("SLTU", "[rv32i]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.SLTU(x7, x15, x31);
REQUIRE(value == 0x01F7B3B3);
as.RewindBuffer();
as.SLTU(x31, x31, x31);
REQUIRE(value == 0x01FFBFB3);
as.RewindBuffer();
as.SLTU(x0, x0, x0);
REQUIRE(value == 0x00003033);
}
TEST_CASE("SRA", "[rv32i]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.SRA(x7, x15, x31);
REQUIRE(value == 0x41F7D3B3);
as.RewindBuffer();
as.SRA(x31, x31, x31);
REQUIRE(value == 0x41FFDFB3);
as.RewindBuffer();
as.SRA(x0, x0, x0);
REQUIRE(value == 0x40005033);
}
TEST_CASE("SRAI", "[rv32i]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.SRAI(x31, x15, 10);
REQUIRE(value == 0x40A7DF93);
as.RewindBuffer();
as.SRAI(x31, x15, 20);
REQUIRE(value == 0x4147DF93);
as.RewindBuffer();
as.SRAI(x31, x15, 31);
REQUIRE(value == 0x41F7DF93);
}
TEST_CASE("SRL", "[rv32i]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.SRL(x7, x15, x31);
REQUIRE(value == 0x01F7D3B3);
as.RewindBuffer();
as.SRL(x31, x31, x31);
REQUIRE(value == 0x01FFDFB3);
as.RewindBuffer();
as.SRL(x0, x0, x0);
REQUIRE(value == 0x00005033);
}
TEST_CASE("SRLI", "[rv32i]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.SRLI(x31, x15, 10);
REQUIRE(value == 0x00A7DF93);
as.RewindBuffer();
as.SRLI(x31, x15, 20);
REQUIRE(value == 0x0147DF93);
as.RewindBuffer();
as.SRLI(x31, x15, 31);
REQUIRE(value == 0x01F7DF93);
}
TEST_CASE("SUB", "[rv32i]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.SUB(x7, x15, x31);
REQUIRE(value == 0x41F783B3);
as.RewindBuffer();
as.SUB(x31, x31, x31);
REQUIRE(value == 0x41FF8FB3);
as.RewindBuffer();
as.SUB(x0, x0, x0);
REQUIRE(value == 0x40000033);
}
TEST_CASE("SW", "[rv32i]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.SW(x31, 1024, x15);
REQUIRE(value == 0x41F7A023);
as.RewindBuffer();
as.SW(x31, 1536, x15);
REQUIRE(value == 0x61F7A023);
as.RewindBuffer();
as.SW(x31, -1, x15);
REQUIRE(value == 0xFFF7AFA3);
}
TEST_CASE("XOR", "[rv32i]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.XOR(x7, x15, x31);
REQUIRE(value == 0x01F7C3B3);
as.RewindBuffer();
as.XOR(x31, x31, x31);
REQUIRE(value == 0x01FFCFB3);
as.RewindBuffer();
as.XOR(x0, x0, x0);
REQUIRE(value == 0x00004033);
}
TEST_CASE("XORI", "[rv32i]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.XORI(x15, x31, 1024);
REQUIRE(value == 0x400FC793);
as.RewindBuffer();
as.XORI(x15, x31, 2048);
REQUIRE(value == 0x800FC793);
as.RewindBuffer();
as.XORI(x15, x31, 4095);
REQUIRE(value == 0xFFFFC793);
}

View file

@ -0,0 +1,437 @@
#include <catch/catch.hpp>
#include <array>
#include <biscuit/assembler.hpp>
#include "assembler_test_utils.hpp"
using namespace biscuit;
TEST_CASE("ADDIW", "[rv64i]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.ADDIW(x31, x15, 1024);
REQUIRE(value == 0x40078F9B);
as.RewindBuffer();
as.ADDIW(x31, x15, 2048);
REQUIRE(value == 0x80078F9B);
as.RewindBuffer();
as.ADDIW(x31, x15, 4095);
REQUIRE(value == 0xFFF78F9B);
}
TEST_CASE("ADDW", "[rv64i]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.ADDW(x7, x15, x31);
REQUIRE(value == 0x01F783BB);
as.RewindBuffer();
as.ADDW(x31, x31, x31);
REQUIRE(value == 0x01FF8FBB);
as.RewindBuffer();
as.ADDW(x0, x0, x0);
REQUIRE(value == 0x0000003B);
}
TEST_CASE("LWU", "[rv64i]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.LWU(x15, 1024, x31);
REQUIRE(value == 0x400FE783);
as.RewindBuffer();
as.LWU(x15, 1536, x31);
REQUIRE(value == 0x600FE783);
as.RewindBuffer();
as.LWU(x15, -1, x31);
REQUIRE(value == 0xFFFFE783);
}
TEST_CASE("LD", "[rv64i]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.LD(x15, 1024, x31);
REQUIRE(value == 0x400FB783);
as.RewindBuffer();
as.LD(x15, 1536, x31);
REQUIRE(value == 0x600FB783);
as.RewindBuffer();
as.LD(x15, -1, x31);
REQUIRE(value == 0xFFFFB783);
}
TEST_CASE("LI (RV64)", "[rv64i]") {
// Up to 8 instructions can be generated
constexpr size_t MAX_INSTS = 8;
std::array<uint32_t, MAX_INSTS> vals{};
auto as = MakeAssembler64(vals);
const auto compare_vals = [&vals]<typename... Args>(const Args&... args) {
static_assert(sizeof...(args) <= MAX_INSTS);
size_t i = 0;
for (const auto arg : {args...}) {
REQUIRE(vals[i] == arg);
i++;
}
};
///////// Single ADDIW cases
as.LI(x1, 0);
// addiw x1, x0, 0
compare_vals(0x0000009BU, 0x00000000U);
as.RewindBuffer();
vals = {};
as.LI(x1, uint64_t(-1));
// addiw x1, x0, -1
compare_vals(0xFFF0009BU, 0x00000000U);
as.RewindBuffer();
vals = {};
as.LI(x1, 42);
// addiw x1, x0, 42
compare_vals(0x02A0009BU, 0x000000000U);
as.RewindBuffer();
vals = {};
as.LI(x1, 0x7ff);
// addiw x1, x0, 2047
compare_vals(0x7FF0009BU, 0x00000000U);
as.RewindBuffer();
vals = {};
///////// Single LUI cases
as.LI(x1, 0x2A000);
// lui x1, 42
compare_vals(0x0002A0B7U, 0x00000000U);
as.RewindBuffer();
vals = {};
as.LI(x1, ~0xFFFULL);
// lui x1, -1
compare_vals(0xFFFFF0B7U, 0x00000000U);
as.RewindBuffer();
vals = {};
as.LI(x1, uint64_t(INT32_MIN));
// lui x1, -524288
compare_vals(0x800000B7U, 0x00000000U);
as.RewindBuffer();
vals = {};
///////// LUI+ADDIW cases
as.LI(x1, 0x11111111);
// lui x1, 69905
// addiw x1, x1, 273
compare_vals(0x111110B7U, 0x1110809BU, 0x00000000U);
as.RewindBuffer();
vals = {};
as.LI(x1, INT32_MAX);
// lui x1, -524288
// addiw x1, x1, -1
compare_vals(0x800000B7U, 0xFFF0809BU, 0x00000000U);
as.RewindBuffer();
vals = {};
///////// ADDIW+SLLI cases
as.LI(x1, 0x7FF0000000ULL);
// addiw x1, x0, 2047
// slli x1, x1, 28
compare_vals(0x7FF0009BU, 0x01C09093U, 0x000000000U);
as.RewindBuffer();
vals = {};
as.LI(x1, 0xABC00000ULL);
// addiw x1, x0, 687
// slli x1, x1, 22
compare_vals(0x2AF0009BU, 0x01609093U, 0x000000000U);
as.RewindBuffer();
vals = {};
///////// LUI+ADDIW+SLLI cases
as.LI(x1, 0x7FFFFFFF0000ULL);
// lui x1, -524288
// addiw x1, x1, -1
// slli x1, x1, 16
compare_vals(0x800000B7U, 0xFFF0809BU, 0x01009093U, 0x000000000U);
as.RewindBuffer();
vals = {};
///////// LUI+ADDIW+SLLI+ADDI cases
as.LI(x1, 0x7FFFFFFF0123);
// lui x1, -524288
// addiw x1, x1, -1
// slli x1, x1, 16
// addi x1, x1, 291
compare_vals(0x800000B7U, 0xfff0809BU, 0x01009093U, 0x12308093U,
0x000000000U);
as.RewindBuffer();
vals = {};
///////// ADDIW+SLLI+ADDI+SLLI+ADDI cases
as.LI(x1, 0x8000000080000001ULL);
// addiw x1, x0, -1
// slli x1, x1, 32
// addi x1, x1, 1
// slli x1, x1, 31
// addi x1, x1, 1
compare_vals(0xFFF0009BU, 0x02009093U, 0x00108093U, 0x01F09093U,
0x00108093U, 0x000000000U);
as.RewindBuffer();
vals = {};
///////// Full LUI+ADDIW+SLLI+ADDI+SLLI+ADDI+SLLI+ADDI cases
as.LI(x1, 0x80808000808080F1ULL);
// lui x1, -16
// addiw x1, x1, 257
// slli x1, x1, 16
// addi x1, x1, 1
// slli x1, x1, 16
// addi x1, x1, 257
// slli x1, x1, 15
// addi x1, x1, 241
compare_vals(0xFFFF00B7U, 0x1010809BU, 0x01009093U, 0x00108093U,
0x01009093U, 0x10108093U, 0x00F09093U, 0x0F108093U);
}
TEST_CASE("SD", "[rv64i]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.SD(x15, 1024, x31);
REQUIRE(value == 0x40FFB023);
as.RewindBuffer();
as.SD(x15, 1536, x31);
REQUIRE(value == 0x60FFB023);
as.RewindBuffer();
as.SD(x15, -1, x31);
REQUIRE(value == 0xFEFFBFA3);
}
TEST_CASE("SLLI (RV64)", "[rv64i]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.SLLI(x31, x15, 10);
REQUIRE(value == 0x00A79F93);
as.RewindBuffer();
as.SLLI(x31, x15, 20);
REQUIRE(value == 0x01479F93);
as.RewindBuffer();
as.SLLI(x31, x15, 31);
REQUIRE(value == 0x01F79F93);
as.RewindBuffer();
as.SLLI(x31, x15, 63);
REQUIRE(value == 0x03F79F93);
}
TEST_CASE("SLLIW", "[rv64i]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.SLLIW(x31, x15, 10);
REQUIRE(value == 0x00A79F9B);
as.RewindBuffer();
as.SLLIW(x31, x15, 20);
REQUIRE(value == 0x01479F9B);
as.RewindBuffer();
as.SLLIW(x31, x15, 31);
REQUIRE(value == 0x01F79F9B);
}
TEST_CASE("SLLW", "[rv64i]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.SLLW(x7, x15, x31);
REQUIRE(value == 0x01F793BB);
as.RewindBuffer();
as.SLLW(x31, x31, x31);
REQUIRE(value == 0x01FF9FBB);
as.RewindBuffer();
as.SLLW(x0, x0, x0);
REQUIRE(value == 0x0000103B);
}
TEST_CASE("SRAI (RV64)", "[rv64i]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.SRAI(x31, x15, 10);
REQUIRE(value == 0x40A7DF93);
as.RewindBuffer();
as.SRAI(x31, x15, 20);
REQUIRE(value == 0x4147DF93);
as.RewindBuffer();
as.SRAI(x31, x15, 31);
REQUIRE(value == 0x41F7DF93);
as.RewindBuffer();
as.SRAI(x31, x15, 63);
REQUIRE(value == 0x43F7DF93);
}
TEST_CASE("SRAIW", "[rv64i]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.SRAIW(x31, x15, 10);
REQUIRE(value == 0x40A7DF9B);
as.RewindBuffer();
as.SRAIW(x31, x15, 20);
REQUIRE(value == 0x4147DF9B);
as.RewindBuffer();
as.SRAIW(x31, x15, 31);
REQUIRE(value == 0x41F7DF9B);
}
TEST_CASE("SRAW", "[rv64i]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.SRAW(x7, x15, x31);
REQUIRE(value == 0x41F7D3BB);
as.RewindBuffer();
as.SRAW(x31, x31, x31);
REQUIRE(value == 0x41FFDFBB);
as.RewindBuffer();
as.SRAW(x0, x0, x0);
REQUIRE(value == 0x4000503B);
}
TEST_CASE("SRLI (RV64)", "[rv64i]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.SRLI(x31, x15, 10);
REQUIRE(value == 0x00A7DF93);
as.RewindBuffer();
as.SRLI(x31, x15, 20);
REQUIRE(value == 0x0147DF93);
as.RewindBuffer();
as.SRLI(x31, x15, 31);
REQUIRE(value == 0x01F7DF93);
as.RewindBuffer();
as.SRLI(x31, x15, 63);
REQUIRE(value == 0x03F7DF93);
}
TEST_CASE("SRLIW", "[rv64i]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.SRLIW(x31, x15, 10);
REQUIRE(value == 0x00A7DF9B);
as.RewindBuffer();
as.SRLIW(x31, x15, 20);
REQUIRE(value == 0x0147DF9B);
as.RewindBuffer();
as.SRLIW(x31, x15, 31);
REQUIRE(value == 0x01F7DF9B);
}
TEST_CASE("SRLW", "[rv64i]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.SRLW(x7, x15, x31);
REQUIRE(value == 0x01F7D3BB);
as.RewindBuffer();
as.SRLW(x31, x31, x31);
REQUIRE(value == 0x01FFDFBB);
as.RewindBuffer();
as.SRLW(x0, x0, x0);
REQUIRE(value == 0x0000503B);
}
TEST_CASE("SUBW", "[rv64i]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.SUBW(x7, x15, x31);
REQUIRE(value == 0x41F783BB);
as.RewindBuffer();
as.SUBW(x31, x31, x31);
REQUIRE(value == 0x41FF8FBB);
as.RewindBuffer();
as.SUBW(x0, x0, x0);
REQUIRE(value == 0x4000003B);
}

View file

@ -0,0 +1,513 @@
#include <catch/catch.hpp>
#include <biscuit/assembler.hpp>
#include "assembler_test_utils.hpp"
using namespace biscuit;
TEST_CASE("AMOADD.D", "[rv64a]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.AMOADD_D(Ordering::None, x31, x7, x15);
REQUIRE(value == 0x0077BFAF);
as.RewindBuffer();
as.AMOADD_D(Ordering::AQ, x31, x7, x15);
REQUIRE(value == 0x0477BFAF);
as.RewindBuffer();
as.AMOADD_D(Ordering::RL, x31, x7, x15);
REQUIRE(value == 0x0277BFAF);
as.RewindBuffer();
as.AMOADD_D(Ordering::AQRL, x31, x7, x15);
REQUIRE(value == 0x0677BFAF);
}
TEST_CASE("AMOADD.W", "[rv32a]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.AMOADD_W(Ordering::None, x31, x7, x15);
REQUIRE(value == 0x0077AFAF);
as.RewindBuffer();
as.AMOADD_W(Ordering::AQ, x31, x7, x15);
REQUIRE(value == 0x0477AFAF);
as.RewindBuffer();
as.AMOADD_W(Ordering::RL, x31, x7, x15);
REQUIRE(value == 0x0277AFAF);
as.RewindBuffer();
as.AMOADD_W(Ordering::AQRL, x31, x7, x15);
REQUIRE(value == 0x0677AFAF);
}
TEST_CASE("AMOAND.D", "[rv64a]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.AMOAND_D(Ordering::None, x31, x7, x15);
REQUIRE(value == 0x6077BFAF);
as.RewindBuffer();
as.AMOAND_D(Ordering::AQ, x31, x7, x15);
REQUIRE(value == 0x6477BFAF);
as.RewindBuffer();
as.AMOAND_D(Ordering::RL, x31, x7, x15);
REQUIRE(value == 0x6277BFAF);
as.RewindBuffer();
as.AMOAND_D(Ordering::AQRL, x31, x7, x15);
REQUIRE(value == 0x6677BFAF);
}
TEST_CASE("AMOAND.W", "[rv32a]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.AMOAND_W(Ordering::None, x31, x7, x15);
REQUIRE(value == 0x6077AFAF);
as.RewindBuffer();
as.AMOAND_W(Ordering::AQ, x31, x7, x15);
REQUIRE(value == 0x6477AFAF);
as.RewindBuffer();
as.AMOAND_W(Ordering::RL, x31, x7, x15);
REQUIRE(value == 0x6277AFAF);
as.RewindBuffer();
as.AMOAND_W(Ordering::AQRL, x31, x7, x15);
REQUIRE(value == 0x6677AFAF);
}
TEST_CASE("AMOMAX.D", "[rv64a]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.AMOMAX_D(Ordering::None, x31, x7, x15);
REQUIRE(value == 0xA077BFAF);
as.RewindBuffer();
as.AMOMAX_D(Ordering::AQ, x31, x7, x15);
REQUIRE(value == 0xA477BFAF);
as.RewindBuffer();
as.AMOMAX_D(Ordering::RL, x31, x7, x15);
REQUIRE(value == 0xA277BFAF);
as.RewindBuffer();
as.AMOMAX_D(Ordering::AQRL, x31, x7, x15);
REQUIRE(value == 0xA677BFAF);
}
TEST_CASE("AMOMAX.W", "[rv32a]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.AMOMAX_W(Ordering::None, x31, x7, x15);
REQUIRE(value == 0xA077AFAF);
as.RewindBuffer();
as.AMOMAX_W(Ordering::AQ, x31, x7, x15);
REQUIRE(value == 0xA477AFAF);
as.RewindBuffer();
as.AMOMAX_W(Ordering::RL, x31, x7, x15);
REQUIRE(value == 0xA277AFAF);
as.RewindBuffer();
as.AMOMAX_W(Ordering::AQRL, x31, x7, x15);
REQUIRE(value == 0xA677AFAF);
}
TEST_CASE("AMOMAXU.D", "[rv64a]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.AMOMAXU_D(Ordering::None, x31, x7, x15);
REQUIRE(value == 0xE077BFAF);
as.RewindBuffer();
as.AMOMAXU_D(Ordering::AQ, x31, x7, x15);
REQUIRE(value == 0xE477BFAF);
as.RewindBuffer();
as.AMOMAXU_D(Ordering::RL, x31, x7, x15);
REQUIRE(value == 0xE277BFAF);
as.RewindBuffer();
as.AMOMAXU_D(Ordering::AQRL, x31, x7, x15);
REQUIRE(value == 0xE677BFAF);
}
TEST_CASE("AMOMAXU.W", "[rv32a]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.AMOMAXU_W(Ordering::None, x31, x7, x15);
REQUIRE(value == 0xE077AFAF);
as.RewindBuffer();
as.AMOMAXU_W(Ordering::AQ, x31, x7, x15);
REQUIRE(value == 0xE477AFAF);
as.RewindBuffer();
as.AMOMAXU_W(Ordering::RL, x31, x7, x15);
REQUIRE(value == 0xE277AFAF);
as.RewindBuffer();
as.AMOMAXU_W(Ordering::AQRL, x31, x7, x15);
REQUIRE(value == 0xE677AFAF);
}
TEST_CASE("AMOMIN.D", "[rv64a]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.AMOMIN_D(Ordering::None, x31, x7, x15);
REQUIRE(value == 0x8077BFAF);
as.RewindBuffer();
as.AMOMIN_D(Ordering::AQ, x31, x7, x15);
REQUIRE(value == 0x8477BFAF);
as.RewindBuffer();
as.AMOMIN_D(Ordering::RL, x31, x7, x15);
REQUIRE(value == 0x8277BFAF);
as.RewindBuffer();
as.AMOMIN_D(Ordering::AQRL, x31, x7, x15);
REQUIRE(value == 0x8677BFAF);
}
TEST_CASE("AMOMIN.W", "[rv32a]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.AMOMIN_W(Ordering::None, x31, x7, x15);
REQUIRE(value == 0x8077AFAF);
as.RewindBuffer();
as.AMOMIN_W(Ordering::AQ, x31, x7, x15);
REQUIRE(value == 0x8477AFAF);
as.RewindBuffer();
as.AMOMIN_W(Ordering::RL, x31, x7, x15);
REQUIRE(value == 0x8277AFAF);
as.RewindBuffer();
as.AMOMIN_W(Ordering::AQRL, x31, x7, x15);
REQUIRE(value == 0x8677AFAF);
}
TEST_CASE("AMOMINU.D", "[rv64a]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.AMOMINU_D(Ordering::None, x31, x7, x15);
REQUIRE(value == 0xC077BFAF);
as.RewindBuffer();
as.AMOMINU_D(Ordering::AQ, x31, x7, x15);
REQUIRE(value == 0xC477BFAF);
as.RewindBuffer();
as.AMOMINU_D(Ordering::RL, x31, x7, x15);
REQUIRE(value == 0xC277BFAF);
as.RewindBuffer();
as.AMOMINU_D(Ordering::AQRL, x31, x7, x15);
REQUIRE(value == 0xC677BFAF);
}
TEST_CASE("AMOMINU.W", "[rv32a]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.AMOMINU_W(Ordering::None, x31, x7, x15);
REQUIRE(value == 0xC077AFAF);
as.RewindBuffer();
as.AMOMINU_W(Ordering::AQ, x31, x7, x15);
REQUIRE(value == 0xC477AFAF);
as.RewindBuffer();
as.AMOMINU_W(Ordering::RL, x31, x7, x15);
REQUIRE(value == 0xC277AFAF);
as.RewindBuffer();
as.AMOMINU_W(Ordering::AQRL, x31, x7, x15);
REQUIRE(value == 0xC677AFAF);
}
TEST_CASE("AMOOR.D", "[rv64a]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.AMOOR_D(Ordering::None, x31, x7, x15);
REQUIRE(value == 0x4077BFAF);
as.RewindBuffer();
as.AMOOR_D(Ordering::AQ, x31, x7, x15);
REQUIRE(value == 0x4477BFAF);
as.RewindBuffer();
as.AMOOR_D(Ordering::RL, x31, x7, x15);
REQUIRE(value == 0x4277BFAF);
as.RewindBuffer();
as.AMOOR_D(Ordering::AQRL, x31, x7, x15);
REQUIRE(value == 0x4677BFAF);
}
TEST_CASE("AMOOR.W", "[rv32a]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.AMOOR_W(Ordering::None, x31, x7, x15);
REQUIRE(value == 0x4077AFAF);
as.RewindBuffer();
as.AMOOR_W(Ordering::AQ, x31, x7, x15);
REQUIRE(value == 0x4477AFAF);
as.RewindBuffer();
as.AMOOR_W(Ordering::RL, x31, x7, x15);
REQUIRE(value == 0x4277AFAF);
as.RewindBuffer();
as.AMOOR_W(Ordering::AQRL, x31, x7, x15);
REQUIRE(value == 0x4677AFAF);
}
TEST_CASE("AMOSWAP.D", "[rv64a]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.AMOSWAP_D(Ordering::None, x31, x7, x15);
REQUIRE(value == 0x0877BFAF);
as.RewindBuffer();
as.AMOSWAP_D(Ordering::AQ, x31, x7, x15);
REQUIRE(value == 0x0C77BFAF);
as.RewindBuffer();
as.AMOSWAP_D(Ordering::RL, x31, x7, x15);
REQUIRE(value == 0x0A77BFAF);
as.RewindBuffer();
as.AMOSWAP_D(Ordering::AQRL, x31, x7, x15);
REQUIRE(value == 0x0E77BFAF);
}
TEST_CASE("AMOSWAP.W", "[rv32a]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.AMOSWAP_W(Ordering::None, x31, x7, x15);
REQUIRE(value == 0x0877AFAF);
as.RewindBuffer();
as.AMOSWAP_W(Ordering::AQ, x31, x7, x15);
REQUIRE(value == 0x0C77AFAF);
as.RewindBuffer();
as.AMOSWAP_W(Ordering::RL, x31, x7, x15);
REQUIRE(value == 0x0A77AFAF);
as.RewindBuffer();
as.AMOSWAP_W(Ordering::AQRL, x31, x7, x15);
REQUIRE(value == 0x0E77AFAF);
}
TEST_CASE("AMOXOR.D", "[rv64a]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.AMOXOR_D(Ordering::None, x31, x7, x15);
REQUIRE(value == 0x2077BFAF);
as.RewindBuffer();
as.AMOXOR_D(Ordering::AQ, x31, x7, x15);
REQUIRE(value == 0x2477BFAF);
as.RewindBuffer();
as.AMOXOR_D(Ordering::RL, x31, x7, x15);
REQUIRE(value == 0x2277BFAF);
as.RewindBuffer();
as.AMOXOR_D(Ordering::AQRL, x31, x7, x15);
REQUIRE(value == 0x2677BFAF);
}
TEST_CASE("AMOXOR.W", "[rv32a]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.AMOXOR_W(Ordering::None, x31, x7, x15);
REQUIRE(value == 0x2077AFAF);
as.RewindBuffer();
as.AMOXOR_W(Ordering::AQ, x31, x7, x15);
REQUIRE(value == 0x2477AFAF);
as.RewindBuffer();
as.AMOXOR_W(Ordering::RL, x31, x7, x15);
REQUIRE(value == 0x2277AFAF);
as.RewindBuffer();
as.AMOXOR_W(Ordering::AQRL, x31, x7, x15);
REQUIRE(value == 0x2677AFAF);
}
TEST_CASE("LR.D", "[rv64a]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.LR_D(Ordering::None, x31, x15);
REQUIRE(value == 0x1007BFAF);
as.RewindBuffer();
as.LR_D(Ordering::AQ, x31, x15);
REQUIRE(value == 0x1407BFAF);
as.RewindBuffer();
as.LR_D(Ordering::RL, x31, x15);
REQUIRE(value == 0x1207BFAF);
as.RewindBuffer();
as.LR_D(Ordering::AQRL, x31, x15);
REQUIRE(value == 0x1607BFAF);
}
TEST_CASE("LR.W", "[rv32a]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.LR_W(Ordering::None, x31, x15);
REQUIRE(value == 0x1007AFAF);
as.RewindBuffer();
as.LR_W(Ordering::AQ, x31, x15);
REQUIRE(value == 0x1407AFAF);
as.RewindBuffer();
as.LR_W(Ordering::RL, x31, x15);
REQUIRE(value == 0x1207AFAF);
as.RewindBuffer();
as.LR_W(Ordering::AQRL, x31, x15);
REQUIRE(value == 0x1607AFAF);
}
TEST_CASE("SC.D", "[rv64a]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.SC_D(Ordering::None, x31, x7, x15);
REQUIRE(value == 0x1877BFAF);
as.RewindBuffer();
as.SC_D(Ordering::AQ, x31, x7, x15);
REQUIRE(value == 0x1C77BFAF);
as.RewindBuffer();
as.SC_D(Ordering::RL, x31, x7, x15);
REQUIRE(value == 0x1A77BFAF);
as.RewindBuffer();
as.SC_D(Ordering::AQRL, x31, x7, x15);
REQUIRE(value == 0x1E77BFAF);
}
TEST_CASE("SC.W", "[rv32a]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.SC_W(Ordering::None, x31, x7, x15);
REQUIRE(value == 0x1877AFAF);
as.RewindBuffer();
as.SC_W(Ordering::AQ, x31, x7, x15);
REQUIRE(value == 0x1C77AFAF);
as.RewindBuffer();
as.SC_W(Ordering::RL, x31, x7, x15);
REQUIRE(value == 0x1A77AFAF);
as.RewindBuffer();
as.SC_W(Ordering::AQRL, x31, x7, x15);
REQUIRE(value == 0x1E77AFAF);
}

View file

@ -0,0 +1,610 @@
#include <catch/catch.hpp>
#include <biscuit/assembler.hpp>
#include "assembler_test_utils.hpp"
using namespace biscuit;
TEST_CASE("ADD.UW", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.ADDUW(x31, x7, x15);
REQUIRE(value == 0x08F38FBB);
as.RewindBuffer();
// Pseudo instruction
as.ZEXTW(x31, x7);
REQUIRE(value == 0x08038FBB);
}
TEST_CASE("ANDN", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.ANDN(x31, x7, x15);
REQUIRE(value == 0x40F3FFB3);
}
TEST_CASE("BCLR", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.BCLR(x31, x7, x15);
REQUIRE(value == 0x48F39FB3);
}
TEST_CASE("BCLRI", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.BCLRI(x31, x7, 0);
REQUIRE(value == 0x48039F93);
as.RewindBuffer();
as.BCLRI(x31, x7, 15);
REQUIRE(value == 0x48F39F93);
as.RewindBuffer();
as.BCLRI(x31, x7, 31);
REQUIRE(value == 0x49F39F93);
}
TEST_CASE("BCLRI (RV64)", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.BCLRI(x31, x7, 0);
REQUIRE(value == 0x48039F93);
as.RewindBuffer();
as.BCLRI(x31, x7, 15);
REQUIRE(value == 0x48F39F93);
as.RewindBuffer();
as.BCLRI(x31, x7, 31);
REQUIRE(value == 0x49F39F93);
as.RewindBuffer();
as.BCLRI(x31, x7, 63);
REQUIRE(value == 0x4BF39F93);
}
TEST_CASE("BEXT", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.BEXT(x31, x7, x15);
REQUIRE(value == 0x48F3DFB3);
}
TEST_CASE("BEXTI", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.BEXTI(x31, x7, 0);
REQUIRE(value == 0x4803DF93);
as.RewindBuffer();
as.BEXTI(x31, x7, 15);
REQUIRE(value == 0x48F3DF93);
as.RewindBuffer();
as.BEXTI(x31, x7, 31);
REQUIRE(value == 0x49F3DF93);
}
TEST_CASE("BEXTI (RV64)", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.BEXTI(x31, x7, 0);
REQUIRE(value == 0x4803DF93);
as.RewindBuffer();
as.BEXTI(x31, x7, 15);
REQUIRE(value == 0x48F3DF93);
as.RewindBuffer();
as.BEXTI(x31, x7, 31);
REQUIRE(value == 0x49F3DF93);
as.RewindBuffer();
as.BEXTI(x31, x7, 63);
REQUIRE(value == 0x4BF3DF93);
}
TEST_CASE("BINV", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.BINV(x31, x7, x15);
REQUIRE(value == 0x68F39FB3);
}
TEST_CASE("BINVI", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.BINVI(x31, x7, 0);
REQUIRE(value == 0x68039F93);
as.RewindBuffer();
as.BINVI(x31, x7, 15);
REQUIRE(value == 0x68F39F93);
as.RewindBuffer();
as.BINVI(x31, x7, 31);
REQUIRE(value == 0x69F39F93);
}
TEST_CASE("BINVI (RV64)", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.BINVI(x31, x7, 0);
REQUIRE(value == 0x68039F93);
as.RewindBuffer();
as.BINVI(x31, x7, 15);
REQUIRE(value == 0x68F39F93);
as.RewindBuffer();
as.BINVI(x31, x7, 31);
REQUIRE(value == 0x69F39F93);
as.RewindBuffer();
as.BINVI(x31, x7, 63);
REQUIRE(value == 0x6BF39F93);
}
TEST_CASE("BREV8", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.BREV8(x31, x31);
REQUIRE(value == 0x687FDF93);
as.RewindBuffer();
as.BREV8(x1, x2);
REQUIRE(value == 0x68715093);
}
TEST_CASE("BSET", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.BSET(x31, x7, x15);
REQUIRE(value == 0x28F39FB3);
}
TEST_CASE("BSETI", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.BSETI(x31, x7, 0);
REQUIRE(value == 0x28039FB3);
as.RewindBuffer();
as.BSETI(x31, x7, 15);
REQUIRE(value == 0x28F39FB3);
as.RewindBuffer();
as.BSETI(x31, x7, 31);
REQUIRE(value == 0x29F39FB3);
}
TEST_CASE("BSETI (RV64)", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.BSETI(x31, x7, 0);
REQUIRE(value == 0x28039FB3);
as.RewindBuffer();
as.BSETI(x31, x7, 15);
REQUIRE(value == 0x28F39FB3);
as.RewindBuffer();
as.BSETI(x31, x7, 31);
REQUIRE(value == 0x29F39FB3);
as.RewindBuffer();
as.BSETI(x31, x7, 63);
REQUIRE(value == 0x2BF39FB3);
}
TEST_CASE("CLMUL", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.CLMUL(x31, x7, x15);
REQUIRE(value == 0x0AF39FB3);
}
TEST_CASE("CLMULH", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.CLMULH(x31, x7, x15);
REQUIRE(value == 0x0AF3BFB3);
}
TEST_CASE("CLMULR", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.CLMULR(x31, x7, x15);
REQUIRE(value == 0x0AF3AFB3);
}
TEST_CASE("CLZ", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.CLZ(x31, x7);
REQUIRE(value == 0x60039F93);
}
TEST_CASE("CLZW", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.CLZW(x31, x7);
REQUIRE(value == 0x60039F9B);
}
TEST_CASE("CPOP", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.CPOP(x31, x7);
REQUIRE(value == 0x60239F93);
}
TEST_CASE("CPOPW", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.CPOPW(x31, x7);
REQUIRE(value == 0x60239F9B);
}
TEST_CASE("CTZ", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.CTZ(x31, x7);
REQUIRE(value == 0x60139F93);
}
TEST_CASE("CTZW", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.CTZW(x31, x7);
REQUIRE(value == 0x60139F9B);
}
TEST_CASE("MAX", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.MAX(x31, x7, x15);
REQUIRE(value == 0x0AF3EFB3);
}
TEST_CASE("MAXU", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.MAXU(x31, x7, x15);
REQUIRE(value == 0x0AF3FFB3);
}
TEST_CASE("MIN", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.MIN(x31, x7, x15);
REQUIRE(value == 0x0AF3CFB3);
}
TEST_CASE("MINU", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.MINU(x31, x7, x15);
REQUIRE(value == 0x0AF3DFB3);
}
TEST_CASE("ORC.B", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.ORCB(x31, x7);
REQUIRE(value == 0x2873DF93);
}
TEST_CASE("ORN", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.ORN(x31, x7, x15);
REQUIRE(value == 0x40F3EFB3);
}
TEST_CASE("PACK", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.PACK(x31, x7, x2);
REQUIRE(value == 0x0823CFB3);
}
TEST_CASE("PACKH", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.PACKH(x31, x7, x2);
REQUIRE(value == 0x0823FFB3);
}
TEST_CASE("PACKW", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.PACKW(x31, x7, x2);
REQUIRE(value == 0x0823CFBB);
}
TEST_CASE("REV8", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.REV8(x31, x7);
REQUIRE(value == 0x6983DF93);
}
TEST_CASE("REV8 (RV64)", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.REV8(x31, x7);
REQUIRE(value == 0x6B83DF93);
}
TEST_CASE("ROL", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.ROL(x31, x7, x15);
REQUIRE(value == 0x60F39FB3);
}
TEST_CASE("ROLW", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.ROLW(x31, x7, x15);
REQUIRE(value == 0x60F39FBB);
}
TEST_CASE("ROR", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.ROR(x31, x7, x15);
REQUIRE(value == 0x60F3DFB3);
}
TEST_CASE("RORW", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.RORW(x31, x7, x15);
REQUIRE(value == 0x60F3DFBB);
}
TEST_CASE("RORI", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.RORI(x31, x7, 0);
REQUIRE(value == 0x6003DF93);
as.RewindBuffer();
as.RORI(x31, x7, 63);
REQUIRE(value == 0x63F3DF93);
}
TEST_CASE("RORIW", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.RORIW(x31, x7, 0);
REQUIRE(value == 0x6003DF9B);
as.RewindBuffer();
as.RORIW(x31, x7, 63);
REQUIRE(value == 0x63F3DF9B);
}
TEST_CASE("SEXT.B", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.SEXTB(x31, x7);
REQUIRE(value == 0x60439F93);
}
TEST_CASE("SEXT.H", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.SEXTH(x31, x7);
REQUIRE(value == 0x60539F93);
}
TEST_CASE("SH1ADD", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.SH1ADD(x31, x7, x15);
REQUIRE(value == 0x20F3AFB3);
}
TEST_CASE("SH1ADD.UW", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.SH1ADDUW(x31, x7, x15);
REQUIRE(value == 0x20F3AFBB);
}
TEST_CASE("SH2ADD", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.SH2ADD(x31, x7, x15);
REQUIRE(value == 0x20F3CFB3);
}
TEST_CASE("SH2ADD.UW", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.SH2ADDUW(x31, x7, x15);
REQUIRE(value == 0x20F3CFBB);
}
TEST_CASE("SH3ADD", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.SH3ADD(x31, x7, x15);
REQUIRE(value == 0x20F3EFB3);
}
TEST_CASE("SH3ADD.UW", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.SH3ADDUW(x31, x7, x15);
REQUIRE(value == 0x20F3EFBB);
}
TEST_CASE("SLLI.UW", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.SLLIUW(x31, x7, 0);
REQUIRE(value == 0x08039F9B);
as.RewindBuffer();
as.SLLIUW(x31, x7, 63);
REQUIRE(value == 0x0BF39F9B);
}
TEST_CASE("UNZIP", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.UNZIP(x31, x31);
REQUIRE(value == 0x09FFDF93);
as.RewindBuffer();
as.UNZIP(x1, x2);
REQUIRE(value == 0x09F15093);
}
TEST_CASE("XNOR", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.XNOR(x31, x7, x15);
REQUIRE(value == 0x40F3CFB3);
}
TEST_CASE("XPERM4", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.XPERM4(x31, x31, x31);
REQUIRE(value == 0x29FFAFB3);
as.RewindBuffer();
as.XPERM4(x1, x2, x3);
REQUIRE(value == 0x283120B3);
}
TEST_CASE("XPERM8", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.XPERM8(x31, x31, x31);
REQUIRE(value == 0x29FFCFB3);
as.RewindBuffer();
as.XPERM8(x1, x2, x3);
REQUIRE(value == 0x283140B3);
}
TEST_CASE("ZEXT.H", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.ZEXTH(x31, x7);
REQUIRE(value == 0x0803CFB3);
}
TEST_CASE("ZEXT.H (RV64)", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.ZEXTH(x31, x7);
REQUIRE(value == 0x0803CFBB);
}
TEST_CASE("ZIP", "[rvb]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.ZIP(x31, x31);
REQUIRE(value == 0x09EF9F93);
as.RewindBuffer();
as.ZIP(x1, x2);
REQUIRE(value == 0x09E11093);
}

View file

@ -0,0 +1,595 @@
#include <catch/catch.hpp>
#include <biscuit/assembler.hpp>
#include "assembler_test_utils.hpp"
using namespace biscuit;
TEST_CASE("C.ADD", "[rvc]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.C_ADD(x31, x31);
REQUIRE(value == 0x9FFE);
as.RewindBuffer();
as.C_ADD(x15, x8);
REQUIRE(value == 0x97A2);
}
TEST_CASE("C.ADDI", "[rvc]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.C_ADDI(x15, -1);
REQUIRE(value == 0x17FD);
as.RewindBuffer();
as.C_ADDI(x15, -32);
REQUIRE(value == 0x1781);
as.RewindBuffer();
as.C_ADDI(x15, 31);
REQUIRE(value == 0x07FD);
}
TEST_CASE("C.ADDIW", "[rvc]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.C_ADDIW(x15, -1);
REQUIRE(value == 0x37FD);
as.RewindBuffer();
as.C_ADDIW(x15, -32);
REQUIRE(value == 0x3781);
as.RewindBuffer();
as.C_ADDIW(x15, 31);
REQUIRE(value == 0x27FD);
}
TEST_CASE("C.ADDI4SPN", "[rvc]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.C_ADDI4SPN(x15, 252);
REQUIRE(value == 0x19FC);
as.RewindBuffer();
as.C_ADDI4SPN(x8, 1020);
REQUIRE(value == 0x1FE0);
as.RewindBuffer();
as.C_ADDI4SPN(x15, 1020);
REQUIRE(value == 0x1FFC);
}
TEST_CASE("C.ADDI16SP", "[rvc]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.C_ADDI16SP(16);
REQUIRE(value == 0x6141);
as.RewindBuffer();
as.C_ADDI16SP(64);
REQUIRE(value == 0x6121);
}
TEST_CASE("C.ADDW", "[rvc]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.C_ADDW(x15, x15);
REQUIRE(value == 0x9FBD);
as.RewindBuffer();
as.C_ADDW(x15, x8);
REQUIRE(value == 0x9FA1);
}
TEST_CASE("C.AND", "[rvc]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.C_AND(x15, x15);
REQUIRE(value == 0x8FFD);
as.RewindBuffer();
as.C_AND(x15, x8);
REQUIRE(value == 0x8FE1);
}
TEST_CASE("C.ANDI", "[rvc]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.C_ANDI(x15, 16);
REQUIRE(value == 0x8BC1);
as.RewindBuffer();
as.C_ANDI(x15, 31);
REQUIRE(value == 0x8BFD);
}
TEST_CASE("C.EBREAK", "[rvc]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.C_EBREAK();
REQUIRE(value == 0x9002);
}
TEST_CASE("C.FLD", "[rvc]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.C_FLD(f15, 8, x15);
REQUIRE(value == 0x279C);
as.RewindBuffer();
as.C_FLD(f15, 24, x15);
REQUIRE(value == 0x2F9C);
}
TEST_CASE("C.FLDSP", "[rvc]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.C_FLDSP(f15, 8);
REQUIRE(value == 0x27A2);
as.RewindBuffer();
as.C_FLDSP(f15, 24);
REQUIRE(value == 0x27E2);
}
TEST_CASE("C.FLW", "[rvc]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.C_FLW(f15, 16, x15);
REQUIRE(value == 0x6B9C);
as.RewindBuffer();
as.C_FLW(f15, 24, x15);
REQUIRE(value == 0x6F9C);
}
TEST_CASE("C.FLWSP", "[rvc]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.C_FLWSP(f15, 16);
REQUIRE(value == 0x67C2);
as.RewindBuffer();
as.C_FLWSP(f15, 24);
REQUIRE(value == 0x67E2);
}
TEST_CASE("C.FSD", "[rvc]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.C_FSD(f15, 8, x15);
REQUIRE(value == 0xA79C);
as.RewindBuffer();
as.C_FSD(f15, 24, x15);
REQUIRE(value == 0xAF9C);
}
TEST_CASE("C.FSDSP", "[rvc]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.C_FSDSP(f15, 8);
REQUIRE(value == 0xA43E);
as.RewindBuffer();
as.C_FSDSP(f15, 24);
REQUIRE(value == 0xAC3E);
}
TEST_CASE("C.FSW", "[rvc]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.C_FSW(f15, 16, x15);
REQUIRE(value == 0xEB9C);
as.RewindBuffer();
as.C_FSW(f15, 24, x15);
REQUIRE(value == 0xEF9C);
}
TEST_CASE("C.FSWSP", "[rvc]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.C_FSWSP(f15, 16);
REQUIRE(value == 0xE83E);
as.RewindBuffer();
as.C_FSWSP(f15, 24);
REQUIRE(value == 0xEC3E);
}
TEST_CASE("C.JALR", "[rvc]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.C_JALR(x31);
REQUIRE(value == 0x9F82);
as.RewindBuffer();
as.C_JALR(x15);
REQUIRE(value == 0x9782);
}
TEST_CASE("C.JR", "[rvc]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.C_JR(x31);
REQUIRE(value == 0x8F82);
as.RewindBuffer();
as.C_JR(x15);
REQUIRE(value == 0x8782);
}
TEST_CASE("C.LD", "[rvc]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.C_LD(x15, 8, x15);
REQUIRE(value == 0x679C);
as.RewindBuffer();
as.C_LD(x15, 24, x15);
REQUIRE(value == 0x6F9C);
}
TEST_CASE("C.LDSP", "[rvc]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.C_LDSP(x15, 8);
REQUIRE(value == 0x67A2);
as.RewindBuffer();
as.C_LDSP(x15, 24);
REQUIRE(value == 0x67E2);
}
TEST_CASE("C.LI", "[rvc]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.C_LI(x15, -1);
REQUIRE(value == 0x57FD);
as.RewindBuffer();
as.C_LI(x15, -32);
REQUIRE(value == 0x5781);
as.RewindBuffer();
as.C_LI(x15, 31);
REQUIRE(value == 0x47FD);
}
TEST_CASE("C.LQ", "[rvc]") {
uint32_t value = 0;
auto as = MakeAssembler128(value);
as.C_LQ(x15, 16, x15);
REQUIRE(value == 0x2B9C);
as.RewindBuffer();
as.C_LQ(x15, 256, x15);
REQUIRE(value == 0x279C);
}
TEST_CASE("C.LQSP", "[rvc]") {
uint32_t value = 0;
auto as = MakeAssembler128(value);
as.C_LQSP(x15, 16);
REQUIRE(value == 0x27C2);
as.RewindBuffer();
as.C_LQSP(x15, 256);
REQUIRE(value == 0x2792);
}
TEST_CASE("C.LUI", "[rvc]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.C_LUI(x15, 0x3F000);
REQUIRE(value == 0x77FD);
as.RewindBuffer();
as.C_LUI(x15, 0x0F000);
REQUIRE(value == 0x67BD);
}
TEST_CASE("C.LW", "[rvc]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.C_LW(x15, 16, x15);
REQUIRE(value == 0x4B9C);
as.RewindBuffer();
as.C_LW(x15, 24, x15);
REQUIRE(value == 0x4F9C);
}
TEST_CASE("C.LWSP", "[rvc]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.C_LWSP(x15, 16);
REQUIRE(value == 0x47C2);
as.RewindBuffer();
as.C_LWSP(x15, 24);
REQUIRE(value == 0x47E2);
}
TEST_CASE("C.MV", "[rvc]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.C_MV(x31, x31);
REQUIRE(value == 0x8FFE);
as.RewindBuffer();
as.C_MV(x15, x8);
REQUIRE(value == 0x87A2);
}
TEST_CASE("C.NOP", "[rvc]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.C_NOP();
REQUIRE(value == 0x0001);
}
TEST_CASE("C.OR", "[rvc]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.C_OR(x15, x15);
REQUIRE(value == 0x8FDD);
as.RewindBuffer();
as.C_OR(x15, x8);
REQUIRE(value == 0x8FC1);
}
TEST_CASE("C.SD", "[rvc]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.C_SD(x15, 8, x15);
REQUIRE(value == 0xE79C);
as.RewindBuffer();
as.C_SD(x15, 24, x15);
REQUIRE(value == 0xEF9C);
}
TEST_CASE("C.SDSP", "[rvc]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.C_SDSP(x15, 8);
REQUIRE(value == 0xE43E);
as.RewindBuffer();
as.C_SDSP(x15, 24);
REQUIRE(value == 0xEC3E);
}
TEST_CASE("C.SLLI", "[rvc]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.C_SLLI(x15, 15);
REQUIRE(value == 0x07BE);
as.RewindBuffer();
as.C_SLLI(x15, 31);
REQUIRE(value == 0x07FE);
}
TEST_CASE("C.SLLI (RV128)", "[rv128c]") {
uint32_t value = 0;
auto as = MakeAssembler128(value);
as.C_SLLI(x15, 64);
REQUIRE(value == 0x0782);
}
TEST_CASE("C.SQ", "[rvc]") {
uint32_t value = 0;
auto as = MakeAssembler128(value);
as.C_SQ(x15, 16, x15);
REQUIRE(value == 0xAB9C);
as.RewindBuffer();
as.C_SQ(x15, 256, x15);
REQUIRE(value == 0xA79C);
}
TEST_CASE("C.SQSP", "[rvc]") {
uint32_t value = 0;
auto as = MakeAssembler128(value);
as.C_SQSP(x15, 16);
REQUIRE(value == 0xA83E);
as.RewindBuffer();
as.C_SQSP(x15, 256);
REQUIRE(value == 0xA23E);
}
TEST_CASE("C.SRAI", "[rvc]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.C_SRAI(x15, 16);
REQUIRE(value == 0x87C1);
as.RewindBuffer();
as.C_SRAI(x15, 31);
REQUIRE(value == 0x87FD);
}
TEST_CASE("C.SRAI (RV128)", "[rv128c]") {
uint32_t value = 0;
auto as = MakeAssembler128(value);
as.C_SRAI(x15, 64);
REQUIRE(value == 0x8781);
}
TEST_CASE("C.SRLI", "[rvc]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.C_SRLI(x15, 16);
REQUIRE(value == 0x83C1);
as.RewindBuffer();
as.C_SRLI(x15, 31);
REQUIRE(value == 0x83FD);
}
TEST_CASE("C.SRLI (RV128)", "[rv128c]") {
uint32_t value = 0;
auto as = MakeAssembler128(value);
as.C_SRLI(x15, 64);
REQUIRE(value == 0x8381);
}
TEST_CASE("C.SUB", "[rvc]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.C_SUB(x15, x15);
REQUIRE(value == 0x8F9D);
as.RewindBuffer();
as.C_SUB(x15, x8);
REQUIRE(value == 0x8F81);
}
TEST_CASE("C.SUBW", "[rvc]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.C_SUBW(x15, x15);
REQUIRE(value == 0x9F9D);
as.RewindBuffer();
as.C_SUBW(x15, x8);
REQUIRE(value == 0x9F81);
}
TEST_CASE("C.SW", "[rvc]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.C_SW(x15, 16, x15);
REQUIRE(value == 0xCB9C);
as.RewindBuffer();
as.C_SW(x15, 24, x15);
REQUIRE(value == 0xCF9C);
}
TEST_CASE("C.SWSP", "[rvc]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.C_SWSP(x15, 16);
REQUIRE(value == 0xC83E);
as.RewindBuffer();
as.C_SWSP(x15, 24);
REQUIRE(value == 0xCC3E);
}
TEST_CASE("C.UNDEF", "[rvc]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.C_UNDEF();
REQUIRE(value == 0);
}
TEST_CASE("C.XOR", "[rvc]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.C_XOR(x15, x15);
REQUIRE(value == 0x8FBD);
as.RewindBuffer();
as.C_XOR(x15, x8);
REQUIRE(value == 0x8FA1);
}

View file

@ -0,0 +1,528 @@
#include <catch/catch.hpp>
#include <biscuit/assembler.hpp>
#include "assembler_test_utils.hpp"
using namespace biscuit;
TEST_CASE("FADD.D", "[rv32d]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FADD_D(f31, f7, f26, RMode::RNE);
REQUIRE(value == 0x03A38FD3);
as.RewindBuffer();
as.FADD_D(f31, f7, f26, RMode::RMM);
REQUIRE(value == 0x03A3CFD3);
as.RewindBuffer();
as.FADD_D(f31, f7, f26, RMode::DYN);
REQUIRE(value == 0x03A3FFD3);
}
TEST_CASE("FCLASS.D", "[rv32d]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FCLASS_D(x31, f7);
REQUIRE(value == 0xE2039FD3);
as.RewindBuffer();
as.FCLASS_D(x7, f31);
REQUIRE(value == 0xE20F93D3);
}
TEST_CASE("FCVT.D.S", "[rv32d]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FCVT_D_S(f31, f7, RMode::RNE);
REQUIRE(value == 0x42038FD3);
as.RewindBuffer();
as.FCVT_D_S(f31, f7, RMode::RMM);
REQUIRE(value == 0x4203CFD3);
as.RewindBuffer();
as.FCVT_D_S(f31, f7, RMode::DYN);
REQUIRE(value == 0x4203FFD3);
}
TEST_CASE("FCVT.D.W", "[rv32d]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FCVT_D_W(f31, x7, RMode::RNE);
REQUIRE(value == 0xD2038FD3);
as.RewindBuffer();
as.FCVT_D_W(f31, x7, RMode::RMM);
REQUIRE(value == 0xD203CFD3);
as.RewindBuffer();
as.FCVT_D_W(f31, x7, RMode::DYN);
REQUIRE(value == 0xD203FFD3);
}
TEST_CASE("FCVT.D.WU", "[rv32d]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FCVT_D_WU(f31, x7, RMode::RNE);
REQUIRE(value == 0xD2138FD3);
as.RewindBuffer();
as.FCVT_D_WU(f31, x7, RMode::RMM);
REQUIRE(value == 0xD213CFD3);
as.RewindBuffer();
as.FCVT_D_WU(f31, x7, RMode::DYN);
REQUIRE(value == 0xD213FFD3);
}
TEST_CASE("FCVT.L.D", "[rv64d]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.FCVT_L_D(x31, f7, RMode::RNE);
REQUIRE(value == 0xC2238FD3);
as.RewindBuffer();
as.FCVT_L_D(x31, f7, RMode::RMM);
REQUIRE(value == 0xC223CFD3);
as.RewindBuffer();
as.FCVT_L_D(x31, f7, RMode::DYN);
REQUIRE(value == 0xC223FFD3);
}
TEST_CASE("FCVT.LU.D", "[rv64d]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.FCVT_LU_D(x31, f7, RMode::RNE);
REQUIRE(value == 0xC2338FD3);
as.RewindBuffer();
as.FCVT_LU_D(x31, f7, RMode::RMM);
REQUIRE(value == 0xC233CFD3);
as.RewindBuffer();
as.FCVT_LU_D(x31, f7, RMode::DYN);
REQUIRE(value == 0xC233FFD3);
}
TEST_CASE("FCVT.D.L", "[rv64d]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.FCVT_D_L(f31, x7, RMode::RNE);
REQUIRE(value == 0xD2238FD3);
as.RewindBuffer();
as.FCVT_D_L(f31, x7, RMode::RMM);
REQUIRE(value == 0xD223CFD3);
as.RewindBuffer();
as.FCVT_D_L(f31, x7, RMode::DYN);
REQUIRE(value == 0xD223FFD3);
}
TEST_CASE("FCVT.D.LU", "[rv64d]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.FCVT_D_LU(f31, x7, RMode::RNE);
REQUIRE(value == 0xD2338FD3);
as.RewindBuffer();
as.FCVT_D_LU(f31, x7, RMode::RMM);
REQUIRE(value == 0xD233CFD3);
as.RewindBuffer();
as.FCVT_D_LU(f31, x7, RMode::DYN);
REQUIRE(value == 0xD233FFD3);
}
TEST_CASE("FCVT.W.D", "[rv32d]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FCVT_W_D(x31, f7, RMode::RNE);
REQUIRE(value == 0xC2038FD3);
as.RewindBuffer();
as.FCVT_W_D(x31, f7, RMode::RMM);
REQUIRE(value == 0xC203CFD3);
as.RewindBuffer();
as.FCVT_W_D(x31, f7, RMode::DYN);
REQUIRE(value == 0xC203FFD3);
}
TEST_CASE("FCVT.WU.D", "[rv32d]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FCVT_WU_D(x31, f7, RMode::RNE);
REQUIRE(value == 0xC2138FD3);
as.RewindBuffer();
as.FCVT_WU_D(x31, f7, RMode::RMM);
REQUIRE(value == 0xC213CFD3);
as.RewindBuffer();
as.FCVT_WU_D(x31, f7, RMode::DYN);
REQUIRE(value == 0xC213FFD3);
}
TEST_CASE("FCVT.S.D", "[rv32d]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FCVT_S_D(f31, f7, RMode::RNE);
REQUIRE(value == 0x40138FD3);
as.RewindBuffer();
as.FCVT_S_D(f31, f7, RMode::RMM);
REQUIRE(value == 0x4013CFD3);
as.RewindBuffer();
as.FCVT_S_D(f31, f7, RMode::DYN);
REQUIRE(value == 0x4013FFD3);
}
TEST_CASE("FDIV.D", "[rv32d]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FDIV_D(f31, f7, f26, RMode::RNE);
REQUIRE(value == 0x1BA38FD3);
as.RewindBuffer();
as.FDIV_D(f31, f7, f26, RMode::RMM);
REQUIRE(value == 0x1BA3CFD3);
as.RewindBuffer();
as.FDIV_D(f31, f7, f26, RMode::DYN);
REQUIRE(value == 0x1BA3FFD3);
}
TEST_CASE("FEQ.D", "[rv32d]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FEQ_D(x31, f7, f26);
REQUIRE(value == 0xA3A3AFD3);
as.RewindBuffer();
as.FEQ_D(x31, f26, f7);
REQUIRE(value == 0xA27D2FD3);
}
TEST_CASE("FLE.D", "[rv32d]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FLE_D(x31, f7, f26);
REQUIRE(value == 0xA3A38FD3);
as.RewindBuffer();
as.FLE_D(x31, f26, f7);
REQUIRE(value == 0xA27D0FD3);
}
TEST_CASE("FLT.D", "[rv32d]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FLT_D(x31, f7, f26);
REQUIRE(value == 0xA3A39FD3);
as.RewindBuffer();
as.FLT_D(x31, f26, f7);
REQUIRE(value == 0xA27D1FD3);
}
TEST_CASE("FLD", "[rv32d]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FLD(f15, 1024, x31);
REQUIRE(value == 0x400FB787);
as.RewindBuffer();
as.FLD(f15, 1536, x31);
REQUIRE(value == 0x600FB787);
as.RewindBuffer();
as.FLD(f15, -1, x31);
REQUIRE(value == 0xFFFFB787);
}
TEST_CASE("FMADD.D", "[rv32d]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FMADD_D(f15, f31, f7, f26, RMode::RNE);
REQUIRE(value == 0xD27F87C3);
as.RewindBuffer();
as.FMADD_D(f15, f31, f7, f26, RMode::RMM);
REQUIRE(value == 0xD27FC7C3);
as.RewindBuffer();
as.FMADD_D(f15, f31, f7, f26, RMode::DYN);
REQUIRE(value == 0xD27FF7C3);
}
TEST_CASE("FMAX.D", "[rv32d]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FMAX_D(f31, f7, f26);
REQUIRE(value == 0x2BA39FD3);
as.RewindBuffer();
as.FMAX_D(f31, f31, f31);
REQUIRE(value == 0x2BFF9FD3);
}
TEST_CASE("FMIN.D", "[rv32d]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FMIN_D(f31, f7, f26);
REQUIRE(value == 0x2BA38FD3);
as.RewindBuffer();
as.FMIN_D(f31, f31, f31);
REQUIRE(value == 0x2BFF8FD3);
}
TEST_CASE("FMSUB.D", "[rv32d]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FMSUB_D(f15, f31, f7, f26, RMode::RNE);
REQUIRE(value == 0xD27F87C7);
as.RewindBuffer();
as.FMSUB_D(f15, f31, f7, f26, RMode::RMM);
REQUIRE(value == 0xD27FC7C7);
as.RewindBuffer();
as.FMSUB_D(f15, f31, f7, f26, RMode::DYN);
REQUIRE(value == 0xD27FF7C7);
}
TEST_CASE("FMUL.D", "[rv32d]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FMUL_D(f31, f7, f26, RMode::RNE);
REQUIRE(value == 0x13A38FD3);
as.RewindBuffer();
as.FMUL_D(f31, f7, f26, RMode::RMM);
REQUIRE(value == 0x13A3CFD3);
as.RewindBuffer();
as.FMUL_D(f31, f7, f26, RMode::DYN);
REQUIRE(value == 0x13A3FFD3);
}
TEST_CASE("FMV.D.X", "[rv64d]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.FMV_D_X(f31, x7);
REQUIRE(value == 0xF2038FD3);
as.RewindBuffer();
as.FMV_D_X(f7, x31);
REQUIRE(value == 0xF20F83D3);
}
TEST_CASE("FMV.X.D", "[rv64d]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.FMV_X_D(x31, f7);
REQUIRE(value == 0xE2038FD3);
as.RewindBuffer();
as.FMV_X_D(x7, f31);
REQUIRE(value == 0xE20F83D3);
}
TEST_CASE("FNMADD.D", "[rv32d]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FNMADD_D(f15, f31, f7, f26, RMode::RNE);
REQUIRE(value == 0xD27F87CF);
as.RewindBuffer();
as.FNMADD_D(f15, f31, f7, f26, RMode::RMM);
REQUIRE(value == 0xD27FC7CF);
as.RewindBuffer();
as.FNMADD_D(f15, f31, f7, f26, RMode::DYN);
REQUIRE(value == 0xD27FF7CF);
}
TEST_CASE("FNMSUB.D", "[rv32d]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FNMSUB_D(f15, f31, f7, f26, RMode::RNE);
REQUIRE(value == 0xD27F87CB);
as.RewindBuffer();
as.FNMSUB_D(f15, f31, f7, f26, RMode::RMM);
REQUIRE(value == 0xD27FC7CB);
as.RewindBuffer();
as.FNMSUB_D(f15, f31, f7, f26, RMode::DYN);
REQUIRE(value == 0xD27FF7CB);
}
TEST_CASE("FSGNJ.D", "[rv32d]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FSGNJ_D(f31, f7, f26);
REQUIRE(value == 0x23A38FD3);
as.RewindBuffer();
as.FSGNJ_D(f31, f31, f31);
REQUIRE(value == 0x23FF8FD3);
}
TEST_CASE("FSGNJN.D", "[rv32d]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FSGNJN_D(f31, f7, f26);
REQUIRE(value == 0x23A39FD3);
as.RewindBuffer();
as.FSGNJN_D(f31, f31, f31);
REQUIRE(value == 0x23FF9FD3);
}
TEST_CASE("FSGNJX.D", "[rv32d]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FSGNJX_D(f31, f7, f26);
REQUIRE(value == 0x23A3AFD3);
as.RewindBuffer();
as.FSGNJX_D(f31, f31, f31);
REQUIRE(value == 0x23FFAFD3);
}
TEST_CASE("FSQRT.D", "[rv32d]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FSQRT_D(f31, f7, RMode::RNE);
REQUIRE(value == 0x5A038FD3);
as.RewindBuffer();
as.FSQRT_D(f31, f7, RMode::RMM);
REQUIRE(value == 0x5A03CFD3);
as.RewindBuffer();
as.FSQRT_D(f31, f7, RMode::DYN);
REQUIRE(value == 0x5A03FFD3);
}
TEST_CASE("FSUB.D", "[rv32d]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FSUB_D(f31, f7, f26, RMode::RNE);
REQUIRE(value == 0x0BA38FD3);
as.RewindBuffer();
as.FSUB_D(f31, f7, f26, RMode::RMM);
REQUIRE(value == 0x0BA3CFD3);
as.RewindBuffer();
as.FSUB_D(f31, f7, f26, RMode::DYN);
REQUIRE(value == 0x0BA3FFD3);
}
TEST_CASE("FSD", "[rv32d]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FSD(f31, 1024, x15);
REQUIRE(value == 0x41F7B027);
as.RewindBuffer();
as.FSD(f31, 1536, x15);
REQUIRE(value == 0x61F7B027);
as.RewindBuffer();
as.FSD(f31, -1, x15);
REQUIRE(value == 0xFFF7BFA7);
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,384 @@
#include <catch/catch.hpp>
#include <biscuit/assembler.hpp>
#include "assembler_test_utils.hpp"
using namespace biscuit;
TEST_CASE("AES32DSI", "[rvk]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.AES32DSI(x31, x31, x31, 0b11);
REQUIRE(value == 0xEBFF8FB3);
as.RewindBuffer();
as.AES32DSI(x1, x2, x3, 0b10);
REQUIRE(value == 0xAA3100B3);
}
TEST_CASE("AES32DSMI", "[rvk]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.AES32DSMI(x31, x31, x31, 0b11);
REQUIRE(value == 0xEFFF8FB3);
as.RewindBuffer();
as.AES32DSMI(x1, x2, x3, 0b10);
REQUIRE(value == 0xAE3100B3);
}
TEST_CASE("AES32ESI", "[rvk]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.AES32ESI(x31, x31, x31, 0b11);
REQUIRE(value == 0xE3FF8FB3);
as.RewindBuffer();
as.AES32ESI(x1, x2, x3, 0b10);
REQUIRE(value == 0xA23100B3);
}
TEST_CASE("AES32ESMI", "[rvk]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.AES32ESMI(x31, x31, x31, 0b11);
REQUIRE(value == 0xE7FF8FB3);
as.RewindBuffer();
as.AES32ESMI(x1, x2, x3, 0b10);
REQUIRE(value == 0xA63100B3);
}
TEST_CASE("AES64DS", "[rvk]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.AES64DS(x31, x31, x31);
REQUIRE(value == 0x3BFF8FB3);
as.RewindBuffer();
as.AES64DS(x1, x2, x3);
REQUIRE(value == 0x3A3100B3);
}
TEST_CASE("AES64DSM", "[rvk]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.AES64DSM(x31, x31, x31);
REQUIRE(value == 0x3FFF8FB3);
as.RewindBuffer();
as.AES64DSM(x1, x2, x3);
REQUIRE(value == 0x3E3100B3);
}
TEST_CASE("AES64ES", "[rvk]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.AES64ES(x31, x31, x31);
REQUIRE(value == 0x33FF8FB3);
as.RewindBuffer();
as.AES64ES(x1, x2, x3);
REQUIRE(value == 0x323100B3);
}
TEST_CASE("AES64ESM", "[rvk]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.AES64ESM(x31, x31, x31);
REQUIRE(value == 0x37FF8FB3);
as.RewindBuffer();
as.AES64ESM(x1, x2, x3);
REQUIRE(value == 0x363100B3);
}
TEST_CASE("AES64IM", "[rvk]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.AES64IM(x31, x31);
REQUIRE(value == 0x300F9F93);
as.RewindBuffer();
as.AES64IM(x1, x2);
REQUIRE(value == 0x30011093);
}
TEST_CASE("AES64KS1I", "[rvk]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.AES64KS1I(x31, x31, 0xA);
REQUIRE(value == 0x31AF9F93);
as.RewindBuffer();
as.AES64KS1I(x1, x2, 0x5);
REQUIRE(value == 0x31511093);
}
TEST_CASE("AES64KS2", "[rvk]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.AES64KS2(x31, x31, x31);
REQUIRE(value == 0x7FFF8FB3);
as.RewindBuffer();
as.AES64KS2(x1, x2, x3);
REQUIRE(value == 0x7E3100B3);
}
TEST_CASE("SHA256SIG0", "[rvk]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.SHA256SIG0(x31, x31);
REQUIRE(value == 0x102F9F93);
as.RewindBuffer();
as.SHA256SIG0(x1, x2);
REQUIRE(value == 0x10211093);
}
TEST_CASE("SHA256SIG1", "[rvk]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.SHA256SIG1(x31, x31);
REQUIRE(value == 0x103F9F93);
as.RewindBuffer();
as.SHA256SIG1(x1, x2);
REQUIRE(value == 0x10311093);
}
TEST_CASE("SHA256SUM0", "[rvk]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.SHA256SUM0(x31, x31);
REQUIRE(value == 0x100F9F93);
as.RewindBuffer();
as.SHA256SUM0(x1, x2);
REQUIRE(value == 0x10011093);
}
TEST_CASE("SHA256SUM1", "[rvk]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.SHA256SUM1(x31, x31);
REQUIRE(value == 0x101F9F93);
as.RewindBuffer();
as.SHA256SUM1(x1, x2);
REQUIRE(value == 0x10111093);
}
TEST_CASE("SHA512SIG0", "[rvk]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.SHA512SIG0(x31, x31);
REQUIRE(value == 0x106F9F93);
as.RewindBuffer();
as.SHA512SIG0(x1, x2);
REQUIRE(value == 0x10611093);
}
TEST_CASE("SHA512SIG0H", "[rvk]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.SHA512SIG0H(x31, x31, x31);
REQUIRE(value == 0x5DFF8FB3);
as.RewindBuffer();
as.SHA512SIG0H(x1, x2, x3);
REQUIRE(value == 0x5C3100B3);
}
TEST_CASE("SHA512SIG0L", "[rvk]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.SHA512SIG0L(x31, x31, x31);
REQUIRE(value == 0x55FF8FB3);
as.RewindBuffer();
as.SHA512SIG0L(x1, x2, x3);
REQUIRE(value == 0x543100B3);
}
TEST_CASE("SHA512SIG1", "[rvk]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.SHA512SIG1(x31, x31);
REQUIRE(value == 0x107F9F93);
as.RewindBuffer();
as.SHA512SIG1(x1, x2);
REQUIRE(value == 0x10711093);
}
TEST_CASE("SHA512SIG1H", "[rvk]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.SHA512SIG1H(x31, x31, x31);
REQUIRE(value == 0x5FFF8FB3);
as.RewindBuffer();
as.SHA512SIG1H(x1, x2, x3);
REQUIRE(value == 0x5E3100B3);
}
TEST_CASE("SHA512SIG1L", "[rvk]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.SHA512SIG1L(x31, x31, x31);
REQUIRE(value == 0x57FF8FB3);
as.RewindBuffer();
as.SHA512SIG1L(x1, x2, x3);
REQUIRE(value == 0x563100B3);
}
TEST_CASE("SHA512SUM0", "[rvk]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.SHA512SUM0(x31, x31);
REQUIRE(value == 0x104F9F93);
as.RewindBuffer();
as.SHA512SUM0(x1, x2);
REQUIRE(value == 0x10411093);
}
TEST_CASE("SHA512SUM0R", "[rvk]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.SHA512SUM0R(x31, x31, x31);
REQUIRE(value == 0x51FF8FB3);
as.RewindBuffer();
as.SHA512SUM0R(x1, x2, x3);
REQUIRE(value == 0x503100B3);
}
TEST_CASE("SHA512SUM1", "[rvk]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.SHA512SUM1(x31, x31);
REQUIRE(value == 0x105F9F93);
as.RewindBuffer();
as.SHA512SUM1(x1, x2);
REQUIRE(value == 0x10511093);
}
TEST_CASE("SHA512SUM1R", "[rvk]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.SHA512SUM1R(x31, x31, x31);
REQUIRE(value == 0x53FF8FB3);
as.RewindBuffer();
as.SHA512SUM1R(x1, x2, x3);
REQUIRE(value == 0x523100B3);
}
TEST_CASE("SM3P0", "[rvk]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.SM3P0(x31, x31);
REQUIRE(value == 0x108F9F93);
as.RewindBuffer();
as.SM3P0(x1, x2);
REQUIRE(value == 0x10811093);
}
TEST_CASE("SM3P1", "[rvk]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.SM3P1(x31, x31);
REQUIRE(value == 0x109F9F93);
as.RewindBuffer();
as.SM3P1(x1, x2);
REQUIRE(value == 0x10911093);
}
TEST_CASE("SM4ED", "[rvk]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.SM4ED(x31, x31, x31, 0b11);
REQUIRE(value == 0xF1FF8FB3);
as.RewindBuffer();
as.SM4ED(x1, x2, x3, 0b10);
REQUIRE(value == 0xB03100B3);
}
TEST_CASE("SM4KS", "[rvk]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.SM4KS(x31, x31, x31, 0b11);
REQUIRE(value == 0xF5FF8FB3);
as.RewindBuffer();
as.SM4KS(x1, x2, x3, 0b10);
REQUIRE(value == 0xB43100B3);
}

View file

@ -0,0 +1,241 @@
#include <catch/catch.hpp>
#include <biscuit/assembler.hpp>
#include "assembler_test_utils.hpp"
using namespace biscuit;
TEST_CASE("DIV", "[rv32m]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.DIV(x31, x15, x20);
REQUIRE(value == 0x0347CFB3);
as.RewindBuffer();
as.DIV(x31, x20, x15);
REQUIRE(value == 0x02FA4FB3);
as.RewindBuffer();
as.DIV(x20, x31, x15);
REQUIRE(value == 0x02FFCA33);
}
TEST_CASE("DIVW", "[rv64m]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.DIVW(x31, x15, x20);
REQUIRE(value == 0x0347CFBB);
as.RewindBuffer();
as.DIVW(x31, x20, x15);
REQUIRE(value == 0x02FA4FBB);
as.RewindBuffer();
as.DIVW(x20, x31, x15);
REQUIRE(value == 0x02FFCA3B);
}
TEST_CASE("DIVU", "[rv32m]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.DIVU(x31, x15, x20);
REQUIRE(value == 0x0347DFB3);
as.RewindBuffer();
as.DIVU(x31, x20, x15);
REQUIRE(value == 0x02FA5FB3);
as.RewindBuffer();
as.DIVU(x20, x31, x15);
REQUIRE(value == 0x02FFDA33);
}
TEST_CASE("DIVUW", "[rv64m]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.DIVUW(x31, x15, x20);
REQUIRE(value == 0x0347DFBB);
as.RewindBuffer();
as.DIVUW(x31, x20, x15);
REQUIRE(value == 0x02FA5FBB);
as.RewindBuffer();
as.DIVUW(x20, x31, x15);
REQUIRE(value == 0x02FFDA3B);
}
TEST_CASE("MUL", "[rv32m]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.MUL(x31, x15, x20);
REQUIRE(value == 0x03478FB3);
as.RewindBuffer();
as.MUL(x31, x20, x15);
REQUIRE(value == 0x02FA0FB3);
as.RewindBuffer();
as.MUL(x20, x31, x15);
REQUIRE(value == 0x02FF8A33);
}
TEST_CASE("MULH", "[rv32m]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.MULH(x31, x15, x20);
REQUIRE(value == 0x03479FB3);
as.RewindBuffer();
as.MULH(x31, x20, x15);
REQUIRE(value == 0x02FA1FB3);
as.RewindBuffer();
as.MULH(x20, x31, x15);
REQUIRE(value == 0x02FF9A33);
}
TEST_CASE("MULW", "[rv64m]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.MULW(x31, x15, x20);
REQUIRE(value == 0x03478FBB);
as.RewindBuffer();
as.MULW(x31, x20, x15);
REQUIRE(value == 0x02FA0FBB);
as.RewindBuffer();
as.MULW(x20, x31, x15);
REQUIRE(value == 0x02FF8A3B);
}
TEST_CASE("MULHSU", "[rv32m]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.MULHSU(x31, x15, x20);
REQUIRE(value == 0x0347AFB3);
as.RewindBuffer();
as.MULHSU(x31, x20, x15);
REQUIRE(value == 0x02FA2FB3);
as.RewindBuffer();
as.MULHSU(x20, x31, x15);
REQUIRE(value == 0x02FFAA33);
}
TEST_CASE("MULHU", "[rv32m]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.MULHU(x31, x15, x20);
REQUIRE(value == 0x0347BFB3);
as.RewindBuffer();
as.MULHU(x31, x20, x15);
REQUIRE(value == 0x02FA3FB3);
as.RewindBuffer();
as.MULHU(x20, x31, x15);
REQUIRE(value == 0x02FFBA33);
}
TEST_CASE("REM", "[rv32m]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.REM(x31, x15, x20);
REQUIRE(value == 0x0347EFB3);
as.RewindBuffer();
as.REM(x31, x20, x15);
REQUIRE(value == 0x02FA6FB3);
as.RewindBuffer();
as.REM(x20, x31, x15);
REQUIRE(value == 0x02FFEA33);
}
TEST_CASE("REMW", "[rv64m]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.REMW(x31, x15, x20);
REQUIRE(value == 0x0347EFBB);
as.RewindBuffer();
as.REMW(x31, x20, x15);
REQUIRE(value == 0x02FA6FBB);
as.RewindBuffer();
as.REMW(x20, x31, x15);
REQUIRE(value == 0x02FFEA3B);
}
TEST_CASE("REMU", "[rv32m]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.REMU(x31, x15, x20);
REQUIRE(value == 0x0347FFB3);
as.RewindBuffer();
as.REMU(x31, x20, x15);
REQUIRE(value == 0x02FA7FB3);
as.RewindBuffer();
as.REMU(x20, x31, x15);
REQUIRE(value == 0x02FFFA33);
}
TEST_CASE("REMUW", "[rv64m]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.REMUW(x31, x15, x20);
REQUIRE(value == 0x0347FFBB);
as.RewindBuffer();
as.REMUW(x31, x20, x15);
REQUIRE(value == 0x02FA7FBB);
as.RewindBuffer();
as.REMUW(x20, x31, x15);
REQUIRE(value == 0x02FFFA3B);
}

View file

@ -0,0 +1,538 @@
#include <catch/catch.hpp>
#include <biscuit/assembler.hpp>
#include "assembler_test_utils.hpp"
using namespace biscuit;
TEST_CASE("FADD.Q", "[rv32q]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FADD_Q(f31, f7, f26, RMode::RNE);
REQUIRE(value == 0x07A38FD3);
as.RewindBuffer();
as.FADD_Q(f31, f7, f26, RMode::RMM);
REQUIRE(value == 0x07A3CFD3);
as.RewindBuffer();
as.FADD_Q(f31, f7, f26, RMode::DYN);
REQUIRE(value == 0x07A3FFD3);
}
TEST_CASE("FCLASS.Q", "[rv32q]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FCLASS_Q(x31, f7);
REQUIRE(value == 0xE6039FD3);
as.RewindBuffer();
as.FCLASS_Q(x7, f31);
REQUIRE(value == 0xE60F93D3);
}
TEST_CASE("FCVT.Q.D", "[rv32q]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FCVT_Q_D(f31, f7, RMode::RNE);
REQUIRE(value == 0x46138FD3);
as.RewindBuffer();
as.FCVT_Q_D(f31, f7, RMode::RMM);
REQUIRE(value == 0x4613CFD3);
as.RewindBuffer();
as.FCVT_Q_D(f31, f7, RMode::DYN);
REQUIRE(value == 0x4613FFD3);
}
TEST_CASE("FCVT.Q.S", "[rv32q]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FCVT_Q_S(f31, f7, RMode::RNE);
REQUIRE(value == 0x46038FD3);
as.RewindBuffer();
as.FCVT_Q_S(f31, f7, RMode::RMM);
REQUIRE(value == 0x4603CFD3);
as.RewindBuffer();
as.FCVT_Q_S(f31, f7, RMode::DYN);
REQUIRE(value == 0x4603FFD3);
}
TEST_CASE("FCVT.Q.W", "[rv32q]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FCVT_Q_W(f31, x7, RMode::RNE);
REQUIRE(value == 0xD6038FD3);
as.RewindBuffer();
as.FCVT_Q_W(f31, x7, RMode::RMM);
REQUIRE(value == 0xD603CFD3);
as.RewindBuffer();
as.FCVT_Q_W(f31, x7, RMode::DYN);
REQUIRE(value == 0xD603FFD3);
}
TEST_CASE("FCVT.Q.WU", "[rv32q]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FCVT_Q_WU(f31, x7, RMode::RNE);
REQUIRE(value == 0xD6138FD3);
as.RewindBuffer();
as.FCVT_Q_WU(f31, x7, RMode::RMM);
REQUIRE(value == 0xD613CFD3);
as.RewindBuffer();
as.FCVT_Q_WU(f31, x7, RMode::DYN);
REQUIRE(value == 0xD613FFD3);
}
TEST_CASE("FCVT.L.Q", "[rv64q]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.FCVT_L_Q(x31, f7, RMode::RNE);
REQUIRE(value == 0xC6238FD3);
as.RewindBuffer();
as.FCVT_L_Q(x31, f7, RMode::RMM);
REQUIRE(value == 0xC623CFD3);
as.RewindBuffer();
as.FCVT_L_Q(x31, f7, RMode::DYN);
REQUIRE(value == 0xC623FFD3);
}
TEST_CASE("FCVT.LU.Q", "[rv64q]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.FCVT_LU_Q(x31, f7, RMode::RNE);
REQUIRE(value == 0xC6338FD3);
as.RewindBuffer();
as.FCVT_LU_Q(x31, f7, RMode::RMM);
REQUIRE(value == 0xC633CFD3);
as.RewindBuffer();
as.FCVT_LU_Q(x31, f7, RMode::DYN);
REQUIRE(value == 0xC633FFD3);
}
TEST_CASE("FCVT.Q.L", "[rv64q]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.FCVT_Q_L(f31, x7, RMode::RNE);
REQUIRE(value == 0xD6238FD3);
as.RewindBuffer();
as.FCVT_Q_L(f31, x7, RMode::RMM);
REQUIRE(value == 0xD623CFD3);
as.RewindBuffer();
as.FCVT_Q_L(f31, x7, RMode::DYN);
REQUIRE(value == 0xD623FFD3);
}
TEST_CASE("FCVT.Q.LU", "[rv64q]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.FCVT_Q_LU(f31, x7, RMode::RNE);
REQUIRE(value == 0xD6338FD3);
as.RewindBuffer();
as.FCVT_Q_LU(f31, x7, RMode::RMM);
REQUIRE(value == 0xD633CFD3);
as.RewindBuffer();
as.FCVT_Q_LU(f31, x7, RMode::DYN);
REQUIRE(value == 0xD633FFD3);
}
TEST_CASE("FCVT.W.Q", "[rv32q]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FCVT_W_Q(x31, f7, RMode::RNE);
REQUIRE(value == 0xC6038FD3);
as.RewindBuffer();
as.FCVT_W_Q(x31, f7, RMode::RMM);
REQUIRE(value == 0xC603CFD3);
as.RewindBuffer();
as.FCVT_W_Q(x31, f7, RMode::DYN);
REQUIRE(value == 0xC603FFD3);
}
TEST_CASE("FCVT.WU.Q", "[rv32q]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FCVT_WU_Q(x31, f7, RMode::RNE);
REQUIRE(value == 0xC6138FD3);
as.RewindBuffer();
as.FCVT_WU_Q(x31, f7, RMode::RMM);
REQUIRE(value == 0xC613CFD3);
as.RewindBuffer();
as.FCVT_WU_Q(x31, f7, RMode::DYN);
REQUIRE(value == 0xC613FFD3);
}
TEST_CASE("FCVT.D.Q", "[rv32q]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FCVT_D_Q(f31, f7, RMode::RNE);
REQUIRE(value == 0x42338FD3);
as.RewindBuffer();
as.FCVT_D_Q(f31, f7, RMode::RMM);
REQUIRE(value == 0x4233CFD3);
as.RewindBuffer();
as.FCVT_D_Q(f31, f7, RMode::DYN);
REQUIRE(value == 0x4233FFD3);
}
TEST_CASE("FCVT.S.Q", "[rv32q]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FCVT_S_Q(f31, f7, RMode::RNE);
REQUIRE(value == 0x40338FD3);
as.RewindBuffer();
as.FCVT_S_Q(f31, f7, RMode::RMM);
REQUIRE(value == 0x4033CFD3);
as.RewindBuffer();
as.FCVT_S_Q(f31, f7, RMode::DYN);
REQUIRE(value == 0x4033FFD3);
}
TEST_CASE("FDIV.Q", "[rv32q]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FDIV_Q(f31, f7, f26, RMode::RNE);
REQUIRE(value == 0x1FA38FD3);
as.RewindBuffer();
as.FDIV_Q(f31, f7, f26, RMode::RMM);
REQUIRE(value == 0x1FA3CFD3);
as.RewindBuffer();
as.FDIV_Q(f31, f7, f26, RMode::DYN);
REQUIRE(value == 0x1FA3FFD3);
}
TEST_CASE("FEQ.Q", "[rv32q]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FEQ_Q(x31, f7, f26);
REQUIRE(value == 0xA7A3AFD3);
as.RewindBuffer();
as.FEQ_Q(x31, f26, f7);
REQUIRE(value == 0xA67D2FD3);
}
TEST_CASE("FLE.Q", "[rv32q]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FLE_Q(x31, f7, f26);
REQUIRE(value == 0xA7A38FD3);
as.RewindBuffer();
as.FLE_Q(x31, f26, f7);
REQUIRE(value == 0xA67D0FD3);
}
TEST_CASE("FLT.Q", "[rv32q]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FLT_Q(x31, f7, f26);
REQUIRE(value == 0xA7A39FD3);
as.RewindBuffer();
as.FLT_Q(x31, f26, f7);
REQUIRE(value == 0xA67D1FD3);
}
TEST_CASE("FLQ", "[rv32q]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FLQ(f15, 1024, x31);
REQUIRE(value == 0x400FC787);
as.RewindBuffer();
as.FLQ(f15, 1536, x31);
REQUIRE(value == 0x600FC787);
as.RewindBuffer();
as.FLQ(f15, -1, x31);
REQUIRE(value == 0xFFFFC787);
}
TEST_CASE("FMADD.Q", "[rv32q]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FMADD_Q(f15, f31, f7, f26, RMode::RNE);
REQUIRE(value == 0xD67F87C3);
as.RewindBuffer();
as.FMADD_Q(f15, f31, f7, f26, RMode::RMM);
REQUIRE(value == 0xD67FC7C3);
as.RewindBuffer();
as.FMADD_Q(f15, f31, f7, f26, RMode::DYN);
REQUIRE(value == 0xD67FF7C3);
}
TEST_CASE("FMAX.Q", "[rv32q]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FMAX_Q(f31, f7, f26);
REQUIRE(value == 0x2FA39FD3);
as.RewindBuffer();
as.FMAX_Q(f31, f31, f31);
REQUIRE(value == 0x2FFF9FD3);
}
TEST_CASE("FMIN.Q", "[rv32q]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FMIN_Q(f31, f7, f26);
REQUIRE(value == 0x2FA38FD3);
as.RewindBuffer();
as.FMIN_Q(f31, f31, f31);
REQUIRE(value == 0x2FFF8FD3);
}
TEST_CASE("FMSUB.Q", "[rv32q]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FMSUB_Q(f15, f31, f7, f26, RMode::RNE);
REQUIRE(value == 0xD67F87C7);
as.RewindBuffer();
as.FMSUB_Q(f15, f31, f7, f26, RMode::RMM);
REQUIRE(value == 0xD67FC7C7);
as.RewindBuffer();
as.FMSUB_Q(f15, f31, f7, f26, RMode::DYN);
REQUIRE(value == 0xD67FF7C7);
}
TEST_CASE("FMUL.Q", "[rv32q]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FMUL_Q(f31, f7, f26, RMode::RNE);
REQUIRE(value == 0x17A38FD3);
as.RewindBuffer();
as.FMUL_Q(f31, f7, f26, RMode::RMM);
REQUIRE(value == 0x17A3CFD3);
as.RewindBuffer();
as.FMUL_Q(f31, f7, f26, RMode::DYN);
REQUIRE(value == 0x17A3FFD3);
}
TEST_CASE("FNMADD.Q", "[rv32q]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FNMADD_Q(f15, f31, f7, f26, RMode::RNE);
REQUIRE(value == 0xD67F87CF);
as.RewindBuffer();
as.FNMADD_Q(f15, f31, f7, f26, RMode::RMM);
REQUIRE(value == 0xD67FC7CF);
as.RewindBuffer();
as.FNMADD_Q(f15, f31, f7, f26, RMode::DYN);
REQUIRE(value == 0xD67FF7CF);
}
TEST_CASE("FNMSUB.Q", "[rv32q]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FNMSUB_Q(f15, f31, f7, f26, RMode::RNE);
REQUIRE(value == 0xD67F87CB);
as.RewindBuffer();
as.FNMSUB_Q(f15, f31, f7, f26, RMode::RMM);
REQUIRE(value == 0xD67FC7CB);
as.RewindBuffer();
as.FNMSUB_Q(f15, f31, f7, f26, RMode::DYN);
REQUIRE(value == 0xD67FF7CB);
}
TEST_CASE("FSGNJ.Q", "[rv32q]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FSGNJ_Q(f31, f7, f26);
REQUIRE(value == 0x27A38FD3);
as.RewindBuffer();
as.FSGNJ_Q(f31, f31, f31);
REQUIRE(value == 0x27FF8FD3);
}
TEST_CASE("FSGNJN.Q", "[rv32q]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FSGNJN_Q(f31, f7, f26);
REQUIRE(value == 0x27A39FD3);
as.RewindBuffer();
as.FSGNJN_Q(f31, f31, f31);
REQUIRE(value == 0x27FF9FD3);
}
TEST_CASE("FSGNJX.Q", "[rv32q]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FSGNJX_Q(f31, f7, f26);
REQUIRE(value == 0x27A3AFD3);
as.RewindBuffer();
as.FSGNJX_Q(f31, f31, f31);
REQUIRE(value == 0x27FFAFD3);
}
TEST_CASE("FSQRT.Q", "[rv32q]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FSQRT_Q(f31, f7, RMode::RNE);
REQUIRE(value == 0x5E038FD3);
as.RewindBuffer();
as.FSQRT_Q(f31, f7, RMode::RMM);
REQUIRE(value == 0x5E03CFD3);
as.RewindBuffer();
as.FSQRT_Q(f31, f7, RMode::DYN);
REQUIRE(value == 0x5E03FFD3);
}
TEST_CASE("FSUB.Q", "[rv32q]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FSUB_Q(f31, f7, f26, RMode::RNE);
REQUIRE(value == 0x0FA38FD3);
as.RewindBuffer();
as.FSUB_Q(f31, f7, f26, RMode::RMM);
REQUIRE(value == 0x0FA3CFD3);
as.RewindBuffer();
as.FSUB_Q(f31, f7, f26, RMode::DYN);
REQUIRE(value == 0x0FA3FFD3);
}
TEST_CASE("FSQ", "[rv32q]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FSQ(f31, 1024, x15);
REQUIRE(value == 0x41F7C027);
as.RewindBuffer();
as.FSQ(f31, 1536, x15);
REQUIRE(value == 0x61F7C027);
as.RewindBuffer();
as.FSQ(f31, -1, x15);
REQUIRE(value == 0xFFF7CFA7);
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,23 @@
#pragma once
#include <biscuit/assembler.hpp>
#include <cstdint>
namespace biscuit {
template <typename T>
inline Assembler MakeAssembler32(T& buffer) {
return Assembler{reinterpret_cast<uint8_t*>(&buffer), sizeof(buffer), ArchFeature::RV32};
}
template <typename T>
inline Assembler MakeAssembler64(T& buffer) {
return Assembler{reinterpret_cast<uint8_t*>(&buffer), sizeof(buffer), ArchFeature::RV64};
}
template <typename T>
inline Assembler MakeAssembler128(T& buffer) {
return Assembler{reinterpret_cast<uint8_t*>(&buffer), sizeof(buffer), ArchFeature::RV128};
}
} // namespace biscuit

View file

@ -0,0 +1,495 @@
#include <catch/catch.hpp>
#include <biscuit/assembler.hpp>
#include "assembler_test_utils.hpp"
using namespace biscuit;
TEST_CASE("VANDN.VV", "[Zvbb]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.VANDN(v20, v12, v10, VecMask::Yes);
REQUIRE(value == 0x04C50A57);
as.RewindBuffer();
as.VANDN(v20, v12, v10, VecMask::No);
REQUIRE(value == 0x06C50A57);
}
TEST_CASE("VANDN.VX", "[Zvbb]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.VANDN(v20, v12, x10, VecMask::Yes);
REQUIRE(value == 0x04C54A57);
as.RewindBuffer();
as.VANDN(v20, v12, x10, VecMask::No);
REQUIRE(value == 0x06C54A57);
}
TEST_CASE("VBREV.V", "[Zvbb]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.VBREV(v20, v12, VecMask::Yes);
REQUIRE(value == 0x48C52A57);
as.RewindBuffer();
as.VBREV(v20, v12, VecMask::No);
REQUIRE(value == 0x4AC52A57);
}
TEST_CASE("VBREV8.V", "[Zvbb]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.VBREV8(v20, v12, VecMask::Yes);
REQUIRE(value == 0x48C42A57);
as.RewindBuffer();
as.VBREV8(v20, v12, VecMask::No);
REQUIRE(value == 0x4AC42A57);
}
TEST_CASE("VREV8.V", "[Zvbb]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.VREV8(v20, v12, VecMask::Yes);
REQUIRE(value == 0x48C4AA57);
as.RewindBuffer();
as.VREV8(v20, v12, VecMask::No);
REQUIRE(value == 0x4AC4AA57);
}
TEST_CASE("VCLZ.V", "[Zvbb]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.VCLZ(v20, v12, VecMask::Yes);
REQUIRE(value == 0x48C62A57);
as.RewindBuffer();
as.VCLZ(v20, v12, VecMask::No);
REQUIRE(value == 0x4AC62A57);
}
TEST_CASE("VCTZ.V", "[Zvbb]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.VCTZ(v20, v12, VecMask::Yes);
REQUIRE(value == 0x48C6AA57);
as.RewindBuffer();
as.VCTZ(v20, v12, VecMask::No);
REQUIRE(value == 0x4AC6AA57);
}
TEST_CASE("VCPOP.V", "[Zvbb]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.VCPOP(v20, v12, VecMask::Yes);
REQUIRE(value == 0x48C72A57);
as.RewindBuffer();
as.VCPOP(v20, v12, VecMask::No);
REQUIRE(value == 0x4AC72A57);
}
TEST_CASE("VROL.VV", "[Zvbb]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.VROL(v20, v12, v10, VecMask::Yes);
REQUIRE(value == 0x54C50A57);
as.RewindBuffer();
as.VROL(v20, v12, v10, VecMask::No);
REQUIRE(value == 0x56C50A57);
}
TEST_CASE("VROL.VX", "[Zvbb]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.VROL(v20, v12, x10, VecMask::Yes);
REQUIRE(value == 0x54C54A57);
as.RewindBuffer();
as.VROL(v20, v12, x10, VecMask::No);
REQUIRE(value == 0x56C54A57);
}
TEST_CASE("VROR.VV", "[Zvbb]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.VROR(v20, v12, v10, VecMask::Yes);
REQUIRE(value == 0x50C50A57);
as.RewindBuffer();
as.VROR(v20, v12, v10, VecMask::No);
REQUIRE(value == 0x52C50A57);
}
TEST_CASE("VROR.VX", "[Zvbb]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.VROR(v20, v12, x10, VecMask::Yes);
REQUIRE(value == 0x50C54A57);
as.RewindBuffer();
as.VROR(v20, v12, x10, VecMask::No);
REQUIRE(value == 0x52C54A57);
}
TEST_CASE("VROR.VI", "[Zvbb]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.VROR(v20, v12, 63, VecMask::Yes);
REQUIRE(value == 0x54CFBA57);
as.RewindBuffer();
as.VROR(v20, v12, 31, VecMask::Yes);
REQUIRE(value == 0x50CFBA57);
as.RewindBuffer();
as.VROR(v20, v12, 63, VecMask::No);
REQUIRE(value == 0x56CFBA57);
as.RewindBuffer();
as.VROR(v20, v12, 31, VecMask::No);
REQUIRE(value == 0x52CFBA57);
}
TEST_CASE("VWSLL.VV", "[Zvbb]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.VWSLL(v20, v12, v10, VecMask::Yes);
REQUIRE(value == 0xD4C50A57);
as.RewindBuffer();
as.VWSLL(v20, v12, v10, VecMask::No);
REQUIRE(value == 0xD6C50A57);
}
TEST_CASE("VWSLL.VX", "[Zvbb]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.VWSLL(v20, v12, x10, VecMask::Yes);
REQUIRE(value == 0xD4C54A57);
as.RewindBuffer();
as.VWSLL(v20, v12, x10, VecMask::No);
REQUIRE(value == 0xD6C54A57);
}
TEST_CASE("VWSLL.VI", "[Zvbb]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.VWSLL(v20, v12, 31, VecMask::Yes);
REQUIRE(value == 0xD4CFBA57);
as.RewindBuffer();
as.VWSLL(v20, v12, 15, VecMask::Yes);
REQUIRE(value == 0xD4C7BA57);
as.RewindBuffer();
as.VWSLL(v20, v12, 31, VecMask::No);
REQUIRE(value == 0xD6CFBA57);
as.RewindBuffer();
as.VWSLL(v20, v12, 15, VecMask::No);
REQUIRE(value == 0xD6C7BA57);
}
TEST_CASE("VCLMUL.VV", "[Zvbc]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.VCLMUL(v20, v12, v10, VecMask::Yes);
REQUIRE(value == 0x30C52A57);
as.RewindBuffer();
as.VCLMUL(v20, v12, v10, VecMask::No);
REQUIRE(value == 0x32C52A57);
}
TEST_CASE("VCLMUL.VX", "[Zvbc]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.VCLMUL(v20, v12, x10, VecMask::Yes);
REQUIRE(value == 0x30C56A57);
as.RewindBuffer();
as.VCLMUL(v20, v12, x10, VecMask::No);
REQUIRE(value == 0x32C56A57);
}
TEST_CASE("VCLMULH.VV", "[Zvbc]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.VCLMULH(v20, v12, v10, VecMask::Yes);
REQUIRE(value == 0x34C52A57);
as.RewindBuffer();
as.VCLMULH(v20, v12, v10, VecMask::No);
REQUIRE(value == 0x36C52A57);
}
TEST_CASE("VCLMULH.VX", "[Zvbc]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.VCLMULH(v20, v12, x10, VecMask::Yes);
REQUIRE(value == 0x34C56A57);
as.RewindBuffer();
as.VCLMULH(v20, v12, x10, VecMask::No);
REQUIRE(value == 0x36C56A57);
}
TEST_CASE("VGHSH.VV", "[Zvkg]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.VGHSH(v20, v12, v10);
REQUIRE(value == 0xB2C52A77);
}
TEST_CASE("VGMUL.VV", "[Zvkg]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.VGMUL(v20, v12);
REQUIRE(value == 0xA2C8AA77);
}
TEST_CASE("VAESDF.VV", "[Zvkned]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.VAESDF_VV(v20, v12);
REQUIRE(value == 0xA2C0AA77);
}
TEST_CASE("VAESDF.VS", "[Zvkned]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.VAESDF_VS(v20, v12);
REQUIRE(value == 0xA6C0AA77);
}
TEST_CASE("VAESDM.VV", "[Zvkned]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.VAESDM_VV(v20, v12);
REQUIRE(value == 0xA2C02A77);
}
TEST_CASE("VAESDM.VS", "[Zvkned]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.VAESDM_VS(v20, v12);
REQUIRE(value == 0xA6C02A77);
}
TEST_CASE("VAESEF.VV", "[Zvkned]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.VAESEF_VV(v20, v12);
REQUIRE(value == 0xA2C1AA77);
}
TEST_CASE("VAESEF.VS", "[Zvkned]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.VAESEF_VS(v20, v12);
REQUIRE(value == 0xA6C1AA77);
}
TEST_CASE("VAESEM.VV", "[Zvkned]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.VAESEM_VV(v20, v12);
REQUIRE(value == 0xA2C12A77);
}
TEST_CASE("VAESEM.VS", "[Zvkned]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.VAESEM_VS(v20, v12);
REQUIRE(value == 0xA6C12A77);
}
TEST_CASE("VAESKF1.VI", "[Zvkned]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
// Test mapping of out of range indices
for (const uint32_t idx : {0U, 11U, 12U, 13U, 14U, 15U}) {
as.VAESKF1(v20, v12, idx);
const auto op_base = 0x8AC02A77U;
const auto inverted_b3 = idx ^ 0b1000;
const auto verify = op_base | (inverted_b3 << 15);
REQUIRE(value == verify);
as.RewindBuffer();
}
as.VAESKF1(v20, v12, 8);
REQUIRE(value == 0x8AC42A77);
}
TEST_CASE("VAESKF2.VI", "[Zvkned]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
// Test mapping of out of range indices
for (const uint32_t idx : {0U, 1U, 15U}) {
as.VAESKF2(v20, v12, idx);
const auto op_base = 0xAAC02A77;
const auto inverted_b3 = idx ^ 0b1000;
const auto verify = op_base | (inverted_b3 << 15);
REQUIRE(value == verify);
as.RewindBuffer();
}
as.VAESKF2(v20, v12, 8);
REQUIRE(value == 0xAAC42A77);
}
TEST_CASE("VAESZ.VS", "[Zvkned]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.VAESZ(v20, v12);
REQUIRE(value == 0xA6C3AA77);
}
TEST_CASE("VSHA2MS.VV", "[Zvknhb]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.VSHA2MS(v20, v12, v10);
REQUIRE(value == 0xB6C52A77);
}
TEST_CASE("VSHA2CH.VV", "[Zvknhb]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.VSHA2CH(v20, v12, v10);
REQUIRE(value == 0xBAC52A77);
}
TEST_CASE("VSHA2CL.VV", "[Zvknhb]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.VSHA2CL(v20, v12, v10);
REQUIRE(value == 0xBEC52A77);
}
TEST_CASE("VSM4K.VI", "[Zvksed]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
for (uint32_t i = 0; i <= 7; i++) {
as.VSM4K(v20, v12, i);
const auto op_base = 0x86C02A77U;
const auto verify = op_base | (i << 15);
REQUIRE(value == verify);
as.RewindBuffer();
}
}
TEST_CASE("VSM4R.VV", "[Zvksed]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.VSM4R_VV(v20, v12);
REQUIRE(value == 0xA2C82A77);
}
TEST_CASE("VSM4R.VS", "[Zvksed]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.VSM4R_VS(v20, v12);
REQUIRE(value == 0xA6C82A77);
}
TEST_CASE("VSM3C.VI", "[Zvksh]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
for (uint32_t i = 0; i <= 31; i++) {
as.VSM3C(v20, v12, i);
const auto op_base = 0xAEC02A77U;
const auto verify = op_base | (i << 15);
REQUIRE(value == verify);
as.RewindBuffer();
}
}
TEST_CASE("VSM3ME.VV", "[Zvksh]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.VSM3ME(v20, v12, v10);
REQUIRE(value == 0x82C52A77);
}

View file

@ -0,0 +1,56 @@
#include <catch/catch.hpp>
#include <biscuit/assembler.hpp>
#include "assembler_test_utils.hpp"
using namespace biscuit;
TEST_CASE("TH.MVEQZ", "[XThead]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.TH_MVEQZ(x31, x30, x29);
REQUIRE(value == 0x41DF1F8B);
as.RewindBuffer();
as.TH_MVEQZ(x1, x2, x3);
REQUIRE(value == 0x4031108B);
}
TEST_CASE("TH.MVNEZ", "[XThead]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.TH_MVNEZ(x31, x30, x29);
REQUIRE(value == 0x43DF1F8B);
as.RewindBuffer();
as.TH_MVNEZ(x1, x2, x3);
REQUIRE(value == 0x4231108B);
}
TEST_CASE("TH.ADDSL", "[XThead]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.TH_ADDSL(x31, x30, x29, 0);
REQUIRE(value == 0x01DF1F8B);
as.RewindBuffer();
as.TH_ADDSL(x31, x30, x29, 1);
REQUIRE(value == 0x03DF1F8B);
as.RewindBuffer();
as.TH_ADDSL(x31, x30, x29, 2);
REQUIRE(value == 0x05DF1F8B);
as.RewindBuffer();
as.TH_ADDSL(x31, x30, x29, 3);
REQUIRE(value == 0x07DF1F8B);
}

View file

@ -0,0 +1,467 @@
#include <catch/catch.hpp>
#include <biscuit/assembler.hpp>
#include "assembler_test_utils.hpp"
using namespace biscuit;
TEST_CASE("AMOADD.H", "[Zabha]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.AMOADD_H(Ordering::None, x31, x7, x15);
REQUIRE(value == 0x00779FAF);
as.RewindBuffer();
as.AMOADD_H(Ordering::AQ, x31, x7, x15);
REQUIRE(value == 0x04779FAF);
as.RewindBuffer();
as.AMOADD_H(Ordering::RL, x31, x7, x15);
REQUIRE(value == 0x02779FAF);
as.RewindBuffer();
as.AMOADD_H(Ordering::AQRL, x31, x7, x15);
REQUIRE(value == 0x06779FAF);
}
TEST_CASE("AMOADD.B", "[Zabha]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.AMOADD_B(Ordering::None, x31, x7, x15);
REQUIRE(value == 0x00778FAF);
as.RewindBuffer();
as.AMOADD_B(Ordering::AQ, x31, x7, x15);
REQUIRE(value == 0x04778FAF);
as.RewindBuffer();
as.AMOADD_B(Ordering::RL, x31, x7, x15);
REQUIRE(value == 0x02778FAF);
as.RewindBuffer();
as.AMOADD_B(Ordering::AQRL, x31, x7, x15);
REQUIRE(value == 0x06778FAF);
}
TEST_CASE("AMOAND.H", "[Zabha]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.AMOAND_H(Ordering::None, x31, x7, x15);
REQUIRE(value == 0x60779FAF);
as.RewindBuffer();
as.AMOAND_H(Ordering::AQ, x31, x7, x15);
REQUIRE(value == 0x64779FAF);
as.RewindBuffer();
as.AMOAND_H(Ordering::RL, x31, x7, x15);
REQUIRE(value == 0x62779FAF);
as.RewindBuffer();
as.AMOAND_H(Ordering::AQRL, x31, x7, x15);
REQUIRE(value == 0x66779FAF);
}
TEST_CASE("AMOAND.B", "[Zabha]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.AMOAND_B(Ordering::None, x31, x7, x15);
REQUIRE(value == 0x60778FAF);
as.RewindBuffer();
as.AMOAND_B(Ordering::AQ, x31, x7, x15);
REQUIRE(value == 0x64778FAF);
as.RewindBuffer();
as.AMOAND_B(Ordering::RL, x31, x7, x15);
REQUIRE(value == 0x62778FAF);
as.RewindBuffer();
as.AMOAND_B(Ordering::AQRL, x31, x7, x15);
REQUIRE(value == 0x66778FAF);
}
TEST_CASE("AMOMAX.H", "[Zabha]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.AMOMAX_H(Ordering::None, x31, x7, x15);
REQUIRE(value == 0xA0779FAF);
as.RewindBuffer();
as.AMOMAX_H(Ordering::AQ, x31, x7, x15);
REQUIRE(value == 0xA4779FAF);
as.RewindBuffer();
as.AMOMAX_H(Ordering::RL, x31, x7, x15);
REQUIRE(value == 0xA2779FAF);
as.RewindBuffer();
as.AMOMAX_H(Ordering::AQRL, x31, x7, x15);
REQUIRE(value == 0xA6779FAF);
}
TEST_CASE("AMOMAX.B", "[Zabha]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.AMOMAX_B(Ordering::None, x31, x7, x15);
REQUIRE(value == 0xA0778FAF);
as.RewindBuffer();
as.AMOMAX_B(Ordering::AQ, x31, x7, x15);
REQUIRE(value == 0xA4778FAF);
as.RewindBuffer();
as.AMOMAX_B(Ordering::RL, x31, x7, x15);
REQUIRE(value == 0xA2778FAF);
as.RewindBuffer();
as.AMOMAX_B(Ordering::AQRL, x31, x7, x15);
REQUIRE(value == 0xA6778FAF);
}
TEST_CASE("AMOMAXU.H", "[Zabha]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.AMOMAXU_H(Ordering::None, x31, x7, x15);
REQUIRE(value == 0xE0779FAF);
as.RewindBuffer();
as.AMOMAXU_H(Ordering::AQ, x31, x7, x15);
REQUIRE(value == 0xE4779FAF);
as.RewindBuffer();
as.AMOMAXU_H(Ordering::RL, x31, x7, x15);
REQUIRE(value == 0xE2779FAF);
as.RewindBuffer();
as.AMOMAXU_H(Ordering::AQRL, x31, x7, x15);
REQUIRE(value == 0xE6779FAF);
}
TEST_CASE("AMOMAXU.B", "[Zabha]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.AMOMAXU_B(Ordering::None, x31, x7, x15);
REQUIRE(value == 0xE0778FAF);
as.RewindBuffer();
as.AMOMAXU_B(Ordering::AQ, x31, x7, x15);
REQUIRE(value == 0xE4778FAF);
as.RewindBuffer();
as.AMOMAXU_B(Ordering::RL, x31, x7, x15);
REQUIRE(value == 0xE2778FAF);
as.RewindBuffer();
as.AMOMAXU_B(Ordering::AQRL, x31, x7, x15);
REQUIRE(value == 0xE6778FAF);
}
TEST_CASE("AMOMIN.H", "[Zabha]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.AMOMIN_H(Ordering::None, x31, x7, x15);
REQUIRE(value == 0x80779FAF);
as.RewindBuffer();
as.AMOMIN_H(Ordering::AQ, x31, x7, x15);
REQUIRE(value == 0x84779FAF);
as.RewindBuffer();
as.AMOMIN_H(Ordering::RL, x31, x7, x15);
REQUIRE(value == 0x82779FAF);
as.RewindBuffer();
as.AMOMIN_H(Ordering::AQRL, x31, x7, x15);
REQUIRE(value == 0x86779FAF);
}
TEST_CASE("AMOMIN.B", "[Zabha]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.AMOMIN_B(Ordering::None, x31, x7, x15);
REQUIRE(value == 0x80778FAF);
as.RewindBuffer();
as.AMOMIN_B(Ordering::AQ, x31, x7, x15);
REQUIRE(value == 0x84778FAF);
as.RewindBuffer();
as.AMOMIN_B(Ordering::RL, x31, x7, x15);
REQUIRE(value == 0x82778FAF);
as.RewindBuffer();
as.AMOMIN_B(Ordering::AQRL, x31, x7, x15);
REQUIRE(value == 0x86778FAF);
}
TEST_CASE("AMOMINU.H", "[Zabha]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.AMOMINU_H(Ordering::None, x31, x7, x15);
REQUIRE(value == 0xC0779FAF);
as.RewindBuffer();
as.AMOMINU_H(Ordering::AQ, x31, x7, x15);
REQUIRE(value == 0xC4779FAF);
as.RewindBuffer();
as.AMOMINU_H(Ordering::RL, x31, x7, x15);
REQUIRE(value == 0xC2779FAF);
as.RewindBuffer();
as.AMOMINU_H(Ordering::AQRL, x31, x7, x15);
REQUIRE(value == 0xC6779FAF);
}
TEST_CASE("AMOMINU.B", "[Zabha]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.AMOMINU_B(Ordering::None, x31, x7, x15);
REQUIRE(value == 0xC0778FAF);
as.RewindBuffer();
as.AMOMINU_B(Ordering::AQ, x31, x7, x15);
REQUIRE(value == 0xC4778FAF);
as.RewindBuffer();
as.AMOMINU_B(Ordering::RL, x31, x7, x15);
REQUIRE(value == 0xC2778FAF);
as.RewindBuffer();
as.AMOMINU_B(Ordering::AQRL, x31, x7, x15);
REQUIRE(value == 0xC6778FAF);
}
TEST_CASE("AMOOR.H", "[Zabha]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.AMOOR_H(Ordering::None, x31, x7, x15);
REQUIRE(value == 0x40779FAF);
as.RewindBuffer();
as.AMOOR_H(Ordering::AQ, x31, x7, x15);
REQUIRE(value == 0x44779FAF);
as.RewindBuffer();
as.AMOOR_H(Ordering::RL, x31, x7, x15);
REQUIRE(value == 0x42779FAF);
as.RewindBuffer();
as.AMOOR_H(Ordering::AQRL, x31, x7, x15);
REQUIRE(value == 0x46779FAF);
}
TEST_CASE("AMOOR.B", "[Zabha]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.AMOOR_B(Ordering::None, x31, x7, x15);
REQUIRE(value == 0x40778FAF);
as.RewindBuffer();
as.AMOOR_B(Ordering::AQ, x31, x7, x15);
REQUIRE(value == 0x44778FAF);
as.RewindBuffer();
as.AMOOR_B(Ordering::RL, x31, x7, x15);
REQUIRE(value == 0x42778FAF);
as.RewindBuffer();
as.AMOOR_B(Ordering::AQRL, x31, x7, x15);
REQUIRE(value == 0x46778FAF);
}
TEST_CASE("AMOSWAP.H", "[Zabha]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.AMOSWAP_H(Ordering::None, x31, x7, x15);
REQUIRE(value == 0x08779FAF);
as.RewindBuffer();
as.AMOSWAP_H(Ordering::AQ, x31, x7, x15);
REQUIRE(value == 0x0C779FAF);
as.RewindBuffer();
as.AMOSWAP_H(Ordering::RL, x31, x7, x15);
REQUIRE(value == 0x0A779FAF);
as.RewindBuffer();
as.AMOSWAP_H(Ordering::AQRL, x31, x7, x15);
REQUIRE(value == 0x0E779FAF);
}
TEST_CASE("AMOSWAP.B", "[Zabha]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.AMOSWAP_B(Ordering::None, x31, x7, x15);
REQUIRE(value == 0x08778FAF);
as.RewindBuffer();
as.AMOSWAP_B(Ordering::AQ, x31, x7, x15);
REQUIRE(value == 0x0C778FAF);
as.RewindBuffer();
as.AMOSWAP_B(Ordering::RL, x31, x7, x15);
REQUIRE(value == 0x0A778FAF);
as.RewindBuffer();
as.AMOSWAP_B(Ordering::AQRL, x31, x7, x15);
REQUIRE(value == 0x0E778FAF);
}
TEST_CASE("AMOXOR.H", "[Zabha]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.AMOXOR_H(Ordering::None, x31, x7, x15);
REQUIRE(value == 0x20779FAF);
as.RewindBuffer();
as.AMOXOR_H(Ordering::AQ, x31, x7, x15);
REQUIRE(value == 0x24779FAF);
as.RewindBuffer();
as.AMOXOR_H(Ordering::RL, x31, x7, x15);
REQUIRE(value == 0x22779FAF);
as.RewindBuffer();
as.AMOXOR_H(Ordering::AQRL, x31, x7, x15);
REQUIRE(value == 0x26779FAF);
}
TEST_CASE("AMOXOR.B", "[Zabha]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.AMOXOR_B(Ordering::None, x31, x7, x15);
REQUIRE(value == 0x20778FAF);
as.RewindBuffer();
as.AMOXOR_B(Ordering::AQ, x31, x7, x15);
REQUIRE(value == 0x24778FAF);
as.RewindBuffer();
as.AMOXOR_B(Ordering::RL, x31, x7, x15);
REQUIRE(value == 0x22778FAF);
as.RewindBuffer();
as.AMOXOR_B(Ordering::AQRL, x31, x7, x15);
REQUIRE(value == 0x26778FAF);
}
TEST_CASE("AMOCAS.H", "[Zabha]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.AMOCAS_H(Ordering::None, x31, x7, x15);
REQUIRE(value == 0x28779FAF);
as.RewindBuffer();
as.AMOCAS_H(Ordering::AQ, x31, x7, x15);
REQUIRE(value == 0x2C779FAF);
as.RewindBuffer();
as.AMOCAS_H(Ordering::RL, x31, x7, x15);
REQUIRE(value == 0x2A779FAF);
as.RewindBuffer();
as.AMOCAS_H(Ordering::AQRL, x31, x7, x15);
REQUIRE(value == 0x2E779FAF);
}
TEST_CASE("AMOCAS.B", "[Zabha]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.AMOCAS_B(Ordering::None, x31, x7, x15);
REQUIRE(value == 0x28778FAF);
as.RewindBuffer();
as.AMOCAS_B(Ordering::AQ, x31, x7, x15);
REQUIRE(value == 0x2C778FAF);
as.RewindBuffer();
as.AMOCAS_B(Ordering::RL, x31, x7, x15);
REQUIRE(value == 0x2A778FAF);
as.RewindBuffer();
as.AMOCAS_B(Ordering::AQRL, x31, x7, x15);
REQUIRE(value == 0x2E778FAF);
}

View file

@ -0,0 +1,76 @@
#include <catch/catch.hpp>
#include <biscuit/assembler.hpp>
#include "assembler_test_utils.hpp"
using namespace biscuit;
TEST_CASE("AMOCAS.D", "[Zacas]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.AMOCAS_D(Ordering::None, x31, x7, x15);
REQUIRE(value == 0x2877BFAF);
as.RewindBuffer();
as.AMOCAS_D(Ordering::AQ, x31, x7, x15);
REQUIRE(value == 0x2C77BFAF);
as.RewindBuffer();
as.AMOCAS_D(Ordering::RL, x31, x7, x15);
REQUIRE(value == 0x2A77BFAF);
as.RewindBuffer();
as.AMOCAS_D(Ordering::AQRL, x31, x7, x15);
REQUIRE(value == 0x2E77BFAF);
}
TEST_CASE("AMOCAS.Q", "[Zacas]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.AMOCAS_Q(Ordering::None, x30, x6, x14);
REQUIRE(value == 0x28674F2F);
as.RewindBuffer();
as.AMOCAS_Q(Ordering::AQ, x30, x6, x14);
REQUIRE(value == 0x2C674F2F);
as.RewindBuffer();
as.AMOCAS_Q(Ordering::RL, x30, x6, x14);
REQUIRE(value == 0x2A674F2F);
as.RewindBuffer();
as.AMOCAS_Q(Ordering::AQRL, x30, x6, x14);
REQUIRE(value == 0x2E674F2F);
}
TEST_CASE("AMOCAS.W", "[Zacas]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.AMOCAS_W(Ordering::None, x31, x7, x15);
REQUIRE(value == 0x2877AFAF);
as.RewindBuffer();
as.AMOCAS_W(Ordering::AQ, x31, x7, x15);
REQUIRE(value == 0x2C77AFAF);
as.RewindBuffer();
as.AMOCAS_W(Ordering::RL, x31, x7, x15);
REQUIRE(value == 0x2A77AFAF);
as.RewindBuffer();
as.AMOCAS_W(Ordering::AQRL, x31, x7, x15);
REQUIRE(value == 0x2E77AFAF);
}

View file

@ -0,0 +1,23 @@
#include <catch/catch.hpp>
#include <biscuit/assembler.hpp>
#include "assembler_test_utils.hpp"
using namespace biscuit;
TEST_CASE("WRS.NTO", "[Zawrs]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.WRS_NTO();
REQUIRE(value == 0x00D00073);
}
TEST_CASE("WRS.STO", "[Zawrs]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.WRS_STO();
REQUIRE(value == 0x01D00073);
}

View file

@ -0,0 +1,457 @@
#include <catch/catch.hpp>
#include <array>
#include <biscuit/assembler.hpp>
#include "assembler_test_utils.hpp"
using namespace biscuit;
TEST_CASE("C.LBU", "[Zc]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.C_LBU(x12, 0, x15);
REQUIRE(value == 0x8390U);
as.RewindBuffer();
as.C_LBU(x12, 1, x15);
REQUIRE(value == 0x83D0U);
as.RewindBuffer();
as.C_LBU(x12, 2, x15);
REQUIRE(value == 0x83B0U);
as.RewindBuffer();
as.C_LBU(x12, 3, x15);
REQUIRE(value == 0x83F0U);
}
TEST_CASE("C.LH", "[Zc]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.C_LH(x12, 0, x15);
REQUIRE(value == 0x87D0U);
as.RewindBuffer();
as.C_LH(x12, 2, x15);
REQUIRE(value == 0x87F0U);
}
TEST_CASE("C.LHU", "[Zc]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.C_LHU(x12, 0, x15);
REQUIRE(value == 0x8790U);
as.RewindBuffer();
as.C_LHU(x12, 2, x15);
REQUIRE(value == 0x87B0U);
}
TEST_CASE("C.SB", "[Zc]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.C_SB(x12, 0, x15);
REQUIRE(value == 0x8B90U);
as.RewindBuffer();
as.C_SB(x12, 1, x15);
REQUIRE(value == 0x8BD0U);
as.RewindBuffer();
as.C_SB(x12, 2, x15);
REQUIRE(value == 0x8BB0U);
as.RewindBuffer();
as.C_SB(x12, 3, x15);
REQUIRE(value == 0x8BF0U);
}
TEST_CASE("C.SH", "[Zc]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.C_SH(x12, 0, x15);
REQUIRE(value == 0x8F90U);
as.RewindBuffer();
as.C_SH(x12, 2, x15);
REQUIRE(value == 0x8FB0U);
}
TEST_CASE("C.SEXT.B", "[Zc]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.C_SEXT_B(x12);
REQUIRE(value == 0x9E65);
as.RewindBuffer();
as.C_SEXT_B(x15);
REQUIRE(value == 0x9FE5);
}
TEST_CASE("C.SEXT.H", "[Zc]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.C_SEXT_H(x12);
REQUIRE(value == 0x9E6D);
as.RewindBuffer();
as.C_SEXT_H(x15);
REQUIRE(value == 0x9FED);
}
TEST_CASE("C.ZEXT.B", "[Zc]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.C_ZEXT_B(x12);
REQUIRE(value == 0x9E61);
as.RewindBuffer();
as.C_ZEXT_B(x15);
REQUIRE(value == 0x9FE1);
}
TEST_CASE("C.ZEXT.H", "[Zc]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.C_ZEXT_H(x12);
REQUIRE(value == 0x9E69);
as.RewindBuffer();
as.C_ZEXT_H(x15);
REQUIRE(value == 0x9FE9);
}
TEST_CASE("C.ZEXT.W", "[Zc]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.C_ZEXT_W(x12);
REQUIRE(value == 0x9E71);
as.RewindBuffer();
as.C_ZEXT_W(x15);
REQUIRE(value == 0x9FF1);
}
TEST_CASE("C.MUL", "[Zc]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.C_MUL(x12, x15);
REQUIRE(value == 0x9E5D);
as.RewindBuffer();
as.C_MUL(x15, x12);
REQUIRE(value == 0x9FD1);
}
TEST_CASE("C.NOT", "[Zc]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.C_NOT(x12);
REQUIRE(value == 0x9E75);
as.RewindBuffer();
as.C_NOT(x15);
REQUIRE(value == 0x9FF5);
}
TEST_CASE("CM.MVA01S", "[Zc]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.CM_MVA01S(s7, s6);
REQUIRE(value == 0xAFFA);
as.RewindBuffer();
as.CM_MVA01S(s3, s4);
REQUIRE(value == 0xADF2);
}
TEST_CASE("CM.MVSA01", "[Zc]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.CM_MVSA01(s7, s6);
REQUIRE(value == 0xAFBA);
as.RewindBuffer();
as.CM_MVSA01(s3, s4);
REQUIRE(value == 0xADB2);
}
TEST_CASE("CM.JALT", "[Zc]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
for (uint32_t i = 32; i <= 255; i++) {
const uint32_t op_base = 0xA002;
const uint32_t op = op_base | (i << 2);
as.CM_JALT(i);
REQUIRE(value == op);
as.RewindBuffer();
}
}
TEST_CASE("CM.JT", "[Zc]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
for (uint32_t i = 0; i <= 31; i++) {
const uint32_t op_base = 0xA002;
const uint32_t op = op_base | (i << 2);
as.CM_JT(i);
REQUIRE(value == op);
as.RewindBuffer();
}
}
constexpr std::array stack_adj_bases_rv32{
0, 0, 0, 0, 16, 16, 16, 16,
32, 32, 32, 32, 48, 48, 48, 64,
};
constexpr std::array stack_adj_bases_rv64{
0, 0, 0, 0, 16, 16, 32, 32,
48, 48, 64, 64, 80, 80, 96, 112,
};
TEST_CASE("CM.POP", "[Zc]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.CM_POP({ra}, 16);
REQUIRE(value == 0xBA42);
as.RewindBuffer();
// s10 intentionally omitted, since no direct encoding for it exists.
uint32_t rlist = 5;
for (const GPR sreg : {s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s11}) {
const auto op_base = 0xBA02U;
const auto stack_adj_base = stack_adj_bases_rv64[rlist];
for (int32_t i = 0; i <= 3; i++) {
const auto op = op_base | (rlist << 4) | uint32_t(i) << 2;
as.CM_POP({ra, {s0, sreg}}, stack_adj_base + (16 * i));
REQUIRE(value == op);
as.RewindBuffer();
}
rlist++;
}
}
TEST_CASE("CM.POP (RV32)", "[Zc]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.CM_POP({ra}, 16);
REQUIRE(value == 0xBA42);
as.RewindBuffer();
// s10 intentionally omitted, since no direct encoding for it exists.
uint32_t rlist = 5;
for (const GPR sreg : {s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s11}) {
const auto op_base = 0xBA02U;
const auto stack_adj_base = stack_adj_bases_rv32[rlist];
for (int32_t i = 0; i <= 3; i++) {
const auto op = op_base | (rlist << 4) | uint32_t(i) << 2;
as.CM_POP({ra, {s0, sreg}}, stack_adj_base + (16 * i));
REQUIRE(value == op);
as.RewindBuffer();
}
rlist++;
}
}
TEST_CASE("CM.POPRET", "[Zc]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.CM_POPRET({ra}, 16);
REQUIRE(value == 0xBE42);
as.RewindBuffer();
// s10 intentionally omitted, since no direct encoding for it exists.
uint32_t rlist = 5;
for (const GPR sreg : {s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s11}) {
const auto op_base = 0xBE02U;
const auto stack_adj_base = stack_adj_bases_rv64[rlist];
for (int32_t i = 0; i <= 3; i++) {
const auto op = op_base | (rlist << 4) | uint32_t(i) << 2;
as.CM_POPRET({ra, {s0, sreg}}, stack_adj_base + (16 * i));
REQUIRE(value == op);
as.RewindBuffer();
}
rlist++;
}
}
TEST_CASE("CM.POPRET (RV32)", "[Zc]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.CM_POPRET({ra}, 16);
REQUIRE(value == 0xBE42);
as.RewindBuffer();
// s10 intentionally omitted, since no direct encoding for it exists.
uint32_t rlist = 5;
for (const GPR sreg : {s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s11}) {
const auto op_base = 0xBE02U;
const auto stack_adj_base = stack_adj_bases_rv32[rlist];
for (int32_t i = 0; i <= 3; i++) {
const auto op = op_base | (rlist << 4) | uint32_t(i) << 2;
as.CM_POPRET({ra, {s0, sreg}}, stack_adj_base + (16 * i));
REQUIRE(value == op);
as.RewindBuffer();
}
rlist++;
}
}
TEST_CASE("CM.POPRETZ", "[Zc]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.CM_POPRETZ({ra}, 16);
REQUIRE(value == 0xBC42);
as.RewindBuffer();
// s10 intentionally omitted, since no direct encoding for it exists.
uint32_t rlist = 5;
for (const GPR sreg : {s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s11}) {
const auto op_base = 0xBC02U;
const auto stack_adj_base = stack_adj_bases_rv64[rlist];
for (int32_t i = 0; i <= 3; i++) {
const auto op = op_base | (rlist << 4) | uint32_t(i) << 2;
as.CM_POPRETZ({ra, {s0, sreg}}, stack_adj_base + (16 * i));
REQUIRE(value == op);
as.RewindBuffer();
}
rlist++;
}
}
TEST_CASE("CM.POPRETZ (RV32)", "[Zc]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.CM_POPRETZ({ra}, 16);
REQUIRE(value == 0xBC42);
as.RewindBuffer();
// s10 intentionally omitted, since no direct encoding for it exists.
uint32_t rlist = 5;
for (const GPR sreg : {s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s11}) {
const auto op_base = 0xBC02U;
const auto stack_adj_base = stack_adj_bases_rv32[rlist];
for (int32_t i = 0; i <= 3; i++) {
const auto op = op_base | (rlist << 4) | uint32_t(i) << 2;
as.CM_POPRETZ({ra, {s0, sreg}}, stack_adj_base + (16 * i));
REQUIRE(value == op);
as.RewindBuffer();
}
rlist++;
}
}
TEST_CASE("CM.PUSH", "[Zc]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.CM_PUSH({ra}, -16);
REQUIRE(value == 0xB842);
as.RewindBuffer();
// s10 intentionally omitted, since no direct encoding for it exists.
uint32_t rlist = 5;
for (const GPR sreg : {s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s11}) {
const auto op_base = 0xB802U;
const auto stack_adj_base = stack_adj_bases_rv64[rlist];
for (int32_t i = 0; i <= 3; i++) {
const auto op = op_base | (rlist << 4) | uint32_t(i) << 2;
as.CM_PUSH({ra, {s0, sreg}}, -stack_adj_base + (-16 * i));
REQUIRE(value == op);
as.RewindBuffer();
}
rlist++;
}
}
TEST_CASE("CM.PUSH (RV32)", "[Zc]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.CM_PUSH({ra}, -16);
REQUIRE(value == 0xB842);
as.RewindBuffer();
// s10 intentionally omitted, since no direct encoding for it exists.
uint32_t rlist = 5;
for (const GPR sreg : {s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s11}) {
const auto op_base = 0xB802U;
const auto stack_adj_base = stack_adj_bases_rv32[rlist];
for (int32_t i = 0; i <= 3; i++) {
const auto op = op_base | (rlist << 4) | uint32_t(i) << 2;
as.CM_PUSH({ra, {s0, sreg}}, -stack_adj_base + (-16 * i));
REQUIRE(value == op);
as.RewindBuffer();
}
rlist++;
}
}

View file

@ -0,0 +1,414 @@
#include <catch/catch.hpp>
#include <array>
#include <cstring>
#include <biscuit/assembler.hpp>
#include "assembler_test_utils.hpp"
using namespace biscuit;
static constexpr std::array fli_constants{
0xBFF0000000000000ULL, // -1.0
0x0010000000000000ULL, // Minimum positive normal
0x3EF0000000000000ULL, // 1.0 * 2^-16
0x3F00000000000000ULL, // 1.0 * 2^-15
0x3F70000000000000ULL, // 1.0 * 2^-8
0x3F80000000000000ULL, // 1.0 * 2^-7
0x3FB0000000000000ULL, // 1.0 * 2^-4
0x3FC0000000000000ULL, // 1.0 * 2^-3
0x3FD0000000000000ULL, // 0.25
0x3FD4000000000000ULL, // 0.3125
0x3FD8000000000000ULL, // 0.375
0x3FDC000000000000ULL, // 0.4375
0x3FE0000000000000ULL, // 0.5
0x3FE4000000000000ULL, // 0.625
0x3FE8000000000000ULL, // 0.75
0x3FEC000000000000ULL, // 0.875
0x3FF0000000000000ULL, // 1.0
0x3FF4000000000000ULL, // 1.25
0x3FF8000000000000ULL, // 1.5
0x3FFC000000000000ULL, // 1.75
0x4000000000000000ULL, // 2.0
0x4004000000000000ULL, // 2.5
0x4008000000000000ULL, // 3
0x4010000000000000ULL, // 4
0x4020000000000000ULL, // 8
0x4030000000000000ULL, // 16
0x4060000000000000ULL, // 2^7
0x4070000000000000ULL, // 2^8
0x40E0000000000000ULL, // 2^15
0x40F0000000000000ULL, // 2^16
0x7FF0000000000000ULL, // +inf
0x7FF8000000000000ULL, // Canonical NaN
};
TEST_CASE("FLI.D", "[Zfa]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
for (size_t i = 0; i < fli_constants.size(); i++) {
const auto constant = fli_constants[i];
double fconstant{};
std::memcpy(&fconstant, &constant, sizeof(fconstant));
as.FLI_D(f10, fconstant);
const auto op_base = 0xF2100553;
const auto verify = op_base | (i << 15);
REQUIRE(value == verify);
as.RewindBuffer();
}
}
TEST_CASE("FLI.H", "[Zfa]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
for (size_t i = 0; i < fli_constants.size(); i++) {
const auto constant = fli_constants[i];
double fconstant{};
std::memcpy(&fconstant, &constant, sizeof(fconstant));
as.FLI_H(f10, fconstant);
const auto op_base = 0xF4100553;
const auto verify = op_base | (i << 15);
REQUIRE(value == verify);
as.RewindBuffer();
}
}
TEST_CASE("FLI.S", "[Zfa]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
for (size_t i = 0; i < fli_constants.size(); i++) {
const auto constant = fli_constants[i];
double fconstant{};
std::memcpy(&fconstant, &constant, sizeof(fconstant));
as.FLI_S(f10, fconstant);
const auto op_base = 0xF0100553;
const auto verify = op_base | (i << 15);
REQUIRE(value == verify);
as.RewindBuffer();
}
}
TEST_CASE("FMINM.D", "[Zfa]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.FMINM_D(f20, f12, f10);
REQUIRE(value == 0x2AA62A53);
}
TEST_CASE("FMINM.H", "[Zfa]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.FMINM_H(f20, f12, f10);
REQUIRE(value == 0x2CA62A53);
}
TEST_CASE("FMINM.Q", "[Zfa]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.FMINM_Q(f20, f12, f10);
REQUIRE(value == 0x2EA62A53);
}
TEST_CASE("FMINM.S", "[Zfa]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.FMINM_S(f20, f12, f10);
REQUIRE(value == 0x28A62A53);
}
TEST_CASE("FMAXM.D", "[Zfa]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.FMAXM_D(f20, f12, f10);
REQUIRE(value == 0x2AA63A53);
}
TEST_CASE("FMAXM.H", "[Zfa]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.FMAXM_H(f20, f12, f10);
REQUIRE(value == 0x2CA63A53);
}
TEST_CASE("FMAXM.Q", "[Zfa]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.FMAXM_Q(f20, f12, f10);
REQUIRE(value == 0x2EA63A53);
}
TEST_CASE("FMAXM.S", "[Zfa]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.FMAXM_S(f20, f12, f10);
REQUIRE(value == 0x28A63A53);
}
TEST_CASE("FROUND.D", "[Zfa]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.FROUND_D(f31, f7, RMode::RNE);
REQUIRE(value == 0x42438FD3);
as.RewindBuffer();
as.FROUND_D(f31, f7, RMode::RMM);
REQUIRE(value == 0x4243CFD3);
as.RewindBuffer();
as.FROUND_D(f31, f7, RMode::DYN);
REQUIRE(value == 0x4243FFD3);
}
TEST_CASE("FROUND.H", "[Zfa]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.FROUND_H(f31, f7, RMode::RNE);
REQUIRE(value == 0x44438FD3);
as.RewindBuffer();
as.FROUND_H(f31, f7, RMode::RMM);
REQUIRE(value == 0x4443CFD3);
as.RewindBuffer();
as.FROUND_H(f31, f7, RMode::DYN);
REQUIRE(value == 0x4443FFD3);
}
TEST_CASE("FROUND.Q", "[Zfa]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.FROUND_Q(f31, f7, RMode::RNE);
REQUIRE(value == 0x46438FD3);
as.RewindBuffer();
as.FROUND_Q(f31, f7, RMode::RMM);
REQUIRE(value == 0x4643CFD3);
as.RewindBuffer();
as.FROUND_Q(f31, f7, RMode::DYN);
REQUIRE(value == 0x4643FFD3);
}
TEST_CASE("FROUND.S", "[Zfa]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.FROUND_S(f31, f7, RMode::RNE);
REQUIRE(value == 0x40438FD3);
as.RewindBuffer();
as.FROUND_S(f31, f7, RMode::RMM);
REQUIRE(value == 0x4043CFD3);
as.RewindBuffer();
as.FROUND_S(f31, f7, RMode::DYN);
REQUIRE(value == 0x4043FFD3);
}
TEST_CASE("FROUNDNX.D", "[Zfa]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.FROUNDNX_D(f31, f7, RMode::RNE);
REQUIRE(value == 0x42538FD3);
as.RewindBuffer();
as.FROUNDNX_D(f31, f7, RMode::RMM);
REQUIRE(value == 0x4253CFD3);
as.RewindBuffer();
as.FROUNDNX_D(f31, f7, RMode::DYN);
REQUIRE(value == 0x4253FFD3);
}
TEST_CASE("FROUNDNX.H", "[Zfa]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.FROUNDNX_H(f31, f7, RMode::RNE);
REQUIRE(value == 0x44538FD3);
as.RewindBuffer();
as.FROUNDNX_H(f31, f7, RMode::RMM);
REQUIRE(value == 0x4453CFD3);
as.RewindBuffer();
as.FROUNDNX_H(f31, f7, RMode::DYN);
REQUIRE(value == 0x4453FFD3);
}
TEST_CASE("FROUNDNX.Q", "[Zfa]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.FROUNDNX_Q(f31, f7, RMode::RNE);
REQUIRE(value == 0x46538FD3);
as.RewindBuffer();
as.FROUNDNX_Q(f31, f7, RMode::RMM);
REQUIRE(value == 0x4653CFD3);
as.RewindBuffer();
as.FROUNDNX_Q(f31, f7, RMode::DYN);
REQUIRE(value == 0x4653FFD3);
}
TEST_CASE("FROUNDNX.S", "[Zfa]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.FROUNDNX_S(f31, f7, RMode::RNE);
REQUIRE(value == 0x40538FD3);
as.RewindBuffer();
as.FROUNDNX_S(f31, f7, RMode::RMM);
REQUIRE(value == 0x4053CFD3);
as.RewindBuffer();
as.FROUNDNX_S(f31, f7, RMode::DYN);
REQUIRE(value == 0x4053FFD3);
}
TEST_CASE("FCVTMOD.W.D", "[Zfa]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.FCVTMOD_W_D(x31, f7);
REQUIRE(value == 0xC2839FD3);
}
TEST_CASE("FMVH.X.D", "[Zfa]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FMVH_X_D(x31, f7);
REQUIRE(value == 0xE2138FD3);
}
TEST_CASE("FMVH.X.Q", "[Zfa]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.FMVH_X_Q(x31, f7);
REQUIRE(value == 0xE6138FD3);
}
TEST_CASE("FMVP.D.X", "[Zfa]") {
uint32_t value = 0;
auto as = MakeAssembler32(value);
as.FMVP_D_X(f31, x7, x8);
REQUIRE(value == 0xB2838FD3);
}
TEST_CASE("FMVP.Q.X", "[Zfa]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.FMVP_Q_X(f31, x7, x8);
REQUIRE(value == 0xB6838FD3);
}
TEST_CASE("FLEQ.D", "[Zfa]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.FLEQ_D(x31, f7, f15);
REQUIRE(value == 0xA2F3CFD3);
}
TEST_CASE("FLTQ.D", "[Zfa]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.FLTQ_D(x31, f7, f15);
REQUIRE(value == 0xA2F3DFD3);
}
TEST_CASE("FLEQ.H", "[Zfa]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.FLEQ_H(x31, f7, f15);
REQUIRE(value == 0xA4F3CFD3);
}
TEST_CASE("FLTQ.H", "[Zfa]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.FLTQ_H(x31, f7, f15);
REQUIRE(value == 0xA4F3DFD3);
}
TEST_CASE("FLEQ.Q", "[Zfa]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.FLEQ_Q(x31, f7, f15);
REQUIRE(value == 0xA6F3CFD3);
}
TEST_CASE("FLTQ.Q", "[Zfa]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.FLTQ_Q(x31, f7, f15);
REQUIRE(value == 0xA6F3DFD3);
}
TEST_CASE("FLEQ.S", "[Zfa]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.FLEQ_S(x31, f7, f15);
REQUIRE(value == 0xA0F3CFD3);
}
TEST_CASE("FLTQ.S", "[Zfa]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.FLTQ_S(x31, f7, f15);
REQUIRE(value == 0xA0F3DFD3);
}

View file

@ -0,0 +1,33 @@
#include <catch/catch.hpp>
#include <biscuit/assembler.hpp>
#include "assembler_test_utils.hpp"
using namespace biscuit;
TEST_CASE("CZERO.EQZ", "[Zicond]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.CZERO_EQZ(x31, x30, x29);
REQUIRE(value == 0x0FDF5FB3);
as.RewindBuffer();
as.CZERO_EQZ(x1, x2, x3);
REQUIRE(value == 0x0E3150B3);
}
TEST_CASE("CZERO.NEZ", "[Zicond]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.CZERO_NEZ(x31, x30, x29);
REQUIRE(value == 0x0FDF7FB3);
as.RewindBuffer();
as.CZERO_NEZ(x1, x2, x3);
REQUIRE(value == 0x0E3170B3);
}

View file

@ -0,0 +1,130 @@
#include <catch/catch.hpp>
#include <biscuit/assembler.hpp>
#include "assembler_test_utils.hpp"
using namespace biscuit;
TEST_CASE("CSRRC", "[Zicsr]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.CSRRC(x31, CSR::Cycle, x15);
REQUIRE(value == 0xC007BFF3);
as.RewindBuffer();
as.CSRRC(x31, CSR::FFlags, x15);
REQUIRE(value == 0x0017BFF3);
as.RewindBuffer();
as.CSRRC(x31, CSR::FRM, x15);
REQUIRE(value == 0x0027BFF3);
as.RewindBuffer();
as.CSRRC(x31, CSR::FCSR, x15);
REQUIRE(value == 0x0037BFF3);
}
TEST_CASE("CSRRCI", "[Zicsr]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.CSRRCI(x31, CSR::Cycle, 0);
REQUIRE(value == 0xC0007FF3);
as.RewindBuffer();
as.CSRRCI(x31, CSR::FFlags, 0x1F);
REQUIRE(value == 0x001FFFF3);
as.RewindBuffer();
as.CSRRCI(x31, CSR::FRM, 0x7);
REQUIRE(value == 0x0023FFF3);
}
TEST_CASE("CSRRS", "[Zicsr]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.CSRRS(x31, CSR::Cycle, x15);
REQUIRE(value == 0xC007AFF3);
as.RewindBuffer();
as.CSRRS(x31, CSR::FFlags, x15);
REQUIRE(value == 0x0017AFF3);
as.RewindBuffer();
as.CSRRS(x31, CSR::FRM, x15);
REQUIRE(value == 0x0027AFF3);
as.RewindBuffer();
as.CSRRS(x31, CSR::FCSR, x15);
REQUIRE(value == 0x0037AFF3);
}
TEST_CASE("CSRRSI", "[Zicsr]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.CSRRSI(x31, CSR::Cycle, 0);
REQUIRE(value == 0xC0006FF3);
as.RewindBuffer();
as.CSRRSI(x31, CSR::FFlags, 0x1F);
REQUIRE(value == 0x001FEFF3);
as.RewindBuffer();
as.CSRRSI(x31, CSR::FRM, 0x7);
REQUIRE(value == 0x0023EFF3);
}
TEST_CASE("CSRRW", "[Zicsr]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.CSRRW(x31, CSR::Cycle, x15);
REQUIRE(value == 0xC0079FF3);
as.RewindBuffer();
as.CSRRW(x31, CSR::FFlags, x15);
REQUIRE(value == 0x00179FF3);
as.RewindBuffer();
as.CSRRW(x31, CSR::FRM, x15);
REQUIRE(value == 0x00279FF3);
as.RewindBuffer();
as.CSRRW(x31, CSR::FCSR, x15);
REQUIRE(value == 0x00379FF3);
}
TEST_CASE("CSRRWI", "[Zicsr]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.CSRRWI(x31, CSR::Cycle, 0);
REQUIRE(value == 0xC0005FF3);
as.RewindBuffer();
as.CSRRWI(x31, CSR::FFlags, 0x1F);
REQUIRE(value == 0x001FDFF3);
as.RewindBuffer();
as.CSRRWI(x31, CSR::FRM, 0x7);
REQUIRE(value == 0x0023DFF3);
}

View file

@ -0,0 +1,71 @@
#include <catch/catch.hpp>
#include <biscuit/assembler.hpp>
#include "assembler_test_utils.hpp"
using namespace biscuit;
TEST_CASE("C.NTL.ALL", "[Zihintntl]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.C_NTL_ALL();
REQUIRE(value == 0x9016);
}
TEST_CASE("C.NTL.S1", "[Zihintntl]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.C_NTL_S1();
REQUIRE(value == 0x9012);
}
TEST_CASE("C.NTL.P1", "[Zihintntl]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.C_NTL_P1();
REQUIRE(value == 0x900A);
}
TEST_CASE("C.NTL.PALL", "[Zihintntl]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.C_NTL_PALL();
REQUIRE(value == 0x900E);
}
TEST_CASE("NTL.ALL", "[Zihintntl]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.NTL_ALL();
REQUIRE(value == 0x00500033);
}
TEST_CASE("NTL.S1", "[Zihintntl]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.NTL_S1();
REQUIRE(value == 0x00400033);
}
TEST_CASE("NTL.P1", "[Zihintntl]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.NTL_P1();
REQUIRE(value == 0x00200033);
}
TEST_CASE("NTL.PALL", "[Zihintntl]") {
uint32_t value = 0;
auto as = MakeAssembler64(value);
as.NTL_PALL();
REQUIRE(value == 0x00300033);
}

View file

@ -0,0 +1,2 @@
#define CATCH_CONFIG_MAIN
#include <catch/catch.hpp>

1
externals/dynarmic/externals/catch vendored Submodule

@ -0,0 +1 @@
Subproject commit 74fcff6e5b190fb833a231b7f7c1829e3c3ac54d

1
externals/dynarmic/externals/fmt vendored Submodule

@ -0,0 +1 @@
Subproject commit 02de29e00321787fa515ca60f0f5911e61892dc6

View file

@ -0,0 +1,218 @@
---
Language: Cpp
AccessModifierOffset: -4
AlignAfterOpenBracket: Align
AlignConsecutiveMacros: None
AlignConsecutiveAssignments: None
AlignConsecutiveBitFields: None
AlignConsecutiveDeclarations: None
AlignConsecutiveMacros: None
AlignEscapedNewlines: Right
AlignOperands: AlignAfterOperator
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: true
AllowAllConstructorInitializersOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortEnumsOnASingleLine: true
AllowShortBlocksOnASingleLine: Empty
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Inline
AllowShortLambdasOnASingleLine: All
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: true
AlwaysBreakTemplateDeclarations: Yes
AttributeMacros:
- __capability
BinPackArguments: true
BinPackParameters: false
BitFieldColonSpacing: Both
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: Never
AfterEnum: false
AfterFunction: true
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
AfterExternBlock: false
BeforeCatch: false
BeforeElse: false
BeforeLambdaBody: false
BeforeWhile: false
IndentBraces: false
SplitEmptyFunction: false
SplitEmptyRecord: false
SplitEmptyNamespace: false
BreakBeforeBinaryOperators: All
BreakBeforeBraces: Custom
BreakBeforeConceptDeclarations: true
BreakBeforeTernaryOperators: true
BreakBeforeInheritanceComma: false
BreakConstructorInitializersBeforeComma: true
BreakConstructorInitializers: BeforeComma
BreakInheritanceList: BeforeComma
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 0
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DeriveLineEnding: true
DerivePointerAlignment: false
DisableFormat: false
# EmptyLineAfterAccessModifier: Leave
EmptyLineBeforeAccessModifier: Always
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IncludeBlocks: Regroup
IncludeCategories:
- Regex: '^<mach/'
Priority: 1
SortPriority: 0
CaseSensitive: false
- Regex: '^<windows.h>'
Priority: 1
SortPriority: 0
CaseSensitive: false
- Regex: '(^<signal.h>)|(^<sys/ucontext.h>)|(^<ucontext.h>)'
Priority: 1
SortPriority: 0
CaseSensitive: false
- Regex: '^<([^\.])*>$'
Priority: 2
SortPriority: 0
CaseSensitive: false
- Regex: '^<.*\.'
Priority: 3
SortPriority: 0
CaseSensitive: false
- Regex: '.*'
Priority: 4
SortPriority: 0
CaseSensitive: false
IncludeIsMainRegex: '([-_](test|unittest))?$'
IncludeIsMainSourceRegex: ''
# IndentAccessModifiers: false
IndentCaseBlocks: false
IndentCaseLabels: false
IndentExternBlock: NoIndent
IndentGotoLabels: false
IndentPPDirectives: AfterHash
IndentRequires: false
IndentWidth: 4
IndentWrappedFunctionNames: false
# InsertTrailingCommas: None
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: false
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
NamespaceMacros:
ObjCBinPackProtocolList: Never
ObjCBlockIndentWidth: 2
ObjCBreakBeforeNestedBlockParam: true
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 1
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 200
PenaltyIndentedWhitespace: 0
PointerAlignment: Left
RawStringFormats:
- Language: Cpp
Delimiters:
- cc
- CC
- cpp
- Cpp
- CPP
- 'c++'
- 'C++'
CanonicalDelimiter: ''
BasedOnStyle: google
- Language: TextProto
Delimiters:
- pb
- PB
- proto
- PROTO
EnclosingFunctions:
- EqualsProto
- EquivToProto
- PARSE_PARTIAL_TEXT_PROTO
- PARSE_TEST_PROTO
- PARSE_TEXT_PROTO
- ParseTextOrDie
- ParseTextProtoOrDie
- ParseTestProto
- ParsePartialTestProto
CanonicalDelimiter: ''
BasedOnStyle: google
ReflowComments: true
# ShortNamespaceLines: 5
SortIncludes: true
SortJavaStaticImport: Before
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: false
SpaceAroundPointerQualifiers: Default
SpaceBeforeAssignmentOperators: true
SpaceBeforeCaseColon: false
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceAroundPointerQualifiers: Default
SpaceBeforeRangeBasedForLoopColon: true
SpaceBeforeSquareBrackets: false
SpaceInEmptyBlock: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 2
SpacesInAngles: false
SpacesInConditionalStatement: false
SpacesInCStyleCastParentheses: false
SpacesInConditionalStatement: false
SpacesInContainerLiterals: false
# SpacesInLineCommentPrefix: -1
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Latest
StatementAttributeLikeMacros:
- Q_EMIT
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
TabWidth: 4
TypenameMacros:
UseCRLF: false
UseTab: Never
WhitespaceSensitiveMacros:
- STRINGIZE
- PP_STRINGIZE
- BOOST_PP_STRINGIZE
- NS_SWIFT_NAME
- CF_SWIFT_NAME
- FCODE
- ICODE
...

View file

@ -0,0 +1,2 @@
*build*/
.DS_Store

View file

@ -0,0 +1,135 @@
cmake_minimum_required(VERSION 3.12 FATAL_ERROR)
include(GNUInstallDirs)
project(mcl LANGUAGES CXX VERSION 0.1.13)
# Determine if we're built as a subproject (using add_subdirectory)
# or if this is the master project.
set(MASTER_PROJECT OFF)
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
set(MASTER_PROJECT ON)
endif()
# Project options
option(MCL_WARNINGS_AS_ERRORS "Warnings as errors" ${MASTER_PROJECT})
option(MCL_INSTALL "Enable installation" ${MASTER_PROJECT})
# Default to a Release build
if (NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE)
message(STATUS "Defaulting to a Release build")
endif()
# Set hard requirements for C++
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
# Warn on CMake API deprecations
set(CMAKE_WARN_DEPRECATED ON)
# Disable in-source builds
set(CMAKE_DISABLE_SOURCE_CHANGES ON)
set(CMAKE_DISABLE_IN_SOURCE_BUILD ON)
if ("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}")
message(SEND_ERROR "In-source builds are not allowed.")
endif()
# Add the module directory to the list of paths
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/CMakeModules")
# Compiler flags
if (MSVC)
set(MCL_CXX_FLAGS
/std:c++latest
/experimental:external
/external:W0
/external:anglebrackets
/W4
/w44263 # Non-virtual member function hides base class virtual function
/w44265 # Class has virtual functions, but destructor is not virtual
/w44456 # Declaration of 'var' hides previous local declaration
/w44457 # Declaration of 'var' hides function parameter
/w44458 # Declaration of 'var' hides class member
/w44459 # Declaration of 'var' hides global definition
/w44946 # Reinterpret-cast between related types
/wd4592 # Symbol will be dynamically initialized (implementation limitation)
/permissive- # Stricter C++ standards conformance
/MP
/Zi
/Zo
/EHsc
/Zc:externConstexpr # Allows external linkage for variables declared "extern constexpr", as the standard permits.
/Zc:inline # Omits inline functions from object-file output.
/Zc:throwingNew # Assumes new (without std::nothrow) never returns null.
/volatile:iso # Use strict standard-abiding volatile semantics
/bigobj # Increase number of sections in .obj files
/DNOMINMAX)
if (MCL_WARNINGS_AS_ERRORS)
list(APPEND MCL_CXX_FLAGS /WX)
endif()
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
list(APPEND MCL_CXX_FLAGS
-Qunused-arguments
-Wno-missing-braces)
endif()
else()
set(MCL_CXX_FLAGS
-Wall
-Wextra
-Wcast-qual
-pedantic
-pedantic-errors
-Wfatal-errors
-Wno-missing-braces)
if (MCL_WARNINGS_AS_ERRORS)
list(APPEND MCL_CXX_FLAGS -Werror)
endif()
endif()
# Dependencies
if (NOT TARGET Catch2::Catch2)
find_package(Catch2 3 QUIET)
endif()
if (NOT TARGET fmt::fmt)
find_package(fmt REQUIRED)
endif()
# Project files
add_subdirectory(src)
if (TARGET Catch2::Catch2 AND MASTER_PROJECT)
add_subdirectory(tests)
endif()
# Install instructions
if (MCL_INSTALL)
include(GNUInstallDirs)
include(CMakePackageConfigHelpers)
install(TARGETS mcl EXPORT mclTargets)
install(EXPORT mclTargets
NAMESPACE merry::
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/mcl"
)
configure_package_config_file(CMakeModules/mclConfig.cmake.in
mclConfig.cmake
INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/mcl"
)
write_basic_package_version_file(mclConfigVersion.cmake
COMPATIBILITY SameMajorVersion
)
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/mclConfig.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/mclConfigVersion.cmake"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/mcl"
)
install(DIRECTORY include/ TYPE INCLUDE FILES_MATCHING PATTERN "*.hpp")
endif()

View file

@ -0,0 +1,17 @@
# This function should be passed a name of an existing target. It will automatically generate
# file groups following the directory hierarchy, so that the layout of the files in IDEs matches the
# one in the filesystem.
function(create_target_directory_groups target_name)
# Place any files that aren't in the source list in a separate group so that they don't get in
# the way.
source_group("Other Files" REGULAR_EXPRESSION ".")
get_target_property(target_sources "${target_name}" SOURCES)
foreach(file_name IN LISTS target_sources)
get_filename_component(dir_name "${file_name}" PATH)
# Group names use '\' as a separator even though the entire rest of CMake uses '/'...
string(REPLACE "/" "\\" group_name "${dir_name}")
source_group("${group_name}" FILES "${file_name}")
endforeach()
endfunction()

View file

@ -0,0 +1,56 @@
include(CheckSymbolExists)
function(detect_architecture symbol arch)
if (NOT DEFINED ARCHITECTURE)
set(CMAKE_REQUIRED_QUIET YES)
check_symbol_exists("${symbol}" "" DETECT_ARCHITECTURE_${arch})
unset(CMAKE_REQUIRED_QUIET)
if (DETECT_ARCHITECTURE_${arch})
set(ARCHITECTURE "${arch}" PARENT_SCOPE)
endif()
unset(DETECT_ARCHITECTURE_${arch} CACHE)
endif()
endfunction()
detect_architecture("__ARM64__" arm64)
detect_architecture("__aarch64__" arm64)
detect_architecture("_M_ARM64" arm64)
detect_architecture("__arm__" arm32)
detect_architecture("__TARGET_ARCH_ARM" arm32)
detect_architecture("_M_ARM" arm32)
detect_architecture("__x86_64" x86_64)
detect_architecture("__x86_64__" x86_64)
detect_architecture("__amd64" x86_64)
detect_architecture("_M_X64" x86_64)
detect_architecture("__i386" x86_32)
detect_architecture("__i386__" x86_32)
detect_architecture("_M_IX86" x86_32)
detect_architecture("__ia64" ia64)
detect_architecture("__ia64__" ia64)
detect_architecture("_M_IA64" ia64)
detect_architecture("__mips" mips)
detect_architecture("__mips__" mips)
detect_architecture("_M_MRX000" mips)
detect_architecture("__ppc64__" ppc64)
detect_architecture("__powerpc64__" ppc64)
detect_architecture("__ppc__" ppc32)
detect_architecture("__ppc" ppc32)
detect_architecture("__powerpc__" ppc32)
detect_architecture("_ARCH_COM" ppc32)
detect_architecture("_ARCH_PWR" ppc32)
detect_architecture("_ARCH_PPC" ppc32)
detect_architecture("_M_MPPC" ppc32)
detect_architecture("_M_PPC" ppc32)
detect_architecture("__riscv" riscv)
detect_architecture("__EMSCRIPTEN__" wasm)

View file

@ -0,0 +1,5 @@
@PACKAGE_INIT@
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
check_required_components(@PROJECT_NAME@)

View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022 merryhime
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

17
externals/dynarmic/externals/mcl/README vendored Normal file
View file

@ -0,0 +1,17 @@
oooo
`888
ooo. .oo. .oo. .ooooo. 888
`888P"Y88bP"Y88b d88' `"Y8 888
888 888 888 888 888
888 888 888 888 .o8 888
o888o o888o o888o `Y8bod8P' o888o
.-.-. .-.-. .-.-. .-.-. .-.-. .-.-. .-.-. .-.-. .-.-. .-.-.
/ / \ \ / / \ \ / / \ \ / / \ \ / / \ \ / / \ \ / / \ \ / / \ \ / / \ \ / / \ \
`-' `-`-' `-`-' `-`-' `-`-' `-`-' `-`-' `-`-' `-`-' `-`-' `-`
A collection of C++20 utilities which is common to a number of merry's projects.
MIT licensed.

View file

@ -0,0 +1,62 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include <stdexcept>
#include <type_traits>
#include <fmt/format.h>
#include "mcl/hint/assume.hpp"
namespace mcl::detail {
[[noreturn]] void assert_terminate_impl(const char* expr_str, fmt::string_view msg, fmt::format_args args);
template<typename... Ts>
[[noreturn]] void assert_terminate(const char* expr_str, fmt::string_view msg, Ts... args)
{
assert_terminate_impl(expr_str, msg, fmt::make_format_args(args...));
}
} // namespace mcl::detail
#define UNREACHABLE() ASSERT_FALSE("Unreachable code!")
#define ASSERT(expr) \
[&] { \
if (std::is_constant_evaluated()) { \
if (!(expr)) { \
throw std::logic_error{"ASSERT failed at compile time"}; \
} \
} else { \
if (!(expr)) [[unlikely]] { \
::mcl::detail::assert_terminate(#expr, "(none)"); \
} \
} \
}()
#define ASSERT_MSG(expr, ...) \
[&] { \
if (std::is_constant_evaluated()) { \
if (!(expr)) { \
throw std::logic_error{"ASSERT_MSG failed at compile time"}; \
} \
} else { \
if (!(expr)) [[unlikely]] { \
::mcl::detail::assert_terminate(#expr, __VA_ARGS__); \
} \
} \
}()
#define ASSERT_FALSE(...) ::mcl::detail::assert_terminate("false", __VA_ARGS__)
#if defined(NDEBUG) || defined(MCL_IGNORE_ASSERTS)
# define DEBUG_ASSERT(expr) ASSUME(expr)
# define DEBUG_ASSERT_MSG(expr, ...) ASSUME(expr)
#else
# define DEBUG_ASSERT(expr) ASSERT(expr)
# define DEBUG_ASSERT_MSG(expr, ...) ASSERT_MSG(expr, __VA_ARGS__)
#endif

View file

@ -0,0 +1,58 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include <bitset>
#include "mcl/bitsizeof.hpp"
#include "mcl/concepts/bit_integral.hpp"
#include "mcl/stdint.hpp"
namespace mcl::bit {
template<BitIntegral T>
inline size_t count_ones(T x)
{
return std::bitset<bitsizeof<T>>(x).count();
}
template<BitIntegral T>
constexpr size_t count_leading_zeros(T x)
{
size_t result = bitsizeof<T>;
while (x != 0) {
x >>= 1;
result--;
}
return result;
}
template<BitIntegral T>
constexpr int highest_set_bit(T x)
{
int result = -1;
while (x != 0) {
x >>= 1;
result++;
}
return result;
}
template<BitIntegral T>
constexpr size_t lowest_set_bit(T x)
{
if (x == 0) {
return bitsizeof<T>;
}
size_t result = 0;
while ((x & 1) == 0) {
x >>= 1;
result++;
}
return result;
}
} // namespace mcl::bit

View file

@ -0,0 +1,221 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include "mcl/assert.hpp"
#include "mcl/bitsizeof.hpp"
#include "mcl/concepts/bit_integral.hpp"
#include "mcl/stdint.hpp"
namespace mcl::bit {
/// Create a mask with `count` number of one bits.
template<size_t count, BitIntegral T>
constexpr T ones()
{
static_assert(count <= bitsizeof<T>, "count larger than bitsize of T");
if constexpr (count == 0) {
return 0;
} else {
return static_cast<T>(~static_cast<T>(0)) >> (bitsizeof<T> - count);
}
}
/// Create a mask with `count` number of one bits.
template<BitIntegral T>
constexpr T ones(size_t count)
{
ASSERT_MSG(count <= bitsizeof<T>, "count larger than bitsize of T");
if (count == 0) {
return 0;
}
return static_cast<T>(~static_cast<T>(0)) >> (bitsizeof<T> - count);
}
/// Create a mask of type T for bits [begin_bit, end_bit] inclusive.
template<size_t begin_bit, size_t end_bit, BitIntegral T>
constexpr T mask()
{
static_assert(begin_bit <= end_bit, "invalid bit range (position of beginning bit cannot be greater than that of end bit)");
static_assert(begin_bit < bitsizeof<T>, "begin_bit must be smaller than size of T");
static_assert(end_bit < bitsizeof<T>, "end_bit must be smaller than size of T");
return ones<end_bit - begin_bit + 1, T>() << begin_bit;
}
/// Create a mask of type T for bits [begin_bit, end_bit] inclusive.
template<BitIntegral T>
constexpr T mask(size_t begin_bit, size_t end_bit)
{
ASSERT_MSG(begin_bit <= end_bit, "invalid bit range (position of beginning bit cannot be greater than that of end bit)");
ASSERT_MSG(begin_bit < bitsizeof<T>, "begin_bit must be smaller than size of T");
ASSERT_MSG(end_bit < bitsizeof<T>, "end_bit must be smaller than size of T");
return ones<T>(end_bit - begin_bit + 1) << begin_bit;
}
/// Extract bits [begin_bit, end_bit] inclusive from value of type T.
template<size_t begin_bit, size_t end_bit, BitIntegral T>
constexpr T get_bits(T value)
{
constexpr T m = mask<begin_bit, end_bit, T>();
return (value & m) >> begin_bit;
}
/// Extract bits [begin_bit, end_bit] inclusive from value of type T.
template<BitIntegral T>
constexpr T get_bits(size_t begin_bit, size_t end_bit, T value)
{
const T m = mask<T>(begin_bit, end_bit);
return (value & m) >> begin_bit;
}
/// Clears bits [begin_bit, end_bit] inclusive of value of type T.
template<size_t begin_bit, size_t end_bit, BitIntegral T>
constexpr T clear_bits(T value)
{
constexpr T m = mask<begin_bit, end_bit, T>();
return value & ~m;
}
/// Clears bits [begin_bit, end_bit] inclusive of value of type T.
template<BitIntegral T>
constexpr T clear_bits(size_t begin_bit, size_t end_bit, T value)
{
const T m = mask<T>(begin_bit, end_bit);
return value & ~m;
}
/// Modifies bits [begin_bit, end_bit] inclusive of value of type T.
template<size_t begin_bit, size_t end_bit, BitIntegral T>
constexpr T set_bits(T value, T new_bits)
{
constexpr T m = mask<begin_bit, end_bit, T>();
return (value & ~m) | ((new_bits << begin_bit) & m);
}
/// Modifies bits [begin_bit, end_bit] inclusive of value of type T.
template<BitIntegral T>
constexpr T set_bits(size_t begin_bit, size_t end_bit, T value, T new_bits)
{
const T m = mask<T>(begin_bit, end_bit);
return (value & ~m) | ((new_bits << begin_bit) & m);
}
/// Extract bit at bit_position from value of type T.
template<size_t bit_position, BitIntegral T>
constexpr bool get_bit(T value)
{
constexpr T m = mask<bit_position, bit_position, T>();
return (value & m) != 0;
}
/// Extract bit at bit_position from value of type T.
template<BitIntegral T>
constexpr bool get_bit(size_t bit_position, T value)
{
const T m = mask<T>(bit_position, bit_position);
return (value & m) != 0;
}
/// Clears bit at bit_position of value of type T.
template<size_t bit_position, BitIntegral T>
constexpr T clear_bit(T value)
{
constexpr T m = mask<bit_position, bit_position, T>();
return value & ~m;
}
/// Clears bit at bit_position of value of type T.
template<BitIntegral T>
constexpr T clear_bit(size_t bit_position, T value)
{
const T m = mask<T>(bit_position, bit_position);
return value & ~m;
}
/// Modifies bit at bit_position of value of type T.
template<size_t bit_position, BitIntegral T>
constexpr T set_bit(T value, bool new_bit)
{
constexpr T m = mask<bit_position, bit_position, T>();
return (value & ~m) | (new_bit ? m : static_cast<T>(0));
}
/// Modifies bit at bit_position of value of type T.
template<BitIntegral T>
constexpr T set_bit(size_t bit_position, T value, bool new_bit)
{
const T m = mask<T>(bit_position, bit_position);
return (value & ~m) | (new_bit ? m : static_cast<T>(0));
}
/// Sign-extends a value that has bit_count bits to the full bitwidth of type T.
template<size_t bit_count, BitIntegral T>
constexpr T sign_extend(T value)
{
static_assert(bit_count != 0, "cannot sign-extend zero-sized value");
using S = std::make_signed_t<T>;
constexpr size_t shift_amount = bitsizeof<T> - bit_count;
return static_cast<T>(static_cast<S>(value << shift_amount) >> shift_amount);
}
/// Sign-extends a value that has bit_count bits to the full bitwidth of type T.
template<BitIntegral T>
constexpr T sign_extend(size_t bit_count, T value)
{
ASSERT_MSG(bit_count != 0, "cannot sign-extend zero-sized value");
using S = std::make_signed_t<T>;
const size_t shift_amount = bitsizeof<T> - bit_count;
return static_cast<T>(static_cast<S>(value << shift_amount) >> shift_amount);
}
/// Replicate an element across a value of type T.
template<size_t element_size, BitIntegral T>
constexpr T replicate_element(T value)
{
static_assert(element_size <= bitsizeof<T>, "element_size is too large");
static_assert(bitsizeof<T> % element_size == 0, "bitsize of T not divisible by element_size");
if constexpr (element_size == bitsizeof<T>) {
return value;
} else {
return replicate_element<element_size * 2, T>(static_cast<T>(value | (value << element_size)));
}
}
/// Replicate an element of type U across a value of type T.
template<BitIntegral U, BitIntegral T>
constexpr T replicate_element(T value)
{
static_assert(bitsizeof<U> <= bitsizeof<T>, "element_size is too large");
return replicate_element<bitsizeof<U>, T>(value);
}
/// Replicate an element across a value of type T.
template<BitIntegral T>
constexpr T replicate_element(size_t element_size, T value)
{
ASSERT_MSG(element_size <= bitsizeof<T>, "element_size is too large");
ASSERT_MSG(bitsizeof<T> % element_size == 0, "bitsize of T not divisible by element_size");
if (element_size == bitsizeof<T>) {
return value;
}
return replicate_element<T>(element_size * 2, static_cast<T>(value | (value << element_size)));
}
template<BitIntegral T>
constexpr bool most_significant_bit(T value)
{
return get_bit<bitsizeof<T> - 1, T>(value);
}
} // namespace mcl::bit

View file

@ -0,0 +1,33 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include "mcl/bitsizeof.hpp"
#include "mcl/concepts/bit_integral.hpp"
#include "mcl/stdint.hpp"
namespace mcl::bit {
template<BitIntegral T>
constexpr T rotate_right(T x, size_t amount)
{
amount %= bitsizeof<T>;
if (amount == 0) {
return x;
}
return static_cast<T>((x >> amount) | (x << (bitsizeof<T> - amount)));
}
template<BitIntegral T>
constexpr T rotate_left(T x, size_t amount)
{
amount %= bitsizeof<T>;
if (amount == 0) {
return x;
}
return static_cast<T>((x << amount) | (x >> (bitsizeof<T> - amount)));
}
} // namespace mcl::bit

View file

@ -0,0 +1,56 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include "mcl/concepts/bit_integral.hpp"
namespace mcl::bit {
constexpr u16 swap_bytes_16(u16 value)
{
return static_cast<u16>(u32{value} >> 8 | u32{value} << 8);
}
constexpr u32 swap_bytes_32(u32 value)
{
return ((value & 0xff000000u) >> 24)
| ((value & 0x00ff0000u) >> 8)
| ((value & 0x0000ff00u) << 8)
| ((value & 0x000000ffu) << 24);
}
constexpr u64 swap_bytes_64(u64 value)
{
return ((value & 0xff00000000000000ull) >> 56)
| ((value & 0x00ff000000000000ull) >> 40)
| ((value & 0x0000ff0000000000ull) >> 24)
| ((value & 0x000000ff00000000ull) >> 8)
| ((value & 0x00000000ff000000ull) << 8)
| ((value & 0x0000000000ff0000ull) << 24)
| ((value & 0x000000000000ff00ull) << 40)
| ((value & 0x00000000000000ffull) << 56);
}
constexpr u32 swap_halves_32(u32 value)
{
return ((value & 0xffff0000u) >> 16)
| ((value & 0x0000ffffu) << 16);
}
constexpr u64 swap_halves_64(u64 value)
{
return ((value & 0xffff000000000000ull) >> 48)
| ((value & 0x0000ffff00000000ull) >> 16)
| ((value & 0x00000000ffff0000ull) << 16)
| ((value & 0x000000000000ffffull) << 48);
}
constexpr u64 swap_words_64(u64 value)
{
return ((value & 0xffffffff00000000ull) >> 32)
| ((value & 0x00000000ffffffffull) << 32);
}
} // namespace mcl::bit

View file

@ -0,0 +1,38 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include <cstring>
#include <type_traits>
namespace mcl {
/// Reinterpret objects of one type as another by bit-casting between object representations.
template<class Dest, class Source>
inline Dest bit_cast(const Source& source) noexcept
{
static_assert(sizeof(Dest) == sizeof(Source), "size of destination and source objects must be equal");
static_assert(std::is_trivially_copyable_v<Dest>, "destination type must be trivially copyable.");
static_assert(std::is_trivially_copyable_v<Source>, "source type must be trivially copyable");
std::aligned_storage_t<sizeof(Dest), alignof(Dest)> dest;
std::memcpy(&dest, &source, sizeof(dest));
return reinterpret_cast<Dest&>(dest);
}
/// Reinterpret objects of any arbitrary type as another type by bit-casting between object representations.
/// Note that here we do not verify if source pointed to by source_ptr has enough bytes to read from.
template<class Dest, class SourcePtr>
inline Dest bit_cast_pointee(const SourcePtr source_ptr) noexcept
{
static_assert(sizeof(SourcePtr) == sizeof(void*), "source pointer must have size of a pointer");
static_assert(std::is_trivially_copyable_v<Dest>, "destination type must be trivially copyable.");
std::aligned_storage_t<sizeof(Dest), alignof(Dest)> dest;
std::memcpy(&dest, bit_cast<void*>(source_ptr), sizeof(dest));
return reinterpret_cast<Dest&>(dest);
}
} // namespace mcl

View file

@ -0,0 +1,15 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include <climits>
#include <cstddef>
namespace mcl {
template<typename T>
constexpr std::size_t bitsizeof = CHAR_BIT * sizeof(T);
} // namespace mcl

View file

@ -0,0 +1,16 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include "mcl/concepts/is_any_of.hpp"
#include "mcl/stdint.hpp"
namespace mcl {
/// Integral upon which bit operations can be safely performed.
template<typename T>
concept BitIntegral = IsAnyOf<T, u8, u16, u32, u64, uptr, size_t>;
} // namespace mcl

View file

@ -0,0 +1,14 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include "mcl/concepts/same_as.hpp"
namespace mcl {
template<typename T, typename... U>
concept IsAnyOf = (SameAs<T, U> || ...);
} // namespace mcl

View file

@ -0,0 +1,19 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
namespace mcl {
namespace detail {
template<typename T, typename U>
concept SameHelper = std::is_same_v<T, U>;
} // namespace detail
template<typename T, typename U>
concept SameAs = detail::SameHelper<T, U> && detail::SameHelper<U, T>;
} // namespace mcl

View file

@ -0,0 +1,35 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include "mcl/bitsizeof.hpp"
#include "mcl/stdint.hpp"
namespace mcl::detail {
/// if MSB is 0, this is a full slot. remaining 7 bits is a partial hash of the key.
/// if MSB is 1, this is a non-full slot.
enum class meta_byte : u8 {
empty = 0xff,
tombstone = 0x80,
end_sentinel = 0x88,
};
inline bool is_full(meta_byte mb)
{
return (static_cast<u8>(mb) & 0x80) == 0;
}
inline meta_byte meta_byte_from_hash(size_t hash)
{
return static_cast<meta_byte>(hash >> (bitsizeof<size_t> - 7));
}
inline size_t group_index_from_hash(size_t hash, size_t group_index_mask)
{
return hash & group_index_mask;
}
} // namespace mcl::detail

View file

@ -0,0 +1,263 @@
// This file is part of the mcl project.
// Copyright (c) 2022 merryhime
// SPDX-License-Identifier: MIT
#pragma once
#include <array>
#include <bit>
#include "mcl/assert.hpp"
#include "mcl/container/detail/meta_byte.hpp"
#include "mcl/macro/architecture.hpp"
#include "mcl/stdint.hpp"
#if defined(MCL_ARCHITECTURE_ARM64)
# include <arm_neon.h>
#elif defined(MCL_ARCHITECTURE_X86_64)
# include <emmintrin.h>
# include "mcl/bit_cast.hpp"
#else
# include <cstring>
#endif
namespace mcl::detail {
#if defined(MCL_ARCHITECTURE_ARM64)
struct meta_byte_group {
static constexpr size_t max_group_size{16};
explicit meta_byte_group(meta_byte* ptr)
: data{vld1q_u8(reinterpret_cast<u8*>(ptr))}
{}
explicit meta_byte_group(const std::array<meta_byte, 16>& array)
: data{vld1q_u8(reinterpret_cast<const u8*>(array.data()))}
{}
uint64x2_t match(meta_byte cmp) const
{
return vreinterpretq_u64_u8(vandq_u8(vceqq_u8(data,
vdupq_n_u8(static_cast<u8>(cmp))),
vdupq_n_u8(0x80)));
}
uint64x2_t match_empty_or_tombstone() const
{
return vreinterpretq_u64_u8(vandq_u8(data,
vdupq_n_u8(0x80)));
}
bool is_any_empty() const
{
static_assert(meta_byte::empty == static_cast<meta_byte>(0xff), "empty must be maximal u8 value");
return vmaxvq_u8(data) == 0xff;
}
bool is_all_empty_or_tombstone() const
{
return vminvq_u8(vandq_u8(data, vdupq_n_u8(0x80))) == 0x80;
}
meta_byte get(size_t index) const
{
return static_cast<meta_byte>(data[index]);
}
void set(size_t index, meta_byte value)
{
data[index] = static_cast<u8>(value);
}
uint8x16_t data;
};
# define MCL_HMAP_MATCH_META_BYTE_GROUP(MATCH, ...) \
{ \
const uint64x2_t match_result{MATCH}; \
\
for (u64 match_result_v{match_result[0]}; match_result_v != 0; match_result_v &= match_result_v - 1) { \
const size_t match_index{static_cast<size_t>(std::countr_zero(match_result_v) / 8)}; \
__VA_ARGS__ \
} \
\
for (u64 match_result_v{match_result[1]}; match_result_v != 0; match_result_v &= match_result_v - 1) { \
const size_t match_index{static_cast<size_t>(8 + std::countr_zero(match_result_v) / 8)}; \
__VA_ARGS__ \
} \
}
# define MCL_HMAP_MATCH_META_BYTE_GROUP_EXCEPT_LAST(MATCH, ...) \
{ \
const uint64x2_t match_result{MATCH}; \
\
for (u64 match_result_v{match_result[0]}; match_result_v != 0; match_result_v &= match_result_v - 1) { \
const size_t match_index{static_cast<size_t>(std::countr_zero(match_result_v) / 8)}; \
__VA_ARGS__ \
} \
\
for (u64 match_result_v{match_result[1] & 0x00ffffffffffffff}; match_result_v != 0; match_result_v &= match_result_v - 1) { \
const size_t match_index{static_cast<size_t>(8 + std::countr_zero(match_result_v) / 8)}; \
__VA_ARGS__ \
} \
}
#elif defined(MCL_ARCHITECTURE_X86_64)
struct meta_byte_group {
static constexpr size_t max_group_size{16};
explicit meta_byte_group(meta_byte* ptr)
: data{_mm_load_si128(reinterpret_cast<__m128i const*>(ptr))}
{}
explicit meta_byte_group(const std::array<meta_byte, 16>& array)
: data{_mm_loadu_si128(reinterpret_cast<__m128i const*>(array.data()))}
{}
u16 match(meta_byte cmp) const
{
return _mm_movemask_epi8(_mm_cmpeq_epi8(data, _mm_set1_epi8(static_cast<u8>(cmp))));
}
u16 match_empty_or_tombstone() const
{
return _mm_movemask_epi8(data);
}
bool is_any_empty() const
{
return match(meta_byte::empty);
}
bool is_all_empty_or_tombstone() const
{
return match_empty_or_tombstone() == 0xffff;
}
meta_byte get(size_t index) const
{
return mcl::bit_cast<std::array<meta_byte, max_group_size>>(data)[index];
}
void set(size_t index, meta_byte value)
{
auto array = mcl::bit_cast<std::array<meta_byte, max_group_size>>(data);
array[index] = value;
data = mcl::bit_cast<__m128i>(array);
}
__m128i data;
};
# define MCL_HMAP_MATCH_META_BYTE_GROUP(MATCH, ...) \
{ \
for (u16 match_result{MATCH}; match_result != 0; match_result &= match_result - 1) { \
const size_t match_index{static_cast<size_t>(std::countr_zero(match_result))}; \
__VA_ARGS__ \
} \
}
# define MCL_HMAP_MATCH_META_BYTE_GROUP_EXCEPT_LAST(MATCH, ...) \
{ \
for (u16 match_result{static_cast<u16>((MATCH) & (0x7fff))}; match_result != 0; match_result &= match_result - 1) { \
const size_t match_index{static_cast<size_t>(std::countr_zero(match_result))}; \
__VA_ARGS__ \
} \
}
#else
struct meta_byte_group {
static constexpr size_t max_group_size{16};
static constexpr u64 msb{0x8080808080808080};
static constexpr u64 lsb{0x0101010101010101};
static constexpr u64 not_msb{0x7f7f7f7f7f7f7f7f};
static constexpr u64 not_lsb{0xfefefefefefefefe};
explicit meta_byte_group(meta_byte* ptr)
{
std::memcpy(data.data(), ptr, sizeof(data));
}
explicit meta_byte_group(const std::array<meta_byte, 16>& array)
: data{array}
{}
std::array<u64, 2> match(meta_byte cmp) const
{
DEBUG_ASSERT(is_full(cmp));
const u64 vcmp{lsb * static_cast<u64>(cmp)};
return {(msb - ((data[0] ^ vcmp) & not_msb)) & ~data[0] & msb, (msb - ((data[1] ^ vcmp) & not_msb)) & ~data[1] & msb};
}
std::array<u64, 2> match_empty_or_tombstone() const
{
return {data[0] & msb, data[1] & msb};
}
bool is_any_empty() const
{
static_assert((static_cast<u8>(meta_byte::empty) & 0xc0) == 0xc0);
static_assert((static_cast<u8>(meta_byte::tombstone) & 0xc0) == 0x80);
return (data[0] & (data[0] << 1) & msb) || (data[1] & (data[1] << 1) & msb);
}
bool is_all_empty_or_tombstone() const
{
return (data[0] & data[1] & msb) == msb;
}
meta_byte get(size_t index) const
{
return mcl::bit_cast<std::array<meta_byte, max_group_size>>(data)[index];
}
void set(size_t index, meta_byte value)
{
auto array = mcl::bit_cast<std::array<meta_byte, max_group_size>>(data);
array[index] = value;
data = mcl::bit_cast<std::array<u64, 2>>(array);
}
std::array<u64, 2> data;
};
# define MCL_HMAP_MATCH_META_BYTE_GROUP(MATCH, ...) \
{ \
const std::array<u64, 2> match_result{MATCH}; \
\
for (u64 match_result_v{match_result[0]}; match_result_v != 0; match_result_v &= match_result_v - 1) { \
const size_t match_index{static_cast<size_t>(std::countr_zero(match_result_v) / 8)}; \
__VA_ARGS__ \
} \
\
for (u64 match_result_v{match_result[1]}; match_result_v != 0; match_result_v &= match_result_v - 1) { \
const size_t match_index{static_cast<size_t>(8 + std::countr_zero(match_result_v) / 8)}; \
__VA_ARGS__ \
} \
}
# define MCL_HMAP_MATCH_META_BYTE_GROUP_EXCEPT_LAST(MATCH, ...) \
{ \
const std::array<u64, 2> match_result{MATCH}; \
\
for (u64 match_result_v{match_result[0]}; match_result_v != 0; match_result_v &= match_result_v - 1) { \
const size_t match_index{static_cast<size_t>(std::countr_zero(match_result_v) / 8)}; \
__VA_ARGS__ \
} \
\
for (u64 match_result_v{match_result[1] & 0x00ffffffffffffff}; match_result_v != 0; match_result_v &= match_result_v - 1) { \
const size_t match_index{static_cast<size_t>(8 + std::countr_zero(match_result_v) / 8)}; \
__VA_ARGS__ \
} \
}
#endif
} // namespace mcl::detail

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