dep: Add biscuit and riscv-disas
This commit is contained in:
parent
c561400a47
commit
49a4901c78
|
@ -35,3 +35,7 @@ if(${CPU_ARCH} STREQUAL "aarch32" OR ${CPU_ARCH} STREQUAL "aarch64")
|
|||
add_subdirectory(vixl)
|
||||
endif()
|
||||
|
||||
if(${CPU_ARCH} STREQUAL "riscv64")
|
||||
add_subdirectory(biscuit)
|
||||
add_subdirectory(riscv-disas)
|
||||
endif()
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
cmake_minimum_required(VERSION 3.15)
|
||||
project(biscuit VERSION 0.9.1)
|
||||
|
||||
#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()
|
|
@ -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.
|
|
@ -0,0 +1,144 @@
|
|||
# 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 RC |
|
||||
| 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 |
|
||||
| Sstc | 0.5.4 |
|
||||
| Zfh | 1.0 |
|
||||
| Zfhmin | 1.0 |
|
||||
| Zicbom | 1.0 |
|
||||
| Zicbop | 1.0 |
|
||||
| Zicboz | 1.0 |
|
||||
| Zicsr | 2.0 |
|
||||
| Zifencei | 2.0 |
|
||||
| Zihintntl | 0.2 |
|
||||
|
||||
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();
|
||||
}
|
||||
```
|
|
@ -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
|
||||
...
|
|
@ -0,0 +1,5 @@
|
|||
@PACKAGE_INIT@
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@-targets.cmake")
|
||||
|
||||
check_required_components(@PROJECT_NAME@)
|
File diff suppressed because it is too large
Load Diff
|
@ -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)
|
|
@ -0,0 +1,211 @@
|
|||
#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());
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
BISCUIT_ASSERT(offset >= 0 && offset <= GetCursorOffset());
|
||||
return m_buffer + offset;
|
||||
}
|
||||
|
||||
/// Retrieves the pointer to an arbitrary location within the buffer.
|
||||
[[nodiscard]] const uint8_t* GetOffsetPointer(ptrdiff_t offset) const noexcept {
|
||||
BISCUIT_ASSERT(offset >= 0 && offset <= GetCursorOffset());
|
||||
return m_buffer + offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
|
@ -0,0 +1,101 @@
|
|||
// 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>
|
||||
|
||||
#if defined(__linux__) && defined(__riscv)
|
||||
#include <sys/auxv.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <asm/hwcap.h>
|
||||
#endif
|
||||
|
||||
namespace biscuit {
|
||||
|
||||
#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
|
||||
|
||||
enum class RISCVExtension : uint64_t {
|
||||
I = COMPAT_HWCAP_ISA_I,
|
||||
M = COMPAT_HWCAP_ISA_M,
|
||||
A = COMPAT_HWCAP_ISA_A,
|
||||
F = COMPAT_HWCAP_ISA_F,
|
||||
D = COMPAT_HWCAP_ISA_D,
|
||||
C = COMPAT_HWCAP_ISA_C,
|
||||
V = COMPAT_HWCAP_ISA_V
|
||||
};
|
||||
|
||||
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
|
|
@ -0,0 +1,390 @@
|
|||
#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)
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
|
||||
HTVal = 0x643, // Hypervisor bad guest physical address
|
||||
HIP = 0x644, // Hypervisor interrupt pending
|
||||
HVIP = 0x645, // Hypervisor virtual interrupt pending
|
||||
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
|
||||
|
||||
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
|
||||
MStatusH = 0x310, // Additional machine status register, RV32 only
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
MContext = 0x7A8, // Machine-mode context register
|
||||
|
||||
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
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,276 @@
|
|||
#pragma once
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
/// Determines whether or not this register is a general-purpose register.
|
||||
[[nodiscard]] constexpr bool IsGPR() const noexcept {
|
||||
return m_type == Type::GPR;
|
||||
}
|
||||
|
||||
/// Determines whether or not this register is a floating-point register.
|
||||
[[nodiscard]] constexpr bool IsFPR() const noexcept {
|
||||
return m_type == Type::FPR;
|
||||
}
|
||||
|
||||
/// Determines whether or not this register is a vector register.
|
||||
[[nodiscard]] constexpr bool IsVector() const noexcept {
|
||||
return m_type == Type::Vector;
|
||||
}
|
||||
|
||||
protected:
|
||||
enum class Type {
|
||||
GPR, // General purpose register
|
||||
FPR, // Floating-point register
|
||||
Vector, // Vector register
|
||||
};
|
||||
|
||||
constexpr Register(uint32_t index, Type type) noexcept
|
||||
: m_index{index}, m_type{type} {}
|
||||
|
||||
private:
|
||||
uint32_t m_index{};
|
||||
Type m_type{};
|
||||
};
|
||||
|
||||
/// General purpose register.
|
||||
class GPR final : public Register {
|
||||
public:
|
||||
constexpr GPR() noexcept : Register{0, Type::GPR} {}
|
||||
constexpr explicit GPR(uint32_t index) noexcept : Register{index, Type::GPR} {}
|
||||
|
||||
friend constexpr bool operator==(GPR lhs, GPR rhs) noexcept {
|
||||
return lhs.Index() == rhs.Index();
|
||||
}
|
||||
friend constexpr bool operator!=(GPR lhs, GPR rhs) noexcept {
|
||||
return !operator==(lhs, rhs);
|
||||
}
|
||||
};
|
||||
|
||||
/// Floating point register.
|
||||
class FPR final : public Register {
|
||||
public:
|
||||
constexpr FPR() noexcept : Register{0, Type::FPR} {}
|
||||
constexpr explicit FPR(uint32_t index) noexcept : Register{index, Type::FPR} {}
|
||||
|
||||
friend constexpr bool operator==(FPR lhs, FPR rhs) noexcept {
|
||||
return lhs.Index() == rhs.Index();
|
||||
}
|
||||
friend constexpr bool operator!=(FPR lhs, FPR rhs) noexcept {
|
||||
return !operator==(lhs, rhs);
|
||||
}
|
||||
};
|
||||
|
||||
/// Vector register.
|
||||
class Vec final : public Register {
|
||||
public:
|
||||
constexpr Vec() noexcept : Register{0, Type::Vector} {}
|
||||
constexpr explicit Vec(uint32_t index) noexcept : Register{index, Type::Vector} {}
|
||||
|
||||
friend constexpr bool operator==(Vec lhs, Vec rhs) noexcept {
|
||||
return lhs.Index() == rhs.Index();
|
||||
}
|
||||
friend constexpr bool operator!=(Vec lhs, Vec rhs) noexcept {
|
||||
return !operator==(lhs, rhs);
|
||||
}
|
||||
};
|
||||
|
||||
// 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};
|
||||
|
||||
} // namespace biscuit
|
|
@ -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
|
|
@ -0,0 +1,153 @@
|
|||
# Main library
|
||||
|
||||
add_library(biscuit
|
||||
# Source files
|
||||
assembler.cpp
|
||||
assembler_crypto.cpp
|
||||
assembler_vector.cpp
|
||||
code_buffer.cpp
|
||||
cpuinfo.cpp
|
||||
|
||||
# Headers
|
||||
"${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
|
@ -0,0 +1,149 @@
|
|||
#include <biscuit/assert.hpp>
|
||||
#include <biscuit/assembler.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 {
|
||||
EmitAES32Instruction(m_buffer, 0x2A000033, rd, rs1, rs2, bs);
|
||||
}
|
||||
|
||||
void Assembler::AES32DSMI(GPR rd, GPR rs1, GPR rs2, uint32_t bs) noexcept {
|
||||
EmitAES32Instruction(m_buffer, 0x2E000033, rd, rs1, rs2, bs);
|
||||
}
|
||||
|
||||
void Assembler::AES32ESI(GPR rd, GPR rs1, GPR rs2, uint32_t bs) noexcept {
|
||||
EmitAES32Instruction(m_buffer, 0x22000033, rd, rs1, rs2, bs);
|
||||
}
|
||||
|
||||
void Assembler::AES32ESMI(GPR rd, GPR rs1, GPR rs2, uint32_t bs) noexcept {
|
||||
EmitAES32Instruction(m_buffer, 0x26000033, rd, rs1, rs2, bs);
|
||||
}
|
||||
|
||||
void Assembler::AES64DS(GPR rd, GPR rs1, GPR rs2) noexcept {
|
||||
EmitAES64Instruction(m_buffer, 0x3A000033, rd, rs1, rs2);
|
||||
}
|
||||
|
||||
void Assembler::AES64DSM(GPR rd, GPR rs1, GPR rs2) noexcept {
|
||||
EmitAES64Instruction(m_buffer, 0x3E000033, rd, rs1, rs2);
|
||||
}
|
||||
|
||||
void Assembler::AES64ES(GPR rd, GPR rs1, GPR rs2) noexcept {
|
||||
EmitAES64Instruction(m_buffer, 0x32000033, rd, rs1, rs2);
|
||||
}
|
||||
|
||||
void Assembler::AES64ESM(GPR rd, GPR rs1, GPR rs2) noexcept {
|
||||
EmitAES64Instruction(m_buffer, 0x36000033, rd, rs1, rs2);
|
||||
}
|
||||
|
||||
void Assembler::AES64IM(GPR rd, GPR rs) noexcept {
|
||||
EmitAES64Instruction(m_buffer, 0x30001013, rd, rs, x0);
|
||||
}
|
||||
|
||||
void Assembler::AES64KS1I(GPR rd, GPR rs, uint32_t rnum) noexcept {
|
||||
// RVK spec states that 0xB to 0xF are reserved.
|
||||
BISCUIT_ASSERT(rnum <= 0xA);
|
||||
EmitAES64Instruction(m_buffer, 0x31001013, rd, rs, GPR{rnum});
|
||||
}
|
||||
|
||||
void Assembler::AES64KS2(GPR rd, GPR rs1, GPR rs2) noexcept {
|
||||
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 {
|
||||
EmitSHAInstruction(m_buffer, 0x10601013, rd, rs, x0);
|
||||
}
|
||||
|
||||
void Assembler::SHA512SIG0H(GPR rd, GPR rs1, GPR rs2) noexcept {
|
||||
EmitSHAInstruction(m_buffer, 0x5C000033, rd, rs1, rs2);
|
||||
}
|
||||
|
||||
void Assembler::SHA512SIG0L(GPR rd, GPR rs1, GPR rs2) noexcept {
|
||||
EmitSHAInstruction(m_buffer, 0x54000033, rd, rs1, rs2);
|
||||
}
|
||||
|
||||
void Assembler::SHA512SIG1(GPR rd, GPR rs) noexcept {
|
||||
EmitSHAInstruction(m_buffer, 0x10701013, rd, rs, x0);
|
||||
}
|
||||
|
||||
void Assembler::SHA512SIG1H(GPR rd, GPR rs1, GPR rs2) noexcept {
|
||||
EmitSHAInstruction(m_buffer, 0x5E000033, rd, rs1, rs2);
|
||||
}
|
||||
|
||||
void Assembler::SHA512SIG1L(GPR rd, GPR rs1, GPR rs2) noexcept {
|
||||
EmitSHAInstruction(m_buffer, 0x56000033, rd, rs1, rs2);
|
||||
}
|
||||
|
||||
void Assembler::SHA512SUM0(GPR rd, GPR rs) noexcept {
|
||||
EmitSHAInstruction(m_buffer, 0x10401013, rd, rs, x0);
|
||||
}
|
||||
|
||||
void Assembler::SHA512SUM0R(GPR rd, GPR rs1, GPR rs2) noexcept {
|
||||
EmitSHAInstruction(m_buffer, 0x50000033, rd, rs1, rs2);
|
||||
}
|
||||
|
||||
void Assembler::SHA512SUM1(GPR rd, GPR rs) noexcept {
|
||||
EmitSHAInstruction(m_buffer, 0x10501013, rd, rs, x0);
|
||||
}
|
||||
|
||||
void Assembler::SHA512SUM1R(GPR rd, GPR rs1, GPR rs2) noexcept {
|
||||
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
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
|
@ -0,0 +1,39 @@
|
|||
// 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/cpuinfo.hpp>
|
||||
|
||||
namespace biscuit {
|
||||
|
||||
bool CPUInfo::Has(RISCVExtension extension) const {
|
||||
#if defined(__linux__) && defined(__riscv)
|
||||
const static 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
|
||||
);
|
||||
#else
|
||||
const static uint64_t features = 0;
|
||||
#endif
|
||||
|
||||
return (features & static_cast<uint64_t>(extension)) != 0;
|
||||
}
|
||||
|
||||
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
|
|
@ -0,0 +1,7 @@
|
|||
add_library(riscv-disas
|
||||
include/riscv-disas.h
|
||||
src/riscv-disas.c
|
||||
)
|
||||
|
||||
target_include_directories(riscv-disas PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
# RISC-V Disassembler
|
||||
|
||||
RISC-V Disassembler with support for RV32/RV64/RV128 IMAFDC
|
||||
|
||||
## Build instructions
|
||||
|
||||
```
|
||||
cmake -B build
|
||||
cmake --build build
|
||||
```
|
|
@ -0,0 +1,520 @@
|
|||
/*
|
||||
* RISC-V Disassembler
|
||||
*
|
||||
* Copyright (c) 2016-2017 Michael Clark <michaeljclark@mac.com>
|
||||
* Copyright (c) 2017-2018 SiFive, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef RISCV_DISASSEMBLER_H
|
||||
#define RISCV_DISASSEMBLER_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
|
||||
/* types */
|
||||
|
||||
typedef uint64_t rv_inst;
|
||||
typedef uint16_t rv_opcode;
|
||||
|
||||
/* enums */
|
||||
|
||||
typedef enum {
|
||||
rv32,
|
||||
rv64,
|
||||
rv128
|
||||
} rv_isa;
|
||||
|
||||
typedef enum {
|
||||
rv_rm_rne = 0,
|
||||
rv_rm_rtz = 1,
|
||||
rv_rm_rdn = 2,
|
||||
rv_rm_rup = 3,
|
||||
rv_rm_rmm = 4,
|
||||
rv_rm_dyn = 7,
|
||||
} rv_rm;
|
||||
|
||||
typedef enum {
|
||||
rv_fence_i = 8,
|
||||
rv_fence_o = 4,
|
||||
rv_fence_r = 2,
|
||||
rv_fence_w = 1,
|
||||
} rv_fence;
|
||||
|
||||
typedef enum {
|
||||
rv_ireg_zero,
|
||||
rv_ireg_ra,
|
||||
rv_ireg_sp,
|
||||
rv_ireg_gp,
|
||||
rv_ireg_tp,
|
||||
rv_ireg_t0,
|
||||
rv_ireg_t1,
|
||||
rv_ireg_t2,
|
||||
rv_ireg_s0,
|
||||
rv_ireg_s1,
|
||||
rv_ireg_a0,
|
||||
rv_ireg_a1,
|
||||
rv_ireg_a2,
|
||||
rv_ireg_a3,
|
||||
rv_ireg_a4,
|
||||
rv_ireg_a5,
|
||||
rv_ireg_a6,
|
||||
rv_ireg_a7,
|
||||
rv_ireg_s2,
|
||||
rv_ireg_s3,
|
||||
rv_ireg_s4,
|
||||
rv_ireg_s5,
|
||||
rv_ireg_s6,
|
||||
rv_ireg_s7,
|
||||
rv_ireg_s8,
|
||||
rv_ireg_s9,
|
||||
rv_ireg_s10,
|
||||
rv_ireg_s11,
|
||||
rv_ireg_t3,
|
||||
rv_ireg_t4,
|
||||
rv_ireg_t5,
|
||||
rv_ireg_t6,
|
||||
} rv_ireg;
|
||||
|
||||
typedef enum {
|
||||
rvc_end,
|
||||
rvc_rd_eq_ra,
|
||||
rvc_rd_eq_x0,
|
||||
rvc_rs1_eq_x0,
|
||||
rvc_rs2_eq_x0,
|
||||
rvc_rs2_eq_rs1,
|
||||
rvc_rs1_eq_ra,
|
||||
rvc_imm_eq_zero,
|
||||
rvc_imm_eq_n1,
|
||||
rvc_imm_eq_p1,
|
||||
rvc_csr_eq_0x001,
|
||||
rvc_csr_eq_0x002,
|
||||
rvc_csr_eq_0x003,
|
||||
rvc_csr_eq_0xc00,
|
||||
rvc_csr_eq_0xc01,
|
||||
rvc_csr_eq_0xc02,
|
||||
rvc_csr_eq_0xc80,
|
||||
rvc_csr_eq_0xc81,
|
||||
rvc_csr_eq_0xc82,
|
||||
} rvc_constraint;
|
||||
|
||||
typedef enum {
|
||||
rv_codec_illegal,
|
||||
rv_codec_none,
|
||||
rv_codec_u,
|
||||
rv_codec_uj,
|
||||
rv_codec_i,
|
||||
rv_codec_i_sh5,
|
||||
rv_codec_i_sh6,
|
||||
rv_codec_i_sh7,
|
||||
rv_codec_i_csr,
|
||||
rv_codec_s,
|
||||
rv_codec_sb,
|
||||
rv_codec_r,
|
||||
rv_codec_r_m,
|
||||
rv_codec_r4_m,
|
||||
rv_codec_r_a,
|
||||
rv_codec_r_l,
|
||||
rv_codec_r_f,
|
||||
rv_codec_cb,
|
||||
rv_codec_cb_imm,
|
||||
rv_codec_cb_sh5,
|
||||
rv_codec_cb_sh6,
|
||||
rv_codec_ci,
|
||||
rv_codec_ci_sh5,
|
||||
rv_codec_ci_sh6,
|
||||
rv_codec_ci_16sp,
|
||||
rv_codec_ci_lwsp,
|
||||
rv_codec_ci_ldsp,
|
||||
rv_codec_ci_lqsp,
|
||||
rv_codec_ci_li,
|
||||
rv_codec_ci_lui,
|
||||
rv_codec_ci_none,
|
||||
rv_codec_ciw_4spn,
|
||||
rv_codec_cj,
|
||||
rv_codec_cj_jal,
|
||||
rv_codec_cl_lw,
|
||||
rv_codec_cl_ld,
|
||||
rv_codec_cl_lq,
|
||||
rv_codec_cr,
|
||||
rv_codec_cr_mv,
|
||||
rv_codec_cr_jalr,
|
||||
rv_codec_cr_jr,
|
||||
rv_codec_cs,
|
||||
rv_codec_cs_sw,
|
||||
rv_codec_cs_sd,
|
||||
rv_codec_cs_sq,
|
||||
rv_codec_css_swsp,
|
||||
rv_codec_css_sdsp,
|
||||
rv_codec_css_sqsp,
|
||||
} rv_codec;
|
||||
|
||||
typedef enum {
|
||||
rv_op_illegal,
|
||||
rv_op_lui,
|
||||
rv_op_auipc,
|
||||
rv_op_jal,
|
||||
rv_op_jalr,
|
||||
rv_op_beq,
|
||||
rv_op_bne,
|
||||
rv_op_blt,
|
||||
rv_op_bge,
|
||||
rv_op_bltu,
|
||||
rv_op_bgeu,
|
||||
rv_op_lb,
|
||||
rv_op_lh,
|
||||
rv_op_lw,
|
||||
rv_op_lbu,
|
||||
rv_op_lhu,
|
||||
rv_op_sb,
|
||||
rv_op_sh,
|
||||
rv_op_sw,
|
||||
rv_op_addi,
|
||||
rv_op_slti,
|
||||
rv_op_sltiu,
|
||||
rv_op_xori,
|
||||
rv_op_ori,
|
||||
rv_op_andi,
|
||||
rv_op_slli,
|
||||
rv_op_srli,
|
||||
rv_op_srai,
|
||||
rv_op_add,
|
||||
rv_op_sub,
|
||||
rv_op_sll,
|
||||
rv_op_slt,
|
||||
rv_op_sltu,
|
||||
rv_op_xor,
|
||||
rv_op_srl,
|
||||
rv_op_sra,
|
||||
rv_op_or,
|
||||
rv_op_and,
|
||||
rv_op_fence,
|
||||
rv_op_fence_i,
|
||||
rv_op_lwu,
|
||||
rv_op_ld,
|
||||
rv_op_sd,
|
||||
rv_op_addiw,
|
||||
rv_op_slliw,
|
||||
rv_op_srliw,
|
||||
rv_op_sraiw,
|
||||
rv_op_addw,
|
||||
rv_op_subw,
|
||||
rv_op_sllw,
|
||||
rv_op_srlw,
|
||||
rv_op_sraw,
|
||||
rv_op_ldu,
|
||||
rv_op_lq,
|
||||
rv_op_sq,
|
||||
rv_op_addid,
|
||||
rv_op_sllid,
|
||||
rv_op_srlid,
|
||||
rv_op_sraid,
|
||||
rv_op_addd,
|
||||
rv_op_subd,
|
||||
rv_op_slld,
|
||||
rv_op_srld,
|
||||
rv_op_srad,
|
||||
rv_op_mul,
|
||||
rv_op_mulh,
|
||||
rv_op_mulhsu,
|
||||
rv_op_mulhu,
|
||||
rv_op_div,
|
||||
rv_op_divu,
|
||||
rv_op_rem,
|
||||
rv_op_remu,
|
||||
rv_op_mulw,
|
||||
rv_op_divw,
|
||||
rv_op_divuw,
|
||||
rv_op_remw,
|
||||
rv_op_remuw,
|
||||
rv_op_muld,
|
||||
rv_op_divd,
|
||||
rv_op_divud,
|
||||
rv_op_remd,
|
||||
rv_op_remud,
|
||||
rv_op_lr_w,
|
||||
rv_op_sc_w,
|
||||
rv_op_amoswap_w,
|
||||
rv_op_amoadd_w,
|
||||
rv_op_amoxor_w,
|
||||
rv_op_amoor_w,
|
||||
rv_op_amoand_w,
|
||||
rv_op_amomin_w,
|
||||
rv_op_amomax_w,
|
||||
rv_op_amominu_w,
|
||||
rv_op_amomaxu_w,
|
||||
rv_op_lr_d,
|
||||
rv_op_sc_d,
|
||||
rv_op_amoswap_d,
|
||||
rv_op_amoadd_d,
|
||||
rv_op_amoxor_d,
|
||||
rv_op_amoor_d,
|
||||
rv_op_amoand_d,
|
||||
rv_op_amomin_d,
|
||||
rv_op_amomax_d,
|
||||
rv_op_amominu_d,
|
||||
rv_op_amomaxu_d,
|
||||
rv_op_lr_q,
|
||||
rv_op_sc_q,
|
||||
rv_op_amoswap_q,
|
||||
rv_op_amoadd_q,
|
||||
rv_op_amoxor_q,
|
||||
rv_op_amoor_q,
|
||||
rv_op_amoand_q,
|
||||
rv_op_amomin_q,
|
||||
rv_op_amomax_q,
|
||||
rv_op_amominu_q,
|
||||
rv_op_amomaxu_q,
|
||||
rv_op_ecall,
|
||||
rv_op_ebreak,
|
||||
rv_op_uret,
|
||||
rv_op_sret,
|
||||
rv_op_hret,
|
||||
rv_op_mret,
|
||||
rv_op_dret,
|
||||
rv_op_sfence_vm,
|
||||
rv_op_sfence_vma,
|
||||
rv_op_wfi,
|
||||
rv_op_csrrw,
|
||||
rv_op_csrrs,
|
||||
rv_op_csrrc,
|
||||
rv_op_csrrwi,
|
||||
rv_op_csrrsi,
|
||||
rv_op_csrrci,
|
||||
rv_op_flw,
|
||||
rv_op_fsw,
|
||||
rv_op_fmadd_s,
|
||||
rv_op_fmsub_s,
|
||||
rv_op_fnmsub_s,
|
||||
rv_op_fnmadd_s,
|
||||
rv_op_fadd_s,
|
||||
rv_op_fsub_s,
|
||||
rv_op_fmul_s,
|
||||
rv_op_fdiv_s,
|
||||
rv_op_fsgnj_s,
|
||||
rv_op_fsgnjn_s,
|
||||
rv_op_fsgnjx_s,
|
||||
rv_op_fmin_s,
|
||||
rv_op_fmax_s,
|
||||
rv_op_fsqrt_s,
|
||||
rv_op_fle_s,
|
||||
rv_op_flt_s,
|
||||
rv_op_feq_s,
|
||||
rv_op_fcvt_w_s,
|
||||
rv_op_fcvt_wu_s,
|
||||
rv_op_fcvt_s_w,
|
||||
rv_op_fcvt_s_wu,
|
||||
rv_op_fmv_x_s,
|
||||
rv_op_fclass_s,
|
||||
rv_op_fmv_s_x,
|
||||
rv_op_fcvt_l_s,
|
||||
rv_op_fcvt_lu_s,
|
||||
rv_op_fcvt_s_l,
|
||||
rv_op_fcvt_s_lu,
|
||||
rv_op_fld,
|
||||
rv_op_fsd,
|
||||
rv_op_fmadd_d,
|
||||
rv_op_fmsub_d,
|
||||
rv_op_fnmsub_d,
|
||||
rv_op_fnmadd_d,
|
||||
rv_op_fadd_d,
|
||||
rv_op_fsub_d,
|
||||
rv_op_fmul_d,
|
||||
rv_op_fdiv_d,
|
||||
rv_op_fsgnj_d,
|
||||
rv_op_fsgnjn_d,
|
||||
rv_op_fsgnjx_d,
|
||||
rv_op_fmin_d,
|
||||
rv_op_fmax_d,
|
||||
rv_op_fcvt_s_d,
|
||||
rv_op_fcvt_d_s,
|
||||
rv_op_fsqrt_d,
|
||||
rv_op_fle_d,
|
||||
rv_op_flt_d,
|
||||
rv_op_feq_d,
|
||||
rv_op_fcvt_w_d,
|
||||
rv_op_fcvt_wu_d,
|
||||
rv_op_fcvt_d_w,
|
||||
rv_op_fcvt_d_wu,
|
||||
rv_op_fclass_d,
|
||||
rv_op_fcvt_l_d,
|
||||
rv_op_fcvt_lu_d,
|
||||
rv_op_fmv_x_d,
|
||||
rv_op_fcvt_d_l,
|
||||
rv_op_fcvt_d_lu,
|
||||
rv_op_fmv_d_x,
|
||||
rv_op_flq,
|
||||
rv_op_fsq,
|
||||
rv_op_fmadd_q,
|
||||
rv_op_fmsub_q,
|
||||
rv_op_fnmsub_q,
|
||||
rv_op_fnmadd_q,
|
||||
rv_op_fadd_q,
|
||||
rv_op_fsub_q,
|
||||
rv_op_fmul_q,
|
||||
rv_op_fdiv_q,
|
||||
rv_op_fsgnj_q,
|
||||
rv_op_fsgnjn_q,
|
||||
rv_op_fsgnjx_q,
|
||||
rv_op_fmin_q,
|
||||
rv_op_fmax_q,
|
||||
rv_op_fcvt_s_q,
|
||||
rv_op_fcvt_q_s,
|
||||
rv_op_fcvt_d_q,
|
||||
rv_op_fcvt_q_d,
|
||||
rv_op_fsqrt_q,
|
||||
rv_op_fle_q,
|
||||
rv_op_flt_q,
|
||||
rv_op_feq_q,
|
||||
rv_op_fcvt_w_q,
|
||||
rv_op_fcvt_wu_q,
|
||||
rv_op_fcvt_q_w,
|
||||
rv_op_fcvt_q_wu,
|
||||
rv_op_fclass_q,
|
||||
rv_op_fcvt_l_q,
|
||||
rv_op_fcvt_lu_q,
|
||||
rv_op_fcvt_q_l,
|
||||
rv_op_fcvt_q_lu,
|
||||
rv_op_fmv_x_q,
|
||||
rv_op_fmv_q_x,
|
||||
rv_op_c_addi4spn,
|
||||
rv_op_c_fld,
|
||||
rv_op_c_lw,
|
||||
rv_op_c_flw,
|
||||
rv_op_c_fsd,
|
||||
rv_op_c_sw,
|
||||
rv_op_c_fsw,
|
||||
rv_op_c_nop,
|
||||
rv_op_c_addi,
|
||||
rv_op_c_jal,
|
||||
rv_op_c_li,
|
||||
rv_op_c_addi16sp,
|
||||
rv_op_c_lui,
|
||||
rv_op_c_srli,
|
||||
rv_op_c_srai,
|
||||
rv_op_c_andi,
|
||||
rv_op_c_sub,
|
||||
rv_op_c_xor,
|
||||
rv_op_c_or,
|
||||
rv_op_c_and,
|
||||
rv_op_c_subw,
|
||||
rv_op_c_addw,
|
||||
rv_op_c_j,
|
||||
rv_op_c_beqz,
|
||||
rv_op_c_bnez,
|
||||
rv_op_c_slli,
|
||||
rv_op_c_fldsp,
|
||||
rv_op_c_lwsp,
|
||||
rv_op_c_flwsp,
|
||||
rv_op_c_jr,
|
||||
rv_op_c_mv,
|
||||
rv_op_c_ebreak,
|
||||
rv_op_c_jalr,
|
||||
rv_op_c_add,
|
||||
rv_op_c_fsdsp,
|
||||
rv_op_c_swsp,
|
||||
rv_op_c_fswsp,
|
||||
rv_op_c_ld,
|
||||
rv_op_c_sd,
|
||||
rv_op_c_addiw,
|
||||
rv_op_c_ldsp,
|
||||
rv_op_c_sdsp,
|
||||
rv_op_c_lq,
|
||||
rv_op_c_sq,
|
||||
rv_op_c_lqsp,
|
||||
rv_op_c_sqsp,
|
||||
rv_op_nop,
|
||||
rv_op_mv,
|
||||
rv_op_not,
|
||||
rv_op_neg,
|
||||
rv_op_negw,
|
||||
rv_op_sext_w,
|
||||
rv_op_seqz,
|
||||
rv_op_snez,
|
||||
rv_op_sltz,
|
||||
rv_op_sgtz,
|
||||
rv_op_fmv_s,
|
||||
rv_op_fabs_s,
|
||||
rv_op_fneg_s,
|
||||
rv_op_fmv_d,
|
||||
rv_op_fabs_d,
|
||||
rv_op_fneg_d,
|
||||
rv_op_fmv_q,
|
||||
rv_op_fabs_q,
|
||||
rv_op_fneg_q,
|
||||
rv_op_beqz,
|
||||
rv_op_bnez,
|
||||
rv_op_blez,
|
||||
rv_op_bgez,
|
||||
rv_op_bltz,
|
||||
rv_op_bgtz,
|
||||
rv_op_ble,
|
||||
rv_op_bleu,
|
||||
rv_op_bgt,
|
||||
rv_op_bgtu,
|
||||
rv_op_j,
|
||||
rv_op_ret,
|
||||
rv_op_jr,
|
||||
rv_op_rdcycle,
|
||||
rv_op_rdtime,
|
||||
rv_op_rdinstret,
|
||||
rv_op_rdcycleh,
|
||||
rv_op_rdtimeh,
|
||||
rv_op_rdinstreth,
|
||||
rv_op_frcsr,
|
||||
rv_op_frrm,
|
||||
rv_op_frflags,
|
||||
rv_op_fscsr,
|
||||
rv_op_fsrm,
|
||||
rv_op_fsflags,
|
||||
rv_op_fsrmi,
|
||||
rv_op_fsflagsi,
|
||||
} rv_op;
|
||||
|
||||
/* structures */
|
||||
|
||||
typedef struct {
|
||||
uint64_t pc;
|
||||
uint64_t inst;
|
||||
int32_t imm;
|
||||
uint16_t op;
|
||||
uint8_t codec;
|
||||
uint8_t rd;
|
||||
uint8_t rs1;
|
||||
uint8_t rs2;
|
||||
uint8_t rs3;
|
||||
uint8_t rm;
|
||||
uint8_t pred;
|
||||
uint8_t succ;
|
||||
uint8_t aq;
|
||||
uint8_t rl;
|
||||
} rv_decode;
|
||||
|
||||
/* functions */
|
||||
|
||||
size_t inst_length(rv_inst inst);
|
||||
void inst_fetch(const uint8_t *data, rv_inst *instp, size_t *length);
|
||||
void disasm_inst(char *buf, size_t buflen, rv_isa isa, uint64_t pc, rv_inst inst);
|
||||
|
||||
#endif
|
|
@ -0,0 +1 @@
|
|||
https://github.com/michaeljclark/riscv-disassembler
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue