dep: Add biscuit and riscv-disas

This commit is contained in:
Stenzek 2023-05-30 00:11:06 +10:00
parent c561400a47
commit 49a4901c78
26 changed files with 10449 additions and 0 deletions

View file

@ -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()

View file

@ -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()

12
dep/biscuit/LICENSE.md Normal file
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.

144
dep/biscuit/README.md Normal file
View file

@ -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();
}
```

88
dep/biscuit/clang-format Normal file
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@)

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,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

View file

@ -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

View file

@ -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

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,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

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,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

View file

@ -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

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,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

View file

@ -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")

10
dep/riscv-disas/README.md Normal file
View file

@ -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
```

View file

@ -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

View file

@ -0,0 +1 @@
https://github.com/michaeljclark/riscv-disassembler

File diff suppressed because it is too large Load diff