diff options
Diffstat (limited to 'externals/biscuit')
55 files changed, 40248 insertions, 0 deletions
diff --git a/externals/biscuit/.github/workflows/build-and-test.yml b/externals/biscuit/.github/workflows/build-and-test.yml new file mode 100644 index 00000000..3fa8c40f --- /dev/null +++ b/externals/biscuit/.github/workflows/build-and-test.yml @@ -0,0 +1,45 @@ +name: Build and Test + +on: [push, pull_request] + +env: + BUILD_TYPE: Release + +jobs: + build: + strategy: + matrix: + os: [ubuntu-latest, macos-latest] + cpu_detection: [0, 1] + fail-fast: false + + runs-on: ${{matrix.os}} + + steps: + + - name: Install build dependencies + if: ${{matrix.os == 'ubuntu-latest'}} + run: sudo apt-get install llvm ninja-build + + - name: Install build dependencies + if: ${{matrix.os == 'macos-latest'}} + run: | + brew install llvm ninja + echo "/usr/local/opt/llvm/bin" >> $GITHUB_PATH + + - name: Checkout biscuit repo + uses: actions/checkout@v2 + + - name: Configure CMake + run: > + cmake + -B ${{github.workspace}}/build + -G Ninja + + - name: Build + working-directory: ${{github.workspace}}/build + run: ninja + + - name: Test + working-directory: ${{github.workspace}}/build + run: ctest --extra-verbose -C ${{env.BUILD_TYPE}} diff --git a/externals/biscuit/.gitignore b/externals/biscuit/.gitignore new file mode 100644 index 00000000..0e34a625 --- /dev/null +++ b/externals/biscuit/.gitignore @@ -0,0 +1,3 @@ +# Built files +build/ +build-*/ diff --git a/externals/biscuit/CMakeLists.txt b/externals/biscuit/CMakeLists.txt new file mode 100644 index 00000000..503c8c48 --- /dev/null +++ b/externals/biscuit/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.15) +project(biscuit VERSION 0.14.0) + +include(CTest) + +option(BISCUIT_CODE_BUFFER_MMAP "Use mmap for handling code buffers instead of new" OFF) + +# Source directories +add_subdirectory(src) + +if (BUILD_TESTING) + add_subdirectory(tests) +endif() + +if (BUILD_EXAMPLES) + add_subdirectory(examples) +endif() diff --git a/externals/biscuit/LICENSE.md b/externals/biscuit/LICENSE.md new file mode 100644 index 00000000..53cde664 --- /dev/null +++ b/externals/biscuit/LICENSE.md @@ -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.
\ No newline at end of file diff --git a/externals/biscuit/README.md b/externals/biscuit/README.md new file mode 100644 index 00000000..db4379a4 --- /dev/null +++ b/externals/biscuit/README.md @@ -0,0 +1,157 @@ +# Biscuit: RISC-V Runtime Code Generation Library + +*RISC it for the biscuit* + +## About + +An experimental runtime code generator for RISC-V. + +This allows for runtime code generation of RISC-V instructions. Similar +to how [Xbyak](https://github.com/herumi/xbyak) allows for runtime code generation of x86 instructions. + + +## Implemented ISA Features + +Includes both 32-bit and 64-bit instructions in the following: + +| Feature | Version | +|:----------|:-------:| +| A | 2.1 | +| B | 1.0 | +| C | 2.0 | +| D | 2.2 | +| F | 2.2 | +| H | 1.0 | +| K | 1.0.1 | +| M | 2.0 | +| N | 1.1 | +| Q | 2.2 | +| RV32I | 2.1 | +| RV64I | 2.1 | +| S | 1.12 | +| V | 1.0 | +| Sstc | 0.5.4 | +| Zacas | 1.0 | +| Zawrs | 1.01 | +| Zcb | 1.0.4 | +| Zcmp | 1.0.4 | +| Zcmt | 1.0.4 | +| Zfa | 1.0 | +| Zfbfmin | 1.0 rc2 | +| Zfh | 1.0 | +| Zfhmin | 1.0 | +| Zicbom | 1.0 | +| Zicbop | 1.0 | +| Zicboz | 1.0 | +| Zicond | 1.0.1 | +| Zicsr | 2.0 | +| Zifencei | 2.0 | +| Zihintntl | 1.0 | +| Zvbb | 1.0 | +| Zvbc | 1.0 | +| Zvfbfmin | 1.0 rc2 | +| Zvfbfwma | 1.0 rc2 | +| Zvkn | 1.0 | + +Note that usually only extensions considered ratified will be implemented +as non-ratified documents are considerably more likely to have +large changes made to them, which makes maintaining instruction +APIs a little annoying. + + +## Dependencies + +Biscuit requires no external dependencies for its library other than the C++ standard library. +The tests, however, use the Catch2 testing library. This is included in tree so there's no need +to worry about installing it yourself if you wish to run said tests. + + +## Building Biscuit + +1. Generate the build files for the project with CMake +2. Hit the build button in your IDE of choice, or run the relevant console command to build for the CMake generator you've chosen. +3. Done. + + +## Running Tests + +1. Generate the build files for the project with CMake +2. Build the tests +3. Run the test executable directly, or enter `ctest` into your terminal. + + +## License + +The library is licensed under the MIT license. + +While it's not a requirement whatsoever, it'd be pretty neat if you told me that you found the library useful :-) + + +## Example + +The following is an adapted equivalent of the `strlen` implementation within the RISC-V bit manipulation extension specification. +For brevity, it has been condensed to only handle little-endian platforms. + +```cpp +// We prepare some contiguous buffer and give the pointer to the beginning +// of the data and the total size of the buffer in bytes to the assembler. + +void strlen_example(uint8_t* buffer, size_t buffer_size) { + using namespace biscuit; + + constexpr int ptrlog = 3; + constexpr int szreg = 8; + + Assembler as(buffer, buffer_size); + Label done; + Label loop; + + as.ANDI(a3, a0, szreg - 1); // Offset + as.ANDI(a1, a0, 0xFF8); // Align pointer + + as.LI(a4, szreg); + as.SUB(a4, a4, a3); // XLEN - offset + as.SLLI(a3, a3, ptrlog); // offset * 8 + as.LD(a2, 0, a1); // Chunk + + // + // Shift the partial/unaligned chunk we loaded to remove the bytes + // from before the start of the string, adding NUL bytes at the end. + // + as.SRL(a2, a2, a3); // chunk >> (offset * 8) + as.ORCB(a2, a2); + as.NOT(a2, a2); + + // Non-NUL bytes in the string have been expanded to 0x00, while + // NUL bytes have become 0xff. Search for the first set bit + // (corresponding to a NUL byte in the original chunk). + as.CTZ(a2, a2); + + // The first chunk is special: compare against the number of valid + // bytes in this chunk. + as.SRLI(a0, a2, 3); + as.BGTU(a4, a0, &done); + as.ADDI(a3, a1, szreg); + as.LI(a4, -1); + + // Our critical loop is 4 instructions and processes data in 4 byte + // or 8 byte chunks. + as.Bind(&loop); + + as.LD(a2, szreg, a1); + as.ADDI(a1, a1, szreg); + as.ORCB(a2, a2); + as.BEQ(a2, a4, &loop); + + as.NOT(a2, a2); + as.CTZ(a2, a2); + as.SUB(a1, a1, a3); + as.ADD(a0, a0, a1); + as.SRLI(a2, a2, 3); + as.ADD(a0, a0, a2); + + as.Bind(&done); + + as.RET(); +} +``` diff --git a/externals/biscuit/clang-format b/externals/biscuit/clang-format new file mode 100644 index 00000000..1c6b71b2 --- /dev/null +++ b/externals/biscuit/clang-format @@ -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 +... diff --git a/externals/biscuit/cmake/biscuit-config.cmake.in b/externals/biscuit/cmake/biscuit-config.cmake.in new file mode 100644 index 00000000..46b180ab --- /dev/null +++ b/externals/biscuit/cmake/biscuit-config.cmake.in @@ -0,0 +1,5 @@ +@PACKAGE_INIT@ + +include("${CMAKE_CURRENT_LIST_DIR}/@[email protected]") + +check_required_components(@PROJECT_NAME@) diff --git a/externals/biscuit/examples/CMakeLists.txt b/externals/biscuit/examples/CMakeLists.txt new file mode 100644 index 00000000..8b3246cf --- /dev/null +++ b/externals/biscuit/examples/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(cpuinfo) diff --git a/externals/biscuit/examples/cpuinfo/CMakeLists.txt b/externals/biscuit/examples/cpuinfo/CMakeLists.txt new file mode 100644 index 00000000..16e6e4d0 --- /dev/null +++ b/externals/biscuit/examples/cpuinfo/CMakeLists.txt @@ -0,0 +1,3 @@ +add_executable(cpuinfo cpuinfo.cpp) +target_link_libraries(cpuinfo biscuit) +set_property(TARGET cpuinfo PROPERTY CXX_STANDARD 20) diff --git a/externals/biscuit/examples/cpuinfo/cpuinfo.cpp b/externals/biscuit/examples/cpuinfo/cpuinfo.cpp new file mode 100644 index 00000000..450dfbdb --- /dev/null +++ b/externals/biscuit/examples/cpuinfo/cpuinfo.cpp @@ -0,0 +1,31 @@ +// Copyright (c), 2022, KNS Group LLC (YADRO) +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +#include <biscuit/assembler.hpp> +#include <biscuit/cpuinfo.hpp> + +#include <iostream> + +using namespace biscuit; + +int main() +{ + CPUInfo cpu; + + std::cout << "Has I:" << cpu.Has(RISCVExtension::I) << std::endl; + std::cout << "Has M:" << cpu.Has(RISCVExtension::M) << std::endl; + std::cout << "Has A:" << cpu.Has(RISCVExtension::A) << std::endl; + std::cout << "Has F:" << cpu.Has(RISCVExtension::F) << std::endl; + std::cout << "Has D:" << cpu.Has(RISCVExtension::D) << std::endl; + std::cout << "Has C:" << cpu.Has(RISCVExtension::C) << std::endl; + std::cout << "Has V:" << cpu.Has(RISCVExtension::V) << std::endl; + + if (cpu.Has(RISCVExtension::V)) { + std::cout << "VLENB:" << cpu.GetVlenb() << std::endl; + } + + return 0; +} diff --git a/externals/biscuit/include/biscuit/assembler.hpp b/externals/biscuit/include/biscuit/assembler.hpp new file mode 100644 index 00000000..78673d3b --- /dev/null +++ b/externals/biscuit/include/biscuit/assembler.hpp @@ -0,0 +1,1475 @@ +#pragma once + +#include <biscuit/code_buffer.hpp> +#include <biscuit/csr.hpp> +#include <biscuit/isa.hpp> +#include <biscuit/label.hpp> +#include <biscuit/registers.hpp> +#include <biscuit/vector.hpp> +#include <cstddef> +#include <cstdint> + +namespace biscuit { + +/** + * Defines the set of features that a particular assembler instance + * would like to assemble for. + * + * This allows for assertions and extra logic checking to be done. + * + * It can also affect various behaviors as well. e.g. LI, shifts, etc + * will take these into account to adjust for emission on different + * environments transparently. + */ +enum class ArchFeature : uint32_t { + RV32, //< 32-bit RISC-V + RV64, //< 64-bit RISC-V + RV128, //< 128-bit RISC-V +}; + +/** + * Code generator for RISC-V code. + * + * User code may inherit from this in order to make use of + * the API more convenient, or use it separately if desired. + */ +class Assembler { +public: + /** + * Constructor + * + * Initializes the underlying code buffer to be able to hold `capacity` bytes. + * + * @param capacity The capacity for the underlying code buffer in bytes. + * If no capacity is specified, then the underlying buffer + * will be 4KB in size. + * + * @note Will assume to be assembling for RV64 unless changed. + */ + [[nodiscard]] explicit Assembler(size_t capacity = CodeBuffer::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`. + * @param features Architectural features to make the assembler aware of. + * + * @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. + */ + [[nodiscard]] explicit Assembler(uint8_t* buffer, size_t capacity, + ArchFeature features = ArchFeature::RV64); + + // Copy constructor and assignment. + Assembler(const Assembler&) = delete; + Assembler& operator=(const Assembler&) = delete; + + // Move constructor and assignment. + Assembler(Assembler&&) = default; + Assembler& operator=(Assembler&&) = default; + + // Destructor + virtual ~Assembler(); + + /** + * Tells the assembler what features to take into account. + * + * Will alter how some code is emitted and also enforce asserts suitable + * for those particular features. + */ + void SetArchFeatures(ArchFeature features) noexcept { + m_features = features; + } + + /// Gets the underlying code buffer being managed by this assembler. + CodeBuffer& GetCodeBuffer(); + + /** + * Allows swapping out the code buffer used by the assembler. + * + * @param buffer The new buffer for the assembler to emit code into. + * + * @returns The old buffer that the assembler made use of. + */ + CodeBuffer SwapCodeBuffer(CodeBuffer&& buffer) noexcept; + + /** + * 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 RewindBuffer(ptrdiff_t offset = 0) { + m_buffer.RewindCursor(offset); + } + + /// Retrieves the cursor pointer for the underlying code buffer. + [[nodiscard]] uint8_t* GetCursorPointer() noexcept { + return m_buffer.GetCursorPointer(); + } + + /// Retrieves the cursor for the underlying code buffer. + [[nodiscard]] const uint8_t* GetCursorPointer() const noexcept { + return m_buffer.GetCursorPointer(); + } + + /// Retrieves the pointer to an arbitrary location within the underlying code buffer. + [[nodiscard]] uint8_t* GetBufferPointer(ptrdiff_t offset) noexcept { + return m_buffer.GetOffsetPointer(offset); + } + + /// Retrieves the pointer to an arbitrary location within the underlying code buffer. + [[nodiscard]] const uint8_t* GetBufferPointer(ptrdiff_t offset) const noexcept { + return m_buffer.GetOffsetPointer(offset); + } + + /** + * Binds a label to the current offset within the code buffer + * + * @param label A non-null valid label to bind. + */ + void Bind(Label* label); + + // RV32I Instructions + + void ADD(GPR rd, GPR lhs, GPR rhs) noexcept; + void ADDI(GPR rd, GPR rs, int32_t imm) noexcept; + void AND(GPR rd, GPR lhs, GPR rhs) noexcept; + void ANDI(GPR rd, GPR rs, uint32_t imm) noexcept; + + void AUIPC(GPR rd, int32_t imm) noexcept; + + void BEQ(GPR rs1, GPR rs2, Label* label) noexcept; + void BEQZ(GPR rs, Label* label) noexcept; + void BGE(GPR rs1, GPR rs2, Label* label) noexcept; + void BGEU(GPR rs1, GPR rs2, Label* label) noexcept; + void BGEZ(GPR rs, Label* label) noexcept; + void BGT(GPR rs, GPR rt, Label* label) noexcept; + void BGTU(GPR rs, GPR rt, Label* label) noexcept; + void BGTZ(GPR rs, Label* label) noexcept; + void BLE(GPR rs, GPR rt, Label* label) noexcept; + void BLEU(GPR rs, GPR rt, Label* label) noexcept; + void BLEZ(GPR rs, Label* label) noexcept; + void BLT(GPR rs1, GPR rs2, Label* label) noexcept; + void BLTU(GPR rs1, GPR rs2, Label* label) noexcept; + void BLTZ(GPR rs, Label* label) noexcept; + void BNE(GPR rs1, GPR rs2, Label* label) noexcept; + void BNEZ(GPR rs, Label* label) noexcept; + + void BEQ(GPR rs1, GPR rs2, int32_t imm) noexcept; + void BEQZ(GPR rs, int32_t imm) noexcept; + void BGE(GPR rs1, GPR rs2, int32_t imm) noexcept; + void BGEU(GPR rs1, GPR rs2, int32_t imm) noexcept; + void BGEZ(GPR rs, int32_t imm) noexcept; + void BGT(GPR rs, GPR rt, int32_t imm) noexcept; + void BGTU(GPR rs, GPR rt, int32_t imm) noexcept; + void BGTZ(GPR rs, int32_t imm) noexcept; + void BLE(GPR rs, GPR rt, int32_t imm) noexcept; + void BLEU(GPR rs, GPR rt, int32_t imm) noexcept; + void BLEZ(GPR rs, int32_t imm) noexcept; + void BLT(GPR rs1, GPR rs2, int32_t imm) noexcept; + void BLTU(GPR rs1, GPR rs2, int32_t imm) noexcept; + void BLTZ(GPR rs, int32_t imm) noexcept; + void BNE(GPR rs1, GPR rs2, int32_t imm) noexcept; + void BNEZ(GPR rs, int32_t imm) noexcept; + + void CALL(int32_t offset) noexcept; + + void EBREAK() noexcept; + void ECALL() noexcept; + + void FENCE() noexcept; + void FENCE(FenceOrder pred, FenceOrder succ) noexcept; + void FENCEI(GPR rd = x0, GPR rs = x0, uint32_t imm = 0) noexcept; + void FENCETSO() noexcept; + + void J(Label* label) noexcept; + void JAL(Label* label) noexcept; + void JAL(GPR rd, Label* label) noexcept; + + void J(int32_t imm) noexcept; + void JAL(int32_t imm) noexcept; + void JAL(GPR rd, int32_t imm) noexcept; + void JALR(GPR rs) noexcept; + void JALR(GPR rd, int32_t imm, GPR rs1) noexcept; + void JR(GPR rs) noexcept; + + void LB(GPR rd, int32_t imm, GPR rs) noexcept; + void LBU(GPR rd, int32_t imm, GPR rs) noexcept; + void LH(GPR rd, int32_t imm, GPR rs) noexcept; + void LHU(GPR rd, int32_t imm, GPR rs) noexcept; + void LI(GPR rd, uint64_t imm) noexcept; + void LUI(GPR rd, uint32_t imm) noexcept; + void LW(GPR rd, int32_t imm, GPR rs) noexcept; + + void MV(GPR rd, GPR rs) noexcept; + void NEG(GPR rd, GPR rs) noexcept; + + void NOP() noexcept; + + void NOT(GPR rd, GPR rs) noexcept; + void OR(GPR rd, GPR lhs, GPR rhs) noexcept; + void ORI(GPR rd, GPR rs, uint32_t imm) noexcept; + + void PAUSE() noexcept; + void RET() noexcept; + + void SB(GPR rs2, int32_t imm, GPR rs1) noexcept; + void SH(GPR rs2, int32_t imm, GPR rs1) noexcept; + void SW(GPR rs2, int32_t imm, GPR rs1) noexcept; + + void SEQZ(GPR rd, GPR rs) noexcept; + void SGTZ(GPR rd, GPR rs) noexcept; + + void SLL(GPR rd, GPR lhs, GPR rhs) noexcept; + void SLLI(GPR rd, GPR rs, uint32_t shift) noexcept; + + void SLT(GPR rd, GPR lhs, GPR rhs) noexcept; + void SLTI(GPR rd, GPR rs, int32_t imm) noexcept; + void SLTIU(GPR rd, GPR rs, int32_t imm) noexcept; + void SLTU(GPR rd, GPR lhs, GPR rhs) noexcept; + void SLTZ(GPR rd, GPR rs) noexcept; + + void SNEZ(GPR rd, GPR rs) noexcept; + + void SRA(GPR rd, GPR lhs, GPR rhs) noexcept; + void SRAI(GPR rd, GPR rs, uint32_t shift) noexcept; + + void SRL(GPR rd, GPR lhs, GPR rhs) noexcept; + void SRLI(GPR rd, GPR rs, uint32_t shift) noexcept; + + void SUB(GPR rd, GPR lhs, GPR rhs) noexcept; + + void XOR(GPR rd, GPR lhs, GPR rhs) noexcept; + void XORI(GPR rd, GPR rs, uint32_t imm) noexcept; + + // RV64I Base Instruction Set + + void ADDIW(GPR rd, GPR rs, int32_t imm) noexcept; + void ADDW(GPR rd, GPR lhs, GPR rhs) noexcept; + void LD(GPR rd, int32_t imm, GPR rs) noexcept; + void LWU(GPR rd, int32_t imm, GPR rs) noexcept; + void SD(GPR rs2, int32_t imm, GPR rs1) noexcept; + + void SLLIW(GPR rd, GPR rs, uint32_t shift) noexcept; + void SRAIW(GPR rd, GPR rs, uint32_t shift) noexcept; + void SRLIW(GPR rd, GPR rs, uint32_t shift) noexcept; + + void SLLW(GPR rd, GPR lhs, GPR rhs) noexcept; + void SRAW(GPR rd, GPR lhs, GPR rhs) noexcept; + void SRLW(GPR rd, GPR lhs, GPR rhs) noexcept; + void SUBW(GPR rd, GPR lhs, GPR rhs) noexcept; + + // Zawrs Extension Instructions + void WRS_NTO() noexcept; + void WRS_STO() noexcept; + + // Zacas Extension Instructions + // + // NOTE: If targeting RV32 and using AMOCAS.D, rd and rs2 must be even-numbered + // registers, since they both indicate a register pair. + // + // On RV64, even and odd numbered registers can be used, + // + // On both RV32 and RV64, AMOCAS.Q requires rd and rs2 to be even-numbered + // since it also treats them like their own register pairs. + + void AMOCAS_D(Ordering ordering, GPR rd, GPR rs2, GPR rs1) noexcept; + void AMOCAS_Q(Ordering ordering, GPR rd, GPR rs2, GPR rs1) noexcept; + void AMOCAS_W(Ordering ordering, GPR rd, GPR rs2, GPR rs1) noexcept; + + // Zicond Extension Instructions + void CZERO_EQZ(GPR rd, GPR value, GPR condition) noexcept; + void CZERO_NEZ(GPR rd, GPR value, GPR condition) noexcept; + + // Zicsr Extension Instructions + + void CSRRC(GPR rd, CSR csr, GPR rs) noexcept; + void CSRRCI(GPR rd, CSR csr, uint32_t imm) noexcept; + void CSRRS(GPR rd, CSR csr, GPR rs) noexcept; + void CSRRSI(GPR rd, CSR csr, uint32_t imm) noexcept; + void CSRRW(GPR rd, CSR csr, GPR rs) noexcept; + void CSRRWI(GPR rd, CSR csr, uint32_t imm) noexcept; + + void CSRR(GPR rd, CSR csr) noexcept; + void CSWR(CSR csr, GPR rs) noexcept; + + void CSRS(CSR csr, GPR rs) noexcept; + void CSRC(CSR csr, GPR rs) noexcept; + + void CSRCI(CSR csr, uint32_t imm) noexcept; + void CSRSI(CSR csr, uint32_t imm) noexcept; + void CSRWI(CSR csr, uint32_t imm) noexcept; + + void FRCSR(GPR rd) noexcept; + void FSCSR(GPR rd, GPR rs) noexcept; + void FSCSR(GPR rs) noexcept; + + void FRRM(GPR rd) noexcept; + void FSRM(GPR rd, GPR rs) noexcept; + void FSRM(GPR rs) noexcept; + + void FSRMI(GPR rd, uint32_t imm) noexcept; + void FSRMI(uint32_t imm) noexcept; + + void FRFLAGS(GPR rd) noexcept; + void FSFLAGS(GPR rd, GPR rs) noexcept; + void FSFLAGS(GPR rs) noexcept; + + void FSFLAGSI(GPR rd, uint32_t imm) noexcept; + void FSFLAGSI(uint32_t imm) noexcept; + + void RDCYCLE(GPR rd) noexcept; + void RDCYCLEH(GPR rd) noexcept; + + void RDINSTRET(GPR rd) noexcept; + void RDINSTRETH(GPR rd) noexcept; + + void RDTIME(GPR rd) noexcept; + void RDTIMEH(GPR rd) noexcept; + + // Zihintntl Extension Instructions + + void C_NTL_ALL() noexcept; + void C_NTL_S1() noexcept; + void C_NTL_P1() noexcept; + void C_NTL_PALL() noexcept; + void NTL_ALL() noexcept; + void NTL_S1() noexcept; + void NTL_P1() noexcept; + void NTL_PALL() noexcept; + + // RV32M Extension Instructions + + void DIV(GPR rd, GPR rs1, GPR rs2) noexcept; + void DIVU(GPR rd, GPR rs1, GPR rs2) noexcept; + void MUL(GPR rd, GPR rs1, GPR rs2) noexcept; + void MULH(GPR rd, GPR rs1, GPR rs2) noexcept; + void MULHSU(GPR rd, GPR rs1, GPR rs2) noexcept; + void MULHU(GPR rd, GPR rs1, GPR rs2) noexcept; + void REM(GPR rd, GPR rs1, GPR rs2) noexcept; + void REMU(GPR rd, GPR rs1, GPR rs2) noexcept; + + // RV64M Extension Instructions + + void DIVW(GPR rd, GPR rs1, GPR rs2) noexcept; + void DIVUW(GPR rd, GPR rs1, GPR rs2) noexcept; + void MULW(GPR rd, GPR rs1, GPR rs2) noexcept; + void REMW(GPR rd, GPR rs1, GPR rs2) noexcept; + void REMUW(GPR rd, GPR rs1, GPR rs2) noexcept; + + // RV32A Extension Instructions + + void AMOADD_W(Ordering ordering, GPR rd, GPR rs2, GPR rs1) noexcept; + void AMOAND_W(Ordering ordering, GPR rd, GPR rs2, GPR rs1) noexcept; + void AMOMAX_W(Ordering ordering, GPR rd, GPR rs2, GPR rs1) noexcept; + void AMOMAXU_W(Ordering ordering, GPR rd, GPR rs2, GPR rs1) noexcept; + void AMOMIN_W(Ordering ordering, GPR rd, GPR rs2, GPR rs1) noexcept; + void AMOMINU_W(Ordering ordering, GPR rd, GPR rs2, GPR rs1) noexcept; + void AMOOR_W(Ordering ordering, GPR rd, GPR rs2, GPR rs1) noexcept; + void AMOSWAP_W(Ordering ordering, GPR rd, GPR rs2, GPR rs1) noexcept; + void AMOXOR_W(Ordering ordering, GPR rd, GPR rs2, GPR rs1) noexcept; + void LR_W(Ordering ordering, GPR rd, GPR rs) noexcept; + void SC_W(Ordering ordering, GPR rd, GPR rs2, GPR rs1) noexcept; + + // RV64A Extension Instructions + + void AMOADD_D(Ordering ordering, GPR rd, GPR rs2, GPR rs1) noexcept; + void AMOAND_D(Ordering ordering, GPR rd, GPR rs2, GPR rs1) noexcept; + void AMOMAX_D(Ordering ordering, GPR rd, GPR rs2, GPR rs1) noexcept; + void AMOMAXU_D(Ordering ordering, GPR rd, GPR rs2, GPR rs1) noexcept; + void AMOMIN_D(Ordering ordering, GPR rd, GPR rs2, GPR rs1) noexcept; + void AMOMINU_D(Ordering ordering, GPR rd, GPR rs2, GPR rs1) noexcept; + void AMOOR_D(Ordering ordering, GPR rd, GPR rs2, GPR rs1) noexcept; + void AMOSWAP_D(Ordering ordering, GPR rd, GPR rs2, GPR rs1) noexcept; + void AMOXOR_D(Ordering ordering, GPR rd, GPR rs2, GPR rs1) noexcept; + void LR_D(Ordering ordering, GPR rd, GPR rs) noexcept; + void SC_D(Ordering ordering, GPR rd, GPR rs2, GPR rs1) noexcept; + + // RV32F Extension Instructions + + void FADD_S(FPR rd, FPR rs1, FPR rs2, RMode rmode = RMode::DYN) noexcept; + void FCLASS_S(GPR rd, FPR rs1) noexcept; + void FCVT_S_W(FPR rd, GPR rs1, RMode rmode = RMode::DYN) noexcept; + void FCVT_S_WU(FPR rd, GPR rs1, RMode rmode = RMode::DYN) noexcept; + void FCVT_W_S(GPR rd, FPR rs1, RMode rmode = RMode::DYN) noexcept; + void FCVT_WU_S(GPR rd, FPR rs1, RMode rmode = RMode::DYN) noexcept; + void FDIV_S(FPR rd, FPR rs1, FPR rs2, RMode rmode = RMode::DYN) noexcept; + void FEQ_S(GPR rd, FPR rs1, FPR rs2) noexcept; + void FLE_S(GPR rd, FPR rs1, FPR rs2) noexcept; + void FLT_S(GPR rd, FPR rs1, FPR rs2) noexcept; + void FLW(FPR rd, int32_t offset, GPR rs) noexcept; + void FMADD_S(FPR rd, FPR rs1, FPR rs2, FPR rs3, RMode rmode = RMode::DYN) noexcept; + void FMAX_S(FPR rd, FPR rs1, FPR rs2) noexcept; + void FMIN_S(FPR rd, FPR rs1, FPR rs2) noexcept; + void FMSUB_S(FPR rd, FPR rs1, FPR rs2, FPR rs3, RMode rmode = RMode::DYN) noexcept; + void FMUL_S(FPR rd, FPR rs1, FPR rs2, RMode rmode = RMode::DYN) noexcept; + void FMV_W_X(FPR rd, GPR rs1) noexcept; + void FMV_X_W(GPR rd, FPR rs1) noexcept; + void FNMADD_S(FPR rd, FPR rs1, FPR rs2, FPR rs3, RMode rmode = RMode::DYN) noexcept; + void FNMSUB_S(FPR rd, FPR rs1, FPR rs2, FPR rs3, RMode rmode = RMode::DYN) noexcept; + void FSGNJ_S(FPR rd, FPR rs1, FPR rs2) noexcept; + void FSGNJN_S(FPR rd, FPR rs1, FPR rs2) noexcept; + void FSGNJX_S(FPR rd, FPR rs1, FPR rs2) noexcept; + void FSQRT_S(FPR rd, FPR rs1, RMode rmode = RMode::DYN) noexcept; + void FSUB_S(FPR rd, FPR rs1, FPR rs2, RMode rmode = RMode::DYN) noexcept; + void FSW(FPR rs2, int32_t offset, GPR rs1) noexcept; + + void FABS_S(FPR rd, FPR rs) noexcept; + void FMV_S(FPR rd, FPR rs) noexcept; + void FNEG_S(FPR rd, FPR rs) noexcept; + + // RV64F Extension Instructions + + void FCVT_L_S(GPR rd, FPR rs1, RMode rmode = RMode::DYN) noexcept; + void FCVT_LU_S(GPR rd, FPR rs1, RMode rmode = RMode::DYN) noexcept; + void FCVT_S_L(FPR rd, GPR rs1, RMode rmode = RMode::DYN) noexcept; + void FCVT_S_LU(FPR rd, GPR rs1, RMode rmode = RMode::DYN) noexcept; + + // RV32D Extension Instructions + + void FADD_D(FPR rd, FPR rs1, FPR rs2, RMode rmode = RMode::DYN) noexcept; + void FCLASS_D(GPR rd, FPR rs1) noexcept; + void FCVT_D_W(FPR rd, GPR rs1, RMode rmode = RMode::DYN) noexcept; + void FCVT_D_WU(FPR rd, GPR rs1, RMode rmode = RMode::DYN) noexcept; + void FCVT_W_D(GPR rd, FPR rs1, RMode rmode = RMode::DYN) noexcept; + void FCVT_WU_D(GPR rd, FPR rs1, RMode rmode = RMode::DYN) noexcept; + void FCVT_D_S(FPR rd, FPR rs1, RMode rmode = RMode::DYN) noexcept; + void FCVT_S_D(FPR rd, FPR rs1, RMode rmode = RMode::DYN) noexcept; + void FDIV_D(FPR rd, FPR rs1, FPR rs2, RMode rmode = RMode::DYN) noexcept; + void FEQ_D(GPR rd, FPR rs1, FPR rs2) noexcept; + void FLE_D(GPR rd, FPR rs1, FPR rs2) noexcept; + void FLT_D(GPR rd, FPR rs1, FPR rs2) noexcept; + void FLD(FPR rd, int32_t offset, GPR rs) noexcept; + void FMADD_D(FPR rd, FPR rs1, FPR rs2, FPR rs3, RMode rmode = RMode::DYN) noexcept; + void FMAX_D(FPR rd, FPR rs1, FPR rs2) noexcept; + void FMIN_D(FPR rd, FPR rs1, FPR rs2) noexcept; + void FMSUB_D(FPR rd, FPR rs1, FPR rs2, FPR rs3, RMode rmode = RMode::DYN) noexcept; + void FMUL_D(FPR rd, FPR rs1, FPR rs2, RMode rmode = RMode::DYN) noexcept; + void FNMADD_D(FPR rd, FPR rs1, FPR rs2, FPR rs3, RMode rmode = RMode::DYN) noexcept; + void FNMSUB_D(FPR rd, FPR rs1, FPR rs2, FPR rs3, RMode rmode = RMode::DYN) noexcept; + void FSGNJ_D(FPR rd, FPR rs1, FPR rs2) noexcept; + void FSGNJN_D(FPR rd, FPR rs1, FPR rs2) noexcept; + void FSGNJX_D(FPR rd, FPR rs1, FPR rs2) noexcept; + void FSQRT_D(FPR rd, FPR rs1, RMode rmode = RMode::DYN) noexcept; + void FSUB_D(FPR rd, FPR rs1, FPR rs2, RMode rmode = RMode::DYN) noexcept; + void FSD(FPR rs2, int32_t offset, GPR rs1) noexcept; + + void FABS_D(FPR rd, FPR rs) noexcept; + void FMV_D(FPR rd, FPR rs) noexcept; + void FNEG_D(FPR rd, FPR rs) noexcept; + + // RV64D Extension Instructions + + void FCVT_L_D(GPR rd, FPR rs1, RMode rmode = RMode::DYN) noexcept; + void FCVT_LU_D(GPR rd, FPR rs1, RMode rmode = RMode::DYN) noexcept; + void FCVT_D_L(FPR rd, GPR rs1, RMode rmode = RMode::DYN) noexcept; + void FCVT_D_LU(FPR rd, GPR rs1, RMode rmode = RMode::DYN) noexcept; + void FMV_D_X(FPR rd, GPR rs1) noexcept; + void FMV_X_D(GPR rd, FPR rs1) noexcept; + + // RV32Q Extension Instructions + + void FADD_Q(FPR rd, FPR rs1, FPR rs2, RMode rmode = RMode::DYN) noexcept; + void FCLASS_Q(GPR rd, FPR rs1) noexcept; + void FCVT_Q_W(FPR rd, GPR rs1, RMode rmode = RMode::DYN) noexcept; + void FCVT_Q_WU(FPR rd, GPR rs1, RMode rmode = RMode::DYN) noexcept; + void FCVT_W_Q(GPR rd, FPR rs1, RMode rmode = RMode::DYN) noexcept; + void FCVT_WU_Q(GPR rd, FPR rs1, RMode rmode = RMode::DYN) noexcept; + void FCVT_Q_D(FPR rd, FPR rs1, RMode rmode = RMode::DYN) noexcept; + void FCVT_D_Q(FPR rd, FPR rs1, RMode rmode = RMode::DYN) noexcept; + void FCVT_Q_S(FPR rd, FPR rs1, RMode rmode = RMode::DYN) noexcept; + void FCVT_S_Q(FPR rd, FPR rs1, RMode rmode = RMode::DYN) noexcept; + void FDIV_Q(FPR rd, FPR rs1, FPR rs2, RMode rmode = RMode::DYN) noexcept; + void FEQ_Q(GPR rd, FPR rs1, FPR rs2) noexcept; + void FLE_Q(GPR rd, FPR rs1, FPR rs2) noexcept; + void FLT_Q(GPR rd, FPR rs1, FPR rs2) noexcept; + void FLQ(FPR rd, int32_t offset, GPR rs) noexcept; + void FMADD_Q(FPR rd, FPR rs1, FPR rs2, FPR rs3, RMode rmode = RMode::DYN) noexcept; + void FMAX_Q(FPR rd, FPR rs1, FPR rs2) noexcept; + void FMIN_Q(FPR rd, FPR rs1, FPR rs2) noexcept; + void FMSUB_Q(FPR rd, FPR rs1, FPR rs2, FPR rs3, RMode rmode = RMode::DYN) noexcept; + void FMUL_Q(FPR rd, FPR rs1, FPR rs2, RMode rmode = RMode::DYN) noexcept; + void FNMADD_Q(FPR rd, FPR rs1, FPR rs2, FPR rs3, RMode rmode = RMode::DYN) noexcept; + void FNMSUB_Q(FPR rd, FPR rs1, FPR rs2, FPR rs3, RMode rmode = RMode::DYN) noexcept; + void FSGNJ_Q(FPR rd, FPR rs1, FPR rs2) noexcept; + void FSGNJN_Q(FPR rd, FPR rs1, FPR rs2) noexcept; + void FSGNJX_Q(FPR rd, FPR rs1, FPR rs2) noexcept; + void FSQRT_Q(FPR rd, FPR rs1, RMode rmode = RMode::DYN) noexcept; + void FSUB_Q(FPR rd, FPR rs1, FPR rs2, RMode rmode = RMode::DYN) noexcept; + void FSQ(FPR rs2, int32_t offset, GPR rs1) noexcept; + + void FABS_Q(FPR rd, FPR rs) noexcept; + void FMV_Q(FPR rd, FPR rs) noexcept; + void FNEG_Q(FPR rd, FPR rs) noexcept; + + // RV64Q Extension Instructions + + void FCVT_L_Q(GPR rd, FPR rs1, RMode rmode = RMode::DYN) noexcept; + void FCVT_LU_Q(GPR rd, FPR rs1, RMode rmode = RMode::DYN) noexcept; + void FCVT_Q_L(FPR rd, GPR rs1, RMode rmode = RMode::DYN) noexcept; + void FCVT_Q_LU(FPR rd, GPR rs1, RMode rmode = RMode::DYN) noexcept; + + // RV32Zfh Extension Instructions + + void FADD_H(FPR rd, FPR rs1, FPR rs2, RMode rmode = RMode::DYN) noexcept; + void FCLASS_H(GPR rd, FPR rs1) noexcept; + void FCVT_D_H(FPR rd, FPR rs1, RMode rmode = RMode::DYN) noexcept; + void FCVT_H_D(FPR rd, FPR rs1, RMode rmode = RMode::DYN) noexcept; + void FCVT_H_Q(FPR rd, FPR rs1, RMode rmode = RMode::DYN) noexcept; + void FCVT_H_S(FPR rd, FPR rs1, RMode rmode = RMode::DYN) noexcept; + void FCVT_H_W(FPR rd, GPR rs1, RMode rmode = RMode::DYN) noexcept; + void FCVT_H_WU(FPR rd, GPR rs1, RMode rmode = RMode::DYN) noexcept; + void FCVT_Q_H(FPR rd, FPR rs1, RMode rmode = RMode::DYN) noexcept; + void FCVT_S_H(FPR rd, FPR rs1, RMode rmode = RMode::DYN) noexcept; + void FCVT_W_H(GPR rd, FPR rs1, RMode rmode = RMode::DYN) noexcept; + void FCVT_WU_H(GPR rd, FPR rs1, RMode rmode = RMode::DYN) noexcept; + void FDIV_H(FPR rd, FPR rs1, FPR rs2, RMode rmode = RMode::DYN) noexcept; + void FEQ_H(GPR rd, FPR rs1, FPR rs2) noexcept; + void FLE_H(GPR rd, FPR rs1, FPR rs2) noexcept; + void FLH(FPR rd, int32_t offset, GPR rs) noexcept; + void FLT_H(GPR rd, FPR rs1, FPR rs2) noexcept; + void FMADD_H(FPR rd, FPR rs1, FPR rs2, FPR rs3, RMode rmode = RMode::DYN) noexcept; + void FMAX_H(FPR rd, FPR rs1, FPR rs2) noexcept; + void FMIN_H(FPR rd, FPR rs1, FPR rs2) noexcept; + void FMSUB_H(FPR rd, FPR rs1, FPR rs2, FPR rs3, RMode rmode = RMode::DYN) noexcept; + void FMUL_H(FPR rd, FPR rs1, FPR rs2, RMode rmode = RMode::DYN) noexcept; + void FMV_H_X(FPR rd, GPR rs1) noexcept; + void FMV_X_H(GPR rd, FPR rs1) noexcept; + void FNMADD_H(FPR rd, FPR rs1, FPR rs2, FPR rs3, RMode rmode = RMode::DYN) noexcept; + void FNMSUB_H(FPR rd, FPR rs1, FPR rs2, FPR rs3, RMode rmode = RMode::DYN) noexcept; + void FSGNJ_H(FPR rd, FPR rs1, FPR rs2) noexcept; + void FSGNJN_H(FPR rd, FPR rs1, FPR rs2) noexcept; + void FSGNJX_H(FPR rd, FPR rs1, FPR rs2) noexcept; + void FSH(FPR rs2, int32_t offset, GPR rs1) noexcept; + void FSQRT_H(FPR rd, FPR rs1, RMode rmode = RMode::DYN) noexcept; + void FSUB_H(FPR rd, FPR rs1, FPR rs2, RMode rmode = RMode::DYN) noexcept; + + // RV64Zfh Extension Instructions + + void FCVT_L_H(GPR rd, FPR rs1, RMode rmode = RMode::DYN) noexcept; + void FCVT_LU_H(GPR rd, FPR rs1, RMode rmode = RMode::DYN) noexcept; + void FCVT_H_L(FPR rd, GPR rs1, RMode rmode = RMode::DYN) noexcept; + void FCVT_H_LU(FPR rd, GPR rs1, RMode rmode = RMode::DYN) noexcept; + + // Zfa Extension Instructions + + void FLI_D(FPR rd, double value) noexcept; + void FLI_H(FPR rd, double value) noexcept; + void FLI_S(FPR rd, double value) noexcept; + + void FMINM_D(FPR rd, FPR rs1, FPR rs2) noexcept; + void FMINM_H(FPR rd, FPR rs1, FPR rs2) noexcept; + void FMINM_Q(FPR rd, FPR rs1, FPR rs2) noexcept; + void FMINM_S(FPR rd, FPR rs1, FPR rs2) noexcept; + + void FMAXM_D(FPR rd, FPR rs1, FPR rs2) noexcept; + void FMAXM_H(FPR rd, FPR rs1, FPR rs2) noexcept; + void FMAXM_Q(FPR rd, FPR rs1, FPR rs2) noexcept; + void FMAXM_S(FPR rd, FPR rs1, FPR rs2) noexcept; + + void FROUND_D(FPR rd, FPR rs1, RMode rmode = RMode::DYN) noexcept; + void FROUND_H(FPR rd, FPR rs1, RMode rmode = RMode::DYN) noexcept; + void FROUND_Q(FPR rd, FPR rs1, RMode rmode = RMode::DYN) noexcept; + void FROUND_S(FPR rd, FPR rs1, RMode rmode = RMode::DYN) noexcept; + + void FROUNDNX_D(FPR rd, FPR rs1, RMode rmode = RMode::DYN) noexcept; + void FROUNDNX_H(FPR rd, FPR rs1, RMode rmode = RMode::DYN) noexcept; + void FROUNDNX_Q(FPR rd, FPR rs1, RMode rmode = RMode::DYN) noexcept; + void FROUNDNX_S(FPR rd, FPR rs1, RMode rmode = RMode::DYN) noexcept; + + void FCVTMOD_W_D(GPR rd, FPR rs1) noexcept; + + void FMVH_X_D(GPR rd, FPR rs1) noexcept; + void FMVH_X_Q(GPR rd, FPR rs1) noexcept; + void FMVP_D_X(FPR rd, GPR rs1, GPR rs2) noexcept; + void FMVP_Q_X(FPR rd, GPR rs1, GPR rs2) noexcept; + + void FLEQ_D(GPR rd, FPR rs1, FPR rs2) noexcept; + void FLTQ_D(GPR rd, FPR rs1, FPR rs2) noexcept; + + void FLEQ_H(GPR rd, FPR rs1, FPR rs2) noexcept; + void FLTQ_H(GPR rd, FPR rs1, FPR rs2) noexcept; + + void FLEQ_Q(GPR rd, FPR rs1, FPR rs2) noexcept; + void FLTQ_Q(GPR rd, FPR rs1, FPR rs2) noexcept; + + void FLEQ_S(GPR rd, FPR rs1, FPR rs2) noexcept; + void FLTQ_S(GPR rd, FPR rs1, FPR rs2) noexcept; + + // Zfbfmin Extension Instructions + + void FCVT_BF16_S(FPR rd, FPR rs, RMode rmode = RMode::DYN) noexcept; + void FCVT_S_BF16(FPR rd, FPR rs, RMode rmode = RMode::DYN) noexcept; + + // RVB Extension Instructions (plus scalar crypto bit operations) + + void ADDUW(GPR rd, GPR rs1, GPR rs2) noexcept; + void ANDN(GPR rd, GPR rs1, GPR rs2) noexcept; + void BCLR(GPR rd, GPR rs1, GPR rs2) noexcept; + void BCLRI(GPR rd, GPR rs, uint32_t bit) noexcept; + void BEXT(GPR rd, GPR rs1, GPR rs2) noexcept; + void BEXTI(GPR rd, GPR rs, uint32_t bit) noexcept; + void BINV(GPR rd, GPR rs1, GPR rs2) noexcept; + void BINVI(GPR rd, GPR rs, uint32_t bit) noexcept; + void BREV8(GPR rd, GPR rs) noexcept; + void BSET(GPR rd, GPR rs1, GPR rs2) noexcept; + void BSETI(GPR rd, GPR rs, uint32_t bit) noexcept; + void CLMUL(GPR rd, GPR rs1, GPR rs2) noexcept; + void CLMULH(GPR rd, GPR rs1, GPR rs2) noexcept; + void CLMULR(GPR rd, GPR rs1, GPR rs2) noexcept; + void CLZ(GPR rd, GPR rs) noexcept; + void CLZW(GPR rd, GPR rs) noexcept; + void CPOP(GPR rd, GPR rs) noexcept; + void CPOPW(GPR rd, GPR rs) noexcept; + void CTZ(GPR rd, GPR rs) noexcept; + void CTZW(GPR rd, GPR rs) noexcept; + void MAX(GPR rd, GPR rs1, GPR rs2) noexcept; + void MAXU(GPR rd, GPR rs1, GPR rs2) noexcept; + void MIN(GPR rd, GPR rs1, GPR rs2) noexcept; + void MINU(GPR rd, GPR rs1, GPR rs2) noexcept; + void ORCB(GPR rd, GPR rs) noexcept; + void ORN(GPR rd, GPR rs1, GPR rs2) noexcept; + void PACK(GPR rd, GPR rs1, GPR rs2) noexcept; + void PACKH(GPR rd, GPR rs1, GPR rs2) noexcept; + void PACKW(GPR rd, GPR rs1, GPR rs2) noexcept; + void REV8(GPR rd, GPR rs) noexcept; + void ROL(GPR rd, GPR rs1, GPR rs2) noexcept; + void ROLW(GPR rd, GPR rs1, GPR rs2) noexcept; + void ROR(GPR rd, GPR rs1, GPR rs2) noexcept; + void RORI(GPR rd, GPR rs, uint32_t rotate_amount) noexcept; + void RORIW(GPR rd, GPR rs, uint32_t rotate_amount) noexcept; + void RORW(GPR rd, GPR rs1, GPR rs2) noexcept; + void SEXTB(GPR rd, GPR rs) noexcept; + void SEXTH(GPR rd, GPR rs) noexcept; + void SH1ADD(GPR rd, GPR rs1, GPR rs2) noexcept; + void SH1ADDUW(GPR rd, GPR rs1, GPR rs2) noexcept; + void SH2ADD(GPR rd, GPR rs1, GPR rs2) noexcept; + void SH2ADDUW(GPR rd, GPR rs1, GPR rs2) noexcept; + void SH3ADD(GPR rd, GPR rs1, GPR rs2) noexcept; + void SH3ADDUW(GPR rd, GPR rs1, GPR rs2) noexcept; + void SLLIUW(GPR rd, GPR rs, uint32_t shift_amount) noexcept; + void UNZIP(GPR rd, GPR rs) noexcept; + void XNOR(GPR rd, GPR rs1, GPR rs2) noexcept; + void XPERM4(GPR rd, GPR rs1, GPR rs2) noexcept; + void XPERM8(GPR rd, GPR rs1, GPR rs2) noexcept; + void ZEXTH(GPR rd, GPR rs) noexcept; + void ZEXTW(GPR rd, GPR rs) noexcept; + void ZIP(GPR rd, GPR rs) noexcept; + + // Scalar Cryptography (RVK) instructions + + void AES32DSI(GPR rd, GPR rs1, GPR rs2, uint32_t bs) noexcept; + void AES32DSMI(GPR rd, GPR rs1, GPR rs2, uint32_t bs) noexcept; + void AES32ESI(GPR rd, GPR rs1, GPR rs2, uint32_t bs) noexcept; + void AES32ESMI(GPR rd, GPR rs1, GPR rs2, uint32_t bs) noexcept; + void AES64DS(GPR rd, GPR rs1, GPR rs2) noexcept; + void AES64DSM(GPR rd, GPR rs1, GPR rs2) noexcept; + void AES64ES(GPR rd, GPR rs1, GPR rs2) noexcept; + void AES64ESM(GPR rd, GPR rs1, GPR rs2) noexcept; + void AES64IM(GPR rd, GPR rs) noexcept; + void AES64KS1I(GPR rd, GPR rs, uint32_t rnum) noexcept; + void AES64KS2(GPR rd, GPR rs1, GPR rs2) noexcept; + void SHA256SIG0(GPR rd, GPR rs) noexcept; + void SHA256SIG1(GPR rd, GPR rs) noexcept; + void SHA256SUM0(GPR rd, GPR rs) noexcept; + void SHA256SUM1(GPR rd, GPR rs) noexcept; + void SHA512SIG0(GPR rd, GPR rs) noexcept; + void SHA512SIG0H(GPR rd, GPR rs1, GPR rs2) noexcept; + void SHA512SIG0L(GPR rd, GPR rs1, GPR rs2) noexcept; + void SHA512SIG1(GPR rd, GPR rs) noexcept; + void SHA512SIG1H(GPR rd, GPR rs1, GPR rs2) noexcept; + void SHA512SIG1L(GPR rd, GPR rs1, GPR rs2) noexcept; + void SHA512SUM0(GPR rd, GPR rs) noexcept; + void SHA512SUM0R(GPR rd, GPR rs1, GPR rs2) noexcept; + void SHA512SUM1(GPR rd, GPR rs) noexcept; + void SHA512SUM1R(GPR rd, GPR rs1, GPR rs2) noexcept; + void SM3P0(GPR rd, GPR rs) noexcept; + void SM3P1(GPR rd, GPR rs) noexcept; + void SM4ED(GPR rd, GPR rs1, GPR rs2, uint32_t bs) noexcept; + void SM4KS(GPR rd, GPR rs1, GPR rs2, uint32_t bs) noexcept; + + // RVC Extension Instructions + + void C_ADD(GPR rd, GPR rs) noexcept; + void C_ADDI(GPR rd, int32_t imm) noexcept; + void C_ADDIW(GPR rd, int32_t imm) noexcept; + void C_ADDI4SPN(GPR rd, uint32_t imm) noexcept; + void C_ADDI16SP(int32_t imm) noexcept; + void C_ADDW(GPR rd, GPR rs) noexcept; + void C_AND(GPR rd, GPR rs) noexcept; + void C_ANDI(GPR rd, uint32_t imm) noexcept; + void C_BEQZ(GPR rs, int32_t offset) noexcept; + void C_BEQZ(GPR rs, Label* label) noexcept; + void C_BNEZ(GPR rs, int32_t offset) noexcept; + void C_BNEZ(GPR rs, Label* label) noexcept; + void C_EBREAK() noexcept; + void C_FLD(FPR rd, uint32_t imm, GPR rs) noexcept; + void C_FLDSP(FPR rd, uint32_t imm) noexcept; + void C_FLW(FPR rd, uint32_t imm, GPR rs) noexcept; + void C_FLWSP(FPR rd, uint32_t imm) noexcept; + void C_FSD(FPR rs2, uint32_t imm, GPR rs1) noexcept; + void C_FSDSP(FPR rs, uint32_t imm) noexcept; + void C_FSW(FPR rs2, uint32_t imm, GPR rs1) noexcept; + void C_FSWSP(FPR rs, uint32_t imm) noexcept; + void C_J(int32_t offset) noexcept; + void C_J(Label* label) noexcept; + void C_JAL(Label* label) noexcept; + void C_JAL(int32_t offset) noexcept; + void C_JALR(GPR rs) noexcept; + void C_JR(GPR rs) noexcept; + void C_LD(GPR rd, uint32_t imm, GPR rs) noexcept; + void C_LDSP(GPR rd, uint32_t imm) noexcept; + void C_LI(GPR rd, int32_t imm) noexcept; + void C_LQ(GPR rd, uint32_t imm, GPR rs) noexcept; + void C_LQSP(GPR rd, uint32_t imm) noexcept; + void C_LUI(GPR rd, uint32_t imm) noexcept; + void C_LW(GPR rd, uint32_t imm, GPR rs) noexcept; + void C_LWSP(GPR rd, uint32_t imm) noexcept; + void C_MV(GPR rd, GPR rs) noexcept; + void C_NOP() noexcept; + void C_OR(GPR rd, GPR rs) noexcept; + void C_SD(GPR rs2, uint32_t imm, GPR rs1) noexcept; + void C_SDSP(GPR rs, uint32_t imm) noexcept; + void C_SLLI(GPR rd, uint32_t shift) noexcept; + void C_SQ(GPR rs2, uint32_t imm, GPR rs1) noexcept; + void C_SQSP(GPR rs, uint32_t imm) noexcept; + void C_SRAI(GPR rd, uint32_t shift) noexcept; + void C_SRLI(GPR rd, uint32_t shift) noexcept; + void C_SUB(GPR rd, GPR rs) noexcept; + void C_SUBW(GPR rd, GPR rs) noexcept; + void C_SW(GPR rs2, uint32_t imm, GPR rs1) noexcept; + void C_SWSP(GPR rs, uint32_t imm) noexcept; + void C_UNDEF() noexcept; + void C_XOR(GPR rd, GPR rs) noexcept; + + // Zc Extension Instructions + + void C_LBU(GPR rd, uint32_t uimm, GPR rs) noexcept; + void C_LH(GPR rd, uint32_t uimm, GPR rs) noexcept; + void C_LHU(GPR rd, uint32_t uimm, GPR rs) noexcept; + void C_SB(GPR rs2, uint32_t uimm, GPR rs1) noexcept; + void C_SH(GPR rs2, uint32_t uimm, GPR rs1) noexcept; + + void C_SEXT_B(GPR rd) noexcept; + void C_SEXT_H(GPR rd) noexcept; + void C_ZEXT_B(GPR rd) noexcept; + void C_ZEXT_H(GPR rd) noexcept; + void C_ZEXT_W(GPR rd) noexcept; + + void C_MUL(GPR rsd, GPR rs2) noexcept; + void C_NOT(GPR rd) noexcept; + + void CM_MVA01S(GPR r1s, GPR r2s) noexcept; + void CM_MVSA01(GPR r1s, GPR r2s) noexcept; + + void CM_POP(PushPopList reg_list, int32_t stack_adj) noexcept; + void CM_POPRET(PushPopList reg_list, int32_t stack_adj) noexcept; + void CM_POPRETZ(PushPopList reg_list, int32_t stack_adj) noexcept; + void CM_PUSH(PushPopList reg_list, int32_t stack_adj) noexcept; + + void CM_JALT(uint32_t index) noexcept; + void CM_JT(uint32_t index) noexcept; + + // Cache Management Operation Extension Instructions + + void CBO_CLEAN(GPR rs) noexcept; + void CBO_FLUSH(GPR rs) noexcept; + void CBO_INVAL(GPR rs) noexcept; + void CBO_ZERO(GPR rs) noexcept; + void PREFETCH_I(GPR rs, int32_t offset = 0) noexcept; + void PREFETCH_R(GPR rs, int32_t offset = 0) noexcept; + void PREFETCH_W(GPR rs, int32_t offset = 0) noexcept; + + // Privileged Instructions + + void HFENCE_GVMA(GPR rs1, GPR rs2) noexcept; + void HFENCE_VVMA(GPR rs1, GPR rs2) noexcept; + void HINVAL_GVMA(GPR rs1, GPR rs2) noexcept; + void HINVAL_VVMA(GPR rs1, GPR rs2) noexcept; + void HLV_B(GPR rd, GPR rs) noexcept; + void HLV_BU(GPR rd, GPR rs) noexcept; + void HLV_D(GPR rd, GPR rs) noexcept; + void HLV_H(GPR rd, GPR rs) noexcept; + void HLV_HU(GPR rd, GPR rs) noexcept; + void HLV_W(GPR rd, GPR rs) noexcept; + void HLV_WU(GPR rd, GPR rs) noexcept; + void HLVX_HU(GPR rd, GPR rs) noexcept; + void HLVX_WU(GPR rd, GPR rs) noexcept; + void HSV_B(GPR rs2, GPR rs1) noexcept; + void HSV_D(GPR rs2, GPR rs1) noexcept; + void HSV_H(GPR rs2, GPR rs1) noexcept; + void HSV_W(GPR rs2, GPR rs1) noexcept; + void MRET() noexcept; + void SFENCE_INVAL_IR() noexcept; + void SFENCE_VMA(GPR rs1, GPR rs2) noexcept; + void SFENCE_W_INVAL() noexcept; + void SINVAL_VMA(GPR rs1, GPR rs2) noexcept; + void SRET() noexcept; + void URET() noexcept; + void WFI() noexcept; + + // Vector Extension Instructions + + // Vector Integer Instructions + + void VAADD(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VAADD(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + + void VAADDU(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VAADDU(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + + void VADC(Vec vd, Vec vs2, Vec vs1) noexcept; + void VADC(Vec vd, Vec vs2, GPR rs1) noexcept; + void VADC(Vec vd, Vec vs2, int32_t simm) noexcept; + + void VADD(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VADD(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + void VADD(Vec vd, Vec vs2, int32_t simm, VecMask mask = VecMask::No) noexcept; + + void VAND(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VAND(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + void VAND(Vec vd, Vec vs2, int32_t simm, VecMask mask = VecMask::No) noexcept; + + void VASUB(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VASUB(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + + void VASUBU(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VASUBU(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + + void VCOMPRESS(Vec vd, Vec vs2, Vec vs1) noexcept; + + void VDIV(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VDIV(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + + void VDIVU(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VDIVU(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + + void VFIRST(GPR rd, Vec vs, VecMask mask = VecMask::No) noexcept; + + void VID(Vec vd, VecMask mask = VecMask::No) noexcept; + + void VIOTA(Vec vd, Vec vs, VecMask mask = VecMask::No) noexcept; + + void VMACC(Vec vd, Vec vs1, Vec vs2, VecMask mask = VecMask::No) noexcept; + void VMACC(Vec vd, GPR rs1, Vec vs2, VecMask mask = VecMask::No) noexcept; + + void VMADC(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VMADC(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + void VMADC(Vec vd, Vec vs2, int32_t simm, VecMask mask = VecMask::No) noexcept; + + void VMADD(Vec vd, Vec vs1, Vec vs2, VecMask mask = VecMask::No) noexcept; + void VMADD(Vec vd, GPR rs1, Vec vs2, VecMask mask = VecMask::No) noexcept; + + void VMAND(Vec vd, Vec vs2, Vec vs1) noexcept; + void VMANDNOT(Vec vd, Vec vs2, Vec vs1) noexcept; + void VMNAND(Vec vd, Vec vs2, Vec vs1) noexcept; + void VMNOR(Vec vd, Vec vs2, Vec vs1) noexcept; + void VMOR(Vec vd, Vec vs2, Vec vs1) noexcept; + void VMORNOT(Vec vd, Vec vs2, Vec vs1) noexcept; + void VMXNOR(Vec vd, Vec vs2, Vec vs1) noexcept; + void VMXOR(Vec vd, Vec vs2, Vec vs1) noexcept; + + void VMAX(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VMAX(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + + void VMAXU(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VMAXU(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + + void VMERGE(Vec vd, Vec vs2, Vec vs1) noexcept; + void VMERGE(Vec vd, Vec vs2, GPR rs1) noexcept; + void VMERGE(Vec vd, Vec vs2, int32_t simm) noexcept; + + void VMIN(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VMIN(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + + void VMINU(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VMINU(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + + void VMSBC(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VMSBC(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + + void VMSBF(Vec vd, Vec vs, VecMask mask = VecMask::No) noexcept; + void VMSIF(Vec vd, Vec vs, VecMask mask = VecMask::No) noexcept; + void VMSOF(Vec vd, Vec vs, VecMask mask = VecMask::No) noexcept; + + void VMSEQ(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VMSEQ(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + void VMSEQ(Vec vd, Vec vs2, int32_t simm, VecMask mask = VecMask::No) noexcept; + + void VMSGT(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + void VMSGT(Vec vd, Vec vs2, int32_t simm, VecMask mask = VecMask::No) noexcept; + + void VMSGTU(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + void VMSGTU(Vec vd, Vec vs2, int32_t simm, VecMask mask = VecMask::No) noexcept; + + void VMSLE(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VMSLE(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + void VMSLE(Vec vd, Vec vs2, int32_t simm, VecMask mask = VecMask::No) noexcept; + + void VMSLEU(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VMSLEU(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + void VMSLEU(Vec vd, Vec vs2, int32_t simm, VecMask mask = VecMask::No) noexcept; + + void VMSLT(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VMSLT(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + + void VMSLTU(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VMSLTU(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + + void VMSNE(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VMSNE(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + void VMSNE(Vec vd, Vec vs2, int32_t simm, VecMask mask = VecMask::No) noexcept; + + void VMUL(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VMUL(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + + void VMULH(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VMULH(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + + void VMULHSU(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VMULHSU(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + + void VMULHU(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VMULHU(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + + void VMV(Vec vd, Vec vs1) noexcept; + void VMV(Vec vd, GPR rs1) noexcept; + void VMV(Vec vd, int32_t simm) noexcept; + + void VMV1R(Vec vd, Vec vs) noexcept; + void VMV2R(Vec vd, Vec vs) noexcept; + void VMV4R(Vec vd, Vec vs) noexcept; + void VMV8R(Vec vd, Vec vs) noexcept; + + void VMV_SX(Vec vd, GPR rs) noexcept; + void VMV_XS(GPR rd, Vec vs) noexcept; + + void VNCLIP(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VNCLIP(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + void VNCLIP(Vec vd, Vec vs2, uint32_t uimm, VecMask mask = VecMask::No) noexcept; + + void VNCLIPU(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VNCLIPU(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + void VNCLIPU(Vec vd, Vec vs2, uint32_t uimm, VecMask mask = VecMask::No) noexcept; + + void VNMSAC(Vec vd, Vec vs1, Vec vs2, VecMask mask = VecMask::No) noexcept; + void VNMSAC(Vec vd, GPR rs1, Vec vs2, VecMask mask = VecMask::No) noexcept; + + void VNMSUB(Vec vd, Vec vs1, Vec vs2, VecMask mask = VecMask::No) noexcept; + void VNMSUB(Vec vd, GPR rs1, Vec vs2, VecMask mask = VecMask::No) noexcept; + + void VNSRA(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VNSRA(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + void VNSRA(Vec vd, Vec vs2, uint32_t uimm, VecMask mask = VecMask::No) noexcept; + + void VNSRL(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VNSRL(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + void VNSRL(Vec vd, Vec vs2, uint32_t uimm, VecMask mask = VecMask::No) noexcept; + + void VOR(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VOR(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + void VOR(Vec vd, Vec vs2, int32_t simm, VecMask mask = VecMask::No) noexcept; + + void VPOPC(GPR rd, Vec vs, VecMask mask = VecMask::No) noexcept; + + void VREDAND(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VREDMAX(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VREDMAXU(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VREDMIN(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VREDMINU(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VREDOR(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VREDSUM(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VREDXOR(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + + void VREM(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VREM(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + + void VREMU(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VREMU(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + + void VRGATHER(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VRGATHER(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + void VRGATHER(Vec vd, Vec vs2, uint32_t uimm, VecMask mask = VecMask::No) noexcept; + + void VRGATHEREI16(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + + void VRSUB(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + void VRSUB(Vec vd, Vec vs2, int32_t simm, VecMask mask = VecMask::No) noexcept; + + void VSADD(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VSADD(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + void VSADD(Vec vd, Vec vs2, int32_t simm, VecMask mask = VecMask::No) noexcept; + + void VSADDU(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VSADDU(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + void VSADDU(Vec vd, Vec vs2, int32_t simm, VecMask mask = VecMask::No) noexcept; + + void VSBC(Vec vd, Vec vs2, Vec vs1) noexcept; + void VSBC(Vec vd, Vec vs2, GPR rs1) noexcept; + + void VSEXTVF2(Vec vd, Vec vs, VecMask mask = VecMask::No) noexcept; + void VSEXTVF4(Vec vd, Vec vs, VecMask mask = VecMask::No) noexcept; + void VSEXTVF8(Vec vd, Vec vs, VecMask mask = VecMask::No) noexcept; + + void VSLIDE1DOWN(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + void VSLIDEDOWN(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + void VSLIDEDOWN(Vec vd, Vec vs2, uint32_t uimm, VecMask mask = VecMask::No) noexcept; + + void VSLIDE1UP(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + void VSLIDEUP(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + void VSLIDEUP(Vec vd, Vec vs2, uint32_t uimm, VecMask mask = VecMask::No) noexcept; + + void VSLL(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VSLL(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + void VSLL(Vec vd, Vec vs2, uint32_t uimm, VecMask mask = VecMask::No) noexcept; + + void VSMUL(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VSMUL(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + + void VSRA(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VSRA(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + void VSRA(Vec vd, Vec vs2, uint32_t uimm, VecMask mask = VecMask::No) noexcept; + + void VSRL(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VSRL(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + void VSRL(Vec vd, Vec vs2, uint32_t uimm, VecMask mask = VecMask::No) noexcept; + + void VSSRA(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VSSRA(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + void VSSRA(Vec vd, Vec vs2, uint32_t uimm, VecMask mask = VecMask::No) noexcept; + + void VSSRL(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VSSRL(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + void VSSRL(Vec vd, Vec vs2, uint32_t uimm, VecMask mask = VecMask::No) noexcept; + + void VSSUB(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VSSUB(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + + void VSSUBU(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VSSUBU(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + + void VSUB(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VSUB(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + + void VWADD(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VWADD(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + + void VWADDW(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VWADDW(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + + void VWADDU(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VWADDU(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + + void VWADDUW(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VWADDUW(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + + void VWMACC(Vec vd, Vec vs1, Vec vs2, VecMask mask = VecMask::No) noexcept; + void VWMACC(Vec vd, GPR rs1, Vec vs2, VecMask mask = VecMask::No) noexcept; + + void VWMACCSU(Vec vd, Vec vs1, Vec vs2, VecMask mask = VecMask::No) noexcept; + void VWMACCSU(Vec vd, GPR rs1, Vec vs2, VecMask mask = VecMask::No) noexcept; + + void VWMACCU(Vec vd, Vec vs1, Vec vs2, VecMask mask = VecMask::No) noexcept; + void VWMACCU(Vec vd, GPR rs1, Vec vs2, VecMask mask = VecMask::No) noexcept; + + void VWMACCUS(Vec vd, GPR rs1, Vec vs2, VecMask mask = VecMask::No) noexcept; + + void VWMUL(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VWMUL(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + + void VWMULSU(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VWMULSU(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + + void VWMULU(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VWMULU(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + + void VWREDSUM(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VWREDSUMU(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + + void VWSUB(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VWSUB(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + + void VWSUBW(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VWSUBW(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + + void VWSUBU(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VWSUBU(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + + void VWSUBUW(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VWSUBUW(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + + void VXOR(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VXOR(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + void VXOR(Vec vd, Vec vs2, int32_t simm, VecMask mask = VecMask::No) noexcept; + + void VZEXTVF2(Vec vd, Vec vs, VecMask mask = VecMask::No) noexcept; + void VZEXTVF4(Vec vd, Vec vs, VecMask mask = VecMask::No) noexcept; + void VZEXTVF8(Vec vd, Vec vs, VecMask mask = VecMask::No) noexcept; + + // Vector Floating-Point Instructions + + void VFADD(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VFADD(Vec vd, Vec vs2, FPR rs1, VecMask mask = VecMask::No) noexcept; + + void VFCLASS(Vec vd, Vec vs, VecMask mask = VecMask::No) noexcept; + + void VFCVT_F_X(Vec vd, Vec vs, VecMask mask = VecMask::No) noexcept; + void VFCVT_F_XU(Vec vd, Vec vs, VecMask mask = VecMask::No) noexcept; + void VFCVT_RTZ_X_F(Vec vd, Vec vs, VecMask mask = VecMask::No) noexcept; + void VFCVT_RTZ_XU_F(Vec vd, Vec vs, VecMask mask = VecMask::No) noexcept; + void VFCVT_X_F(Vec vd, Vec vs, VecMask mask = VecMask::No) noexcept; + void VFCVT_XU_F(Vec vd, Vec vs, VecMask mask = VecMask::No) noexcept; + + void VFNCVT_F_F(Vec vd, Vec vs, VecMask mask = VecMask::No) noexcept; + void VFNCVT_F_X(Vec vd, Vec vs, VecMask mask = VecMask::No) noexcept; + void VFNCVT_F_XU(Vec vd, Vec vs, VecMask mask = VecMask::No) noexcept; + void VFNCVT_ROD_F_F(Vec vd, Vec vs, VecMask mask = VecMask::No) noexcept; + void VFNCVT_RTZ_X_F(Vec vd, Vec vs, VecMask mask = VecMask::No) noexcept; + void VFNCVT_RTZ_XU_F(Vec vd, Vec vs, VecMask mask = VecMask::No) noexcept; + void VFNCVT_X_F(Vec vd, Vec vs, VecMask mask = VecMask::No) noexcept; + void VFNCVT_XU_F(Vec vd, Vec vs, VecMask mask = VecMask::No) noexcept; + + void VFWCVT_F_F(Vec vd, Vec vs, VecMask mask = VecMask::No) noexcept; + void VFWCVT_F_X(Vec vd, Vec vs, VecMask mask = VecMask::No) noexcept; + void VFWCVT_F_XU(Vec vd, Vec vs, VecMask mask = VecMask::No) noexcept; + void VFWCVT_RTZ_X_F(Vec vd, Vec vs, VecMask mask = VecMask::No) noexcept; + void VFWCVT_RTZ_XU_F(Vec vd, Vec vs, VecMask mask = VecMask::No) noexcept; + void VFWCVT_X_F(Vec vd, Vec vs, VecMask mask = VecMask::No) noexcept; + void VFWCVT_XU_F(Vec vd, Vec vs, VecMask mask = VecMask::No) noexcept; + + void VFDIV(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VFDIV(Vec vd, Vec vs2, FPR rs1, VecMask mask = VecMask::No) noexcept; + void VFRDIV(Vec vd, Vec vs2, FPR rs1, VecMask mask = VecMask::No) noexcept; + + void VFREDMAX(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VFREDMIN(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + + void VFREDSUM(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VFREDOSUM(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + + void VFMACC(Vec vd, Vec vs1, Vec vs2, VecMask mask = VecMask::No) noexcept; + void VFMACC(Vec vd, FPR rs1, Vec vs2, VecMask mask = VecMask::No) noexcept; + + void VFMADD(Vec vd, Vec vs1, Vec vs2, VecMask mask = VecMask::No) noexcept; + void VFMADD(Vec vd, FPR rs1, Vec vs2, VecMask mask = VecMask::No) noexcept; + + void VFMAX(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VFMAX(Vec vd, Vec vs2, FPR rs1, VecMask mask = VecMask::No) noexcept; + + void VFMERGE(Vec vd, Vec vs2, FPR rs1) noexcept; + + void VFMIN(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VFMIN(Vec vd, Vec vs2, FPR rs1, VecMask mask = VecMask::No) noexcept; + + void VFMSAC(Vec vd, Vec vs1, Vec vs2, VecMask mask = VecMask::No) noexcept; + void VFMSAC(Vec vd, FPR rs1, Vec vs2, VecMask mask = VecMask::No) noexcept; + + void VFMSUB(Vec vd, Vec vs1, Vec vs2, VecMask mask = VecMask::No) noexcept; + void VFMSUB(Vec vd, FPR rs1, Vec vs2, VecMask mask = VecMask::No) noexcept; + + void VFMUL(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VFMUL(Vec vd, Vec vs2, FPR rs1, VecMask mask = VecMask::No) noexcept; + + void VFMV(Vec vd, FPR rs) noexcept; + void VFMV_FS(FPR rd, Vec vs) noexcept; + void VFMV_SF(Vec vd, FPR rs) noexcept; + + void VFNMACC(Vec vd, Vec vs1, Vec vs2, VecMask mask = VecMask::No) noexcept; + void VFNMACC(Vec vd, FPR rs1, Vec vs2, VecMask mask = VecMask::No) noexcept; + + void VFNMADD(Vec vd, Vec vs1, Vec vs2, VecMask mask = VecMask::No) noexcept; + void VFNMADD(Vec vd, FPR rs1, Vec vs2, VecMask mask = VecMask::No) noexcept; + + void VFNMSAC(Vec vd, Vec vs1, Vec vs2, VecMask mask = VecMask::No) noexcept; + void VFNMSAC(Vec vd, FPR rs1, Vec vs2, VecMask mask = VecMask::No) noexcept; + + void VFNMSUB(Vec vd, Vec vs1, Vec vs2, VecMask mask = VecMask::No) noexcept; + void VFNMSUB(Vec vd, FPR rs1, Vec vs2, VecMask mask = VecMask::No) noexcept; + + void VFREC7(Vec vd, Vec vs, VecMask mask = VecMask::No) noexcept; + + void VFSGNJ(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VFSGNJ(Vec vd, Vec vs2, FPR rs1, VecMask mask = VecMask::No) noexcept; + + void VFSGNJN(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VFSGNJN(Vec vd, Vec vs2, FPR rs1, VecMask mask = VecMask::No) noexcept; + + void VFSGNJX(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VFSGNJX(Vec vd, Vec vs2, FPR rs1, VecMask mask = VecMask::No) noexcept; + + void VFSQRT(Vec vd, Vec vs, VecMask mask = VecMask::No) noexcept; + void VFRSQRT7(Vec vd, Vec vs, VecMask mask = VecMask::No) noexcept; + + void VFSLIDE1DOWN(Vec vd, Vec vs2, FPR rs1, VecMask mask = VecMask::No) noexcept; + void VFSLIDE1UP(Vec vd, Vec vs2, FPR rs1, VecMask mask = VecMask::No) noexcept; + + void VFSUB(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VFSUB(Vec vd, Vec vs2, FPR rs1, VecMask mask = VecMask::No) noexcept; + void VFRSUB(Vec vd, Vec vs2, FPR rs1, VecMask mask = VecMask::No) noexcept; + + void VFWADD(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VFWADD(Vec vd, Vec vs2, FPR rs1, VecMask mask = VecMask::No) noexcept; + + void VFWADDW(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VFWADDW(Vec vd, Vec vs2, FPR rs1, VecMask mask = VecMask::No) noexcept; + + void VFWMACC(Vec vd, Vec vs1, Vec vs2, VecMask mask = VecMask::No) noexcept; + void VFWMACC(Vec vd, FPR rs1, Vec vs2, VecMask mask = VecMask::No) noexcept; + + void VFWMUL(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VFWMUL(Vec vd, Vec vs2, FPR rs1, VecMask mask = VecMask::No) noexcept; + + void VFWNMACC(Vec vd, Vec vs1, Vec vs2, VecMask mask = VecMask::No) noexcept; + void VFWNMACC(Vec vd, FPR rs1, Vec vs2, VecMask mask = VecMask::No) noexcept; + + void VFWNMSAC(Vec vd, Vec vs1, Vec vs2, VecMask mask = VecMask::No) noexcept; + void VFWNMSAC(Vec vd, FPR rs1, Vec vs2, VecMask mask = VecMask::No) noexcept; + + void VFWREDSUM(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VFWREDOSUM(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + + void VFWMSAC(Vec vd, Vec vs1, Vec vs2, VecMask mask = VecMask::No) noexcept; + void VFWMSAC(Vec vd, FPR rs1, Vec vs2, VecMask mask = VecMask::No) noexcept; + + void VFWSUB(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VFWSUB(Vec vd, Vec vs2, FPR rs1, VecMask mask = VecMask::No) noexcept; + + void VFWSUBW(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VFWSUBW(Vec vd, Vec vs2, FPR rs1, VecMask mask = VecMask::No) noexcept; + + void VMFEQ(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VMFEQ(Vec vd, Vec vs2, FPR rs1, VecMask mask = VecMask::No) noexcept; + + void VMFGE(Vec vd, Vec vs2, FPR rs1, VecMask mask = VecMask::No) noexcept; + void VMFGT(Vec vd, Vec vs2, FPR rs1, VecMask mask = VecMask::No) noexcept; + + void VMFLE(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VMFLE(Vec vd, Vec vs2, FPR rs1, VecMask mask = VecMask::No) noexcept; + + void VMFLT(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VMFLT(Vec vd, Vec vs2, FPR rs1, VecMask mask = VecMask::No) noexcept; + + void VMFNE(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VMFNE(Vec vd, Vec vs2, FPR rs1, VecMask mask = VecMask::No) noexcept; + + // Vector Load/Store Instructions + + void VLE8(Vec vd, GPR rs, VecMask mask = VecMask::No) noexcept; + void VLE16(Vec vd, GPR rs, VecMask mask = VecMask::No) noexcept; + void VLE32(Vec vd, GPR rs, VecMask mask = VecMask::No) noexcept; + void VLE64(Vec vd, GPR rs, VecMask mask = VecMask::No) noexcept; + void VLM(Vec vd, GPR rs) noexcept; + + void VLSE8(Vec vd, GPR rs1, GPR rs2, VecMask mask = VecMask::No) noexcept; + void VLSE16(Vec vd, GPR rs1, GPR rs2, VecMask mask = VecMask::No) noexcept; + void VLSE32(Vec vd, GPR rs1, GPR rs2, VecMask mask = VecMask::No) noexcept; + void VLSE64(Vec vd, GPR rs1, GPR rs2, VecMask mask = VecMask::No) noexcept; + + void VLOXEI8(Vec vd, GPR rs, Vec vs, VecMask mask = VecMask::No) noexcept; + void VLOXEI16(Vec vd, GPR rs, Vec vs, VecMask mask = VecMask::No) noexcept; + void VLOXEI32(Vec vd, GPR rs, Vec vs, VecMask mask = VecMask::No) noexcept; + void VLOXEI64(Vec vd, GPR rs, Vec vs, VecMask mask = VecMask::No) noexcept; + + void VLUXEI8(Vec vd, GPR rs, Vec vs, VecMask mask = VecMask::No) noexcept; + void VLUXEI16(Vec vd, GPR rs, Vec vs, VecMask mask = VecMask::No) noexcept; + void VLUXEI32(Vec vd, GPR rs, Vec vs, VecMask mask = VecMask::No) noexcept; + void VLUXEI64(Vec vd, GPR rs, Vec vs, VecMask mask = VecMask::No) noexcept; + + void VLE8FF(Vec vd, GPR rs, VecMask mask = VecMask::No) noexcept; + void VLE16FF(Vec vd, GPR rs, VecMask mask = VecMask::No) noexcept; + void VLE32FF(Vec vd, GPR rs, VecMask mask = VecMask::No) noexcept; + void VLE64FF(Vec vd, GPR rs, VecMask mask = VecMask::No) noexcept; + + void VLSEGE8(uint32_t num_segments, Vec vd, GPR rs, VecMask mask = VecMask::No) noexcept; + void VLSEGE16(uint32_t num_segments, Vec vd, GPR rs, VecMask mask = VecMask::No) noexcept; + void VLSEGE32(uint32_t num_segments, Vec vd, GPR rs, VecMask mask = VecMask::No) noexcept; + void VLSEGE64(uint32_t num_segments, Vec vd, GPR rs, VecMask mask = VecMask::No) noexcept; + + void VLSSEGE8(uint32_t num_segments, Vec vd, GPR rs1, GPR rs2, VecMask mask = VecMask::No) noexcept; + void VLSSEGE16(uint32_t num_segments, Vec vd, GPR rs1, GPR rs2, VecMask mask = VecMask::No) noexcept; + void VLSSEGE32(uint32_t num_segments, Vec vd, GPR rs1, GPR rs2, VecMask mask = VecMask::No) noexcept; + void VLSSEGE64(uint32_t num_segments, Vec vd, GPR rs1, GPR rs2, VecMask mask = VecMask::No) noexcept; + + void VLOXSEGEI8(uint32_t num_segments, Vec vd, GPR rs, Vec vs, VecMask mask = VecMask::No) noexcept; + void VLOXSEGEI16(uint32_t num_segments, Vec vd, GPR rs, Vec vs, VecMask mask = VecMask::No) noexcept; + void VLOXSEGEI32(uint32_t num_segments, Vec vd, GPR rs, Vec vs, VecMask mask = VecMask::No) noexcept; + void VLOXSEGEI64(uint32_t num_segments, Vec vd, GPR rs, Vec vs, VecMask mask = VecMask::No) noexcept; + + void VLUXSEGEI8(uint32_t num_segments, Vec vd, GPR rs, Vec vs, VecMask mask = VecMask::No) noexcept; + void VLUXSEGEI16(uint32_t num_segments, Vec vd, GPR rs, Vec vs, VecMask mask = VecMask::No) noexcept; + void VLUXSEGEI32(uint32_t num_segments, Vec vd, GPR rs, Vec vs, VecMask mask = VecMask::No) noexcept; + void VLUXSEGEI64(uint32_t num_segments, Vec vd, GPR rs, Vec vs, VecMask mask = VecMask::No) noexcept; + + void VLRE8(uint32_t num_registers, Vec vd, GPR rs) noexcept; + void VL1RE8(Vec vd, GPR rs) noexcept; + void VL2RE8(Vec vd, GPR rs) noexcept; + void VL4RE8(Vec vd, GPR rs) noexcept; + void VL8RE8(Vec vd, GPR rs) noexcept; + + void VLRE16(uint32_t num_registers, Vec vd, GPR rs) noexcept; + void VL1RE16(Vec vd, GPR rs) noexcept; + void VL2RE16(Vec vd, GPR rs) noexcept; + void VL4RE16(Vec vd, GPR rs) noexcept; + void VL8RE16(Vec vd, GPR rs) noexcept; + + void VLRE32(uint32_t num_registers, Vec vd, GPR rs) noexcept; + void VL1RE32(Vec vd, GPR rs) noexcept; + void VL2RE32(Vec vd, GPR rs) noexcept; + void VL4RE32(Vec vd, GPR rs) noexcept; + void VL8RE32(Vec vd, GPR rs) noexcept; + + void VLRE64(uint32_t num_registers, Vec vd, GPR rs) noexcept; + void VL1RE64(Vec vd, GPR rs) noexcept; + void VL2RE64(Vec vd, GPR rs) noexcept; + void VL4RE64(Vec vd, GPR rs) noexcept; + void VL8RE64(Vec vd, GPR rs) noexcept; + + void VSE8(Vec vs, GPR rs, VecMask mask = VecMask::No) noexcept; + void VSE16(Vec vs, GPR rs, VecMask mask = VecMask::No) noexcept; + void VSE32(Vec vs, GPR rs, VecMask mask = VecMask::No) noexcept; + void VSE64(Vec vs, GPR rs, VecMask mask = VecMask::No) noexcept; + void VSM(Vec vs, GPR rs) noexcept; + + void VSSE8(Vec vs, GPR rs1, GPR rs2, VecMask mask = VecMask::No) noexcept; + void VSSE16(Vec vs, GPR rs1, GPR rs2, VecMask mask = VecMask::No) noexcept; + void VSSE32(Vec vs, GPR rs1, GPR rs2, VecMask mask = VecMask::No) noexcept; + void VSSE64(Vec vs, GPR rs1, GPR rs2, VecMask mask = VecMask::No) noexcept; + + void VSOXEI8(Vec vd, GPR rs, Vec vs, VecMask mask = VecMask::No) noexcept; + void VSOXEI16(Vec vd, GPR rs, Vec vs, VecMask mask = VecMask::No) noexcept; + void VSOXEI32(Vec vd, GPR rs, Vec vs, VecMask mask = VecMask::No) noexcept; + void VSOXEI64(Vec vd, GPR rs, Vec vs, VecMask mask = VecMask::No) noexcept; + + void VSUXEI8(Vec vd, GPR rs, Vec vs, VecMask mask = VecMask::No) noexcept; + void VSUXEI16(Vec vd, GPR rs, Vec vs, VecMask mask = VecMask::No) noexcept; + void VSUXEI32(Vec vd, GPR rs, Vec vs, VecMask mask = VecMask::No) noexcept; + void VSUXEI64(Vec vd, GPR rs, Vec vs, VecMask mask = VecMask::No) noexcept; + + void VSSEGE8(uint32_t num_segments, Vec vs, GPR rs, VecMask mask = VecMask::No) noexcept; + void VSSEGE16(uint32_t num_segments, Vec vs, GPR rs, VecMask mask = VecMask::No) noexcept; + void VSSEGE32(uint32_t num_segments, Vec vs, GPR rs, VecMask mask = VecMask::No) noexcept; + void VSSEGE64(uint32_t num_segments, Vec vs, GPR rs, VecMask mask = VecMask::No) noexcept; + + void VSSSEGE8(uint32_t num_segments, Vec vs, GPR rs1, GPR rs2, VecMask mask = VecMask::No) noexcept; + void VSSSEGE16(uint32_t num_segments, Vec vs, GPR rs1, GPR rs2, VecMask mask = VecMask::No) noexcept; + void VSSSEGE32(uint32_t num_segments, Vec vs, GPR rs1, GPR rs2, VecMask mask = VecMask::No) noexcept; + void VSSSEGE64(uint32_t num_segments, Vec vs, GPR rs1, GPR rs2, VecMask mask = VecMask::No) noexcept; + + void VSOXSEGEI8(uint32_t num_segments, Vec vd, GPR rs, Vec vs, VecMask mask = VecMask::No) noexcept; + void VSOXSEGEI16(uint32_t num_segments, Vec vd, GPR rs, Vec vs, VecMask mask = VecMask::No) noexcept; + void VSOXSEGEI32(uint32_t num_segments, Vec vd, GPR rs, Vec vs, VecMask mask = VecMask::No) noexcept; + void VSOXSEGEI64(uint32_t num_segments, Vec vd, GPR rs, Vec vs, VecMask mask = VecMask::No) noexcept; + + void VSUXSEGEI8(uint32_t num_segments, Vec vd, GPR rs, Vec vs, VecMask mask = VecMask::No) noexcept; + void VSUXSEGEI16(uint32_t num_segments, Vec vd, GPR rs, Vec vs, VecMask mask = VecMask::No) noexcept; + void VSUXSEGEI32(uint32_t num_segments, Vec vd, GPR rs, Vec vs, VecMask mask = VecMask::No) noexcept; + void VSUXSEGEI64(uint32_t num_segments, Vec vd, GPR rs, Vec vs, VecMask mask = VecMask::No) noexcept; + + void VSR(uint32_t num_registers, Vec vs, GPR rs) noexcept; + void VS1R(Vec vs, GPR rs) noexcept; + void VS2R(Vec vs, GPR rs) noexcept; + void VS4R(Vec vs, GPR rs) noexcept; + void VS8R(Vec vs, GPR rs) noexcept; + + // Vector Configuration Setting Instructions + + void VSETIVLI(GPR rd, uint32_t imm, SEW sew, LMUL lmul = LMUL::M1, VTA vta = VTA::No, VMA vma = VMA::No) noexcept; + void VSETVL(GPR rd, GPR rs1, GPR rs2) noexcept; + void VSETVLI(GPR rd, GPR rs, SEW sew, LMUL lmul = LMUL::M1, VTA vta = VTA::No, VMA vma = VMA::No) noexcept; + + // Vector Cryptography Instructions + + void VANDN(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VANDN(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + + void VBREV(Vec vd, Vec vs2, VecMask mask = VecMask::No) noexcept; + void VBREV8(Vec vd, Vec vs2, VecMask mask = VecMask::No) noexcept; + void VREV8(Vec vd, Vec vs2, VecMask mask = VecMask::No) noexcept; + + void VCLZ(Vec vd, Vec vs2, VecMask mask = VecMask::No) noexcept; + void VCTZ(Vec vd, Vec vs2, VecMask mask = VecMask::No) noexcept; + void VCPOP(Vec vd, Vec vs2, VecMask mask = VecMask::No) noexcept; + + void VROL(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VROL(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + + void VROR(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VROR(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + void VROR(Vec vd, Vec vs2, uint32_t uimm, VecMask mask = VecMask::No) noexcept; + + void VWSLL(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VWSLL(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + void VWSLL(Vec vd, Vec vs2, uint32_t uimm, VecMask mask = VecMask::No) noexcept; + + void VCLMUL(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VCLMUL(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + + void VCLMULH(Vec vd, Vec vs2, Vec vs1, VecMask mask = VecMask::No) noexcept; + void VCLMULH(Vec vd, Vec vs2, GPR rs1, VecMask mask = VecMask::No) noexcept; + + void VGHSH(Vec vd, Vec vs2, Vec vs1) noexcept; + void VGMUL(Vec vd, Vec vs2) noexcept; + + void VAESDF_VV(Vec vd, Vec vs2) noexcept; + void VAESDF_VS(Vec vd, Vec vs2) noexcept; + + void VAESDM_VV(Vec vd, Vec vs2) noexcept; + void VAESDM_VS(Vec vd, Vec vs2) noexcept; + + void VAESEF_VV(Vec vd, Vec vs2) noexcept; + void VAESEF_VS(Vec vd, Vec vs2) noexcept; + + void VAESEM_VV(Vec vd, Vec vs2) noexcept; + void VAESEM_VS(Vec vd, Vec vs2) noexcept; + + void VAESKF1(Vec vd, Vec vs2, uint32_t uimm) noexcept; + void VAESKF2(Vec vd, Vec vs2, uint32_t uimm) noexcept; + + void VAESZ(Vec vd, Vec vs2) noexcept; + + void VSHA2MS(Vec vd, Vec vs2, Vec vs1) noexcept; + void VSHA2CH(Vec vd, Vec vs2, Vec vs1) noexcept; + void VSHA2CL(Vec vd, Vec vs2, Vec vs1) noexcept; + + void VSM4K(Vec vd, Vec vs2, uint32_t uimm) noexcept; + void VSM4R_VV(Vec vd, Vec vs2) noexcept; + void VSM4R_VS(Vec vd, Vec vs2) noexcept; + + void VSM3C(Vec vd, Vec vs2, uint32_t uimm) noexcept; + void VSM3ME(Vec vd, Vec vs2, Vec vs1) noexcept; + + // Zvfbfmin, Zvfbfwma Extension Instructions + + void VFNCVTBF16_F_F_W(Vec vd, Vec vs, VecMask mask = VecMask::No) noexcept; + void VFWCVTBF16_F_F_V(Vec vd, Vec vs, VecMask mask = VecMask::No) noexcept; + + void VFWMACCBF16(Vec vd, FPR rs1, Vec vs2, VecMask mask = VecMask::No) noexcept; + void VFWMACCBF16(Vec vd, Vec vs1, Vec vs2, VecMask mask = VecMask::No) noexcept; + +private: + // Binds a label to a given offset. + void BindToOffset(Label* label, Label::LocationOffset offset); + + // Links the given label and returns the offset to it. + ptrdiff_t LinkAndGetOffset(Label* label); + + // Resolves all label offsets and patches any necessary + // branch offsets into the branch instructions that + // requires them. + void ResolveLabelOffsets(Label* label); + + CodeBuffer m_buffer; + ArchFeature m_features = ArchFeature::RV64; +}; + +} // namespace biscuit diff --git a/externals/biscuit/include/biscuit/assert.hpp b/externals/biscuit/include/biscuit/assert.hpp new file mode 100644 index 00000000..f6c5fa97 --- /dev/null +++ b/externals/biscuit/include/biscuit/assert.hpp @@ -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) diff --git a/externals/biscuit/include/biscuit/code_buffer.hpp b/externals/biscuit/include/biscuit/code_buffer.hpp new file mode 100644 index 00000000..46314e48 --- /dev/null +++ b/externals/biscuit/include/biscuit/code_buffer.hpp @@ -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 diff --git a/externals/biscuit/include/biscuit/cpuinfo.hpp b/externals/biscuit/include/biscuit/cpuinfo.hpp new file mode 100644 index 00000000..b5efa739 --- /dev/null +++ b/externals/biscuit/include/biscuit/cpuinfo.hpp @@ -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 diff --git a/externals/biscuit/include/biscuit/csr.hpp b/externals/biscuit/include/biscuit/csr.hpp new file mode 100644 index 00000000..e31243ea --- /dev/null +++ b/externals/biscuit/include/biscuit/csr.hpp @@ -0,0 +1,443 @@ +#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) + + JVT = 0x017, // Table jump base vector and control register + + Cycle = 0xC00, // Cycle counter for RDCYCLE instruction. + Time = 0xC01, // Timer for RDTIME instruction. + InstRet = 0xC02, // Instructions retired counter for RDINSTRET instruction. + HPMCounter3 = 0xC03, // Performance-monitoring counter. + HPMCounter4 = 0xC04, // Performance-monitoring counter. + HPMCounter5 = 0xC05, // Performance-monitoring counter. + HPMCounter6 = 0xC06, // Performance-monitoring counter. + HPMCounter7 = 0xC07, // Performance-monitoring counter. + HPMCounter8 = 0xC08, // Performance-monitoring counter. + HPMCounter9 = 0xC09, // Performance-monitoring counter. + HPMCounter10 = 0xC0A, // Performance-monitoring counter. + HPMCounter11 = 0xC0B, // Performance-monitoring counter. + HPMCounter12 = 0xC0C, // Performance-monitoring counter. + HPMCounter13 = 0xC0D, // Performance-monitoring counter. + HPMCounter14 = 0xC0E, // Performance-monitoring counter. + HPMCounter15 = 0xC0F, // Performance-monitoring counter. + HPMCounter16 = 0xC10, // Performance-monitoring counter. + HPMCounter17 = 0xC11, // Performance-monitoring counter. + HPMCounter18 = 0xC12, // Performance-monitoring counter. + HPMCounter19 = 0xC13, // Performance-monitoring counter. + HPMCounter20 = 0xC14, // Performance-monitoring counter. + HPMCounter21 = 0xC15, // Performance-monitoring counter. + HPMCounter22 = 0xC16, // Performance-monitoring counter. + HPMCounter23 = 0xC17, // Performance-monitoring counter. + HPMCounter24 = 0xC18, // Performance-monitoring counter. + HPMCounter25 = 0xC19, // Performance-monitoring counter. + HPMCounter26 = 0xC1A, // Performance-monitoring counter. + HPMCounter27 = 0xC1B, // Performance-monitoring counter. + HPMCounter28 = 0xC1C, // Performance-monitoring counter. + HPMCounter29 = 0xC1D, // Performance-monitoring counter. + HPMCounter30 = 0xC1E, // Performance-monitoring counter. + HPMCounter31 = 0xC1F, // Performance-monitoring counter. + CycleH = 0xC80, // Upper 32 bits of cycle, RV32I only. + TimeH = 0xC81, // Upper 32 bits of time, RV32I only. + InstRetH = 0xC82, // Upper 32 bits of instret, RV32I only. + HPMCounter3H = 0xC83, // Upper 32 bits of HPMCounter3, RV32I only. + HPMCounter4H = 0xC84, // Upper 32 bits of HPMCounter4, RV32I only. + HPMCounter5H = 0xC85, // Upper 32 bits of HPMCounter5, RV32I only. + HPMCounter6H = 0xC86, // Upper 32 bits of HPMCounter6, RV32I only. + HPMCounter7H = 0xC87, // Upper 32 bits of HPMCounter7, RV32I only. + HPMCounter8H = 0xC88, // Upper 32 bits of HPMCounter8, RV32I only. + HPMCounter9H = 0xC89, // Upper 32 bits of HPMCounter9, RV32I only. + HPMCounter10H = 0xC8A, // Upper 32 bits of HPMCounter10, RV32I only. + HPMCounter11H = 0xC8B, // Upper 32 bits of HPMCounter11, RV32I only. + HPMCounter12H = 0xC8C, // Upper 32 bits of HPMCounter12, RV32I only. + HPMCounter13H = 0xC8D, // Upper 32 bits of HPMCounter13, RV32I only. + HPMCounter14H = 0xC8E, // Upper 32 bits of HPMCounter14, RV32I only. + HPMCounter15H = 0xC8F, // Upper 32 bits of HPMCounter15, RV32I only. + HPMCounter16H = 0xC90, // Upper 32 bits of HPMCounter16, RV32I only. + HPMCounter17H = 0xC91, // Upper 32 bits of HPMCounter17, RV32I only. + HPMCounter18H = 0xC92, // Upper 32 bits of HPMCounter18, RV32I only. + HPMCounter19H = 0xC93, // Upper 32 bits of HPMCounter19, RV32I only. + HPMCounter20H = 0xC94, // Upper 32 bits of HPMCounter20, RV32I only. + HPMCounter21H = 0xC95, // Upper 32 bits of HPMCounter21, RV32I only. + HPMCounter22H = 0xC96, // Upper 32 bits of HPMCounter22, RV32I only. + HPMCounter23H = 0xC97, // Upper 32 bits of HPMCounter23, RV32I only. + HPMCounter24H = 0xC98, // Upper 32 bits of HPMCounter24, RV32I only. + HPMCounter25H = 0xC99, // Upper 32 bits of HPMCounter25, RV32I only. + HPMCounter26H = 0xC9A, // Upper 32 bits of HPMCounter26, RV32I only. + HPMCounter27H = 0xC9B, // Upper 32 bits of HPMCounter27, RV32I only. + HPMCounter28H = 0xC9C, // Upper 32 bits of HPMCounter28, RV32I only. + HPMCounter29H = 0xC9D, // Upper 32 bits of HPMCounter29, RV32I only. + HPMCounter30H = 0xC9E, // Upper 32 bits of HPMCounter30, RV32I only. + HPMCounter31H = 0xC9F, // Upper 32 bits of HPMCounter31, RV32I only. + + // Supervisor-level CSRs + + SStatus = 0x100, // Supervisor status register + SEDeleg = 0x102, // Supervisor exception delegation register + SIDeleg = 0x103, // Supervisor interrupt delegation register + SIE = 0x104, // Supervisor interrupt-enable register + STVec = 0x105, // Supervisor trap handler base address + SCounterEn = 0x106, // Supervisor counter enable + + SEnvCfg = 0x10A, // Supervisor environment configuration register + + SScratch = 0x140, // Scratch register for supervisor trap handlers + SEPC = 0x141, // Supervisor exception program counter + SCause = 0x142, // Supervisor trap cause + STVal = 0x143, // Supervisor bad address or instruction + SIP = 0x144, // Supervisor interrupt pending. + + SISelect = 0x150, // Supervisor indirect register select + SIReg = 0x151, // Supervisor indirect register alias + + StopEI = 0x15C, // Supervisor top external interrupt (only with an IMSIC) + StopI = 0xDB0, // Supervisor top interrupt + + SIEH = 0x114, // Upper 32 bits of sie + SIPH = 0x154, // Upper 32 bits of sip + + STimeCmp = 0x14D, // Supervisor timer register + STimeCmpH = 0x15D, // Supervisor timer register, RV32 only + + SATP = 0x180, // Supervisor address translation and protection + + SContext = 0x5A8, // Supervisor-mode context register + + // Hypervisor-level CSRs + + HStatus = 0x600, // Hypervisor status register + HEDeleg = 0x602, // Hypervisor exception delegation register + HIDeleg = 0x603, // Hypervisor interrupt delegation register + HIE = 0x604, // Hypervisor interrupt-enable register + HCounterEn = 0x606, // Hypervisor counter enable + HGEIE = 0x607, // Hypervisor guest external interrupt-enable register + HVIEN = 0x608, // Hypervisor virtual interrupt enables + HVICTL = 0x609, // Hypervisor virtual interrupt control + + HIDelegH = 0x613, // Upper 32 bits of hideleg + HVIENH = 0x618, // Upper 32 bits of hvien + HVIPH = 0x655, // Upper 32 bits of hvip + HVIPrio1H = 0x656, // Upper 32 bits of hviprio1 + HVIPrio2H = 0x657, // Upper 32 bits of hviprio2 + VSIEH = 0x214, // Upper 32 bits of vsie + VSIPH = 0x254, // Upper 32 bits of vsiph + + HTVal = 0x643, // Hypervisor bad guest physical address + HIP = 0x644, // Hypervisor interrupt pending + HVIP = 0x645, // Hypervisor virtual interrupt pending + HVIPrio1 = 0x646, // Hypervisor VS-level interrupt priorities + HVIPrio2 = 0x647, // Hypervisor VS-level interrupt priorities + HTInst = 0x64A, // Hypervisor trap instruction (transformed) + HGEIP = 0xE12, // Hypervisor guest external interrupt pending + + HEnvCfg = 0x60A, // Hypervisor environment configuration register + HEnvCfgH = 0x61A, // Additional hypervisor environment configuration register, RV32 only + + HGATP = 0x680, // Hypervisor guest address translation and protection + + HContext = 0x6A8, // Hypervisor-mode context register + + HTimeDelta = 0x605, // Delta for VS/VU-mode timer + HTimeDeltaH = 0x615, // Upper 32 bits of HTimeDelta, HSXLEN=32 only + + VSStatus = 0x200, // Virtual supervisor status register + VSIE = 0x204, // Virtual supervisor interrupt-enable register + VSTVec = 0x205, // Virtual supervisor trap handler base address + VSScratch = 0x240, // Virtual supervisor scratch register + VSEPC = 0x241, // Virtual supervisor exception program register + VSCause = 0x242, // Virtual supervisor trap cause + VSTVal = 0x243, // Virtual supervisor bad address or instruction + VSIP = 0x244, // Virtual supervisor interrupt pending + + VSISelect = 0x250, // Virtual supervisor indirect register select + VSIReg = 0x251, // Virtual supervisor indirect register alias + + VStopEI = 0x25C, // Virtual supervisor top external interrupt (only with an IMSIC) + VStopI = 0xEB0, // Virtual supervisor top interrupt + + VSTimeCmp = 0x24D, // Virtual supervisor timer register + VSTimeCmpH = 0x25D, // Virtual supervisor timer register, RV32 only + + VSATP = 0x280, // Virtual supervisor address translation and protection + + // Machine-level CSRs + + MVendorID = 0xF11, // Vendor ID + MArchID = 0xF12, // Architecture ID + MImpID = 0xF13, // Implementation ID + MHartID = 0xF14, // Hardware Thread ID + MConfigPtr = 0xF15, // Pointer to configuration data structure + + MStatus = 0x300, // Machine status register + MISA = 0x301, // ISA and extensions + MEDeleg = 0x302, // Machine exception delegation register + MIDeleg = 0x303, // Machine interrupt delegation register + MIE = 0x304, // Machine interrupt-enable register + MRVec = 0x305, // Machine trap-handler base address + MCounterEn = 0x306, // Machine counter enable + MVIEN = 0x308, // Machine virtual interrupt enables + MVIP = 0x309, // Machine virtual interrupt-pending bits + MStatusH = 0x310, // Additional machine status register, RV32 only + + MIDelegH = 0x313, // Upper 32 bits of of mideleg (only with S-mode) + MIEH = 0x314, // Upper 32 bits of mie + MVIENH = 0x318, // Upper 32 bits of mvien (only with S-mode) + MVIPH = 0x319, // Upper 32 bits of mvip (only with S-mode) + MIPH = 0x354, // Upper 32 bits of mip + + MScratch = 0x340, // Scratch register for machine trap handlers + MEPC = 0x341, // Machine exception program counter + MCause = 0x342, // Machine trap cause + MTVal = 0x343, // Machine bad address or instruction + MIP = 0x344, // Machine interrupt pending + MTInst = 0x34A, // Machine trap instruction (transformed) + MTVal2 = 0x34B, // Machine bad guest physical address + + MISelect = 0x350, // Machine indirect register select + MIReg = 0x351, // Machine indirect register alias + + MTopEI = 0x35C, // Machine top external interrupt (only with an IMSIC) + MTopI = 0xFB0, // Machine top interrupt + + MEnvCfg = 0x30A, // Machine environment configuration register + MEnvCfgH = 0x31A, // Additional machine environment configuration register, RV32 only + MSecCfg = 0x747, // Machine security configuration register + MSecCfgH = 0x757, // Additional machine security configuration register, RV32 only + + PMPCfg0 = 0x3A0, // Physical memory protection configuration + PMPCfg1 = 0x3A1, // Physical memory protection configuration, RV32 only + PMPCfg2 = 0x3A2, // Physical memory protection configuration + PMPCfg3 = 0x3A3, // Physical memory protection configuration, RV32 only + PMPCfg4 = 0x3A4, // Physical memory protection configuration + PMPCfg5 = 0x3A5, // Physical memory protection configuration, RV32 only + PMPCfg6 = 0x3A6, // Physical memory protection configuration + PMPCfg7 = 0x3A7, // Physical memory protection configuration, RV32 only + PMPCfg8 = 0x3A8, // Physical memory protection configuration + PMPCfg9 = 0x3A9, // Physical memory protection configuration, RV32 only + PMPCfg10 = 0x3AA, // Physical memory protection configuration + PMPCfg11 = 0x3AB, // Physical memory protection configuration, RV32 only + PMPCfg12 = 0x3AC, // Physical memory protection configuration + PMPCfg13 = 0x3AD, // Physical memory protection configuration, RV32 only + PMPCfg14 = 0x3AE, // Physical memory protection configuration + PMPCfg15 = 0x3AF, // Physical memory protection configuration, RV32 only + PMPAddr0 = 0x3B0, // Physical memory protection address register + PMPAddr1 = 0x3B1, // Physical memory protection address register + PMPAddr2 = 0x3B2, // Physical memory protection address register + PMPAddr3 = 0x3B3, // Physical memory protection address register + PMPAddr4 = 0x3B4, // Physical memory protection address register + PMPAddr5 = 0x3B5, // Physical memory protection address register + PMPAddr6 = 0x3B6, // Physical memory protection address register + PMPAddr7 = 0x3B7, // Physical memory protection address register + PMPAddr8 = 0x3B8, // Physical memory protection address register + PMPAddr9 = 0x3B9, // Physical memory protection address register + PMPAddr10 = 0x3BA, // Physical memory protection address register + PMPAddr11 = 0x3BB, // Physical memory protection address register + PMPAddr12 = 0x3BC, // Physical memory protection address register + PMPAddr13 = 0x3BD, // Physical memory protection address register + PMPAddr14 = 0x3BE, // Physical memory protection address register + PMPAddr15 = 0x3BF, // Physical memory protection address register + PMPAddr16 = 0x3C0, // Physical memory protection address register + PMPAddr17 = 0x3C1, // Physical memory protection address register + PMPAddr18 = 0x3C2, // Physical memory protection address register + PMPAddr19 = 0x3C3, // Physical memory protection address register + PMPAddr20 = 0x3C4, // Physical memory protection address register + PMPAddr21 = 0x3C5, // Physical memory protection address register + PMPAddr22 = 0x3C6, // Physical memory protection address register + PMPAddr23 = 0x3C7, // Physical memory protection address register + PMPAddr24 = 0x3C8, // Physical memory protection address register + PMPAddr25 = 0x3C9, // Physical memory protection address register + PMPAddr26 = 0x3CA, // Physical memory protection address register + PMPAddr27 = 0x3CB, // Physical memory protection address register + PMPAddr28 = 0x3CC, // Physical memory protection address register + PMPAddr29 = 0x3CD, // Physical memory protection address register + PMPAddr30 = 0x3CE, // Physical memory protection address register + PMPAddr31 = 0x3CF, // Physical memory protection address register + PMPAddr32 = 0x3D0, // Physical memory protection address register + PMPAddr33 = 0x3D1, // Physical memory protection address register + PMPAddr34 = 0x3D2, // Physical memory protection address register + PMPAddr35 = 0x3D3, // Physical memory protection address register + PMPAddr36 = 0x3D4, // Physical memory protection address register + PMPAddr37 = 0x3D5, // Physical memory protection address register + PMPAddr38 = 0x3D6, // Physical memory protection address register + PMPAddr39 = 0x3D7, // Physical memory protection address register + PMPAddr40 = 0x3D8, // Physical memory protection address register + PMPAddr41 = 0x3D9, // Physical memory protection address register + PMPAddr42 = 0x3DA, // Physical memory protection address register + PMPAddr43 = 0x3DB, // Physical memory protection address register + PMPAddr44 = 0x3DC, // Physical memory protection address register + PMPAddr45 = 0x3DD, // Physical memory protection address register + PMPAddr46 = 0x3DE, // Physical memory protection address register + PMPAddr47 = 0x3DF, // Physical memory protection address register + PMPAddr48 = 0x3E0, // Physical memory protection address register + PMPAddr49 = 0x3E1, // Physical memory protection address register + PMPAddr50 = 0x3E2, // Physical memory protection address register + PMPAddr51 = 0x3E3, // Physical memory protection address register + PMPAddr52 = 0x3E4, // Physical memory protection address register + PMPAddr53 = 0x3E5, // Physical memory protection address register + PMPAddr54 = 0x3E6, // Physical memory protection address register + PMPAddr55 = 0x3E7, // Physical memory protection address register + PMPAddr56 = 0x3E8, // Physical memory protection address register + PMPAddr57 = 0x3E9, // Physical memory protection address register + PMPAddr58 = 0x3EA, // Physical memory protection address register + PMPAddr59 = 0x3EB, // Physical memory protection address register + PMPAddr60 = 0x3EC, // Physical memory protection address register + PMPAddr61 = 0x3ED, // Physical memory protection address register + PMPAddr62 = 0x3EE, // Physical memory protection address register + PMPAddr63 = 0x3EF, // Physical memory protection address register + + MNScratch = 0x740, // Resumable NMI scratch register + MNEPC = 0x741, // Resumable NMI program counter + MNCause = 0x742, // Resumable NMI cause + MNStatus = 0x744, // Resumable NMI status + + MCycle = 0xB00, // Machine cycle counter + MInstRet = 0xB02, // Machine instructions-retired counter + MHPMCounter3 = 0xB03, // Machine performance-monitoring counter + MHPMCounter4 = 0xB04, // Machine performance-monitoring counter + MHPMCounter5 = 0xB05, // Machine performance-monitoring counter + MHPMCounter6 = 0xB06, // Machine performance-monitoring counter + MHPMCounter7 = 0xB07, // Machine performance-monitoring counter + MHPMCounter8 = 0xB08, // Machine performance-monitoring counter + MHPMCounter9 = 0xB09, // Machine performance-monitoring counter + MHPMCounter10 = 0xB0A, // Machine performance-monitoring counter + MHPMCounter11 = 0xB0B, // Machine performance-monitoring counter + MHPMCounter12 = 0xB0C, // Machine performance-monitoring counter + MHPMCounter13 = 0xB0D, // Machine performance-monitoring counter + MHPMCounter14 = 0xB0E, // Machine performance-monitoring counter + MHPMCounter15 = 0xB0F, // Machine performance-monitoring counter + MHPMCounter16 = 0xB10, // Machine performance-monitoring counter + MHPMCounter17 = 0xB11, // Machine performance-monitoring counter + MHPMCounter18 = 0xB12, // Machine performance-monitoring counter + MHPMCounter19 = 0xB13, // Machine performance-monitoring counter + MHPMCounter20 = 0xB14, // Machine performance-monitoring counter + MHPMCounter21 = 0xB15, // Machine performance-monitoring counter + MHPMCounter22 = 0xB16, // Machine performance-monitoring counter + MHPMCounter23 = 0xB17, // Machine performance-monitoring counter + MHPMCounter24 = 0xB18, // Machine performance-monitoring counter + MHPMCounter25 = 0xB19, // Machine performance-monitoring counter + MHPMCounter26 = 0xB1A, // Machine performance-monitoring counter + MHPMCounter27 = 0xB1B, // Machine performance-monitoring counter + MHPMCounter28 = 0xB1C, // Machine performance-monitoring counter + MHPMCounter29 = 0xB1D, // Machine performance-monitoring counter + MHPMCounter30 = 0xB1E, // Machine performance-monitoring counter + MHPMCounter31 = 0xB1F, // Machine performance-monitoring counter + + MCycleH = 0xB80, // Upper 32 bits ofmcycle, RV32I only + MInstRetH = 0xB82, // Upper 32 bits ofminstret, RV32I only + + MHPMCounter3H = 0xB83, // Upper 32 bits of MHPMCounter3, RV32I only + MHPMCounter4H = 0xB84, // Upper 32 bits of MHPMCounter4, RV32I only + MHPMCounter5H = 0xB85, // Upper 32 bits of MHPMCounter5, RV32I only + MHPMCounter6H = 0xB86, // Upper 32 bits of MHPMCounter6, RV32I only + MHPMCounter7H = 0xB87, // Upper 32 bits of MHPMCounter7, RV32I only + MHPMCounter8H = 0xB88, // Upper 32 bits of MHPMCounter8, RV32I only + MHPMCounter9H = 0xB89, // Upper 32 bits of MHPMCounter9, RV32I only + MHPMCounter10H = 0xB8A, // Upper 32 bits of MHPMCounter10, RV32I only + MHPMCounter11H = 0xB8B, // Upper 32 bits of MHPMCounter11, RV32I only + MHPMCounter12H = 0xB8C, // Upper 32 bits of MHPMCounter12, RV32I only + MHPMCounter13H = 0xB8D, // Upper 32 bits of MHPMCounter13, RV32I only + MHPMCounter14H = 0xB8E, // Upper 32 bits of MHPMCounter14, RV32I only + MHPMCounter15H = 0xB8F, // Upper 32 bits of MHPMCounter15, RV32I only + MHPMCounter16H = 0xB90, // Upper 32 bits of MHPMCounter16, RV32I only + MHPMCounter17H = 0xB91, // Upper 32 bits of MHPMCounter17, RV32I only + MHPMCounter18H = 0xB92, // Upper 32 bits of MHPMCounter18, RV32I only + MHPMCounter19H = 0xB93, // Upper 32 bits of MHPMCounter19, RV32I only + MHPMCounter20H = 0xB94, // Upper 32 bits of MHPMCounter20, RV32I only + MHPMCounter21H = 0xB95, // Upper 32 bits of MHPMCounter21, RV32I only + MHPMCounter22H = 0xB96, // Upper 32 bits of MHPMCounter22, RV32I only + MHPMCounter23H = 0xB97, // Upper 32 bits of MHPMCounter23, RV32I only + MHPMCounter24H = 0xB98, // Upper 32 bits of MHPMCounter24, RV32I only + MHPMCounter25H = 0xB99, // Upper 32 bits of MHPMCounter25, RV32I only + MHPMCounter26H = 0xB9A, // Upper 32 bits of MHPMCounter26, RV32I only + MHPMCounter27H = 0xB9B, // Upper 32 bits of MHPMCounter27, RV32I only + MHPMCounter28H = 0xB9C, // Upper 32 bits of MHPMCounter28, RV32I only + MHPMCounter29H = 0xB9D, // Upper 32 bits of MHPMCounter29, RV32I only + MHPMCounter30H = 0xB9E, // Upper 32 bits of MHPMCounter30, RV32I only + MHPMCounter31H = 0xB9F, // Upper 32 bits of MHPMCounter31, RV32I only + + MCountInhibit = 0x320, // Machine counter-inhibit register + + MCycleCfg = 0x321, // Privilege mode filtering for cycle counter + MCycleCfgH = 0x721, // Privilege mode filtering for cycle counter (RV32) + MInstRetCfg = 0x322, // Privilege mode filtering for instret counters + MInstRetCfgH = 0x722, // Privilege mode filtering for instret counters (RV32) + + MHPMEvent3 = 0x323, // Machine performance-monitoring event selector + MHPMEvent4 = 0x324, // Machine performance-monitoring event selector + MHPMEvent5 = 0x325, // Machine performance-monitoring event selector + MHPMEvent6 = 0x326, // Machine performance-monitoring event selector + MHPMEvent7 = 0x327, // Machine performance-monitoring event selector + MHPMEvent8 = 0x328, // Machine performance-monitoring event selector + MHPMEvent9 = 0x329, // Machine performance-monitoring event selector + MHPMEvent10 = 0x32A, // Machine performance-monitoring event selector + MHPMEvent11 = 0x32B, // Machine performance-monitoring event selector + MHPMEvent12 = 0x32C, // Machine performance-monitoring event selector + MHPMEvent13 = 0x32D, // Machine performance-monitoring event selector + MHPMEvent14 = 0x32E, // Machine performance-monitoring event selector + MHPMEvent15 = 0x32F, // Machine performance-monitoring event selector + MHPMEvent16 = 0x330, // Machine performance-monitoring event selector + MHPMEvent17 = 0x331, // Machine performance-monitoring event selector + MHPMEvent18 = 0x332, // Machine performance-monitoring event selector + MHPMEvent19 = 0x333, // Machine performance-monitoring event selector + MHPMEvent20 = 0x334, // Machine performance-monitoring event selector + MHPMEvent21 = 0x335, // Machine performance-monitoring event selector + MHPMEvent22 = 0x336, // Machine performance-monitoring event selector + MHPMEvent23 = 0x337, // Machine performance-monitoring event selector + MHPMEvent24 = 0x338, // Machine performance-monitoring event selector + MHPMEvent25 = 0x339, // Machine performance-monitoring event selector + MHPMEvent26 = 0x33A, // Machine performance-monitoring event selector + MHPMEvent27 = 0x33B, // Machine performance-monitoring event selector + MHPMEvent28 = 0x33C, // Machine performance-monitoring event selector + MHPMEvent29 = 0x33D, // Machine performance-monitoring event selector + MHPMEvent30 = 0x33E, // Machine performance-monitoring event selector + MHPMEvent31 = 0x33F, // Machine performance-monitoring event selector + + TSelect = 0x7A0, // Debug/Trace trigger register select + TData1 = 0x7A1, // First Debug/Trace trigger data register + TData2 = 0x7A2, // Second Debug/Trace trigger data register + TData3 = 0x7A3, // Third Debug/Trace trigger data register + 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 diff --git a/externals/biscuit/include/biscuit/isa.hpp b/externals/biscuit/include/biscuit/isa.hpp new file mode 100644 index 00000000..94a9c239 --- /dev/null +++ b/externals/biscuit/include/biscuit/isa.hpp @@ -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 diff --git a/externals/biscuit/include/biscuit/label.hpp b/externals/biscuit/include/biscuit/label.hpp new file mode 100644 index 00000000..8cfeeb2c --- /dev/null +++ b/externals/biscuit/include/biscuit/label.hpp @@ -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 diff --git a/externals/biscuit/include/biscuit/registers.hpp b/externals/biscuit/include/biscuit/registers.hpp new file mode 100644 index 00000000..08c47787 --- /dev/null +++ b/externals/biscuit/include/biscuit/registers.hpp @@ -0,0 +1,315 @@ +#pragma once + +#include <biscuit/assert.hpp> + +#include <compare> +#include <cstdint> + +namespace biscuit { + +/** + * Generic abstraction around a register. + * + * This is less bug-prone than using raw primitive sizes + * in opcode emitter functions, since it provides stronger typing. + */ +class Register { +public: + constexpr Register() noexcept = default; + + /// Gets the index for this register. + [[nodiscard]] constexpr uint32_t Index() const noexcept { + return m_index; + } + + friend constexpr bool operator==(Register, Register) = default; + friend constexpr auto operator<=>(Register, Register) = default; + +protected: + constexpr explicit Register(uint32_t index) noexcept + : m_index{index} {} + +private: + uint32_t m_index{}; +}; + +/// General purpose register. +class GPR final : public Register { +public: + constexpr GPR() noexcept : Register{0} {} + constexpr explicit GPR(uint32_t index) noexcept : Register{index} {} + + friend constexpr bool operator==(GPR, GPR) = default; + friend constexpr auto operator<=>(GPR, GPR) = default; +}; + +/// Floating point register. +class FPR final : public Register { +public: + constexpr FPR() noexcept : Register{0} {} + constexpr explicit FPR(uint32_t index) noexcept : Register{index} {} + + friend constexpr bool operator==(FPR, FPR) = default; + friend constexpr auto operator<=>(FPR, FPR) = default; +}; + +/// Vector register. +class Vec final : public Register { +public: + constexpr Vec() noexcept : Register{0} {} + constexpr explicit Vec(uint32_t index) noexcept : Register{index} {} + + friend constexpr bool operator==(Vec, Vec) = default; + friend constexpr auto operator<=>(Vec, Vec) = default; +}; + +// General-purpose Registers + +constexpr GPR x0{0}; +constexpr GPR x1{1}; +constexpr GPR x2{2}; +constexpr GPR x3{3}; +constexpr GPR x4{4}; +constexpr GPR x5{5}; +constexpr GPR x6{6}; +constexpr GPR x7{7}; +constexpr GPR x8{8}; +constexpr GPR x9{9}; +constexpr GPR x10{10}; +constexpr GPR x11{11}; +constexpr GPR x12{12}; +constexpr GPR x13{13}; +constexpr GPR x14{14}; +constexpr GPR x15{15}; +constexpr GPR x16{16}; +constexpr GPR x17{17}; +constexpr GPR x18{18}; +constexpr GPR x19{19}; +constexpr GPR x20{20}; +constexpr GPR x21{21}; +constexpr GPR x22{22}; +constexpr GPR x23{23}; +constexpr GPR x24{24}; +constexpr GPR x25{25}; +constexpr GPR x26{26}; +constexpr GPR x27{27}; +constexpr GPR x28{28}; +constexpr GPR x29{29}; +constexpr GPR x30{30}; +constexpr GPR x31{31}; + +// Symbolic General-purpose Register Names + +constexpr GPR zero{x0}; + +constexpr GPR ra{x1}; +constexpr GPR sp{x2}; +constexpr GPR gp{x3}; +constexpr GPR tp{x4}; +constexpr GPR fp{x8}; + +constexpr GPR a0{x10}; +constexpr GPR a1{x11}; +constexpr GPR a2{x12}; +constexpr GPR a3{x13}; +constexpr GPR a4{x14}; +constexpr GPR a5{x15}; +constexpr GPR a6{x16}; +constexpr GPR a7{x17}; + +constexpr GPR s0{x8}; +constexpr GPR s1{x9}; +constexpr GPR s2{x18}; +constexpr GPR s3{x19}; +constexpr GPR s4{x20}; +constexpr GPR s5{x21}; +constexpr GPR s6{x22}; +constexpr GPR s7{x23}; +constexpr GPR s8{x24}; +constexpr GPR s9{x25}; +constexpr GPR s10{x26}; +constexpr GPR s11{x27}; + +constexpr GPR t0{x5}; +constexpr GPR t1{x6}; +constexpr GPR t2{x7}; +constexpr GPR t3{x28}; +constexpr GPR t4{x29}; +constexpr GPR t5{x30}; +constexpr GPR t6{x31}; + +// Floating-point registers + +constexpr FPR f0{0}; +constexpr FPR f1{1}; +constexpr FPR f2{2}; +constexpr FPR f3{3}; +constexpr FPR f4{4}; +constexpr FPR f5{5}; +constexpr FPR f6{6}; +constexpr FPR f7{7}; +constexpr FPR f8{8}; +constexpr FPR f9{9}; +constexpr FPR f10{10}; +constexpr FPR f11{11}; +constexpr FPR f12{12}; +constexpr FPR f13{13}; +constexpr FPR f14{14}; +constexpr FPR f15{15}; +constexpr FPR f16{16}; +constexpr FPR f17{17}; +constexpr FPR f18{18}; +constexpr FPR f19{19}; +constexpr FPR f20{20}; +constexpr FPR f21{21}; +constexpr FPR f22{22}; +constexpr FPR f23{23}; +constexpr FPR f24{24}; +constexpr FPR f25{25}; +constexpr FPR f26{26}; +constexpr FPR f27{27}; +constexpr FPR f28{28}; +constexpr FPR f29{29}; +constexpr FPR f30{30}; +constexpr FPR f31{31}; + +// Symbolic Floating-point Register Names + +constexpr FPR fa0{f10}; +constexpr FPR fa1{f11}; +constexpr FPR fa2{f12}; +constexpr FPR fa3{f13}; +constexpr FPR fa4{f14}; +constexpr FPR fa5{f15}; +constexpr FPR fa6{f16}; +constexpr FPR fa7{f17}; + +constexpr FPR ft0{f0}; +constexpr FPR ft1{f1}; +constexpr FPR ft2{f2}; +constexpr FPR ft3{f3}; +constexpr FPR ft4{f4}; +constexpr FPR ft5{f5}; +constexpr FPR ft6{f6}; +constexpr FPR ft7{f7}; +constexpr FPR ft8{f28}; +constexpr FPR ft9{f29}; +constexpr FPR ft10{f30}; +constexpr FPR ft11{f31}; + +constexpr FPR fs0{f8}; +constexpr FPR fs1{f9}; +constexpr FPR fs2{f18}; +constexpr FPR fs3{f19}; +constexpr FPR fs4{f20}; +constexpr FPR fs5{f21}; +constexpr FPR fs6{f22}; +constexpr FPR fs7{f23}; +constexpr FPR fs8{f24}; +constexpr FPR fs9{f25}; +constexpr FPR fs10{f26}; +constexpr FPR fs11{f27}; + +// Vector registers (V extension) + +constexpr Vec v0{0}; +constexpr Vec v1{1}; +constexpr Vec v2{2}; +constexpr Vec v3{3}; +constexpr Vec v4{4}; +constexpr Vec v5{5}; +constexpr Vec v6{6}; +constexpr Vec v7{7}; +constexpr Vec v8{8}; +constexpr Vec v9{9}; +constexpr Vec v10{10}; +constexpr Vec v11{11}; +constexpr Vec v12{12}; +constexpr Vec v13{13}; +constexpr Vec v14{14}; +constexpr Vec v15{15}; +constexpr Vec v16{16}; +constexpr Vec v17{17}; +constexpr Vec v18{18}; +constexpr Vec v19{19}; +constexpr Vec v20{20}; +constexpr Vec v21{21}; +constexpr Vec v22{22}; +constexpr Vec v23{23}; +constexpr Vec v24{24}; +constexpr Vec v25{25}; +constexpr Vec v26{26}; +constexpr Vec v27{27}; +constexpr Vec v28{28}; +constexpr Vec v29{29}; +constexpr Vec v30{30}; +constexpr Vec v31{31}; + +// Register utilities + +// Used with compressed stack management instructions +// (cm.push, cm.pop, etc) for building up a register list to encode. +// +// Also enforces that only valid registers are used in the lists. +class PushPopList final { +public: + // Represents an inclusive range ([start, end]) of registers. + struct Range final { + // Signifies an empty range. Normally this doesn't need to explicitly + // be created. Default parameters will usually take care of it. + constexpr Range() : start{UINT32_MAX}, end{UINT32_MAX} {} + + // This particular constructor is used for the case of rlist=5 + // where only ra and s0 get stored. + constexpr Range(GPR start_end) noexcept : start{start_end}, end{start_end} { + BISCUIT_ASSERT(start_end == s0); + } + + constexpr Range(GPR start_, GPR end_) noexcept : start{start_}, end{end_} { + BISCUIT_ASSERT(start_ == s0); + BISCUIT_ASSERT(IsSRegister(end_)); + + // See the Zc spec. The only way for s10 to be used is to also include s11. + BISCUIT_ASSERT(end_ != s10); + } + + GPR start; + GPR end; + }; + + // Deliberately non-explicit to allow for convenient instantiation at usage sites. + // e.g. Rather than CM.POP(PushPopList{ra, {s0, s2}}, 16), we can just have the + // usage be transparent like CM.POP({ra, {s0, s2}}, 16). Nice and compact! + constexpr PushPopList(GPR ra_reg, const Range& range = {}) noexcept + : m_bitmask{BuildBitmask(range)} { + BISCUIT_ASSERT(ra_reg == ra); + } + + // Gets the built-up bitmask of passed in registers + [[nodiscard]] constexpr uint32_t GetBitmask() const noexcept { + return m_bitmask; + } + +private: + [[nodiscard]] static constexpr uint32_t BuildBitmask(const Range& range) noexcept { + if (range.end.Index() == UINT32_MAX) { + return 4U; + } + if (range.end == s11) { + return 15U; + } + if (range.end == s0 || range.end == s1) { + return range.end.Index() - 3U; + } + return range.end.Index() - 11U; + } + + // Aside from ra, it's only valid for s0-s11 to show up the register list ranges. + [[nodiscard]] static constexpr bool IsSRegister(const GPR gpr) noexcept { + return gpr == s0 || gpr == s1 || (gpr >= s2 && gpr <= s11); + } + + uint32_t m_bitmask = 0; +}; + +} // namespace biscuit diff --git a/externals/biscuit/include/biscuit/vector.hpp b/externals/biscuit/include/biscuit/vector.hpp new file mode 100644 index 00000000..d31208ed --- /dev/null +++ b/externals/biscuit/include/biscuit/vector.hpp @@ -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 diff --git a/externals/biscuit/src/CMakeLists.txt b/externals/biscuit/src/CMakeLists.txt new file mode 100644 index 00000000..3d841023 --- /dev/null +++ b/externals/biscuit/src/CMakeLists.txt @@ -0,0 +1,156 @@ +# Main library + +add_library(biscuit + # Source files + assembler.cpp + assembler_compressed.cpp + assembler_crypto.cpp + assembler_floating_point.cpp + assembler_vector.cpp + code_buffer.cpp + cpuinfo.cpp + + # Headers + assembler_util.hpp + "${PROJECT_SOURCE_DIR}/include/biscuit/assembler.hpp" + "${PROJECT_SOURCE_DIR}/include/biscuit/assert.hpp" + "${PROJECT_SOURCE_DIR}/include/biscuit/code_buffer.hpp" + "${PROJECT_SOURCE_DIR}/include/biscuit/csr.hpp" + "${PROJECT_SOURCE_DIR}/include/biscuit/isa.hpp" + "${PROJECT_SOURCE_DIR}/include/biscuit/label.hpp" + "${PROJECT_SOURCE_DIR}/include/biscuit/registers.hpp" + "${PROJECT_SOURCE_DIR}/include/biscuit/vector.hpp" + "${PROJECT_SOURCE_DIR}/include/biscuit/cpuinfo.hpp" +) +add_library(biscuit::biscuit ALIAS biscuit) + +target_include_directories(biscuit +PUBLIC + $<INSTALL_INTERFACE:include> + $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include> + +PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} +) + +target_compile_features(biscuit +PRIVATE + cxx_std_20 +) + +if (MSVC) + target_compile_options(biscuit + PRIVATE + /MP + /Zi + /Zo + /permissive- + /EHsc + /utf-8 + /volatile:iso + /Zc:externConstexpr + /Zc:inline + /Zc:throwingNew + + # Warnings + /W4 + /we4062 # enumerator 'identifier' in a switch of enum 'enumeration' is not handled + /we4101 # 'identifier': unreferenced local variable + /we4265 # 'class': class has virtual functions, but destructor is not virtual + /we4287 # 'operator' : unsigned/negative constant mismatch + /we4365 # 'action' : conversion from 'type_1' to 'type_2', signed/unsigned mismatch + /we4388 # signed/unsigned mismatch + /we4547 # 'operator' : operator before comma has no effect; expected operator with side-effect + /we4549 # 'operator1': operator before comma has no effect; did you intend 'operator2'? + /we4555 # Expression has no effect; expected expression with side-effect + /we4715 # 'function': not all control paths return a value + /we4834 # Discarding return value of function with 'nodiscard' attribute + /we5038 # data member 'member1' will be initialized after data member 'member2' + ) +elseif (("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU")) + target_compile_options(biscuit + PRIVATE + -Wall + -Wextra + -Wconversion + -Wsign-conversion + + -Werror=array-bounds + -Werror=cast-qual + -Werror=ignored-qualifiers + -Werror=implicit-fallthrough + -Werror=sign-compare + -Werror=reorder + -Werror=uninitialized + -Werror=unused-function + -Werror=unused-result + -Werror=unused-variable + ) +endif() + +if (BISCUIT_CODE_BUFFER_MMAP) + target_compile_definitions(biscuit + PRIVATE + -DBISCUIT_CODE_BUFFER_MMAP + ) +endif() + +# Install target + +include(GNUInstallDirs) +set(BISCUIT_INSTALL_CONFIGDIR "${CMAKE_INSTALL_LIBDIR}/cmake/biscuit") + +# Set install target and relevant includes. +install(TARGETS biscuit + EXPORT biscuit-targets + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" +) +install( + DIRECTORY "${PROJECT_SOURCE_DIR}/include/" + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" +) + +# Export targets to a script +install(EXPORT biscuit-targets + FILE + biscuit-targets.cmake + NAMESPACE + biscuit:: + DESTINATION + "${BISCUIT_INSTALL_CONFIGDIR}" +) + +# Now create the config version script +include(CMakePackageConfigHelpers) +write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/biscuit-config-version.cmake" + VERSION + ${PROJECT_VERSION} + COMPATIBILITY + SameMajorVersion +) + +configure_package_config_file( + "${PROJECT_SOURCE_DIR}/cmake/biscuit-config.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/biscuit-config.cmake" + + INSTALL_DESTINATION "${BISCUIT_INSTALL_CONFIGDIR}" +) + +# Now install the config and version files. +install(FILES + "${CMAKE_CURRENT_BINARY_DIR}/biscuit-config.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/biscuit-config-version.cmake" + + DESTINATION "${BISCUIT_INSTALL_CONFIGDIR}" +) + +# Export library from the build tree. +export(EXPORT biscuit-targets + FILE + "${CMAKE_CURRENT_BINARY_DIR}/biscuit-targets.cmake" + NAMESPACE + biscuit:: +) +export(PACKAGE biscuit) diff --git a/externals/biscuit/src/assembler.cpp b/externals/biscuit/src/assembler.cpp new file mode 100644 index 00000000..a44da23d --- /dev/null +++ b/externals/biscuit/src/assembler.cpp @@ -0,0 +1,1401 @@ +#include <biscuit/assert.hpp> +#include <biscuit/assembler.hpp> + +#include <bit> +#include <cstring> +#include <utility> + +#include "assembler_util.hpp" + +namespace biscuit { + +Assembler::Assembler(size_t capacity) + : m_buffer(capacity) {} + +Assembler::Assembler(uint8_t* buffer, size_t capacity, ArchFeature features) + : m_buffer(buffer, capacity), m_features{features} {} + +Assembler::~Assembler() = default; + +CodeBuffer& Assembler::GetCodeBuffer() { + return m_buffer; +} + +CodeBuffer Assembler::SwapCodeBuffer(CodeBuffer&& buffer) noexcept { + return std::exchange(m_buffer, std::move(buffer)); +} + +void Assembler::Bind(Label* label) { + BindToOffset(label, m_buffer.GetCursorOffset()); +} + +void Assembler::ADD(GPR rd, GPR lhs, GPR rhs) noexcept { + EmitRType(m_buffer, 0b0000000, rhs, lhs, 0b000, rd, 0b0110011); +} + +void Assembler::ADDI(GPR rd, GPR rs, int32_t imm) noexcept { + EmitIType(m_buffer, static_cast<uint32_t>(imm), rs, 0b000, rd, 0b0010011); +} + +void Assembler::AND(GPR rd, GPR lhs, GPR rhs) noexcept { + EmitRType(m_buffer, 0b0000000, rhs, lhs, 0b111, rd, 0b0110011); +} + +void Assembler::ANDI(GPR rd, GPR rs, uint32_t imm) noexcept { + EmitIType(m_buffer, imm, rs, 0b111, rd, 0b0010011); +} + +void Assembler::AUIPC(GPR rd, int32_t imm) noexcept { + EmitUType(m_buffer, static_cast<uint32_t>(imm), rd, 0b0010111); +} + +void Assembler::BEQ(GPR rs1, GPR rs2, Label* label) noexcept { + const auto address = LinkAndGetOffset(label); + BEQ(rs1, rs2, static_cast<int32_t>(address)); +} + +void Assembler::BEQZ(GPR rs, Label* label) noexcept { + const auto address = LinkAndGetOffset(label); + BEQZ(rs, static_cast<int32_t>(address)); +} + +void Assembler::BGE(GPR rs1, GPR rs2, Label* label) noexcept { + const auto address = LinkAndGetOffset(label); + BGE(rs1, rs2, static_cast<int32_t>(address)); +} + +void Assembler::BGEU(GPR rs1, GPR rs2, Label* label) noexcept { + const auto address = LinkAndGetOffset(label); + BGEU(rs1, rs2, static_cast<int32_t>(address)); +} + +void Assembler::BGEZ(GPR rs, Label* label) noexcept { + const auto address = LinkAndGetOffset(label); + BGEZ(rs, static_cast<int32_t>(address)); +} + +void Assembler::BGT(GPR rs, GPR rt, Label* label) noexcept { + const auto address = LinkAndGetOffset(label); + BGT(rs, rt, static_cast<int32_t>(address)); +} + +void Assembler::BGTU(GPR rs, GPR rt, Label* label) noexcept { + const auto address = LinkAndGetOffset(label); + BGTU(rs, rt, static_cast<int32_t>(address)); +} + +void Assembler::BGTZ(GPR rs, Label* label) noexcept { + const auto address = LinkAndGetOffset(label); + BGTZ(rs, static_cast<int32_t>(address)); +} + +void Assembler::BLE(GPR rs, GPR rt, Label* label) noexcept { + const auto address = LinkAndGetOffset(label); + BLE(rs, rt, static_cast<int32_t>(address)); +} + +void Assembler::BLEU(GPR rs, GPR rt, Label* label) noexcept { + const auto address = LinkAndGetOffset(label); + BLEU(rs, rt, static_cast<int32_t>(address)); +} + +void Assembler::BLEZ(GPR rs, Label* label) noexcept { + const auto address = LinkAndGetOffset(label); + BLEZ(rs, static_cast<int32_t>(address)); +} + +void Assembler::BLT(GPR rs1, GPR rs2, Label* label) noexcept { + const auto address = LinkAndGetOffset(label); + BLT(rs1, rs2, static_cast<int32_t>(address)); +} + +void Assembler::BLTU(GPR rs1, GPR rs2, Label* label) noexcept { + const auto address = LinkAndGetOffset(label); + BLTU(rs1, rs2, static_cast<int32_t>(address)); +} + +void Assembler::BLTZ(GPR rs, Label* label) noexcept { + const auto address = LinkAndGetOffset(label); + BLTZ(rs, static_cast<int32_t>(address)); +} + +void Assembler::BNE(GPR rs1, GPR rs2, Label* label) noexcept { + const auto address = LinkAndGetOffset(label); + BNE(rs1, rs2, static_cast<int32_t>(address)); +} + +void Assembler::BNEZ(GPR rs, Label* label) noexcept { + const auto address = LinkAndGetOffset(label); + BNEZ(rs, static_cast<int32_t>(address)); +} + +void Assembler::BEQ(GPR rs1, GPR rs2, int32_t imm) noexcept { + BISCUIT_ASSERT(IsValidBTypeImm(imm)); + EmitBType(m_buffer, static_cast<uint32_t>(imm), rs2, rs1, 0b000, 0b1100011); +} + +void Assembler::BEQZ(GPR rs, int32_t imm) noexcept { + BEQ(rs, x0, imm); +} + +void Assembler::BGE(GPR rs1, GPR rs2, int32_t imm) noexcept { + BISCUIT_ASSERT(IsValidBTypeImm(imm)); + EmitBType(m_buffer, static_cast<uint32_t>(imm), rs2, rs1, 0b101, 0b1100011); +} + +void Assembler::BGEU(GPR rs1, GPR rs2, int32_t imm) noexcept { + BISCUIT_ASSERT(IsValidBTypeImm(imm)); + EmitBType(m_buffer, static_cast<uint32_t>(imm), rs2, rs1, 0b111, 0b1100011); +} + +void Assembler::BGEZ(GPR rs, int32_t imm) noexcept { + BGE(rs, x0, imm); +} + +void Assembler::BGT(GPR rs, GPR rt, int32_t imm) noexcept { + BLT(rt, rs, imm); +} + +void Assembler::BGTU(GPR rs, GPR rt, int32_t imm) noexcept { + BLTU(rt, rs, imm); +} + +void Assembler::BGTZ(GPR rs, int32_t imm) noexcept { + BLT(x0, rs, imm); +} + +void Assembler::BLE(GPR rs, GPR rt, int32_t imm) noexcept { + BGE(rt, rs, imm); +} + +void Assembler::BLEU(GPR rs, GPR rt, int32_t imm) noexcept { + BGEU(rt, rs, imm); +} + +void Assembler::BLEZ(GPR rs, int32_t imm) noexcept { + BGE(x0, rs, imm); +} + +void Assembler::BLT(GPR rs1, GPR rs2, int32_t imm) noexcept { + BISCUIT_ASSERT(IsValidBTypeImm(imm)); + EmitBType(m_buffer, static_cast<uint32_t>(imm), rs2, rs1, 0b100, 0b1100011); +} + +void Assembler::BLTU(GPR rs1, GPR rs2, int32_t imm) noexcept { + BISCUIT_ASSERT(IsValidBTypeImm(imm)); + EmitBType(m_buffer, static_cast<uint32_t>(imm), rs2, rs1, 0b110, 0b1100011); +} + +void Assembler::BLTZ(GPR rs, int32_t imm) noexcept { + BLT(rs, x0, imm); +} + +void Assembler::BNE(GPR rs1, GPR rs2, int32_t imm) noexcept { + BISCUIT_ASSERT(IsValidBTypeImm(imm)); + EmitBType(m_buffer, static_cast<uint32_t>(imm), rs2, rs1, 0b001, 0b1100011); +} + +void Assembler::BNEZ(GPR rs, int32_t imm) noexcept { + BNE(x0, rs, imm); +} + +void Assembler::CALL(int32_t offset) noexcept { + const auto uimm = static_cast<uint32_t>(offset); + const auto lower = uimm & 0xFFF; + const auto upper = (uimm & 0xFFFFF000) >> 12; + const auto needs_increment = (uimm & 0x800) != 0; + + // Sign-extend the lower portion if the MSB of it is set. + const auto new_lower = needs_increment ? static_cast<int32_t>(lower << 20) >> 20 + : static_cast<int32_t>(lower); + const auto new_upper = needs_increment ? upper + 1 : upper; + + AUIPC(x1, static_cast<int32_t>(new_upper)); + JALR(x1, new_lower, x1); +} + +void Assembler::EBREAK() noexcept { + m_buffer.Emit32(0x00100073); +} + +void Assembler::ECALL() noexcept { + m_buffer.Emit32(0x00000073); +} + +void Assembler::FENCE() noexcept { + FENCE(FenceOrder::IORW, FenceOrder::IORW); +} + +void Assembler::FENCE(FenceOrder pred, FenceOrder succ) noexcept { + EmitFENCE(m_buffer, 0b0000, pred, succ, x0, 0b000, x0, 0b0001111); +} + +void Assembler::FENCEI(GPR rd, GPR rs, uint32_t imm) noexcept { + m_buffer.Emit32(((imm & 0xFFF) << 20) | (rs.Index() << 15) | 0x1000U | (rd.Index() << 7) | 0b0001111); +} + +void Assembler::FENCETSO() noexcept { + EmitFENCE(m_buffer, 0b1000, FenceOrder::RW, FenceOrder::RW, x0, 0b000, x0, 0b0001111); +} + +void Assembler::J(Label* label) noexcept { + const auto address = LinkAndGetOffset(label); + BISCUIT_ASSERT(IsValidJTypeImm(address)); + J(static_cast<int32_t>(address)); +} + +void Assembler::JAL(Label* label) noexcept { + const auto address = LinkAndGetOffset(label); + BISCUIT_ASSERT(IsValidJTypeImm(address)); + JAL(static_cast<int32_t>(address)); +} + +void Assembler::JAL(GPR rd, Label* label) noexcept { + const auto address = LinkAndGetOffset(label); + BISCUIT_ASSERT(IsValidJTypeImm(address)); + JAL(rd, static_cast<int32_t>(address)); +} + +void Assembler::J(int32_t imm) noexcept { + BISCUIT_ASSERT(IsValidJTypeImm(imm)); + JAL(x0, imm); +} + +void Assembler::JAL(int32_t imm) noexcept { + BISCUIT_ASSERT(IsValidJTypeImm(imm)); + EmitJType(m_buffer, static_cast<uint32_t>(imm), x1, 0b1101111); +} + +void Assembler::JAL(GPR rd, int32_t imm) noexcept { + BISCUIT_ASSERT(IsValidJTypeImm(imm)); + EmitJType(m_buffer, static_cast<uint32_t>(imm), rd, 0b1101111); +} + +void Assembler::JALR(GPR rs) noexcept { + JALR(x1, 0, rs); +} + +void Assembler::JALR(GPR rd, int32_t imm, GPR rs1) noexcept { + BISCUIT_ASSERT(IsValidSigned12BitImm(imm)); + EmitIType(m_buffer, static_cast<uint32_t>(imm), rs1, 0b000, rd, 0b1100111); +} + +void Assembler::JR(GPR rs) noexcept { + JALR(x0, 0, rs); +} + +void Assembler::LB(GPR rd, int32_t imm, GPR rs) noexcept { + BISCUIT_ASSERT(IsValidSigned12BitImm(imm)); + EmitIType(m_buffer, static_cast<uint32_t>(imm), rs, 0b000, rd, 0b0000011); +} + +void Assembler::LBU(GPR rd, int32_t imm, GPR rs) noexcept { + BISCUIT_ASSERT(IsValidSigned12BitImm(imm)); + EmitIType(m_buffer, static_cast<uint32_t>(imm), rs, 0b100, rd, 0b0000011); +} + +void Assembler::LH(GPR rd, int32_t imm, GPR rs) noexcept { + BISCUIT_ASSERT(IsValidSigned12BitImm(imm)); + EmitIType(m_buffer, static_cast<uint32_t>(imm), rs, 0b001, rd, 0b0000011); +} + +void Assembler::LHU(GPR rd, int32_t imm, GPR rs) noexcept { + BISCUIT_ASSERT(IsValidSigned12BitImm(imm)); + EmitIType(m_buffer, static_cast<uint32_t>(imm), rs, 0b101, rd, 0b0000011); +} + +void Assembler::LI(GPR rd, uint64_t imm) noexcept { + if (IsRV32(m_features)) { + // Depending on imm, the following instructions are emitted. + // hi20 == 0 -> ADDI + // lo12 == 0 && hi20 != 0 -> LUI + // otherwise -> LUI+ADDI + + // Add 0x800 to cancel out the signed extension of ADDI. + const auto uimm32 = static_cast<uint32_t>(imm); + const auto hi20 = (uimm32 + 0x800) >> 12 & 0xFFFFF; + const auto lo12 = static_cast<int32_t>(uimm32) & 0xFFF; + GPR rs1 = zero; + + if (hi20 != 0) { + LUI(rd, hi20); + rs1 = rd; + } + + if (lo12 != 0 || hi20 == 0) { + ADDI(rd, rs1, lo12); + } + } else { + // For 64-bit imm, a sequence of up to 8 instructions (i.e. LUI+ADDIW+SLLI+ + // ADDI+SLLI+ADDI+SLLI+ADDI) is emitted. + // In the following, imm is processed from LSB to MSB while instruction emission + // is performed from MSB to LSB by calling LI() recursively. In each recursion, + // the lowest 12 bits are removed from imm and the optimal shift amount is + // calculated. Then, the remaining part of imm is processed recursively and + // LI() get called as soon as it fits into 32 bits. + + if (static_cast<uint64_t>(static_cast<int64_t>(imm << 32) >> 32) == imm) { + // Depending on imm, the following instructions are emitted. + // hi20 == 0 -> ADDIW + // lo12 == 0 && hi20 != 0 -> LUI + // otherwise -> LUI+ADDIW + + // Add 0x800 to cancel out the signed extension of ADDIW. + const auto hi20 = (static_cast<uint32_t>(imm) + 0x800) >> 12 & 0xFFFFF; + const auto lo12 = static_cast<int32_t>(imm) & 0xFFF; + GPR rs1 = zero; + + if (hi20 != 0) { + LUI(rd, hi20); + rs1 = rd; + } + + if (lo12 != 0 || hi20 == 0) { + ADDIW(rd, rs1, lo12); + } + return; + } + + const auto lo12 = static_cast<int32_t>(static_cast<int64_t>(imm << 52) >> 52); + // Add 0x800 to cancel out the signed extension of ADDI. + uint64_t hi52 = (imm + 0x800) >> 12; + const uint32_t shift = 12 + static_cast<uint32_t>(std::countr_zero(hi52)); + hi52 = static_cast<uint64_t>((static_cast<int64_t>(hi52 >> (shift - 12)) << shift) >> shift); + LI(rd, hi52); + SLLI(rd, rd, shift); + if (lo12 != 0) { + ADDI(rd, rd, lo12); + } + } +} + +void Assembler::LUI(GPR rd, uint32_t imm) noexcept { + EmitUType(m_buffer, imm, rd, 0b0110111); +} + +void Assembler::LW(GPR rd, int32_t imm, GPR rs) noexcept { + BISCUIT_ASSERT(IsValidSigned12BitImm(imm)); + EmitIType(m_buffer, static_cast<uint32_t>(imm), rs, 0b010, rd, 0b0000011); +} + +void Assembler::MV(GPR rd, GPR rs) noexcept { + ADDI(rd, rs, 0); +} + +void Assembler::NEG(GPR rd, GPR rs) noexcept { + SUB(rd, x0, rs); +} + +void Assembler::NOP() noexcept { + ADDI(x0, x0, 0); +} + +void Assembler::NOT(GPR rd, GPR rs) noexcept { + XORI(rd, rs, UINT32_MAX); +} + +void Assembler::OR(GPR rd, GPR lhs, GPR rhs) noexcept { + EmitRType(m_buffer, 0b0000000, rhs, lhs, 0b110, rd, 0b0110011); +} + +void Assembler::ORI(GPR rd, GPR rs, uint32_t imm) noexcept { + EmitIType(m_buffer, imm, rs, 0b110, rd, 0b0010011); +} + +void Assembler::PAUSE() noexcept { + m_buffer.Emit32(0x0100000F); +} + +void Assembler::RET() noexcept { + JALR(x0, 0, x1); +} + +void Assembler::SB(GPR rs2, int32_t imm, GPR rs1) noexcept { + BISCUIT_ASSERT(IsValidSigned12BitImm(imm)); + EmitSType(m_buffer, static_cast<uint32_t>(imm), rs2, rs1, 0b000, 0b0100011); +} + +void Assembler::SEQZ(GPR rd, GPR rs) noexcept { + SLTIU(rd, rs, 1); +} + +void Assembler::SGTZ(GPR rd, GPR rs) noexcept { + SLT(rd, x0, rs); +} + +void Assembler::SH(GPR rs2, int32_t imm, GPR rs1) noexcept { + BISCUIT_ASSERT(IsValidSigned12BitImm(imm)); + EmitSType(m_buffer, static_cast<uint32_t>(imm), rs2, rs1, 0b001, 0b0100011); +} + +void Assembler::SLL(GPR rd, GPR lhs, GPR rhs) noexcept { + EmitRType(m_buffer, 0b0000000, rhs, lhs, 0b001, rd, 0b0110011); +} + +void Assembler::SLLI(GPR rd, GPR rs, uint32_t shift) noexcept { + if (IsRV32(m_features)) { + BISCUIT_ASSERT(shift <= 31); + EmitIType(m_buffer, shift & 0x1F, rs, 0b001, rd, 0b0010011); + } else { + BISCUIT_ASSERT(shift <= 63); + EmitIType(m_buffer, shift & 0x3F, rs, 0b001, rd, 0b0010011); + } +} + +void Assembler::SLT(GPR rd, GPR lhs, GPR rhs) noexcept { + EmitRType(m_buffer, 0b0000000, rhs, lhs, 0b010, rd, 0b0110011); +} + +void Assembler::SLTI(GPR rd, GPR rs, int32_t imm) noexcept { + BISCUIT_ASSERT(IsValidSigned12BitImm(imm)); + EmitIType(m_buffer, static_cast<uint32_t>(imm), rs, 0b010, rd, 0b0010011); +} + +void Assembler::SLTIU(GPR rd, GPR rs, int32_t imm) noexcept { + BISCUIT_ASSERT(IsValidSigned12BitImm(imm)); + EmitIType(m_buffer, static_cast<uint32_t>(imm), rs, 0b011, rd, 0b0010011); +} + +void Assembler::SLTU(GPR rd, GPR lhs, GPR rhs) noexcept { + EmitRType(m_buffer, 0b0000000, rhs, lhs, 0b011, rd, 0b0110011); +} + +void Assembler::SLTZ(GPR rd, GPR rs) noexcept { + SLT(rd, rs, x0); +} + +void Assembler::SNEZ(GPR rd, GPR rs) noexcept { + SLTU(rd, x0, rs); +} + +void Assembler::SRA(GPR rd, GPR lhs, GPR rhs) noexcept { + EmitRType(m_buffer, 0b0100000, rhs, lhs, 0b101, rd, 0b0110011); +} + +void Assembler::SRAI(GPR rd, GPR rs, uint32_t shift) noexcept { + if (IsRV32(m_features)) { + BISCUIT_ASSERT(shift <= 31); + EmitIType(m_buffer, (0b0100000 << 5) | (shift & 0x1F), rs, 0b101, rd, 0b0010011); + } else { + BISCUIT_ASSERT(shift <= 63); + EmitIType(m_buffer, (0b0100000 << 5) | (shift & 0x3F), rs, 0b101, rd, 0b0010011); + } +} + +void Assembler::SRL(GPR rd, GPR lhs, GPR rhs) noexcept { + EmitRType(m_buffer, 0b0000000, rhs, lhs, 0b101, rd, 0b0110011); +} + +void Assembler::SRLI(GPR rd, GPR rs, uint32_t shift) noexcept { + if (IsRV32(m_features)) { + BISCUIT_ASSERT(shift <= 31); + EmitIType(m_buffer, shift & 0x1F, rs, 0b101, rd, 0b0010011); + } else { + BISCUIT_ASSERT(shift <= 63); + EmitIType(m_buffer, shift & 0x3F, rs, 0b101, rd, 0b0010011); + } +} + +void Assembler::SUB(GPR rd, GPR lhs, GPR rhs) noexcept { + EmitRType(m_buffer, 0b0100000, rhs, lhs, 0b000, rd, 0b0110011); +} + +void Assembler::SW(GPR rs2, int32_t imm, GPR rs1) noexcept { + BISCUIT_ASSERT(IsValidSigned12BitImm(imm)); + EmitSType(m_buffer, static_cast<uint32_t>(imm), rs2, rs1, 0b010, 0b0100011); +} + +void Assembler::XOR(GPR rd, GPR lhs, GPR rhs) noexcept { + EmitRType(m_buffer, 0b0000000, rhs, lhs, 0b100, rd, 0b0110011); +} + +void Assembler::XORI(GPR rd, GPR rs, uint32_t imm) noexcept { + EmitIType(m_buffer, imm, rs, 0b100, rd, 0b0010011); +} + +// RV64I Instructions + +void Assembler::ADDIW(GPR rd, GPR rs, int32_t imm) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + EmitIType(m_buffer, static_cast<uint32_t>(imm), rs, 0b000, rd, 0b0011011); +} + +void Assembler::ADDW(GPR rd, GPR lhs, GPR rhs) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + EmitRType(m_buffer, 0b0000000, rhs, lhs, 0b000, rd, 0b0111011); +} + +void Assembler::LD(GPR rd, int32_t imm, GPR rs) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + BISCUIT_ASSERT(IsValidSigned12BitImm(imm)); + EmitIType(m_buffer, static_cast<uint32_t>(imm), rs, 0b011, rd, 0b0000011); +} + +void Assembler::LWU(GPR rd, int32_t imm, GPR rs) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + BISCUIT_ASSERT(IsValidSigned12BitImm(imm)); + EmitIType(m_buffer, static_cast<uint32_t>(imm), rs, 0b110, rd, 0b0000011); +} + +void Assembler::SD(GPR rs2, int32_t imm, GPR rs1) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + BISCUIT_ASSERT(IsValidSigned12BitImm(imm)); + EmitSType(m_buffer, static_cast<uint32_t>(imm), rs2, rs1, 0b011, 0b0100011); +} + +void Assembler::SLLIW(GPR rd, GPR rs, uint32_t shift) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + BISCUIT_ASSERT(shift <= 31); + EmitIType(m_buffer, shift & 0x1F, rs, 0b001, rd, 0b0011011); +} +void Assembler::SRAIW(GPR rd, GPR rs, uint32_t shift) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + BISCUIT_ASSERT(shift <= 31); + EmitIType(m_buffer, (0b0100000 << 5) | (shift & 0x1F), rs, 0b101, rd, 0b0011011); +} +void Assembler::SRLIW(GPR rd, GPR rs, uint32_t shift) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + BISCUIT_ASSERT(shift <= 31); + EmitIType(m_buffer, shift & 0x1F, rs, 0b101, rd, 0b0011011); +} + +void Assembler::SLLW(GPR rd, GPR lhs, GPR rhs) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + EmitRType(m_buffer, 0b0000000, rhs, lhs, 0b001, rd, 0b0111011); +} +void Assembler::SRAW(GPR rd, GPR lhs, GPR rhs) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + EmitRType(m_buffer, 0b0100000, rhs, lhs, 0b101, rd, 0b0111011); +} +void Assembler::SRLW(GPR rd, GPR lhs, GPR rhs) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + EmitRType(m_buffer, 0b0000000, rhs, lhs, 0b101, rd, 0b0111011); +} + +void Assembler::SUBW(GPR rd, GPR lhs, GPR rhs) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + EmitRType(m_buffer, 0b0100000, rhs, lhs, 0b000, rd, 0b0111011); +} + +// Zawrs Extension Instructions + +void Assembler::WRS_NTO() noexcept { + EmitIType(m_buffer, 0b01101, x0, 0, x0, 0b1110011); +} +void Assembler::WRS_STO() noexcept { + EmitIType(m_buffer, 0b11101, x0, 0, x0, 0b1110011); +} + +// Zacas Extension Instructions + +void Assembler::AMOCAS_D(Ordering ordering, GPR rd, GPR rs2, GPR rs1) noexcept { + if (IsRV32(m_features)) { + BISCUIT_ASSERT((rd.Index() % 2) == 0); + BISCUIT_ASSERT((rs1.Index() % 2) == 0); + BISCUIT_ASSERT((rs2.Index() % 2) == 0); + } + EmitAtomic(m_buffer, 0b00101, ordering, rs2, rs1, 0b011, rd, 0b0101111); +} +void Assembler::AMOCAS_Q(Ordering ordering, GPR rd, GPR rs2, GPR rs1) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + + // Both rd and rs2 indicate a register pair, so they need to be even-numbered. + BISCUIT_ASSERT((rd.Index() % 2) == 0); + BISCUIT_ASSERT((rs1.Index() % 2) == 0); + BISCUIT_ASSERT((rs2.Index() % 2) == 0); + EmitAtomic(m_buffer, 0b00101, ordering, rs2, rs1, 0b100, rd, 0b0101111); +} +void Assembler::AMOCAS_W(Ordering ordering, GPR rd, GPR rs2, GPR rs1) noexcept { + EmitAtomic(m_buffer, 0b00101, ordering, rs2, rs1, 0b010, rd, 0b0101111); +} + +// Zicond Extension Instructions + +void Assembler::CZERO_EQZ(GPR rd, GPR value, GPR condition) noexcept { + EmitRType(m_buffer, 0b0000111, condition, value, 0b101, rd, 0b0110011); +} +void Assembler::CZERO_NEZ(GPR rd, GPR value, GPR condition) noexcept { + EmitRType(m_buffer, 0b0000111, condition, value, 0b111, rd, 0b0110011); +} + +// Zicsr Extension Instructions + +void Assembler::CSRRC(GPR rd, CSR csr, GPR rs) noexcept { + EmitIType(m_buffer, static_cast<uint32_t>(csr), rs, 0b011, rd, 0b1110011); +} +void Assembler::CSRRCI(GPR rd, CSR csr, uint32_t imm) noexcept { + BISCUIT_ASSERT(imm <= 0x1F); + EmitIType(m_buffer, static_cast<uint32_t>(csr), GPR{imm & 0x1F}, 0b111, rd, 0b1110011); +} +void Assembler::CSRRS(GPR rd, CSR csr, GPR rs) noexcept { + EmitIType(m_buffer, static_cast<uint32_t>(csr), rs, 0b010, rd, 0b1110011); +} +void Assembler::CSRRSI(GPR rd, CSR csr, uint32_t imm) noexcept { + BISCUIT_ASSERT(imm <= 0x1F); + EmitIType(m_buffer, static_cast<uint32_t>(csr), GPR{imm & 0x1F}, 0b110, rd, 0b1110011); +} +void Assembler::CSRRW(GPR rd, CSR csr, GPR rs) noexcept { + EmitIType(m_buffer, static_cast<uint32_t>(csr), rs, 0b001, rd, 0b1110011); +} +void Assembler::CSRRWI(GPR rd, CSR csr, uint32_t imm) noexcept { + BISCUIT_ASSERT(imm <= 0x1F); + EmitIType(m_buffer, static_cast<uint32_t>(csr), GPR{imm & 0x1F}, 0b101, rd, 0b1110011); +} + +void Assembler::CSRR(GPR rd, CSR csr) noexcept { + CSRRS(rd, csr, x0); +} +void Assembler::CSWR(CSR csr, GPR rs) noexcept { + CSRRW(x0, csr, rs); +} + +void Assembler::CSRS(CSR csr, GPR rs) noexcept { + CSRRS(x0, csr, rs); +} +void Assembler::CSRC(CSR csr, GPR rs) noexcept { + CSRRC(x0, csr, rs); +} + +void Assembler::CSRCI(CSR csr, uint32_t imm) noexcept { + CSRRCI(x0, csr, imm); +} +void Assembler::CSRSI(CSR csr, uint32_t imm) noexcept { + CSRRSI(x0, csr, imm); +} +void Assembler::CSRWI(CSR csr, uint32_t imm) noexcept { + CSRRWI(x0, csr, imm); +} + +void Assembler::FRCSR(GPR rd) noexcept { + CSRRS(rd, CSR::FCSR, x0); +} +void Assembler::FSCSR(GPR rd, GPR rs) noexcept { + CSRRW(rd, CSR::FCSR, rs); +} +void Assembler::FSCSR(GPR rs) noexcept { + CSRRW(x0, CSR::FCSR, rs); +} + +void Assembler::FRRM(GPR rd) noexcept { + CSRRS(rd, CSR::FRM, x0); +} +void Assembler::FSRM(GPR rd, GPR rs) noexcept { + CSRRW(rd, CSR::FRM, rs); +} +void Assembler::FSRM(GPR rs) noexcept { + CSRRW(x0, CSR::FRM, rs); +} + +void Assembler::FSRMI(GPR rd, uint32_t imm) noexcept { + CSRRWI(rd, CSR::FRM, imm); +} +void Assembler::FSRMI(uint32_t imm) noexcept { + CSRRWI(x0, CSR::FRM, imm); +} + +void Assembler::FRFLAGS(GPR rd) noexcept { + CSRRS(rd, CSR::FFlags, x0); +} +void Assembler::FSFLAGS(GPR rd, GPR rs) noexcept { + CSRRW(rd, CSR::FFlags, rs); +} +void Assembler::FSFLAGS(GPR rs) noexcept { + CSRRW(x0, CSR::FFlags, rs); +} + +void Assembler::FSFLAGSI(GPR rd, uint32_t imm) noexcept { + CSRRWI(rd, CSR::FFlags, imm); +} +void Assembler::FSFLAGSI(uint32_t imm) noexcept { + CSRRWI(x0, CSR::FFlags, imm); +} + +void Assembler::RDCYCLE(GPR rd) noexcept { + CSRRS(rd, CSR::Cycle, x0); +} +void Assembler::RDCYCLEH(GPR rd) noexcept { + CSRRS(rd, CSR::CycleH, x0); +} + +void Assembler::RDINSTRET(GPR rd) noexcept { + CSRRS(rd, CSR::InstRet, x0); +} +void Assembler::RDINSTRETH(GPR rd) noexcept { + CSRRS(rd, CSR::InstRetH, x0); +} + +void Assembler::RDTIME(GPR rd) noexcept { + CSRRS(rd, CSR::Time, x0); +} +void Assembler::RDTIMEH(GPR rd) noexcept { + CSRRS(rd, CSR::TimeH, x0); +} + +// Zihintntl Extension Instructions + +void Assembler::C_NTL_ALL() noexcept { + C_ADD(x0, x5); +} +void Assembler::C_NTL_S1() noexcept { + C_ADD(x0, x4); +} +void Assembler::C_NTL_P1() noexcept { + C_ADD(x0, x2); +} +void Assembler::C_NTL_PALL() noexcept { + C_ADD(x0, x3); +} +void Assembler::NTL_ALL() noexcept { + ADD(x0, x0, x5); +} +void Assembler::NTL_S1() noexcept { + ADD(x0, x0, x4); +} +void Assembler::NTL_P1() noexcept { + ADD(x0, x0, x2); +} +void Assembler::NTL_PALL() noexcept { + ADD(x0, x0, x3); +} + +// RV32M Extension Instructions + +void Assembler::DIV(GPR rd, GPR rs1, GPR rs2) noexcept { + EmitRType(m_buffer, 0b0000001, rs2, rs1, 0b100, rd, 0b0110011); +} +void Assembler::DIVU(GPR rd, GPR rs1, GPR rs2) noexcept { + EmitRType(m_buffer, 0b0000001, rs2, rs1, 0b101, rd, 0b0110011); +} +void Assembler::MUL(GPR rd, GPR rs1, GPR rs2) noexcept { + EmitRType(m_buffer, 0b0000001, rs2, rs1, 0b000, rd, 0b0110011); +} +void Assembler::MULH(GPR rd, GPR rs1, GPR rs2) noexcept { + EmitRType(m_buffer, 0b0000001, rs2, rs1, 0b001, rd, 0b0110011); +} +void Assembler::MULHSU(GPR rd, GPR rs1, GPR rs2) noexcept { + EmitRType(m_buffer, 0b0000001, rs2, rs1, 0b010, rd, 0b0110011); +} +void Assembler::MULHU(GPR rd, GPR rs1, GPR rs2) noexcept { + EmitRType(m_buffer, 0b0000001, rs2, rs1, 0b011, rd, 0b0110011); +} +void Assembler::REM(GPR rd, GPR rs1, GPR rs2) noexcept { + EmitRType(m_buffer, 0b0000001, rs2, rs1, 0b110, rd, 0b0110011); +} +void Assembler::REMU(GPR rd, GPR rs1, GPR rs2) noexcept { + EmitRType(m_buffer, 0b0000001, rs2, rs1, 0b111, rd, 0b0110011); +} + +// RV64M Extension Instructions + +void Assembler::DIVW(GPR rd, GPR rs1, GPR rs2) noexcept { + EmitRType(m_buffer, 0b0000001, rs2, rs1, 0b100, rd, 0b0111011); +} +void Assembler::DIVUW(GPR rd, GPR rs1, GPR rs2) noexcept { + EmitRType(m_buffer, 0b0000001, rs2, rs1, 0b101, rd, 0b0111011); +} +void Assembler::MULW(GPR rd, GPR rs1, GPR rs2) noexcept { + EmitRType(m_buffer, 0b0000001, rs2, rs1, 0b000, rd, 0b0111011); +} +void Assembler::REMW(GPR rd, GPR rs1, GPR rs2) noexcept { + EmitRType(m_buffer, 0b0000001, rs2, rs1, 0b110, rd, 0b0111011); +} +void Assembler::REMUW(GPR rd, GPR rs1, GPR rs2) noexcept { + EmitRType(m_buffer, 0b0000001, rs2, rs1, 0b111, rd, 0b0111011); +} + +// RV32A Extension Instructions + +void Assembler::AMOADD_W(Ordering ordering, GPR rd, GPR rs2, GPR rs1) noexcept { + EmitAtomic(m_buffer, 0b00000, ordering, rs2, rs1, 0b010, rd, 0b0101111); +} +void Assembler::AMOAND_W(Ordering ordering, GPR rd, GPR rs2, GPR rs1) noexcept { + EmitAtomic(m_buffer, 0b01100, ordering, rs2, rs1, 0b010, rd, 0b0101111); +} +void Assembler::AMOMAX_W(Ordering ordering, GPR rd, GPR rs2, GPR rs1) noexcept { + EmitAtomic(m_buffer, 0b10100, ordering, rs2, rs1, 0b010, rd, 0b0101111); +} +void Assembler::AMOMAXU_W(Ordering ordering, GPR rd, GPR rs2, GPR rs1) noexcept { + EmitAtomic(m_buffer, 0b11100, ordering, rs2, rs1, 0b010, rd, 0b0101111); +} +void Assembler::AMOMIN_W(Ordering ordering, GPR rd, GPR rs2, GPR rs1) noexcept { + EmitAtomic(m_buffer, 0b10000, ordering, rs2, rs1, 0b010, rd, 0b0101111); +} +void Assembler::AMOMINU_W(Ordering ordering, GPR rd, GPR rs2, GPR rs1) noexcept { + EmitAtomic(m_buffer, 0b11000, ordering, rs2, rs1, 0b010, rd, 0b0101111); +} +void Assembler::AMOOR_W(Ordering ordering, GPR rd, GPR rs2, GPR rs1) noexcept { + EmitAtomic(m_buffer, 0b01000, ordering, rs2, rs1, 0b010, rd, 0b0101111); +} +void Assembler::AMOSWAP_W(Ordering ordering, GPR rd, GPR rs2, GPR rs1) noexcept { + EmitAtomic(m_buffer, 0b00001, ordering, rs2, rs1, 0b010, rd, 0b0101111); +} +void Assembler::AMOXOR_W(Ordering ordering, GPR rd, GPR rs2, GPR rs1) noexcept { + EmitAtomic(m_buffer, 0b00100, ordering, rs2, rs1, 0b010, rd, 0b0101111); +} +void Assembler::LR_W(Ordering ordering, GPR rd, GPR rs) noexcept { + EmitAtomic(m_buffer, 0b00010, ordering, x0, rs, 0b010, rd, 0b0101111); +} +void Assembler::SC_W(Ordering ordering, GPR rd, GPR rs2, GPR rs1) noexcept { + EmitAtomic(m_buffer, 0b00011, ordering, rs2, rs1, 0b010, rd, 0b0101111); +} + +// RV64A Extension Instructions + +void Assembler::AMOADD_D(Ordering ordering, GPR rd, GPR rs2, GPR rs1) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + EmitAtomic(m_buffer, 0b00000, ordering, rs2, rs1, 0b011, rd, 0b0101111); +} +void Assembler::AMOAND_D(Ordering ordering, GPR rd, GPR rs2, GPR rs1) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + EmitAtomic(m_buffer, 0b01100, ordering, rs2, rs1, 0b011, rd, 0b0101111); +} +void Assembler::AMOMAX_D(Ordering ordering, GPR rd, GPR rs2, GPR rs1) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + EmitAtomic(m_buffer, 0b10100, ordering, rs2, rs1, 0b011, rd, 0b0101111); +} +void Assembler::AMOMAXU_D(Ordering ordering, GPR rd, GPR rs2, GPR rs1) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + EmitAtomic(m_buffer, 0b11100, ordering, rs2, rs1, 0b011, rd, 0b0101111); +} +void Assembler::AMOMIN_D(Ordering ordering, GPR rd, GPR rs2, GPR rs1) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + EmitAtomic(m_buffer, 0b10000, ordering, rs2, rs1, 0b011, rd, 0b0101111); +} +void Assembler::AMOMINU_D(Ordering ordering, GPR rd, GPR rs2, GPR rs1) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + EmitAtomic(m_buffer, 0b11000, ordering, rs2, rs1, 0b011, rd, 0b0101111); +} +void Assembler::AMOOR_D(Ordering ordering, GPR rd, GPR rs2, GPR rs1) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + EmitAtomic(m_buffer, 0b01000, ordering, rs2, rs1, 0b011, rd, 0b0101111); +} +void Assembler::AMOSWAP_D(Ordering ordering, GPR rd, GPR rs2, GPR rs1) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + EmitAtomic(m_buffer, 0b00001, ordering, rs2, rs1, 0b011, rd, 0b0101111); +} +void Assembler::AMOXOR_D(Ordering ordering, GPR rd, GPR rs2, GPR rs1) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + EmitAtomic(m_buffer, 0b00100, ordering, rs2, rs1, 0b011, rd, 0b0101111); +} +void Assembler::LR_D(Ordering ordering, GPR rd, GPR rs) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + EmitAtomic(m_buffer, 0b00010, ordering, x0, rs, 0b011, rd, 0b0101111); +} +void Assembler::SC_D(Ordering ordering, GPR rd, GPR rs2, GPR rs1) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + EmitAtomic(m_buffer, 0b00011, ordering, rs2, rs1, 0b011, rd, 0b0101111); +} + +// RVB Extension Instructions + +void Assembler::ADDUW(GPR rd, GPR rs1, GPR rs2) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + EmitRType(m_buffer, 0b0000100, rs2, rs1, 0b000, rd, 0b0111011); +} + +void Assembler::ANDN(GPR rd, GPR rs1, GPR rs2) noexcept { + EmitRType(m_buffer, 0b0100000, rs2, rs1, 0b111, rd, 0b0110011); +} + +void Assembler::BCLR(GPR rd, GPR rs1, GPR rs2) noexcept { + EmitRType(m_buffer, 0b0100100, rs2, rs1, 0b001, rd, 0b0110011); +} + +void Assembler::BCLRI(GPR rd, GPR rs, uint32_t bit) noexcept { + if (IsRV32(m_features)) { + BISCUIT_ASSERT(bit <= 31); + } else { + BISCUIT_ASSERT(bit <= 63); + } + + const auto imm = (0b010010U << 6) | bit; + EmitIType(m_buffer, imm, rs, 0b001, rd, 0b0010011); +} + +void Assembler::BEXT(GPR rd, GPR rs1, GPR rs2) noexcept { + EmitRType(m_buffer, 0b0100100, rs2, rs1, 0b101, rd, 0b0110011); +} + +void Assembler::BEXTI(GPR rd, GPR rs, uint32_t bit) noexcept { + if (IsRV32(m_features)) { + BISCUIT_ASSERT(bit <= 31); + } else { + BISCUIT_ASSERT(bit <= 63); + } + + const auto imm = (0b010010U << 6) | bit; + EmitIType(m_buffer, imm, rs, 0b101, rd, 0b0010011); +} + +void Assembler::BINV(GPR rd, GPR rs1, GPR rs2) noexcept { + EmitRType(m_buffer, 0b0110100, rs2, rs1, 0b001, rd, 0b0110011); +} + +void Assembler::BINVI(GPR rd, GPR rs, uint32_t bit) noexcept { + if (IsRV32(m_features)) { + BISCUIT_ASSERT(bit <= 31); + } else { + BISCUIT_ASSERT(bit <= 63); + } + + const auto imm = (0b011010U << 6) | bit; + EmitIType(m_buffer, imm, rs, 0b001, rd, 0b0010011); +} + +void Assembler::BREV8(GPR rd, GPR rs) noexcept { + EmitIType(m_buffer, 0b011010000111, rs, 0b101, rd, 0b0010011); +} + +void Assembler::BSET(GPR rd, GPR rs1, GPR rs2) noexcept { + EmitRType(m_buffer, 0b0010100, rs2, rs1, 0b001, rd, 0b0110011); +} + +void Assembler::BSETI(GPR rd, GPR rs, uint32_t bit) noexcept { + if (IsRV32(m_features)) { + BISCUIT_ASSERT(bit <= 31); + } else { + BISCUIT_ASSERT(bit <= 63); + } + + const auto imm = (0b001010U << 6) | bit; + EmitIType(m_buffer, imm, rs, 0b001, rd, 0b0110011); +} + +void Assembler::CLMUL(GPR rd, GPR rs1, GPR rs2) noexcept { + EmitRType(m_buffer, 0b0000101, rs2, rs1, 0b001, rd, 0b0110011); +} + +void Assembler::CLMULH(GPR rd, GPR rs1, GPR rs2) noexcept { + EmitRType(m_buffer, 0b0000101, rs2, rs1, 0b011, rd, 0b0110011); +} + +void Assembler::CLMULR(GPR rd, GPR rs1, GPR rs2) noexcept { + EmitRType(m_buffer, 0b0000101, rs2, rs1, 0b010, rd, 0b0110011); +} + +void Assembler::CLZ(GPR rd, GPR rs) noexcept { + EmitIType(m_buffer, 0b011000000000, rs, 0b001, rd, 0b0010011); +} + +void Assembler::CLZW(GPR rd, GPR rs) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + EmitIType(m_buffer, 0b011000000000, rs, 0b001, rd, 0b0011011); +} + +void Assembler::CPOP(GPR rd, GPR rs) noexcept { + EmitIType(m_buffer, 0b011000000010, rs, 0b001, rd, 0b0010011); +} + +void Assembler::CPOPW(GPR rd, GPR rs) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + EmitIType(m_buffer, 0b011000000010, rs, 0b001, rd, 0b0011011); +} + +void Assembler::CTZ(GPR rd, GPR rs) noexcept { + EmitIType(m_buffer, 0b011000000001, rs, 0b001, rd, 0b0010011); +} + +void Assembler::CTZW(GPR rd, GPR rs) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + EmitIType(m_buffer, 0b011000000001, rs, 0b001, rd, 0b0011011); +} + +void Assembler::MAX(GPR rd, GPR rs1, GPR rs2) noexcept { + EmitRType(m_buffer, 0b0000101, rs2, rs1, 0b110, rd, 0b0110011); +} + +void Assembler::MAXU(GPR rd, GPR rs1, GPR rs2) noexcept { + EmitRType(m_buffer, 0b0000101, rs2, rs1, 0b111, rd, 0b0110011); +} + +void Assembler::MIN(GPR rd, GPR rs1, GPR rs2) noexcept { + EmitRType(m_buffer, 0b0000101, rs2, rs1, 0b100, rd, 0b0110011); +} + +void Assembler::MINU(GPR rd, GPR rs1, GPR rs2) noexcept { + EmitRType(m_buffer, 0b0000101, rs2, rs1, 0b101, rd, 0b0110011); +} + +void Assembler::ORCB(GPR rd, GPR rs) noexcept { + EmitIType(m_buffer, 0b001010000111, rs, 0b101, rd, 0b0010011); +} + +void Assembler::ORN(GPR rd, GPR rs1, GPR rs2) noexcept { + EmitRType(m_buffer, 0b0100000, rs2, rs1, 0b110, rd, 0b0110011); +} + +void Assembler::PACK(GPR rd, GPR rs1, GPR rs2) noexcept { + EmitRType(m_buffer, 0b0000100, rs2, rs1, 0b100, rd, 0b0110011); +} + +void Assembler::PACKH(GPR rd, GPR rs1, GPR rs2) noexcept { + EmitRType(m_buffer, 0b0000100, rs2, rs1, 0b111, rd, 0b0110011); +} + +void Assembler::PACKW(GPR rd, GPR rs1, GPR rs2) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + EmitRType(m_buffer, 0b0000100, rs2, rs1, 0b100, rd, 0b0111011); +} + +void Assembler::REV8(GPR rd, GPR rs) noexcept { + if (IsRV32(m_features)) { + EmitIType(m_buffer, 0b011010011000, rs, 0b101, rd, 0b0010011); + } else { + EmitIType(m_buffer, 0b011010111000, rs, 0b101, rd, 0b0010011); + } +} + +void Assembler::ROL(GPR rd, GPR rs1, GPR rs2) noexcept { + EmitRType(m_buffer, 0b0110000, rs2, rs1, 0b001, rd, 0b0110011); +} + +void Assembler::ROLW(GPR rd, GPR rs1, GPR rs2) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + EmitRType(m_buffer, 0b0110000, rs2, rs1, 0b001, rd, 0b0111011); +} + +void Assembler::ROR(GPR rd, GPR rs1, GPR rs2) noexcept { + EmitRType(m_buffer, 0b0110000, rs2, rs1, 0b101, rd, 0b0110011); +} + +void Assembler::RORI(GPR rd, GPR rs, uint32_t rotate_amount) noexcept { + BISCUIT_ASSERT(rotate_amount <= 63); + const auto imm = (0b011000U << 6) | rotate_amount; + EmitIType(m_buffer, imm, rs, 0b101, rd, 0b0010011); +} + +void Assembler::RORIW(GPR rd, GPR rs, uint32_t rotate_amount) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + BISCUIT_ASSERT(rotate_amount <= 63); + const auto imm = (0b011000U << 6) | rotate_amount; + EmitIType(m_buffer, imm, rs, 0b101, rd, 0b0011011); +} + +void Assembler::RORW(GPR rd, GPR rs1, GPR rs2) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + EmitRType(m_buffer, 0b0110000, rs2, rs1, 0b101, rd, 0b0111011); +} + +void Assembler::SEXTB(GPR rd, GPR rs) noexcept { + EmitIType(m_buffer, 0b011000000100, rs, 0b001, rd, 0b0010011); +} + +void Assembler::SEXTH(GPR rd, GPR rs) noexcept { + EmitIType(m_buffer, 0b011000000101, rs, 0b001, rd, 0b0010011); +} + +void Assembler::SH1ADD(GPR rd, GPR rs1, GPR rs2) noexcept { + EmitRType(m_buffer, 0b0010000, rs2, rs1, 0b010, rd, 0b0110011); +} + +void Assembler::SH1ADDUW(GPR rd, GPR rs1, GPR rs2) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + EmitRType(m_buffer, 0b0010000, rs2, rs1, 0b010, rd, 0b0111011); +} + +void Assembler::SH2ADD(GPR rd, GPR rs1, GPR rs2) noexcept { + EmitRType(m_buffer, 0b0010000, rs2, rs1, 0b100, rd, 0b0110011); +} + +void Assembler::SH2ADDUW(GPR rd, GPR rs1, GPR rs2) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + EmitRType(m_buffer, 0b0010000, rs2, rs1, 0b100, rd, 0b0111011); +} + +void Assembler::SH3ADD(GPR rd, GPR rs1, GPR rs2) noexcept { + EmitRType(m_buffer, 0b0010000, rs2, rs1, 0b110, rd, 0b0110011); +} + +void Assembler::SH3ADDUW(GPR rd, GPR rs1, GPR rs2) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + EmitRType(m_buffer, 0b0010000, rs2, rs1, 0b110, rd, 0b0111011); +} + +void Assembler::SLLIUW(GPR rd, GPR rs, uint32_t shift_amount) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + BISCUIT_ASSERT(shift_amount <= 63); + const auto imm = (0b000010U << 6) | shift_amount; + EmitIType(m_buffer, imm, rs, 0b001, rd, 0b0011011); +} + +void Assembler::UNZIP(GPR rd, GPR rs) noexcept { + BISCUIT_ASSERT(IsRV32(m_features)); + EmitIType(m_buffer, 0b000010011111, rs, 0b101, rd, 0b0010011); +} + +void Assembler::XNOR(GPR rd, GPR rs1, GPR rs2) noexcept { + EmitRType(m_buffer, 0b0100000, rs2, rs1, 0b100, rd, 0b0110011); +} + +void Assembler::XPERM4(GPR rd, GPR rs1, GPR rs2) noexcept { + EmitRType(m_buffer, 0b0010100, rs2, rs1, 0b010, rd, 0b0110011); +} + +void Assembler::XPERM8(GPR rd, GPR rs1, GPR rs2) noexcept { + EmitRType(m_buffer, 0b0010100, rs2, rs1, 0b100, rd, 0b0110011); +} + +void Assembler::ZEXTH(GPR rd, GPR rs) noexcept { + if (IsRV32(m_features)) { + EmitIType(m_buffer, 0b000010000000, rs, 0b100, rd, 0b0110011); + } else { + EmitIType(m_buffer, 0b000010000000, rs, 0b100, rd, 0b0111011); + } +} + +void Assembler::ZEXTW(GPR rd, GPR rs) noexcept { + ADDUW(rd, rs, x0); +} + +void Assembler::ZIP(GPR rd, GPR rs) noexcept { + BISCUIT_ASSERT(IsRV32(m_features)); + EmitIType(m_buffer, 0b000010011110, rs, 0b001, rd, 0b0010011); +} + +// Cache Management Operation Extension Instructions + +void Assembler::CBO_CLEAN(GPR rs) noexcept { + EmitRType(m_buffer, 0b0000000, x1, rs, 0b010, x0, 0b0001111); +} + +void Assembler::CBO_FLUSH(GPR rs) noexcept { + EmitRType(m_buffer, 0b0000000, x2, rs, 0b010, x0, 0b0001111); +} + +void Assembler::CBO_INVAL(GPR rs) noexcept { + EmitRType(m_buffer, 0b0000000, x0, rs, 0b010, x0, 0b0001111); +} + +void Assembler::CBO_ZERO(GPR rs) noexcept { + EmitRType(m_buffer, 0b0000000, x4, rs, 0b010, x0, 0b0001111); +} + +void Assembler::PREFETCH_I(GPR rs, int32_t offset) noexcept { + // Offset must be able to fit in a 12-bit signed immediate and be + // cleanly divisible by 32 since the bottom 5 bits are encoded as zero. + BISCUIT_ASSERT(IsValidSigned12BitImm(offset)); + BISCUIT_ASSERT(offset % 32 == 0); + EmitIType(m_buffer, static_cast<uint32_t>(offset), rs, 0b110, x0, 0b0010011); +} + +void Assembler::PREFETCH_R(GPR rs, int32_t offset) noexcept { + // Offset must be able to fit in a 12-bit signed immediate and be + // cleanly divisible by 32 since the bottom 5 bits are encoded as zero. + BISCUIT_ASSERT(IsValidSigned12BitImm(offset)); + BISCUIT_ASSERT(offset % 32 == 0); + EmitIType(m_buffer, static_cast<uint32_t>(offset) | 0b01, rs, 0b110, x0, 0b0010011); +} + +void Assembler::PREFETCH_W(GPR rs, int32_t offset) noexcept { + // Offset must be able to fit in a 12-bit signed immediate and be + // cleanly divisible by 32 since the bottom 5 bits are encoded as zero. + BISCUIT_ASSERT(IsValidSigned12BitImm(offset)); + BISCUIT_ASSERT(offset % 32 == 0); + EmitIType(m_buffer, static_cast<uint32_t>(offset) | 0b11, rs, 0b110, x0, 0b0010011); +} + +// Privileged Instructions + +void Assembler::HFENCE_GVMA(GPR rs1, GPR rs2) noexcept { + EmitRType(m_buffer, 0b0110001, rs2, rs1, 0b000, x0, 0b1110011); +} + +void Assembler::HFENCE_VVMA(GPR rs1, GPR rs2) noexcept { + EmitRType(m_buffer, 0b0010001, rs2, rs1, 0b000, x0, 0b1110011); +} + +void Assembler::HINVAL_GVMA(GPR rs1, GPR rs2) noexcept { + EmitRType(m_buffer, 0b0110011, rs2, rs1, 0b000, x0, 0b1110011); +} + +void Assembler::HINVAL_VVMA(GPR rs1, GPR rs2) noexcept { + EmitRType(m_buffer, 0b0010011, rs2, rs1, 0b000, x0, 0b1110011); +} + +void Assembler::HLV_B(GPR rd, GPR rs) noexcept { + EmitRType(m_buffer, 0b0110000, x0, rs, 0b100, rd, 0b1110011); +} + +void Assembler::HLV_BU(GPR rd, GPR rs) noexcept { + EmitRType(m_buffer, 0b0110000, x1, rs, 0b100, rd, 0b1110011); +} + +void Assembler::HLV_D(GPR rd, GPR rs) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + EmitRType(m_buffer, 0b0110110, x0, rs, 0b100, rd, 0b1110011); +} + +void Assembler::HLV_H(GPR rd, GPR rs) noexcept { + EmitRType(m_buffer, 0b0110010, x0, rs, 0b100, rd, 0b1110011); +} + +void Assembler::HLV_HU(GPR rd, GPR rs) noexcept { + EmitRType(m_buffer, 0b0110010, x1, rs, 0b100, rd, 0b1110011); +} + +void Assembler::HLV_W(GPR rd, GPR rs) noexcept { + EmitRType(m_buffer, 0b0110100, x0, rs, 0b100, rd, 0b1110011); +} + +void Assembler::HLV_WU(GPR rd, GPR rs) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + EmitRType(m_buffer, 0b0110100, x1, rs, 0b100, rd, 0b1110011); +} + +void Assembler::HLVX_HU(GPR rd, GPR rs) noexcept { + EmitRType(m_buffer, 0b0110010, x3, rs, 0b100, rd, 0b1110011); +} + +void Assembler::HLVX_WU(GPR rd, GPR rs) noexcept { + EmitRType(m_buffer, 0b0110100, x3, rs, 0b100, rd, 0b1110011); +} + +void Assembler::HSV_B(GPR rs2, GPR rs1) noexcept { + EmitRType(m_buffer, 0b0110001, rs2, rs1, 0b100, x0, 0b1110011); +} + +void Assembler::HSV_D(GPR rs2, GPR rs1) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + EmitRType(m_buffer, 0b0110111, rs2, rs1, 0b100, x0, 0b1110011); +} + +void Assembler::HSV_H(GPR rs2, GPR rs1) noexcept { + EmitRType(m_buffer, 0b0110011, rs2, rs1, 0b100, x0, 0b1110011); +} + +void Assembler::HSV_W(GPR rs2, GPR rs1) noexcept { + EmitRType(m_buffer, 0b0110101, rs2, rs1, 0b100, x0, 0b1110011); +} + +void Assembler::MRET() noexcept { + m_buffer.Emit32(0x30200073); +} + +void Assembler::SFENCE_INVAL_IR() noexcept { + m_buffer.Emit32(0x18100073U); +} + +void Assembler::SFENCE_VMA(GPR rs1, GPR rs2) noexcept { + EmitRType(m_buffer, 0b0001001, rs2, rs1, 0b000, x0, 0b1110011); +} + +void Assembler::SFENCE_W_INVAL() noexcept { + m_buffer.Emit32(0x18000073U); +} + +void Assembler::SINVAL_VMA(GPR rs1, GPR rs2) noexcept { + EmitRType(m_buffer, 0b0001011, rs2, rs1, 0b000, x0, 0b1110011); +} + +void Assembler::SRET() noexcept { + m_buffer.Emit32(0x10200073); +} + +void Assembler::URET() noexcept { + m_buffer.Emit32(0x00200073); +} + +void Assembler::WFI() noexcept { + m_buffer.Emit32(0x10500073); +} + +void Assembler::BindToOffset(Label* label, Label::LocationOffset offset) { + BISCUIT_ASSERT(label != nullptr); + BISCUIT_ASSERT(offset >= 0 && offset <= m_buffer.GetCursorOffset()); + + label->Bind(offset); + ResolveLabelOffsets(label); + label->ClearOffsets(); +} + +ptrdiff_t Assembler::LinkAndGetOffset(Label* label) { + BISCUIT_ASSERT(label != nullptr); + + // If we have a bound label, then it's straightforward to calculate + // the offsets. + if (label->IsBound()) { + const auto cursor_address = m_buffer.GetCursorAddress(); + const auto label_offset = m_buffer.GetOffsetAddress(*label->GetLocation()); + return static_cast<ptrdiff_t>(label_offset - cursor_address); + } + + // If we don't have a bound location, we return an offset of zero. + // While the emitter will emit a bogus branch instruction initially, + // the offset will be patched over once the label has been properly + // bound to a location. + label->AddOffset(m_buffer.GetCursorOffset()); + return 0; +} + +void Assembler::ResolveLabelOffsets(Label* label) { + // Conditional branch instructions make use of the B-type immediate encoding for offsets. + const auto is_b_type = [](uint32_t instruction) { + return (instruction & 0x7F) == 0b1100011; + }; + // JAL makes use of the J-type immediate encoding for offsets. + const auto is_j_type = [](uint32_t instruction) { + return (instruction & 0x7F) == 0b1101111; + }; + // C.BEQZ and C.BNEZ make use of this encoding type. + const auto is_cb_type = [](uint32_t instruction) { + const auto op = instruction & 0b11; + const auto funct3 = instruction & 0xE000; + return op == 0b01 && funct3 >= 0xC000; + }; + // C.JAL and C.J make use of this encoding type. + const auto is_cj_type = [](uint32_t instruction) { + const auto op = instruction & 0b11; + const auto funct3 = instruction & 0xE000; + return op == 0b01 && (funct3 == 0x2000 || funct3 == 0xA000); + }; + // If we know an instruction is a compressed branch, then it's a 16-bit instruction + // Otherwise it's a regular-sized 32-bit instruction. + const auto determine_inst_size = [&](uint32_t instruction) -> size_t { + if (is_cj_type(instruction) || is_cb_type(instruction)) { + return 2; + } else { + return 4; + } + }; + + const auto label_location = *label->GetLocation(); + + for (const auto offset : label->m_offsets) { + const auto address = m_buffer.GetOffsetAddress(offset); + auto* const ptr = reinterpret_cast<uint8_t*>(address); + const auto inst_size = determine_inst_size(uint32_t{*ptr} | (uint32_t{*(ptr + 1)} << 8)); + + uint32_t instruction = 0; + std::memcpy(&instruction, ptr, inst_size); + + // Given all branch instructions we need to patch have 0 encoded as + // their branch offset, we don't need to worry about any masking work. + // + // It's enough to verify that the immediate is going to be valid + // and then OR it into the instruction. + + const auto encoded_offset = label_location - offset; + + if (inst_size == sizeof(uint32_t)) { + if (is_b_type(instruction)) { + BISCUIT_ASSERT(IsValidBTypeImm(encoded_offset)); + instruction |= TransformToBTypeImm(static_cast<uint32_t>(encoded_offset)); + } else if (is_j_type(instruction)) { + BISCUIT_ASSERT(IsValidJTypeImm(encoded_offset)); + instruction |= TransformToJTypeImm(static_cast<uint32_t>(encoded_offset)); + } + } else { + if (is_cb_type(instruction)) { + BISCUIT_ASSERT(IsValidCBTypeImm(encoded_offset)); + instruction |= TransformToCBTypeImm(static_cast<uint32_t>(encoded_offset)); + } else if (is_cj_type(instruction)) { + BISCUIT_ASSERT(IsValidCJTypeImm(encoded_offset)); + instruction |= TransformToCJTypeImm(static_cast<uint32_t>(encoded_offset)); + } + } + + std::memcpy(ptr, &instruction, inst_size); + } +} + +} // namespace biscuit diff --git a/externals/biscuit/src/assembler_compressed.cpp b/externals/biscuit/src/assembler_compressed.cpp new file mode 100644 index 00000000..cca0b42e --- /dev/null +++ b/externals/biscuit/src/assembler_compressed.cpp @@ -0,0 +1,696 @@ +#include <biscuit/assert.hpp> +#include <biscuit/assembler.hpp> + +#include <array> +#include <cmath> + +#include "assembler_util.hpp" + +// RVC Extension Instructions + +namespace biscuit { +namespace { +// Emits a compressed branch instruction. These consist of: +// funct3 | imm[8|4:3] | rs | imm[7:6|2:1|5] | op +void EmitCompressedBranch(CodeBuffer& buffer, uint32_t funct3, int32_t offset, GPR rs, uint32_t op) { + BISCUIT_ASSERT(IsValidCBTypeImm(offset)); + BISCUIT_ASSERT(IsValid3BitCompressedReg(rs)); + + const auto transformed_imm = TransformToCBTypeImm(static_cast<uint32_t>(offset)); + const auto rs_san = CompressedRegTo3BitEncoding(rs); + buffer.Emit16(((funct3 & 0b111) << 13) | transformed_imm | (rs_san << 7) | (op & 0b11)); +} + +// Emits a compressed jump instruction. These consist of: +// funct3 | imm | op +void EmitCompressedJump(CodeBuffer& buffer, uint32_t funct3, int32_t offset, uint32_t op) { + BISCUIT_ASSERT(IsValidCJTypeImm(offset)); + BISCUIT_ASSERT((offset % 2) == 0); + + buffer.Emit16(TransformToCJTypeImm(static_cast<uint32_t>(offset)) | + ((funct3 & 0b111) << 13) | (op & 0b11)); +} + +// Emits a compress immediate instruction. These consist of: +// funct3 | imm | rd | imm | op +void EmitCompressedImmediate(CodeBuffer& buffer, uint32_t funct3, uint32_t imm, GPR rd, uint32_t op) { + BISCUIT_ASSERT(rd != x0); + + const auto new_imm = ((imm & 0b11111) << 2) | ((imm & 0b100000) << 7); + buffer.Emit16(((funct3 & 0b111) << 13) | new_imm | (rd.Index() << 7) | (op & 0b11)); +} + +// Emits a compressed load instruction. These consist of: +// funct3 | imm | rs1 | imm | rd | op +void EmitCompressedLoad(CodeBuffer& buffer, uint32_t funct3, uint32_t imm, GPR rs, + Register rd, uint32_t op) { + BISCUIT_ASSERT(IsValid3BitCompressedReg(rs)); + BISCUIT_ASSERT(IsValid3BitCompressedReg(rd)); + + imm &= 0xF8; + + const auto imm_enc = ((imm & 0x38) << 7) | ((imm & 0xC0) >> 1); + const auto rd_san = CompressedRegTo3BitEncoding(rd); + const auto rs_san = CompressedRegTo3BitEncoding(rs); + buffer.Emit16(((funct3 & 0b111) << 13) | imm_enc | (rs_san << 7) | (rd_san << 2) | (op & 0b11)); +} + +// Emits a compressed register arithmetic instruction. These consist of: +// funct6 | rd | funct2 | rs | op +void EmitCompressedRegArith(CodeBuffer& buffer, uint32_t funct6, GPR rd, uint32_t funct2, + GPR rs, uint32_t op) { + BISCUIT_ASSERT(IsValid3BitCompressedReg(rs)); + BISCUIT_ASSERT(IsValid3BitCompressedReg(rd)); + + const auto rd_san = CompressedRegTo3BitEncoding(rd); + const auto rs_san = CompressedRegTo3BitEncoding(rs); + buffer.Emit16(((funct6 & 0b111111) << 10) | (rd_san << 7) | ((funct2 & 0b11) << 5) | + (rs_san << 2) | (op & 0b11)); +} + +// Emits a compressed store instruction. These consist of: +// funct3 | imm | rs1 | imm | rs2 | op +void EmitCompressedStore(CodeBuffer& buffer, uint32_t funct3, uint32_t imm, GPR rs1, + Register rs2, uint32_t op) { + // This has the same format as a compressed load, with rs2 taking the place of rd. + // We can reuse the code we've already written to handle this. + EmitCompressedLoad(buffer, funct3, imm, rs1, rs2, op); +} + +// Emits a compressed wide immediate instruction. These consist of: +// funct3 | imm | rd | opcode +void EmitCompressedWideImmediate(CodeBuffer& buffer, uint32_t funct3, uint32_t imm, + GPR rd, uint32_t op) { + BISCUIT_ASSERT(IsValid3BitCompressedReg(rd)); + + const auto rd_sanitized = CompressedRegTo3BitEncoding(rd); + buffer.Emit16(((funct3 & 0b111) << 13) | ((imm & 0xFF) << 5) | + (rd_sanitized << 2) | (op & 0b11)); +} + +void EmitCLBType(CodeBuffer& buffer, uint32_t funct6, GPR rs, uint32_t uimm, GPR rd, + uint32_t op, uint32_t b6) { + BISCUIT_ASSERT(IsValid3BitCompressedReg(rs)); + BISCUIT_ASSERT(IsValid3BitCompressedReg(rd)); + BISCUIT_ASSERT(uimm <= 3); + + const auto rd_san = CompressedRegTo3BitEncoding(rd); + const auto rs_san = CompressedRegTo3BitEncoding(rs); + + buffer.Emit16((funct6 << 10) | (rs_san << 7) | (b6 << 6) | (uimm << 5) | (rd_san << 2) | op); +} + +void EmitCLHType(CodeBuffer& buffer, uint32_t funct6, GPR rs, uint32_t uimm, GPR rd, + uint32_t op, uint32_t b6) { + BISCUIT_ASSERT((uimm % 2) == 0); + BISCUIT_ASSERT(uimm <= 2); + + // Only have 1 bit of encoding space for the immediate. + const uint32_t uimm_fixed = uimm >> 1; + EmitCLBType(buffer, funct6, rs, uimm_fixed, rd, op, b6); +} + +// These have the same layout as the equivalent loads, we just essentially alias +// the name of those to provide better intent at the call site. +void EmitCSBType(CodeBuffer& buffer, uint32_t funct6, GPR rs, uint32_t uimm, GPR rd, uint32_t op) { + EmitCLBType(buffer, funct6, rs, uimm, rd, op, 0); +} +void EmitCSHType(CodeBuffer& buffer, uint32_t funct6, GPR rs, uint32_t uimm, GPR rd, uint32_t op) { + EmitCLHType(buffer, funct6, rs, uimm, rd, op, 0); +} + +void EmitCUType(CodeBuffer& buffer, uint32_t funct6, GPR rd, uint32_t funct5, uint32_t op) { + BISCUIT_ASSERT(IsValid3BitCompressedReg(rd)); + const auto rd_san = CompressedRegTo3BitEncoding(rd); + + buffer.Emit16((funct6 << 10) | (rd_san << 7) | (funct5 << 2) | op); +} + +void EmitCMJTType(CodeBuffer& buffer, uint32_t funct6, uint32_t index, uint32_t op) { + buffer.Emit16((funct6 << 10) | (index << 2) | op); +} + +void EmitCMMVType(CodeBuffer& buffer, uint32_t funct6, GPR r1s, uint32_t funct2, GPR r2s, uint32_t op) { + const auto is_valid_s_register = [](GPR reg) { + return reg == s0 || reg == s1 || (reg >= s2 && reg <= s7); + }; + + BISCUIT_ASSERT(r1s != r2s); + BISCUIT_ASSERT(is_valid_s_register(r1s)); + BISCUIT_ASSERT(is_valid_s_register(r2s)); + + const auto r1s_san = r1s.Index() & 0b111; + const auto r2s_san = r2s.Index() & 0b111; + + buffer.Emit16((funct6 << 10) | (r1s_san << 7) | (funct2 << 5) | (r2s_san << 2) | op); +} + +void EmitCMPPType(CodeBuffer& buffer, uint32_t funct6, uint32_t funct2, PushPopList reglist, + int32_t stack_adj, uint32_t op, ArchFeature feature) { + BISCUIT_ASSERT(stack_adj % 16 == 0); + + static constexpr std::array stack_adj_bases_rv32{ + 0U, 0U, 0U, 0U, 16U, 16U, 16U, 16U, + 32U, 32U, 32U, 32U, 48U, 48U, 48U, 64U, + }; + static constexpr std::array stack_adj_bases_rv64{ + 0U, 0U, 0U, 0U, 16U, 16U, 32U, 32U, + 48U, 48U, 64U, 64U, 80U, 80U, 96U, 112U + }; + + const auto bitmask = reglist.GetBitmask(); + const auto stack_adj_base = IsRV64(feature) ? stack_adj_bases_rv64[bitmask] + : stack_adj_bases_rv32[bitmask]; + const auto stack_adj_u = static_cast<uint32_t>(std::abs(stack_adj)); + const auto spimm = (stack_adj_u - stack_adj_base) / 16U; + + // We can only encode up to three differenct values as the upper spimm bits. + // Ensure we catch any cases where we end up going outside of them. + BISCUIT_ASSERT(stack_adj_u == stack_adj_base || + stack_adj_u == stack_adj_base + 16 || + stack_adj_u == stack_adj_base + 32 || + stack_adj_u == stack_adj_base + 48); + + buffer.Emit16((funct6 << 10) | (funct2 << 8) | (bitmask << 4) | (spimm << 2) | op); +} +} // Anonymous namespace + +void Assembler::C_ADD(GPR rd, GPR rs) noexcept { + BISCUIT_ASSERT(rs != x0); + m_buffer.Emit16(0x9002 | (rd.Index() << 7) | (rs.Index() << 2)); +} + +void Assembler::C_ADDI(GPR rd, int32_t imm) noexcept { + BISCUIT_ASSERT(imm != 0); + BISCUIT_ASSERT(IsValidSigned6BitImm(imm)); + EmitCompressedImmediate(m_buffer, 0b000, static_cast<uint32_t>(imm), rd, 0b01); +} + +void Assembler::C_ADDIW(GPR rd, int32_t imm) noexcept { + BISCUIT_ASSERT(IsRV64OrRV128(m_features)); + BISCUIT_ASSERT(IsValidSigned6BitImm(imm)); + EmitCompressedImmediate(m_buffer, 0b001, static_cast<uint32_t>(imm), rd, 0b01); +} + +void Assembler::C_ADDI4SPN(GPR rd, uint32_t imm) noexcept { + BISCUIT_ASSERT(imm != 0); + BISCUIT_ASSERT(imm <= 1020); + BISCUIT_ASSERT(imm % 4 == 0); + + // clang-format off + const auto new_imm = ((imm & 0x030) << 2) | + ((imm & 0x3C0) >> 4) | + ((imm & 0x004) >> 1) | + ((imm & 0x008) >> 3); + // clang-format on + + EmitCompressedWideImmediate(m_buffer, 0b000, new_imm, rd, 0b00); +} + +void Assembler::C_ADDW(GPR rd, GPR rs) noexcept { + BISCUIT_ASSERT(IsRV64OrRV128(m_features)); + EmitCompressedRegArith(m_buffer, 0b100111, rd, 0b01, rs, 0b01); +} + +void Assembler::C_ADDI16SP(int32_t imm) noexcept { + BISCUIT_ASSERT(imm != 0); + BISCUIT_ASSERT(imm >= -512 && imm <= 496); + BISCUIT_ASSERT(imm % 16 == 0); + + // clang-format off + const auto uimm = static_cast<uint32_t>(imm); + const auto new_imm = ((uimm & 0x020) >> 3) | + ((uimm & 0x180) >> 4) | + ((uimm & 0x040) >> 1) | + ((uimm & 0x010) << 2) | + ((uimm & 0x200) << 3); + // clang-format on + + m_buffer.Emit16(0x6000U | new_imm | (x2.Index() << 7) | 0b01U); +} + +void Assembler::C_AND(GPR rd, GPR rs) noexcept { + EmitCompressedRegArith(m_buffer, 0b100011, rd, 0b11, rs, 0b01); +} + +void Assembler::C_ANDI(GPR rd, uint32_t imm) noexcept { + BISCUIT_ASSERT(IsValid3BitCompressedReg(rd)); + + constexpr auto base = 0x8801U; + const auto shift_enc = ((imm & 0b11111) << 2) | ((imm & 0b100000) << 7); + const auto reg = CompressedRegTo3BitEncoding(rd); + + m_buffer.Emit16(base | shift_enc | (reg << 7)); +} + +void Assembler::C_BEQZ(GPR rs, int32_t offset) noexcept { + EmitCompressedBranch(m_buffer, 0b110, offset, rs, 0b01); +} + +void Assembler::C_BEQZ(GPR rs, Label* label) noexcept { + const auto address = LinkAndGetOffset(label); + C_BEQZ(rs, static_cast<int32_t>(address)); +} + +void Assembler::C_BNEZ(GPR rs, int32_t offset) noexcept { + EmitCompressedBranch(m_buffer, 0b111, offset, rs, 0b01); +} + +void Assembler::C_BNEZ(GPR rs, Label* label) noexcept { + const auto address = LinkAndGetOffset(label); + C_BNEZ(rs, static_cast<int32_t>(address)); +} + +void Assembler::C_EBREAK() noexcept { + m_buffer.Emit16(0x9002); +} + +void Assembler::C_FLD(FPR rd, uint32_t imm, GPR rs) noexcept { + BISCUIT_ASSERT(IsRV32OrRV64(m_features)); + BISCUIT_ASSERT(imm <= 248); + BISCUIT_ASSERT(imm % 8 == 0); + + EmitCompressedLoad(m_buffer, 0b001, imm, rs, rd, 0b00); +} + +void Assembler::C_FLDSP(FPR rd, uint32_t imm) noexcept { + BISCUIT_ASSERT(IsRV32OrRV64(m_features)); + BISCUIT_ASSERT(imm <= 504); + BISCUIT_ASSERT(imm % 8 == 0); + + // clang-format off + const auto new_imm = ((imm & 0x018) << 2) | + ((imm & 0x1C0) >> 4) | + ((imm & 0x020) << 7); + // clang-format on + + m_buffer.Emit16(0x2002U | (rd.Index() << 7) | new_imm); +} + +void Assembler::C_FLW(FPR rd, uint32_t imm, GPR rs) noexcept { + BISCUIT_ASSERT(IsRV32(m_features)); + BISCUIT_ASSERT(imm <= 124); + BISCUIT_ASSERT(imm % 4 == 0); + + imm &= 0x7C; + const auto new_imm = ((imm & 0b0100) << 5) | (imm & 0x78); + EmitCompressedLoad(m_buffer, 0b011, new_imm, rs, rd, 0b00); +} + +void Assembler::C_FLWSP(FPR rd, uint32_t imm) noexcept { + BISCUIT_ASSERT(IsRV32(m_features)); + BISCUIT_ASSERT(imm <= 252); + BISCUIT_ASSERT(imm % 4 == 0); + + // clang-format off + const auto new_imm = ((imm & 0x020) << 7) | + ((imm & 0x0C0) >> 4) | + ((imm & 0x01C) << 2); + // clang-format on + + m_buffer.Emit16(0x6002U | (rd.Index() << 7) | new_imm); +} + +void Assembler::C_FSD(FPR rs2, uint32_t imm, GPR rs1) noexcept { + BISCUIT_ASSERT(IsRV32OrRV64(m_features)); + BISCUIT_ASSERT(imm <= 248); + BISCUIT_ASSERT(imm % 8 == 0); + + EmitCompressedStore(m_buffer, 0b101, imm, rs1, rs2, 0b00); +} + +void Assembler::C_FSDSP(FPR rs, uint32_t imm) noexcept { + BISCUIT_ASSERT(IsRV32OrRV64(m_features)); + BISCUIT_ASSERT(imm <= 504); + BISCUIT_ASSERT(imm % 8 == 0); + + // clang-format off + const auto new_imm = ((imm & 0x038) << 7) | + ((imm & 0x1C0) << 1); + // clang-format on + + m_buffer.Emit16(0xA002U | (rs.Index() << 2) | new_imm); +} + +void Assembler::C_J(Label* label) noexcept { + const auto address = LinkAndGetOffset(label); + C_J(static_cast<int32_t>(address)); +} + +void Assembler::C_J(int32_t offset) noexcept { + EmitCompressedJump(m_buffer, 0b101, offset, 0b01); +} + +void Assembler::C_JAL(Label* label) noexcept { + const auto address = LinkAndGetOffset(label); + C_JAL(static_cast<int32_t>(address)); +} + +void Assembler::C_JAL(int32_t offset) noexcept { + BISCUIT_ASSERT(IsRV32(m_features)); + EmitCompressedJump(m_buffer, 0b001, offset, 0b01); +} + +void Assembler::C_FSW(FPR rs2, uint32_t imm, GPR rs1) noexcept { + BISCUIT_ASSERT(IsRV32(m_features)); + BISCUIT_ASSERT(imm <= 124); + BISCUIT_ASSERT(imm % 4 == 0); + + imm &= 0x7C; + const auto new_imm = ((imm & 0b0100) << 5) | (imm & 0x78); + EmitCompressedStore(m_buffer, 0b111, new_imm, rs1, rs2, 0b00); +} + +void Assembler::C_FSWSP(FPR rs, uint32_t imm) noexcept { + BISCUIT_ASSERT(IsRV32(m_features)); + BISCUIT_ASSERT(imm <= 252); + BISCUIT_ASSERT(imm % 4 == 0); + + // clang-format off + const auto new_imm = ((imm & 0x0C0) << 1) | + ((imm & 0x03C) << 7); + // clang-format on + + m_buffer.Emit16(0xE002U | (rs.Index() << 2) | new_imm); +} + +void Assembler::C_JALR(GPR rs) noexcept { + BISCUIT_ASSERT(rs != x0); + m_buffer.Emit16(0x9002 | (rs.Index() << 7)); +} + +void Assembler::C_JR(GPR rs) noexcept { + BISCUIT_ASSERT(rs != x0); + m_buffer.Emit16(0x8002 | (rs.Index() << 7)); +} + +void Assembler::C_LD(GPR rd, uint32_t imm, GPR rs) noexcept { + BISCUIT_ASSERT(IsRV64OrRV128(m_features)); + BISCUIT_ASSERT(imm <= 248); + BISCUIT_ASSERT(imm % 8 == 0); + + EmitCompressedLoad(m_buffer, 0b011, imm, rs, rd, 0b00); +} + +void Assembler::C_LDSP(GPR rd, uint32_t imm) noexcept { + BISCUIT_ASSERT(IsRV64OrRV128(m_features)); + BISCUIT_ASSERT(rd != x0); + BISCUIT_ASSERT(imm <= 504); + BISCUIT_ASSERT(imm % 8 == 0); + + // clang-format off + const auto new_imm = ((imm & 0x018) << 2) | + ((imm & 0x1C0) >> 4) | + ((imm & 0x020) << 7); + // clang-format on + + m_buffer.Emit16(0x6002U | (rd.Index() << 7) | new_imm); +} + +void Assembler::C_LI(GPR rd, int32_t imm) noexcept { + BISCUIT_ASSERT(IsValidSigned6BitImm(imm)); + EmitCompressedImmediate(m_buffer, 0b010, static_cast<uint32_t>(imm), rd, 0b01); +} + +void Assembler::C_LQ(GPR rd, uint32_t imm, GPR rs) noexcept { + BISCUIT_ASSERT(IsRV128(m_features)); + BISCUIT_ASSERT(imm <= 496); + BISCUIT_ASSERT(imm % 16 == 0); + + imm &= 0x1F0; + const auto new_imm = ((imm & 0x100) >> 5) | (imm & 0xF0); + EmitCompressedLoad(m_buffer, 0b001, new_imm, rs, rd, 0b00); +} + +void Assembler::C_LQSP(GPR rd, uint32_t imm) noexcept { + BISCUIT_ASSERT(IsRV128(m_features)); + BISCUIT_ASSERT(rd != x0); + BISCUIT_ASSERT(imm <= 1008); + BISCUIT_ASSERT(imm % 16 == 0); + + // clang-format off + const auto new_imm = ((imm & 0x020) << 7) | + ((imm & 0x010) << 2) | + ((imm & 0x3C0) >> 4); + // clang-format on + + m_buffer.Emit16(0x2002U | (rd.Index() << 7) | new_imm); +} + +void Assembler::C_LUI(GPR rd, uint32_t imm) noexcept { + BISCUIT_ASSERT(imm != 0); + BISCUIT_ASSERT(rd != x0 && rd != x2); + + const auto new_imm = (imm & 0x3F000) >> 12; + EmitCompressedImmediate(m_buffer, 0b011, new_imm, rd, 0b01); +} + +void Assembler::C_LW(GPR rd, uint32_t imm, GPR rs) noexcept { + BISCUIT_ASSERT(imm <= 124); + BISCUIT_ASSERT(imm % 4 == 0); + + imm &= 0x7C; + const auto new_imm = ((imm & 0b0100) << 5) | (imm & 0x78); + EmitCompressedLoad(m_buffer, 0b010, new_imm, rs, rd, 0b00); +} + +void Assembler::C_LWSP(GPR rd, uint32_t imm) noexcept { + BISCUIT_ASSERT(rd != x0); + BISCUIT_ASSERT(imm <= 252); + BISCUIT_ASSERT(imm % 4 == 0); + + // clang-format off + const auto new_imm = ((imm & 0x020) << 7) | + ((imm & 0x0C0) >> 4) | + ((imm & 0x01C) << 2); + // clang-format on + + m_buffer.Emit16(0x4002U | (rd.Index() << 7) | new_imm); +} + +void Assembler::C_MV(GPR rd, GPR rs) noexcept { + BISCUIT_ASSERT(rd != x0); + BISCUIT_ASSERT(rs != x0); + m_buffer.Emit16(0x8002 | (rd.Index() << 7) | (rs.Index() << 2)); +} + +void Assembler::C_NOP() noexcept { + m_buffer.Emit16(1); +} + +void Assembler::C_OR(GPR rd, GPR rs) noexcept { + EmitCompressedRegArith(m_buffer, 0b100011, rd, 0b10, rs, 0b01); +} + +void Assembler::C_SD(GPR rs2, uint32_t imm, GPR rs1) noexcept { + BISCUIT_ASSERT(IsRV64OrRV128(m_features)); + BISCUIT_ASSERT(imm <= 248); + BISCUIT_ASSERT(imm % 8 == 0); + + EmitCompressedLoad(m_buffer, 0b111, imm, rs1, rs2, 0b00); +} + +void Assembler::C_SDSP(GPR rs, uint32_t imm) noexcept { + BISCUIT_ASSERT(IsRV64OrRV128(m_features)); + BISCUIT_ASSERT(imm <= 504); + BISCUIT_ASSERT(imm % 8 == 0); + + // clang-format off + const auto new_imm = ((imm & 0x038) << 7) | + ((imm & 0x1C0) << 1); + // clang-format on + + m_buffer.Emit16(0xE002U | (rs.Index() << 2) | new_imm); +} + +void Assembler::C_SLLI(GPR rd, uint32_t shift) noexcept { + BISCUIT_ASSERT(rd != x0); + BISCUIT_ASSERT(IsValidCompressedShiftAmount(shift)); + + // RV128C encodes a 64-bit shift with an encoding of 0. + if (shift == 64) { + BISCUIT_ASSERT(IsRV128(m_features)); + shift = 0; + } + + const auto shift_enc = ((shift & 0b11111) << 2) | ((shift & 0b100000) << 7); + m_buffer.Emit16(0x0002U | shift_enc | (rd.Index() << 7)); +} + +void Assembler::C_SQ(GPR rs2, uint32_t imm, GPR rs1) noexcept { + BISCUIT_ASSERT(IsRV128(m_features)); + BISCUIT_ASSERT(imm <= 496); + BISCUIT_ASSERT(imm % 16 == 0); + + imm &= 0x1F0; + const auto new_imm = ((imm & 0x100) >> 5) | (imm & 0xF0); + EmitCompressedStore(m_buffer, 0b101, new_imm, rs1, rs2, 0b00); +} + +void Assembler::C_SQSP(GPR rs, uint32_t imm) noexcept { + BISCUIT_ASSERT(IsRV128(m_features)); + BISCUIT_ASSERT(imm <= 1008); + BISCUIT_ASSERT(imm % 16 == 0); + + // clang-format off + const auto new_imm = ((imm & 0x3C0) << 1) | + ((imm & 0x030) << 7); + // clang-format on + + m_buffer.Emit16(0xA002U | (rs.Index() << 2) | new_imm); +} + +void Assembler::C_SRAI(GPR rd, uint32_t shift) noexcept { + BISCUIT_ASSERT(IsValid3BitCompressedReg(rd)); + BISCUIT_ASSERT(IsValidCompressedShiftAmount(shift)); + + // RV128C encodes a 64-bit shift with an encoding of 0. + if (shift == 64) { + BISCUIT_ASSERT(IsRV128(m_features)); + shift = 0; + } + + constexpr auto base = 0x8401U; + const auto shift_enc = ((shift & 0b11111) << 2) | ((shift & 0b100000) << 7); + const auto reg = CompressedRegTo3BitEncoding(rd); + + m_buffer.Emit16(base | shift_enc | (reg << 7)); +} + +void Assembler::C_SRLI(GPR rd, uint32_t shift) noexcept { + BISCUIT_ASSERT(IsValid3BitCompressedReg(rd)); + BISCUIT_ASSERT(IsValidCompressedShiftAmount(shift)); + + // RV128C encodes a 64-bit shift with an encoding of 0. + if (shift == 64) { + BISCUIT_ASSERT(IsRV128(m_features)); + shift = 0; + } + + constexpr auto base = 0x8001U; + const auto shift_enc = ((shift & 0b11111) << 2) | ((shift & 0b100000) << 7); + const auto reg = CompressedRegTo3BitEncoding(rd); + + m_buffer.Emit16(base | shift_enc | (reg << 7)); +} + +void Assembler::C_SUB(GPR rd, GPR rs) noexcept { + EmitCompressedRegArith(m_buffer, 0b100011, rd, 0b00, rs, 0b01); +} + +void Assembler::C_SUBW(GPR rd, GPR rs) noexcept { + BISCUIT_ASSERT(IsRV64OrRV128(m_features)); + EmitCompressedRegArith(m_buffer, 0b100111, rd, 0b00, rs, 0b01); +} + +void Assembler::C_SW(GPR rs2, uint32_t imm, GPR rs1) noexcept { + BISCUIT_ASSERT(imm <= 124); + BISCUIT_ASSERT(imm % 4 == 0); + + imm &= 0x7C; + const auto new_imm = ((imm & 0b0100) << 5) | (imm & 0x78); + EmitCompressedStore(m_buffer, 0b110, new_imm, rs1, rs2, 0b00); +} + +void Assembler::C_SWSP(GPR rs, uint32_t imm) noexcept { + BISCUIT_ASSERT(imm <= 252); + BISCUIT_ASSERT(imm % 4 == 0); + + // clang-format off + const auto new_imm = ((imm & 0x0C0) << 1) | + ((imm & 0x03C) << 7); + // clang-format on + + m_buffer.Emit16(0xC002U | (rs.Index() << 2) | new_imm); +} + +void Assembler::C_UNDEF() noexcept { + m_buffer.Emit16(0); +} + +void Assembler::C_XOR(GPR rd, GPR rs) noexcept { + EmitCompressedRegArith(m_buffer, 0b100011, rd, 0b01, rs, 0b01); +} + +// Zc Extension Instructions + +void Assembler::C_LBU(GPR rd, uint32_t uimm, GPR rs) noexcept { + // C.LBU swaps the ordering of the immediate. + const auto uimm_fixed = ((uimm & 0b01) << 1) | ((uimm & 0b10) >> 1); + + EmitCLBType(m_buffer, 0b100000, rs, uimm_fixed, rd, 0b00, 0); +} +void Assembler::C_LH(GPR rd, uint32_t uimm, GPR rs) noexcept { + EmitCLHType(m_buffer, 0b100001, rs, uimm, rd, 0b00, 1); +} +void Assembler::C_LHU(GPR rd, uint32_t uimm, GPR rs) noexcept { + EmitCLHType(m_buffer, 0b100001, rs, uimm, rd, 0b00, 0); +} +void Assembler::C_SB(GPR rs2, uint32_t uimm, GPR rs1) noexcept { + // C.SB swaps the ordering of the immediate. + const auto uimm_fixed = ((uimm & 0b01) << 1) | ((uimm & 0b10) >> 1); + + EmitCSBType(m_buffer, 0b100010, rs1, uimm_fixed, rs2, 0b00); +} +void Assembler::C_SH(GPR rs2, uint32_t uimm, GPR rs1) noexcept { + EmitCSHType(m_buffer, 0b100011, rs1, uimm, rs2, 0b00); +} + +void Assembler::C_SEXT_B(GPR rd) noexcept { + EmitCUType(m_buffer, 0b100111, rd, 0b11001, 0b01); +} +void Assembler::C_SEXT_H(GPR rd) noexcept { + EmitCUType(m_buffer, 0b100111, rd, 0b11011, 0b01); +} +void Assembler::C_ZEXT_B(GPR rd) noexcept { + EmitCUType(m_buffer, 0b100111, rd, 0b11000, 0b01); +} +void Assembler::C_ZEXT_H(GPR rd) noexcept { + EmitCUType(m_buffer, 0b100111, rd, 0b11010, 0b01); +} +void Assembler::C_ZEXT_W(GPR rd) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + EmitCUType(m_buffer, 0b100111, rd, 0b11100, 0b01); +} + +void Assembler::C_MUL(GPR rsd, GPR rs2) noexcept { + EmitCompressedRegArith(m_buffer, 0b100111, rsd, 0b10, rs2, 0b01); +} +void Assembler::C_NOT(GPR rd) noexcept { + EmitCUType(m_buffer, 0b100111, rd, 0b11101, 0b01); +} + +void Assembler::CM_JALT(uint32_t index) noexcept { + BISCUIT_ASSERT(index >= 32 && index <= 255); + EmitCMJTType(m_buffer, 0b101000, index, 0b10); +} +void Assembler::CM_JT(uint32_t index) noexcept { + BISCUIT_ASSERT(index <= 31); + EmitCMJTType(m_buffer, 0b101000, index, 0b10); +} + +void Assembler::CM_MVA01S(GPR r1s, GPR r2s) noexcept { + EmitCMMVType(m_buffer, 0b101011, r1s, 0b11, r2s, 0b10); +} +void Assembler::CM_MVSA01(GPR r1s, GPR r2s) noexcept { + EmitCMMVType(m_buffer, 0b101011, r1s, 0b01, r2s, 0b10); +} + +void Assembler::CM_POP(PushPopList reg_list, int32_t stack_adj) noexcept { + BISCUIT_ASSERT(stack_adj > 0); + EmitCMPPType(m_buffer, 0b101110, 0b10, reg_list, stack_adj, 0b10, m_features); +} +void Assembler::CM_POPRET(PushPopList reg_list, int32_t stack_adj) noexcept { + BISCUIT_ASSERT(stack_adj > 0); + EmitCMPPType(m_buffer, 0b101111, 0b10, reg_list, stack_adj, 0b10, m_features); +} +void Assembler::CM_POPRETZ(PushPopList reg_list, int32_t stack_adj) noexcept { + BISCUIT_ASSERT(stack_adj > 0); + EmitCMPPType(m_buffer, 0b101111, 0b00, reg_list, stack_adj, 0b10, m_features); +} +void Assembler::CM_PUSH(PushPopList reg_list, int32_t stack_adj) noexcept { + BISCUIT_ASSERT(stack_adj < 0); + EmitCMPPType(m_buffer, 0b101110, 0b00, reg_list, stack_adj, 0b10, m_features); +} + +} // namespace biscuit diff --git a/externals/biscuit/src/assembler_crypto.cpp b/externals/biscuit/src/assembler_crypto.cpp new file mode 100644 index 00000000..8d83bc41 --- /dev/null +++ b/externals/biscuit/src/assembler_crypto.cpp @@ -0,0 +1,172 @@ +#include <biscuit/assert.hpp> +#include <biscuit/assembler.hpp> + +#include "assembler_util.hpp" + +namespace biscuit { +namespace { +void EmitAES32Instruction(CodeBuffer& buffer, uint32_t op, GPR rd, GPR rs1, GPR rs2, uint32_t bs) noexcept { + BISCUIT_ASSERT(bs <= 0b11); + buffer.Emit32(op | (bs << 30) | (rs2.Index() << 20) | + (rs1.Index() << 15) | (rd.Index() << 7)); +} + +void EmitSM4Instruction(CodeBuffer& buffer, uint32_t op, GPR rd, GPR rs1, GPR rs2, uint32_t bs) noexcept { + // Same behavior, function exists for a better contextual name. + EmitAES32Instruction(buffer, op, rd, rs1, rs2, bs); +} + +void EmitAES64Instruction(CodeBuffer& buffer, uint32_t op, GPR rd, GPR rs1, GPR rs2) noexcept { + buffer.Emit32(op | (rs2.Index() << 20) | (rs1.Index() << 15) | (rd.Index() << 7)); +} + +void EmitSHAInstruction(CodeBuffer& buffer, uint32_t op, GPR rd, GPR rs1, GPR rs2) noexcept { + // Same behavior, function exists for a better contextual name. + EmitAES64Instruction(buffer, op, rd, rs1, rs2); +} + +void EmitSM3Instruction(CodeBuffer& buffer, uint32_t op, GPR rd, GPR rs) noexcept { + // Same behavior, function exists for a better contextual name. + EmitAES64Instruction(buffer, op, rd, rs, x0); +} +} // Anonymous namespace + +void Assembler::AES32DSI(GPR rd, GPR rs1, GPR rs2, uint32_t bs) noexcept { + BISCUIT_ASSERT(IsRV32(m_features)); + EmitAES32Instruction(m_buffer, 0x2A000033, rd, rs1, rs2, bs); +} + +void Assembler::AES32DSMI(GPR rd, GPR rs1, GPR rs2, uint32_t bs) noexcept { + BISCUIT_ASSERT(IsRV32(m_features)); + EmitAES32Instruction(m_buffer, 0x2E000033, rd, rs1, rs2, bs); +} + +void Assembler::AES32ESI(GPR rd, GPR rs1, GPR rs2, uint32_t bs) noexcept { + BISCUIT_ASSERT(IsRV32(m_features)); + EmitAES32Instruction(m_buffer, 0x22000033, rd, rs1, rs2, bs); +} + +void Assembler::AES32ESMI(GPR rd, GPR rs1, GPR rs2, uint32_t bs) noexcept { + BISCUIT_ASSERT(IsRV32(m_features)); + EmitAES32Instruction(m_buffer, 0x26000033, rd, rs1, rs2, bs); +} + +void Assembler::AES64DS(GPR rd, GPR rs1, GPR rs2) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + EmitAES64Instruction(m_buffer, 0x3A000033, rd, rs1, rs2); +} + +void Assembler::AES64DSM(GPR rd, GPR rs1, GPR rs2) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + EmitAES64Instruction(m_buffer, 0x3E000033, rd, rs1, rs2); +} + +void Assembler::AES64ES(GPR rd, GPR rs1, GPR rs2) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + EmitAES64Instruction(m_buffer, 0x32000033, rd, rs1, rs2); +} + +void Assembler::AES64ESM(GPR rd, GPR rs1, GPR rs2) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + EmitAES64Instruction(m_buffer, 0x36000033, rd, rs1, rs2); +} + +void Assembler::AES64IM(GPR rd, GPR rs) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + EmitAES64Instruction(m_buffer, 0x30001013, rd, rs, x0); +} + +void Assembler::AES64KS1I(GPR rd, GPR rs, uint32_t rnum) noexcept { + // RVK spec states that rnums 0xB to 0xF are reserved. + BISCUIT_ASSERT(IsRV64(m_features)); + BISCUIT_ASSERT(rnum <= 0xA); + EmitAES64Instruction(m_buffer, 0x31001013, rd, rs, GPR{rnum}); +} + +void Assembler::AES64KS2(GPR rd, GPR rs1, GPR rs2) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + EmitAES64Instruction(m_buffer, 0x7E000033, rd, rs1, rs2); +} + +void Assembler::SHA256SIG0(GPR rd, GPR rs) noexcept { + EmitSHAInstruction(m_buffer, 0x10201013, rd, rs, x0); +} + +void Assembler::SHA256SIG1(GPR rd, GPR rs) noexcept { + EmitSHAInstruction(m_buffer, 0x10301013, rd, rs, x0); +} + +void Assembler::SHA256SUM0(GPR rd, GPR rs) noexcept { + EmitSHAInstruction(m_buffer, 0x10001013, rd, rs, x0); +} + +void Assembler::SHA256SUM1(GPR rd, GPR rs) noexcept { + EmitSHAInstruction(m_buffer, 0x10101013, rd, rs, x0); +} + +void Assembler::SHA512SIG0(GPR rd, GPR rs) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + EmitSHAInstruction(m_buffer, 0x10601013, rd, rs, x0); +} + +void Assembler::SHA512SIG0H(GPR rd, GPR rs1, GPR rs2) noexcept { + BISCUIT_ASSERT(IsRV32(m_features)); + EmitSHAInstruction(m_buffer, 0x5C000033, rd, rs1, rs2); +} + +void Assembler::SHA512SIG0L(GPR rd, GPR rs1, GPR rs2) noexcept { + BISCUIT_ASSERT(IsRV32(m_features)); + EmitSHAInstruction(m_buffer, 0x54000033, rd, rs1, rs2); +} + +void Assembler::SHA512SIG1(GPR rd, GPR rs) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + EmitSHAInstruction(m_buffer, 0x10701013, rd, rs, x0); +} + +void Assembler::SHA512SIG1H(GPR rd, GPR rs1, GPR rs2) noexcept { + BISCUIT_ASSERT(IsRV32(m_features)); + EmitSHAInstruction(m_buffer, 0x5E000033, rd, rs1, rs2); +} + +void Assembler::SHA512SIG1L(GPR rd, GPR rs1, GPR rs2) noexcept { + BISCUIT_ASSERT(IsRV32(m_features)); + EmitSHAInstruction(m_buffer, 0x56000033, rd, rs1, rs2); +} + +void Assembler::SHA512SUM0(GPR rd, GPR rs) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + EmitSHAInstruction(m_buffer, 0x10401013, rd, rs, x0); +} + +void Assembler::SHA512SUM0R(GPR rd, GPR rs1, GPR rs2) noexcept { + BISCUIT_ASSERT(IsRV32(m_features)); + EmitSHAInstruction(m_buffer, 0x50000033, rd, rs1, rs2); +} + +void Assembler::SHA512SUM1(GPR rd, GPR rs) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + EmitSHAInstruction(m_buffer, 0x10501013, rd, rs, x0); +} + +void Assembler::SHA512SUM1R(GPR rd, GPR rs1, GPR rs2) noexcept { + BISCUIT_ASSERT(IsRV32(m_features)); + EmitSHAInstruction(m_buffer, 0x52000033, rd, rs1, rs2); +} + +void Assembler::SM3P0(GPR rd, GPR rs) noexcept { + EmitSM3Instruction(m_buffer, 0x10801013, rd, rs); +} + +void Assembler::SM3P1(GPR rd, GPR rs) noexcept { + EmitSM3Instruction(m_buffer, 0x10901013, rd, rs); +} + +void Assembler::SM4ED(GPR rd, GPR rs1, GPR rs2, uint32_t bs) noexcept { + EmitSM4Instruction(m_buffer, 0x30000033, rd, rs1, rs2, bs); +} + +void Assembler::SM4KS(GPR rd, GPR rs1, GPR rs2, uint32_t bs) noexcept { + EmitSM4Instruction(m_buffer, 0x34000033, rd, rs1, rs2, bs); +} +} // namespace biscuit diff --git a/externals/biscuit/src/assembler_floating_point.cpp b/externals/biscuit/src/assembler_floating_point.cpp new file mode 100644 index 00000000..2c6d4901 --- /dev/null +++ b/externals/biscuit/src/assembler_floating_point.cpp @@ -0,0 +1,648 @@ +#include <biscuit/assert.hpp> +#include <biscuit/assembler.hpp> + +#include <algorithm> +#include <array> +#include <cstring> +#include <iterator> + +#include "assembler_util.hpp" + +// Various floating-point-based extension instructions. + +namespace biscuit { + +// RV32F Extension Instructions + +void Assembler::FADD_S(FPR rd, FPR rs1, FPR rs2, RMode rmode) noexcept { + EmitRType(m_buffer, 0b0000000, rs2, rs1, rmode, rd, 0b1010011); +} +void Assembler::FCLASS_S(GPR rd, FPR rs1) noexcept { + EmitRType(m_buffer, 0b1110000, f0, rs1, 0b001, rd, 0b1010011); +} +void Assembler::FCVT_S_W(FPR rd, GPR rs1, RMode rmode) noexcept { + EmitRType(m_buffer, 0b1101000, f0, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011); +} +void Assembler::FCVT_S_WU(FPR rd, GPR rs1, RMode rmode) noexcept { + EmitRType(m_buffer, 0b1101000, f1, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011); +} +void Assembler::FCVT_W_S(GPR rd, FPR rs1, RMode rmode) noexcept { + EmitRType(m_buffer, 0b1100000, f0, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011); +} +void Assembler::FCVT_WU_S(GPR rd, FPR rs1, RMode rmode) noexcept { + EmitRType(m_buffer, 0b1100000, f1, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011); +} +void Assembler::FDIV_S(FPR rd, FPR rs1, FPR rs2, RMode rmode) noexcept { + EmitRType(m_buffer, 0b0001100, rs2, rs1, rmode, rd, 0b1010011); +} +void Assembler::FEQ_S(GPR rd, FPR rs1, FPR rs2) noexcept { + EmitRType(m_buffer, 0b1010000, rs2, rs1, 0b010, rd, 0b1010011); +} +void Assembler::FLE_S(GPR rd, FPR rs1, FPR rs2) noexcept { + EmitRType(m_buffer, 0b1010000, rs2, rs1, 0b000, rd, 0b1010011); +} +void Assembler::FLT_S(GPR rd, FPR rs1, FPR rs2) noexcept { + EmitRType(m_buffer, 0b1010000, rs2, rs1, 0b001, rd, 0b1010011); +} +void Assembler::FLW(FPR rd, int32_t offset, GPR rs) noexcept { + BISCUIT_ASSERT(IsValidSigned12BitImm(offset)); + EmitIType(m_buffer, static_cast<uint32_t>(offset), rs, 0b010, rd, 0b0000111); +} +void Assembler::FMADD_S(FPR rd, FPR rs1, FPR rs2, FPR rs3, RMode rmode) noexcept { + EmitR4Type(m_buffer, rs3, 0b00, rs2, rs1, rmode, rd, 0b1000011); +} +void Assembler::FMAX_S(FPR rd, FPR rs1, FPR rs2) noexcept { + EmitRType(m_buffer, 0b0010100, rs2, rs1, 0b001, rd, 0b1010011); +} +void Assembler::FMIN_S(FPR rd, FPR rs1, FPR rs2) noexcept { + EmitRType(m_buffer, 0b0010100, rs2, rs1, 0b000, rd, 0b1010011); +} +void Assembler::FMSUB_S(FPR rd, FPR rs1, FPR rs2, FPR rs3, RMode rmode) noexcept { + EmitR4Type(m_buffer, rs3, 0b00, rs2, rs1, rmode, rd, 0b1000111); +} +void Assembler::FMUL_S(FPR rd, FPR rs1, FPR rs2, RMode rmode) noexcept { + EmitRType(m_buffer, 0b0001000, rs2, rs1, rmode, rd, 0b1010011); +} +void Assembler::FMV_W_X(FPR rd, GPR rs1) noexcept { + EmitRType(m_buffer, 0b1111000, f0, rs1, 0b000, rd, 0b1010011); +} +void Assembler::FMV_X_W(GPR rd, FPR rs1) noexcept { + EmitRType(m_buffer, 0b1110000, f0, rs1, 0b000, rd, 0b1010011); +} +void Assembler::FNMADD_S(FPR rd, FPR rs1, FPR rs2, FPR rs3, RMode rmode) noexcept { + EmitR4Type(m_buffer, rs3, 0b00, rs2, rs1, rmode, rd, 0b1001111); +} +void Assembler::FNMSUB_S(FPR rd, FPR rs1, FPR rs2, FPR rs3, RMode rmode) noexcept { + EmitR4Type(m_buffer, rs3, 0b00, rs2, rs1, rmode, rd, 0b1001011); +} +void Assembler::FSGNJ_S(FPR rd, FPR rs1, FPR rs2) noexcept { + EmitRType(m_buffer, 0b0010000, rs2, rs1, 0b000, rd, 0b1010011); +} +void Assembler::FSGNJN_S(FPR rd, FPR rs1, FPR rs2) noexcept { + EmitRType(m_buffer, 0b0010000, rs2, rs1, 0b001, rd, 0b1010011); +} +void Assembler::FSGNJX_S(FPR rd, FPR rs1, FPR rs2) noexcept { + EmitRType(m_buffer, 0b0010000, rs2, rs1, 0b010, rd, 0b1010011); +} +void Assembler::FSQRT_S(FPR rd, FPR rs1, RMode rmode) noexcept { + EmitRType(m_buffer, 0b0101100, f0, rs1, rmode, rd, 0b1010011); +} +void Assembler::FSUB_S(FPR rd, FPR rs1, FPR rs2, RMode rmode) noexcept { + EmitRType(m_buffer, 0b0000100, rs2, rs1, rmode, rd, 0b1010011); +} +void Assembler::FSW(FPR rs2, int32_t offset, GPR rs1) noexcept { + BISCUIT_ASSERT(IsValidSigned12BitImm(offset)); + EmitSType(m_buffer, static_cast<uint32_t>(offset), rs2, rs1, 0b010, 0b0100111); +} + +void Assembler::FABS_S(FPR rd, FPR rs) noexcept { + FSGNJX_S(rd, rs, rs); +} +void Assembler::FMV_S(FPR rd, FPR rs) noexcept { + FSGNJ_S(rd, rs, rs); +} +void Assembler::FNEG_S(FPR rd, FPR rs) noexcept { + FSGNJN_S(rd, rs, rs); +} + +// RV64F Extension Instructions + +void Assembler::FCVT_L_S(GPR rd, FPR rs1, RMode rmode) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + EmitRType(m_buffer, 0b1100000, f2, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011); +} +void Assembler::FCVT_LU_S(GPR rd, FPR rs1, RMode rmode) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + EmitRType(m_buffer, 0b1100000, f3, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011); +} +void Assembler::FCVT_S_L(FPR rd, GPR rs1, RMode rmode) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + EmitRType(m_buffer, 0b1101000, f2, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011); +} +void Assembler::FCVT_S_LU(FPR rd, GPR rs1, RMode rmode) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + EmitRType(m_buffer, 0b1101000, f3, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011); +} + +// RV32D Extension Instructions + +void Assembler::FADD_D(FPR rd, FPR rs1, FPR rs2, RMode rmode) noexcept { + EmitRType(m_buffer, 0b0000001, rs2, rs1, rmode, rd, 0b1010011); +} +void Assembler::FCLASS_D(GPR rd, FPR rs1) noexcept { + EmitRType(m_buffer, 0b1110001, f0, rs1, 0b001, rd, 0b1010011); +} +void Assembler::FCVT_D_W(FPR rd, GPR rs1, RMode rmode) noexcept { + EmitRType(m_buffer, 0b1101001, f0, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011); +} +void Assembler::FCVT_D_WU(FPR rd, GPR rs1, RMode rmode) noexcept { + EmitRType(m_buffer, 0b1101001, f1, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011); +} +void Assembler::FCVT_W_D(GPR rd, FPR rs1, RMode rmode) noexcept { + EmitRType(m_buffer, 0b1100001, f0, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011); +} +void Assembler::FCVT_WU_D(GPR rd, FPR rs1, RMode rmode) noexcept { + EmitRType(m_buffer, 0b1100001, f1, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011); +} +void Assembler::FCVT_D_S(FPR rd, FPR rs1, RMode rmode) noexcept { + EmitRType(m_buffer, 0b0100001, f0, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011); +} +void Assembler::FCVT_S_D(FPR rd, FPR rs1, RMode rmode) noexcept { + EmitRType(m_buffer, 0b0100000, f1, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011); +} +void Assembler::FDIV_D(FPR rd, FPR rs1, FPR rs2, RMode rmode) noexcept { + EmitRType(m_buffer, 0b0001101, rs2, rs1, rmode, rd, 0b1010011); +} +void Assembler::FEQ_D(GPR rd, FPR rs1, FPR rs2) noexcept { + EmitRType(m_buffer, 0b1010001, rs2, rs1, 0b010, rd, 0b1010011); +} +void Assembler::FLE_D(GPR rd, FPR rs1, FPR rs2) noexcept { + EmitRType(m_buffer, 0b1010001, rs2, rs1, 0b000, rd, 0b1010011); +} +void Assembler::FLT_D(GPR rd, FPR rs1, FPR rs2) noexcept { + EmitRType(m_buffer, 0b1010001, rs2, rs1, 0b001, rd, 0b1010011); +} +void Assembler::FLD(FPR rd, int32_t offset, GPR rs) noexcept { + BISCUIT_ASSERT(IsValidSigned12BitImm(offset)); + EmitIType(m_buffer, static_cast<uint32_t>(offset), rs, 0b011, rd, 0b0000111); +} +void Assembler::FMADD_D(FPR rd, FPR rs1, FPR rs2, FPR rs3, RMode rmode) noexcept { + EmitR4Type(m_buffer, rs3, 0b01, rs2, rs1, rmode, rd, 0b1000011); +} +void Assembler::FMAX_D(FPR rd, FPR rs1, FPR rs2) noexcept { + EmitRType(m_buffer, 0b0010101, rs2, rs1, 0b001, rd, 0b1010011); +} +void Assembler::FMIN_D(FPR rd, FPR rs1, FPR rs2) noexcept { + EmitRType(m_buffer, 0b0010101, rs2, rs1, 0b000, rd, 0b1010011); +} +void Assembler::FMSUB_D(FPR rd, FPR rs1, FPR rs2, FPR rs3, RMode rmode) noexcept { + EmitR4Type(m_buffer, rs3, 0b01, rs2, rs1, rmode, rd, 0b1000111); +} +void Assembler::FMUL_D(FPR rd, FPR rs1, FPR rs2, RMode rmode) noexcept { + EmitRType(m_buffer, 0b0001001, rs2, rs1, rmode, rd, 0b1010011); +} +void Assembler::FNMADD_D(FPR rd, FPR rs1, FPR rs2, FPR rs3, RMode rmode) noexcept { + EmitR4Type(m_buffer, rs3, 0b01, rs2, rs1, rmode, rd, 0b1001111); +} +void Assembler::FNMSUB_D(FPR rd, FPR rs1, FPR rs2, FPR rs3, RMode rmode) noexcept { + EmitR4Type(m_buffer, rs3, 0b01, rs2, rs1, rmode, rd, 0b1001011); +} +void Assembler::FSGNJ_D(FPR rd, FPR rs1, FPR rs2) noexcept { + EmitRType(m_buffer, 0b0010001, rs2, rs1, 0b000, rd, 0b1010011); +} +void Assembler::FSGNJN_D(FPR rd, FPR rs1, FPR rs2) noexcept { + EmitRType(m_buffer, 0b0010001, rs2, rs1, 0b001, rd, 0b1010011); +} +void Assembler::FSGNJX_D(FPR rd, FPR rs1, FPR rs2) noexcept { + EmitRType(m_buffer, 0b0010001, rs2, rs1, 0b010, rd, 0b1010011); +} +void Assembler::FSQRT_D(FPR rd, FPR rs1, RMode rmode) noexcept { + EmitRType(m_buffer, 0b0101101, f0, rs1, rmode, rd, 0b1010011); +} +void Assembler::FSUB_D(FPR rd, FPR rs1, FPR rs2, RMode rmode) noexcept { + EmitRType(m_buffer, 0b0000101, rs2, rs1, rmode, rd, 0b1010011); +} +void Assembler::FSD(FPR rs2, int32_t offset, GPR rs1) noexcept { + BISCUIT_ASSERT(IsValidSigned12BitImm(offset)); + EmitSType(m_buffer, static_cast<uint32_t>(offset), rs2, rs1, 0b011, 0b0100111); +} + +void Assembler::FABS_D(FPR rd, FPR rs) noexcept { + FSGNJX_D(rd, rs, rs); +} +void Assembler::FMV_D(FPR rd, FPR rs) noexcept { + FSGNJ_D(rd, rs, rs); +} +void Assembler::FNEG_D(FPR rd, FPR rs) noexcept { + FSGNJN_D(rd, rs, rs); +} + +// RV64D Extension Instructions + +void Assembler::FCVT_L_D(GPR rd, FPR rs1, RMode rmode) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + EmitRType(m_buffer, 0b1100001, f2, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011); +} +void Assembler::FCVT_LU_D(GPR rd, FPR rs1, RMode rmode) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + EmitRType(m_buffer, 0b1100001, f3, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011); +} +void Assembler::FCVT_D_L(FPR rd, GPR rs1, RMode rmode) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + EmitRType(m_buffer, 0b1101001, f2, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011); +} +void Assembler::FCVT_D_LU(FPR rd, GPR rs1, RMode rmode) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + EmitRType(m_buffer, 0b1101001, f3, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011); +} +void Assembler::FMV_D_X(FPR rd, GPR rs1) noexcept { + BISCUIT_ASSERT(IsRV64OrRV128(m_features)); + EmitRType(m_buffer, 0b1111001, f0, rs1, 0b000, rd, 0b1010011); +} +void Assembler::FMV_X_D(GPR rd, FPR rs1) noexcept { + BISCUIT_ASSERT(IsRV64OrRV128(m_features)); + EmitRType(m_buffer, 0b1110001, f0, rs1, 0b000, rd, 0b1010011); +} + +// RV32Q Extension Instructions + +void Assembler::FADD_Q(FPR rd, FPR rs1, FPR rs2, RMode rmode) noexcept { + EmitRType(m_buffer, 0b0000011, rs2, rs1, rmode, rd, 0b1010011); +} +void Assembler::FCLASS_Q(GPR rd, FPR rs1) noexcept { + EmitRType(m_buffer, 0b1110011, f0, rs1, 0b001, rd, 0b1010011); +} +void Assembler::FCVT_Q_W(FPR rd, GPR rs1, RMode rmode) noexcept { + EmitRType(m_buffer, 0b1101011, f0, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011); +} +void Assembler::FCVT_Q_WU(FPR rd, GPR rs1, RMode rmode) noexcept { + EmitRType(m_buffer, 0b1101011, f1, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011); +} +void Assembler::FCVT_W_Q(GPR rd, FPR rs1, RMode rmode) noexcept { + EmitRType(m_buffer, 0b1100011, f0, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011); +} +void Assembler::FCVT_WU_Q(GPR rd, FPR rs1, RMode rmode) noexcept { + EmitRType(m_buffer, 0b1100011, f1, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011); +} +void Assembler::FCVT_Q_D(FPR rd, FPR rs1, RMode rmode) noexcept { + EmitRType(m_buffer, 0b0100011, f1, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011); +} +void Assembler::FCVT_D_Q(FPR rd, FPR rs1, RMode rmode) noexcept { + EmitRType(m_buffer, 0b0100001, f3, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011); +} +void Assembler::FCVT_Q_S(FPR rd, FPR rs1, RMode rmode) noexcept { + EmitRType(m_buffer, 0b0100011, f0, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011); +} +void Assembler::FCVT_S_Q(FPR rd, FPR rs1, RMode rmode) noexcept { + EmitRType(m_buffer, 0b0100000, f3, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011); +} +void Assembler::FDIV_Q(FPR rd, FPR rs1, FPR rs2, RMode rmode) noexcept { + EmitRType(m_buffer, 0b0001111, rs2, rs1, rmode, rd, 0b1010011); +} +void Assembler::FEQ_Q(GPR rd, FPR rs1, FPR rs2) noexcept { + EmitRType(m_buffer, 0b1010011, rs2, rs1, 0b010, rd, 0b1010011); +} +void Assembler::FLE_Q(GPR rd, FPR rs1, FPR rs2) noexcept { + EmitRType(m_buffer, 0b1010011, rs2, rs1, 0b000, rd, 0b1010011); +} +void Assembler::FLT_Q(GPR rd, FPR rs1, FPR rs2) noexcept { + EmitRType(m_buffer, 0b1010011, rs2, rs1, 0b001, rd, 0b1010011); +} +void Assembler::FLQ(FPR rd, int32_t offset, GPR rs) noexcept { + BISCUIT_ASSERT(IsValidSigned12BitImm(offset)); + EmitIType(m_buffer, static_cast<uint32_t>(offset), rs, 0b100, rd, 0b0000111); +} +void Assembler::FMADD_Q(FPR rd, FPR rs1, FPR rs2, FPR rs3, RMode rmode) noexcept { + EmitR4Type(m_buffer, rs3, 0b11, rs2, rs1, rmode, rd, 0b1000011); +} +void Assembler::FMAX_Q(FPR rd, FPR rs1, FPR rs2) noexcept { + EmitRType(m_buffer, 0b0010111, rs2, rs1, 0b001, rd, 0b1010011); +} +void Assembler::FMIN_Q(FPR rd, FPR rs1, FPR rs2) noexcept { + EmitRType(m_buffer, 0b0010111, rs2, rs1, 0b000, rd, 0b1010011); +} +void Assembler::FMSUB_Q(FPR rd, FPR rs1, FPR rs2, FPR rs3, RMode rmode) noexcept { + EmitR4Type(m_buffer, rs3, 0b11, rs2, rs1, rmode, rd, 0b1000111); +} +void Assembler::FMUL_Q(FPR rd, FPR rs1, FPR rs2, RMode rmode) noexcept { + EmitRType(m_buffer, 0b0001011, rs2, rs1, rmode, rd, 0b1010011); +} +void Assembler::FNMADD_Q(FPR rd, FPR rs1, FPR rs2, FPR rs3, RMode rmode) noexcept { + EmitR4Type(m_buffer, rs3, 0b11, rs2, rs1, rmode, rd, 0b1001111); +} +void Assembler::FNMSUB_Q(FPR rd, FPR rs1, FPR rs2, FPR rs3, RMode rmode) noexcept { + EmitR4Type(m_buffer, rs3, 0b11, rs2, rs1, rmode, rd, 0b1001011); +} +void Assembler::FSGNJ_Q(FPR rd, FPR rs1, FPR rs2) noexcept { + EmitRType(m_buffer, 0b0010011, rs2, rs1, 0b000, rd, 0b1010011); +} +void Assembler::FSGNJN_Q(FPR rd, FPR rs1, FPR rs2) noexcept { + EmitRType(m_buffer, 0b0010011, rs2, rs1, 0b001, rd, 0b1010011); +} +void Assembler::FSGNJX_Q(FPR rd, FPR rs1, FPR rs2) noexcept { + EmitRType(m_buffer, 0b0010011, rs2, rs1, 0b010, rd, 0b1010011); +} +void Assembler::FSQRT_Q(FPR rd, FPR rs1, RMode rmode) noexcept { + EmitRType(m_buffer, 0b0101111, f0, rs1, rmode, rd, 0b1010011); +} +void Assembler::FSUB_Q(FPR rd, FPR rs1, FPR rs2, RMode rmode) noexcept { + EmitRType(m_buffer, 0b0000111, rs2, rs1, rmode, rd, 0b1010011); +} +void Assembler::FSQ(FPR rs2, int32_t offset, GPR rs1) noexcept { + BISCUIT_ASSERT(IsValidSigned12BitImm(offset)); + EmitSType(m_buffer, static_cast<uint32_t>(offset), rs2, rs1, 0b100, 0b0100111); +} + +void Assembler::FABS_Q(FPR rd, FPR rs) noexcept { + FSGNJX_Q(rd, rs, rs); +} +void Assembler::FMV_Q(FPR rd, FPR rs) noexcept { + FSGNJ_Q(rd, rs, rs); +} +void Assembler::FNEG_Q(FPR rd, FPR rs) noexcept { + FSGNJN_Q(rd, rs, rs); +} + +// RV64Q Extension Instructions + +void Assembler::FCVT_L_Q(GPR rd, FPR rs1, RMode rmode) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + EmitRType(m_buffer, 0b1100011, f2, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011); +} +void Assembler::FCVT_LU_Q(GPR rd, FPR rs1, RMode rmode) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + EmitRType(m_buffer, 0b1100011, f3, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011); +} +void Assembler::FCVT_Q_L(FPR rd, GPR rs1, RMode rmode) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + EmitRType(m_buffer, 0b1101011, f2, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011); +} +void Assembler::FCVT_Q_LU(FPR rd, GPR rs1, RMode rmode) noexcept { + BISCUIT_ASSERT(IsRV64(m_features)); + EmitRType(m_buffer, 0b1101011, f3, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011); +} + +// RV32Zfh Extension Instructions + +void Assembler::FADD_H(FPR rd, FPR rs1, FPR rs2, RMode rmode) noexcept { + EmitRType(m_buffer, 0b0000010, rs2, rs1, rmode, rd, 0b1010011); +} +void Assembler::FCLASS_H(GPR rd, FPR rs1) noexcept { + EmitRType(m_buffer, 0b1110010, f0, rs1, 0b001, rd, 0b1010011); +} +void Assembler::FCVT_D_H(FPR rd, FPR rs1, RMode rmode) noexcept { + EmitRType(m_buffer, 0b0100001, f2, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011); +} +void Assembler::FCVT_H_D(FPR rd, FPR rs1, RMode rmode) noexcept { + EmitRType(m_buffer, 0b0100010, f1, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011); +} +void Assembler::FCVT_H_Q(FPR rd, FPR rs1, RMode rmode) noexcept { + EmitRType(m_buffer, 0b0100010, f3, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011); +} +void Assembler::FCVT_H_S(FPR rd, FPR rs1, RMode rmode) noexcept { + EmitRType(m_buffer, 0b0100010, f0, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011); +} +void Assembler::FCVT_H_W(FPR rd, GPR rs1, RMode rmode) noexcept { + EmitRType(m_buffer, 0b1101010, f0, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011); +} +void Assembler::FCVT_H_WU(FPR rd, GPR rs1, RMode rmode) noexcept { + EmitRType(m_buffer, 0b1101010, f1, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011); +} +void Assembler::FCVT_Q_H(FPR rd, FPR rs1, RMode rmode) noexcept { + EmitRType(m_buffer, 0b0100011, f2, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011); +} +void Assembler::FCVT_S_H(FPR rd, FPR rs1, RMode rmode) noexcept { + EmitRType(m_buffer, 0b0100000, f2, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011); +} +void Assembler::FCVT_W_H(GPR rd, FPR rs1, RMode rmode) noexcept { + EmitRType(m_buffer, 0b1100010, f0, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011); +} +void Assembler::FCVT_WU_H(GPR rd, FPR rs1, RMode rmode) noexcept { + EmitRType(m_buffer, 0b1100010, f1, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011); +} +void Assembler::FDIV_H(FPR rd, FPR rs1, FPR rs2, RMode rmode) noexcept { + EmitRType(m_buffer, 0b0001110, rs2, rs1, rmode, rd, 0b1010011); +} +void Assembler::FEQ_H(GPR rd, FPR rs1, FPR rs2) noexcept { + EmitRType(m_buffer, 0b1010010, rs2, rs1, 0b010, rd, 0b1010011); +} +void Assembler::FLE_H(GPR rd, FPR rs1, FPR rs2) noexcept { + EmitRType(m_buffer, 0b1010010, rs2, rs1, 0b000, rd, 0b1010011); +} +void Assembler::FLH(FPR rd, int32_t offset, GPR rs) noexcept { + BISCUIT_ASSERT(IsValidSigned12BitImm(offset)); + EmitIType(m_buffer, static_cast<uint32_t>(offset), rs, 0b001, rd, 0b0000111); +} +void Assembler::FLT_H(GPR rd, FPR rs1, FPR rs2) noexcept { + EmitRType(m_buffer, 0b1010010, rs2, rs1, 0b001, rd, 0b1010011); +} +void Assembler::FMADD_H(FPR rd, FPR rs1, FPR rs2, FPR rs3, RMode rmode) noexcept { + EmitR4Type(m_buffer, rs3, 0b10, rs2, rs1, rmode, rd, 0b1000011); +} +void Assembler::FMAX_H(FPR rd, FPR rs1, FPR rs2) noexcept { + EmitRType(m_buffer, 0b0010110, rs2, rs1, 0b001, rd, 0b1010011); +} +void Assembler::FMIN_H(FPR rd, FPR rs1, FPR rs2) noexcept { + EmitRType(m_buffer, 0b0010110, rs2, rs1, 0b000, rd, 0b1010011); +} +void Assembler::FMSUB_H(FPR rd, FPR rs1, FPR rs2, FPR rs3, RMode rmode) noexcept { + EmitR4Type(m_buffer, rs3, 0b10, rs2, rs1, rmode, rd, 0b1000111); +} +void Assembler::FMUL_H(FPR rd, FPR rs1, FPR rs2, RMode rmode) noexcept { + EmitRType(m_buffer, 0b0001010, rs2, rs1, rmode, rd, 0b1010011); +} +void Assembler::FMV_H_X(FPR rd, GPR rs1) noexcept { + EmitRType(m_buffer, 0b1111010, f0, rs1, 0b000, rd, 0b1010011); +} +void Assembler::FMV_X_H(GPR rd, FPR rs1) noexcept { + EmitRType(m_buffer, 0b1110010, f0, rs1, 0b000, rd, 0b1010011); +} +void Assembler::FNMADD_H(FPR rd, FPR rs1, FPR rs2, FPR rs3, RMode rmode) noexcept { + EmitR4Type(m_buffer, rs3, 0b10, rs2, rs1, rmode, rd, 0b1001111); +} +void Assembler::FNMSUB_H(FPR rd, FPR rs1, FPR rs2, FPR rs3, RMode rmode) noexcept { + EmitR4Type(m_buffer, rs3, 0b10, rs2, rs1, rmode, rd, 0b1001011); +} +void Assembler::FSGNJ_H(FPR rd, FPR rs1, FPR rs2) noexcept { + EmitRType(m_buffer, 0b0010010, rs2, rs1, 0b000, rd, 0b1010011); +} +void Assembler::FSGNJN_H(FPR rd, FPR rs1, FPR rs2) noexcept { + EmitRType(m_buffer, 0b0010010, rs2, rs1, 0b001, rd, 0b1010011); +} +void Assembler::FSGNJX_H(FPR rd, FPR rs1, FPR rs2) noexcept { + EmitRType(m_buffer, 0b0010010, rs2, rs1, 0b010, rd, 0b1010011); +} +void Assembler::FSH(FPR rs2, int32_t offset, GPR rs1) noexcept { + BISCUIT_ASSERT(IsValidSigned12BitImm(offset)); + EmitSType(m_buffer, static_cast<uint32_t>(offset), rs2, rs1, 0b001, 0b0100111); +} +void Assembler::FSQRT_H(FPR rd, FPR rs1, RMode rmode) noexcept { + EmitRType(m_buffer, 0b0101110, f0, rs1, rmode, rd, 0b1010011); +} +void Assembler::FSUB_H(FPR rd, FPR rs1, FPR rs2, RMode rmode) noexcept { + EmitRType(m_buffer, 0b0000110, rs2, rs1, rmode, rd, 0b1010011); +} + +// RV64Zfh Extension Instructions + +void Assembler::FCVT_L_H(GPR rd, FPR rs1, RMode rmode) noexcept { + EmitRType(m_buffer, 0b1100010, f2, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011); +} +void Assembler::FCVT_LU_H(GPR rd, FPR rs1, RMode rmode) noexcept { + EmitRType(m_buffer, 0b1100010, f3, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011); +} +void Assembler::FCVT_H_L(FPR rd, GPR rs1, RMode rmode) noexcept { + EmitRType(m_buffer, 0b1101010, f2, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011); +} +void Assembler::FCVT_H_LU(FPR rd, GPR rs1, RMode rmode) noexcept { + EmitRType(m_buffer, 0b1101010, f3, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011); +} + +// Zfa Extension Instructions + +static void FLIImpl(CodeBuffer& buffer, uint32_t funct7, FPR rd, double value) noexcept { + static constexpr std::array fli_table{ + 0xBFF0000000000000ULL, // -1.0 + 0x0010000000000000ULL, // Minimum positive normal + 0x3EF0000000000000ULL, // 1.0 * 2^-16 + 0x3F00000000000000ULL, // 1.0 * 2^-15 + 0x3F70000000000000ULL, // 1.0 * 2^-8 + 0x3F80000000000000ULL, // 1.0 * 2^-7 + 0x3FB0000000000000ULL, // 1.0 * 2^-4 + 0x3FC0000000000000ULL, // 1.0 * 2^-3 + 0x3FD0000000000000ULL, // 0.25 + 0x3FD4000000000000ULL, // 0.3125 + 0x3FD8000000000000ULL, // 0.375 + 0x3FDC000000000000ULL, // 0.4375 + 0x3FE0000000000000ULL, // 0.5 + 0x3FE4000000000000ULL, // 0.625 + 0x3FE8000000000000ULL, // 0.75 + 0x3FEC000000000000ULL, // 0.875 + 0x3FF0000000000000ULL, // 1.0 + 0x3FF4000000000000ULL, // 1.25 + 0x3FF8000000000000ULL, // 1.5 + 0x3FFC000000000000ULL, // 1.75 + 0x4000000000000000ULL, // 2.0 + 0x4004000000000000ULL, // 2.5 + 0x4008000000000000ULL, // 3 + 0x4010000000000000ULL, // 4 + 0x4020000000000000ULL, // 8 + 0x4030000000000000ULL, // 16 + 0x4060000000000000ULL, // 2^7 + 0x4070000000000000ULL, // 2^8 + 0x40E0000000000000ULL, // 2^15 + 0x40F0000000000000ULL, // 2^16 + 0x7FF0000000000000ULL, // +inf + 0x7FF8000000000000ULL, // Canonical NaN + }; + + uint64_t ivalue{}; + std::memcpy(&ivalue, &value, sizeof(uint64_t)); + + const auto iter = std::find_if(fli_table.cbegin(), fli_table.cend(), [ivalue](uint64_t entry) { + return entry == ivalue; + }); + BISCUIT_ASSERT(iter != fli_table.cend()); + + const auto index = static_cast<uint32_t>(std::distance(fli_table.cbegin(), iter)); + EmitRType(buffer, funct7, f1, GPR{index}, 0b000, rd, 0b1010011); +} + +void Assembler::FLI_D(FPR rd, double value) noexcept { + FLIImpl(m_buffer, 0b1111001, rd, value); +} +void Assembler::FLI_H(FPR rd, double value) noexcept { + FLIImpl(m_buffer, 0b1111010, rd, value); +} +void Assembler::FLI_S(FPR rd, double value) noexcept { + FLIImpl(m_buffer, 0b1111000, rd, value); +} + +void Assembler::FMINM_D(FPR rd, FPR rs1, FPR rs2) noexcept { + EmitRType(m_buffer, 0b0010101, rs2, rs1, 0b010, rd, 0b1010011); +} +void Assembler::FMINM_H(FPR rd, FPR rs1, FPR rs2) noexcept { + EmitRType(m_buffer, 0b0010110, rs2, rs1, 0b010, rd, 0b1010011); +} +void Assembler::FMINM_Q(FPR rd, FPR rs1, FPR rs2) noexcept { + EmitRType(m_buffer, 0b0010111, rs2, rs1, 0b010, rd, 0b1010011); +} +void Assembler::FMINM_S(FPR rd, FPR rs1, FPR rs2) noexcept { + EmitRType(m_buffer, 0b0010100, rs2, rs1, 0b010, rd, 0b1010011); +} + +void Assembler::FMAXM_D(FPR rd, FPR rs1, FPR rs2) noexcept { + EmitRType(m_buffer, 0b0010101, rs2, rs1, 0b011, rd, 0b1010011); +} +void Assembler::FMAXM_H(FPR rd, FPR rs1, FPR rs2) noexcept { + EmitRType(m_buffer, 0b0010110, rs2, rs1, 0b011, rd, 0b1010011); +} +void Assembler::FMAXM_Q(FPR rd, FPR rs1, FPR rs2) noexcept { + EmitRType(m_buffer, 0b0010111, rs2, rs1, 0b011, rd, 0b1010011); +} +void Assembler::FMAXM_S(FPR rd, FPR rs1, FPR rs2) noexcept { + EmitRType(m_buffer, 0b0010100, rs2, rs1, 0b011, rd, 0b1010011); +} + +void Assembler::FROUND_D(FPR rd, FPR rs1, RMode rmode) noexcept { + EmitRType(m_buffer, 0b0100001, f4, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011); +} +void Assembler::FROUND_H(FPR rd, FPR rs1, RMode rmode) noexcept { + EmitRType(m_buffer, 0b0100010, f4, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011); +} +void Assembler::FROUND_Q(FPR rd, FPR rs1, RMode rmode) noexcept { + EmitRType(m_buffer, 0b0100011, f4, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011); +} +void Assembler::FROUND_S(FPR rd, FPR rs1, RMode rmode) noexcept { + EmitRType(m_buffer, 0b0100000, f4, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011); +} + +void Assembler::FROUNDNX_D(FPR rd, FPR rs1, RMode rmode) noexcept { + EmitRType(m_buffer, 0b0100001, f5, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011); +} +void Assembler::FROUNDNX_H(FPR rd, FPR rs1, RMode rmode) noexcept { + EmitRType(m_buffer, 0b0100010, f5, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011); +} +void Assembler::FROUNDNX_Q(FPR rd, FPR rs1, RMode rmode) noexcept { + EmitRType(m_buffer, 0b0100011, f5, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011); +} +void Assembler::FROUNDNX_S(FPR rd, FPR rs1, RMode rmode) noexcept { + EmitRType(m_buffer, 0b0100000, f5, rs1, static_cast<uint32_t>(rmode), rd, 0b1010011); +} + +void Assembler::FCVTMOD_W_D(GPR rd, FPR rs1) noexcept { + EmitRType(m_buffer, 0b1100001, f8, rs1, static_cast<uint32_t>(RMode::RTZ), rd, 0b1010011); +} + +void Assembler::FMVH_X_D(GPR rd, FPR rs1) noexcept { + EmitRType(m_buffer, 0b1110001, f1, rs1, 0b000, rd, 0b1010011); +} +void Assembler::FMVH_X_Q(GPR rd, FPR rs1) noexcept { + EmitRType(m_buffer, 0b1110011, f1, rs1, 0b000, rd, 0b1010011); +} +void Assembler::FMVP_D_X(FPR rd, GPR rs1, GPR rs2) noexcept { + EmitRType(m_buffer, 0b1011001, rs2, rs1, 0b000, rd, 0b1010011); +} +void Assembler::FMVP_Q_X(FPR rd, GPR rs1, GPR rs2) noexcept { + EmitRType(m_buffer, 0b1011011, rs2, rs1, 0b000, rd, 0b1010011); +} + +void Assembler::FLEQ_D(GPR rd, FPR rs1, FPR rs2) noexcept { + EmitRType(m_buffer, 0b1010001, rs2, rs1, 0b100, rd, 0b1010011); +} +void Assembler::FLTQ_D(GPR rd, FPR rs1, FPR rs2) noexcept { + EmitRType(m_buffer, 0b1010001, rs2, rs1, 0b101, rd, 0b1010011); +} + +void Assembler::FLEQ_H(GPR rd, FPR rs1, FPR rs2) noexcept { + EmitRType(m_buffer, 0b1010010, rs2, rs1, 0b100, rd, 0b1010011); +} +void Assembler::FLTQ_H(GPR rd, FPR rs1, FPR rs2) noexcept { + EmitRType(m_buffer, 0b1010010, rs2, rs1, 0b101, rd, 0b1010011); +} + +void Assembler::FLEQ_Q(GPR rd, FPR rs1, FPR rs2) noexcept { + EmitRType(m_buffer, 0b1010011, rs2, rs1, 0b100, rd, 0b1010011); +} +void Assembler::FLTQ_Q(GPR rd, FPR rs1, FPR rs2) noexcept { + EmitRType(m_buffer, 0b1010011, rs2, rs1, 0b101, rd, 0b1010011); +} + +void Assembler::FLEQ_S(GPR rd, FPR rs1, FPR rs2) noexcept { + EmitRType(m_buffer, 0b1010000, rs2, rs1, 0b100, rd, 0b1010011); +} +void Assembler::FLTQ_S(GPR rd, FPR rs1, FPR rs2) noexcept { + EmitRType(m_buffer, 0b1010000, rs2, rs1, 0b101, rd, 0b1010011); +} + +// Zfbfmin, Zvfbfmin, Zvfbfwma Extension Instructions + +void Assembler::FCVT_BF16_S(FPR rd, FPR rs, RMode rmode) noexcept { + EmitRType(m_buffer, 0b0100010, f8, rs, static_cast<uint32_t>(rmode), rd, 0b1010011); +} + +void Assembler::FCVT_S_BF16(FPR rd, FPR rs, RMode rmode) noexcept { + EmitRType(m_buffer, 0b0100000, f6, rs, static_cast<uint32_t>(rmode), rd, 0b1010011); +} + +} // namespace biscuit diff --git a/externals/biscuit/src/assembler_util.hpp b/externals/biscuit/src/assembler_util.hpp new file mode 100644 index 00000000..fdb98940 --- /dev/null +++ b/externals/biscuit/src/assembler_util.hpp @@ -0,0 +1,224 @@ +#pragma once + +#include <biscuit/assert.hpp> +#include <biscuit/code_buffer.hpp> +#include <biscuit/registers.hpp> + +#include <cstddef> +#include <cstdint> + +// Generic internal utility header for various helper functions related +// to encoding instructions. + +namespace biscuit { +// Determines if a value lies within the range of a 6-bit immediate. +[[nodiscard]] constexpr bool IsValidSigned6BitImm(ptrdiff_t value) { + return value >= -32 && value <= 31; +} + +// S-type and I-type immediates are 12 bits in size +[[nodiscard]] constexpr bool IsValidSigned12BitImm(ptrdiff_t value) { + return value >= -2048 && value <= 2047; +} + +// B-type immediates only provide -4KiB to +4KiB range branches. +[[nodiscard]] constexpr bool IsValidBTypeImm(ptrdiff_t value) { + return value >= -4096 && value <= 4095; +} + +// J-type immediates only provide -1MiB to +1MiB range branches. +[[nodiscard]] constexpr bool IsValidJTypeImm(ptrdiff_t value) { + return value >= -0x80000 && value <= 0x7FFFF; +} + +// CB-type immediates only provide -256B to +256B range branches. +[[nodiscard]] constexpr bool IsValidCBTypeImm(ptrdiff_t value) { + return value >= -256 && value <= 255; +} + +// CJ-type immediates only provide -2KiB to +2KiB range branches. +[[nodiscard]] constexpr bool IsValidCJTypeImm(ptrdiff_t value) { + return IsValidSigned12BitImm(value); +} + +// Determines whether or not the register fits in 3-bit compressed encoding. +[[nodiscard]] constexpr bool IsValid3BitCompressedReg(Register reg) { + const auto index = reg.Index(); + return index >= 8 && index <= 15; +} + +// Determines whether or not the given shift amount is valid for a compressed shift instruction +[[nodiscard]] constexpr bool IsValidCompressedShiftAmount(uint32_t shift) { + return shift > 0 && shift <= 64; +} + +// Turns a compressed register into its encoding. +[[nodiscard]] constexpr uint32_t CompressedRegTo3BitEncoding(Register reg) { + return reg.Index() - 8; +} + +// Transforms a regular value into an immediate encoded in a B-type instruction. +[[nodiscard]] constexpr uint32_t TransformToBTypeImm(uint32_t imm) { + // clang-format off + return ((imm & 0x07E0) << 20) | + ((imm & 0x1000) << 19) | + ((imm & 0x001E) << 7) | + ((imm & 0x0800) >> 4); + // clang-format on +} + +// Transforms a regular value into an immediate encoded in a J-type instruction. +[[nodiscard]] constexpr uint32_t TransformToJTypeImm(uint32_t imm) { + // clang-format off + return ((imm & 0x0FF000) >> 0) | + ((imm & 0x000800) << 9) | + ((imm & 0x0007FE) << 20) | + ((imm & 0x100000) << 11); + // clang-format on +} + +// Transforms a regular value into an immediate encoded in a CB-type instruction. +[[nodiscard]] constexpr uint32_t TransformToCBTypeImm(uint32_t imm) { + // clang-format off + return ((imm & 0x0C0) >> 1) | + ((imm & 0x006) << 2) | + ((imm & 0x020) >> 3) | + ((imm & 0x018) << 7) | + ((imm & 0x100) << 4); + // clang-format on +} + +// Transforms a regular value into an immediate encoded in a CJ-type instruction. +[[nodiscard]] constexpr uint32_t TransformToCJTypeImm(uint32_t imm) { + // clang-format off + return ((imm & 0x800) << 1) | + ((imm & 0x010) << 7) | + ((imm & 0x300) << 1) | + ((imm & 0x400) >> 2) | + ((imm & 0x040) << 1) | + ((imm & 0x080) >> 1) | + ((imm & 0x00E) << 4) | + ((imm & 0x020) >> 3); + // clang-format on +} + +// Emits a B type RISC-V instruction. These consist of: +// imm[12|10:5] | rs2 | rs1 | funct3 | imm[4:1] | imm[11] | opcode +inline void EmitBType(CodeBuffer& buffer, uint32_t imm, GPR rs2, GPR rs1, + uint32_t funct3, uint32_t opcode) { + imm &= 0x1FFE; + + buffer.Emit32(TransformToBTypeImm(imm) | (rs2.Index() << 20) | (rs1.Index() << 15) | + ((funct3 & 0b111) << 12) | (opcode & 0x7F)); +} + +// Emits a I type RISC-V instruction. These consist of: +// imm[11:0] | rs1 | funct3 | rd | opcode +inline void EmitIType(CodeBuffer& buffer, uint32_t imm, Register rs1, uint32_t funct3, + Register rd, uint32_t opcode) { + imm &= 0xFFF; + + buffer.Emit32((imm << 20) | (rs1.Index() << 15) | ((funct3 & 0b111) << 12) | + (rd.Index() << 7) | (opcode & 0x7F)); +} + +// Emits a J type RISC-V instruction. These consist of: +// imm[20|10:1|11|19:12] | rd | opcode +inline void EmitJType(CodeBuffer& buffer, uint32_t imm, GPR rd, uint32_t opcode) { + imm &= 0x1FFFFE; + + buffer.Emit32(TransformToJTypeImm(imm) | rd.Index() << 7 | (opcode & 0x7F)); +} + +// Emits a R type RISC instruction. These consist of: +// funct7 | rs2 | rs1 | funct3 | rd | opcode +inline void EmitRType(CodeBuffer& buffer, uint32_t funct7, Register rs2, Register rs1, + uint32_t funct3, Register rd, uint32_t opcode) { + // clang-format off + const auto value = ((funct7 & 0xFF) << 25) | + (rs2.Index() << 20) | + (rs1.Index() << 15) | + ((funct3 & 0b111) << 12) | + (rd.Index() << 7) | + (opcode & 0x7F); + // clang-format off + + buffer.Emit32(value); +} + +// Emits a R type RISC instruction. These consist of: +// funct7 | rs2 | rs1 | funct3 | rd | opcode +inline void EmitRType(CodeBuffer& buffer, uint32_t funct7, FPR rs2, FPR rs1, RMode funct3, + FPR rd, uint32_t opcode) { + EmitRType(buffer, funct7, rs2, rs1, static_cast<uint32_t>(funct3), rd, opcode); +} + +// Emits a R4 type RISC instruction. These consist of: +// rs3 | funct2 | rs2 | rs1 | funct3 | rd | opcode +inline void EmitR4Type(CodeBuffer& buffer, FPR rs3, uint32_t funct2, FPR rs2, FPR rs1, + RMode funct3, FPR rd, uint32_t opcode) { + const auto reg_bits = (rs3.Index() << 27) | (rs2.Index() << 20) | (rs1.Index() << 15) | (rd.Index() << 7); + const auto funct_bits = ((funct2 & 0b11) << 25) | (static_cast<uint32_t>(funct3) << 12); + buffer.Emit32(reg_bits | funct_bits | (opcode & 0x7F)); +} + +// Emits a S type RISC-V instruction. These consist of: +// imm[11:5] | rs2 | rs1 | funct3 | imm[4:0] | opcode +inline void EmitSType(CodeBuffer& buffer, uint32_t imm, Register rs2, GPR rs1, + uint32_t funct3, uint32_t opcode) { + imm &= 0xFFF; + + // clang-format off + const auto new_imm = ((imm & 0x01F) << 7) | + ((imm & 0xFE0) << 20); + // clang-format on + + buffer.Emit32(new_imm | (rs2.Index() << 20) | (rs1.Index() << 15) | + ((funct3 & 0b111) << 12) | (opcode & 0x7F)); +} + +// Emits a U type RISC-V instruction. These consist of: +// imm[31:12] | rd | opcode +inline void EmitUType(CodeBuffer& buffer, uint32_t imm, GPR rd, uint32_t opcode) { + buffer.Emit32((imm & 0x000FFFFF) << 12 | rd.Index() << 7 | (opcode & 0x7F)); +} + +// Emits an atomic instruction. +inline void EmitAtomic(CodeBuffer& buffer, uint32_t funct5, Ordering ordering, GPR rs2, GPR rs1, + uint32_t funct3, GPR rd, uint32_t opcode) noexcept { + const auto funct7 = (funct5 << 2) | static_cast<uint32_t>(ordering); + EmitRType(buffer, funct7, rs2, rs1, funct3, rd, opcode); +} + +// Emits a fence instruction +inline void EmitFENCE(CodeBuffer& buffer, uint32_t fm, FenceOrder pred, FenceOrder succ, + GPR rs, uint32_t funct3, GPR rd, uint32_t opcode) noexcept { + // clang-format off + buffer.Emit32(((fm & 0b1111) << 28) | + (static_cast<uint32_t>(pred) << 24) | + (static_cast<uint32_t>(succ) << 20) | + (rs.Index() << 15) | + ((funct3 & 0b111) << 12) | + (rd.Index() << 7) | + (opcode & 0x7F)); + // clang-format on +} + +// Internal helpers for siloing away particular comparisons for behavior. +constexpr bool IsRV32(ArchFeature feature) { + return feature == ArchFeature::RV32; +} +constexpr bool IsRV64(ArchFeature feature) { + return feature == ArchFeature::RV64; +} +constexpr bool IsRV128(ArchFeature feature) { + return feature == ArchFeature::RV128; +} +constexpr bool IsRV32OrRV64(ArchFeature feature) { + return IsRV32(feature) || IsRV64(feature); +} +constexpr bool IsRV64OrRV128(ArchFeature feature) { + return IsRV64(feature) || IsRV128(feature); +} + +} // namespace biscuit diff --git a/externals/biscuit/src/assembler_vector.cpp b/externals/biscuit/src/assembler_vector.cpp new file mode 100644 index 00000000..8f12e541 --- /dev/null +++ b/externals/biscuit/src/assembler_vector.cpp @@ -0,0 +1,2146 @@ +#include <biscuit/assert.hpp> +#include <biscuit/assembler.hpp> + +namespace biscuit { +namespace { + +enum class AddressingMode : uint32_t { + // clang-format off + UnitStride = 0b00, + IndexedUnordered = 0b01, + Strided = 0b10, + IndexedOrdered = 0b11, + // clang-format on +}; + +enum class UnitStrideLoadAddressingMode : uint32_t { + // clang-format off + Load = 0b00000, + MaskLoad = 0b01011, + LoadFaultOnlyFirst = 0b10000, + // clang-format on +}; + +enum class UnitStrideStoreAddressingMode : uint32_t { + // clang-format off + Store = 0b00000, + MaskStore = 0b01011, + // clang-format on +}; + +enum class WidthEncoding : uint32_t { + // clang-format off + E8 = 0b000, + E16 = 0b101, + E32 = 0b110, + E64 = 0b111, + // clang-format on +}; + +void EmitVectorLoadImpl(CodeBuffer& buffer, uint32_t nf, bool mew, AddressingMode mop, + VecMask vm, uint32_t lumop, GPR rs, WidthEncoding width, Vec vd) noexcept { + BISCUIT_ASSERT(nf <= 8); + + // Fit to encoding space. Allows for being more explicit about the size in calling functions + // (e.g. using 8 for 8 elements instead of 7). + if (nf != 0) { + nf -= 1; + } + + // clang-format off + const auto value = (nf << 29) | + (static_cast<uint32_t>(mew) << 28) | + (static_cast<uint32_t>(mop) << 26) | + (static_cast<uint32_t>(vm) << 25) | + (lumop << 20) | + (rs.Index() << 15) | + (static_cast<uint32_t>(width) << 12) | + (vd.Index() << 7); + // clang-format on + + buffer.Emit32(value | 0b111); +} + +void EmitVectorLoad(CodeBuffer& buffer, uint32_t nf, bool mew, AddressingMode mop, + VecMask vm, UnitStrideLoadAddressingMode lumop, GPR rs, + WidthEncoding width, Vec vd) noexcept { + EmitVectorLoadImpl(buffer, nf, mew, mop, vm, static_cast<uint32_t>(lumop), rs, width, vd); +} + +void EmitVectorLoad(CodeBuffer& buffer, uint32_t nf, bool mew, AddressingMode mop, + VecMask vm, GPR rs2, GPR rs1, WidthEncoding width, Vec vd) noexcept { + EmitVectorLoadImpl(buffer, nf, mew, mop, vm, rs2.Index(), rs1, width, vd); +} + +void EmitVectorLoad(CodeBuffer& buffer, uint32_t nf, bool mew, AddressingMode mop, + VecMask vm, Vec vs2, GPR rs1, WidthEncoding width, Vec vd) noexcept { + EmitVectorLoadImpl(buffer, nf, mew, mop, vm, vs2.Index(), rs1, width, vd); +} + +void EmitVectorLoadWholeReg(CodeBuffer& buffer, uint32_t nf, bool mew, GPR rs, + WidthEncoding width, Vec vd) noexcept { + // RISC-V V extension spec (as of 1.0RC) only allows these nf values. + BISCUIT_ASSERT(nf == 1 || nf == 2 || nf == 4 || nf == 8); + + EmitVectorLoadImpl(buffer, nf, mew, AddressingMode::UnitStride, + VecMask::No, 0b01000, rs, width, vd); +} + +void EmitVectorStoreImpl(CodeBuffer& buffer, uint32_t nf, bool mew, AddressingMode mop, + VecMask vm, uint32_t sumop, GPR rs, WidthEncoding width, Vec vd) noexcept { + BISCUIT_ASSERT(nf <= 8); + + // Fit to encoding space. Allows for being more explicit about the size in calling functions + // (e.g. using 8 for 8 elements instead of 7). + if (nf != 0) { + nf -= 1; + } + + // clang-format off + const auto value = (nf << 29) | + (static_cast<uint32_t>(mew) << 28) | + (static_cast<uint32_t>(mop) << 26) | + (static_cast<uint32_t>(vm) << 25) | + (sumop << 20) | + (rs.Index() << 15) | + (static_cast<uint32_t>(width) << 12) | + (vd.Index() << 7); + // clang-format on + + buffer.Emit32(value | 0b100111); +} + +void EmitVectorStore(CodeBuffer& buffer, uint32_t nf, bool mew, AddressingMode mop, + VecMask vm, UnitStrideStoreAddressingMode lumop, GPR rs, + WidthEncoding width, Vec vs) noexcept { + EmitVectorStoreImpl(buffer, nf, mew, mop, vm, static_cast<uint32_t>(lumop), rs, width, vs); +} + +void EmitVectorStore(CodeBuffer& buffer, uint32_t nf, bool mew, AddressingMode mop, + VecMask vm, GPR rs2, GPR rs1, WidthEncoding width, Vec vs3) noexcept { + EmitVectorStoreImpl(buffer, nf, mew, mop, vm, rs2.Index(), rs1, width, vs3); +} + +void EmitVectorStore(CodeBuffer& buffer, uint32_t nf, bool mew, AddressingMode mop, + VecMask vm, Vec vs2, GPR rs1, WidthEncoding width, Vec vs3) noexcept { + EmitVectorStoreImpl(buffer, nf, mew, mop, vm, vs2.Index(), rs1, width, vs3); +} + +void EmitVectorStoreWholeReg(CodeBuffer& buffer, uint32_t nf, GPR rs, Vec vs) noexcept { + // RISC-V V extension spec (as of 1.0RC) only allows these nf values. + BISCUIT_ASSERT(nf == 1 || nf == 2 || nf == 4 || nf == 8); + + EmitVectorStoreImpl(buffer, nf, false, AddressingMode::UnitStride, VecMask::No, + 0b01000, rs, WidthEncoding::E8, vs); +} + +void EmitVectorOPIVIImpl(CodeBuffer& buffer, uint32_t funct6, VecMask vm, Vec vs2, uint32_t imm5, Vec vd) noexcept { + // clang-format off + const auto value = (funct6 << 26) | + (static_cast<uint32_t>(vm) << 25) | + (vs2.Index() << 20) | + ((imm5 & 0b11111) << 15) | + (0b011U << 12) | + (vd.Index() << 7); + // clang-format on + + buffer.Emit32(value | 0b1010111); +} + +void EmitVectorOPIVI(CodeBuffer& buffer, uint32_t funct6, VecMask vm, Vec vs2, int32_t simm5, Vec vd) noexcept { + BISCUIT_ASSERT(simm5 >= -16 && simm5 <= 15); + EmitVectorOPIVIImpl(buffer, funct6, vm, vs2, static_cast<uint32_t>(simm5), vd); +} + +void EmitVectorOPIVUI(CodeBuffer& buffer, uint32_t funct6, VecMask vm, Vec vs2, uint32_t uimm5, Vec vd) noexcept { + BISCUIT_ASSERT(uimm5 <= 31); + EmitVectorOPIVIImpl(buffer, funct6, vm, vs2, uimm5, vd); +} + +void EmitVectorOPIVV(CodeBuffer& buffer, uint32_t funct6, VecMask vm, Vec vs2, Vec vs1, Vec vd) noexcept { + // clang-format off + const auto value = (funct6 << 26) | + (static_cast<uint32_t>(vm) << 25) | + (vs2.Index() << 20) | + (vs1.Index() << 15) | + (vd.Index() << 7); + // clang-format on + + buffer.Emit32(value | 0b1010111); +} + +void EmitVectorOPIVX(CodeBuffer& buffer, uint32_t funct6, VecMask vm, Vec vs2, GPR rs1, Vec vd) noexcept { + // clang-format off + const auto value = (funct6 << 26) | + (static_cast<uint32_t>(vm) << 25) | + (vs2.Index() << 20) | + (rs1.Index() << 15) | + (0b100U << 12) | + (vd.Index() << 7); + // clang-format on + + buffer.Emit32(value | 0b1010111); +} + +void EmitVectorOPMVVImpl(CodeBuffer& buffer, uint32_t funct6, VecMask vm, Vec vs2, Vec vs1, Vec vd, + uint32_t op) noexcept { + // clang-format off + const auto value = (funct6 << 26) | + (static_cast<uint32_t>(vm) << 25) | + (vs2.Index() << 20) | + (vs1.Index() << 15) | + (0b010U << 12) | + (vd.Index() << 7); + // clang-format on + + buffer.Emit32(value | op); +} + +void EmitVectorOPMVV(CodeBuffer& buffer, uint32_t funct6, VecMask vm, Vec vs2, Vec vs1, Vec vd) noexcept { + EmitVectorOPMVVImpl(buffer, funct6, vm, vs2, vs1, vd, 0b1010111); +} + +void EmitVectorOPMVVP(CodeBuffer& buffer, uint32_t funct6, VecMask vm, Vec vs2, Vec vs1, Vec vd) noexcept { + EmitVectorOPMVVImpl(buffer, funct6, vm, vs2, vs1, vd, 0b1110111); +} + +void EmitVectorOPMVX(CodeBuffer& buffer, uint32_t funct6, VecMask vm, Vec vs2, GPR rs1, Vec vd) noexcept { + // clang-format off + const auto value = (funct6 << 26) | + (static_cast<uint32_t>(vm) << 25) | + (vs2.Index() << 20) | + (rs1.Index() << 15) | + (0b110U << 12) | + (vd.Index() << 7); + // clang-format on + + buffer.Emit32(value | 0b1010111); +} + +void EmitVectorOPFVV(CodeBuffer& buffer, uint32_t funct6, VecMask vm, Vec vs2, Vec vs1, Vec vd) noexcept { + // clang-format off + const auto value = (funct6 << 26) | + (static_cast<uint32_t>(vm) << 25) | + (vs2.Index() << 20) | + (vs1.Index() << 15) | + (0b001U << 12) | + (vd.Index() << 7); + // clang-format on + + buffer.Emit32(value | 0b1010111); +} + +void EmitVectorOPFVF(CodeBuffer& buffer, uint32_t funct6, VecMask vm, Vec vs2, FPR rs1, Vec vd) noexcept { + // clang-format off + const auto value = (funct6 << 26) | + (static_cast<uint32_t>(vm) << 25) | + (vs2.Index() << 20) | + (rs1.Index() << 15) | + (0b101U << 12) | + (vd.Index() << 7); + // clang-format on + + buffer.Emit32(value | 0b1010111); +} +} // Anonymous namespace + +// Vector Integer Arithmetic Instructions + +void Assembler::VAADD(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b001001, mask, vs2, vs1, vd); +} + +void Assembler::VAADD(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPMVX(m_buffer, 0b001001, mask, vs2, rs1, vd); +} + +void Assembler::VAADDU(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b001000, mask, vs2, vs1, vd); +} + +void Assembler::VAADDU(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPMVX(m_buffer, 0b001000, mask, vs2, rs1, vd); +} + +void Assembler::VADC(Vec vd, Vec vs2, Vec vs1) noexcept { + EmitVectorOPIVV(m_buffer, 0b010000, VecMask::Yes, vs2, vs1, vd); +} + +void Assembler::VADC(Vec vd, Vec vs2, GPR rs1) noexcept { + EmitVectorOPIVX(m_buffer, 0b010000, VecMask::Yes, vs2, rs1, vd); +} + +void Assembler::VADC(Vec vd, Vec vs2, int32_t simm) noexcept { + EmitVectorOPIVI(m_buffer, 0b010000, VecMask::Yes, vs2, simm, vd); +} + +void Assembler::VADD(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPIVV(m_buffer, 0b000000, mask, vs2, vs1, vd); +} + +void Assembler::VADD(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPIVX(m_buffer, 0b000000, mask, vs2, rs1, vd); +} + +void Assembler::VADD(Vec vd, Vec vs2, int32_t simm, VecMask mask) noexcept { + EmitVectorOPIVI(m_buffer, 0b000000, mask, vs2, simm, vd); +} + +void Assembler::VAND(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPIVV(m_buffer, 0b001001, mask, vs2, vs1, vd); +} + +void Assembler::VAND(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPIVX(m_buffer, 0b001001, mask, vs2, rs1, vd); +} + +void Assembler::VAND(Vec vd, Vec vs2, int32_t simm, VecMask mask) noexcept { + EmitVectorOPIVI(m_buffer, 0b001001, mask, vs2, simm, vd); +} + +void Assembler::VASUB(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b001011, mask, vs2, vs1, vd); +} + +void Assembler::VASUB(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPMVX(m_buffer, 0b001011, mask, vs2, rs1, vd); +} + +void Assembler::VASUBU(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b001010, mask, vs2, vs1, vd); +} + +void Assembler::VASUBU(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPMVX(m_buffer, 0b001010, mask, vs2, rs1, vd); +} + +void Assembler::VCOMPRESS(Vec vd, Vec vs2, Vec vs1) noexcept { + // Note: Destination register may not overlap any of the source registers, + // as per the RVV spec (as of 1.0RC; see section 16.5) + EmitVectorOPMVV(m_buffer, 0b010111, VecMask::No, vs2, vs1, vd); +} + +void Assembler::VDIV(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b100001, mask, vs2, vs1, vd); +} + +void Assembler::VDIV(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPMVX(m_buffer, 0b100001, mask, vs2, rs1, vd); +} + +void Assembler::VDIVU(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b100000, mask, vs2, vs1, vd); +} + +void Assembler::VDIVU(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPMVX(m_buffer, 0b100000, mask, vs2, rs1, vd); +} + +void Assembler::VFIRST(GPR rd, Vec vs, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b010000, mask, vs, v17, Vec{rd.Index()}); +} + +void Assembler::VID(Vec vd, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b010100, mask, v0, v17, vd); +} + +void Assembler::VIOTA(Vec vd, Vec vs, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b010100, mask, vs, v16, vd); +} + +void Assembler::VMACC(Vec vd, Vec vs1, Vec vs2, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b101101, mask, vs2, vs1, vd); +} + +void Assembler::VMACC(Vec vd, GPR rs1, Vec vs2, VecMask mask) noexcept { + EmitVectorOPMVX(m_buffer, 0b101101, mask, vs2, rs1, vd); +} + +void Assembler::VMADC(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPIVV(m_buffer, 0b010001, mask, vs2, vs1, vd); +} + +void Assembler::VMADC(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPIVX(m_buffer, 0b010001, mask, vs2, rs1, vd); +} + +void Assembler::VMADC(Vec vd, Vec vs2, int32_t simm, VecMask mask) noexcept { + EmitVectorOPIVI(m_buffer, 0b010001, mask, vs2, simm, vd); +} + +void Assembler::VMADD(Vec vd, Vec vs1, Vec vs2, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b101001, mask, vs2, vs1, vd); +} + +void Assembler::VMADD(Vec vd, GPR rs1, Vec vs2, VecMask mask) noexcept { + EmitVectorOPMVX(m_buffer, 0b101001, mask, vs2, rs1, vd); +} + +void Assembler::VMAND(Vec vd, Vec vs2, Vec vs1) noexcept { + EmitVectorOPMVV(m_buffer, 0b011001, VecMask::No, vs2, vs1, vd); +} + +void Assembler::VMANDNOT(Vec vd, Vec vs2, Vec vs1) noexcept { + EmitVectorOPMVV(m_buffer, 0b011000, VecMask::No, vs2, vs1, vd); +} + +void Assembler::VMNAND(Vec vd, Vec vs2, Vec vs1) noexcept { + EmitVectorOPMVV(m_buffer, 0b011101, VecMask::No, vs2, vs1, vd); +} + +void Assembler::VMNOR(Vec vd, Vec vs2, Vec vs1) noexcept { + EmitVectorOPMVV(m_buffer, 0b011110, VecMask::No, vs2, vs1, vd); +} + +void Assembler::VMOR(Vec vd, Vec vs2, Vec vs1) noexcept { + EmitVectorOPMVV(m_buffer, 0b011010, VecMask::No, vs2, vs1, vd); +} + +void Assembler::VMORNOT(Vec vd, Vec vs2, Vec vs1) noexcept { + EmitVectorOPMVV(m_buffer, 0b011100, VecMask::No, vs2, vs1, vd); +} + +void Assembler::VMXNOR(Vec vd, Vec vs2, Vec vs1) noexcept { + EmitVectorOPMVV(m_buffer, 0b011111, VecMask::No, vs2, vs1, vd); +} + +void Assembler::VMXOR(Vec vd, Vec vs2, Vec vs1) noexcept { + EmitVectorOPMVV(m_buffer, 0b011011, VecMask::No, vs2, vs1, vd); +} + +void Assembler::VMAX(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPIVV(m_buffer, 0b000111, mask, vs2, vs1, vd); +} + +void Assembler::VMAX(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPIVX(m_buffer, 0b000111, mask, vs2, rs1, vd); +} + +void Assembler::VMAXU(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPIVV(m_buffer, 0b000110, mask, vs2, vs1, vd); +} + +void Assembler::VMAXU(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPIVX(m_buffer, 0b000110, mask, vs2, rs1, vd); +} + +void Assembler::VMERGE(Vec vd, Vec vs2, Vec vs1) noexcept { + EmitVectorOPIVV(m_buffer, 0b010111, VecMask::Yes, vs2, vs1, vd); +} + +void Assembler::VMERGE(Vec vd, Vec vs2, GPR rs1) noexcept { + EmitVectorOPIVX(m_buffer, 0b010111, VecMask::Yes, vs2, rs1, vd); +} + +void Assembler::VMERGE(Vec vd, Vec vs2, int32_t simm) noexcept { + EmitVectorOPIVI(m_buffer, 0b010111, VecMask::Yes, vs2, simm, vd); +} + +void Assembler::VMIN(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPIVV(m_buffer, 0b000101, mask, vs2, vs1, vd); +} + +void Assembler::VMIN(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPIVX(m_buffer, 0b000101, mask, vs2, rs1, vd); +} + +void Assembler::VMINU(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPIVV(m_buffer, 0b000100, mask, vs2, vs1, vd); +} + +void Assembler::VMINU(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPIVX(m_buffer, 0b000100, mask, vs2, rs1, vd); +} + +void Assembler::VMSBC(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPIVV(m_buffer, 0b010011, mask, vs2, vs1, vd); +} + +void Assembler::VMSBC(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPIVX(m_buffer, 0b010011, mask, vs2, rs1, vd); +} + +void Assembler::VMSBF(Vec vd, Vec vs, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b010100, mask, vs, v1, vd); +} + +void Assembler::VMSIF(Vec vd, Vec vs, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b010100, mask, vs, v3, vd); +} + +void Assembler::VMSOF(Vec vd, Vec vs, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b010100, mask, vs, v2, vd); +} + +void Assembler::VMSEQ(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPIVV(m_buffer, 0b011000, mask, vs2, vs1, vd); +} + +void Assembler::VMSEQ(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPIVX(m_buffer, 0b011000, mask, vs2, rs1, vd); +} + +void Assembler::VMSEQ(Vec vd, Vec vs2, int32_t simm, VecMask mask) noexcept { + EmitVectorOPIVI(m_buffer, 0b011000, mask, vs2, simm, vd); +} + +void Assembler::VMSGT(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPIVX(m_buffer, 0b011111, mask, vs2, rs1, vd); +} + +void Assembler::VMSGT(Vec vd, Vec vs2, int32_t simm, VecMask mask) noexcept { + EmitVectorOPIVI(m_buffer, 0b011111, mask, vs2, simm, vd); +} + +void Assembler::VMSGTU(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPIVX(m_buffer, 0b011110, mask, vs2, rs1, vd); +} + +void Assembler::VMSGTU(Vec vd, Vec vs2, int32_t simm, VecMask mask) noexcept { + EmitVectorOPIVI(m_buffer, 0b011110, mask, vs2, simm, vd); +} + +void Assembler::VMSLE(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPIVV(m_buffer, 0b011101, mask, vs2, vs1, vd); +} + +void Assembler::VMSLE(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPIVX(m_buffer, 0b011101, mask, vs2, rs1, vd); +} + +void Assembler::VMSLE(Vec vd, Vec vs2, int32_t simm, VecMask mask) noexcept { + EmitVectorOPIVI(m_buffer, 0b011101, mask, vs2, simm, vd); +} + +void Assembler::VMSLEU(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPIVV(m_buffer, 0b011100, mask, vs2, vs1, vd); +} + +void Assembler::VMSLEU(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPIVX(m_buffer, 0b011100, mask, vs2, rs1, vd); +} + +void Assembler::VMSLEU(Vec vd, Vec vs2, int32_t simm, VecMask mask) noexcept { + EmitVectorOPIVI(m_buffer, 0b011100, mask, vs2, simm, vd); +} + +void Assembler::VMSLT(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPIVV(m_buffer, 0b011011, mask, vs2, vs1, vd); +} + +void Assembler::VMSLT(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPIVX(m_buffer, 0b011011, mask, vs2, rs1, vd); +} + +void Assembler::VMSLTU(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPIVV(m_buffer, 0b011010, mask, vs2, vs1, vd); +} + +void Assembler::VMSLTU(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPIVX(m_buffer, 0b011010, mask, vs2, rs1, vd); +} + +void Assembler::VMSNE(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPIVV(m_buffer, 0b011001, mask, vs2, vs1, vd); +} + +void Assembler::VMSNE(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPIVX(m_buffer, 0b011001, mask, vs2, rs1, vd); +} + +void Assembler::VMSNE(Vec vd, Vec vs2, int32_t simm, VecMask mask) noexcept { + EmitVectorOPIVI(m_buffer, 0b011001, mask, vs2, simm, vd); +} + +void Assembler::VMUL(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b100101, mask, vs2, vs1, vd); +} + +void Assembler::VMUL(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPMVX(m_buffer, 0b100101, mask, vs2, rs1, vd); +} + +void Assembler::VMULH(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b100111, mask, vs2, vs1, vd); +} + +void Assembler::VMULH(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPMVX(m_buffer, 0b100111, mask, vs2, rs1, vd); +} + +void Assembler::VMULHSU(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b100110, mask, vs2, vs1, vd); +} + +void Assembler::VMULHSU(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPMVX(m_buffer, 0b100110, mask, vs2, rs1, vd); +} + +void Assembler::VMULHU(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b100100, mask, vs2, vs1, vd); +} + +void Assembler::VMULHU(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPMVX(m_buffer, 0b100100, mask, vs2, rs1, vd); +} + +void Assembler::VMV(Vec vd, Vec vs1) noexcept { + EmitVectorOPIVV(m_buffer, 0b010111, VecMask::No, v0, vs1, vd); +} + +void Assembler::VMV(Vec vd, GPR rs1) noexcept { + EmitVectorOPIVX(m_buffer, 0b010111, VecMask::No, v0, rs1, vd); +} + +void Assembler::VMV(Vec vd, int32_t simm) noexcept { + EmitVectorOPIVI(m_buffer, 0b010111, VecMask::No, v0, simm, vd); +} + +void Assembler::VMV1R(Vec vd, Vec vs) noexcept { + EmitVectorOPIVI(m_buffer, 0b100111, VecMask::No, vs, 0b00000, vd); +} + +void Assembler::VMV2R(Vec vd, Vec vs) noexcept { + // Registers must be aligned to the register group size, per the + // RVV spec (as of 1.0RC) + BISCUIT_ASSERT(vd.Index() % 2 == 0); + BISCUIT_ASSERT(vs.Index() % 2 == 0); + + EmitVectorOPIVI(m_buffer, 0b100111, VecMask::No, vs, 0b00001, vd); +} + +void Assembler::VMV4R(Vec vd, Vec vs) noexcept { + // Registers must be aligned to the register group size, per the + // RVV spec (as of 1.0RC) + BISCUIT_ASSERT(vd.Index() % 4 == 0); + BISCUIT_ASSERT(vs.Index() % 4 == 0); + + EmitVectorOPIVI(m_buffer, 0b100111, VecMask::No, vs, 0b00011, vd); +} + +void Assembler::VMV8R(Vec vd, Vec vs) noexcept { + // Registers must be aligned to the register group size, per the + // RVV spec (as of 1.0RC) + BISCUIT_ASSERT(vd.Index() % 8 == 0); + BISCUIT_ASSERT(vs.Index() % 8 == 0); + + EmitVectorOPIVI(m_buffer, 0b100111, VecMask::No, vs, 0b00111, vd); +} + +void Assembler::VMV_SX(Vec vd, GPR rs) noexcept { + EmitVectorOPMVX(m_buffer, 0b010000, VecMask::No, v0, rs, vd); +} + +void Assembler::VMV_XS(GPR rd, Vec vs) noexcept { + EmitVectorOPMVV(m_buffer, 0b010000, VecMask::No, vs, v0, Vec{rd.Index()}); +} + +void Assembler::VNCLIP(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPIVV(m_buffer, 0b101111, mask, vs2, vs1, vd); +} + +void Assembler::VNCLIP(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPIVX(m_buffer, 0b101111, mask, vs2, rs1, vd); +} + +void Assembler::VNCLIP(Vec vd, Vec vs2, uint32_t uimm, VecMask mask) noexcept { + EmitVectorOPIVUI(m_buffer, 0b101111, mask, vs2, uimm, vd); +} + +void Assembler::VNCLIPU(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPIVV(m_buffer, 0b101110, mask, vs2, vs1, vd); +} + +void Assembler::VNCLIPU(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPIVX(m_buffer, 0b101110, mask, vs2, rs1, vd); +} + +void Assembler::VNCLIPU(Vec vd, Vec vs2, uint32_t uimm, VecMask mask) noexcept { + EmitVectorOPIVUI(m_buffer, 0b101110, mask, vs2, uimm, vd); +} + +void Assembler::VNMSAC(Vec vd, Vec vs1, Vec vs2, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b101111, mask, vs2, vs1, vd); +} + +void Assembler::VNMSAC(Vec vd, GPR rs1, Vec vs2, VecMask mask) noexcept { + EmitVectorOPMVX(m_buffer, 0b101111, mask, vs2, rs1, vd); +} + +void Assembler::VNMSUB(Vec vd, Vec vs1, Vec vs2, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b101011, mask, vs2, vs1, vd); +} + +void Assembler::VNMSUB(Vec vd, GPR rs1, Vec vs2, VecMask mask) noexcept { + EmitVectorOPMVX(m_buffer, 0b101011, mask, vs2, rs1, vd); +} + +void Assembler::VNSRA(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPIVV(m_buffer, 0b101101, mask, vs2, vs1, vd); +} + +void Assembler::VNSRA(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPIVX(m_buffer, 0b101101, mask, vs2, rs1, vd); +} + +void Assembler::VNSRA(Vec vd, Vec vs2, uint32_t uimm, VecMask mask) noexcept { + EmitVectorOPIVUI(m_buffer, 0b101101, mask, vs2, uimm, vd); +} + +void Assembler::VNSRL(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPIVV(m_buffer, 0b101100, mask, vs2, vs1, vd); +} + +void Assembler::VNSRL(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPIVX(m_buffer, 0b101100, mask, vs2, rs1, vd); +} + +void Assembler::VNSRL(Vec vd, Vec vs2, uint32_t uimm, VecMask mask) noexcept { + EmitVectorOPIVUI(m_buffer, 0b101100, mask, vs2, uimm, vd); +} + +void Assembler::VOR(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPIVV(m_buffer, 0b001010, mask, vs2, vs1, vd); +} + +void Assembler::VOR(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPIVX(m_buffer, 0b001010, mask, vs2, rs1, vd); +} + +void Assembler::VOR(Vec vd, Vec vs2, int32_t simm, VecMask mask) noexcept { + EmitVectorOPIVI(m_buffer, 0b001010, mask, vs2, simm, vd); +} + +void Assembler::VPOPC(GPR rd, Vec vs, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b010000, mask, vs, v16, Vec{rd.Index()}); +} + +void Assembler::VREDAND(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b000001, mask, vs2, vs1, vd); +} + +void Assembler::VREDMAX(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b000111, mask, vs2, vs1, vd); +} + +void Assembler::VREDMAXU(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b000110, mask, vs2, vs1, vd); +} + +void Assembler::VREDMIN(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b000101, mask, vs2, vs1, vd); +} + +void Assembler::VREDMINU(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b000100, mask, vs2, vs1, vd); +} + +void Assembler::VREDOR(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b000010, mask, vs2, vs1, vd); +} + +void Assembler::VREDSUM(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b000000, mask, vs2, vs1, vd); +} + +void Assembler::VREDXOR(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b000011, mask, vs2, vs1, vd); +} + +void Assembler::VREM(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b100011, mask, vs2, vs1, vd); +} + +void Assembler::VREM(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPMVX(m_buffer, 0b100011, mask, vs2, rs1, vd); +} + +void Assembler::VREMU(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b100010, mask, vs2, vs1, vd); +} + +void Assembler::VREMU(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPMVX(m_buffer, 0b100010, mask, vs2, rs1, vd); +} + +void Assembler::VRGATHER(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPIVV(m_buffer, 0b001100, mask, vs2, vs1, vd); +} + +void Assembler::VRGATHER(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPIVX(m_buffer, 0b001100, mask, vs2, rs1, vd); +} + +void Assembler::VRGATHER(Vec vd, Vec vs2, uint32_t uimm, VecMask mask) noexcept { + EmitVectorOPIVUI(m_buffer, 0b001100, mask, vs2, uimm, vd); +} + +void Assembler::VRGATHEREI16(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPIVV(m_buffer, 0b001110, mask, vs2, vs1, vd); +} + +void Assembler::VRSUB(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPIVX(m_buffer, 0b000011, mask, vs2, rs1, vd); +} + +void Assembler::VRSUB(Vec vd, Vec vs2, int32_t simm, VecMask mask) noexcept { + EmitVectorOPIVI(m_buffer, 0b000011, mask, vs2, simm, vd); +} + +void Assembler::VSADD(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPIVV(m_buffer, 0b100001, mask, vs2, vs1, vd); +} + +void Assembler::VSADD(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPIVX(m_buffer, 0b100001, mask, vs2, rs1, vd); +} + +void Assembler::VSADD(Vec vd, Vec vs2, int32_t simm, VecMask mask) noexcept { + EmitVectorOPIVI(m_buffer, 0b100001, mask, vs2, simm, vd); +} + +void Assembler::VSADDU(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPIVV(m_buffer, 0b100000, mask, vs2, vs1, vd); +} + +void Assembler::VSADDU(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPIVX(m_buffer, 0b100000, mask, vs2, rs1, vd); +} + +void Assembler::VSADDU(Vec vd, Vec vs2, int32_t simm, VecMask mask) noexcept { + EmitVectorOPIVI(m_buffer, 0b100000, mask, vs2, simm, vd); +} + +void Assembler::VSBC(Vec vd, Vec vs2, Vec vs1) noexcept { + EmitVectorOPIVV(m_buffer, 0b010010, VecMask::Yes, vs2, vs1, vd); +} + +void Assembler::VSBC(Vec vd, Vec vs2, GPR rs1) noexcept { + EmitVectorOPIVX(m_buffer, 0b010010, VecMask::Yes, vs2, rs1, vd); +} + +void Assembler::VSEXTVF2(Vec vd, Vec vs, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b010010, mask, vs, v7, vd); +} + +void Assembler::VSEXTVF4(Vec vd, Vec vs, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b010010, mask, vs, v5, vd); +} + +void Assembler::VSEXTVF8(Vec vd, Vec vs, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b010010, mask, vs, v3, vd); +} + +void Assembler::VSLIDE1DOWN(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPMVX(m_buffer, 0b001111, mask, vs2, rs1, vd); +} + +void Assembler::VSLIDEDOWN(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPIVX(m_buffer, 0b001111, mask, vs2, rs1, vd); +} + +void Assembler::VSLIDEDOWN(Vec vd, Vec vs2, uint32_t uimm, VecMask mask) noexcept { + EmitVectorOPIVUI(m_buffer, 0b001111, mask, vs2, uimm, vd); +} + +void Assembler::VSLIDE1UP(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPMVX(m_buffer, 0b001110, mask, vs2, rs1, vd); +} + +void Assembler::VSLIDEUP(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPIVX(m_buffer, 0b001110, mask, vs2, rs1, vd); +} + +void Assembler::VSLIDEUP(Vec vd, Vec vs2, uint32_t uimm, VecMask mask) noexcept { + EmitVectorOPIVUI(m_buffer, 0b001110, mask, vs2, uimm, vd); +} + +void Assembler::VSLL(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPIVV(m_buffer, 0b100101, mask, vs2, vs1, vd); +} + +void Assembler::VSLL(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPIVX(m_buffer, 0b100101, mask, vs2, rs1, vd); +} + +void Assembler::VSLL(Vec vd, Vec vs2, uint32_t uimm, VecMask mask) noexcept { + EmitVectorOPIVUI(m_buffer, 0b100101, mask, vs2, uimm, vd); +} + +void Assembler::VSMUL(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPIVV(m_buffer, 0b100111, mask, vs2, vs1, vd); +} + +void Assembler::VSMUL(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPIVX(m_buffer, 0b100111, mask, vs2, rs1, vd); +} + +void Assembler::VSRA(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPIVV(m_buffer, 0b101001, mask, vs2, vs1, vd); +} + +void Assembler::VSRA(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPIVX(m_buffer, 0b101001, mask, vs2, rs1, vd); +} + +void Assembler::VSRA(Vec vd, Vec vs2, uint32_t uimm, VecMask mask) noexcept { + EmitVectorOPIVUI(m_buffer, 0b101001, mask, vs2, uimm, vd); +} + +void Assembler::VSRL(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPIVV(m_buffer, 0b101000, mask, vs2, vs1, vd); +} + +void Assembler::VSRL(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPIVX(m_buffer, 0b101000, mask, vs2, rs1, vd); +} + +void Assembler::VSRL(Vec vd, Vec vs2, uint32_t uimm, VecMask mask) noexcept { + EmitVectorOPIVUI(m_buffer, 0b101000, mask, vs2, uimm, vd); +} + +void Assembler::VSSRA(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPIVV(m_buffer, 0b101011, mask, vs2, vs1, vd); +} + +void Assembler::VSSRA(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPIVX(m_buffer, 0b101011, mask, vs2, rs1, vd); +} + +void Assembler::VSSRA(Vec vd, Vec vs2, uint32_t uimm, VecMask mask) noexcept { + EmitVectorOPIVUI(m_buffer, 0b101011, mask, vs2, uimm, vd); +} + +void Assembler::VSSRL(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPIVV(m_buffer, 0b101010, mask, vs2, vs1, vd); +} + +void Assembler::VSSRL(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPIVX(m_buffer, 0b101010, mask, vs2, rs1, vd); +} + +void Assembler::VSSRL(Vec vd, Vec vs2, uint32_t uimm, VecMask mask) noexcept { + EmitVectorOPIVUI(m_buffer, 0b101010, mask, vs2, uimm, vd); +} + +void Assembler::VSSUB(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPIVV(m_buffer, 0b100011, mask, vs2, vs1, vd); +} + +void Assembler::VSSUB(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPIVX(m_buffer, 0b100011, mask, vs2, rs1, vd); +} + +void Assembler::VSSUBU(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPIVV(m_buffer, 0b100010, mask, vs2, vs1, vd); +} + +void Assembler::VSSUBU(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPIVX(m_buffer, 0b100010, mask, vs2, rs1, vd); +} + +void Assembler::VSUB(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPIVV(m_buffer, 0b000010, mask, vs2, vs1, vd); +} + +void Assembler::VSUB(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPIVX(m_buffer, 0b000010, mask, vs2, rs1, vd); +} + +void Assembler::VWADD(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b110001, mask, vs2, vs1, vd); +} + +void Assembler::VWADD(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPMVX(m_buffer, 0b110001, mask, vs2, rs1, vd); +} + +void Assembler::VWADDW(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b110101, mask, vs2, vs1, vd); +} + +void Assembler::VWADDW(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPMVX(m_buffer, 0b110101, mask, vs2, rs1, vd); +} + +void Assembler::VWADDU(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b110000, mask, vs2, vs1, vd); +} + +void Assembler::VWADDU(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPMVX(m_buffer, 0b110000, mask, vs2, rs1, vd); +} + +void Assembler::VWADDUW(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b110100, mask, vs2, vs1, vd); +} + +void Assembler::VWADDUW(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPMVX(m_buffer, 0b110100, mask, vs2, rs1, vd); +} + +void Assembler::VWMACC(Vec vd, Vec vs1, Vec vs2, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b111101, mask, vs2, vs1, vd); +} + +void Assembler::VWMACC(Vec vd, GPR rs1, Vec vs2, VecMask mask) noexcept { + EmitVectorOPMVX(m_buffer, 0b111101, mask, vs2, rs1, vd); +} + +void Assembler::VWMACCSU(Vec vd, Vec vs1, Vec vs2, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b111111, mask, vs2, vs1, vd); +} + +void Assembler::VWMACCSU(Vec vd, GPR rs1, Vec vs2, VecMask mask) noexcept { + EmitVectorOPMVX(m_buffer, 0b111111, mask, vs2, rs1, vd); +} + +void Assembler::VWMACCU(Vec vd, Vec vs1, Vec vs2, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b111100, mask, vs2, vs1, vd); +} + +void Assembler::VWMACCU(Vec vd, GPR rs1, Vec vs2, VecMask mask) noexcept { + EmitVectorOPMVX(m_buffer, 0b111100, mask, vs2, rs1, vd); +} + +void Assembler::VWMACCUS(Vec vd, GPR rs1, Vec vs2, VecMask mask) noexcept { + EmitVectorOPMVX(m_buffer, 0b111110, mask, vs2, rs1, vd); +} + +void Assembler::VWMUL(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b111011, mask, vs2, vs1, vd); +} + +void Assembler::VWMUL(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPMVX(m_buffer, 0b111011, mask, vs2, rs1, vd); +} + +void Assembler::VWMULSU(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b111010, mask, vs2, vs1, vd); +} + +void Assembler::VWMULSU(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPMVX(m_buffer, 0b111010, mask, vs2, rs1, vd); +} + +void Assembler::VWMULU(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b111000, mask, vs2, vs1, vd); +} + +void Assembler::VWMULU(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPMVX(m_buffer, 0b111000, mask, vs2, rs1, vd); +} + +void Assembler::VWREDSUM(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPIVV(m_buffer, 0b110001, mask, vs2, vs1, vd); +} + +void Assembler::VWREDSUMU(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPIVV(m_buffer, 0b110000, mask, vs2, vs1, vd); +} + +void Assembler::VWSUB(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b110011, mask, vs2, vs1, vd); +} + +void Assembler::VWSUB(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPMVX(m_buffer, 0b110011, mask, vs2, rs1, vd); +} + +void Assembler::VWSUBW(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b110111, mask, vs2, vs1, vd); +} + +void Assembler::VWSUBW(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPMVX(m_buffer, 0b110111, mask, vs2, rs1, vd); +} + +void Assembler::VWSUBU(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b110010, mask, vs2, vs1, vd); +} + +void Assembler::VWSUBU(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPMVX(m_buffer, 0b110010, mask, vs2, rs1, vd); +} + +void Assembler::VWSUBUW(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b110110, mask, vs2, vs1, vd); +} + +void Assembler::VWSUBUW(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPMVX(m_buffer, 0b110110, mask, vs2, rs1, vd); +} + +void Assembler::VXOR(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPIVV(m_buffer, 0b001011, mask, vs2, vs1, vd); +} + +void Assembler::VXOR(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPIVX(m_buffer, 0b001011, mask, vs2, rs1, vd); +} + +void Assembler::VXOR(Vec vd, Vec vs2, int32_t simm, VecMask mask) noexcept { + EmitVectorOPIVI(m_buffer, 0b001011, mask, vs2, simm, vd); +} + +void Assembler::VZEXTVF2(Vec vd, Vec vs, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b010010, mask, vs, v6, vd); +} + +void Assembler::VZEXTVF4(Vec vd, Vec vs, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b010010, mask, vs, v4, vd); +} + +void Assembler::VZEXTVF8(Vec vd, Vec vs, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b010010, mask, vs, v2, vd); +} + +// Vector Floating-Point Instructions + +void Assembler::VFADD(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b000000, mask, vs2, vs1, vd); +} + +void Assembler::VFADD(Vec vd, Vec vs2, FPR rs1, VecMask mask) noexcept { + EmitVectorOPFVF(m_buffer, 0b000000, mask, vs2, rs1, vd); +} + +void Assembler::VFCLASS(Vec vd, Vec vs, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b010011, mask, vs, v16, vd); +} + +void Assembler::VFCVT_F_X(Vec vd, Vec vs, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b010010, mask, vs, v3, vd); +} + +void Assembler::VFCVT_F_XU(Vec vd, Vec vs, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b010010, mask, vs, v2, vd); +} + +void Assembler::VFCVT_RTZ_X_F(Vec vd, Vec vs, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b010010, mask, vs, v7, vd); +} + +void Assembler::VFCVT_RTZ_XU_F(Vec vd, Vec vs, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b010010, mask, vs, v6, vd); +} + +void Assembler::VFCVT_X_F(Vec vd, Vec vs, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b010010, mask, vs, v1, vd); +} + +void Assembler::VFCVT_XU_F(Vec vd, Vec vs, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b010010, mask, vs, v0, vd); +} + +void Assembler::VFNCVT_F_F(Vec vd, Vec vs, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b010010, mask, vs, v20, vd); +} + +void Assembler::VFNCVT_F_X(Vec vd, Vec vs, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b010010, mask, vs, v19, vd); +} + +void Assembler::VFNCVT_F_XU(Vec vd, Vec vs, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b010010, mask, vs, v18, vd); +} + +void Assembler::VFNCVT_ROD_F_F(Vec vd, Vec vs, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b010010, mask, vs, v21, vd); +} + +void Assembler::VFNCVT_RTZ_X_F(Vec vd, Vec vs, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b010010, mask, vs, v23, vd); +} + +void Assembler::VFNCVT_RTZ_XU_F(Vec vd, Vec vs, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b010010, mask, vs, v22, vd); +} + +void Assembler::VFNCVT_X_F(Vec vd, Vec vs, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b010010, mask, vs, v17, vd); +} + +void Assembler::VFNCVT_XU_F(Vec vd, Vec vs, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b010010, mask, vs, v16, vd); +} + +void Assembler::VFWCVT_F_F(Vec vd, Vec vs, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b010010, mask, vs, v12, vd); +} + +void Assembler::VFWCVT_F_X(Vec vd, Vec vs, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b010010, mask, vs, v11, vd); +} + +void Assembler::VFWCVT_F_XU(Vec vd, Vec vs, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b010010, mask, vs, v10, vd); +} + +void Assembler::VFWCVT_RTZ_X_F(Vec vd, Vec vs, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b010010, mask, vs, v15, vd); +} + +void Assembler::VFWCVT_RTZ_XU_F(Vec vd, Vec vs, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b010010, mask, vs, v14, vd); +} + +void Assembler::VFWCVT_X_F(Vec vd, Vec vs, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b010010, mask, vs, v9, vd); +} + +void Assembler::VFWCVT_XU_F(Vec vd, Vec vs, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b010010, mask, vs, v8, vd); +} + +void Assembler::VFDIV(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b100000, mask, vs2, vs1, vd); +} + +void Assembler::VFDIV(Vec vd, Vec vs2, FPR rs1, VecMask mask) noexcept { + EmitVectorOPFVF(m_buffer, 0b100000, mask, vs2, rs1, vd); +} + +void Assembler::VFRDIV(Vec vd, Vec vs2, FPR rs1, VecMask mask) noexcept { + EmitVectorOPFVF(m_buffer, 0b100001, mask, vs2, rs1, vd); +} + +void Assembler::VFREDMAX(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b000111, mask, vs2, vs1, vd); +} + +void Assembler::VFREDMIN(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b000101, mask, vs2, vs1, vd); +} + +void Assembler::VFREDSUM(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b000001, mask, vs2, vs1, vd); +} + +void Assembler::VFREDOSUM(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b000011, mask, vs2, vs1, vd); +} + +void Assembler::VFMACC(Vec vd, Vec vs1, Vec vs2, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b101100, mask, vs2, vs1, vd); +} + +void Assembler::VFMACC(Vec vd, FPR rs1, Vec vs2, VecMask mask) noexcept { + EmitVectorOPFVF(m_buffer, 0b101100, mask, vs2, rs1, vd); +} + +void Assembler::VFMADD(Vec vd, Vec vs1, Vec vs2, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b101000, mask, vs2, vs1, vd); +} + +void Assembler::VFMADD(Vec vd, FPR rs1, Vec vs2, VecMask mask) noexcept { + EmitVectorOPFVF(m_buffer, 0b101000, mask, vs2, rs1, vd); +} + +void Assembler::VFMAX(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b000110, mask, vs2, vs1, vd); +} + +void Assembler::VFMAX(Vec vd, Vec vs2, FPR rs1, VecMask mask) noexcept { + EmitVectorOPFVF(m_buffer, 0b000110, mask, vs2, rs1, vd); +} + +void Assembler::VFMERGE(Vec vd, Vec vs2, FPR rs1) noexcept { + EmitVectorOPFVF(m_buffer, 0b010111, VecMask::Yes, vs2, rs1, vd); +} + +void Assembler::VFMIN(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b000100, mask, vs2, vs1, vd); +} + +void Assembler::VFMIN(Vec vd, Vec vs2, FPR rs1, VecMask mask) noexcept { + EmitVectorOPFVF(m_buffer, 0b000100, mask, vs2, rs1, vd); +} + +void Assembler::VFMSAC(Vec vd, Vec vs1, Vec vs2, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b101110, mask, vs2, vs1, vd); +} + +void Assembler::VFMSAC(Vec vd, FPR rs1, Vec vs2, VecMask mask) noexcept { + EmitVectorOPFVF(m_buffer, 0b101110, mask, vs2, rs1, vd); +} + +void Assembler::VFMSUB(Vec vd, Vec vs1, Vec vs2, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b101010, mask, vs2, vs1, vd); +} + +void Assembler::VFMSUB(Vec vd, FPR rs1, Vec vs2, VecMask mask) noexcept { + EmitVectorOPFVF(m_buffer, 0b101010, mask, vs2, rs1, vd); +} + +void Assembler::VFMUL(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b100100, mask, vs2, vs1, vd); +} + +void Assembler::VFMUL(Vec vd, Vec vs2, FPR rs1, VecMask mask) noexcept { + EmitVectorOPFVF(m_buffer, 0b100100, mask, vs2, rs1, vd); +} + +void Assembler::VFMV(Vec vd, FPR rs) noexcept { + EmitVectorOPFVF(m_buffer, 0b010111, VecMask::No, v0, rs, vd); +} + +void Assembler::VFMV_FS(FPR rd, Vec vs) noexcept { + EmitVectorOPFVV(m_buffer, 0b010000, VecMask::No, vs, v0, Vec{rd.Index()}); +} + +void Assembler::VFMV_SF(Vec vd, FPR rs) noexcept { + EmitVectorOPFVF(m_buffer, 0b010000, VecMask::No, v0, rs, vd); +} + +void Assembler::VFNMACC(Vec vd, Vec vs1, Vec vs2, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b101101, mask, vs2, vs1, vd); +} + +void Assembler::VFNMACC(Vec vd, FPR rs1, Vec vs2, VecMask mask) noexcept { + EmitVectorOPFVF(m_buffer, 0b101101, mask, vs2, rs1, vd); +} + +void Assembler::VFNMADD(Vec vd, Vec vs1, Vec vs2, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b101001, mask, vs2, vs1, vd); +} + +void Assembler::VFNMADD(Vec vd, FPR rs1, Vec vs2, VecMask mask) noexcept { + EmitVectorOPFVF(m_buffer, 0b101001, mask, vs2, rs1, vd); +} + +void Assembler::VFNMSAC(Vec vd, Vec vs1, Vec vs2, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b101111, mask, vs2, vs1, vd); +} + +void Assembler::VFNMSAC(Vec vd, FPR rs1, Vec vs2, VecMask mask) noexcept { + EmitVectorOPFVF(m_buffer, 0b101111, mask, vs2, rs1, vd); +} + +void Assembler::VFNMSUB(Vec vd, Vec vs1, Vec vs2, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b101011, mask, vs2, vs1, vd); +} + +void Assembler::VFNMSUB(Vec vd, FPR rs1, Vec vs2, VecMask mask) noexcept { + EmitVectorOPFVF(m_buffer, 0b101011, mask, vs2, rs1, vd); +} + +void Assembler::VFREC7(Vec vd, Vec vs, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b010011, mask, vs, v5, vd); +} + +void Assembler::VFSGNJ(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b001000, mask, vs2, vs1, vd); +} + +void Assembler::VFSGNJ(Vec vd, Vec vs2, FPR rs1, VecMask mask) noexcept { + EmitVectorOPFVF(m_buffer, 0b001000, mask, vs2, rs1, vd); +} + +void Assembler::VFSGNJN(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b001001, mask, vs2, vs1, vd); +} + +void Assembler::VFSGNJN(Vec vd, Vec vs2, FPR rs1, VecMask mask) noexcept { + EmitVectorOPFVF(m_buffer, 0b001001, mask, vs2, rs1, vd); +} + +void Assembler::VFSGNJX(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b001010, mask, vs2, vs1, vd); +} + +void Assembler::VFSGNJX(Vec vd, Vec vs2, FPR rs1, VecMask mask) noexcept { + EmitVectorOPFVF(m_buffer, 0b001010, mask, vs2, rs1, vd); +} + +void Assembler::VFSQRT(Vec vd, Vec vs, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b010011, mask, vs, v0, vd); +} + +void Assembler::VFRSQRT7(Vec vd, Vec vs, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b010011, mask, vs, v4, vd); +} + +void Assembler::VFSLIDE1DOWN(Vec vd, Vec vs2, FPR rs1, VecMask mask) noexcept { + EmitVectorOPFVF(m_buffer, 0b001111, mask, vs2, rs1, vd); +} + +void Assembler::VFSLIDE1UP(Vec vd, Vec vs2, FPR rs1, VecMask mask) noexcept { + EmitVectorOPFVF(m_buffer, 0b001110, mask, vs2, rs1, vd); +} + +void Assembler::VFSUB(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b000010, mask, vs2, vs1, vd); +} + +void Assembler::VFSUB(Vec vd, Vec vs2, FPR rs1, VecMask mask) noexcept { + EmitVectorOPFVF(m_buffer, 0b000010, mask, vs2, rs1, vd); +} + +void Assembler::VFRSUB(Vec vd, Vec vs2, FPR rs1, VecMask mask) noexcept { + EmitVectorOPFVF(m_buffer, 0b100111, mask, vs2, rs1, vd); +} + +void Assembler::VFWADD(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b110000, mask, vs2, vs1, vd); +} + +void Assembler::VFWADD(Vec vd, Vec vs2, FPR rs1, VecMask mask) noexcept { + EmitVectorOPFVF(m_buffer, 0b110000, mask, vs2, rs1, vd); +} + +void Assembler::VFWADDW(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b110100, mask, vs2, vs1, vd); +} + +void Assembler::VFWADDW(Vec vd, Vec vs2, FPR rs1, VecMask mask) noexcept { + EmitVectorOPFVF(m_buffer, 0b110100, mask, vs2, rs1, vd); +} + +void Assembler::VFWMACC(Vec vd, Vec vs1, Vec vs2, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b111100, mask, vs2, vs1, vd); +} + +void Assembler::VFWMACC(Vec vd, FPR rs1, Vec vs2, VecMask mask) noexcept { + EmitVectorOPFVF(m_buffer, 0b111100, mask, vs2, rs1, vd); +} + +void Assembler::VFWMUL(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b111000, mask, vs2, vs1, vd); +} + +void Assembler::VFWMUL(Vec vd, Vec vs2, FPR rs1, VecMask mask) noexcept { + EmitVectorOPFVF(m_buffer, 0b111000, mask, vs2, rs1, vd); +} + +void Assembler::VFWNMACC(Vec vd, Vec vs1, Vec vs2, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b111101, mask, vs2, vs1, vd); +} + +void Assembler::VFWNMACC(Vec vd, FPR rs1, Vec vs2, VecMask mask) noexcept { + EmitVectorOPFVF(m_buffer, 0b111101, mask, vs2, rs1, vd); +} + +void Assembler::VFWNMSAC(Vec vd, Vec vs1, Vec vs2, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b111111, mask, vs2, vs1, vd); +} + +void Assembler::VFWNMSAC(Vec vd, FPR rs1, Vec vs2, VecMask mask) noexcept { + EmitVectorOPFVF(m_buffer, 0b111111, mask, vs2, rs1, vd); +} + +void Assembler::VFWREDSUM(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b110001, mask, vs2, vs1, vd); +} + +void Assembler::VFWREDOSUM(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b110011, mask, vs2, vs1, vd); +} + +void Assembler::VFWMSAC(Vec vd, Vec vs1, Vec vs2, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b111110, mask, vs2, vs1, vd); +} + +void Assembler::VFWMSAC(Vec vd, FPR rs1, Vec vs2, VecMask mask) noexcept { + EmitVectorOPFVF(m_buffer, 0b111110, mask, vs2, rs1, vd); +} + +void Assembler::VFWSUB(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b110010, mask, vs2, vs1, vd); +} + +void Assembler::VFWSUB(Vec vd, Vec vs2, FPR rs1, VecMask mask) noexcept { + EmitVectorOPFVF(m_buffer, 0b110010, mask, vs2, rs1, vd); +} + +void Assembler::VFWSUBW(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b110110, mask, vs2, vs1, vd); +} + +void Assembler::VFWSUBW(Vec vd, Vec vs2, FPR rs1, VecMask mask) noexcept { + EmitVectorOPFVF(m_buffer, 0b110110, mask, vs2, rs1, vd); +} + +void Assembler::VMFEQ(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b011000, mask, vs2, vs1, vd); +} + +void Assembler::VMFEQ(Vec vd, Vec vs2, FPR rs1, VecMask mask) noexcept { + EmitVectorOPFVF(m_buffer, 0b011000, mask, vs2, rs1, vd); +} + +void Assembler::VMFGE(Vec vd, Vec vs2, FPR rs1, VecMask mask) noexcept { + EmitVectorOPFVF(m_buffer, 0b011111, mask, vs2, rs1, vd); +} + +void Assembler::VMFGT(Vec vd, Vec vs2, FPR rs1, VecMask mask) noexcept { + EmitVectorOPFVF(m_buffer, 0b011101, mask, vs2, rs1, vd); +} + +void Assembler::VMFLE(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b011001, mask, vs2, vs1, vd); +} + +void Assembler::VMFLE(Vec vd, Vec vs2, FPR rs1, VecMask mask) noexcept { + EmitVectorOPFVF(m_buffer, 0b011001, mask, vs2, rs1, vd); +} + +void Assembler::VMFLT(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b011011, mask, vs2, vs1, vd); +} + +void Assembler::VMFLT(Vec vd, Vec vs2, FPR rs1, VecMask mask) noexcept { + EmitVectorOPFVF(m_buffer, 0b011011, mask, vs2, rs1, vd); +} + +void Assembler::VMFNE(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b011100, mask, vs2, vs1, vd); +} + +void Assembler::VMFNE(Vec vd, Vec vs2, FPR rs1, VecMask mask) noexcept { + EmitVectorOPFVF(m_buffer, 0b011100, mask, vs2, rs1, vd); +} + +// Vector Load/Store Instructions + +void Assembler::VLE8(Vec vd, GPR rs, VecMask mask) noexcept { + VLSEGE8(1, vd, rs, mask); +} + +void Assembler::VLE16(Vec vd, GPR rs, VecMask mask) noexcept { + VLSEGE16(1, vd, rs, mask); +} + +void Assembler::VLE32(Vec vd, GPR rs, VecMask mask) noexcept { + VLSEGE32(1, vd, rs, mask); +} + +void Assembler::VLE64(Vec vd, GPR rs, VecMask mask) noexcept { + VLSEGE64(1, vd, rs, mask); +} + +void Assembler::VLM(Vec vd, GPR rs) noexcept { + EmitVectorLoad(m_buffer, 0b000, false, AddressingMode::UnitStride, VecMask::No, + UnitStrideLoadAddressingMode::MaskLoad, rs, WidthEncoding::E8, vd); +} + +void Assembler::VLSE8(Vec vd, GPR rs1, GPR rs2, VecMask mask) noexcept { + VLSSEGE8(1, vd, rs1, rs2, mask); +} + +void Assembler::VLSE16(Vec vd, GPR rs1, GPR rs2, VecMask mask) noexcept { + VLSSEGE16(1, vd, rs1, rs2, mask); +} + +void Assembler::VLSE32(Vec vd, GPR rs1, GPR rs2, VecMask mask) noexcept { + VLSSEGE32(1, vd, rs1, rs2, mask); +} + +void Assembler::VLSE64(Vec vd, GPR rs1, GPR rs2, VecMask mask) noexcept { + VLSSEGE64(1, vd, rs1, rs2, mask); +} + +void Assembler::VLOXEI8(Vec vd, GPR rs, Vec vs, VecMask mask) noexcept { + VLOXSEGEI8(1, vd, rs, vs, mask); +} + +void Assembler::VLOXEI16(Vec vd, GPR rs, Vec vs, VecMask mask) noexcept { + VLOXSEGEI16(1, vd, rs, vs, mask); +} + +void Assembler::VLOXEI32(Vec vd, GPR rs, Vec vs, VecMask mask) noexcept { + VLOXSEGEI32(1, vd, rs, vs, mask); +} + +void Assembler::VLOXEI64(Vec vd, GPR rs, Vec vs, VecMask mask) noexcept { + VLOXSEGEI64(1, vd, rs, vs, mask); +} + +void Assembler::VLUXEI8(Vec vd, GPR rs, Vec vs, VecMask mask) noexcept { + VLUXSEGEI8(1, vd, rs, vs, mask); +} + +void Assembler::VLUXEI16(Vec vd, GPR rs, Vec vs, VecMask mask) noexcept { + VLUXSEGEI16(1, vd, rs, vs, mask); +} + +void Assembler::VLUXEI32(Vec vd, GPR rs, Vec vs, VecMask mask) noexcept { + VLUXSEGEI32(1, vd, rs, vs, mask); +} + +void Assembler::VLUXEI64(Vec vd, GPR rs, Vec vs, VecMask mask) noexcept { + VLUXSEGEI64(1, vd, rs, vs, mask); +} + +void Assembler::VLE8FF(Vec vd, GPR rs, VecMask mask) noexcept { + EmitVectorLoad(m_buffer, 0b000, false, AddressingMode::UnitStride, mask, + UnitStrideLoadAddressingMode::LoadFaultOnlyFirst, rs, WidthEncoding::E8, vd); +} + +void Assembler::VLE16FF(Vec vd, GPR rs, VecMask mask) noexcept { + EmitVectorLoad(m_buffer, 0b000, false, AddressingMode::UnitStride, mask, + UnitStrideLoadAddressingMode::LoadFaultOnlyFirst, rs, WidthEncoding::E16, vd); +} + +void Assembler::VLE32FF(Vec vd, GPR rs, VecMask mask) noexcept { + EmitVectorLoad(m_buffer, 0b000, false, AddressingMode::UnitStride, mask, + UnitStrideLoadAddressingMode::LoadFaultOnlyFirst, rs, WidthEncoding::E32, vd); +} + +void Assembler::VLE64FF(Vec vd, GPR rs, VecMask mask) noexcept { + EmitVectorLoad(m_buffer, 0b000, false, AddressingMode::UnitStride, mask, + UnitStrideLoadAddressingMode::LoadFaultOnlyFirst, rs, WidthEncoding::E64, vd); +} + +void Assembler::VLSEGE8(uint32_t num_segments, Vec vd, GPR rs, VecMask mask) noexcept { + EmitVectorLoad(m_buffer, num_segments, false, AddressingMode::UnitStride, mask, + UnitStrideLoadAddressingMode::Load, rs, WidthEncoding::E8, vd); +} + +void Assembler::VLSEGE16(uint32_t num_segments, Vec vd, GPR rs, VecMask mask) noexcept { + EmitVectorLoad(m_buffer, num_segments, false, AddressingMode::UnitStride, mask, + UnitStrideLoadAddressingMode::Load, rs, WidthEncoding::E16, vd); +} + +void Assembler::VLSEGE32(uint32_t num_segments, Vec vd, GPR rs, VecMask mask) noexcept { + EmitVectorLoad(m_buffer, num_segments, false, AddressingMode::UnitStride, mask, + UnitStrideLoadAddressingMode::Load, rs, WidthEncoding::E32, vd); +} + +void Assembler::VLSEGE64(uint32_t num_segments, Vec vd, GPR rs, VecMask mask) noexcept { + EmitVectorLoad(m_buffer, num_segments, false, AddressingMode::UnitStride, mask, + UnitStrideLoadAddressingMode::Load, rs, WidthEncoding::E64, vd); +} + +void Assembler::VLSSEGE8(uint32_t num_segments, Vec vd, GPR rs1, GPR rs2, VecMask mask) noexcept { + EmitVectorLoad(m_buffer, num_segments, false, AddressingMode::Strided, mask, + rs2, rs1, WidthEncoding::E8, vd); +} + +void Assembler::VLSSEGE16(uint32_t num_segments, Vec vd, GPR rs1, GPR rs2, VecMask mask) noexcept { + EmitVectorLoad(m_buffer, num_segments, false, AddressingMode::Strided, mask, + rs2, rs1, WidthEncoding::E16, vd); +} + +void Assembler::VLSSEGE32(uint32_t num_segments, Vec vd, GPR rs1, GPR rs2, VecMask mask) noexcept { + EmitVectorLoad(m_buffer, num_segments, false, AddressingMode::Strided, mask, + rs2, rs1, WidthEncoding::E32, vd); +} + +void Assembler::VLSSEGE64(uint32_t num_segments, Vec vd, GPR rs1, GPR rs2, VecMask mask) noexcept { + EmitVectorLoad(m_buffer, num_segments, false, AddressingMode::Strided, mask, + rs2, rs1, WidthEncoding::E64, vd); +} + +void Assembler::VLOXSEGEI8(uint32_t num_segments, Vec vd, GPR rs, Vec vs, VecMask mask) noexcept { + EmitVectorLoad(m_buffer, num_segments, false, AddressingMode::IndexedOrdered, mask, + vs, rs, WidthEncoding::E8, vd); +} + +void Assembler::VLOXSEGEI16(uint32_t num_segments, Vec vd, GPR rs, Vec vs, VecMask mask) noexcept { + EmitVectorLoad(m_buffer, num_segments, false, AddressingMode::IndexedOrdered, mask, + vs, rs, WidthEncoding::E16, vd); +} + +void Assembler::VLOXSEGEI32(uint32_t num_segments, Vec vd, GPR rs, Vec vs, VecMask mask) noexcept { + EmitVectorLoad(m_buffer, num_segments, false, AddressingMode::IndexedOrdered, mask, + vs, rs, WidthEncoding::E32, vd); +} + +void Assembler::VLOXSEGEI64(uint32_t num_segments, Vec vd, GPR rs, Vec vs, VecMask mask) noexcept { + EmitVectorLoad(m_buffer, num_segments, false, AddressingMode::IndexedOrdered, mask, + vs, rs, WidthEncoding::E64, vd); +} + +void Assembler::VLUXSEGEI8(uint32_t num_segments, Vec vd, GPR rs, Vec vs, VecMask mask) noexcept { + EmitVectorLoad(m_buffer, num_segments, false, AddressingMode::IndexedUnordered, mask, + vs, rs, WidthEncoding::E8, vd); +} + +void Assembler::VLUXSEGEI16(uint32_t num_segments, Vec vd, GPR rs, Vec vs, VecMask mask) noexcept { + EmitVectorLoad(m_buffer, num_segments, false, AddressingMode::IndexedUnordered, mask, + vs, rs, WidthEncoding::E16, vd); +} + +void Assembler::VLUXSEGEI32(uint32_t num_segments, Vec vd, GPR rs, Vec vs, VecMask mask) noexcept { + EmitVectorLoad(m_buffer, num_segments, false, AddressingMode::IndexedUnordered, mask, + vs, rs, WidthEncoding::E32, vd); +} + +void Assembler::VLUXSEGEI64(uint32_t num_segments, Vec vd, GPR rs, Vec vs, VecMask mask) noexcept { + EmitVectorLoad(m_buffer, num_segments, false, AddressingMode::IndexedUnordered, mask, + vs, rs, WidthEncoding::E64, vd); +} + +void Assembler::VLRE8(uint32_t num_registers, Vec vd, GPR rs) noexcept { + BISCUIT_ASSERT(vd.Index() % num_registers == 0); + EmitVectorLoadWholeReg(m_buffer, num_registers, false, rs, WidthEncoding::E8, vd); +} + +void Assembler::VL1RE8(Vec vd, GPR rs) noexcept { + VLRE8(1, vd, rs); +} + +void Assembler::VL2RE8(Vec vd, GPR rs) noexcept { + VLRE8(2, vd, rs); +} + +void Assembler::VL4RE8(Vec vd, GPR rs) noexcept { + VLRE8(4, vd, rs); +} + +void Assembler::VL8RE8(Vec vd, GPR rs) noexcept { + VLRE8(8, vd, rs); +} + +void Assembler::VLRE16(uint32_t num_registers, Vec vd, GPR rs) noexcept { + BISCUIT_ASSERT(vd.Index() % num_registers == 0); + EmitVectorLoadWholeReg(m_buffer, num_registers, false, rs, WidthEncoding::E16, vd); +} + +void Assembler::VL1RE16(Vec vd, GPR rs) noexcept { + VLRE16(1, vd, rs); +} + +void Assembler::VL2RE16(Vec vd, GPR rs) noexcept { + VLRE16(2, vd, rs); +} + +void Assembler::VL4RE16(Vec vd, GPR rs) noexcept { + VLRE16(4, vd, rs); +} + +void Assembler::VL8RE16(Vec vd, GPR rs) noexcept { + VLRE16(8, vd, rs); +} + +void Assembler::VLRE32(uint32_t num_registers, Vec vd, GPR rs) noexcept { + BISCUIT_ASSERT(vd.Index() % num_registers == 0); + EmitVectorLoadWholeReg(m_buffer, num_registers, false, rs, WidthEncoding::E32, vd); +} + +void Assembler::VL1RE32(Vec vd, GPR rs) noexcept { + VLRE32(1, vd, rs); +} + +void Assembler::VL2RE32(Vec vd, GPR rs) noexcept { + VLRE32(2, vd, rs); +} + +void Assembler::VL4RE32(Vec vd, GPR rs) noexcept { + VLRE32(4, vd, rs); +} + +void Assembler::VL8RE32(Vec vd, GPR rs) noexcept { + VLRE32(8, vd, rs); +} + +void Assembler::VLRE64(uint32_t num_registers, Vec vd, GPR rs) noexcept { + BISCUIT_ASSERT(vd.Index() % num_registers == 0); + EmitVectorLoadWholeReg(m_buffer, num_registers, false, rs, WidthEncoding::E64, vd); +} + +void Assembler::VL1RE64(Vec vd, GPR rs) noexcept { + VLRE64(1, vd, rs); +} + +void Assembler::VL2RE64(Vec vd, GPR rs) noexcept { + VLRE64(2, vd, rs); +} + +void Assembler::VL4RE64(Vec vd, GPR rs) noexcept { + VLRE64(4, vd, rs); +} + +void Assembler::VL8RE64(Vec vd, GPR rs) noexcept { + VLRE64(8, vd, rs); +} + +void Assembler::VSE8(Vec vs, GPR rs, VecMask mask) noexcept { + VSSEGE8(1, vs, rs, mask); +} + +void Assembler::VSE16(Vec vs, GPR rs, VecMask mask) noexcept { + VSSEGE16(1, vs, rs, mask); +} + +void Assembler::VSE32(Vec vs, GPR rs, VecMask mask) noexcept { + VSSEGE32(1, vs, rs, mask); +} + +void Assembler::VSE64(Vec vs, GPR rs, VecMask mask) noexcept { + VSSEGE64(1, vs, rs, mask); +} + +void Assembler::VSM(Vec vs, GPR rs) noexcept { + EmitVectorStore(m_buffer, 0b000, false, AddressingMode::UnitStride, VecMask::No, + UnitStrideStoreAddressingMode::MaskStore, rs, WidthEncoding::E8, vs); +} + +void Assembler::VSSE8(Vec vs, GPR rs1, GPR rs2, VecMask mask) noexcept { + VSSSEGE8(1, vs, rs1, rs2, mask); +} + +void Assembler::VSSE16(Vec vs, GPR rs1, GPR rs2, VecMask mask) noexcept { + VSSSEGE16(1, vs, rs1, rs2, mask); +} + +void Assembler::VSSE32(Vec vs, GPR rs1, GPR rs2, VecMask mask) noexcept { + VSSSEGE32(1, vs, rs1, rs2, mask); +} + +void Assembler::VSSE64(Vec vs, GPR rs1, GPR rs2, VecMask mask) noexcept { + VSSSEGE64(1, vs, rs1, rs2, mask); +} + +void Assembler::VSOXEI8(Vec vd, GPR rs, Vec vs, VecMask mask) noexcept { + VSOXSEGEI8(1, vd, rs, vs, mask); +} + +void Assembler::VSOXEI16(Vec vd, GPR rs, Vec vs, VecMask mask) noexcept { + VSOXSEGEI16(1, vd, rs, vs, mask); +} + +void Assembler::VSOXEI32(Vec vd, GPR rs, Vec vs, VecMask mask) noexcept { + VSOXSEGEI32(1, vd, rs, vs, mask); +} + +void Assembler::VSOXEI64(Vec vd, GPR rs, Vec vs, VecMask mask) noexcept { + VSOXSEGEI64(1, vd, rs, vs, mask); +} + +void Assembler::VSUXEI8(Vec vd, GPR rs, Vec vs, VecMask mask) noexcept { + VSUXSEGEI8(1, vd, rs, vs, mask); +} + +void Assembler::VSUXEI16(Vec vd, GPR rs, Vec vs, VecMask mask) noexcept { + VSUXSEGEI16(1, vd, rs, vs, mask); +} + +void Assembler::VSUXEI32(Vec vd, GPR rs, Vec vs, VecMask mask) noexcept { + VSUXSEGEI32(1, vd, rs, vs, mask); +} + +void Assembler::VSUXEI64(Vec vd, GPR rs, Vec vs, VecMask mask) noexcept { + VSUXSEGEI64(1, vd, rs, vs, mask); +} + +void Assembler::VSSEGE8(uint32_t num_segments, Vec vs, GPR rs, VecMask mask) noexcept { + EmitVectorStore(m_buffer, num_segments, false, AddressingMode::UnitStride, mask, + UnitStrideStoreAddressingMode::Store, rs, WidthEncoding::E8, vs); +} + +void Assembler::VSSEGE16(uint32_t num_segments, Vec vs, GPR rs, VecMask mask) noexcept { + EmitVectorStore(m_buffer, num_segments, false, AddressingMode::UnitStride, mask, + UnitStrideStoreAddressingMode::Store, rs, WidthEncoding::E16, vs); +} + +void Assembler::VSSEGE32(uint32_t num_segments, Vec vs, GPR rs, VecMask mask) noexcept { + EmitVectorStore(m_buffer, num_segments, false, AddressingMode::UnitStride, mask, + UnitStrideStoreAddressingMode::Store, rs, WidthEncoding::E32, vs); +} + +void Assembler::VSSEGE64(uint32_t num_segments, Vec vs, GPR rs, VecMask mask) noexcept { + EmitVectorStore(m_buffer, num_segments, false, AddressingMode::UnitStride, mask, + UnitStrideStoreAddressingMode::Store, rs, WidthEncoding::E64, vs); +} + +void Assembler::VSSSEGE8(uint32_t num_segments, Vec vs, GPR rs1, GPR rs2, VecMask mask) noexcept { + EmitVectorStore(m_buffer, num_segments, false, AddressingMode::Strided, mask, + rs2, rs1, WidthEncoding::E8, vs); +} + +void Assembler::VSSSEGE16(uint32_t num_segments, Vec vs, GPR rs1, GPR rs2, VecMask mask) noexcept { + EmitVectorStore(m_buffer, num_segments, false, AddressingMode::Strided, mask, + rs2, rs1, WidthEncoding::E16, vs); +} + +void Assembler::VSSSEGE32(uint32_t num_segments, Vec vs, GPR rs1, GPR rs2, VecMask mask) noexcept { + EmitVectorStore(m_buffer, num_segments, false, AddressingMode::Strided, mask, + rs2, rs1, WidthEncoding::E32, vs); +} + +void Assembler::VSSSEGE64(uint32_t num_segments, Vec vs, GPR rs1, GPR rs2, VecMask mask) noexcept { + EmitVectorStore(m_buffer, num_segments, false, AddressingMode::Strided, mask, + rs2, rs1, WidthEncoding::E64, vs); +} + +void Assembler::VSOXSEGEI8(uint32_t num_segments, Vec vd, GPR rs, Vec vs, VecMask mask) noexcept { + EmitVectorStore(m_buffer, num_segments, false, AddressingMode::IndexedOrdered, mask, + vs, rs, WidthEncoding::E8, vd); +} + +void Assembler::VSOXSEGEI16(uint32_t num_segments, Vec vd, GPR rs, Vec vs, VecMask mask) noexcept { + EmitVectorStore(m_buffer, num_segments, false, AddressingMode::IndexedOrdered, mask, + vs, rs, WidthEncoding::E16, vd); +} + +void Assembler::VSOXSEGEI32(uint32_t num_segments, Vec vd, GPR rs, Vec vs, VecMask mask) noexcept { + EmitVectorStore(m_buffer, num_segments, false, AddressingMode::IndexedOrdered, mask, + vs, rs, WidthEncoding::E32, vd); +} + +void Assembler::VSOXSEGEI64(uint32_t num_segments, Vec vd, GPR rs, Vec vs, VecMask mask) noexcept { + EmitVectorStore(m_buffer, num_segments, false, AddressingMode::IndexedOrdered, mask, + vs, rs, WidthEncoding::E64, vd); +} + +void Assembler::VSUXSEGEI8(uint32_t num_segments, Vec vd, GPR rs, Vec vs, VecMask mask) noexcept { + EmitVectorStore(m_buffer, num_segments, false, AddressingMode::IndexedUnordered, mask, + vs, rs, WidthEncoding::E8, vd); +} + +void Assembler::VSUXSEGEI16(uint32_t num_segments, Vec vd, GPR rs, Vec vs, VecMask mask) noexcept { + EmitVectorStore(m_buffer, num_segments, false, AddressingMode::IndexedUnordered, mask, + vs, rs, WidthEncoding::E16, vd); +} + +void Assembler::VSUXSEGEI32(uint32_t num_segments, Vec vd, GPR rs, Vec vs, VecMask mask) noexcept { + EmitVectorStore(m_buffer, num_segments, false, AddressingMode::IndexedUnordered, mask, + vs, rs, WidthEncoding::E32, vd); +} + +void Assembler::VSUXSEGEI64(uint32_t num_segments, Vec vd, GPR rs, Vec vs, VecMask mask) noexcept { + EmitVectorStore(m_buffer, num_segments, false, AddressingMode::IndexedUnordered, mask, + vs, rs, WidthEncoding::E64, vd); +} + +void Assembler::VSR(uint32_t num_registers, Vec vs, GPR rs) noexcept { + EmitVectorStoreWholeReg(m_buffer, num_registers, rs, vs); +} + +void Assembler::VS1R(Vec vs, GPR rs) noexcept { + VSR(1, vs, rs); +} + +void Assembler::VS2R(Vec vs, GPR rs) noexcept { + BISCUIT_ASSERT(vs.Index() % 2 == 0); + VSR(2, vs, rs); +} + +void Assembler::VS4R(Vec vs, GPR rs) noexcept { + BISCUIT_ASSERT(vs.Index() % 4 == 0); + VSR(4, vs, rs); +} + +void Assembler::VS8R(Vec vs, GPR rs) noexcept { + BISCUIT_ASSERT(vs.Index() % 8 == 0); + VSR(8, vs, rs); +} + +void Assembler::VSETIVLI(GPR rd, uint32_t imm, SEW sew, LMUL lmul, VTA vta, VMA vma) noexcept { + // Immediate must be able to fit in 5 bits. + BISCUIT_ASSERT(imm <= 31); + + // clang-format off + const auto zimm = static_cast<uint32_t>(lmul) | + (static_cast<uint32_t>(sew) << 3) | + (static_cast<uint32_t>(vta) << 6) | + (static_cast<uint32_t>(vma) << 7); + // clang-format on + + m_buffer.Emit32(0xC0007057U | (zimm << 20) | (imm << 15) | (rd.Index() << 7)); +} + +void Assembler::VSETVL(GPR rd, GPR rs1, GPR rs2) noexcept { + m_buffer.Emit32(0x80007057U | (rs2.Index() << 20) | (rs1.Index() << 15) | (rd.Index() << 7)); +} + +void Assembler::VSETVLI(GPR rd, GPR rs, SEW sew, LMUL lmul, VTA vta, VMA vma) noexcept { + // clang-format off + const auto zimm = static_cast<uint32_t>(lmul) | + (static_cast<uint32_t>(sew) << 3) | + (static_cast<uint32_t>(vta) << 6) | + (static_cast<uint32_t>(vma) << 7); + // clang-format on + + m_buffer.Emit32(0x00007057U | (zimm << 20) | (rs.Index() << 15) | (rd.Index() << 7)); +} + +// Vector Cryptography Instructions + +void Assembler::VANDN(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPIVV(m_buffer, 0b000001, mask, vs2, vs1, vd); +} +void Assembler::VANDN(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPIVX(m_buffer, 0b000001, mask, vs2, rs1, vd); +} + +void Assembler::VBREV(Vec vd, Vec vs2, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b010010, mask, vs2, Vec{0b01010}, vd); +} +void Assembler::VBREV8(Vec vd, Vec vs2, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b010010, mask, vs2, Vec{0b01000}, vd); +} +void Assembler::VREV8(Vec vd, Vec vs2, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b010010, mask, vs2, Vec{0b01001}, vd); +} + +void Assembler::VCLZ(Vec vd, Vec vs2, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b010010, mask, vs2, Vec{0b01100}, vd); +} +void Assembler::VCTZ(Vec vd, Vec vs2, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b010010, mask, vs2, Vec{0b01101}, vd); +} +void Assembler::VCPOP(Vec vd, Vec vs2, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b010010, mask, vs2, Vec{0b01110}, vd); +} + +void Assembler::VROL(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPIVV(m_buffer, 0b010101, mask, vs2, vs1, vd); +} +void Assembler::VROL(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPIVX(m_buffer, 0b010101, mask, vs2, rs1, vd); +} + +void Assembler::VROR(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPIVV(m_buffer, 0b010100, mask, vs2, vs1, vd); +} +void Assembler::VROR(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPIVX(m_buffer, 0b010100, mask, vs2, rs1, vd); +} +void Assembler::VROR(Vec vd, Vec vs2, uint32_t uimm, VecMask mask) noexcept { + BISCUIT_ASSERT(uimm <= 63); + + const auto funct6 = 0b010100 | ((uimm & 0b100000) >> 5); + EmitVectorOPIVIImpl(m_buffer, funct6, mask, vs2, uimm, vd); +} + +void Assembler::VWSLL(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPIVV(m_buffer, 0b110101, mask, vs2, vs1, vd); +} +void Assembler::VWSLL(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPIVX(m_buffer, 0b110101, mask, vs2, rs1, vd); +} +void Assembler::VWSLL(Vec vd, Vec vs2, uint32_t uimm, VecMask mask) noexcept { + EmitVectorOPIVUI(m_buffer, 0b110101, mask, vs2, uimm, vd); +} + +void Assembler::VCLMUL(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b001100, mask, vs2, vs1, vd); +} +void Assembler::VCLMUL(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPMVX(m_buffer, 0b001100, mask, vs2, rs1, vd); +} + +void Assembler::VCLMULH(Vec vd, Vec vs2, Vec vs1, VecMask mask) noexcept { + EmitVectorOPMVV(m_buffer, 0b001101, mask, vs2, vs1, vd); +} +void Assembler::VCLMULH(Vec vd, Vec vs2, GPR rs1, VecMask mask) noexcept { + EmitVectorOPMVX(m_buffer, 0b001101, mask, vs2, rs1, vd); +} + +void Assembler::VGHSH(Vec vd, Vec vs2, Vec vs1) noexcept { + EmitVectorOPMVVP(m_buffer, 0b101100, VecMask::No, vs2, vs1, vd); +} +void Assembler::VGMUL(Vec vd, Vec vs2) noexcept { + EmitVectorOPMVVP(m_buffer, 0b101000, VecMask::No, vs2, Vec{0b10001}, vd); +} + +void Assembler::VAESDF_VV(Vec vd, Vec vs2) noexcept { + EmitVectorOPMVVP(m_buffer, 0b101000, VecMask::No, vs2, Vec{0b00001}, vd); +} +void Assembler::VAESDF_VS(Vec vd, Vec vs2) noexcept { + EmitVectorOPMVVP(m_buffer, 0b101001, VecMask::No, vs2, Vec{0b00001}, vd); +} + +void Assembler::VAESDM_VV(Vec vd, Vec vs2) noexcept { + EmitVectorOPMVVP(m_buffer, 0b101000, VecMask::No, vs2, Vec{0}, vd); +} +void Assembler::VAESDM_VS(Vec vd, Vec vs2) noexcept { + EmitVectorOPMVVP(m_buffer, 0b101001, VecMask::No, vs2, Vec{0}, vd); +} + +void Assembler::VAESEF_VV(Vec vd, Vec vs2) noexcept { + EmitVectorOPMVVP(m_buffer, 0b101000, VecMask::No, vs2, Vec{0b00011}, vd); +} +void Assembler::VAESEF_VS(Vec vd, Vec vs2) noexcept { + EmitVectorOPMVVP(m_buffer, 0b101001, VecMask::No, vs2, Vec{0b00011}, vd); +} + +void Assembler::VAESEM_VV(Vec vd, Vec vs2) noexcept { + EmitVectorOPMVVP(m_buffer, 0b101000, VecMask::No, vs2, Vec{0b00010}, vd); +} +void Assembler::VAESEM_VS(Vec vd, Vec vs2) noexcept { + EmitVectorOPMVVP(m_buffer, 0b101001, VecMask::No, vs2, Vec{0b00010}, vd); +} + +// Little bit of weirdness (at first glance) for these is that the round +// number immediate has valid ranges: +// +// - [1, 10] for VAESKF1 +// - [2, 14] for VAESKF2 +// +// Any out of range values (0, 11-15) for VAESKF1, (0-1, 15) for VAESKF2 +// will be re-encoded into a valid range by inverting bit uimm[3] + +void Assembler::VAESKF1(Vec vd, Vec vs2, uint32_t uimm) noexcept { + BISCUIT_ASSERT(uimm <= 15); + + if (uimm == 0 || uimm > 10) { + uimm ^= 0b1000; + } + + EmitVectorOPMVVP(m_buffer, 0b100010, VecMask::No, vs2, Vec{uimm}, vd); +} +void Assembler::VAESKF2(Vec vd, Vec vs2, uint32_t uimm) noexcept { + BISCUIT_ASSERT(uimm <= 15); + + if (uimm < 2 || uimm > 14) { + uimm ^= 0b1000; + } + + EmitVectorOPMVVP(m_buffer, 0b101010, VecMask::No, vs2, Vec{uimm}, vd); +} + +void Assembler::VAESZ(Vec vd, Vec vs2) noexcept { + EmitVectorOPMVVP(m_buffer, 0b101001, VecMask::No, vs2, Vec{0b00111}, vd); +} + +void Assembler::VSHA2MS(Vec vd, Vec vs2, Vec vs1) noexcept { + EmitVectorOPMVVP(m_buffer, 0b101101, VecMask::No, vs2, vs1, vd); +} +void Assembler::VSHA2CH(Vec vd, Vec vs2, Vec vs1) noexcept { + EmitVectorOPMVVP(m_buffer, 0b101110, VecMask::No, vs2, vs1, vd); +} +void Assembler::VSHA2CL(Vec vd, Vec vs2, Vec vs1) noexcept { + EmitVectorOPMVVP(m_buffer, 0b101111, VecMask::No, vs2, vs1, vd); +} + +void Assembler::VSM4K(Vec vd, Vec vs2, uint32_t uimm) noexcept { + BISCUIT_ASSERT(uimm <= 7); + EmitVectorOPMVVP(m_buffer, 0b100001, VecMask::No, vs2, Vec{uimm}, vd); +} + +void Assembler::VSM4R_VV(Vec vd, Vec vs2) noexcept { + EmitVectorOPMVVP(m_buffer, 0b101000, VecMask::No, vs2, Vec{0b10000}, vd); +} +void Assembler::VSM4R_VS(Vec vd, Vec vs2) noexcept { + EmitVectorOPMVVP(m_buffer, 0b101001, VecMask::No, vs2, Vec{0b10000}, vd); +} + +void Assembler::VSM3C(Vec vd, Vec vs2, uint32_t uimm) noexcept { + BISCUIT_ASSERT(uimm <= 31); + EmitVectorOPMVVP(m_buffer, 0b101011, VecMask::No, vs2, Vec{uimm}, vd); +} +void Assembler::VSM3ME(Vec vd, Vec vs2, Vec vs1) noexcept { + EmitVectorOPMVVP(m_buffer, 0b100000, VecMask::No, vs2, vs1, vd); +} + +// Zvfbfmin, Zvfbfwma Extension Instructions + +void Assembler::VFNCVTBF16_F_F_W(Vec vd, Vec vs, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b010010, mask, vs, v29, vd); +} +void Assembler::VFWCVTBF16_F_F_V(Vec vd, Vec vs, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b010010, mask, vs, v13, vd); +} + +void Assembler::VFWMACCBF16(Vec vd, FPR rs1, Vec vs2, VecMask mask) noexcept { + EmitVectorOPFVF(m_buffer, 0b111011, mask, vs2, rs1, vd); +} +void Assembler::VFWMACCBF16(Vec vd, Vec vs1, Vec vs2, VecMask mask) noexcept { + EmitVectorOPFVV(m_buffer, 0b111011, mask, vs2, vs1, vd); +} + +} // namespace biscuit diff --git a/externals/biscuit/src/code_buffer.cpp b/externals/biscuit/src/code_buffer.cpp new file mode 100644 index 00000000..386be375 --- /dev/null +++ b/externals/biscuit/src/code_buffer.cpp @@ -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 diff --git a/externals/biscuit/src/cpuinfo.cpp b/externals/biscuit/src/cpuinfo.cpp new file mode 100644 index 00000000..b0944603 --- /dev/null +++ b/externals/biscuit/src/cpuinfo.cpp @@ -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 diff --git a/externals/biscuit/tests/CMakeLists.txt b/externals/biscuit/tests/CMakeLists.txt new file mode 100644 index 00000000..caaaed45 --- /dev/null +++ b/externals/biscuit/tests/CMakeLists.txt @@ -0,0 +1,76 @@ +project(biscuit_tests) + +add_executable(${PROJECT_NAME} + src/assembler_bfloat_tests.cpp + src/assembler_branch_tests.cpp + src/assembler_cmo_tests.cpp + src/assembler_privileged_tests.cpp + src/assembler_rv32i_tests.cpp + src/assembler_rv64i_tests.cpp + src/assembler_rva_tests.cpp + src/assembler_rvb_tests.cpp + src/assembler_rvc_tests.cpp + src/assembler_rvd_tests.cpp + src/assembler_rvf_tests.cpp + src/assembler_rvk_tests.cpp + src/assembler_rvm_tests.cpp + src/assembler_rvq_tests.cpp + src/assembler_rvv_tests.cpp + src/assembler_vector_crypto_tests.cpp + src/assembler_zacas_tests.cpp + src/assembler_zawrs_tests.cpp + src/assembler_zc_tests.cpp + src/assembler_zfa_tests.cpp + src/assembler_zicond_tests.cpp + src/assembler_zicsr_tests.cpp + src/assembler_zihintntl_tests.cpp + src/main.cpp + + src/assembler_test_utils.hpp +) + +target_include_directories(${PROJECT_NAME} +PRIVATE + externals/ +) + +target_link_libraries(${PROJECT_NAME} +PRIVATE + biscuit +) + +target_compile_features(${PROJECT_NAME} +PRIVATE + cxx_std_20 +) + +if (MSVC) + target_compile_options(${PROJECT_NAME} + PRIVATE + /MP + /Zi + /Zo + /permissive- + /EHsc + /utf-8 + /volatile:iso + /Zc:externConstexpr + /Zc:inline + /Zc:throwingNew + + # Warnings + /W4 + /we4062 # enumerator 'identifier' in a switch of enum 'enumeration' is not handled + /we4101 # 'identifier': unreferenced local variable + /we4265 # 'class': class has virtual functions, but destructor is not virtual + /we4388 # signed/unsigned mismatch + /we4547 # 'operator' : operator before comma has no effect; expected operator with side-effect + /we4549 # 'operator1': operator before comma has no effect; did you intend 'operator2'? + /we4555 # Expression has no effect; expected expression with side-effect + /we4715 # 'function': not all control paths return a value + /we4834 # Discarding return value of function with 'nodiscard' attribute + /we5038 # data member 'member1' will be initialized after data member 'member2' + ) +endif() + +add_test(biscuit_tests_ctest ${PROJECT_NAME})
\ No newline at end of file diff --git a/externals/biscuit/tests/externals/catch/catch.hpp b/externals/biscuit/tests/externals/catch/catch.hpp new file mode 100644 index 00000000..9b309bdd --- /dev/null +++ b/externals/biscuit/tests/externals/catch/catch.hpp @@ -0,0 +1,17976 @@ +/* + * Catch v2.13.10 + * Generated: 2022-10-16 11:01:23.452308 + * ---------------------------------------------------------- + * This file has been merged from multiple headers. Please don't edit it directly + * Copyright (c) 2022 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED +#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED +// start catch.hpp + + +#define CATCH_VERSION_MAJOR 2 +#define CATCH_VERSION_MINOR 13 +#define CATCH_VERSION_PATCH 10 + +#ifdef __clang__ +# pragma clang system_header +#elif defined __GNUC__ +# pragma GCC system_header +#endif + +// start catch_suppress_warnings.h + +#ifdef __clang__ +# ifdef __ICC // icpc defines the __clang__ macro +# pragma warning(push) +# pragma warning(disable: 161 1682) +# else // __ICC +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wpadded" +# pragma clang diagnostic ignored "-Wswitch-enum" +# pragma clang diagnostic ignored "-Wcovered-switch-default" +# endif +#elif defined __GNUC__ + // Because REQUIREs trigger GCC's -Wparentheses, and because still + // supported version of g++ have only buggy support for _Pragmas, + // Wparentheses have to be suppressed globally. +# pragma GCC diagnostic ignored "-Wparentheses" // See #674 for details + +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunused-variable" +# pragma GCC diagnostic ignored "-Wpadded" +#endif +// end catch_suppress_warnings.h +#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER) +# define CATCH_IMPL +# define CATCH_CONFIG_ALL_PARTS +#endif + +// In the impl file, we want to have access to all parts of the headers +// Can also be used to sanely support PCHs +#if defined(CATCH_CONFIG_ALL_PARTS) +# define CATCH_CONFIG_EXTERNAL_INTERFACES +# if defined(CATCH_CONFIG_DISABLE_MATCHERS) +# undef CATCH_CONFIG_DISABLE_MATCHERS +# endif +# if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) +# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +# endif +#endif + +#if !defined(CATCH_CONFIG_IMPL_ONLY) +// start catch_platform.h + +// See e.g.: +// https://opensource.apple.com/source/CarbonHeaders/CarbonHeaders-18.1/TargetConditionals.h.auto.html +#ifdef __APPLE__ +# include <TargetConditionals.h> +# if (defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1) || \ + (defined(TARGET_OS_MAC) && TARGET_OS_MAC == 1) +# define CATCH_PLATFORM_MAC +# elif (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1) +# define CATCH_PLATFORM_IPHONE +# endif + +#elif defined(linux) || defined(__linux) || defined(__linux__) +# define CATCH_PLATFORM_LINUX + +#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__) +# define CATCH_PLATFORM_WINDOWS +#endif + +// end catch_platform.h + +#ifdef CATCH_IMPL +# ifndef CLARA_CONFIG_MAIN +# define CLARA_CONFIG_MAIN_NOT_DEFINED +# define CLARA_CONFIG_MAIN +# endif +#endif + +// start catch_user_interfaces.h + +namespace Catch { + unsigned int rngSeed(); +} + +// end catch_user_interfaces.h +// start catch_tag_alias_autoregistrar.h + +// start catch_common.h + +// start catch_compiler_capabilities.h + +// Detect a number of compiler features - by compiler +// The following features are defined: +// +// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported? +// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported? +// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported? +// CATCH_CONFIG_DISABLE_EXCEPTIONS : Are exceptions enabled? +// **************** +// Note to maintainers: if new toggles are added please document them +// in configuration.md, too +// **************** + +// In general each macro has a _NO_<feature name> form +// (e.g. CATCH_CONFIG_NO_POSIX_SIGNALS) which disables the feature. +// Many features, at point of detection, define an _INTERNAL_ macro, so they +// can be combined, en-mass, with the _NO_ forms later. + +#ifdef __cplusplus + +# if (__cplusplus >= 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L) +# define CATCH_CPP14_OR_GREATER +# endif + +# if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +# define CATCH_CPP17_OR_GREATER +# endif + +#endif + +// Only GCC compiler should be used in this block, so other compilers trying to +// mask themselves as GCC should be ignored. +#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__) +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" ) + +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) + +#endif + +#if defined(__clang__) + +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic push" ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic pop" ) + +// As of this writing, IBM XL's implementation of __builtin_constant_p has a bug +// which results in calls to destructors being emitted for each temporary, +// without a matching initialization. In practice, this can result in something +// like `std::string::~string` being called on an uninitialized value. +// +// For example, this code will likely segfault under IBM XL: +// ``` +// REQUIRE(std::string("12") + "34" == "1234") +// ``` +// +// Therefore, `CATCH_INTERNAL_IGNORE_BUT_WARN` is not implemented. +# if !defined(__ibmxl__) && !defined(__CUDACC__) +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) /* NOLINT(cppcoreguidelines-pro-type-vararg, hicpp-vararg) */ +# endif + +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ + _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") + +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) + +# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" ) + +# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" ) + +# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wunused-template\"" ) + +#endif // __clang__ + +//////////////////////////////////////////////////////////////////////////////// +// Assume that non-Windows platforms support posix signals by default +#if !defined(CATCH_PLATFORM_WINDOWS) + #define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS +#endif + +//////////////////////////////////////////////////////////////////////////////// +// We know some environments not to support full POSIX signals +#if defined(__CYGWIN__) || defined(__QNX__) || defined(__EMSCRIPTEN__) || defined(__DJGPP__) + #define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +#endif + +#ifdef __OS400__ +# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +# define CATCH_CONFIG_COLOUR_NONE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Android somehow still does not support std::to_string +#if defined(__ANDROID__) +# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING +# define CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Not all Windows environments support SEH properly +#if defined(__MINGW32__) +# define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH +#endif + +//////////////////////////////////////////////////////////////////////////////// +// PS4 +#if defined(__ORBIS__) +# define CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Cygwin +#ifdef __CYGWIN__ + +// Required for some versions of Cygwin to declare gettimeofday +// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin +# define _BSD_SOURCE +// some versions of cygwin (most) do not support std::to_string. Use the libstd check. +// https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813 +# if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \ + && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF)) + +# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING + +# endif +#endif // __CYGWIN__ + +//////////////////////////////////////////////////////////////////////////////// +// Visual C++ +#if defined(_MSC_VER) + +// Universal Windows platform does not support SEH +// Or console colours (or console at all...) +# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) +# define CATCH_CONFIG_COLOUR_NONE +# else +# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH +# endif + +# if !defined(__clang__) // Handle Clang masquerading for msvc + +// MSVC traditional preprocessor needs some workaround for __VA_ARGS__ +// _MSVC_TRADITIONAL == 0 means new conformant preprocessor +// _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor +# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL) +# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +# endif // MSVC_TRADITIONAL + +// Only do this if we're not using clang on Windows, which uses `diagnostic push` & `diagnostic pop` +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma( warning(push) ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION __pragma( warning(pop) ) +# endif // __clang__ + +#endif // _MSC_VER + +#if defined(_REENTRANT) || defined(_MSC_VER) +// Enable async processing, as -pthread is specified or no additional linking is required +# define CATCH_INTERNAL_CONFIG_USE_ASYNC +#endif // _MSC_VER + +//////////////////////////////////////////////////////////////////////////////// +// Check if we are compiled with -fno-exceptions or equivalent +#if defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND) +# define CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED +#endif + +//////////////////////////////////////////////////////////////////////////////// +// DJGPP +#ifdef __DJGPP__ +# define CATCH_INTERNAL_CONFIG_NO_WCHAR +#endif // __DJGPP__ + +//////////////////////////////////////////////////////////////////////////////// +// Embarcadero C++Build +#if defined(__BORLANDC__) + #define CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN +#endif + +//////////////////////////////////////////////////////////////////////////////// + +// Use of __COUNTER__ is suppressed during code analysis in +// CLion/AppCode 2017.2.x and former, because __COUNTER__ is not properly +// handled by it. +// Otherwise all supported compilers support COUNTER macro, +// but user still might want to turn it off +#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L ) + #define CATCH_INTERNAL_CONFIG_COUNTER +#endif + +//////////////////////////////////////////////////////////////////////////////// + +// RTX is a special version of Windows that is real time. +// This means that it is detected as Windows, but does not provide +// the same set of capabilities as real Windows does. +#if defined(UNDER_RTSS) || defined(RTX64_BUILD) + #define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH + #define CATCH_INTERNAL_CONFIG_NO_ASYNC + #define CATCH_CONFIG_COLOUR_NONE +#endif + +#if !defined(_GLIBCXX_USE_C99_MATH_TR1) +#define CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER +#endif + +// Various stdlib support checks that require __has_include +#if defined(__has_include) + // Check if string_view is available and usable + #if __has_include(<string_view>) && defined(CATCH_CPP17_OR_GREATER) + # define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW + #endif + + // Check if optional is available and usable + # if __has_include(<optional>) && defined(CATCH_CPP17_OR_GREATER) + # define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL + # endif // __has_include(<optional>) && defined(CATCH_CPP17_OR_GREATER) + + // Check if byte is available and usable + # if __has_include(<cstddef>) && defined(CATCH_CPP17_OR_GREATER) + # include <cstddef> + # if defined(__cpp_lib_byte) && (__cpp_lib_byte > 0) + # define CATCH_INTERNAL_CONFIG_CPP17_BYTE + # endif + # endif // __has_include(<cstddef>) && defined(CATCH_CPP17_OR_GREATER) + + // Check if variant is available and usable + # if __has_include(<variant>) && defined(CATCH_CPP17_OR_GREATER) + # if defined(__clang__) && (__clang_major__ < 8) + // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852 + // fix should be in clang 8, workaround in libstdc++ 8.2 + # include <ciso646> + # if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) + # define CATCH_CONFIG_NO_CPP17_VARIANT + # else + # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT + # endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) + # else + # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT + # endif // defined(__clang__) && (__clang_major__ < 8) + # endif // __has_include(<variant>) && defined(CATCH_CPP17_OR_GREATER) +#endif // defined(__has_include) + +#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) +# define CATCH_CONFIG_COUNTER +#endif +#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH) +# define CATCH_CONFIG_WINDOWS_SEH +#endif +// This is set by default, because we assume that unix compilers are posix-signal-compatible by default. +#if defined(CATCH_INTERNAL_CONFIG_POSIX_SIGNALS) && !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS) +# define CATCH_CONFIG_POSIX_SIGNALS +#endif +// This is set by default, because we assume that compilers with no wchar_t support are just rare exceptions. +#if !defined(CATCH_INTERNAL_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_WCHAR) +# define CATCH_CONFIG_WCHAR +#endif + +#if !defined(CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_CPP11_TO_STRING) +# define CATCH_CONFIG_CPP11_TO_STRING +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_NO_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_CPP17_OPTIONAL) +# define CATCH_CONFIG_CPP17_OPTIONAL +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW) +# define CATCH_CONFIG_CPP17_STRING_VIEW +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_VARIANT) && !defined(CATCH_CONFIG_NO_CPP17_VARIANT) && !defined(CATCH_CONFIG_CPP17_VARIANT) +# define CATCH_CONFIG_CPP17_VARIANT +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_BYTE) && !defined(CATCH_CONFIG_NO_CPP17_BYTE) && !defined(CATCH_CONFIG_CPP17_BYTE) +# define CATCH_CONFIG_CPP17_BYTE +#endif + +#if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) +# define CATCH_INTERNAL_CONFIG_NEW_CAPTURE +#endif + +#if defined(CATCH_INTERNAL_CONFIG_NEW_CAPTURE) && !defined(CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NEW_CAPTURE) +# define CATCH_CONFIG_NEW_CAPTURE +#endif + +#if !defined(CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +# define CATCH_CONFIG_DISABLE_EXCEPTIONS +#endif + +#if defined(CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_NO_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_POLYFILL_ISNAN) +# define CATCH_CONFIG_POLYFILL_ISNAN +#endif + +#if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC) && !defined(CATCH_INTERNAL_CONFIG_NO_ASYNC) && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC) +# define CATCH_CONFIG_USE_ASYNC +#endif + +#if defined(CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_NO_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_ANDROID_LOGWRITE) +# define CATCH_CONFIG_ANDROID_LOGWRITE +#endif + +#if defined(CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_NO_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) +# define CATCH_CONFIG_GLOBAL_NEXTAFTER +#endif + +// Even if we do not think the compiler has that warning, we still have +// to provide a macro that can be used by the code. +#if !defined(CATCH_INTERNAL_START_WARNINGS_SUPPRESSION) +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION +#endif +#if !defined(CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS +#endif + +// The goal of this macro is to avoid evaluation of the arguments, but +// still have the compiler warn on problems inside... +#if !defined(CATCH_INTERNAL_IGNORE_BUT_WARN) +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) +#endif + +#if defined(__APPLE__) && defined(__apple_build_version__) && (__clang_major__ < 10) +# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#elif defined(__clang__) && (__clang_major__ < 5) +# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#endif + +#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#endif + +#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +#define CATCH_TRY if ((true)) +#define CATCH_CATCH_ALL if ((false)) +#define CATCH_CATCH_ANON(type) if ((false)) +#else +#define CATCH_TRY try +#define CATCH_CATCH_ALL catch (...) +#define CATCH_CATCH_ANON(type) catch (type) +#endif + +#if defined(CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_NO_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) +#define CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#endif + +// end catch_compiler_capabilities.h +#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line +#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) +#ifdef CATCH_CONFIG_COUNTER +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) +#else +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) +#endif + +#include <iosfwd> +#include <string> +#include <cstdint> + +// We need a dummy global operator<< so we can bring it into Catch namespace later +struct Catch_global_namespace_dummy {}; +std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy); + +namespace Catch { + + struct CaseSensitive { enum Choice { + Yes, + No + }; }; + + class NonCopyable { + NonCopyable( NonCopyable const& ) = delete; + NonCopyable( NonCopyable && ) = delete; + NonCopyable& operator = ( NonCopyable const& ) = delete; + NonCopyable& operator = ( NonCopyable && ) = delete; + + protected: + NonCopyable(); + virtual ~NonCopyable(); + }; + + struct SourceLineInfo { + + SourceLineInfo() = delete; + SourceLineInfo( char const* _file, std::size_t _line ) noexcept + : file( _file ), + line( _line ) + {} + + SourceLineInfo( SourceLineInfo const& other ) = default; + SourceLineInfo& operator = ( SourceLineInfo const& ) = default; + SourceLineInfo( SourceLineInfo&& ) noexcept = default; + SourceLineInfo& operator = ( SourceLineInfo&& ) noexcept = default; + + bool empty() const noexcept { return file[0] == '\0'; } + bool operator == ( SourceLineInfo const& other ) const noexcept; + bool operator < ( SourceLineInfo const& other ) const noexcept; + + char const* file; + std::size_t line; + }; + + std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); + + // Bring in operator<< from global namespace into Catch namespace + // This is necessary because the overload of operator<< above makes + // lookup stop at namespace Catch + using ::operator<<; + + // Use this in variadic streaming macros to allow + // >> +StreamEndStop + // as well as + // >> stuff +StreamEndStop + struct StreamEndStop { + std::string operator+() const; + }; + template<typename T> + T const& operator + ( T const& value, StreamEndStop ) { + return value; + } +} + +#define CATCH_INTERNAL_LINEINFO \ + ::Catch::SourceLineInfo( __FILE__, static_cast<std::size_t>( __LINE__ ) ) + +// end catch_common.h +namespace Catch { + + struct RegistrarForTagAliases { + RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); + }; + +} // end namespace Catch + +#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + +// end catch_tag_alias_autoregistrar.h +// start catch_test_registry.h + +// start catch_interfaces_testcase.h + +#include <vector> + +namespace Catch { + + class TestSpec; + + struct ITestInvoker { + virtual void invoke () const = 0; + virtual ~ITestInvoker(); + }; + + class TestCase; + struct IConfig; + + struct ITestCaseRegistry { + virtual ~ITestCaseRegistry(); + virtual std::vector<TestCase> const& getAllTests() const = 0; + virtual std::vector<TestCase> const& getAllTestsSorted( IConfig const& config ) const = 0; + }; + + bool isThrowSafe( TestCase const& testCase, IConfig const& config ); + bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); + std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config ); + std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config ); + +} + +// end catch_interfaces_testcase.h +// start catch_stringref.h + +#include <cstddef> +#include <string> +#include <iosfwd> +#include <cassert> + +namespace Catch { + + /// A non-owning string class (similar to the forthcoming std::string_view) + /// Note that, because a StringRef may be a substring of another string, + /// it may not be null terminated. + class StringRef { + public: + using size_type = std::size_t; + using const_iterator = const char*; + + private: + static constexpr char const* const s_empty = ""; + + char const* m_start = s_empty; + size_type m_size = 0; + + public: // construction + constexpr StringRef() noexcept = default; + + StringRef( char const* rawChars ) noexcept; + + constexpr StringRef( char const* rawChars, size_type size ) noexcept + : m_start( rawChars ), + m_size( size ) + {} + + StringRef( std::string const& stdString ) noexcept + : m_start( stdString.c_str() ), + m_size( stdString.size() ) + {} + + explicit operator std::string() const { + return std::string(m_start, m_size); + } + + public: // operators + auto operator == ( StringRef const& other ) const noexcept -> bool; + auto operator != (StringRef const& other) const noexcept -> bool { + return !(*this == other); + } + + auto operator[] ( size_type index ) const noexcept -> char { + assert(index < m_size); + return m_start[index]; + } + + public: // named queries + constexpr auto empty() const noexcept -> bool { + return m_size == 0; + } + constexpr auto size() const noexcept -> size_type { + return m_size; + } + + // Returns the current start pointer. If the StringRef is not + // null-terminated, throws std::domain_exception + auto c_str() const -> char const*; + + public: // substrings and searches + // Returns a substring of [start, start + length). + // If start + length > size(), then the substring is [start, size()). + // If start > size(), then the substring is empty. + auto substr( size_type start, size_type length ) const noexcept -> StringRef; + + // Returns the current start pointer. May not be null-terminated. + auto data() const noexcept -> char const*; + + constexpr auto isNullTerminated() const noexcept -> bool { + return m_start[m_size] == '\0'; + } + + public: // iterators + constexpr const_iterator begin() const { return m_start; } + constexpr const_iterator end() const { return m_start + m_size; } + }; + + auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&; + auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&; + + constexpr auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { + return StringRef( rawChars, size ); + } +} // namespace Catch + +constexpr auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef { + return Catch::StringRef( rawChars, size ); +} + +// end catch_stringref.h +// start catch_preprocessor.hpp + + +#define CATCH_RECURSION_LEVEL0(...) __VA_ARGS__ +#define CATCH_RECURSION_LEVEL1(...) CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL2(...) CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL3(...) CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL4(...) CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL5(...) CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(__VA_ARGS__))) + +#ifdef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_EXPAND_VARGS(...) __VA_ARGS__ +// MSVC needs more evaluations +#define CATCH_RECURSION_LEVEL6(...) CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(__VA_ARGS__))) +#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL6(CATCH_RECURSION_LEVEL6(__VA_ARGS__)) +#else +#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL5(__VA_ARGS__) +#endif + +#define CATCH_REC_END(...) +#define CATCH_REC_OUT + +#define CATCH_EMPTY() +#define CATCH_DEFER(id) id CATCH_EMPTY() + +#define CATCH_REC_GET_END2() 0, CATCH_REC_END +#define CATCH_REC_GET_END1(...) CATCH_REC_GET_END2 +#define CATCH_REC_GET_END(...) CATCH_REC_GET_END1 +#define CATCH_REC_NEXT0(test, next, ...) next CATCH_REC_OUT +#define CATCH_REC_NEXT1(test, next) CATCH_DEFER ( CATCH_REC_NEXT0 ) ( test, next, 0) +#define CATCH_REC_NEXT(test, next) CATCH_REC_NEXT1(CATCH_REC_GET_END test, next) + +#define CATCH_REC_LIST0(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST1(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0) ) ( f, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST2(f, x, peek, ...) f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) + +#define CATCH_REC_LIST0_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST1_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0_UD) ) ( f, userdata, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST2_UD(f, userdata, x, peek, ...) f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) + +// Applies the function macro `f` to each of the remaining parameters, inserts commas between the results, +// and passes userdata as the first parameter to each invocation, +// e.g. CATCH_REC_LIST_UD(f, x, a, b, c) evaluates to f(x, a), f(x, b), f(x, c) +#define CATCH_REC_LIST_UD(f, userdata, ...) CATCH_RECURSE(CATCH_REC_LIST2_UD(f, userdata, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) + +#define CATCH_REC_LIST(f, ...) CATCH_RECURSE(CATCH_REC_LIST2(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) + +#define INTERNAL_CATCH_EXPAND1(param) INTERNAL_CATCH_EXPAND2(param) +#define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO## __VA_ARGS__ +#define INTERNAL_CATCH_DEF(...) INTERNAL_CATCH_DEF __VA_ARGS__ +#define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF +#define INTERNAL_CATCH_STRINGIZE(...) INTERNAL_CATCH_STRINGIZE2(__VA_ARGS__) +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_STRINGIZE2(...) #__VA_ARGS__ +#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) +#else +// MSVC is adding extra space and needs another indirection to expand INTERNAL_CATCH_NOINTERNAL_CATCH_DEF +#define INTERNAL_CATCH_STRINGIZE2(...) INTERNAL_CATCH_STRINGIZE3(__VA_ARGS__) +#define INTERNAL_CATCH_STRINGIZE3(...) #__VA_ARGS__ +#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1) +#endif + +#define INTERNAL_CATCH_MAKE_NAMESPACE2(...) ns_##__VA_ARGS__ +#define INTERNAL_CATCH_MAKE_NAMESPACE(name) INTERNAL_CATCH_MAKE_NAMESPACE2(name) + +#define INTERNAL_CATCH_REMOVE_PARENS(...) INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF __VA_ARGS__) + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) decltype(get_wrapper<INTERNAL_CATCH_REMOVE_PARENS_GEN(__VA_ARGS__)>()) +#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)) +#else +#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) INTERNAL_CATCH_EXPAND_VARGS(decltype(get_wrapper<INTERNAL_CATCH_REMOVE_PARENS_GEN(__VA_ARGS__)>())) +#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))) +#endif + +#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(...)\ + CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,__VA_ARGS__) + +#define INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_0) INTERNAL_CATCH_REMOVE_PARENS(_0) +#define INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_0, _1) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_1) +#define INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_0, _1, _2) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_1, _2) +#define INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_0, _1, _2, _3) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_1, _2, _3) +#define INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_0, _1, _2, _3, _4) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_1, _2, _3, _4) +#define INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_0, _1, _2, _3, _4, _5) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5) +#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _3, _4, _5, _6) +#define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7) +#define INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_1, _2, _3, _4, _5, _6, _7, _8) +#define INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9) +#define INTERNAL_CATCH_REMOVE_PARENS_11_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10) + +#define INTERNAL_CATCH_VA_NARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N + +#define INTERNAL_CATCH_TYPE_GEN\ + template<typename...> struct TypeList {};\ + template<typename...Ts>\ + constexpr auto get_wrapper() noexcept -> TypeList<Ts...> { return {}; }\ + template<template<typename...> class...> struct TemplateTypeList{};\ + template<template<typename...> class...Cs>\ + constexpr auto get_wrapper() noexcept -> TemplateTypeList<Cs...> { return {}; }\ + template<typename...>\ + struct append;\ + template<typename...>\ + struct rewrap;\ + template<template<typename...> class, typename...>\ + struct create;\ + template<template<typename...> class, typename>\ + struct convert;\ + \ + template<typename T> \ + struct append<T> { using type = T; };\ + template< template<typename...> class L1, typename...E1, template<typename...> class L2, typename...E2, typename...Rest>\ + struct append<L1<E1...>, L2<E2...>, Rest...> { using type = typename append<L1<E1...,E2...>, Rest...>::type; };\ + template< template<typename...> class L1, typename...E1, typename...Rest>\ + struct append<L1<E1...>, TypeList<mpl_::na>, Rest...> { using type = L1<E1...>; };\ + \ + template< template<typename...> class Container, template<typename...> class List, typename...elems>\ + struct rewrap<TemplateTypeList<Container>, List<elems...>> { using type = TypeList<Container<elems...>>; };\ + template< template<typename...> class Container, template<typename...> class List, class...Elems, typename...Elements>\ + struct rewrap<TemplateTypeList<Container>, List<Elems...>, Elements...> { using type = typename append<TypeList<Container<Elems...>>, typename rewrap<TemplateTypeList<Container>, Elements...>::type>::type; };\ + \ + template<template <typename...> class Final, template< typename...> class...Containers, typename...Types>\ + struct create<Final, TemplateTypeList<Containers...>, TypeList<Types...>> { using type = typename append<Final<>, typename rewrap<TemplateTypeList<Containers>, Types...>::type...>::type; };\ + template<template <typename...> class Final, template <typename...> class List, typename...Ts>\ + struct convert<Final, List<Ts...>> { using type = typename append<Final<>,TypeList<Ts>...>::type; }; + +#define INTERNAL_CATCH_NTTP_1(signature, ...)\ + template<INTERNAL_CATCH_REMOVE_PARENS(signature)> struct Nttp{};\ + template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\ + constexpr auto get_wrapper() noexcept -> Nttp<__VA_ARGS__> { return {}; } \ + template<template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class...> struct NttpTemplateTypeList{};\ + template<template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class...Cs>\ + constexpr auto get_wrapper() noexcept -> NttpTemplateTypeList<Cs...> { return {}; } \ + \ + template< template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class Container, template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class List, INTERNAL_CATCH_REMOVE_PARENS(signature)>\ + struct rewrap<NttpTemplateTypeList<Container>, List<__VA_ARGS__>> { using type = TypeList<Container<__VA_ARGS__>>; };\ + template< template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class Container, template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class List, INTERNAL_CATCH_REMOVE_PARENS(signature), typename...Elements>\ + struct rewrap<NttpTemplateTypeList<Container>, List<__VA_ARGS__>, Elements...> { using type = typename append<TypeList<Container<__VA_ARGS__>>, typename rewrap<NttpTemplateTypeList<Container>, Elements...>::type>::type; };\ + template<template <typename...> class Final, template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class...Containers, typename...Types>\ + struct create<Final, NttpTemplateTypeList<Containers...>, TypeList<Types...>> { using type = typename append<Final<>, typename rewrap<NttpTemplateTypeList<Containers>, Types...>::type...>::type; }; + +#define INTERNAL_CATCH_DECLARE_SIG_TEST0(TestName) +#define INTERNAL_CATCH_DECLARE_SIG_TEST1(TestName, signature)\ + template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\ + static void TestName() +#define INTERNAL_CATCH_DECLARE_SIG_TEST_X(TestName, signature, ...)\ + template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\ + static void TestName() + +#define INTERNAL_CATCH_DEFINE_SIG_TEST0(TestName) +#define INTERNAL_CATCH_DEFINE_SIG_TEST1(TestName, signature)\ + template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\ + static void TestName() +#define INTERNAL_CATCH_DEFINE_SIG_TEST_X(TestName, signature,...)\ + template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\ + static void TestName() + +#define INTERNAL_CATCH_NTTP_REGISTER0(TestFunc, signature)\ + template<typename Type>\ + void reg_test(TypeList<Type>, Catch::NameAndTags nameAndTags)\ + {\ + Catch::AutoReg( Catch::makeTestInvoker(&TestFunc<Type>), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), nameAndTags);\ + } + +#define INTERNAL_CATCH_NTTP_REGISTER(TestFunc, signature, ...)\ + template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\ + void reg_test(Nttp<__VA_ARGS__>, Catch::NameAndTags nameAndTags)\ + {\ + Catch::AutoReg( Catch::makeTestInvoker(&TestFunc<__VA_ARGS__>), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), nameAndTags);\ + } + +#define INTERNAL_CATCH_NTTP_REGISTER_METHOD0(TestName, signature, ...)\ + template<typename Type>\ + void reg_test(TypeList<Type>, Catch::StringRef className, Catch::NameAndTags nameAndTags)\ + {\ + Catch::AutoReg( Catch::makeTestInvoker(&TestName<Type>::test), CATCH_INTERNAL_LINEINFO, className, nameAndTags);\ + } + +#define INTERNAL_CATCH_NTTP_REGISTER_METHOD(TestName, signature, ...)\ + template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\ + void reg_test(Nttp<__VA_ARGS__>, Catch::StringRef className, Catch::NameAndTags nameAndTags)\ + {\ + Catch::AutoReg( Catch::makeTestInvoker(&TestName<__VA_ARGS__>::test), CATCH_INTERNAL_LINEINFO, className, nameAndTags);\ + } + +#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0(TestName, ClassName) +#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1(TestName, ClassName, signature)\ + template<typename TestType> \ + struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName)<TestType> { \ + void test();\ + } + +#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X(TestName, ClassName, signature, ...)\ + template<INTERNAL_CATCH_REMOVE_PARENS(signature)> \ + struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName)<__VA_ARGS__> { \ + void test();\ + } + +#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0(TestName) +#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1(TestName, signature)\ + template<typename TestType> \ + void INTERNAL_CATCH_MAKE_NAMESPACE(TestName)::TestName<TestType>::test() +#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X(TestName, signature, ...)\ + template<INTERNAL_CATCH_REMOVE_PARENS(signature)> \ + void INTERNAL_CATCH_MAKE_NAMESPACE(TestName)::TestName<__VA_ARGS__>::test() + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_NTTP_0 +#define INTERNAL_CATCH_NTTP_GEN(...) INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__),INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_0) +#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0)(TestName, __VA_ARGS__) +#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0)(TestName, ClassName, __VA_ARGS__) +#define INTERNAL_CATCH_NTTP_REG_METHOD_GEN(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD0, INTERNAL_CATCH_NTTP_REGISTER_METHOD0)(TestName, __VA_ARGS__) +#define INTERNAL_CATCH_NTTP_REG_GEN(TestFunc, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER0, INTERNAL_CATCH_NTTP_REGISTER0)(TestFunc, __VA_ARGS__) +#define INTERNAL_CATCH_DEFINE_SIG_TEST(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST1, INTERNAL_CATCH_DEFINE_SIG_TEST0)(TestName, __VA_ARGS__) +#define INTERNAL_CATCH_DECLARE_SIG_TEST(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST1, INTERNAL_CATCH_DECLARE_SIG_TEST0)(TestName, __VA_ARGS__) +#define INTERNAL_CATCH_REMOVE_PARENS_GEN(...) INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_REMOVE_PARENS_11_ARG,INTERNAL_CATCH_REMOVE_PARENS_10_ARG,INTERNAL_CATCH_REMOVE_PARENS_9_ARG,INTERNAL_CATCH_REMOVE_PARENS_8_ARG,INTERNAL_CATCH_REMOVE_PARENS_7_ARG,INTERNAL_CATCH_REMOVE_PARENS_6_ARG,INTERNAL_CATCH_REMOVE_PARENS_5_ARG,INTERNAL_CATCH_REMOVE_PARENS_4_ARG,INTERNAL_CATCH_REMOVE_PARENS_3_ARG,INTERNAL_CATCH_REMOVE_PARENS_2_ARG,INTERNAL_CATCH_REMOVE_PARENS_1_ARG)(__VA_ARGS__) +#else +#define INTERNAL_CATCH_NTTP_0(signature) +#define INTERNAL_CATCH_NTTP_GEN(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1,INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_0)( __VA_ARGS__)) +#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0)(TestName, __VA_ARGS__)) +#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0)(TestName, ClassName, __VA_ARGS__)) +#define INTERNAL_CATCH_NTTP_REG_METHOD_GEN(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD0, INTERNAL_CATCH_NTTP_REGISTER_METHOD0)(TestName, __VA_ARGS__)) +#define INTERNAL_CATCH_NTTP_REG_GEN(TestFunc, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER0, INTERNAL_CATCH_NTTP_REGISTER0)(TestFunc, __VA_ARGS__)) +#define INTERNAL_CATCH_DEFINE_SIG_TEST(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST1, INTERNAL_CATCH_DEFINE_SIG_TEST0)(TestName, __VA_ARGS__)) +#define INTERNAL_CATCH_DECLARE_SIG_TEST(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST1, INTERNAL_CATCH_DECLARE_SIG_TEST0)(TestName, __VA_ARGS__)) +#define INTERNAL_CATCH_REMOVE_PARENS_GEN(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_REMOVE_PARENS_11_ARG,INTERNAL_CATCH_REMOVE_PARENS_10_ARG,INTERNAL_CATCH_REMOVE_PARENS_9_ARG,INTERNAL_CATCH_REMOVE_PARENS_8_ARG,INTERNAL_CATCH_REMOVE_PARENS_7_ARG,INTERNAL_CATCH_REMOVE_PARENS_6_ARG,INTERNAL_CATCH_REMOVE_PARENS_5_ARG,INTERNAL_CATCH_REMOVE_PARENS_4_ARG,INTERNAL_CATCH_REMOVE_PARENS_3_ARG,INTERNAL_CATCH_REMOVE_PARENS_2_ARG,INTERNAL_CATCH_REMOVE_PARENS_1_ARG)(__VA_ARGS__)) +#endif + +// end catch_preprocessor.hpp +// start catch_meta.hpp + + +#include <type_traits> + +namespace Catch { + template<typename T> + struct always_false : std::false_type {}; + + template <typename> struct true_given : std::true_type {}; + struct is_callable_tester { + template <typename Fun, typename... Args> + true_given<decltype(std::declval<Fun>()(std::declval<Args>()...))> static test(int); + template <typename...> + std::false_type static test(...); + }; + + template <typename T> + struct is_callable; + + template <typename Fun, typename... Args> + struct is_callable<Fun(Args...)> : decltype(is_callable_tester::test<Fun, Args...>(0)) {}; + +#if defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable >= 201703 + // std::result_of is deprecated in C++17 and removed in C++20. Hence, it is + // replaced with std::invoke_result here. + template <typename Func, typename... U> + using FunctionReturnType = std::remove_reference_t<std::remove_cv_t<std::invoke_result_t<Func, U...>>>; +#else + // Keep ::type here because we still support C++11 + template <typename Func, typename... U> + using FunctionReturnType = typename std::remove_reference<typename std::remove_cv<typename std::result_of<Func(U...)>::type>::type>::type; +#endif + +} // namespace Catch + +namespace mpl_{ + struct na; +} + +// end catch_meta.hpp +namespace Catch { + +template<typename C> +class TestInvokerAsMethod : public ITestInvoker { + void (C::*m_testAsMethod)(); +public: + TestInvokerAsMethod( void (C::*testAsMethod)() ) noexcept : m_testAsMethod( testAsMethod ) {} + + void invoke() const override { + C obj; + (obj.*m_testAsMethod)(); + } +}; + +auto makeTestInvoker( void(*testAsFunction)() ) noexcept -> ITestInvoker*; + +template<typename C> +auto makeTestInvoker( void (C::*testAsMethod)() ) noexcept -> ITestInvoker* { + return new(std::nothrow) TestInvokerAsMethod<C>( testAsMethod ); +} + +struct NameAndTags { + NameAndTags( StringRef const& name_ = StringRef(), StringRef const& tags_ = StringRef() ) noexcept; + StringRef name; + StringRef tags; +}; + +struct AutoReg : NonCopyable { + AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef const& classOrMethod, NameAndTags const& nameAndTags ) noexcept; + ~AutoReg(); +}; + +} // end namespace Catch + +#if defined(CATCH_CONFIG_DISABLE) + #define INTERNAL_CATCH_TESTCASE_NO_REGISTRATION( TestName, ... ) \ + static void TestName() + #define INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION( TestName, ClassName, ... ) \ + namespace{ \ + struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \ + void test(); \ + }; \ + } \ + void TestName::test() + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( TestName, TestFunc, Name, Tags, Signature, ... ) \ + INTERNAL_CATCH_DEFINE_SIG_TEST(TestFunc, INTERNAL_CATCH_REMOVE_PARENS(Signature)) + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( TestNameClass, TestName, ClassName, Name, Tags, Signature, ... ) \ + namespace{ \ + namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName) { \ + INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, INTERNAL_CATCH_REMOVE_PARENS(Signature));\ + } \ + } \ + INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, INTERNAL_CATCH_REMOVE_PARENS(Signature)) + + #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(Name, Tags, ...) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, typename TestType, __VA_ARGS__ ) + #else + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(Name, Tags, ...) \ + INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, typename TestType, __VA_ARGS__ ) ) + #endif + + #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(Name, Tags, Signature, ...) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, Signature, __VA_ARGS__ ) + #else + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(Name, Tags, Signature, ...) \ + INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, Signature, __VA_ARGS__ ) ) + #endif + + #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION( ClassName, Name, Tags,... ) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ ) + #else + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION( ClassName, Name, Tags,... ) \ + INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ ) ) + #endif + + #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION( ClassName, Name, Tags, Signature, ... ) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ ) + #else + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION( ClassName, Name, Tags, Signature, ... ) \ + INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ ) ) + #endif +#endif + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \ + static void TestName(); \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &TestName ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ + static void TestName() + #define INTERNAL_CATCH_TESTCASE( ... ) \ + INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ), __VA_ARGS__ ) + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &QualifiedMethod ), CATCH_INTERNAL_LINEINFO, "&" #QualifiedMethod, Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ \ + struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \ + void test(); \ + }; \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \ + } \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ + void TestName::test() + #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \ + INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ), ClassName, __VA_ARGS__ ) + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( Function ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_2(TestName, TestFunc, Name, Tags, Signature, ... )\ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ + INTERNAL_CATCH_DECLARE_SIG_TEST(TestFunc, INTERNAL_CATCH_REMOVE_PARENS(Signature));\ + namespace {\ + namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){\ + INTERNAL_CATCH_TYPE_GEN\ + INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature))\ + INTERNAL_CATCH_NTTP_REG_GEN(TestFunc,INTERNAL_CATCH_REMOVE_PARENS(Signature))\ + template<typename...Types> \ + struct TestName{\ + TestName(){\ + int index = 0; \ + constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, __VA_ARGS__)};\ + using expander = int[];\ + (void)expander{(reg_test(Types{}, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index]), Tags } ), index++)... };/* NOLINT */ \ + }\ + };\ + static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\ + TestName<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(__VA_ARGS__)>();\ + return 0;\ + }();\ + }\ + }\ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ + INTERNAL_CATCH_DEFINE_SIG_TEST(TestFunc,INTERNAL_CATCH_REMOVE_PARENS(Signature)) + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE(Name, Tags, ...) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, typename TestType, __VA_ARGS__ ) +#else + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE(Name, Tags, ...) \ + INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, typename TestType, __VA_ARGS__ ) ) +#endif + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG(Name, Tags, Signature, ...) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, Signature, __VA_ARGS__ ) +#else + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG(Name, Tags, Signature, ...) \ + INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, Signature, __VA_ARGS__ ) ) +#endif + + #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(TestName, TestFuncName, Name, Tags, Signature, TmplTypes, TypesList) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ + template<typename TestType> static void TestFuncName(); \ + namespace {\ + namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName) { \ + INTERNAL_CATCH_TYPE_GEN \ + INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature)) \ + template<typename... Types> \ + struct TestName { \ + void reg_tests() { \ + int index = 0; \ + using expander = int[]; \ + constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes))};\ + constexpr char const* types_list[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TypesList))};\ + constexpr auto num_types = sizeof(types_list) / sizeof(types_list[0]);\ + (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFuncName<Types> ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index / num_types]) + "<" + std::string(types_list[index % num_types]) + ">", Tags } ), index++)... };/* NOLINT */\ + } \ + }; \ + static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \ + using TestInit = typename create<TestName, decltype(get_wrapper<INTERNAL_CATCH_REMOVE_PARENS(TmplTypes)>()), TypeList<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(INTERNAL_CATCH_REMOVE_PARENS(TypesList))>>::type; \ + TestInit t; \ + t.reg_tests(); \ + return 0; \ + }(); \ + } \ + } \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ + template<typename TestType> \ + static void TestFuncName() + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR + #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE(Name, Tags, ...)\ + INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, typename T,__VA_ARGS__) +#else + #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE(Name, Tags, ...)\ + INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, typename T, __VA_ARGS__ ) ) +#endif + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR + #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG(Name, Tags, Signature, ...)\ + INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, Signature, __VA_ARGS__) +#else + #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG(Name, Tags, Signature, ...)\ + INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, Signature, __VA_ARGS__ ) ) +#endif + + #define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_2(TestName, TestFunc, Name, Tags, TmplList)\ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ + template<typename TestType> static void TestFunc(); \ + namespace {\ + namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){\ + INTERNAL_CATCH_TYPE_GEN\ + template<typename... Types> \ + struct TestName { \ + void reg_tests() { \ + int index = 0; \ + using expander = int[]; \ + (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFunc<Types> ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " + std::string(INTERNAL_CATCH_STRINGIZE(TmplList)) + " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */\ + } \ + };\ + static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \ + using TestInit = typename convert<TestName, TmplList>::type; \ + TestInit t; \ + t.reg_tests(); \ + return 0; \ + }(); \ + }}\ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ + template<typename TestType> \ + static void TestFunc() + + #define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE(Name, Tags, TmplList) \ + INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, TmplList ) + + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, Signature, ... ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ + namespace {\ + namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){ \ + INTERNAL_CATCH_TYPE_GEN\ + INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature))\ + INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, INTERNAL_CATCH_REMOVE_PARENS(Signature));\ + INTERNAL_CATCH_NTTP_REG_METHOD_GEN(TestName, INTERNAL_CATCH_REMOVE_PARENS(Signature))\ + template<typename...Types> \ + struct TestNameClass{\ + TestNameClass(){\ + int index = 0; \ + constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, __VA_ARGS__)};\ + using expander = int[];\ + (void)expander{(reg_test(Types{}, #ClassName, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index]), Tags } ), index++)... };/* NOLINT */ \ + }\ + };\ + static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\ + TestNameClass<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(__VA_ARGS__)>();\ + return 0;\ + }();\ + }\ + }\ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ + INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, INTERNAL_CATCH_REMOVE_PARENS(Signature)) + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( ClassName, Name, Tags,... ) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ ) +#else + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( ClassName, Name, Tags,... ) \ + INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ ) ) +#endif + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... ) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ ) +#else + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... ) \ + INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ ) ) +#endif + + #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2(TestNameClass, TestName, ClassName, Name, Tags, Signature, TmplTypes, TypesList)\ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ + template<typename TestType> \ + struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName <TestType>) { \ + void test();\ + };\ + namespace {\ + namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestNameClass) {\ + INTERNAL_CATCH_TYPE_GEN \ + INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature))\ + template<typename...Types>\ + struct TestNameClass{\ + void reg_tests(){\ + int index = 0;\ + using expander = int[];\ + constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes))};\ + constexpr char const* types_list[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TypesList))};\ + constexpr auto num_types = sizeof(types_list) / sizeof(types_list[0]);\ + (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName<Types>::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index / num_types]) + "<" + std::string(types_list[index % num_types]) + ">", Tags } ), index++)... };/* NOLINT */ \ + }\ + };\ + static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\ + using TestInit = typename create<TestNameClass, decltype(get_wrapper<INTERNAL_CATCH_REMOVE_PARENS(TmplTypes)>()), TypeList<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(INTERNAL_CATCH_REMOVE_PARENS(TypesList))>>::type;\ + TestInit t;\ + t.reg_tests();\ + return 0;\ + }(); \ + }\ + }\ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ + template<typename TestType> \ + void TestName<TestType>::test() + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR + #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( ClassName, Name, Tags, ... )\ + INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), ClassName, Name, Tags, typename T, __VA_ARGS__ ) +#else + #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( ClassName, Name, Tags, ... )\ + INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), ClassName, Name, Tags, typename T,__VA_ARGS__ ) ) +#endif + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR + #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... )\ + INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), ClassName, Name, Tags, Signature, __VA_ARGS__ ) +#else + #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... )\ + INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), ClassName, Name, Tags, Signature,__VA_ARGS__ ) ) +#endif + + #define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, TmplList) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ + template<typename TestType> \ + struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName <TestType>) { \ + void test();\ + };\ + namespace {\ + namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){ \ + INTERNAL_CATCH_TYPE_GEN\ + template<typename...Types>\ + struct TestNameClass{\ + void reg_tests(){\ + int index = 0;\ + using expander = int[];\ + (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName<Types>::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(INTERNAL_CATCH_STRINGIZE(TmplList)) + " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */ \ + }\ + };\ + static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\ + using TestInit = typename convert<TestNameClass, TmplList>::type;\ + TestInit t;\ + t.reg_tests();\ + return 0;\ + }(); \ + }}\ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ + template<typename TestType> \ + void TestName<TestType>::test() + +#define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD(ClassName, Name, Tags, TmplList) \ + INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), ClassName, Name, Tags, TmplList ) + +// end catch_test_registry.h +// start catch_capture.hpp + +// start catch_assertionhandler.h + +// start catch_assertioninfo.h + +// start catch_result_type.h + +namespace Catch { + + // ResultWas::OfType enum + struct ResultWas { enum OfType { + Unknown = -1, + Ok = 0, + Info = 1, + Warning = 2, + + FailureBit = 0x10, + + ExpressionFailed = FailureBit | 1, + ExplicitFailure = FailureBit | 2, + + Exception = 0x100 | FailureBit, + + ThrewException = Exception | 1, + DidntThrowException = Exception | 2, + + FatalErrorCondition = 0x200 | FailureBit + + }; }; + + bool isOk( ResultWas::OfType resultType ); + bool isJustInfo( int flags ); + + // ResultDisposition::Flags enum + struct ResultDisposition { enum Flags { + Normal = 0x01, + + ContinueOnFailure = 0x02, // Failures fail test, but execution continues + FalseTest = 0x04, // Prefix expression with ! + SuppressFail = 0x08 // Failures are reported but do not fail the test + }; }; + + ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ); + + bool shouldContinueOnFailure( int flags ); + inline bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; } + bool shouldSuppressFailure( int flags ); + +} // end namespace Catch + +// end catch_result_type.h +namespace Catch { + + struct AssertionInfo + { + StringRef macroName; + SourceLineInfo lineInfo; + StringRef capturedExpression; + ResultDisposition::Flags resultDisposition; + + // We want to delete this constructor but a compiler bug in 4.8 means + // the struct is then treated as non-aggregate + //AssertionInfo() = delete; + }; + +} // end namespace Catch + +// end catch_assertioninfo.h +// start catch_decomposer.h + +// start catch_tostring.h + +#include <vector> +#include <cstddef> +#include <type_traits> +#include <string> +// start catch_stream.h + +#include <iosfwd> +#include <cstddef> +#include <ostream> + +namespace Catch { + + std::ostream& cout(); + std::ostream& cerr(); + std::ostream& clog(); + + class StringRef; + + struct IStream { + virtual ~IStream(); + virtual std::ostream& stream() const = 0; + }; + + auto makeStream( StringRef const &filename ) -> IStream const*; + + class ReusableStringStream : NonCopyable { + std::size_t m_index; + std::ostream* m_oss; + public: + ReusableStringStream(); + ~ReusableStringStream(); + + auto str() const -> std::string; + + template<typename T> + auto operator << ( T const& value ) -> ReusableStringStream& { + *m_oss << value; + return *this; + } + auto get() -> std::ostream& { return *m_oss; } + }; +} + +// end catch_stream.h +// start catch_interfaces_enum_values_registry.h + +#include <vector> + +namespace Catch { + + namespace Detail { + struct EnumInfo { + StringRef m_name; + std::vector<std::pair<int, StringRef>> m_values; + + ~EnumInfo(); + + StringRef lookup( int value ) const; + }; + } // namespace Detail + + struct IMutableEnumValuesRegistry { + virtual ~IMutableEnumValuesRegistry(); + + virtual Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::vector<int> const& values ) = 0; + + template<typename E> + Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::initializer_list<E> values ) { + static_assert(sizeof(int) >= sizeof(E), "Cannot serialize enum to int"); + std::vector<int> intValues; + intValues.reserve( values.size() ); + for( auto enumValue : values ) + intValues.push_back( static_cast<int>( enumValue ) ); + return registerEnum( enumName, allEnums, intValues ); + } + }; + +} // Catch + +// end catch_interfaces_enum_values_registry.h + +#ifdef CATCH_CONFIG_CPP17_STRING_VIEW +#include <string_view> +#endif + +#ifdef __OBJC__ +// start catch_objc_arc.hpp + +#import <Foundation/Foundation.h> + +#ifdef __has_feature +#define CATCH_ARC_ENABLED __has_feature(objc_arc) +#else +#define CATCH_ARC_ENABLED 0 +#endif + +void arcSafeRelease( NSObject* obj ); +id performOptionalSelector( id obj, SEL sel ); + +#if !CATCH_ARC_ENABLED +inline void arcSafeRelease( NSObject* obj ) { + [obj release]; +} +inline id performOptionalSelector( id obj, SEL sel ) { + if( [obj respondsToSelector: sel] ) + return [obj performSelector: sel]; + return nil; +} +#define CATCH_UNSAFE_UNRETAINED +#define CATCH_ARC_STRONG +#else +inline void arcSafeRelease( NSObject* ){} +inline id performOptionalSelector( id obj, SEL sel ) { +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" +#endif + if( [obj respondsToSelector: sel] ) + return [obj performSelector: sel]; +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + return nil; +} +#define CATCH_UNSAFE_UNRETAINED __unsafe_unretained +#define CATCH_ARC_STRONG __strong +#endif + +// end catch_objc_arc.hpp +#endif + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4180) // We attempt to stream a function (address) by const&, which MSVC complains about but is harmless +#endif + +namespace Catch { + namespace Detail { + + extern const std::string unprintableString; + + std::string rawMemoryToString( const void *object, std::size_t size ); + + template<typename T> + std::string rawMemoryToString( const T& object ) { + return rawMemoryToString( &object, sizeof(object) ); + } + + template<typename T> + class IsStreamInsertable { + template<typename Stream, typename U> + static auto test(int) + -> decltype(std::declval<Stream&>() << std::declval<U>(), std::true_type()); + + template<typename, typename> + static auto test(...)->std::false_type; + + public: + static const bool value = decltype(test<std::ostream, const T&>(0))::value; + }; + + template<typename E> + std::string convertUnknownEnumToString( E e ); + + template<typename T> + typename std::enable_if< + !std::is_enum<T>::value && !std::is_base_of<std::exception, T>::value, + std::string>::type convertUnstreamable( T const& ) { + return Detail::unprintableString; + } + template<typename T> + typename std::enable_if< + !std::is_enum<T>::value && std::is_base_of<std::exception, T>::value, + std::string>::type convertUnstreamable(T const& ex) { + return ex.what(); + } + + template<typename T> + typename std::enable_if< + std::is_enum<T>::value + , std::string>::type convertUnstreamable( T const& value ) { + return convertUnknownEnumToString( value ); + } + +#if defined(_MANAGED) + //! Convert a CLR string to a utf8 std::string + template<typename T> + std::string clrReferenceToString( T^ ref ) { + if (ref == nullptr) + return std::string("null"); + auto bytes = System::Text::Encoding::UTF8->GetBytes(ref->ToString()); + cli::pin_ptr<System::Byte> p = &bytes[0]; + return std::string(reinterpret_cast<char const *>(p), bytes->Length); + } +#endif + + } // namespace Detail + + // If we decide for C++14, change these to enable_if_ts + template <typename T, typename = void> + struct StringMaker { + template <typename Fake = T> + static + typename std::enable_if<::Catch::Detail::IsStreamInsertable<Fake>::value, std::string>::type + convert(const Fake& value) { + ReusableStringStream rss; + // NB: call using the function-like syntax to avoid ambiguity with + // user-defined templated operator<< under clang. + rss.operator<<(value); + return rss.str(); + } + + template <typename Fake = T> + static + typename std::enable_if<!::Catch::Detail::IsStreamInsertable<Fake>::value, std::string>::type + convert( const Fake& value ) { +#if !defined(CATCH_CONFIG_FALLBACK_STRINGIFIER) + return Detail::convertUnstreamable(value); +#else + return CATCH_CONFIG_FALLBACK_STRINGIFIER(value); +#endif + } + }; + + namespace Detail { + + // This function dispatches all stringification requests inside of Catch. + // Should be preferably called fully qualified, like ::Catch::Detail::stringify + template <typename T> + std::string stringify(const T& e) { + return ::Catch::StringMaker<typename std::remove_cv<typename std::remove_reference<T>::type>::type>::convert(e); + } + + template<typename E> + std::string convertUnknownEnumToString( E e ) { + return ::Catch::Detail::stringify(static_cast<typename std::underlying_type<E>::type>(e)); + } + +#if defined(_MANAGED) + template <typename T> + std::string stringify( T^ e ) { + return ::Catch::StringMaker<T^>::convert(e); + } +#endif + + } // namespace Detail + + // Some predefined specializations + + template<> + struct StringMaker<std::string> { + static std::string convert(const std::string& str); + }; + +#ifdef CATCH_CONFIG_CPP17_STRING_VIEW + template<> + struct StringMaker<std::string_view> { + static std::string convert(std::string_view str); + }; +#endif + + template<> + struct StringMaker<char const *> { + static std::string convert(char const * str); + }; + template<> + struct StringMaker<char *> { + static std::string convert(char * str); + }; + +#ifdef CATCH_CONFIG_WCHAR + template<> + struct StringMaker<std::wstring> { + static std::string convert(const std::wstring& wstr); + }; + +# ifdef CATCH_CONFIG_CPP17_STRING_VIEW + template<> + struct StringMaker<std::wstring_view> { + static std::string convert(std::wstring_view str); + }; +# endif + + template<> + struct StringMaker<wchar_t const *> { + static std::string convert(wchar_t const * str); + }; + template<> + struct StringMaker<wchar_t *> { + static std::string convert(wchar_t * str); + }; +#endif + + // TBD: Should we use `strnlen` to ensure that we don't go out of the buffer, + // while keeping string semantics? + template<int SZ> + struct StringMaker<char[SZ]> { + static std::string convert(char const* str) { + return ::Catch::Detail::stringify(std::string{ str }); + } + }; + template<int SZ> + struct StringMaker<signed char[SZ]> { + static std::string convert(signed char const* str) { + return ::Catch::Detail::stringify(std::string{ reinterpret_cast<char const *>(str) }); + } + }; + template<int SZ> + struct StringMaker<unsigned char[SZ]> { + static std::string convert(unsigned char const* str) { + return ::Catch::Detail::stringify(std::string{ reinterpret_cast<char const *>(str) }); + } + }; + +#if defined(CATCH_CONFIG_CPP17_BYTE) + template<> + struct StringMaker<std::byte> { + static std::string convert(std::byte value); + }; +#endif // defined(CATCH_CONFIG_CPP17_BYTE) + template<> + struct StringMaker<int> { + static std::string convert(int value); + }; + template<> + struct StringMaker<long> { + static std::string convert(long value); + }; + template<> + struct StringMaker<long long> { + static std::string convert(long long value); + }; + template<> + struct StringMaker<unsigned int> { + static std::string convert(unsigned int value); + }; + template<> + struct StringMaker<unsigned long> { + static std::string convert(unsigned long value); + }; + template<> + struct StringMaker<unsigned long long> { + static std::string convert(unsigned long long value); + }; + + template<> + struct StringMaker<bool> { + static std::string convert(bool b); + }; + + template<> + struct StringMaker<char> { + static std::string convert(char c); + }; + template<> + struct StringMaker<signed char> { + static std::string convert(signed char c); + }; + template<> + struct StringMaker<unsigned char> { + static std::string convert(unsigned char c); + }; + + template<> + struct StringMaker<std::nullptr_t> { + static std::string convert(std::nullptr_t); + }; + + template<> + struct StringMaker<float> { + static std::string convert(float value); + static int precision; + }; + + template<> + struct StringMaker<double> { + static std::string convert(double value); + static int precision; + }; + + template <typename T> + struct StringMaker<T*> { + template <typename U> + static std::string convert(U* p) { + if (p) { + return ::Catch::Detail::rawMemoryToString(p); + } else { + return "nullptr"; + } + } + }; + + template <typename R, typename C> + struct StringMaker<R C::*> { + static std::string convert(R C::* p) { + if (p) { + return ::Catch::Detail::rawMemoryToString(p); + } else { + return "nullptr"; + } + } + }; + +#if defined(_MANAGED) + template <typename T> + struct StringMaker<T^> { + static std::string convert( T^ ref ) { + return ::Catch::Detail::clrReferenceToString(ref); + } + }; +#endif + + namespace Detail { + template<typename InputIterator, typename Sentinel = InputIterator> + std::string rangeToString(InputIterator first, Sentinel last) { + ReusableStringStream rss; + rss << "{ "; + if (first != last) { + rss << ::Catch::Detail::stringify(*first); + for (++first; first != last; ++first) + rss << ", " << ::Catch::Detail::stringify(*first); + } + rss << " }"; + return rss.str(); + } + } + +#ifdef __OBJC__ + template<> + struct StringMaker<NSString*> { + static std::string convert(NSString * nsstring) { + if (!nsstring) + return "nil"; + return std::string("@") + [nsstring UTF8String]; + } + }; + template<> + struct StringMaker<NSObject*> { + static std::string convert(NSObject* nsObject) { + return ::Catch::Detail::stringify([nsObject description]); + } + + }; + namespace Detail { + inline std::string stringify( NSString* nsstring ) { + return StringMaker<NSString*>::convert( nsstring ); + } + + } // namespace Detail +#endif // __OBJC__ + +} // namespace Catch + +////////////////////////////////////////////////////// +// Separate std-lib types stringification, so it can be selectively enabled +// This means that we do not bring in + +#if defined(CATCH_CONFIG_ENABLE_ALL_STRINGMAKERS) +# define CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER +# define CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER +# define CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER +# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +# define CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER +#endif + +// Separate std::pair specialization +#if defined(CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER) +#include <utility> +namespace Catch { + template<typename T1, typename T2> + struct StringMaker<std::pair<T1, T2> > { + static std::string convert(const std::pair<T1, T2>& pair) { + ReusableStringStream rss; + rss << "{ " + << ::Catch::Detail::stringify(pair.first) + << ", " + << ::Catch::Detail::stringify(pair.second) + << " }"; + return rss.str(); + } + }; +} +#endif // CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER + +#if defined(CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_OPTIONAL) +#include <optional> +namespace Catch { + template<typename T> + struct StringMaker<std::optional<T> > { + static std::string convert(const std::optional<T>& optional) { + ReusableStringStream rss; + if (optional.has_value()) { + rss << ::Catch::Detail::stringify(*optional); + } else { + rss << "{ }"; + } + return rss.str(); + } + }; +} +#endif // CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER + +// Separate std::tuple specialization +#if defined(CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER) +#include <tuple> +namespace Catch { + namespace Detail { + template< + typename Tuple, + std::size_t N = 0, + bool = (N < std::tuple_size<Tuple>::value) + > + struct TupleElementPrinter { + static void print(const Tuple& tuple, std::ostream& os) { + os << (N ? ", " : " ") + << ::Catch::Detail::stringify(std::get<N>(tuple)); + TupleElementPrinter<Tuple, N + 1>::print(tuple, os); + } + }; + + template< + typename Tuple, + std::size_t N + > + struct TupleElementPrinter<Tuple, N, false> { + static void print(const Tuple&, std::ostream&) {} + }; + + } + + template<typename ...Types> + struct StringMaker<std::tuple<Types...>> { + static std::string convert(const std::tuple<Types...>& tuple) { + ReusableStringStream rss; + rss << '{'; + Detail::TupleElementPrinter<std::tuple<Types...>>::print(tuple, rss.get()); + rss << " }"; + return rss.str(); + } + }; +} +#endif // CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER + +#if defined(CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_VARIANT) +#include <variant> +namespace Catch { + template<> + struct StringMaker<std::monostate> { + static std::string convert(const std::monostate&) { + return "{ }"; + } + }; + + template<typename... Elements> + struct StringMaker<std::variant<Elements...>> { + static std::string convert(const std::variant<Elements...>& variant) { + if (variant.valueless_by_exception()) { + return "{valueless variant}"; + } else { + return std::visit( + [](const auto& value) { + return ::Catch::Detail::stringify(value); + }, + variant + ); + } + } + }; +} +#endif // CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER + +namespace Catch { + // Import begin/ end from std here + using std::begin; + using std::end; + + namespace detail { + template <typename...> + struct void_type { + using type = void; + }; + + template <typename T, typename = void> + struct is_range_impl : std::false_type { + }; + + template <typename T> + struct is_range_impl<T, typename void_type<decltype(begin(std::declval<T>()))>::type> : std::true_type { + }; + } // namespace detail + + template <typename T> + struct is_range : detail::is_range_impl<T> { + }; + +#if defined(_MANAGED) // Managed types are never ranges + template <typename T> + struct is_range<T^> { + static const bool value = false; + }; +#endif + + template<typename Range> + std::string rangeToString( Range const& range ) { + return ::Catch::Detail::rangeToString( begin( range ), end( range ) ); + } + + // Handle vector<bool> specially + template<typename Allocator> + std::string rangeToString( std::vector<bool, Allocator> const& v ) { + ReusableStringStream rss; + rss << "{ "; + bool first = true; + for( bool b : v ) { + if( first ) + first = false; + else + rss << ", "; + rss << ::Catch::Detail::stringify( b ); + } + rss << " }"; + return rss.str(); + } + + template<typename R> + struct StringMaker<R, typename std::enable_if<is_range<R>::value && !::Catch::Detail::IsStreamInsertable<R>::value>::type> { + static std::string convert( R const& range ) { + return rangeToString( range ); + } + }; + + template <typename T, int SZ> + struct StringMaker<T[SZ]> { + static std::string convert(T const(&arr)[SZ]) { + return rangeToString(arr); + } + }; + +} // namespace Catch + +// Separate std::chrono::duration specialization +#if defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) +#include <ctime> +#include <ratio> +#include <chrono> + +namespace Catch { + +template <class Ratio> +struct ratio_string { + static std::string symbol(); +}; + +template <class Ratio> +std::string ratio_string<Ratio>::symbol() { + Catch::ReusableStringStream rss; + rss << '[' << Ratio::num << '/' + << Ratio::den << ']'; + return rss.str(); +} +template <> +struct ratio_string<std::atto> { + static std::string symbol(); +}; +template <> +struct ratio_string<std::femto> { + static std::string symbol(); +}; +template <> +struct ratio_string<std::pico> { + static std::string symbol(); +}; +template <> +struct ratio_string<std::nano> { + static std::string symbol(); +}; +template <> +struct ratio_string<std::micro> { + static std::string symbol(); +}; +template <> +struct ratio_string<std::milli> { + static std::string symbol(); +}; + + //////////// + // std::chrono::duration specializations + template<typename Value, typename Ratio> + struct StringMaker<std::chrono::duration<Value, Ratio>> { + static std::string convert(std::chrono::duration<Value, Ratio> const& duration) { + ReusableStringStream rss; + rss << duration.count() << ' ' << ratio_string<Ratio>::symbol() << 's'; + return rss.str(); + } + }; + template<typename Value> + struct StringMaker<std::chrono::duration<Value, std::ratio<1>>> { + static std::string convert(std::chrono::duration<Value, std::ratio<1>> const& duration) { + ReusableStringStream rss; + rss << duration.count() << " s"; + return rss.str(); + } + }; + template<typename Value> + struct StringMaker<std::chrono::duration<Value, std::ratio<60>>> { + static std::string convert(std::chrono::duration<Value, std::ratio<60>> const& duration) { + ReusableStringStream rss; + rss << duration.count() << " m"; + return rss.str(); + } + }; + template<typename Value> + struct StringMaker<std::chrono::duration<Value, std::ratio<3600>>> { + static std::string convert(std::chrono::duration<Value, std::ratio<3600>> const& duration) { + ReusableStringStream rss; + rss << duration.count() << " h"; + return rss.str(); + } + }; + + //////////// + // std::chrono::time_point specialization + // Generic time_point cannot be specialized, only std::chrono::time_point<system_clock> + template<typename Clock, typename Duration> + struct StringMaker<std::chrono::time_point<Clock, Duration>> { + static std::string convert(std::chrono::time_point<Clock, Duration> const& time_point) { + return ::Catch::Detail::stringify(time_point.time_since_epoch()) + " since epoch"; + } + }; + // std::chrono::time_point<system_clock> specialization + template<typename Duration> + struct StringMaker<std::chrono::time_point<std::chrono::system_clock, Duration>> { + static std::string convert(std::chrono::time_point<std::chrono::system_clock, Duration> const& time_point) { + auto converted = std::chrono::system_clock::to_time_t(time_point); + +#ifdef _MSC_VER + std::tm timeInfo = {}; + gmtime_s(&timeInfo, &converted); +#else + std::tm* timeInfo = std::gmtime(&converted); +#endif + + auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); + char timeStamp[timeStampSize]; + const char * const fmt = "%Y-%m-%dT%H:%M:%SZ"; + +#ifdef _MSC_VER + std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); +#else + std::strftime(timeStamp, timeStampSize, fmt, timeInfo); +#endif + return std::string(timeStamp); + } + }; +} +#endif // CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER + +#define INTERNAL_CATCH_REGISTER_ENUM( enumName, ... ) \ +namespace Catch { \ + template<> struct StringMaker<enumName> { \ + static std::string convert( enumName value ) { \ + static const auto& enumInfo = ::Catch::getMutableRegistryHub().getMutableEnumValuesRegistry().registerEnum( #enumName, #__VA_ARGS__, { __VA_ARGS__ } ); \ + return static_cast<std::string>(enumInfo.lookup( static_cast<int>( value ) )); \ + } \ + }; \ +} + +#define CATCH_REGISTER_ENUM( enumName, ... ) INTERNAL_CATCH_REGISTER_ENUM( enumName, __VA_ARGS__ ) + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +// end catch_tostring.h +#include <iosfwd> + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4389) // '==' : signed/unsigned mismatch +#pragma warning(disable:4018) // more "signed/unsigned mismatch" +#pragma warning(disable:4312) // Converting int to T* using reinterpret_cast (issue on x64 platform) +#pragma warning(disable:4180) // qualifier applied to function type has no meaning +#pragma warning(disable:4800) // Forcing result to true or false +#endif + +namespace Catch { + + struct ITransientExpression { + auto isBinaryExpression() const -> bool { return m_isBinaryExpression; } + auto getResult() const -> bool { return m_result; } + virtual void streamReconstructedExpression( std::ostream &os ) const = 0; + + ITransientExpression( bool isBinaryExpression, bool result ) + : m_isBinaryExpression( isBinaryExpression ), + m_result( result ) + {} + + // We don't actually need a virtual destructor, but many static analysers + // complain if it's not here :-( + virtual ~ITransientExpression(); + + bool m_isBinaryExpression; + bool m_result; + + }; + + void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs ); + + template<typename LhsT, typename RhsT> + class BinaryExpr : public ITransientExpression { + LhsT m_lhs; + StringRef m_op; + RhsT m_rhs; + + void streamReconstructedExpression( std::ostream &os ) const override { + formatReconstructedExpression + ( os, Catch::Detail::stringify( m_lhs ), m_op, Catch::Detail::stringify( m_rhs ) ); + } + + public: + BinaryExpr( bool comparisonResult, LhsT lhs, StringRef op, RhsT rhs ) + : ITransientExpression{ true, comparisonResult }, + m_lhs( lhs ), + m_op( op ), + m_rhs( rhs ) + {} + + template<typename T> + auto operator && ( T ) const -> BinaryExpr<LhsT, RhsT const&> const { + static_assert(always_false<T>::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template<typename T> + auto operator || ( T ) const -> BinaryExpr<LhsT, RhsT const&> const { + static_assert(always_false<T>::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template<typename T> + auto operator == ( T ) const -> BinaryExpr<LhsT, RhsT const&> const { + static_assert(always_false<T>::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template<typename T> + auto operator != ( T ) const -> BinaryExpr<LhsT, RhsT const&> const { + static_assert(always_false<T>::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template<typename T> + auto operator > ( T ) const -> BinaryExpr<LhsT, RhsT const&> const { + static_assert(always_false<T>::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template<typename T> + auto operator < ( T ) const -> BinaryExpr<LhsT, RhsT const&> const { + static_assert(always_false<T>::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template<typename T> + auto operator >= ( T ) const -> BinaryExpr<LhsT, RhsT const&> const { + static_assert(always_false<T>::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template<typename T> + auto operator <= ( T ) const -> BinaryExpr<LhsT, RhsT const&> const { + static_assert(always_false<T>::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + }; + + template<typename LhsT> + class UnaryExpr : public ITransientExpression { + LhsT m_lhs; + + void streamReconstructedExpression( std::ostream &os ) const override { + os << Catch::Detail::stringify( m_lhs ); + } + + public: + explicit UnaryExpr( LhsT lhs ) + : ITransientExpression{ false, static_cast<bool>(lhs) }, + m_lhs( lhs ) + {} + }; + + // Specialised comparison functions to handle equality comparisons between ints and pointers (NULL deduces as an int) + template<typename LhsT, typename RhsT> + auto compareEqual( LhsT const& lhs, RhsT const& rhs ) -> bool { return static_cast<bool>(lhs == rhs); } + template<typename T> + auto compareEqual( T* const& lhs, int rhs ) -> bool { return lhs == reinterpret_cast<void const*>( rhs ); } + template<typename T> + auto compareEqual( T* const& lhs, long rhs ) -> bool { return lhs == reinterpret_cast<void const*>( rhs ); } + template<typename T> + auto compareEqual( int lhs, T* const& rhs ) -> bool { return reinterpret_cast<void const*>( lhs ) == rhs; } + template<typename T> + auto compareEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast<void const*>( lhs ) == rhs; } + + template<typename LhsT, typename RhsT> + auto compareNotEqual( LhsT const& lhs, RhsT&& rhs ) -> bool { return static_cast<bool>(lhs != rhs); } + template<typename T> + auto compareNotEqual( T* const& lhs, int rhs ) -> bool { return lhs != reinterpret_cast<void const*>( rhs ); } + template<typename T> + auto compareNotEqual( T* const& lhs, long rhs ) -> bool { return lhs != reinterpret_cast<void const*>( rhs ); } + template<typename T> + auto compareNotEqual( int lhs, T* const& rhs ) -> bool { return reinterpret_cast<void const*>( lhs ) != rhs; } + template<typename T> + auto compareNotEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast<void const*>( lhs ) != rhs; } + + template<typename LhsT> + class ExprLhs { + LhsT m_lhs; + public: + explicit ExprLhs( LhsT lhs ) : m_lhs( lhs ) {} + + template<typename RhsT> + auto operator == ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const { + return { compareEqual( m_lhs, rhs ), m_lhs, "==", rhs }; + } + auto operator == ( bool rhs ) -> BinaryExpr<LhsT, bool> const { + return { m_lhs == rhs, m_lhs, "==", rhs }; + } + + template<typename RhsT> + auto operator != ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const { + return { compareNotEqual( m_lhs, rhs ), m_lhs, "!=", rhs }; + } + auto operator != ( bool rhs ) -> BinaryExpr<LhsT, bool> const { + return { m_lhs != rhs, m_lhs, "!=", rhs }; + } + + template<typename RhsT> + auto operator > ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const { + return { static_cast<bool>(m_lhs > rhs), m_lhs, ">", rhs }; + } + template<typename RhsT> + auto operator < ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const { + return { static_cast<bool>(m_lhs < rhs), m_lhs, "<", rhs }; + } + template<typename RhsT> + auto operator >= ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const { + return { static_cast<bool>(m_lhs >= rhs), m_lhs, ">=", rhs }; + } + template<typename RhsT> + auto operator <= ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const { + return { static_cast<bool>(m_lhs <= rhs), m_lhs, "<=", rhs }; + } + template <typename RhsT> + auto operator | (RhsT const& rhs) -> BinaryExpr<LhsT, RhsT const&> const { + return { static_cast<bool>(m_lhs | rhs), m_lhs, "|", rhs }; + } + template <typename RhsT> + auto operator & (RhsT const& rhs) -> BinaryExpr<LhsT, RhsT const&> const { + return { static_cast<bool>(m_lhs & rhs), m_lhs, "&", rhs }; + } + template <typename RhsT> + auto operator ^ (RhsT const& rhs) -> BinaryExpr<LhsT, RhsT const&> const { + return { static_cast<bool>(m_lhs ^ rhs), m_lhs, "^", rhs }; + } + + template<typename RhsT> + auto operator && ( RhsT const& ) -> BinaryExpr<LhsT, RhsT const&> const { + static_assert(always_false<RhsT>::value, + "operator&& is not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template<typename RhsT> + auto operator || ( RhsT const& ) -> BinaryExpr<LhsT, RhsT const&> const { + static_assert(always_false<RhsT>::value, + "operator|| is not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + auto makeUnaryExpr() const -> UnaryExpr<LhsT> { + return UnaryExpr<LhsT>{ m_lhs }; + } + }; + + void handleExpression( ITransientExpression const& expr ); + + template<typename T> + void handleExpression( ExprLhs<T> const& expr ) { + handleExpression( expr.makeUnaryExpr() ); + } + + struct Decomposer { + template<typename T> + auto operator <= ( T const& lhs ) -> ExprLhs<T const&> { + return ExprLhs<T const&>{ lhs }; + } + + auto operator <=( bool value ) -> ExprLhs<bool> { + return ExprLhs<bool>{ value }; + } + }; + +} // end namespace Catch + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +// end catch_decomposer.h +// start catch_interfaces_capture.h + +#include <string> +#include <chrono> + +namespace Catch { + + class AssertionResult; + struct AssertionInfo; + struct SectionInfo; + struct SectionEndInfo; + struct MessageInfo; + struct MessageBuilder; + struct Counts; + struct AssertionReaction; + struct SourceLineInfo; + + struct ITransientExpression; + struct IGeneratorTracker; + +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) + struct BenchmarkInfo; + template <typename Duration = std::chrono::duration<double, std::nano>> + struct BenchmarkStats; +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING + + struct IResultCapture { + + virtual ~IResultCapture(); + + virtual bool sectionStarted( SectionInfo const& sectionInfo, + Counts& assertions ) = 0; + virtual void sectionEnded( SectionEndInfo const& endInfo ) = 0; + virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) = 0; + + virtual auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& = 0; + +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) + virtual void benchmarkPreparing( std::string const& name ) = 0; + virtual void benchmarkStarting( BenchmarkInfo const& info ) = 0; + virtual void benchmarkEnded( BenchmarkStats<> const& stats ) = 0; + virtual void benchmarkFailed( std::string const& error ) = 0; +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING + + virtual void pushScopedMessage( MessageInfo const& message ) = 0; + virtual void popScopedMessage( MessageInfo const& message ) = 0; + + virtual void emplaceUnscopedMessage( MessageBuilder const& builder ) = 0; + + virtual void handleFatalErrorCondition( StringRef message ) = 0; + + virtual void handleExpr + ( AssertionInfo const& info, + ITransientExpression const& expr, + AssertionReaction& reaction ) = 0; + virtual void handleMessage + ( AssertionInfo const& info, + ResultWas::OfType resultType, + StringRef const& message, + AssertionReaction& reaction ) = 0; + virtual void handleUnexpectedExceptionNotThrown + ( AssertionInfo const& info, + AssertionReaction& reaction ) = 0; + virtual void handleUnexpectedInflightException + ( AssertionInfo const& info, + std::string const& message, + AssertionReaction& reaction ) = 0; + virtual void handleIncomplete + ( AssertionInfo const& info ) = 0; + virtual void handleNonExpr + ( AssertionInfo const &info, + ResultWas::OfType resultType, + AssertionReaction &reaction ) = 0; + + virtual bool lastAssertionPassed() = 0; + virtual void assertionPassed() = 0; + + // Deprecated, do not use: + virtual std::string getCurrentTestName() const = 0; + virtual const AssertionResult* getLastResult() const = 0; + virtual void exceptionEarlyReported() = 0; + }; + + IResultCapture& getResultCapture(); +} + +// end catch_interfaces_capture.h +namespace Catch { + + struct TestFailureException{}; + struct AssertionResultData; + struct IResultCapture; + class RunContext; + + class LazyExpression { + friend class AssertionHandler; + friend struct AssertionStats; + friend class RunContext; + + ITransientExpression const* m_transientExpression = nullptr; + bool m_isNegated; + public: + LazyExpression( bool isNegated ); + LazyExpression( LazyExpression const& other ); + LazyExpression& operator = ( LazyExpression const& ) = delete; + + explicit operator bool() const; + + friend auto operator << ( std::ostream& os, LazyExpression const& lazyExpr ) -> std::ostream&; + }; + + struct AssertionReaction { + bool shouldDebugBreak = false; + bool shouldThrow = false; + }; + + class AssertionHandler { + AssertionInfo m_assertionInfo; + AssertionReaction m_reaction; + bool m_completed = false; + IResultCapture& m_resultCapture; + + public: + AssertionHandler + ( StringRef const& macroName, + SourceLineInfo const& lineInfo, + StringRef capturedExpression, + ResultDisposition::Flags resultDisposition ); + ~AssertionHandler() { + if ( !m_completed ) { + m_resultCapture.handleIncomplete( m_assertionInfo ); + } + } + + template<typename T> + void handleExpr( ExprLhs<T> const& expr ) { + handleExpr( expr.makeUnaryExpr() ); + } + void handleExpr( ITransientExpression const& expr ); + + void handleMessage(ResultWas::OfType resultType, StringRef const& message); + + void handleExceptionThrownAsExpected(); + void handleUnexpectedExceptionNotThrown(); + void handleExceptionNotThrownAsExpected(); + void handleThrowingCallSkipped(); + void handleUnexpectedInflightException(); + + void complete(); + void setCompleted(); + + // query + auto allowThrows() const -> bool; + }; + + void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str, StringRef const& matcherString ); + +} // namespace Catch + +// end catch_assertionhandler.h +// start catch_message.h + +#include <string> +#include <vector> + +namespace Catch { + + struct MessageInfo { + MessageInfo( StringRef const& _macroName, + SourceLineInfo const& _lineInfo, + ResultWas::OfType _type ); + + StringRef macroName; + std::string message; + SourceLineInfo lineInfo; + ResultWas::OfType type; + unsigned int sequence; + + bool operator == ( MessageInfo const& other ) const; + bool operator < ( MessageInfo const& other ) const; + private: + static unsigned int globalCount; + }; + + struct MessageStream { + + template<typename T> + MessageStream& operator << ( T const& value ) { + m_stream << value; + return *this; + } + + ReusableStringStream m_stream; + }; + + struct MessageBuilder : MessageStream { + MessageBuilder( StringRef const& macroName, + SourceLineInfo const& lineInfo, + ResultWas::OfType type ); + + template<typename T> + MessageBuilder& operator << ( T const& value ) { + m_stream << value; + return *this; + } + + MessageInfo m_info; + }; + + class ScopedMessage { + public: + explicit ScopedMessage( MessageBuilder const& builder ); + ScopedMessage( ScopedMessage& duplicate ) = delete; + ScopedMessage( ScopedMessage&& old ); + ~ScopedMessage(); + + MessageInfo m_info; + bool m_moved; + }; + + class Capturer { + std::vector<MessageInfo> m_messages; + IResultCapture& m_resultCapture = getResultCapture(); + size_t m_captured = 0; + public: + Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names ); + ~Capturer(); + + void captureValue( size_t index, std::string const& value ); + + template<typename T> + void captureValues( size_t index, T const& value ) { + captureValue( index, Catch::Detail::stringify( value ) ); + } + + template<typename T, typename... Ts> + void captureValues( size_t index, T const& value, Ts const&... values ) { + captureValue( index, Catch::Detail::stringify(value) ); + captureValues( index+1, values... ); + } + }; + +} // end namespace Catch + +// end catch_message.h +#if !defined(CATCH_CONFIG_DISABLE) + +#if !defined(CATCH_CONFIG_DISABLE_STRINGIFICATION) + #define CATCH_INTERNAL_STRINGIFY(...) #__VA_ARGS__ +#else + #define CATCH_INTERNAL_STRINGIFY(...) "Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION" +#endif + +#if defined(CATCH_CONFIG_FAST_COMPILE) || defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + +/////////////////////////////////////////////////////////////////////////////// +// Another way to speed-up compilation is to omit local try-catch for REQUIRE* +// macros. +#define INTERNAL_CATCH_TRY +#define INTERNAL_CATCH_CATCH( capturer ) + +#else // CATCH_CONFIG_FAST_COMPILE + +#define INTERNAL_CATCH_TRY try +#define INTERNAL_CATCH_CATCH( handler ) catch(...) { handler.handleUnexpectedInflightException(); } + +#endif + +#define INTERNAL_CATCH_REACT( handler ) handler.complete(); + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_TEST( macroName, resultDisposition, ... ) \ + do { \ + CATCH_INTERNAL_IGNORE_BUT_WARN(__VA_ARGS__); \ + Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \ + INTERNAL_CATCH_TRY { \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ + catchAssertionHandler.handleExpr( Catch::Decomposer() <= __VA_ARGS__ ); \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ + } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( (void)0, (false) && static_cast<bool>( !!(__VA_ARGS__) ) ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_IF( macroName, resultDisposition, ... ) \ + INTERNAL_CATCH_TEST( macroName, resultDisposition, __VA_ARGS__ ); \ + if( Catch::getResultCapture().lastAssertionPassed() ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_ELSE( macroName, resultDisposition, ... ) \ + INTERNAL_CATCH_TEST( macroName, resultDisposition, __VA_ARGS__ ); \ + if( !Catch::getResultCapture().lastAssertionPassed() ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_NO_THROW( macroName, resultDisposition, ... ) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \ + try { \ + static_cast<void>(__VA_ARGS__); \ + catchAssertionHandler.handleExceptionNotThrownAsExpected(); \ + } \ + catch( ... ) { \ + catchAssertionHandler.handleUnexpectedInflightException(); \ + } \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( false ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_THROWS( macroName, resultDisposition, ... ) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition); \ + if( catchAssertionHandler.allowThrows() ) \ + try { \ + static_cast<void>(__VA_ARGS__); \ + catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ + } \ + catch( ... ) { \ + catchAssertionHandler.handleExceptionThrownAsExpected(); \ + } \ + else \ + catchAssertionHandler.handleThrowingCallSkipped(); \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( false ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_THROWS_AS( macroName, exceptionType, resultDisposition, expr ) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr) ", " CATCH_INTERNAL_STRINGIFY(exceptionType), resultDisposition ); \ + if( catchAssertionHandler.allowThrows() ) \ + try { \ + static_cast<void>(expr); \ + catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ + } \ + catch( exceptionType const& ) { \ + catchAssertionHandler.handleExceptionThrownAsExpected(); \ + } \ + catch( ... ) { \ + catchAssertionHandler.handleUnexpectedInflightException(); \ + } \ + else \ + catchAssertionHandler.handleThrowingCallSkipped(); \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( false ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_MSG( macroName, messageType, resultDisposition, ... ) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::StringRef(), resultDisposition ); \ + catchAssertionHandler.handleMessage( messageType, ( Catch::MessageStream() << __VA_ARGS__ + ::Catch::StreamEndStop() ).m_stream.str() ); \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( false ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_CAPTURE( varName, macroName, ... ) \ + auto varName = Catch::Capturer( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info, #__VA_ARGS__ ); \ + varName.captureValues( 0, __VA_ARGS__ ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_INFO( macroName, log ) \ + Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage )( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log ); + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_UNSCOPED_INFO( macroName, log ) \ + Catch::getResultCapture().emplaceUnscopedMessage( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log ) + +/////////////////////////////////////////////////////////////////////////////// +// Although this is matcher-based, it can be used with just a string +#define INTERNAL_CATCH_THROWS_STR_MATCHES( macroName, resultDisposition, matcher, ... ) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ + if( catchAssertionHandler.allowThrows() ) \ + try { \ + static_cast<void>(__VA_ARGS__); \ + catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ + } \ + catch( ... ) { \ + Catch::handleExceptionMatchExpr( catchAssertionHandler, matcher, #matcher##_catch_sr ); \ + } \ + else \ + catchAssertionHandler.handleThrowingCallSkipped(); \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( false ) + +#endif // CATCH_CONFIG_DISABLE + +// end catch_capture.hpp +// start catch_section.h + +// start catch_section_info.h + +// start catch_totals.h + +#include <cstddef> + +namespace Catch { + + struct Counts { + Counts operator - ( Counts const& other ) const; + Counts& operator += ( Counts const& other ); + + std::size_t total() const; + bool allPassed() const; + bool allOk() const; + + std::size_t passed = 0; + std::size_t failed = 0; + std::size_t failedButOk = 0; + }; + + struct Totals { + + Totals operator - ( Totals const& other ) const; + Totals& operator += ( Totals const& other ); + + Totals delta( Totals const& prevTotals ) const; + + int error = 0; + Counts assertions; + Counts testCases; + }; +} + +// end catch_totals.h +#include <string> + +namespace Catch { + + struct SectionInfo { + SectionInfo + ( SourceLineInfo const& _lineInfo, + std::string const& _name ); + + // Deprecated + SectionInfo + ( SourceLineInfo const& _lineInfo, + std::string const& _name, + std::string const& ) : SectionInfo( _lineInfo, _name ) {} + + std::string name; + std::string description; // !Deprecated: this will always be empty + SourceLineInfo lineInfo; + }; + + struct SectionEndInfo { + SectionInfo sectionInfo; + Counts prevAssertions; + double durationInSeconds; + }; + +} // end namespace Catch + +// end catch_section_info.h +// start catch_timer.h + +#include <cstdint> + +namespace Catch { + + auto getCurrentNanosecondsSinceEpoch() -> uint64_t; + auto getEstimatedClockResolution() -> uint64_t; + + class Timer { + uint64_t m_nanoseconds = 0; + public: + void start(); + auto getElapsedNanoseconds() const -> uint64_t; + auto getElapsedMicroseconds() const -> uint64_t; + auto getElapsedMilliseconds() const -> unsigned int; + auto getElapsedSeconds() const -> double; + }; + +} // namespace Catch + +// end catch_timer.h +#include <string> + +namespace Catch { + + class Section : NonCopyable { + public: + Section( SectionInfo const& info ); + ~Section(); + + // This indicates whether the section should be executed or not + explicit operator bool() const; + + private: + SectionInfo m_info; + + std::string m_name; + Counts m_assertions; + bool m_sectionIncluded; + Timer m_timer; + }; + +} // end namespace Catch + +#define INTERNAL_CATCH_SECTION( ... ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ + if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + +#define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ + if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, (Catch::ReusableStringStream() << __VA_ARGS__).str() ) ) \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + +// end catch_section.h +// start catch_interfaces_exception.h + +// start catch_interfaces_registry_hub.h + +#include <string> +#include <memory> + +namespace Catch { + + class TestCase; + struct ITestCaseRegistry; + struct IExceptionTranslatorRegistry; + struct IExceptionTranslator; + struct IReporterRegistry; + struct IReporterFactory; + struct ITagAliasRegistry; + struct IMutableEnumValuesRegistry; + + class StartupExceptionRegistry; + + using IReporterFactoryPtr = std::shared_ptr<IReporterFactory>; + + struct IRegistryHub { + virtual ~IRegistryHub(); + + virtual IReporterRegistry const& getReporterRegistry() const = 0; + virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0; + virtual ITagAliasRegistry const& getTagAliasRegistry() const = 0; + virtual IExceptionTranslatorRegistry const& getExceptionTranslatorRegistry() const = 0; + + virtual StartupExceptionRegistry const& getStartupExceptionRegistry() const = 0; + }; + + struct IMutableRegistryHub { + virtual ~IMutableRegistryHub(); + virtual void registerReporter( std::string const& name, IReporterFactoryPtr const& factory ) = 0; + virtual void registerListener( IReporterFactoryPtr const& factory ) = 0; + virtual void registerTest( TestCase const& testInfo ) = 0; + virtual void registerTranslator( const IExceptionTranslator* translator ) = 0; + virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) = 0; + virtual void registerStartupException() noexcept = 0; + virtual IMutableEnumValuesRegistry& getMutableEnumValuesRegistry() = 0; + }; + + IRegistryHub const& getRegistryHub(); + IMutableRegistryHub& getMutableRegistryHub(); + void cleanUp(); + std::string translateActiveException(); + +} + +// end catch_interfaces_registry_hub.h +#if defined(CATCH_CONFIG_DISABLE) + #define INTERNAL_CATCH_TRANSLATE_EXCEPTION_NO_REG( translatorName, signature) \ + static std::string translatorName( signature ) +#endif + +#include <exception> +#include <string> +#include <vector> + +namespace Catch { + using exceptionTranslateFunction = std::string(*)(); + + struct IExceptionTranslator; + using ExceptionTranslators = std::vector<std::unique_ptr<IExceptionTranslator const>>; + + struct IExceptionTranslator { + virtual ~IExceptionTranslator(); + virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const = 0; + }; + + struct IExceptionTranslatorRegistry { + virtual ~IExceptionTranslatorRegistry(); + + virtual std::string translateActiveException() const = 0; + }; + + class ExceptionTranslatorRegistrar { + template<typename T> + class ExceptionTranslator : public IExceptionTranslator { + public: + + ExceptionTranslator( std::string(*translateFunction)( T& ) ) + : m_translateFunction( translateFunction ) + {} + + std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const override { +#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + return ""; +#else + try { + if( it == itEnd ) + std::rethrow_exception(std::current_exception()); + else + return (*it)->translate( it+1, itEnd ); + } + catch( T& ex ) { + return m_translateFunction( ex ); + } +#endif + } + + protected: + std::string(*m_translateFunction)( T& ); + }; + + public: + template<typename T> + ExceptionTranslatorRegistrar( std::string(*translateFunction)( T& ) ) { + getMutableRegistryHub().registerTranslator + ( new ExceptionTranslator<T>( translateFunction ) ); + } + }; +} + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_TRANSLATE_EXCEPTION2( translatorName, signature ) \ + static std::string translatorName( signature ); \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &translatorName ); } \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ + static std::string translatorName( signature ) + +#define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION2( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature ) + +// end catch_interfaces_exception.h +// start catch_approx.h + +#include <type_traits> + +namespace Catch { +namespace Detail { + + class Approx { + private: + bool equalityComparisonImpl(double other) const; + // Validates the new margin (margin >= 0) + // out-of-line to avoid including stdexcept in the header + void setMargin(double margin); + // Validates the new epsilon (0 < epsilon < 1) + // out-of-line to avoid including stdexcept in the header + void setEpsilon(double epsilon); + + public: + explicit Approx ( double value ); + + static Approx custom(); + + Approx operator-() const; + + template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> + Approx operator()( T const& value ) const { + Approx approx( static_cast<double>(value) ); + approx.m_epsilon = m_epsilon; + approx.m_margin = m_margin; + approx.m_scale = m_scale; + return approx; + } + + template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> + explicit Approx( T const& value ): Approx(static_cast<double>(value)) + {} + + template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> + friend bool operator == ( const T& lhs, Approx const& rhs ) { + auto lhs_v = static_cast<double>(lhs); + return rhs.equalityComparisonImpl(lhs_v); + } + + template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> + friend bool operator == ( Approx const& lhs, const T& rhs ) { + return operator==( rhs, lhs ); + } + + template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> + friend bool operator != ( T const& lhs, Approx const& rhs ) { + return !operator==( lhs, rhs ); + } + + template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> + friend bool operator != ( Approx const& lhs, T const& rhs ) { + return !operator==( rhs, lhs ); + } + + template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> + friend bool operator <= ( T const& lhs, Approx const& rhs ) { + return static_cast<double>(lhs) < rhs.m_value || lhs == rhs; + } + + template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> + friend bool operator <= ( Approx const& lhs, T const& rhs ) { + return lhs.m_value < static_cast<double>(rhs) || lhs == rhs; + } + + template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> + friend bool operator >= ( T const& lhs, Approx const& rhs ) { + return static_cast<double>(lhs) > rhs.m_value || lhs == rhs; + } + + template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> + friend bool operator >= ( Approx const& lhs, T const& rhs ) { + return lhs.m_value > static_cast<double>(rhs) || lhs == rhs; + } + + template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> + Approx& epsilon( T const& newEpsilon ) { + double epsilonAsDouble = static_cast<double>(newEpsilon); + setEpsilon(epsilonAsDouble); + return *this; + } + + template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> + Approx& margin( T const& newMargin ) { + double marginAsDouble = static_cast<double>(newMargin); + setMargin(marginAsDouble); + return *this; + } + + template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> + Approx& scale( T const& newScale ) { + m_scale = static_cast<double>(newScale); + return *this; + } + + std::string toString() const; + + private: + double m_epsilon; + double m_margin; + double m_scale; + double m_value; + }; +} // end namespace Detail + +namespace literals { + Detail::Approx operator "" _a(long double val); + Detail::Approx operator "" _a(unsigned long long val); +} // end namespace literals + +template<> +struct StringMaker<Catch::Detail::Approx> { + static std::string convert(Catch::Detail::Approx const& value); +}; + +} // end namespace Catch + +// end catch_approx.h +// start catch_string_manip.h + +#include <string> +#include <iosfwd> +#include <vector> + +namespace Catch { + + bool startsWith( std::string const& s, std::string const& prefix ); + bool startsWith( std::string const& s, char prefix ); + bool endsWith( std::string const& s, std::string const& suffix ); + bool endsWith( std::string const& s, char suffix ); + bool contains( std::string const& s, std::string const& infix ); + void toLowerInPlace( std::string& s ); + std::string toLower( std::string const& s ); + //! Returns a new string without whitespace at the start/end + std::string trim( std::string const& str ); + //! Returns a substring of the original ref without whitespace. Beware lifetimes! + StringRef trim(StringRef ref); + + // !!! Be aware, returns refs into original string - make sure original string outlives them + std::vector<StringRef> splitStringRef( StringRef str, char delimiter ); + bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ); + + struct pluralise { + pluralise( std::size_t count, std::string const& label ); + + friend std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ); + + std::size_t m_count; + std::string m_label; + }; +} + +// end catch_string_manip.h +#ifndef CATCH_CONFIG_DISABLE_MATCHERS +// start catch_capture_matchers.h + +// start catch_matchers.h + +#include <string> +#include <vector> + +namespace Catch { +namespace Matchers { + namespace Impl { + + template<typename ArgT> struct MatchAllOf; + template<typename ArgT> struct MatchAnyOf; + template<typename ArgT> struct MatchNotOf; + + class MatcherUntypedBase { + public: + MatcherUntypedBase() = default; + MatcherUntypedBase ( MatcherUntypedBase const& ) = default; + MatcherUntypedBase& operator = ( MatcherUntypedBase const& ) = delete; + std::string toString() const; + + protected: + virtual ~MatcherUntypedBase(); + virtual std::string describe() const = 0; + mutable std::string m_cachedToString; + }; + +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wnon-virtual-dtor" +#endif + + template<typename ObjectT> + struct MatcherMethod { + virtual bool match( ObjectT const& arg ) const = 0; + }; + +#if defined(__OBJC__) + // Hack to fix Catch GH issue #1661. Could use id for generic Object support. + // use of const for Object pointers is very uncommon and under ARC it causes some kind of signature mismatch that breaks compilation + template<> + struct MatcherMethod<NSString*> { + virtual bool match( NSString* arg ) const = 0; + }; +#endif + +#ifdef __clang__ +# pragma clang diagnostic pop +#endif + + template<typename T> + struct MatcherBase : MatcherUntypedBase, MatcherMethod<T> { + + MatchAllOf<T> operator && ( MatcherBase const& other ) const; + MatchAnyOf<T> operator || ( MatcherBase const& other ) const; + MatchNotOf<T> operator ! () const; + }; + + template<typename ArgT> + struct MatchAllOf : MatcherBase<ArgT> { + bool match( ArgT const& arg ) const override { + for( auto matcher : m_matchers ) { + if (!matcher->match(arg)) + return false; + } + return true; + } + std::string describe() const override { + std::string description; + description.reserve( 4 + m_matchers.size()*32 ); + description += "( "; + bool first = true; + for( auto matcher : m_matchers ) { + if( first ) + first = false; + else + description += " and "; + description += matcher->toString(); + } + description += " )"; + return description; + } + + MatchAllOf<ArgT> operator && ( MatcherBase<ArgT> const& other ) { + auto copy(*this); + copy.m_matchers.push_back( &other ); + return copy; + } + + std::vector<MatcherBase<ArgT> const*> m_matchers; + }; + template<typename ArgT> + struct MatchAnyOf : MatcherBase<ArgT> { + + bool match( ArgT const& arg ) const override { + for( auto matcher : m_matchers ) { + if (matcher->match(arg)) + return true; + } + return false; + } + std::string describe() const override { + std::string description; + description.reserve( 4 + m_matchers.size()*32 ); + description += "( "; + bool first = true; + for( auto matcher : m_matchers ) { + if( first ) + first = false; + else + description += " or "; + description += matcher->toString(); + } + description += " )"; + return description; + } + + MatchAnyOf<ArgT> operator || ( MatcherBase<ArgT> const& other ) { + auto copy(*this); + copy.m_matchers.push_back( &other ); + return copy; + } + + std::vector<MatcherBase<ArgT> const*> m_matchers; + }; + + template<typename ArgT> + struct MatchNotOf : MatcherBase<ArgT> { + + MatchNotOf( MatcherBase<ArgT> const& underlyingMatcher ) : m_underlyingMatcher( underlyingMatcher ) {} + + bool match( ArgT const& arg ) const override { + return !m_underlyingMatcher.match( arg ); + } + + std::string describe() const override { + return "not " + m_underlyingMatcher.toString(); + } + MatcherBase<ArgT> const& m_underlyingMatcher; + }; + + template<typename T> + MatchAllOf<T> MatcherBase<T>::operator && ( MatcherBase const& other ) const { + return MatchAllOf<T>() && *this && other; + } + template<typename T> + MatchAnyOf<T> MatcherBase<T>::operator || ( MatcherBase const& other ) const { + return MatchAnyOf<T>() || *this || other; + } + template<typename T> + MatchNotOf<T> MatcherBase<T>::operator ! () const { + return MatchNotOf<T>( *this ); + } + + } // namespace Impl + +} // namespace Matchers + +using namespace Matchers; +using Matchers::Impl::MatcherBase; + +} // namespace Catch + +// end catch_matchers.h +// start catch_matchers_exception.hpp + +namespace Catch { +namespace Matchers { +namespace Exception { + +class ExceptionMessageMatcher : public MatcherBase<std::exception> { + std::string m_message; +public: + + ExceptionMessageMatcher(std::string const& message): + m_message(message) + {} + + bool match(std::exception const& ex) const override; + + std::string describe() const override; +}; + +} // namespace Exception + +Exception::ExceptionMessageMatcher Message(std::string const& message); + +} // namespace Matchers +} // namespace Catch + +// end catch_matchers_exception.hpp +// start catch_matchers_floating.h + +namespace Catch { +namespace Matchers { + + namespace Floating { + + enum class FloatingPointKind : uint8_t; + + struct WithinAbsMatcher : MatcherBase<double> { + WithinAbsMatcher(double target, double margin); + bool match(double const& matchee) const override; + std::string describe() const override; + private: + double m_target; + double m_margin; + }; + + struct WithinUlpsMatcher : MatcherBase<double> { + WithinUlpsMatcher(double target, uint64_t ulps, FloatingPointKind baseType); + bool match(double const& matchee) const override; + std::string describe() const override; + private: + double m_target; + uint64_t m_ulps; + FloatingPointKind m_type; + }; + + // Given IEEE-754 format for floats and doubles, we can assume + // that float -> double promotion is lossless. Given this, we can + // assume that if we do the standard relative comparison of + // |lhs - rhs| <= epsilon * max(fabs(lhs), fabs(rhs)), then we get + // the same result if we do this for floats, as if we do this for + // doubles that were promoted from floats. + struct WithinRelMatcher : MatcherBase<double> { + WithinRelMatcher(double target, double epsilon); + bool match(double const& matchee) const override; + std::string describe() const override; + private: + double m_target; + double m_epsilon; + }; + + } // namespace Floating + + // The following functions create the actual matcher objects. + // This allows the types to be inferred + Floating::WithinUlpsMatcher WithinULP(double target, uint64_t maxUlpDiff); + Floating::WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff); + Floating::WithinAbsMatcher WithinAbs(double target, double margin); + Floating::WithinRelMatcher WithinRel(double target, double eps); + // defaults epsilon to 100*numeric_limits<double>::epsilon() + Floating::WithinRelMatcher WithinRel(double target); + Floating::WithinRelMatcher WithinRel(float target, float eps); + // defaults epsilon to 100*numeric_limits<float>::epsilon() + Floating::WithinRelMatcher WithinRel(float target); + +} // namespace Matchers +} // namespace Catch + +// end catch_matchers_floating.h +// start catch_matchers_generic.hpp + +#include <functional> +#include <string> + +namespace Catch { +namespace Matchers { +namespace Generic { + +namespace Detail { + std::string finalizeDescription(const std::string& desc); +} + +template <typename T> +class PredicateMatcher : public MatcherBase<T> { + std::function<bool(T const&)> m_predicate; + std::string m_description; +public: + + PredicateMatcher(std::function<bool(T const&)> const& elem, std::string const& descr) + :m_predicate(std::move(elem)), + m_description(Detail::finalizeDescription(descr)) + {} + + bool match( T const& item ) const override { + return m_predicate(item); + } + + std::string describe() const override { + return m_description; + } +}; + +} // namespace Generic + + // The following functions create the actual matcher objects. + // The user has to explicitly specify type to the function, because + // inferring std::function<bool(T const&)> is hard (but possible) and + // requires a lot of TMP. + template<typename T> + Generic::PredicateMatcher<T> Predicate(std::function<bool(T const&)> const& predicate, std::string const& description = "") { + return Generic::PredicateMatcher<T>(predicate, description); + } + +} // namespace Matchers +} // namespace Catch + +// end catch_matchers_generic.hpp +// start catch_matchers_string.h + +#include <string> + +namespace Catch { +namespace Matchers { + + namespace StdString { + + struct CasedString + { + CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity ); + std::string adjustString( std::string const& str ) const; + std::string caseSensitivitySuffix() const; + + CaseSensitive::Choice m_caseSensitivity; + std::string m_str; + }; + + struct StringMatcherBase : MatcherBase<std::string> { + StringMatcherBase( std::string const& operation, CasedString const& comparator ); + std::string describe() const override; + + CasedString m_comparator; + std::string m_operation; + }; + + struct EqualsMatcher : StringMatcherBase { + EqualsMatcher( CasedString const& comparator ); + bool match( std::string const& source ) const override; + }; + struct ContainsMatcher : StringMatcherBase { + ContainsMatcher( CasedString const& comparator ); + bool match( std::string const& source ) const override; + }; + struct StartsWithMatcher : StringMatcherBase { + StartsWithMatcher( CasedString const& comparator ); + bool match( std::string const& source ) const override; + }; + struct EndsWithMatcher : StringMatcherBase { + EndsWithMatcher( CasedString const& comparator ); + bool match( std::string const& source ) const override; + }; + + struct RegexMatcher : MatcherBase<std::string> { + RegexMatcher( std::string regex, CaseSensitive::Choice caseSensitivity ); + bool match( std::string const& matchee ) const override; + std::string describe() const override; + + private: + std::string m_regex; + CaseSensitive::Choice m_caseSensitivity; + }; + + } // namespace StdString + + // The following functions create the actual matcher objects. + // This allows the types to be inferred + + StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + StdString::RegexMatcher Matches( std::string const& regex, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + +} // namespace Matchers +} // namespace Catch + +// end catch_matchers_string.h +// start catch_matchers_vector.h + +#include <algorithm> + +namespace Catch { +namespace Matchers { + + namespace Vector { + template<typename T, typename Alloc> + struct ContainsElementMatcher : MatcherBase<std::vector<T, Alloc>> { + + ContainsElementMatcher(T const &comparator) : m_comparator( comparator) {} + + bool match(std::vector<T, Alloc> const &v) const override { + for (auto const& el : v) { + if (el == m_comparator) { + return true; + } + } + return false; + } + + std::string describe() const override { + return "Contains: " + ::Catch::Detail::stringify( m_comparator ); + } + + T const& m_comparator; + }; + + template<typename T, typename AllocComp, typename AllocMatch> + struct ContainsMatcher : MatcherBase<std::vector<T, AllocMatch>> { + + ContainsMatcher(std::vector<T, AllocComp> const &comparator) : m_comparator( comparator ) {} + + bool match(std::vector<T, AllocMatch> const &v) const override { + // !TBD: see note in EqualsMatcher + if (m_comparator.size() > v.size()) + return false; + for (auto const& comparator : m_comparator) { + auto present = false; + for (const auto& el : v) { + if (el == comparator) { + present = true; + break; + } + } + if (!present) { + return false; + } + } + return true; + } + std::string describe() const override { + return "Contains: " + ::Catch::Detail::stringify( m_comparator ); + } + + std::vector<T, AllocComp> const& m_comparator; + }; + + template<typename T, typename AllocComp, typename AllocMatch> + struct EqualsMatcher : MatcherBase<std::vector<T, AllocMatch>> { + + EqualsMatcher(std::vector<T, AllocComp> const &comparator) : m_comparator( comparator ) {} + + bool match(std::vector<T, AllocMatch> const &v) const override { + // !TBD: This currently works if all elements can be compared using != + // - a more general approach would be via a compare template that defaults + // to using !=. but could be specialised for, e.g. std::vector<T, Alloc> etc + // - then just call that directly + if (m_comparator.size() != v.size()) + return false; + for (std::size_t i = 0; i < v.size(); ++i) + if (m_comparator[i] != v[i]) + return false; + return true; + } + std::string describe() const override { + return "Equals: " + ::Catch::Detail::stringify( m_comparator ); + } + std::vector<T, AllocComp> const& m_comparator; + }; + + template<typename T, typename AllocComp, typename AllocMatch> + struct ApproxMatcher : MatcherBase<std::vector<T, AllocMatch>> { + + ApproxMatcher(std::vector<T, AllocComp> const& comparator) : m_comparator( comparator ) {} + + bool match(std::vector<T, AllocMatch> const &v) const override { + if (m_comparator.size() != v.size()) + return false; + for (std::size_t i = 0; i < v.size(); ++i) + if (m_comparator[i] != approx(v[i])) + return false; + return true; + } + std::string describe() const override { + return "is approx: " + ::Catch::Detail::stringify( m_comparator ); + } + template <typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> + ApproxMatcher& epsilon( T const& newEpsilon ) { + approx.epsilon(newEpsilon); + return *this; + } + template <typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> + ApproxMatcher& margin( T const& newMargin ) { + approx.margin(newMargin); + return *this; + } + template <typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> + ApproxMatcher& scale( T const& newScale ) { + approx.scale(newScale); + return *this; + } + + std::vector<T, AllocComp> const& m_comparator; + mutable Catch::Detail::Approx approx = Catch::Detail::Approx::custom(); + }; + + template<typename T, typename AllocComp, typename AllocMatch> + struct UnorderedEqualsMatcher : MatcherBase<std::vector<T, AllocMatch>> { + UnorderedEqualsMatcher(std::vector<T, AllocComp> const& target) : m_target(target) {} + bool match(std::vector<T, AllocMatch> const& vec) const override { + if (m_target.size() != vec.size()) { + return false; + } + return std::is_permutation(m_target.begin(), m_target.end(), vec.begin()); + } + + std::string describe() const override { + return "UnorderedEquals: " + ::Catch::Detail::stringify(m_target); + } + private: + std::vector<T, AllocComp> const& m_target; + }; + + } // namespace Vector + + // The following functions create the actual matcher objects. + // This allows the types to be inferred + + template<typename T, typename AllocComp = std::allocator<T>, typename AllocMatch = AllocComp> + Vector::ContainsMatcher<T, AllocComp, AllocMatch> Contains( std::vector<T, AllocComp> const& comparator ) { + return Vector::ContainsMatcher<T, AllocComp, AllocMatch>( comparator ); + } + + template<typename T, typename Alloc = std::allocator<T>> + Vector::ContainsElementMatcher<T, Alloc> VectorContains( T const& comparator ) { + return Vector::ContainsElementMatcher<T, Alloc>( comparator ); + } + + template<typename T, typename AllocComp = std::allocator<T>, typename AllocMatch = AllocComp> + Vector::EqualsMatcher<T, AllocComp, AllocMatch> Equals( std::vector<T, AllocComp> const& comparator ) { + return Vector::EqualsMatcher<T, AllocComp, AllocMatch>( comparator ); + } + + template<typename T, typename AllocComp = std::allocator<T>, typename AllocMatch = AllocComp> + Vector::ApproxMatcher<T, AllocComp, AllocMatch> Approx( std::vector<T, AllocComp> const& comparator ) { + return Vector::ApproxMatcher<T, AllocComp, AllocMatch>( comparator ); + } + + template<typename T, typename AllocComp = std::allocator<T>, typename AllocMatch = AllocComp> + Vector::UnorderedEqualsMatcher<T, AllocComp, AllocMatch> UnorderedEquals(std::vector<T, AllocComp> const& target) { + return Vector::UnorderedEqualsMatcher<T, AllocComp, AllocMatch>( target ); + } + +} // namespace Matchers +} // namespace Catch + +// end catch_matchers_vector.h +namespace Catch { + + template<typename ArgT, typename MatcherT> + class MatchExpr : public ITransientExpression { + ArgT const& m_arg; + MatcherT m_matcher; + StringRef m_matcherString; + public: + MatchExpr( ArgT const& arg, MatcherT const& matcher, StringRef const& matcherString ) + : ITransientExpression{ true, matcher.match( arg ) }, + m_arg( arg ), + m_matcher( matcher ), + m_matcherString( matcherString ) + {} + + void streamReconstructedExpression( std::ostream &os ) const override { + auto matcherAsString = m_matcher.toString(); + os << Catch::Detail::stringify( m_arg ) << ' '; + if( matcherAsString == Detail::unprintableString ) + os << m_matcherString; + else + os << matcherAsString; + } + }; + + using StringMatcher = Matchers::Impl::MatcherBase<std::string>; + + void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef const& matcherString ); + + template<typename ArgT, typename MatcherT> + auto makeMatchExpr( ArgT const& arg, MatcherT const& matcher, StringRef const& matcherString ) -> MatchExpr<ArgT, MatcherT> { + return MatchExpr<ArgT, MatcherT>( arg, matcher, matcherString ); + } + +} // namespace Catch + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CHECK_THAT( macroName, matcher, resultDisposition, arg ) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(arg) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ + INTERNAL_CATCH_TRY { \ + catchAssertionHandler.handleExpr( Catch::makeMatchExpr( arg, matcher, #matcher##_catch_sr ) ); \ + } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( false ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_THROWS_MATCHES( macroName, exceptionType, resultDisposition, matcher, ... ) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__) ", " CATCH_INTERNAL_STRINGIFY(exceptionType) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ + if( catchAssertionHandler.allowThrows() ) \ + try { \ + static_cast<void>(__VA_ARGS__ ); \ + catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ + } \ + catch( exceptionType const& ex ) { \ + catchAssertionHandler.handleExpr( Catch::makeMatchExpr( ex, matcher, #matcher##_catch_sr ) ); \ + } \ + catch( ... ) { \ + catchAssertionHandler.handleUnexpectedInflightException(); \ + } \ + else \ + catchAssertionHandler.handleThrowingCallSkipped(); \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( false ) + +// end catch_capture_matchers.h +#endif +// start catch_generators.hpp + +// start catch_interfaces_generatortracker.h + + +#include <memory> + +namespace Catch { + + namespace Generators { + class GeneratorUntypedBase { + public: + GeneratorUntypedBase() = default; + virtual ~GeneratorUntypedBase(); + // Attempts to move the generator to the next element + // + // Returns true iff the move succeeded (and a valid element + // can be retrieved). + virtual bool next() = 0; + }; + using GeneratorBasePtr = std::unique_ptr<GeneratorUntypedBase>; + + } // namespace Generators + + struct IGeneratorTracker { + virtual ~IGeneratorTracker(); + virtual auto hasGenerator() const -> bool = 0; + virtual auto getGenerator() const -> Generators::GeneratorBasePtr const& = 0; + virtual void setGenerator( Generators::GeneratorBasePtr&& generator ) = 0; + }; + +} // namespace Catch + +// end catch_interfaces_generatortracker.h +// start catch_enforce.h + +#include <exception> + +namespace Catch { +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + template <typename Ex> + [[noreturn]] + void throw_exception(Ex const& e) { + throw e; + } +#else // ^^ Exceptions are enabled // Exceptions are disabled vv + [[noreturn]] + void throw_exception(std::exception const& e); +#endif + + [[noreturn]] + void throw_logic_error(std::string const& msg); + [[noreturn]] + void throw_domain_error(std::string const& msg); + [[noreturn]] + void throw_runtime_error(std::string const& msg); + +} // namespace Catch; + +#define CATCH_MAKE_MSG(...) \ + (Catch::ReusableStringStream() << __VA_ARGS__).str() + +#define CATCH_INTERNAL_ERROR(...) \ + Catch::throw_logic_error(CATCH_MAKE_MSG( CATCH_INTERNAL_LINEINFO << ": Internal Catch2 error: " << __VA_ARGS__)) + +#define CATCH_ERROR(...) \ + Catch::throw_domain_error(CATCH_MAKE_MSG( __VA_ARGS__ )) + +#define CATCH_RUNTIME_ERROR(...) \ + Catch::throw_runtime_error(CATCH_MAKE_MSG( __VA_ARGS__ )) + +#define CATCH_ENFORCE( condition, ... ) \ + do{ if( !(condition) ) CATCH_ERROR( __VA_ARGS__ ); } while(false) + +// end catch_enforce.h +#include <memory> +#include <vector> +#include <cassert> + +#include <utility> +#include <exception> + +namespace Catch { + +class GeneratorException : public std::exception { + const char* const m_msg = ""; + +public: + GeneratorException(const char* msg): + m_msg(msg) + {} + + const char* what() const noexcept override final; +}; + +namespace Generators { + + // !TBD move this into its own location? + namespace pf{ + template<typename T, typename... Args> + std::unique_ptr<T> make_unique( Args&&... args ) { + return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); + } + } + + template<typename T> + struct IGenerator : GeneratorUntypedBase { + virtual ~IGenerator() = default; + + // Returns the current element of the generator + // + // \Precondition The generator is either freshly constructed, + // or the last call to `next()` returned true + virtual T const& get() const = 0; + using type = T; + }; + + template<typename T> + class SingleValueGenerator final : public IGenerator<T> { + T m_value; + public: + SingleValueGenerator(T&& value) : m_value(std::move(value)) {} + + T const& get() const override { + return m_value; + } + bool next() override { + return false; + } + }; + + template<typename T> + class FixedValuesGenerator final : public IGenerator<T> { + static_assert(!std::is_same<T, bool>::value, + "FixedValuesGenerator does not support bools because of std::vector<bool>" + "specialization, use SingleValue Generator instead."); + std::vector<T> m_values; + size_t m_idx = 0; + public: + FixedValuesGenerator( std::initializer_list<T> values ) : m_values( values ) {} + + T const& get() const override { + return m_values[m_idx]; + } + bool next() override { + ++m_idx; + return m_idx < m_values.size(); + } + }; + + template <typename T> + class GeneratorWrapper final { + std::unique_ptr<IGenerator<T>> m_generator; + public: + GeneratorWrapper(std::unique_ptr<IGenerator<T>> generator): + m_generator(std::move(generator)) + {} + T const& get() const { + return m_generator->get(); + } + bool next() { + return m_generator->next(); + } + }; + + template <typename T> + GeneratorWrapper<T> value(T&& value) { + return GeneratorWrapper<T>(pf::make_unique<SingleValueGenerator<T>>(std::forward<T>(value))); + } + template <typename T> + GeneratorWrapper<T> values(std::initializer_list<T> values) { + return GeneratorWrapper<T>(pf::make_unique<FixedValuesGenerator<T>>(values)); + } + + template<typename T> + class Generators : public IGenerator<T> { + std::vector<GeneratorWrapper<T>> m_generators; + size_t m_current = 0; + + void populate(GeneratorWrapper<T>&& generator) { + m_generators.emplace_back(std::move(generator)); + } + void populate(T&& val) { + m_generators.emplace_back(value(std::forward<T>(val))); + } + template<typename U> + void populate(U&& val) { + populate(T(std::forward<U>(val))); + } + template<typename U, typename... Gs> + void populate(U&& valueOrGenerator, Gs &&... moreGenerators) { + populate(std::forward<U>(valueOrGenerator)); + populate(std::forward<Gs>(moreGenerators)...); + } + + public: + template <typename... Gs> + Generators(Gs &&... moreGenerators) { + m_generators.reserve(sizeof...(Gs)); + populate(std::forward<Gs>(moreGenerators)...); + } + + T const& get() const override { + return m_generators[m_current].get(); + } + + bool next() override { + if (m_current >= m_generators.size()) { + return false; + } + const bool current_status = m_generators[m_current].next(); + if (!current_status) { + ++m_current; + } + return m_current < m_generators.size(); + } + }; + + template<typename... Ts> + GeneratorWrapper<std::tuple<Ts...>> table( std::initializer_list<std::tuple<typename std::decay<Ts>::type...>> tuples ) { + return values<std::tuple<Ts...>>( tuples ); + } + + // Tag type to signal that a generator sequence should convert arguments to a specific type + template <typename T> + struct as {}; + + template<typename T, typename... Gs> + auto makeGenerators( GeneratorWrapper<T>&& generator, Gs &&... moreGenerators ) -> Generators<T> { + return Generators<T>(std::move(generator), std::forward<Gs>(moreGenerators)...); + } + template<typename T> + auto makeGenerators( GeneratorWrapper<T>&& generator ) -> Generators<T> { + return Generators<T>(std::move(generator)); + } + template<typename T, typename... Gs> + auto makeGenerators( T&& val, Gs &&... moreGenerators ) -> Generators<T> { + return makeGenerators( value( std::forward<T>( val ) ), std::forward<Gs>( moreGenerators )... ); + } + template<typename T, typename U, typename... Gs> + auto makeGenerators( as<T>, U&& val, Gs &&... moreGenerators ) -> Generators<T> { + return makeGenerators( value( T( std::forward<U>( val ) ) ), std::forward<Gs>( moreGenerators )... ); + } + + auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker&; + + template<typename L> + // Note: The type after -> is weird, because VS2015 cannot parse + // the expression used in the typedef inside, when it is in + // return type. Yeah. + auto generate( StringRef generatorName, SourceLineInfo const& lineInfo, L const& generatorExpression ) -> decltype(std::declval<decltype(generatorExpression())>().get()) { + using UnderlyingType = typename decltype(generatorExpression())::type; + + IGeneratorTracker& tracker = acquireGeneratorTracker( generatorName, lineInfo ); + if (!tracker.hasGenerator()) { + tracker.setGenerator(pf::make_unique<Generators<UnderlyingType>>(generatorExpression())); + } + + auto const& generator = static_cast<IGenerator<UnderlyingType> const&>( *tracker.getGenerator() ); + return generator.get(); + } + +} // namespace Generators +} // namespace Catch + +#define GENERATE( ... ) \ + Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \ + CATCH_INTERNAL_LINEINFO, \ + [ ]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace) +#define GENERATE_COPY( ... ) \ + Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \ + CATCH_INTERNAL_LINEINFO, \ + [=]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace) +#define GENERATE_REF( ... ) \ + Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \ + CATCH_INTERNAL_LINEINFO, \ + [&]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace) + +// end catch_generators.hpp +// start catch_generators_generic.hpp + +namespace Catch { +namespace Generators { + + template <typename T> + class TakeGenerator : public IGenerator<T> { + GeneratorWrapper<T> m_generator; + size_t m_returned = 0; + size_t m_target; + public: + TakeGenerator(size_t target, GeneratorWrapper<T>&& generator): + m_generator(std::move(generator)), + m_target(target) + { + assert(target != 0 && "Empty generators are not allowed"); + } + T const& get() const override { + return m_generator.get(); + } + bool next() override { + ++m_returned; + if (m_returned >= m_target) { + return false; + } + + const auto success = m_generator.next(); + // If the underlying generator does not contain enough values + // then we cut short as well + if (!success) { + m_returned = m_target; + } + return success; + } + }; + + template <typename T> + GeneratorWrapper<T> take(size_t target, GeneratorWrapper<T>&& generator) { + return GeneratorWrapper<T>(pf::make_unique<TakeGenerator<T>>(target, std::move(generator))); + } + + template <typename T, typename Predicate> + class FilterGenerator : public IGenerator<T> { + GeneratorWrapper<T> m_generator; + Predicate m_predicate; + public: + template <typename P = Predicate> + FilterGenerator(P&& pred, GeneratorWrapper<T>&& generator): + m_generator(std::move(generator)), + m_predicate(std::forward<P>(pred)) + { + if (!m_predicate(m_generator.get())) { + // It might happen that there are no values that pass the + // filter. In that case we throw an exception. + auto has_initial_value = nextImpl(); + if (!has_initial_value) { + Catch::throw_exception(GeneratorException("No valid value found in filtered generator")); + } + } + } + + T const& get() const override { + return m_generator.get(); + } + + bool next() override { + return nextImpl(); + } + + private: + bool nextImpl() { + bool success = m_generator.next(); + if (!success) { + return false; + } + while (!m_predicate(m_generator.get()) && (success = m_generator.next()) == true); + return success; + } + }; + + template <typename T, typename Predicate> + GeneratorWrapper<T> filter(Predicate&& pred, GeneratorWrapper<T>&& generator) { + return GeneratorWrapper<T>(std::unique_ptr<IGenerator<T>>(pf::make_unique<FilterGenerator<T, Predicate>>(std::forward<Predicate>(pred), std::move(generator)))); + } + + template <typename T> + class RepeatGenerator : public IGenerator<T> { + static_assert(!std::is_same<T, bool>::value, + "RepeatGenerator currently does not support bools" + "because of std::vector<bool> specialization"); + GeneratorWrapper<T> m_generator; + mutable std::vector<T> m_returned; + size_t m_target_repeats; + size_t m_current_repeat = 0; + size_t m_repeat_index = 0; + public: + RepeatGenerator(size_t repeats, GeneratorWrapper<T>&& generator): + m_generator(std::move(generator)), + m_target_repeats(repeats) + { + assert(m_target_repeats > 0 && "Repeat generator must repeat at least once"); + } + + T const& get() const override { + if (m_current_repeat == 0) { + m_returned.push_back(m_generator.get()); + return m_returned.back(); + } + return m_returned[m_repeat_index]; + } + + bool next() override { + // There are 2 basic cases: + // 1) We are still reading the generator + // 2) We are reading our own cache + + // In the first case, we need to poke the underlying generator. + // If it happily moves, we are left in that state, otherwise it is time to start reading from our cache + if (m_current_repeat == 0) { + const auto success = m_generator.next(); + if (!success) { + ++m_current_repeat; + } + return m_current_repeat < m_target_repeats; + } + + // In the second case, we need to move indices forward and check that we haven't run up against the end + ++m_repeat_index; + if (m_repeat_index == m_returned.size()) { + m_repeat_index = 0; + ++m_current_repeat; + } + return m_current_repeat < m_target_repeats; + } + }; + + template <typename T> + GeneratorWrapper<T> repeat(size_t repeats, GeneratorWrapper<T>&& generator) { + return GeneratorWrapper<T>(pf::make_unique<RepeatGenerator<T>>(repeats, std::move(generator))); + } + + template <typename T, typename U, typename Func> + class MapGenerator : public IGenerator<T> { + // TBD: provide static assert for mapping function, for friendly error message + GeneratorWrapper<U> m_generator; + Func m_function; + // To avoid returning dangling reference, we have to save the values + T m_cache; + public: + template <typename F2 = Func> + MapGenerator(F2&& function, GeneratorWrapper<U>&& generator) : + m_generator(std::move(generator)), + m_function(std::forward<F2>(function)), + m_cache(m_function(m_generator.get())) + {} + + T const& get() const override { + return m_cache; + } + bool next() override { + const auto success = m_generator.next(); + if (success) { + m_cache = m_function(m_generator.get()); + } + return success; + } + }; + + template <typename Func, typename U, typename T = FunctionReturnType<Func, U>> + GeneratorWrapper<T> map(Func&& function, GeneratorWrapper<U>&& generator) { + return GeneratorWrapper<T>( + pf::make_unique<MapGenerator<T, U, Func>>(std::forward<Func>(function), std::move(generator)) + ); + } + + template <typename T, typename U, typename Func> + GeneratorWrapper<T> map(Func&& function, GeneratorWrapper<U>&& generator) { + return GeneratorWrapper<T>( + pf::make_unique<MapGenerator<T, U, Func>>(std::forward<Func>(function), std::move(generator)) + ); + } + + template <typename T> + class ChunkGenerator final : public IGenerator<std::vector<T>> { + std::vector<T> m_chunk; + size_t m_chunk_size; + GeneratorWrapper<T> m_generator; + bool m_used_up = false; + public: + ChunkGenerator(size_t size, GeneratorWrapper<T> generator) : + m_chunk_size(size), m_generator(std::move(generator)) + { + m_chunk.reserve(m_chunk_size); + if (m_chunk_size != 0) { + m_chunk.push_back(m_generator.get()); + for (size_t i = 1; i < m_chunk_size; ++i) { + if (!m_generator.next()) { + Catch::throw_exception(GeneratorException("Not enough values to initialize the first chunk")); + } + m_chunk.push_back(m_generator.get()); + } + } + } + std::vector<T> const& get() const override { + return m_chunk; + } + bool next() override { + m_chunk.clear(); + for (size_t idx = 0; idx < m_chunk_size; ++idx) { + if (!m_generator.next()) { + return false; + } + m_chunk.push_back(m_generator.get()); + } + return true; + } + }; + + template <typename T> + GeneratorWrapper<std::vector<T>> chunk(size_t size, GeneratorWrapper<T>&& generator) { + return GeneratorWrapper<std::vector<T>>( + pf::make_unique<ChunkGenerator<T>>(size, std::move(generator)) + ); + } + +} // namespace Generators +} // namespace Catch + +// end catch_generators_generic.hpp +// start catch_generators_specific.hpp + +// start catch_context.h + +#include <memory> + +namespace Catch { + + struct IResultCapture; + struct IRunner; + struct IConfig; + struct IMutableContext; + + using IConfigPtr = std::shared_ptr<IConfig const>; + + struct IContext + { + virtual ~IContext(); + + virtual IResultCapture* getResultCapture() = 0; + virtual IRunner* getRunner() = 0; + virtual IConfigPtr const& getConfig() const = 0; + }; + + struct IMutableContext : IContext + { + virtual ~IMutableContext(); + virtual void setResultCapture( IResultCapture* resultCapture ) = 0; + virtual void setRunner( IRunner* runner ) = 0; + virtual void setConfig( IConfigPtr const& config ) = 0; + + private: + static IMutableContext *currentContext; + friend IMutableContext& getCurrentMutableContext(); + friend void cleanUpContext(); + static void createContext(); + }; + + inline IMutableContext& getCurrentMutableContext() + { + if( !IMutableContext::currentContext ) + IMutableContext::createContext(); + // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn) + return *IMutableContext::currentContext; + } + + inline IContext& getCurrentContext() + { + return getCurrentMutableContext(); + } + + void cleanUpContext(); + + class SimplePcg32; + SimplePcg32& rng(); +} + +// end catch_context.h +// start catch_interfaces_config.h + +// start catch_option.hpp + +namespace Catch { + + // An optional type + template<typename T> + class Option { + public: + Option() : nullableValue( nullptr ) {} + Option( T const& _value ) + : nullableValue( new( storage ) T( _value ) ) + {} + Option( Option const& _other ) + : nullableValue( _other ? new( storage ) T( *_other ) : nullptr ) + {} + + ~Option() { + reset(); + } + + Option& operator= ( Option const& _other ) { + if( &_other != this ) { + reset(); + if( _other ) + nullableValue = new( storage ) T( *_other ); + } + return *this; + } + Option& operator = ( T const& _value ) { + reset(); + nullableValue = new( storage ) T( _value ); + return *this; + } + + void reset() { + if( nullableValue ) + nullableValue->~T(); + nullableValue = nullptr; + } + + T& operator*() { return *nullableValue; } + T const& operator*() const { return *nullableValue; } + T* operator->() { return nullableValue; } + const T* operator->() const { return nullableValue; } + + T valueOr( T const& defaultValue ) const { + return nullableValue ? *nullableValue : defaultValue; + } + + bool some() const { return nullableValue != nullptr; } + bool none() const { return nullableValue == nullptr; } + + bool operator !() const { return nullableValue == nullptr; } + explicit operator bool() const { + return some(); + } + + private: + T *nullableValue; + alignas(alignof(T)) char storage[sizeof(T)]; + }; + +} // end namespace Catch + +// end catch_option.hpp +#include <chrono> +#include <iosfwd> +#include <string> +#include <vector> +#include <memory> + +namespace Catch { + + enum class Verbosity { + Quiet = 0, + Normal, + High + }; + + struct WarnAbout { enum What { + Nothing = 0x00, + NoAssertions = 0x01, + NoTests = 0x02 + }; }; + + struct ShowDurations { enum OrNot { + DefaultForReporter, + Always, + Never + }; }; + struct RunTests { enum InWhatOrder { + InDeclarationOrder, + InLexicographicalOrder, + InRandomOrder + }; }; + struct UseColour { enum YesOrNo { + Auto, + Yes, + No + }; }; + struct WaitForKeypress { enum When { + Never, + BeforeStart = 1, + BeforeExit = 2, + BeforeStartAndExit = BeforeStart | BeforeExit + }; }; + + class TestSpec; + + struct IConfig : NonCopyable { + + virtual ~IConfig(); + + virtual bool allowThrows() const = 0; + virtual std::ostream& stream() const = 0; + virtual std::string name() const = 0; + virtual bool includeSuccessfulResults() const = 0; + virtual bool shouldDebugBreak() const = 0; + virtual bool warnAboutMissingAssertions() const = 0; + virtual bool warnAboutNoTests() const = 0; + virtual int abortAfter() const = 0; + virtual bool showInvisibles() const = 0; + virtual ShowDurations::OrNot showDurations() const = 0; + virtual double minDuration() const = 0; + virtual TestSpec const& testSpec() const = 0; + virtual bool hasTestFilters() const = 0; + virtual std::vector<std::string> const& getTestsOrTags() const = 0; + virtual RunTests::InWhatOrder runOrder() const = 0; + virtual unsigned int rngSeed() const = 0; + virtual UseColour::YesOrNo useColour() const = 0; + virtual std::vector<std::string> const& getSectionsToRun() const = 0; + virtual Verbosity verbosity() const = 0; + + virtual bool benchmarkNoAnalysis() const = 0; + virtual int benchmarkSamples() const = 0; + virtual double benchmarkConfidenceInterval() const = 0; + virtual unsigned int benchmarkResamples() const = 0; + virtual std::chrono::milliseconds benchmarkWarmupTime() const = 0; + }; + + using IConfigPtr = std::shared_ptr<IConfig const>; +} + +// end catch_interfaces_config.h +// start catch_random_number_generator.h + +#include <cstdint> + +namespace Catch { + + // This is a simple implementation of C++11 Uniform Random Number + // Generator. It does not provide all operators, because Catch2 + // does not use it, but it should behave as expected inside stdlib's + // distributions. + // The implementation is based on the PCG family (http://pcg-random.org) + class SimplePcg32 { + using state_type = std::uint64_t; + public: + using result_type = std::uint32_t; + static constexpr result_type (min)() { + return 0; + } + static constexpr result_type (max)() { + return static_cast<result_type>(-1); + } + + // Provide some default initial state for the default constructor + SimplePcg32():SimplePcg32(0xed743cc4U) {} + + explicit SimplePcg32(result_type seed_); + + void seed(result_type seed_); + void discard(uint64_t skip); + + result_type operator()(); + + private: + friend bool operator==(SimplePcg32 const& lhs, SimplePcg32 const& rhs); + friend bool operator!=(SimplePcg32 const& lhs, SimplePcg32 const& rhs); + + // In theory we also need operator<< and operator>> + // In practice we do not use them, so we will skip them for now + + std::uint64_t m_state; + // This part of the state determines which "stream" of the numbers + // is chosen -- we take it as a constant for Catch2, so we only + // need to deal with seeding the main state. + // Picked by reading 8 bytes from `/dev/random` :-) + static const std::uint64_t s_inc = (0x13ed0cc53f939476ULL << 1ULL) | 1ULL; + }; + +} // end namespace Catch + +// end catch_random_number_generator.h +#include <random> + +namespace Catch { +namespace Generators { + +template <typename Float> +class RandomFloatingGenerator final : public IGenerator<Float> { + Catch::SimplePcg32& m_rng; + std::uniform_real_distribution<Float> m_dist; + Float m_current_number; +public: + + RandomFloatingGenerator(Float a, Float b): + m_rng(rng()), + m_dist(a, b) { + static_cast<void>(next()); + } + + Float const& get() const override { + return m_current_number; + } + bool next() override { + m_current_number = m_dist(m_rng); + return true; + } +}; + +template <typename Integer> +class RandomIntegerGenerator final : public IGenerator<Integer> { + Catch::SimplePcg32& m_rng; + std::uniform_int_distribution<Integer> m_dist; + Integer m_current_number; +public: + + RandomIntegerGenerator(Integer a, Integer b): + m_rng(rng()), + m_dist(a, b) { + static_cast<void>(next()); + } + + Integer const& get() const override { + return m_current_number; + } + bool next() override { + m_current_number = m_dist(m_rng); + return true; + } +}; + +// TODO: Ideally this would be also constrained against the various char types, +// but I don't expect users to run into that in practice. +template <typename T> +typename std::enable_if<std::is_integral<T>::value && !std::is_same<T, bool>::value, +GeneratorWrapper<T>>::type +random(T a, T b) { + return GeneratorWrapper<T>( + pf::make_unique<RandomIntegerGenerator<T>>(a, b) + ); +} + +template <typename T> +typename std::enable_if<std::is_floating_point<T>::value, +GeneratorWrapper<T>>::type +random(T a, T b) { + return GeneratorWrapper<T>( + pf::make_unique<RandomFloatingGenerator<T>>(a, b) + ); +} + +template <typename T> +class RangeGenerator final : public IGenerator<T> { + T m_current; + T m_end; + T m_step; + bool m_positive; + +public: + RangeGenerator(T const& start, T const& end, T const& step): + m_current(start), + m_end(end), + m_step(step), + m_positive(m_step > T(0)) + { + assert(m_current != m_end && "Range start and end cannot be equal"); + assert(m_step != T(0) && "Step size cannot be zero"); + assert(((m_positive && m_current <= m_end) || (!m_positive && m_current >= m_end)) && "Step moves away from end"); + } + + RangeGenerator(T const& start, T const& end): + RangeGenerator(start, end, (start < end) ? T(1) : T(-1)) + {} + + T const& get() const override { + return m_current; + } + + bool next() override { + m_current += m_step; + return (m_positive) ? (m_current < m_end) : (m_current > m_end); + } +}; + +template <typename T> +GeneratorWrapper<T> range(T const& start, T const& end, T const& step) { + static_assert(std::is_arithmetic<T>::value && !std::is_same<T, bool>::value, "Type must be numeric"); + return GeneratorWrapper<T>(pf::make_unique<RangeGenerator<T>>(start, end, step)); +} + +template <typename T> +GeneratorWrapper<T> range(T const& start, T const& end) { + static_assert(std::is_integral<T>::value && !std::is_same<T, bool>::value, "Type must be an integer"); + return GeneratorWrapper<T>(pf::make_unique<RangeGenerator<T>>(start, end)); +} + +template <typename T> +class IteratorGenerator final : public IGenerator<T> { + static_assert(!std::is_same<T, bool>::value, + "IteratorGenerator currently does not support bools" + "because of std::vector<bool> specialization"); + + std::vector<T> m_elems; + size_t m_current = 0; +public: + template <typename InputIterator, typename InputSentinel> + IteratorGenerator(InputIterator first, InputSentinel last):m_elems(first, last) { + if (m_elems.empty()) { + Catch::throw_exception(GeneratorException("IteratorGenerator received no valid values")); + } + } + + T const& get() const override { + return m_elems[m_current]; + } + + bool next() override { + ++m_current; + return m_current != m_elems.size(); + } +}; + +template <typename InputIterator, + typename InputSentinel, + typename ResultType = typename std::iterator_traits<InputIterator>::value_type> +GeneratorWrapper<ResultType> from_range(InputIterator from, InputSentinel to) { + return GeneratorWrapper<ResultType>(pf::make_unique<IteratorGenerator<ResultType>>(from, to)); +} + +template <typename Container, + typename ResultType = typename Container::value_type> +GeneratorWrapper<ResultType> from_range(Container const& cnt) { + return GeneratorWrapper<ResultType>(pf::make_unique<IteratorGenerator<ResultType>>(cnt.begin(), cnt.end())); +} + +} // namespace Generators +} // namespace Catch + +// end catch_generators_specific.hpp + +// These files are included here so the single_include script doesn't put them +// in the conditionally compiled sections +// start catch_test_case_info.h + +#include <string> +#include <vector> +#include <memory> + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#endif + +namespace Catch { + + struct ITestInvoker; + + struct TestCaseInfo { + enum SpecialProperties{ + None = 0, + IsHidden = 1 << 1, + ShouldFail = 1 << 2, + MayFail = 1 << 3, + Throws = 1 << 4, + NonPortable = 1 << 5, + Benchmark = 1 << 6 + }; + + TestCaseInfo( std::string const& _name, + std::string const& _className, + std::string const& _description, + std::vector<std::string> const& _tags, + SourceLineInfo const& _lineInfo ); + + friend void setTags( TestCaseInfo& testCaseInfo, std::vector<std::string> tags ); + + bool isHidden() const; + bool throws() const; + bool okToFail() const; + bool expectedToFail() const; + + std::string tagsAsString() const; + + std::string name; + std::string className; + std::string description; + std::vector<std::string> tags; + std::vector<std::string> lcaseTags; + SourceLineInfo lineInfo; + SpecialProperties properties; + }; + + class TestCase : public TestCaseInfo { + public: + + TestCase( ITestInvoker* testCase, TestCaseInfo&& info ); + + TestCase withName( std::string const& _newName ) const; + + void invoke() const; + + TestCaseInfo const& getTestCaseInfo() const; + + bool operator == ( TestCase const& other ) const; + bool operator < ( TestCase const& other ) const; + + private: + std::shared_ptr<ITestInvoker> test; + }; + + TestCase makeTestCase( ITestInvoker* testCase, + std::string const& className, + NameAndTags const& nameAndTags, + SourceLineInfo const& lineInfo ); +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +// end catch_test_case_info.h +// start catch_interfaces_runner.h + +namespace Catch { + + struct IRunner { + virtual ~IRunner(); + virtual bool aborting() const = 0; + }; +} + +// end catch_interfaces_runner.h + +#ifdef __OBJC__ +// start catch_objc.hpp + +#import <objc/runtime.h> + +#include <string> + +// NB. Any general catch headers included here must be included +// in catch.hpp first to make sure they are included by the single +// header for non obj-usage + +/////////////////////////////////////////////////////////////////////////////// +// This protocol is really only here for (self) documenting purposes, since +// all its methods are optional. +@protocol OcFixture + +@optional + +-(void) setUp; +-(void) tearDown; + +@end + +namespace Catch { + + class OcMethod : public ITestInvoker { + + public: + OcMethod( Class cls, SEL sel ) : m_cls( cls ), m_sel( sel ) {} + + virtual void invoke() const { + id obj = [[m_cls alloc] init]; + + performOptionalSelector( obj, @selector(setUp) ); + performOptionalSelector( obj, m_sel ); + performOptionalSelector( obj, @selector(tearDown) ); + + arcSafeRelease( obj ); + } + private: + virtual ~OcMethod() {} + + Class m_cls; + SEL m_sel; + }; + + namespace Detail{ + + inline std::string getAnnotation( Class cls, + std::string const& annotationName, + std::string const& testCaseName ) { + NSString* selStr = [[NSString alloc] initWithFormat:@"Catch_%s_%s", annotationName.c_str(), testCaseName.c_str()]; + SEL sel = NSSelectorFromString( selStr ); + arcSafeRelease( selStr ); + id value = performOptionalSelector( cls, sel ); + if( value ) + return [(NSString*)value UTF8String]; + return ""; + } + } + + inline std::size_t registerTestMethods() { + std::size_t noTestMethods = 0; + int noClasses = objc_getClassList( nullptr, 0 ); + + Class* classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc( sizeof(Class) * noClasses); + objc_getClassList( classes, noClasses ); + + for( int c = 0; c < noClasses; c++ ) { + Class cls = classes[c]; + { + u_int count; + Method* methods = class_copyMethodList( cls, &count ); + for( u_int m = 0; m < count ; m++ ) { + SEL selector = method_getName(methods[m]); + std::string methodName = sel_getName(selector); + if( startsWith( methodName, "Catch_TestCase_" ) ) { + std::string testCaseName = methodName.substr( 15 ); + std::string name = Detail::getAnnotation( cls, "Name", testCaseName ); + std::string desc = Detail::getAnnotation( cls, "Description", testCaseName ); + const char* className = class_getName( cls ); + + getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), className, NameAndTags( name.c_str(), desc.c_str() ), SourceLineInfo("",0) ) ); + noTestMethods++; + } + } + free(methods); + } + } + return noTestMethods; + } + +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) + + namespace Matchers { + namespace Impl { + namespace NSStringMatchers { + + struct StringHolder : MatcherBase<NSString*>{ + StringHolder( NSString* substr ) : m_substr( [substr copy] ){} + StringHolder( StringHolder const& other ) : m_substr( [other.m_substr copy] ){} + StringHolder() { + arcSafeRelease( m_substr ); + } + + bool match( NSString* str ) const override { + return false; + } + + NSString* CATCH_ARC_STRONG m_substr; + }; + + struct Equals : StringHolder { + Equals( NSString* substr ) : StringHolder( substr ){} + + bool match( NSString* str ) const override { + return (str != nil || m_substr == nil ) && + [str isEqualToString:m_substr]; + } + + std::string describe() const override { + return "equals string: " + Catch::Detail::stringify( m_substr ); + } + }; + + struct Contains : StringHolder { + Contains( NSString* substr ) : StringHolder( substr ){} + + bool match( NSString* str ) const override { + return (str != nil || m_substr == nil ) && + [str rangeOfString:m_substr].location != NSNotFound; + } + + std::string describe() const override { + return "contains string: " + Catch::Detail::stringify( m_substr ); + } + }; + + struct StartsWith : StringHolder { + StartsWith( NSString* substr ) : StringHolder( substr ){} + + bool match( NSString* str ) const override { + return (str != nil || m_substr == nil ) && + [str rangeOfString:m_substr].location == 0; + } + + std::string describe() const override { + return "starts with: " + Catch::Detail::stringify( m_substr ); + } + }; + struct EndsWith : StringHolder { + EndsWith( NSString* substr ) : StringHolder( substr ){} + + bool match( NSString* str ) const override { + return (str != nil || m_substr == nil ) && + [str rangeOfString:m_substr].location == [str length] - [m_substr length]; + } + + std::string describe() const override { + return "ends with: " + Catch::Detail::stringify( m_substr ); + } + }; + + } // namespace NSStringMatchers + } // namespace Impl + + inline Impl::NSStringMatchers::Equals + Equals( NSString* substr ){ return Impl::NSStringMatchers::Equals( substr ); } + + inline Impl::NSStringMatchers::Contains + Contains( NSString* substr ){ return Impl::NSStringMatchers::Contains( substr ); } + + inline Impl::NSStringMatchers::StartsWith + StartsWith( NSString* substr ){ return Impl::NSStringMatchers::StartsWith( substr ); } + + inline Impl::NSStringMatchers::EndsWith + EndsWith( NSString* substr ){ return Impl::NSStringMatchers::EndsWith( substr ); } + + } // namespace Matchers + + using namespace Matchers; + +#endif // CATCH_CONFIG_DISABLE_MATCHERS + +} // namespace Catch + +/////////////////////////////////////////////////////////////////////////////// +#define OC_MAKE_UNIQUE_NAME( root, uniqueSuffix ) root##uniqueSuffix +#define OC_TEST_CASE2( name, desc, uniqueSuffix ) \ ++(NSString*) OC_MAKE_UNIQUE_NAME( Catch_Name_test_, uniqueSuffix ) \ +{ \ +return @ name; \ +} \ ++(NSString*) OC_MAKE_UNIQUE_NAME( Catch_Description_test_, uniqueSuffix ) \ +{ \ +return @ desc; \ +} \ +-(void) OC_MAKE_UNIQUE_NAME( Catch_TestCase_test_, uniqueSuffix ) + +#define OC_TEST_CASE( name, desc ) OC_TEST_CASE2( name, desc, __LINE__ ) + +// end catch_objc.hpp +#endif + +// Benchmarking needs the externally-facing parts of reporters to work +#if defined(CATCH_CONFIG_EXTERNAL_INTERFACES) || defined(CATCH_CONFIG_ENABLE_BENCHMARKING) +// start catch_external_interfaces.h + +// start catch_reporter_bases.hpp + +// start catch_interfaces_reporter.h + +// start catch_config.hpp + +// start catch_test_spec_parser.h + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#endif + +// start catch_test_spec.h + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#endif + +// start catch_wildcard_pattern.h + +namespace Catch +{ + class WildcardPattern { + enum WildcardPosition { + NoWildcard = 0, + WildcardAtStart = 1, + WildcardAtEnd = 2, + WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd + }; + + public: + + WildcardPattern( std::string const& pattern, CaseSensitive::Choice caseSensitivity ); + virtual ~WildcardPattern() = default; + virtual bool matches( std::string const& str ) const; + + private: + std::string normaliseString( std::string const& str ) const; + CaseSensitive::Choice m_caseSensitivity; + WildcardPosition m_wildcard = NoWildcard; + std::string m_pattern; + }; +} + +// end catch_wildcard_pattern.h +#include <string> +#include <vector> +#include <memory> + +namespace Catch { + + struct IConfig; + + class TestSpec { + class Pattern { + public: + explicit Pattern( std::string const& name ); + virtual ~Pattern(); + virtual bool matches( TestCaseInfo const& testCase ) const = 0; + std::string const& name() const; + private: + std::string const m_name; + }; + using PatternPtr = std::shared_ptr<Pattern>; + + class NamePattern : public Pattern { + public: + explicit NamePattern( std::string const& name, std::string const& filterString ); + bool matches( TestCaseInfo const& testCase ) const override; + private: + WildcardPattern m_wildcardPattern; + }; + + class TagPattern : public Pattern { + public: + explicit TagPattern( std::string const& tag, std::string const& filterString ); + bool matches( TestCaseInfo const& testCase ) const override; + private: + std::string m_tag; + }; + + class ExcludedPattern : public Pattern { + public: + explicit ExcludedPattern( PatternPtr const& underlyingPattern ); + bool matches( TestCaseInfo const& testCase ) const override; + private: + PatternPtr m_underlyingPattern; + }; + + struct Filter { + std::vector<PatternPtr> m_patterns; + + bool matches( TestCaseInfo const& testCase ) const; + std::string name() const; + }; + + public: + struct FilterMatch { + std::string name; + std::vector<TestCase const*> tests; + }; + using Matches = std::vector<FilterMatch>; + using vectorStrings = std::vector<std::string>; + + bool hasFilters() const; + bool matches( TestCaseInfo const& testCase ) const; + Matches matchesByFilter( std::vector<TestCase> const& testCases, IConfig const& config ) const; + const vectorStrings & getInvalidArgs() const; + + private: + std::vector<Filter> m_filters; + std::vector<std::string> m_invalidArgs; + friend class TestSpecParser; + }; +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +// end catch_test_spec.h +// start catch_interfaces_tag_alias_registry.h + +#include <string> + +namespace Catch { + + struct TagAlias; + + struct ITagAliasRegistry { + virtual ~ITagAliasRegistry(); + // Nullptr if not present + virtual TagAlias const* find( std::string const& alias ) const = 0; + virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const = 0; + + static ITagAliasRegistry const& get(); + }; + +} // end namespace Catch + +// end catch_interfaces_tag_alias_registry.h +namespace Catch { + + class TestSpecParser { + enum Mode{ None, Name, QuotedName, Tag, EscapedName }; + Mode m_mode = None; + Mode lastMode = None; + bool m_exclusion = false; + std::size_t m_pos = 0; + std::size_t m_realPatternPos = 0; + std::string m_arg; + std::string m_substring; + std::string m_patternName; + std::vector<std::size_t> m_escapeChars; + TestSpec::Filter m_currentFilter; + TestSpec m_testSpec; + ITagAliasRegistry const* m_tagAliases = nullptr; + + public: + TestSpecParser( ITagAliasRegistry const& tagAliases ); + + TestSpecParser& parse( std::string const& arg ); + TestSpec testSpec(); + + private: + bool visitChar( char c ); + void startNewMode( Mode mode ); + bool processNoneChar( char c ); + void processNameChar( char c ); + bool processOtherChar( char c ); + void endMode(); + void escape(); + bool isControlChar( char c ) const; + void saveLastMode(); + void revertBackToLastMode(); + void addFilter(); + bool separate(); + + // Handles common preprocessing of the pattern for name/tag patterns + std::string preprocessPattern(); + // Adds the current pattern as a test name + void addNamePattern(); + // Adds the current pattern as a tag + void addTagPattern(); + + inline void addCharToPattern(char c) { + m_substring += c; + m_patternName += c; + m_realPatternPos++; + } + + }; + TestSpec parseTestSpec( std::string const& arg ); + +} // namespace Catch + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +// end catch_test_spec_parser.h +// Libstdc++ doesn't like incomplete classes for unique_ptr + +#include <memory> +#include <vector> +#include <string> + +#ifndef CATCH_CONFIG_CONSOLE_WIDTH +#define CATCH_CONFIG_CONSOLE_WIDTH 80 +#endif + +namespace Catch { + + struct IStream; + + struct ConfigData { + bool listTests = false; + bool listTags = false; + bool listReporters = false; + bool listTestNamesOnly = false; + + bool showSuccessfulTests = false; + bool shouldDebugBreak = false; + bool noThrow = false; + bool showHelp = false; + bool showInvisibles = false; + bool filenamesAsTags = false; + bool libIdentify = false; + + int abortAfter = -1; + unsigned int rngSeed = 0; + + bool benchmarkNoAnalysis = false; + unsigned int benchmarkSamples = 100; + double benchmarkConfidenceInterval = 0.95; + unsigned int benchmarkResamples = 100000; + std::chrono::milliseconds::rep benchmarkWarmupTime = 100; + + Verbosity verbosity = Verbosity::Normal; + WarnAbout::What warnings = WarnAbout::Nothing; + ShowDurations::OrNot showDurations = ShowDurations::DefaultForReporter; + double minDuration = -1; + RunTests::InWhatOrder runOrder = RunTests::InDeclarationOrder; + UseColour::YesOrNo useColour = UseColour::Auto; + WaitForKeypress::When waitForKeypress = WaitForKeypress::Never; + + std::string outputFilename; + std::string name; + std::string processName; +#ifndef CATCH_CONFIG_DEFAULT_REPORTER +#define CATCH_CONFIG_DEFAULT_REPORTER "console" +#endif + std::string reporterName = CATCH_CONFIG_DEFAULT_REPORTER; +#undef CATCH_CONFIG_DEFAULT_REPORTER + + std::vector<std::string> testsOrTags; + std::vector<std::string> sectionsToRun; + }; + + class Config : public IConfig { + public: + + Config() = default; + Config( ConfigData const& data ); + virtual ~Config() = default; + + std::string const& getFilename() const; + + bool listTests() const; + bool listTestNamesOnly() const; + bool listTags() const; + bool listReporters() const; + + std::string getProcessName() const; + std::string const& getReporterName() const; + + std::vector<std::string> const& getTestsOrTags() const override; + std::vector<std::string> const& getSectionsToRun() const override; + + TestSpec const& testSpec() const override; + bool hasTestFilters() const override; + + bool showHelp() const; + + // IConfig interface + bool allowThrows() const override; + std::ostream& stream() const override; + std::string name() const override; + bool includeSuccessfulResults() const override; + bool warnAboutMissingAssertions() const override; + bool warnAboutNoTests() const override; + ShowDurations::OrNot showDurations() const override; + double minDuration() const override; + RunTests::InWhatOrder runOrder() const override; + unsigned int rngSeed() const override; + UseColour::YesOrNo useColour() const override; + bool shouldDebugBreak() const override; + int abortAfter() const override; + bool showInvisibles() const override; + Verbosity verbosity() const override; + bool benchmarkNoAnalysis() const override; + int benchmarkSamples() const override; + double benchmarkConfidenceInterval() const override; + unsigned int benchmarkResamples() const override; + std::chrono::milliseconds benchmarkWarmupTime() const override; + + private: + + IStream const* openStream(); + ConfigData m_data; + + std::unique_ptr<IStream const> m_stream; + TestSpec m_testSpec; + bool m_hasTestFilters = false; + }; + +} // end namespace Catch + +// end catch_config.hpp +// start catch_assertionresult.h + +#include <string> + +namespace Catch { + + struct AssertionResultData + { + AssertionResultData() = delete; + + AssertionResultData( ResultWas::OfType _resultType, LazyExpression const& _lazyExpression ); + + std::string message; + mutable std::string reconstructedExpression; + LazyExpression lazyExpression; + ResultWas::OfType resultType; + + std::string reconstructExpression() const; + }; + + class AssertionResult { + public: + AssertionResult() = delete; + AssertionResult( AssertionInfo const& info, AssertionResultData const& data ); + + bool isOk() const; + bool succeeded() const; + ResultWas::OfType getResultType() const; + bool hasExpression() const; + bool hasMessage() const; + std::string getExpression() const; + std::string getExpressionInMacro() const; + bool hasExpandedExpression() const; + std::string getExpandedExpression() const; + std::string getMessage() const; + SourceLineInfo getSourceInfo() const; + StringRef getTestMacroName() const; + + //protected: + AssertionInfo m_info; + AssertionResultData m_resultData; + }; + +} // end namespace Catch + +// end catch_assertionresult.h +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) +// start catch_estimate.hpp + + // Statistics estimates + + +namespace Catch { + namespace Benchmark { + template <typename Duration> + struct Estimate { + Duration point; + Duration lower_bound; + Duration upper_bound; + double confidence_interval; + + template <typename Duration2> + operator Estimate<Duration2>() const { + return { point, lower_bound, upper_bound, confidence_interval }; + } + }; + } // namespace Benchmark +} // namespace Catch + +// end catch_estimate.hpp +// start catch_outlier_classification.hpp + +// Outlier information + +namespace Catch { + namespace Benchmark { + struct OutlierClassification { + int samples_seen = 0; + int low_severe = 0; // more than 3 times IQR below Q1 + int low_mild = 0; // 1.5 to 3 times IQR below Q1 + int high_mild = 0; // 1.5 to 3 times IQR above Q3 + int high_severe = 0; // more than 3 times IQR above Q3 + + int total() const { + return low_severe + low_mild + high_mild + high_severe; + } + }; + } // namespace Benchmark +} // namespace Catch + +// end catch_outlier_classification.hpp + +#include <iterator> +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING + +#include <string> +#include <iosfwd> +#include <map> +#include <set> +#include <memory> +#include <algorithm> + +namespace Catch { + + struct ReporterConfig { + explicit ReporterConfig( IConfigPtr const& _fullConfig ); + + ReporterConfig( IConfigPtr const& _fullConfig, std::ostream& _stream ); + + std::ostream& stream() const; + IConfigPtr fullConfig() const; + + private: + std::ostream* m_stream; + IConfigPtr m_fullConfig; + }; + + struct ReporterPreferences { + bool shouldRedirectStdOut = false; + bool shouldReportAllAssertions = false; + }; + + template<typename T> + struct LazyStat : Option<T> { + LazyStat& operator=( T const& _value ) { + Option<T>::operator=( _value ); + used = false; + return *this; + } + void reset() { + Option<T>::reset(); + used = false; + } + bool used = false; + }; + + struct TestRunInfo { + TestRunInfo( std::string const& _name ); + std::string name; + }; + struct GroupInfo { + GroupInfo( std::string const& _name, + std::size_t _groupIndex, + std::size_t _groupsCount ); + + std::string name; + std::size_t groupIndex; + std::size_t groupsCounts; + }; + + struct AssertionStats { + AssertionStats( AssertionResult const& _assertionResult, + std::vector<MessageInfo> const& _infoMessages, + Totals const& _totals ); + + AssertionStats( AssertionStats const& ) = default; + AssertionStats( AssertionStats && ) = default; + AssertionStats& operator = ( AssertionStats const& ) = delete; + AssertionStats& operator = ( AssertionStats && ) = delete; + virtual ~AssertionStats(); + + AssertionResult assertionResult; + std::vector<MessageInfo> infoMessages; + Totals totals; + }; + + struct SectionStats { + SectionStats( SectionInfo const& _sectionInfo, + Counts const& _assertions, + double _durationInSeconds, + bool _missingAssertions ); + SectionStats( SectionStats const& ) = default; + SectionStats( SectionStats && ) = default; + SectionStats& operator = ( SectionStats const& ) = default; + SectionStats& operator = ( SectionStats && ) = default; + virtual ~SectionStats(); + + SectionInfo sectionInfo; + Counts assertions; + double durationInSeconds; + bool missingAssertions; + }; + + struct TestCaseStats { + TestCaseStats( TestCaseInfo const& _testInfo, + Totals const& _totals, + std::string const& _stdOut, + std::string const& _stdErr, + bool _aborting ); + + TestCaseStats( TestCaseStats const& ) = default; + TestCaseStats( TestCaseStats && ) = default; + TestCaseStats& operator = ( TestCaseStats const& ) = default; + TestCaseStats& operator = ( TestCaseStats && ) = default; + virtual ~TestCaseStats(); + + TestCaseInfo testInfo; + Totals totals; + std::string stdOut; + std::string stdErr; + bool aborting; + }; + + struct TestGroupStats { + TestGroupStats( GroupInfo const& _groupInfo, + Totals const& _totals, + bool _aborting ); + TestGroupStats( GroupInfo const& _groupInfo ); + + TestGroupStats( TestGroupStats const& ) = default; + TestGroupStats( TestGroupStats && ) = default; + TestGroupStats& operator = ( TestGroupStats const& ) = default; + TestGroupStats& operator = ( TestGroupStats && ) = default; + virtual ~TestGroupStats(); + + GroupInfo groupInfo; + Totals totals; + bool aborting; + }; + + struct TestRunStats { + TestRunStats( TestRunInfo const& _runInfo, + Totals const& _totals, + bool _aborting ); + + TestRunStats( TestRunStats const& ) = default; + TestRunStats( TestRunStats && ) = default; + TestRunStats& operator = ( TestRunStats const& ) = default; + TestRunStats& operator = ( TestRunStats && ) = default; + virtual ~TestRunStats(); + + TestRunInfo runInfo; + Totals totals; + bool aborting; + }; + +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) + struct BenchmarkInfo { + std::string name; + double estimatedDuration; + int iterations; + int samples; + unsigned int resamples; + double clockResolution; + double clockCost; + }; + + template <class Duration> + struct BenchmarkStats { + BenchmarkInfo info; + + std::vector<Duration> samples; + Benchmark::Estimate<Duration> mean; + Benchmark::Estimate<Duration> standardDeviation; + Benchmark::OutlierClassification outliers; + double outlierVariance; + + template <typename Duration2> + operator BenchmarkStats<Duration2>() const { + std::vector<Duration2> samples2; + samples2.reserve(samples.size()); + std::transform(samples.begin(), samples.end(), std::back_inserter(samples2), [](Duration d) { return Duration2(d); }); + return { + info, + std::move(samples2), + mean, + standardDeviation, + outliers, + outlierVariance, + }; + } + }; +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING + + struct IStreamingReporter { + virtual ~IStreamingReporter() = default; + + // Implementing class must also provide the following static methods: + // static std::string getDescription(); + // static std::set<Verbosity> getSupportedVerbosities() + + virtual ReporterPreferences getPreferences() const = 0; + + virtual void noMatchingTestCases( std::string const& spec ) = 0; + + virtual void reportInvalidArguments(std::string const&) {} + + virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0; + virtual void testGroupStarting( GroupInfo const& groupInfo ) = 0; + + virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0; + virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0; + +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) + virtual void benchmarkPreparing( std::string const& ) {} + virtual void benchmarkStarting( BenchmarkInfo const& ) {} + virtual void benchmarkEnded( BenchmarkStats<> const& ) {} + virtual void benchmarkFailed( std::string const& ) {} +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING + + virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0; + + // The return value indicates if the messages buffer should be cleared: + virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0; + + virtual void sectionEnded( SectionStats const& sectionStats ) = 0; + virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0; + virtual void testGroupEnded( TestGroupStats const& testGroupStats ) = 0; + virtual void testRunEnded( TestRunStats const& testRunStats ) = 0; + + virtual void skipTest( TestCaseInfo const& testInfo ) = 0; + + // Default empty implementation provided + virtual void fatalErrorEncountered( StringRef name ); + + virtual bool isMulti() const; + }; + using IStreamingReporterPtr = std::unique_ptr<IStreamingReporter>; + + struct IReporterFactory { + virtual ~IReporterFactory(); + virtual IStreamingReporterPtr create( ReporterConfig const& config ) const = 0; + virtual std::string getDescription() const = 0; + }; + using IReporterFactoryPtr = std::shared_ptr<IReporterFactory>; + + struct IReporterRegistry { + using FactoryMap = std::map<std::string, IReporterFactoryPtr>; + using Listeners = std::vector<IReporterFactoryPtr>; + + virtual ~IReporterRegistry(); + virtual IStreamingReporterPtr create( std::string const& name, IConfigPtr const& config ) const = 0; + virtual FactoryMap const& getFactories() const = 0; + virtual Listeners const& getListeners() const = 0; + }; + +} // end namespace Catch + +// end catch_interfaces_reporter.h +#include <algorithm> +#include <cstring> +#include <cfloat> +#include <cstdio> +#include <cassert> +#include <memory> +#include <ostream> + +namespace Catch { + void prepareExpandedExpression(AssertionResult& result); + + // Returns double formatted as %.3f (format expected on output) + std::string getFormattedDuration( double duration ); + + //! Should the reporter show + bool shouldShowDuration( IConfig const& config, double duration ); + + std::string serializeFilters( std::vector<std::string> const& container ); + + template<typename DerivedT> + struct StreamingReporterBase : IStreamingReporter { + + StreamingReporterBase( ReporterConfig const& _config ) + : m_config( _config.fullConfig() ), + stream( _config.stream() ) + { + m_reporterPrefs.shouldRedirectStdOut = false; + if( !DerivedT::getSupportedVerbosities().count( m_config->verbosity() ) ) + CATCH_ERROR( "Verbosity level not supported by this reporter" ); + } + + ReporterPreferences getPreferences() const override { + return m_reporterPrefs; + } + + static std::set<Verbosity> getSupportedVerbosities() { + return { Verbosity::Normal }; + } + + ~StreamingReporterBase() override = default; + + void noMatchingTestCases(std::string const&) override {} + + void reportInvalidArguments(std::string const&) override {} + + void testRunStarting(TestRunInfo const& _testRunInfo) override { + currentTestRunInfo = _testRunInfo; + } + + void testGroupStarting(GroupInfo const& _groupInfo) override { + currentGroupInfo = _groupInfo; + } + + void testCaseStarting(TestCaseInfo const& _testInfo) override { + currentTestCaseInfo = _testInfo; + } + void sectionStarting(SectionInfo const& _sectionInfo) override { + m_sectionStack.push_back(_sectionInfo); + } + + void sectionEnded(SectionStats const& /* _sectionStats */) override { + m_sectionStack.pop_back(); + } + void testCaseEnded(TestCaseStats const& /* _testCaseStats */) override { + currentTestCaseInfo.reset(); + } + void testGroupEnded(TestGroupStats const& /* _testGroupStats */) override { + currentGroupInfo.reset(); + } + void testRunEnded(TestRunStats const& /* _testRunStats */) override { + currentTestCaseInfo.reset(); + currentGroupInfo.reset(); + currentTestRunInfo.reset(); + } + + void skipTest(TestCaseInfo const&) override { + // Don't do anything with this by default. + // It can optionally be overridden in the derived class. + } + + IConfigPtr m_config; + std::ostream& stream; + + LazyStat<TestRunInfo> currentTestRunInfo; + LazyStat<GroupInfo> currentGroupInfo; + LazyStat<TestCaseInfo> currentTestCaseInfo; + + std::vector<SectionInfo> m_sectionStack; + ReporterPreferences m_reporterPrefs; + }; + + template<typename DerivedT> + struct CumulativeReporterBase : IStreamingReporter { + template<typename T, typename ChildNodeT> + struct Node { + explicit Node( T const& _value ) : value( _value ) {} + virtual ~Node() {} + + using ChildNodes = std::vector<std::shared_ptr<ChildNodeT>>; + T value; + ChildNodes children; + }; + struct SectionNode { + explicit SectionNode(SectionStats const& _stats) : stats(_stats) {} + virtual ~SectionNode() = default; + + bool operator == (SectionNode const& other) const { + return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo; + } + bool operator == (std::shared_ptr<SectionNode> const& other) const { + return operator==(*other); + } + + SectionStats stats; + using ChildSections = std::vector<std::shared_ptr<SectionNode>>; + using Assertions = std::vector<AssertionStats>; + ChildSections childSections; + Assertions assertions; + std::string stdOut; + std::string stdErr; + }; + + struct BySectionInfo { + BySectionInfo( SectionInfo const& other ) : m_other( other ) {} + BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {} + bool operator() (std::shared_ptr<SectionNode> const& node) const { + return ((node->stats.sectionInfo.name == m_other.name) && + (node->stats.sectionInfo.lineInfo == m_other.lineInfo)); + } + void operator=(BySectionInfo const&) = delete; + + private: + SectionInfo const& m_other; + }; + + using TestCaseNode = Node<TestCaseStats, SectionNode>; + using TestGroupNode = Node<TestGroupStats, TestCaseNode>; + using TestRunNode = Node<TestRunStats, TestGroupNode>; + + CumulativeReporterBase( ReporterConfig const& _config ) + : m_config( _config.fullConfig() ), + stream( _config.stream() ) + { + m_reporterPrefs.shouldRedirectStdOut = false; + if( !DerivedT::getSupportedVerbosities().count( m_config->verbosity() ) ) + CATCH_ERROR( "Verbosity level not supported by this reporter" ); + } + ~CumulativeReporterBase() override = default; + + ReporterPreferences getPreferences() const override { + return m_reporterPrefs; + } + + static std::set<Verbosity> getSupportedVerbosities() { + return { Verbosity::Normal }; + } + + void testRunStarting( TestRunInfo const& ) override {} + void testGroupStarting( GroupInfo const& ) override {} + + void testCaseStarting( TestCaseInfo const& ) override {} + + void sectionStarting( SectionInfo const& sectionInfo ) override { + SectionStats incompleteStats( sectionInfo, Counts(), 0, false ); + std::shared_ptr<SectionNode> node; + if( m_sectionStack.empty() ) { + if( !m_rootSection ) + m_rootSection = std::make_shared<SectionNode>( incompleteStats ); + node = m_rootSection; + } + else { + SectionNode& parentNode = *m_sectionStack.back(); + auto it = + std::find_if( parentNode.childSections.begin(), + parentNode.childSections.end(), + BySectionInfo( sectionInfo ) ); + if( it == parentNode.childSections.end() ) { + node = std::make_shared<SectionNode>( incompleteStats ); + parentNode.childSections.push_back( node ); + } + else + node = *it; + } + m_sectionStack.push_back( node ); + m_deepestSection = std::move(node); + } + + void assertionStarting(AssertionInfo const&) override {} + + bool assertionEnded(AssertionStats const& assertionStats) override { + assert(!m_sectionStack.empty()); + // AssertionResult holds a pointer to a temporary DecomposedExpression, + // which getExpandedExpression() calls to build the expression string. + // Our section stack copy of the assertionResult will likely outlive the + // temporary, so it must be expanded or discarded now to avoid calling + // a destroyed object later. + prepareExpandedExpression(const_cast<AssertionResult&>( assertionStats.assertionResult ) ); + SectionNode& sectionNode = *m_sectionStack.back(); + sectionNode.assertions.push_back(assertionStats); + return true; + } + void sectionEnded(SectionStats const& sectionStats) override { + assert(!m_sectionStack.empty()); + SectionNode& node = *m_sectionStack.back(); + node.stats = sectionStats; + m_sectionStack.pop_back(); + } + void testCaseEnded(TestCaseStats const& testCaseStats) override { + auto node = std::make_shared<TestCaseNode>(testCaseStats); + assert(m_sectionStack.size() == 0); + node->children.push_back(m_rootSection); + m_testCases.push_back(node); + m_rootSection.reset(); + + assert(m_deepestSection); + m_deepestSection->stdOut = testCaseStats.stdOut; + m_deepestSection->stdErr = testCaseStats.stdErr; + } + void testGroupEnded(TestGroupStats const& testGroupStats) override { + auto node = std::make_shared<TestGroupNode>(testGroupStats); + node->children.swap(m_testCases); + m_testGroups.push_back(node); + } + void testRunEnded(TestRunStats const& testRunStats) override { + auto node = std::make_shared<TestRunNode>(testRunStats); + node->children.swap(m_testGroups); + m_testRuns.push_back(node); + testRunEndedCumulative(); + } + virtual void testRunEndedCumulative() = 0; + + void skipTest(TestCaseInfo const&) override {} + + IConfigPtr m_config; + std::ostream& stream; + std::vector<AssertionStats> m_assertions; + std::vector<std::vector<std::shared_ptr<SectionNode>>> m_sections; + std::vector<std::shared_ptr<TestCaseNode>> m_testCases; + std::vector<std::shared_ptr<TestGroupNode>> m_testGroups; + + std::vector<std::shared_ptr<TestRunNode>> m_testRuns; + + std::shared_ptr<SectionNode> m_rootSection; + std::shared_ptr<SectionNode> m_deepestSection; + std::vector<std::shared_ptr<SectionNode>> m_sectionStack; + ReporterPreferences m_reporterPrefs; + }; + + template<char C> + char const* getLineOfChars() { + static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0}; + if( !*line ) { + std::memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 ); + line[CATCH_CONFIG_CONSOLE_WIDTH-1] = 0; + } + return line; + } + + struct TestEventListenerBase : StreamingReporterBase<TestEventListenerBase> { + TestEventListenerBase( ReporterConfig const& _config ); + + static std::set<Verbosity> getSupportedVerbosities(); + + void assertionStarting(AssertionInfo const&) override; + bool assertionEnded(AssertionStats const&) override; + }; + +} // end namespace Catch + +// end catch_reporter_bases.hpp +// start catch_console_colour.h + +namespace Catch { + + struct Colour { + enum Code { + None = 0, + + White, + Red, + Green, + Blue, + Cyan, + Yellow, + Grey, + + Bright = 0x10, + + BrightRed = Bright | Red, + BrightGreen = Bright | Green, + LightGrey = Bright | Grey, + BrightWhite = Bright | White, + BrightYellow = Bright | Yellow, + + // By intention + FileName = LightGrey, + Warning = BrightYellow, + ResultError = BrightRed, + ResultSuccess = BrightGreen, + ResultExpectedFailure = Warning, + + Error = BrightRed, + Success = Green, + + OriginalExpression = Cyan, + ReconstructedExpression = BrightYellow, + + SecondaryText = LightGrey, + Headers = White + }; + + // Use constructed object for RAII guard + Colour( Code _colourCode ); + Colour( Colour&& other ) noexcept; + Colour& operator=( Colour&& other ) noexcept; + ~Colour(); + + // Use static method for one-shot changes + static void use( Code _colourCode ); + + private: + bool m_moved = false; + }; + + std::ostream& operator << ( std::ostream& os, Colour const& ); + +} // end namespace Catch + +// end catch_console_colour.h +// start catch_reporter_registrars.hpp + + +namespace Catch { + + template<typename T> + class ReporterRegistrar { + + class ReporterFactory : public IReporterFactory { + + IStreamingReporterPtr create( ReporterConfig const& config ) const override { + return std::unique_ptr<T>( new T( config ) ); + } + + std::string getDescription() const override { + return T::getDescription(); + } + }; + + public: + + explicit ReporterRegistrar( std::string const& name ) { + getMutableRegistryHub().registerReporter( name, std::make_shared<ReporterFactory>() ); + } + }; + + template<typename T> + class ListenerRegistrar { + + class ListenerFactory : public IReporterFactory { + + IStreamingReporterPtr create( ReporterConfig const& config ) const override { + return std::unique_ptr<T>( new T( config ) ); + } + std::string getDescription() const override { + return std::string(); + } + }; + + public: + + ListenerRegistrar() { + getMutableRegistryHub().registerListener( std::make_shared<ListenerFactory>() ); + } + }; +} + +#if !defined(CATCH_CONFIG_DISABLE) + +#define CATCH_REGISTER_REPORTER( name, reporterType ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::ReporterRegistrar<reporterType> catch_internal_RegistrarFor##reporterType( name ); } \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + +#define CATCH_REGISTER_LISTENER( listenerType ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::ListenerRegistrar<listenerType> catch_internal_RegistrarFor##listenerType; } \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION +#else // CATCH_CONFIG_DISABLE + +#define CATCH_REGISTER_REPORTER(name, reporterType) +#define CATCH_REGISTER_LISTENER(listenerType) + +#endif // CATCH_CONFIG_DISABLE + +// end catch_reporter_registrars.hpp +// Allow users to base their work off existing reporters +// start catch_reporter_compact.h + +namespace Catch { + + struct CompactReporter : StreamingReporterBase<CompactReporter> { + + using StreamingReporterBase::StreamingReporterBase; + + ~CompactReporter() override; + + static std::string getDescription(); + + void noMatchingTestCases(std::string const& spec) override; + + void assertionStarting(AssertionInfo const&) override; + + bool assertionEnded(AssertionStats const& _assertionStats) override; + + void sectionEnded(SectionStats const& _sectionStats) override; + + void testRunEnded(TestRunStats const& _testRunStats) override; + + }; + +} // end namespace Catch + +// end catch_reporter_compact.h +// start catch_reporter_console.h + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch + // Note that 4062 (not all labels are handled + // and default is missing) is enabled +#endif + +namespace Catch { + // Fwd decls + struct SummaryColumn; + class TablePrinter; + + struct ConsoleReporter : StreamingReporterBase<ConsoleReporter> { + std::unique_ptr<TablePrinter> m_tablePrinter; + + ConsoleReporter(ReporterConfig const& config); + ~ConsoleReporter() override; + static std::string getDescription(); + + void noMatchingTestCases(std::string const& spec) override; + + void reportInvalidArguments(std::string const&arg) override; + + void assertionStarting(AssertionInfo const&) override; + + bool assertionEnded(AssertionStats const& _assertionStats) override; + + void sectionStarting(SectionInfo const& _sectionInfo) override; + void sectionEnded(SectionStats const& _sectionStats) override; + +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) + void benchmarkPreparing(std::string const& name) override; + void benchmarkStarting(BenchmarkInfo const& info) override; + void benchmarkEnded(BenchmarkStats<> const& stats) override; + void benchmarkFailed(std::string const& error) override; +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING + + void testCaseEnded(TestCaseStats const& _testCaseStats) override; + void testGroupEnded(TestGroupStats const& _testGroupStats) override; + void testRunEnded(TestRunStats const& _testRunStats) override; + void testRunStarting(TestRunInfo const& _testRunInfo) override; + private: + + void lazyPrint(); + + void lazyPrintWithoutClosingBenchmarkTable(); + void lazyPrintRunInfo(); + void lazyPrintGroupInfo(); + void printTestCaseAndSectionHeader(); + + void printClosedHeader(std::string const& _name); + void printOpenHeader(std::string const& _name); + + // if string has a : in first line will set indent to follow it on + // subsequent lines + void printHeaderString(std::string const& _string, std::size_t indent = 0); + + void printTotals(Totals const& totals); + void printSummaryRow(std::string const& label, std::vector<SummaryColumn> const& cols, std::size_t row); + + void printTotalsDivider(Totals const& totals); + void printSummaryDivider(); + void printTestFilters(); + + private: + bool m_headerPrinted = false; + }; + +} // end namespace Catch + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +// end catch_reporter_console.h +// start catch_reporter_junit.h + +// start catch_xmlwriter.h + +#include <vector> + +namespace Catch { + enum class XmlFormatting { + None = 0x00, + Indent = 0x01, + Newline = 0x02, + }; + + XmlFormatting operator | (XmlFormatting lhs, XmlFormatting rhs); + XmlFormatting operator & (XmlFormatting lhs, XmlFormatting rhs); + + class XmlEncode { + public: + enum ForWhat { ForTextNodes, ForAttributes }; + + XmlEncode( std::string const& str, ForWhat forWhat = ForTextNodes ); + + void encodeTo( std::ostream& os ) const; + + friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ); + + private: + std::string m_str; + ForWhat m_forWhat; + }; + + class XmlWriter { + public: + + class ScopedElement { + public: + ScopedElement( XmlWriter* writer, XmlFormatting fmt ); + + ScopedElement( ScopedElement&& other ) noexcept; + ScopedElement& operator=( ScopedElement&& other ) noexcept; + + ~ScopedElement(); + + ScopedElement& writeText( std::string const& text, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent ); + + template<typename T> + ScopedElement& writeAttribute( std::string const& name, T const& attribute ) { + m_writer->writeAttribute( name, attribute ); + return *this; + } + + private: + mutable XmlWriter* m_writer = nullptr; + XmlFormatting m_fmt; + }; + + XmlWriter( std::ostream& os = Catch::cout() ); + ~XmlWriter(); + + XmlWriter( XmlWriter const& ) = delete; + XmlWriter& operator=( XmlWriter const& ) = delete; + + XmlWriter& startElement( std::string const& name, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent); + + ScopedElement scopedElement( std::string const& name, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent); + + XmlWriter& endElement(XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent); + + XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ); + + XmlWriter& writeAttribute( std::string const& name, bool attribute ); + + template<typename T> + XmlWriter& writeAttribute( std::string const& name, T const& attribute ) { + ReusableStringStream rss; + rss << attribute; + return writeAttribute( name, rss.str() ); + } + + XmlWriter& writeText( std::string const& text, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent); + + XmlWriter& writeComment(std::string const& text, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent); + + void writeStylesheetRef( std::string const& url ); + + XmlWriter& writeBlankLine(); + + void ensureTagClosed(); + + private: + + void applyFormatting(XmlFormatting fmt); + + void writeDeclaration(); + + void newlineIfNecessary(); + + bool m_tagIsOpen = false; + bool m_needsNewline = false; + std::vector<std::string> m_tags; + std::string m_indent; + std::ostream& m_os; + }; + +} + +// end catch_xmlwriter.h +namespace Catch { + + class JunitReporter : public CumulativeReporterBase<JunitReporter> { + public: + JunitReporter(ReporterConfig const& _config); + + ~JunitReporter() override; + + static std::string getDescription(); + + void noMatchingTestCases(std::string const& /*spec*/) override; + + void testRunStarting(TestRunInfo const& runInfo) override; + + void testGroupStarting(GroupInfo const& groupInfo) override; + + void testCaseStarting(TestCaseInfo const& testCaseInfo) override; + bool assertionEnded(AssertionStats const& assertionStats) override; + + void testCaseEnded(TestCaseStats const& testCaseStats) override; + + void testGroupEnded(TestGroupStats const& testGroupStats) override; + + void testRunEndedCumulative() override; + + void writeGroup(TestGroupNode const& groupNode, double suiteTime); + + void writeTestCase(TestCaseNode const& testCaseNode); + + void writeSection( std::string const& className, + std::string const& rootName, + SectionNode const& sectionNode, + bool testOkToFail ); + + void writeAssertions(SectionNode const& sectionNode); + void writeAssertion(AssertionStats const& stats); + + XmlWriter xml; + Timer suiteTimer; + std::string stdOutForSuite; + std::string stdErrForSuite; + unsigned int unexpectedExceptions = 0; + bool m_okToFail = false; + }; + +} // end namespace Catch + +// end catch_reporter_junit.h +// start catch_reporter_xml.h + +namespace Catch { + class XmlReporter : public StreamingReporterBase<XmlReporter> { + public: + XmlReporter(ReporterConfig const& _config); + + ~XmlReporter() override; + + static std::string getDescription(); + + virtual std::string getStylesheetRef() const; + + void writeSourceInfo(SourceLineInfo const& sourceInfo); + + public: // StreamingReporterBase + + void noMatchingTestCases(std::string const& s) override; + + void testRunStarting(TestRunInfo const& testInfo) override; + + void testGroupStarting(GroupInfo const& groupInfo) override; + + void testCaseStarting(TestCaseInfo const& testInfo) override; + + void sectionStarting(SectionInfo const& sectionInfo) override; + + void assertionStarting(AssertionInfo const&) override; + + bool assertionEnded(AssertionStats const& assertionStats) override; + + void sectionEnded(SectionStats const& sectionStats) override; + + void testCaseEnded(TestCaseStats const& testCaseStats) override; + + void testGroupEnded(TestGroupStats const& testGroupStats) override; + + void testRunEnded(TestRunStats const& testRunStats) override; + +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) + void benchmarkPreparing(std::string const& name) override; + void benchmarkStarting(BenchmarkInfo const&) override; + void benchmarkEnded(BenchmarkStats<> const&) override; + void benchmarkFailed(std::string const&) override; +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING + + private: + Timer m_testCaseTimer; + XmlWriter m_xml; + int m_sectionDepth = 0; + }; + +} // end namespace Catch + +// end catch_reporter_xml.h + +// end catch_external_interfaces.h +#endif + +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) +// start catch_benchmarking_all.hpp + +// A proxy header that includes all of the benchmarking headers to allow +// concise include of the benchmarking features. You should prefer the +// individual includes in standard use. + +// start catch_benchmark.hpp + + // Benchmark + +// start catch_chronometer.hpp + +// User-facing chronometer + + +// start catch_clock.hpp + +// Clocks + + +#include <chrono> +#include <ratio> + +namespace Catch { + namespace Benchmark { + template <typename Clock> + using ClockDuration = typename Clock::duration; + template <typename Clock> + using FloatDuration = std::chrono::duration<double, typename Clock::period>; + + template <typename Clock> + using TimePoint = typename Clock::time_point; + + using default_clock = std::chrono::steady_clock; + + template <typename Clock> + struct now { + TimePoint<Clock> operator()() const { + return Clock::now(); + } + }; + + using fp_seconds = std::chrono::duration<double, std::ratio<1>>; + } // namespace Benchmark +} // namespace Catch + +// end catch_clock.hpp +// start catch_optimizer.hpp + + // Hinting the optimizer + + +#if defined(_MSC_VER) +# include <atomic> // atomic_thread_fence +#endif + +namespace Catch { + namespace Benchmark { +#if defined(__GNUC__) || defined(__clang__) + template <typename T> + inline void keep_memory(T* p) { + asm volatile("" : : "g"(p) : "memory"); + } + inline void keep_memory() { + asm volatile("" : : : "memory"); + } + + namespace Detail { + inline void optimizer_barrier() { keep_memory(); } + } // namespace Detail +#elif defined(_MSC_VER) + +#pragma optimize("", off) + template <typename T> + inline void keep_memory(T* p) { + // thanks @milleniumbug + *reinterpret_cast<char volatile*>(p) = *reinterpret_cast<char const volatile*>(p); + } + // TODO equivalent keep_memory() +#pragma optimize("", on) + + namespace Detail { + inline void optimizer_barrier() { + std::atomic_thread_fence(std::memory_order_seq_cst); + } + } // namespace Detail + +#endif + + template <typename T> + inline void deoptimize_value(T&& x) { + keep_memory(&x); + } + + template <typename Fn, typename... Args> + inline auto invoke_deoptimized(Fn&& fn, Args&&... args) -> typename std::enable_if<!std::is_same<void, decltype(fn(args...))>::value>::type { + deoptimize_value(std::forward<Fn>(fn) (std::forward<Args...>(args...))); + } + + template <typename Fn, typename... Args> + inline auto invoke_deoptimized(Fn&& fn, Args&&... args) -> typename std::enable_if<std::is_same<void, decltype(fn(args...))>::value>::type { + std::forward<Fn>(fn) (std::forward<Args...>(args...)); + } + } // namespace Benchmark +} // namespace Catch + +// end catch_optimizer.hpp +// start catch_complete_invoke.hpp + +// Invoke with a special case for void + + +#include <type_traits> +#include <utility> + +namespace Catch { + namespace Benchmark { + namespace Detail { + template <typename T> + struct CompleteType { using type = T; }; + template <> + struct CompleteType<void> { struct type {}; }; + + template <typename T> + using CompleteType_t = typename CompleteType<T>::type; + + template <typename Result> + struct CompleteInvoker { + template <typename Fun, typename... Args> + static Result invoke(Fun&& fun, Args&&... args) { + return std::forward<Fun>(fun)(std::forward<Args>(args)...); + } + }; + template <> + struct CompleteInvoker<void> { + template <typename Fun, typename... Args> + static CompleteType_t<void> invoke(Fun&& fun, Args&&... args) { + std::forward<Fun>(fun)(std::forward<Args>(args)...); + return {}; + } + }; + + // invoke and not return void :( + template <typename Fun, typename... Args> + CompleteType_t<FunctionReturnType<Fun, Args...>> complete_invoke(Fun&& fun, Args&&... args) { + return CompleteInvoker<FunctionReturnType<Fun, Args...>>::invoke(std::forward<Fun>(fun), std::forward<Args>(args)...); + } + + const std::string benchmarkErrorMsg = "a benchmark failed to run successfully"; + } // namespace Detail + + template <typename Fun> + Detail::CompleteType_t<FunctionReturnType<Fun>> user_code(Fun&& fun) { + CATCH_TRY{ + return Detail::complete_invoke(std::forward<Fun>(fun)); + } CATCH_CATCH_ALL{ + getResultCapture().benchmarkFailed(translateActiveException()); + CATCH_RUNTIME_ERROR(Detail::benchmarkErrorMsg); + } + } + } // namespace Benchmark +} // namespace Catch + +// end catch_complete_invoke.hpp +namespace Catch { + namespace Benchmark { + namespace Detail { + struct ChronometerConcept { + virtual void start() = 0; + virtual void finish() = 0; + virtual ~ChronometerConcept() = default; + }; + template <typename Clock> + struct ChronometerModel final : public ChronometerConcept { + void start() override { started = Clock::now(); } + void finish() override { finished = Clock::now(); } + + ClockDuration<Clock> elapsed() const { return finished - started; } + + TimePoint<Clock> started; + TimePoint<Clock> finished; + }; + } // namespace Detail + + struct Chronometer { + public: + template <typename Fun> + void measure(Fun&& fun) { measure(std::forward<Fun>(fun), is_callable<Fun(int)>()); } + + int runs() const { return k; } + + Chronometer(Detail::ChronometerConcept& meter, int k) + : impl(&meter) + , k(k) {} + + private: + template <typename Fun> + void measure(Fun&& fun, std::false_type) { + measure([&fun](int) { return fun(); }, std::true_type()); + } + + template <typename Fun> + void measure(Fun&& fun, std::true_type) { + Detail::optimizer_barrier(); + impl->start(); + for (int i = 0; i < k; ++i) invoke_deoptimized(fun, i); + impl->finish(); + Detail::optimizer_barrier(); + } + + Detail::ChronometerConcept* impl; + int k; + }; + } // namespace Benchmark +} // namespace Catch + +// end catch_chronometer.hpp +// start catch_environment.hpp + +// Environment information + + +namespace Catch { + namespace Benchmark { + template <typename Duration> + struct EnvironmentEstimate { + Duration mean; + OutlierClassification outliers; + + template <typename Duration2> + operator EnvironmentEstimate<Duration2>() const { + return { mean, outliers }; + } + }; + template <typename Clock> + struct Environment { + using clock_type = Clock; + EnvironmentEstimate<FloatDuration<Clock>> clock_resolution; + EnvironmentEstimate<FloatDuration<Clock>> clock_cost; + }; + } // namespace Benchmark +} // namespace Catch + +// end catch_environment.hpp +// start catch_execution_plan.hpp + + // Execution plan + + +// start catch_benchmark_function.hpp + + // Dumb std::function implementation for consistent call overhead + + +#include <cassert> +#include <type_traits> +#include <utility> +#include <memory> + +namespace Catch { + namespace Benchmark { + namespace Detail { + template <typename T> + using Decay = typename std::decay<T>::type; + template <typename T, typename U> + struct is_related + : std::is_same<Decay<T>, Decay<U>> {}; + + /// We need to reinvent std::function because every piece of code that might add overhead + /// in a measurement context needs to have consistent performance characteristics so that we + /// can account for it in the measurement. + /// Implementations of std::function with optimizations that aren't always applicable, like + /// small buffer optimizations, are not uncommon. + /// This is effectively an implementation of std::function without any such optimizations; + /// it may be slow, but it is consistently slow. + struct BenchmarkFunction { + private: + struct callable { + virtual void call(Chronometer meter) const = 0; + virtual callable* clone() const = 0; + virtual ~callable() = default; + }; + template <typename Fun> + struct model : public callable { + model(Fun&& fun) : fun(std::move(fun)) {} + model(Fun const& fun) : fun(fun) {} + + model<Fun>* clone() const override { return new model<Fun>(*this); } + + void call(Chronometer meter) const override { + call(meter, is_callable<Fun(Chronometer)>()); + } + void call(Chronometer meter, std::true_type) const { + fun(meter); + } + void call(Chronometer meter, std::false_type) const { + meter.measure(fun); + } + + Fun fun; + }; + + struct do_nothing { void operator()() const {} }; + + template <typename T> + BenchmarkFunction(model<T>* c) : f(c) {} + + public: + BenchmarkFunction() + : f(new model<do_nothing>{ {} }) {} + + template <typename Fun, + typename std::enable_if<!is_related<Fun, BenchmarkFunction>::value, int>::type = 0> + BenchmarkFunction(Fun&& fun) + : f(new model<typename std::decay<Fun>::type>(std::forward<Fun>(fun))) {} + + BenchmarkFunction(BenchmarkFunction&& that) + : f(std::move(that.f)) {} + + BenchmarkFunction(BenchmarkFunction const& that) + : f(that.f->clone()) {} + + BenchmarkFunction& operator=(BenchmarkFunction&& that) { + f = std::move(that.f); + return *this; + } + + BenchmarkFunction& operator=(BenchmarkFunction const& that) { + f.reset(that.f->clone()); + return *this; + } + + void operator()(Chronometer meter) const { f->call(meter); } + + private: + std::unique_ptr<callable> f; + }; + } // namespace Detail + } // namespace Benchmark +} // namespace Catch + +// end catch_benchmark_function.hpp +// start catch_repeat.hpp + +// repeat algorithm + + +#include <type_traits> +#include <utility> + +namespace Catch { + namespace Benchmark { + namespace Detail { + template <typename Fun> + struct repeater { + void operator()(int k) const { + for (int i = 0; i < k; ++i) { + fun(); + } + } + Fun fun; + }; + template <typename Fun> + repeater<typename std::decay<Fun>::type> repeat(Fun&& fun) { + return { std::forward<Fun>(fun) }; + } + } // namespace Detail + } // namespace Benchmark +} // namespace Catch + +// end catch_repeat.hpp +// start catch_run_for_at_least.hpp + +// Run a function for a minimum amount of time + + +// start catch_measure.hpp + +// Measure + + +// start catch_timing.hpp + +// Timing + + +#include <tuple> +#include <type_traits> + +namespace Catch { + namespace Benchmark { + template <typename Duration, typename Result> + struct Timing { + Duration elapsed; + Result result; + int iterations; + }; + template <typename Clock, typename Func, typename... Args> + using TimingOf = Timing<ClockDuration<Clock>, Detail::CompleteType_t<FunctionReturnType<Func, Args...>>>; + } // namespace Benchmark +} // namespace Catch + +// end catch_timing.hpp +#include <utility> + +namespace Catch { + namespace Benchmark { + namespace Detail { + template <typename Clock, typename Fun, typename... Args> + TimingOf<Clock, Fun, Args...> measure(Fun&& fun, Args&&... args) { + auto start = Clock::now(); + auto&& r = Detail::complete_invoke(fun, std::forward<Args>(args)...); + auto end = Clock::now(); + auto delta = end - start; + return { delta, std::forward<decltype(r)>(r), 1 }; + } + } // namespace Detail + } // namespace Benchmark +} // namespace Catch + +// end catch_measure.hpp +#include <utility> +#include <type_traits> + +namespace Catch { + namespace Benchmark { + namespace Detail { + template <typename Clock, typename Fun> + TimingOf<Clock, Fun, int> measure_one(Fun&& fun, int iters, std::false_type) { + return Detail::measure<Clock>(fun, iters); + } + template <typename Clock, typename Fun> + TimingOf<Clock, Fun, Chronometer> measure_one(Fun&& fun, int iters, std::true_type) { + Detail::ChronometerModel<Clock> meter; + auto&& result = Detail::complete_invoke(fun, Chronometer(meter, iters)); + + return { meter.elapsed(), std::move(result), iters }; + } + + template <typename Clock, typename Fun> + using run_for_at_least_argument_t = typename std::conditional<is_callable<Fun(Chronometer)>::value, Chronometer, int>::type; + + struct optimized_away_error : std::exception { + const char* what() const noexcept override { + return "could not measure benchmark, maybe it was optimized away"; + } + }; + + template <typename Clock, typename Fun> + TimingOf<Clock, Fun, run_for_at_least_argument_t<Clock, Fun>> run_for_at_least(ClockDuration<Clock> how_long, int seed, Fun&& fun) { + auto iters = seed; + while (iters < (1 << 30)) { + auto&& Timing = measure_one<Clock>(fun, iters, is_callable<Fun(Chronometer)>()); + + if (Timing.elapsed >= how_long) { + return { Timing.elapsed, std::move(Timing.result), iters }; + } + iters *= 2; + } + Catch::throw_exception(optimized_away_error{}); + } + } // namespace Detail + } // namespace Benchmark +} // namespace Catch + +// end catch_run_for_at_least.hpp +#include <algorithm> +#include <iterator> + +namespace Catch { + namespace Benchmark { + template <typename Duration> + struct ExecutionPlan { + int iterations_per_sample; + Duration estimated_duration; + Detail::BenchmarkFunction benchmark; + Duration warmup_time; + int warmup_iterations; + + template <typename Duration2> + operator ExecutionPlan<Duration2>() const { + return { iterations_per_sample, estimated_duration, benchmark, warmup_time, warmup_iterations }; + } + + template <typename Clock> + std::vector<FloatDuration<Clock>> run(const IConfig &cfg, Environment<FloatDuration<Clock>> env) const { + // warmup a bit + Detail::run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(warmup_time), warmup_iterations, Detail::repeat(now<Clock>{})); + + std::vector<FloatDuration<Clock>> times; + times.reserve(cfg.benchmarkSamples()); + std::generate_n(std::back_inserter(times), cfg.benchmarkSamples(), [this, env] { + Detail::ChronometerModel<Clock> model; + this->benchmark(Chronometer(model, iterations_per_sample)); + auto sample_time = model.elapsed() - env.clock_cost.mean; + if (sample_time < FloatDuration<Clock>::zero()) sample_time = FloatDuration<Clock>::zero(); + return sample_time / iterations_per_sample; + }); + return times; + } + }; + } // namespace Benchmark +} // namespace Catch + +// end catch_execution_plan.hpp +// start catch_estimate_clock.hpp + + // Environment measurement + + +// start catch_stats.hpp + +// Statistical analysis tools + + +#include <algorithm> +#include <functional> +#include <vector> +#include <iterator> +#include <numeric> +#include <tuple> +#include <cmath> +#include <utility> +#include <cstddef> +#include <random> + +namespace Catch { + namespace Benchmark { + namespace Detail { + using sample = std::vector<double>; + + double weighted_average_quantile(int k, int q, std::vector<double>::iterator first, std::vector<double>::iterator last); + + template <typename Iterator> + OutlierClassification classify_outliers(Iterator first, Iterator last) { + std::vector<double> copy(first, last); + + auto q1 = weighted_average_quantile(1, 4, copy.begin(), copy.end()); + auto q3 = weighted_average_quantile(3, 4, copy.begin(), copy.end()); + auto iqr = q3 - q1; + auto los = q1 - (iqr * 3.); + auto lom = q1 - (iqr * 1.5); + auto him = q3 + (iqr * 1.5); + auto his = q3 + (iqr * 3.); + + OutlierClassification o; + for (; first != last; ++first) { + auto&& t = *first; + if (t < los) ++o.low_severe; + else if (t < lom) ++o.low_mild; + else if (t > his) ++o.high_severe; + else if (t > him) ++o.high_mild; + ++o.samples_seen; + } + return o; + } + + template <typename Iterator> + double mean(Iterator first, Iterator last) { + auto count = last - first; + double sum = std::accumulate(first, last, 0.); + return sum / count; + } + + template <typename URng, typename Iterator, typename Estimator> + sample resample(URng& rng, int resamples, Iterator first, Iterator last, Estimator& estimator) { + auto n = last - first; + std::uniform_int_distribution<decltype(n)> dist(0, n - 1); + + sample out; + out.reserve(resamples); + std::generate_n(std::back_inserter(out), resamples, [n, first, &estimator, &dist, &rng] { + std::vector<double> resampled; + resampled.reserve(n); + std::generate_n(std::back_inserter(resampled), n, [first, &dist, &rng] { return first[dist(rng)]; }); + return estimator(resampled.begin(), resampled.end()); + }); + std::sort(out.begin(), out.end()); + return out; + } + + template <typename Estimator, typename Iterator> + sample jackknife(Estimator&& estimator, Iterator first, Iterator last) { + auto n = last - first; + auto second = std::next(first); + sample results; + results.reserve(n); + + for (auto it = first; it != last; ++it) { + std::iter_swap(it, first); + results.push_back(estimator(second, last)); + } + + return results; + } + + inline double normal_cdf(double x) { + return std::erfc(-x / std::sqrt(2.0)) / 2.0; + } + + double erfc_inv(double x); + + double normal_quantile(double p); + + template <typename Iterator, typename Estimator> + Estimate<double> bootstrap(double confidence_level, Iterator first, Iterator last, sample const& resample, Estimator&& estimator) { + auto n_samples = last - first; + + double point = estimator(first, last); + // Degenerate case with a single sample + if (n_samples == 1) return { point, point, point, confidence_level }; + + sample jack = jackknife(estimator, first, last); + double jack_mean = mean(jack.begin(), jack.end()); + double sum_squares, sum_cubes; + std::tie(sum_squares, sum_cubes) = std::accumulate(jack.begin(), jack.end(), std::make_pair(0., 0.), [jack_mean](std::pair<double, double> sqcb, double x) -> std::pair<double, double> { + auto d = jack_mean - x; + auto d2 = d * d; + auto d3 = d2 * d; + return { sqcb.first + d2, sqcb.second + d3 }; + }); + + double accel = sum_cubes / (6 * std::pow(sum_squares, 1.5)); + int n = static_cast<int>(resample.size()); + double prob_n = std::count_if(resample.begin(), resample.end(), [point](double x) { return x < point; }) / (double)n; + // degenerate case with uniform samples + if (prob_n == 0) return { point, point, point, confidence_level }; + + double bias = normal_quantile(prob_n); + double z1 = normal_quantile((1. - confidence_level) / 2.); + + auto cumn = [n](double x) -> int { + return std::lround(normal_cdf(x) * n); }; + auto a = [bias, accel](double b) { return bias + b / (1. - accel * b); }; + double b1 = bias + z1; + double b2 = bias - z1; + double a1 = a(b1); + double a2 = a(b2); + auto lo = (std::max)(cumn(a1), 0); + auto hi = (std::min)(cumn(a2), n - 1); + + return { point, resample[lo], resample[hi], confidence_level }; + } + + double outlier_variance(Estimate<double> mean, Estimate<double> stddev, int n); + + struct bootstrap_analysis { + Estimate<double> mean; + Estimate<double> standard_deviation; + double outlier_variance; + }; + + bootstrap_analysis analyse_samples(double confidence_level, int n_resamples, std::vector<double>::iterator first, std::vector<double>::iterator last); + } // namespace Detail + } // namespace Benchmark +} // namespace Catch + +// end catch_stats.hpp +#include <algorithm> +#include <iterator> +#include <tuple> +#include <vector> +#include <cmath> + +namespace Catch { + namespace Benchmark { + namespace Detail { + template <typename Clock> + std::vector<double> resolution(int k) { + std::vector<TimePoint<Clock>> times; + times.reserve(k + 1); + std::generate_n(std::back_inserter(times), k + 1, now<Clock>{}); + + std::vector<double> deltas; + deltas.reserve(k); + std::transform(std::next(times.begin()), times.end(), times.begin(), + std::back_inserter(deltas), + [](TimePoint<Clock> a, TimePoint<Clock> b) { return static_cast<double>((a - b).count()); }); + + return deltas; + } + + const auto warmup_iterations = 10000; + const auto warmup_time = std::chrono::milliseconds(100); + const auto minimum_ticks = 1000; + const auto warmup_seed = 10000; + const auto clock_resolution_estimation_time = std::chrono::milliseconds(500); + const auto clock_cost_estimation_time_limit = std::chrono::seconds(1); + const auto clock_cost_estimation_tick_limit = 100000; + const auto clock_cost_estimation_time = std::chrono::milliseconds(10); + const auto clock_cost_estimation_iterations = 10000; + + template <typename Clock> + int warmup() { + return run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(warmup_time), warmup_seed, &resolution<Clock>) + .iterations; + } + template <typename Clock> + EnvironmentEstimate<FloatDuration<Clock>> estimate_clock_resolution(int iterations) { + auto r = run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(clock_resolution_estimation_time), iterations, &resolution<Clock>) + .result; + return { + FloatDuration<Clock>(mean(r.begin(), r.end())), + classify_outliers(r.begin(), r.end()), + }; + } + template <typename Clock> + EnvironmentEstimate<FloatDuration<Clock>> estimate_clock_cost(FloatDuration<Clock> resolution) { + auto time_limit = (std::min)( + resolution * clock_cost_estimation_tick_limit, + FloatDuration<Clock>(clock_cost_estimation_time_limit)); + auto time_clock = [](int k) { + return Detail::measure<Clock>([k] { + for (int i = 0; i < k; ++i) { + volatile auto ignored = Clock::now(); + (void)ignored; + } + }).elapsed; + }; + time_clock(1); + int iters = clock_cost_estimation_iterations; + auto&& r = run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(clock_cost_estimation_time), iters, time_clock); + std::vector<double> times; + int nsamples = static_cast<int>(std::ceil(time_limit / r.elapsed)); + times.reserve(nsamples); + std::generate_n(std::back_inserter(times), nsamples, [time_clock, &r] { + return static_cast<double>((time_clock(r.iterations) / r.iterations).count()); + }); + return { + FloatDuration<Clock>(mean(times.begin(), times.end())), + classify_outliers(times.begin(), times.end()), + }; + } + + template <typename Clock> + Environment<FloatDuration<Clock>> measure_environment() { + static Environment<FloatDuration<Clock>>* env = nullptr; + if (env) { + return *env; + } + + auto iters = Detail::warmup<Clock>(); + auto resolution = Detail::estimate_clock_resolution<Clock>(iters); + auto cost = Detail::estimate_clock_cost<Clock>(resolution.mean); + + env = new Environment<FloatDuration<Clock>>{ resolution, cost }; + return *env; + } + } // namespace Detail + } // namespace Benchmark +} // namespace Catch + +// end catch_estimate_clock.hpp +// start catch_analyse.hpp + + // Run and analyse one benchmark + + +// start catch_sample_analysis.hpp + +// Benchmark results + + +#include <algorithm> +#include <vector> +#include <string> +#include <iterator> + +namespace Catch { + namespace Benchmark { + template <typename Duration> + struct SampleAnalysis { + std::vector<Duration> samples; + Estimate<Duration> mean; + Estimate<Duration> standard_deviation; + OutlierClassification outliers; + double outlier_variance; + + template <typename Duration2> + operator SampleAnalysis<Duration2>() const { + std::vector<Duration2> samples2; + samples2.reserve(samples.size()); + std::transform(samples.begin(), samples.end(), std::back_inserter(samples2), [](Duration d) { return Duration2(d); }); + return { + std::move(samples2), + mean, + standard_deviation, + outliers, + outlier_variance, + }; + } + }; + } // namespace Benchmark +} // namespace Catch + +// end catch_sample_analysis.hpp +#include <algorithm> +#include <iterator> +#include <vector> + +namespace Catch { + namespace Benchmark { + namespace Detail { + template <typename Duration, typename Iterator> + SampleAnalysis<Duration> analyse(const IConfig &cfg, Environment<Duration>, Iterator first, Iterator last) { + if (!cfg.benchmarkNoAnalysis()) { + std::vector<double> samples; + samples.reserve(last - first); + std::transform(first, last, std::back_inserter(samples), [](Duration d) { return d.count(); }); + + auto analysis = Catch::Benchmark::Detail::analyse_samples(cfg.benchmarkConfidenceInterval(), cfg.benchmarkResamples(), samples.begin(), samples.end()); + auto outliers = Catch::Benchmark::Detail::classify_outliers(samples.begin(), samples.end()); + + auto wrap_estimate = [](Estimate<double> e) { + return Estimate<Duration> { + Duration(e.point), + Duration(e.lower_bound), + Duration(e.upper_bound), + e.confidence_interval, + }; + }; + std::vector<Duration> samples2; + samples2.reserve(samples.size()); + std::transform(samples.begin(), samples.end(), std::back_inserter(samples2), [](double d) { return Duration(d); }); + return { + std::move(samples2), + wrap_estimate(analysis.mean), + wrap_estimate(analysis.standard_deviation), + outliers, + analysis.outlier_variance, + }; + } else { + std::vector<Duration> samples; + samples.reserve(last - first); + + Duration mean = Duration(0); + int i = 0; + for (auto it = first; it < last; ++it, ++i) { + samples.push_back(Duration(*it)); + mean += Duration(*it); + } + mean /= i; + + return { + std::move(samples), + Estimate<Duration>{mean, mean, mean, 0.0}, + Estimate<Duration>{Duration(0), Duration(0), Duration(0), 0.0}, + OutlierClassification{}, + 0.0 + }; + } + } + } // namespace Detail + } // namespace Benchmark +} // namespace Catch + +// end catch_analyse.hpp +#include <algorithm> +#include <functional> +#include <string> +#include <vector> +#include <cmath> + +namespace Catch { + namespace Benchmark { + struct Benchmark { + Benchmark(std::string &&name) + : name(std::move(name)) {} + + template <class FUN> + Benchmark(std::string &&name, FUN &&func) + : fun(std::move(func)), name(std::move(name)) {} + + template <typename Clock> + ExecutionPlan<FloatDuration<Clock>> prepare(const IConfig &cfg, Environment<FloatDuration<Clock>> env) const { + auto min_time = env.clock_resolution.mean * Detail::minimum_ticks; + auto run_time = std::max(min_time, std::chrono::duration_cast<decltype(min_time)>(cfg.benchmarkWarmupTime())); + auto&& test = Detail::run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(run_time), 1, fun); + int new_iters = static_cast<int>(std::ceil(min_time * test.iterations / test.elapsed)); + return { new_iters, test.elapsed / test.iterations * new_iters * cfg.benchmarkSamples(), fun, std::chrono::duration_cast<FloatDuration<Clock>>(cfg.benchmarkWarmupTime()), Detail::warmup_iterations }; + } + + template <typename Clock = default_clock> + void run() { + IConfigPtr cfg = getCurrentContext().getConfig(); + + auto env = Detail::measure_environment<Clock>(); + + getResultCapture().benchmarkPreparing(name); + CATCH_TRY{ + auto plan = user_code([&] { + return prepare<Clock>(*cfg, env); + }); + + BenchmarkInfo info { + name, + plan.estimated_duration.count(), + plan.iterations_per_sample, + cfg->benchmarkSamples(), + cfg->benchmarkResamples(), + env.clock_resolution.mean.count(), + env.clock_cost.mean.count() + }; + + getResultCapture().benchmarkStarting(info); + + auto samples = user_code([&] { + return plan.template run<Clock>(*cfg, env); + }); + + auto analysis = Detail::analyse(*cfg, env, samples.begin(), samples.end()); + BenchmarkStats<FloatDuration<Clock>> stats{ info, analysis.samples, analysis.mean, analysis.standard_deviation, analysis.outliers, analysis.outlier_variance }; + getResultCapture().benchmarkEnded(stats); + + } CATCH_CATCH_ALL{ + if (translateActiveException() != Detail::benchmarkErrorMsg) // benchmark errors have been reported, otherwise rethrow. + std::rethrow_exception(std::current_exception()); + } + } + + // sets lambda to be used in fun *and* executes benchmark! + template <typename Fun, + typename std::enable_if<!Detail::is_related<Fun, Benchmark>::value, int>::type = 0> + Benchmark & operator=(Fun func) { + fun = Detail::BenchmarkFunction(func); + run(); + return *this; + } + + explicit operator bool() { + return true; + } + + private: + Detail::BenchmarkFunction fun; + std::string name; + }; + } +} // namespace Catch + +#define INTERNAL_CATCH_GET_1_ARG(arg1, arg2, ...) arg1 +#define INTERNAL_CATCH_GET_2_ARG(arg1, arg2, ...) arg2 + +#define INTERNAL_CATCH_BENCHMARK(BenchmarkName, name, benchmarkIndex)\ + if( Catch::Benchmark::Benchmark BenchmarkName{name} ) \ + BenchmarkName = [&](int benchmarkIndex) + +#define INTERNAL_CATCH_BENCHMARK_ADVANCED(BenchmarkName, name)\ + if( Catch::Benchmark::Benchmark BenchmarkName{name} ) \ + BenchmarkName = [&] + +// end catch_benchmark.hpp +// start catch_constructor.hpp + +// Constructor and destructor helpers + + +#include <type_traits> + +namespace Catch { + namespace Benchmark { + namespace Detail { + template <typename T, bool Destruct> + struct ObjectStorage + { + ObjectStorage() : data() {} + + ObjectStorage(const ObjectStorage& other) + { + new(&data) T(other.stored_object()); + } + + ObjectStorage(ObjectStorage&& other) + { + new(&data) T(std::move(other.stored_object())); + } + + ~ObjectStorage() { destruct_on_exit<T>(); } + + template <typename... Args> + void construct(Args&&... args) + { + new (&data) T(std::forward<Args>(args)...); + } + + template <bool AllowManualDestruction = !Destruct> + typename std::enable_if<AllowManualDestruction>::type destruct() + { + stored_object().~T(); + } + + private: + // If this is a constructor benchmark, destruct the underlying object + template <typename U> + void destruct_on_exit(typename std::enable_if<Destruct, U>::type* = 0) { destruct<true>(); } + // Otherwise, don't + template <typename U> + void destruct_on_exit(typename std::enable_if<!Destruct, U>::type* = 0) { } + + T& stored_object() { + return *static_cast<T*>(static_cast<void*>(&data)); + } + + T const& stored_object() const { + return *static_cast<T*>(static_cast<void*>(&data)); + } + + struct { alignas(T) unsigned char data[sizeof(T)]; } data; + }; + } + + template <typename T> + using storage_for = Detail::ObjectStorage<T, true>; + + template <typename T> + using destructable_object = Detail::ObjectStorage<T, false>; + } +} + +// end catch_constructor.hpp +// end catch_benchmarking_all.hpp +#endif + +#endif // ! CATCH_CONFIG_IMPL_ONLY + +#ifdef CATCH_IMPL +// start catch_impl.hpp + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wweak-vtables" +#endif + +// Keep these here for external reporters +// start catch_test_case_tracker.h + +#include <string> +#include <vector> +#include <memory> + +namespace Catch { +namespace TestCaseTracking { + + struct NameAndLocation { + std::string name; + SourceLineInfo location; + + NameAndLocation( std::string const& _name, SourceLineInfo const& _location ); + friend bool operator==(NameAndLocation const& lhs, NameAndLocation const& rhs) { + return lhs.name == rhs.name + && lhs.location == rhs.location; + } + }; + + class ITracker; + + using ITrackerPtr = std::shared_ptr<ITracker>; + + class ITracker { + NameAndLocation m_nameAndLocation; + + public: + ITracker(NameAndLocation const& nameAndLoc) : + m_nameAndLocation(nameAndLoc) + {} + + // static queries + NameAndLocation const& nameAndLocation() const { + return m_nameAndLocation; + } + + virtual ~ITracker(); + + // dynamic queries + virtual bool isComplete() const = 0; // Successfully completed or failed + virtual bool isSuccessfullyCompleted() const = 0; + virtual bool isOpen() const = 0; // Started but not complete + virtual bool hasChildren() const = 0; + virtual bool hasStarted() const = 0; + + virtual ITracker& parent() = 0; + + // actions + virtual void close() = 0; // Successfully complete + virtual void fail() = 0; + virtual void markAsNeedingAnotherRun() = 0; + + virtual void addChild( ITrackerPtr const& child ) = 0; + virtual ITrackerPtr findChild( NameAndLocation const& nameAndLocation ) = 0; + virtual void openChild() = 0; + + // Debug/ checking + virtual bool isSectionTracker() const = 0; + virtual bool isGeneratorTracker() const = 0; + }; + + class TrackerContext { + + enum RunState { + NotStarted, + Executing, + CompletedCycle + }; + + ITrackerPtr m_rootTracker; + ITracker* m_currentTracker = nullptr; + RunState m_runState = NotStarted; + + public: + + ITracker& startRun(); + void endRun(); + + void startCycle(); + void completeCycle(); + + bool completedCycle() const; + ITracker& currentTracker(); + void setCurrentTracker( ITracker* tracker ); + }; + + class TrackerBase : public ITracker { + protected: + enum CycleState { + NotStarted, + Executing, + ExecutingChildren, + NeedsAnotherRun, + CompletedSuccessfully, + Failed + }; + + using Children = std::vector<ITrackerPtr>; + TrackerContext& m_ctx; + ITracker* m_parent; + Children m_children; + CycleState m_runState = NotStarted; + + public: + TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ); + + bool isComplete() const override; + bool isSuccessfullyCompleted() const override; + bool isOpen() const override; + bool hasChildren() const override; + bool hasStarted() const override { + return m_runState != NotStarted; + } + + void addChild( ITrackerPtr const& child ) override; + + ITrackerPtr findChild( NameAndLocation const& nameAndLocation ) override; + ITracker& parent() override; + + void openChild() override; + + bool isSectionTracker() const override; + bool isGeneratorTracker() const override; + + void open(); + + void close() override; + void fail() override; + void markAsNeedingAnotherRun() override; + + private: + void moveToParent(); + void moveToThis(); + }; + + class SectionTracker : public TrackerBase { + std::vector<std::string> m_filters; + std::string m_trimmed_name; + public: + SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ); + + bool isSectionTracker() const override; + + bool isComplete() const override; + + static SectionTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ); + + void tryOpen(); + + void addInitialFilters( std::vector<std::string> const& filters ); + void addNextFilters( std::vector<std::string> const& filters ); + //! Returns filters active in this tracker + std::vector<std::string> const& getFilters() const; + //! Returns whitespace-trimmed name of the tracked section + std::string const& trimmedName() const; + }; + +} // namespace TestCaseTracking + +using TestCaseTracking::ITracker; +using TestCaseTracking::TrackerContext; +using TestCaseTracking::SectionTracker; + +} // namespace Catch + +// end catch_test_case_tracker.h + +// start catch_leak_detector.h + +namespace Catch { + + struct LeakDetector { + LeakDetector(); + ~LeakDetector(); + }; + +} +// end catch_leak_detector.h +// Cpp files will be included in the single-header file here +// start catch_stats.cpp + +// Statistical analysis tools + +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) + +#include <cassert> +#include <random> + +#if defined(CATCH_CONFIG_USE_ASYNC) +#include <future> +#endif + +namespace { + double erf_inv(double x) { + // Code accompanying the article "Approximating the erfinv function" in GPU Computing Gems, Volume 2 + double w, p; + + w = -log((1.0 - x) * (1.0 + x)); + + if (w < 6.250000) { + w = w - 3.125000; + p = -3.6444120640178196996e-21; + p = -1.685059138182016589e-19 + p * w; + p = 1.2858480715256400167e-18 + p * w; + p = 1.115787767802518096e-17 + p * w; + p = -1.333171662854620906e-16 + p * w; + p = 2.0972767875968561637e-17 + p * w; + p = 6.6376381343583238325e-15 + p * w; + p = -4.0545662729752068639e-14 + p * w; + p = -8.1519341976054721522e-14 + p * w; + p = 2.6335093153082322977e-12 + p * w; + p = -1.2975133253453532498e-11 + p * w; + p = -5.4154120542946279317e-11 + p * w; + p = 1.051212273321532285e-09 + p * w; + p = -4.1126339803469836976e-09 + p * w; + p = -2.9070369957882005086e-08 + p * w; + p = 4.2347877827932403518e-07 + p * w; + p = -1.3654692000834678645e-06 + p * w; + p = -1.3882523362786468719e-05 + p * w; + p = 0.0001867342080340571352 + p * w; + p = -0.00074070253416626697512 + p * w; + p = -0.0060336708714301490533 + p * w; + p = 0.24015818242558961693 + p * w; + p = 1.6536545626831027356 + p * w; + } else if (w < 16.000000) { + w = sqrt(w) - 3.250000; + p = 2.2137376921775787049e-09; + p = 9.0756561938885390979e-08 + p * w; + p = -2.7517406297064545428e-07 + p * w; + p = 1.8239629214389227755e-08 + p * w; + p = 1.5027403968909827627e-06 + p * w; + p = -4.013867526981545969e-06 + p * w; + p = 2.9234449089955446044e-06 + p * w; + p = 1.2475304481671778723e-05 + p * w; + p = -4.7318229009055733981e-05 + p * w; + p = 6.8284851459573175448e-05 + p * w; + p = 2.4031110387097893999e-05 + p * w; + p = -0.0003550375203628474796 + p * w; + p = 0.00095328937973738049703 + p * w; + p = -0.0016882755560235047313 + p * w; + p = 0.0024914420961078508066 + p * w; + p = -0.0037512085075692412107 + p * w; + p = 0.005370914553590063617 + p * w; + p = 1.0052589676941592334 + p * w; + p = 3.0838856104922207635 + p * w; + } else { + w = sqrt(w) - 5.000000; + p = -2.7109920616438573243e-11; + p = -2.5556418169965252055e-10 + p * w; + p = 1.5076572693500548083e-09 + p * w; + p = -3.7894654401267369937e-09 + p * w; + p = 7.6157012080783393804e-09 + p * w; + p = -1.4960026627149240478e-08 + p * w; + p = 2.9147953450901080826e-08 + p * w; + p = -6.7711997758452339498e-08 + p * w; + p = 2.2900482228026654717e-07 + p * w; + p = -9.9298272942317002539e-07 + p * w; + p = 4.5260625972231537039e-06 + p * w; + p = -1.9681778105531670567e-05 + p * w; + p = 7.5995277030017761139e-05 + p * w; + p = -0.00021503011930044477347 + p * w; + p = -0.00013871931833623122026 + p * w; + p = 1.0103004648645343977 + p * w; + p = 4.8499064014085844221 + p * w; + } + return p * x; + } + + double standard_deviation(std::vector<double>::iterator first, std::vector<double>::iterator last) { + auto m = Catch::Benchmark::Detail::mean(first, last); + double variance = std::accumulate(first, last, 0., [m](double a, double b) { + double diff = b - m; + return a + diff * diff; + }) / (last - first); + return std::sqrt(variance); + } + +} + +namespace Catch { + namespace Benchmark { + namespace Detail { + + double weighted_average_quantile(int k, int q, std::vector<double>::iterator first, std::vector<double>::iterator last) { + auto count = last - first; + double idx = (count - 1) * k / static_cast<double>(q); + int j = static_cast<int>(idx); + double g = idx - j; + std::nth_element(first, first + j, last); + auto xj = first[j]; + if (g == 0) return xj; + + auto xj1 = *std::min_element(first + (j + 1), last); + return xj + g * (xj1 - xj); + } + + double erfc_inv(double x) { + return erf_inv(1.0 - x); + } + + double normal_quantile(double p) { + static const double ROOT_TWO = std::sqrt(2.0); + + double result = 0.0; + assert(p >= 0 && p <= 1); + if (p < 0 || p > 1) { + return result; + } + + result = -erfc_inv(2.0 * p); + // result *= normal distribution standard deviation (1.0) * sqrt(2) + result *= /*sd * */ ROOT_TWO; + // result += normal disttribution mean (0) + return result; + } + + double outlier_variance(Estimate<double> mean, Estimate<double> stddev, int n) { + double sb = stddev.point; + double mn = mean.point / n; + double mg_min = mn / 2.; + double sg = (std::min)(mg_min / 4., sb / std::sqrt(n)); + double sg2 = sg * sg; + double sb2 = sb * sb; + + auto c_max = [n, mn, sb2, sg2](double x) -> double { + double k = mn - x; + double d = k * k; + double nd = n * d; + double k0 = -n * nd; + double k1 = sb2 - n * sg2 + nd; + double det = k1 * k1 - 4 * sg2 * k0; + return (int)(-2. * k0 / (k1 + std::sqrt(det))); + }; + + auto var_out = [n, sb2, sg2](double c) { + double nc = n - c; + return (nc / n) * (sb2 - nc * sg2); + }; + + return (std::min)(var_out(1), var_out((std::min)(c_max(0.), c_max(mg_min)))) / sb2; + } + + bootstrap_analysis analyse_samples(double confidence_level, int n_resamples, std::vector<double>::iterator first, std::vector<double>::iterator last) { + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS + static std::random_device entropy; + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + + auto n = static_cast<int>(last - first); // seriously, one can't use integral types without hell in C++ + + auto mean = &Detail::mean<std::vector<double>::iterator>; + auto stddev = &standard_deviation; + +#if defined(CATCH_CONFIG_USE_ASYNC) + auto Estimate = [=](double(*f)(std::vector<double>::iterator, std::vector<double>::iterator)) { + auto seed = entropy(); + return std::async(std::launch::async, [=] { + std::mt19937 rng(seed); + auto resampled = resample(rng, n_resamples, first, last, f); + return bootstrap(confidence_level, first, last, resampled, f); + }); + }; + + auto mean_future = Estimate(mean); + auto stddev_future = Estimate(stddev); + + auto mean_estimate = mean_future.get(); + auto stddev_estimate = stddev_future.get(); +#else + auto Estimate = [=](double(*f)(std::vector<double>::iterator, std::vector<double>::iterator)) { + auto seed = entropy(); + std::mt19937 rng(seed); + auto resampled = resample(rng, n_resamples, first, last, f); + return bootstrap(confidence_level, first, last, resampled, f); + }; + + auto mean_estimate = Estimate(mean); + auto stddev_estimate = Estimate(stddev); +#endif // CATCH_USE_ASYNC + + double outlier_variance = Detail::outlier_variance(mean_estimate, stddev_estimate, n); + + return { mean_estimate, stddev_estimate, outlier_variance }; + } + } // namespace Detail + } // namespace Benchmark +} // namespace Catch + +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING +// end catch_stats.cpp +// start catch_approx.cpp + +#include <cmath> +#include <limits> + +namespace { + +// Performs equivalent check of std::fabs(lhs - rhs) <= margin +// But without the subtraction to allow for INFINITY in comparison +bool marginComparison(double lhs, double rhs, double margin) { + return (lhs + margin >= rhs) && (rhs + margin >= lhs); +} + +} + +namespace Catch { +namespace Detail { + + Approx::Approx ( double value ) + : m_epsilon( std::numeric_limits<float>::epsilon()*100 ), + m_margin( 0.0 ), + m_scale( 0.0 ), + m_value( value ) + {} + + Approx Approx::custom() { + return Approx( 0 ); + } + + Approx Approx::operator-() const { + auto temp(*this); + temp.m_value = -temp.m_value; + return temp; + } + + std::string Approx::toString() const { + ReusableStringStream rss; + rss << "Approx( " << ::Catch::Detail::stringify( m_value ) << " )"; + return rss.str(); + } + + bool Approx::equalityComparisonImpl(const double other) const { + // First try with fixed margin, then compute margin based on epsilon, scale and Approx's value + // Thanks to Richard Harris for his help refining the scaled margin value + return marginComparison(m_value, other, m_margin) + || marginComparison(m_value, other, m_epsilon * (m_scale + std::fabs(std::isinf(m_value)? 0 : m_value))); + } + + void Approx::setMargin(double newMargin) { + CATCH_ENFORCE(newMargin >= 0, + "Invalid Approx::margin: " << newMargin << '.' + << " Approx::Margin has to be non-negative."); + m_margin = newMargin; + } + + void Approx::setEpsilon(double newEpsilon) { + CATCH_ENFORCE(newEpsilon >= 0 && newEpsilon <= 1.0, + "Invalid Approx::epsilon: " << newEpsilon << '.' + << " Approx::epsilon has to be in [0, 1]"); + m_epsilon = newEpsilon; + } + +} // end namespace Detail + +namespace literals { + Detail::Approx operator "" _a(long double val) { + return Detail::Approx(val); + } + Detail::Approx operator "" _a(unsigned long long val) { + return Detail::Approx(val); + } +} // end namespace literals + +std::string StringMaker<Catch::Detail::Approx>::convert(Catch::Detail::Approx const& value) { + return value.toString(); +} + +} // end namespace Catch +// end catch_approx.cpp +// start catch_assertionhandler.cpp + +// start catch_debugger.h + +namespace Catch { + bool isDebuggerActive(); +} + +#ifdef CATCH_PLATFORM_MAC + + #if defined(__i386__) || defined(__x86_64__) + #define CATCH_TRAP() __asm__("int $3\n" : : ) /* NOLINT */ + #elif defined(__aarch64__) + #define CATCH_TRAP() __asm__(".inst 0xd43e0000") + #endif + +#elif defined(CATCH_PLATFORM_IPHONE) + + // use inline assembler + #if defined(__i386__) || defined(__x86_64__) + #define CATCH_TRAP() __asm__("int $3") + #elif defined(__aarch64__) + #define CATCH_TRAP() __asm__(".inst 0xd4200000") + #elif defined(__arm__) && !defined(__thumb__) + #define CATCH_TRAP() __asm__(".inst 0xe7f001f0") + #elif defined(__arm__) && defined(__thumb__) + #define CATCH_TRAP() __asm__(".inst 0xde01") + #endif + +#elif defined(CATCH_PLATFORM_LINUX) + // If we can use inline assembler, do it because this allows us to break + // directly at the location of the failing check instead of breaking inside + // raise() called from it, i.e. one stack frame below. + #if defined(__GNUC__) && (defined(__i386) || defined(__x86_64)) + #define CATCH_TRAP() asm volatile ("int $3") /* NOLINT */ + #else // Fall back to the generic way. + #include <signal.h> + + #define CATCH_TRAP() raise(SIGTRAP) + #endif +#elif defined(_MSC_VER) + #define CATCH_TRAP() __debugbreak() +#elif defined(__MINGW32__) + extern "C" __declspec(dllimport) void __stdcall DebugBreak(); + #define CATCH_TRAP() DebugBreak() +#endif + +#ifndef CATCH_BREAK_INTO_DEBUGGER + #ifdef CATCH_TRAP + #define CATCH_BREAK_INTO_DEBUGGER() []{ if( Catch::isDebuggerActive() ) { CATCH_TRAP(); } }() + #else + #define CATCH_BREAK_INTO_DEBUGGER() []{}() + #endif +#endif + +// end catch_debugger.h +// start catch_run_context.h + +// start catch_fatal_condition.h + +#include <cassert> + +namespace Catch { + + // Wrapper for platform-specific fatal error (signals/SEH) handlers + // + // Tries to be cooperative with other handlers, and not step over + // other handlers. This means that unknown structured exceptions + // are passed on, previous signal handlers are called, and so on. + // + // Can only be instantiated once, and assumes that once a signal + // is caught, the binary will end up terminating. Thus, there + class FatalConditionHandler { + bool m_started = false; + + // Install/disengage implementation for specific platform. + // Should be if-defed to work on current platform, can assume + // engage-disengage 1:1 pairing. + void engage_platform(); + void disengage_platform(); + public: + // Should also have platform-specific implementations as needed + FatalConditionHandler(); + ~FatalConditionHandler(); + + void engage() { + assert(!m_started && "Handler cannot be installed twice."); + m_started = true; + engage_platform(); + } + + void disengage() { + assert(m_started && "Handler cannot be uninstalled without being installed first"); + m_started = false; + disengage_platform(); + } + }; + + //! Simple RAII guard for (dis)engaging the FatalConditionHandler + class FatalConditionHandlerGuard { + FatalConditionHandler* m_handler; + public: + FatalConditionHandlerGuard(FatalConditionHandler* handler): + m_handler(handler) { + m_handler->engage(); + } + ~FatalConditionHandlerGuard() { + m_handler->disengage(); + } + }; + +} // end namespace Catch + +// end catch_fatal_condition.h +#include <string> + +namespace Catch { + + struct IMutableContext; + + /////////////////////////////////////////////////////////////////////////// + + class RunContext : public IResultCapture, public IRunner { + + public: + RunContext( RunContext const& ) = delete; + RunContext& operator =( RunContext const& ) = delete; + + explicit RunContext( IConfigPtr const& _config, IStreamingReporterPtr&& reporter ); + + ~RunContext() override; + + void testGroupStarting( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount ); + void testGroupEnded( std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount ); + + Totals runTest(TestCase const& testCase); + + IConfigPtr config() const; + IStreamingReporter& reporter() const; + + public: // IResultCapture + + // Assertion handlers + void handleExpr + ( AssertionInfo const& info, + ITransientExpression const& expr, + AssertionReaction& reaction ) override; + void handleMessage + ( AssertionInfo const& info, + ResultWas::OfType resultType, + StringRef const& message, + AssertionReaction& reaction ) override; + void handleUnexpectedExceptionNotThrown + ( AssertionInfo const& info, + AssertionReaction& reaction ) override; + void handleUnexpectedInflightException + ( AssertionInfo const& info, + std::string const& message, + AssertionReaction& reaction ) override; + void handleIncomplete + ( AssertionInfo const& info ) override; + void handleNonExpr + ( AssertionInfo const &info, + ResultWas::OfType resultType, + AssertionReaction &reaction ) override; + + bool sectionStarted( SectionInfo const& sectionInfo, Counts& assertions ) override; + + void sectionEnded( SectionEndInfo const& endInfo ) override; + void sectionEndedEarly( SectionEndInfo const& endInfo ) override; + + auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& override; + +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) + void benchmarkPreparing( std::string const& name ) override; + void benchmarkStarting( BenchmarkInfo const& info ) override; + void benchmarkEnded( BenchmarkStats<> const& stats ) override; + void benchmarkFailed( std::string const& error ) override; +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING + + void pushScopedMessage( MessageInfo const& message ) override; + void popScopedMessage( MessageInfo const& message ) override; + + void emplaceUnscopedMessage( MessageBuilder const& builder ) override; + + std::string getCurrentTestName() const override; + + const AssertionResult* getLastResult() const override; + + void exceptionEarlyReported() override; + + void handleFatalErrorCondition( StringRef message ) override; + + bool lastAssertionPassed() override; + + void assertionPassed() override; + + public: + // !TBD We need to do this another way! + bool aborting() const final; + + private: + + void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr ); + void invokeActiveTestCase(); + + void resetAssertionInfo(); + bool testForMissingAssertions( Counts& assertions ); + + void assertionEnded( AssertionResult const& result ); + void reportExpr + ( AssertionInfo const &info, + ResultWas::OfType resultType, + ITransientExpression const *expr, + bool negated ); + + void populateReaction( AssertionReaction& reaction ); + + private: + + void handleUnfinishedSections(); + + TestRunInfo m_runInfo; + IMutableContext& m_context; + TestCase const* m_activeTestCase = nullptr; + ITracker* m_testCaseTracker = nullptr; + Option<AssertionResult> m_lastResult; + + IConfigPtr m_config; + Totals m_totals; + IStreamingReporterPtr m_reporter; + std::vector<MessageInfo> m_messages; + std::vector<ScopedMessage> m_messageScopes; /* Keeps owners of so-called unscoped messages. */ + AssertionInfo m_lastAssertionInfo; + std::vector<SectionEndInfo> m_unfinishedSections; + std::vector<ITracker*> m_activeSections; + TrackerContext m_trackerContext; + FatalConditionHandler m_fatalConditionhandler; + bool m_lastAssertionPassed = false; + bool m_shouldReportUnexpected = true; + bool m_includeSuccessfulResults; + }; + + void seedRng(IConfig const& config); + unsigned int rngSeed(); +} // end namespace Catch + +// end catch_run_context.h +namespace Catch { + + namespace { + auto operator <<( std::ostream& os, ITransientExpression const& expr ) -> std::ostream& { + expr.streamReconstructedExpression( os ); + return os; + } + } + + LazyExpression::LazyExpression( bool isNegated ) + : m_isNegated( isNegated ) + {} + + LazyExpression::LazyExpression( LazyExpression const& other ) : m_isNegated( other.m_isNegated ) {} + + LazyExpression::operator bool() const { + return m_transientExpression != nullptr; + } + + auto operator << ( std::ostream& os, LazyExpression const& lazyExpr ) -> std::ostream& { + if( lazyExpr.m_isNegated ) + os << "!"; + + if( lazyExpr ) { + if( lazyExpr.m_isNegated && lazyExpr.m_transientExpression->isBinaryExpression() ) + os << "(" << *lazyExpr.m_transientExpression << ")"; + else + os << *lazyExpr.m_transientExpression; + } + else { + os << "{** error - unchecked empty expression requested **}"; + } + return os; + } + + AssertionHandler::AssertionHandler + ( StringRef const& macroName, + SourceLineInfo const& lineInfo, + StringRef capturedExpression, + ResultDisposition::Flags resultDisposition ) + : m_assertionInfo{ macroName, lineInfo, capturedExpression, resultDisposition }, + m_resultCapture( getResultCapture() ) + {} + + void AssertionHandler::handleExpr( ITransientExpression const& expr ) { + m_resultCapture.handleExpr( m_assertionInfo, expr, m_reaction ); + } + void AssertionHandler::handleMessage(ResultWas::OfType resultType, StringRef const& message) { + m_resultCapture.handleMessage( m_assertionInfo, resultType, message, m_reaction ); + } + + auto AssertionHandler::allowThrows() const -> bool { + return getCurrentContext().getConfig()->allowThrows(); + } + + void AssertionHandler::complete() { + setCompleted(); + if( m_reaction.shouldDebugBreak ) { + + // If you find your debugger stopping you here then go one level up on the + // call-stack for the code that caused it (typically a failed assertion) + + // (To go back to the test and change execution, jump over the throw, next) + CATCH_BREAK_INTO_DEBUGGER(); + } + if (m_reaction.shouldThrow) { +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + throw Catch::TestFailureException(); +#else + CATCH_ERROR( "Test failure requires aborting test!" ); +#endif + } + } + void AssertionHandler::setCompleted() { + m_completed = true; + } + + void AssertionHandler::handleUnexpectedInflightException() { + m_resultCapture.handleUnexpectedInflightException( m_assertionInfo, Catch::translateActiveException(), m_reaction ); + } + + void AssertionHandler::handleExceptionThrownAsExpected() { + m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction); + } + void AssertionHandler::handleExceptionNotThrownAsExpected() { + m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction); + } + + void AssertionHandler::handleUnexpectedExceptionNotThrown() { + m_resultCapture.handleUnexpectedExceptionNotThrown( m_assertionInfo, m_reaction ); + } + + void AssertionHandler::handleThrowingCallSkipped() { + m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction); + } + + // This is the overload that takes a string and infers the Equals matcher from it + // The more general overload, that takes any string matcher, is in catch_capture_matchers.cpp + void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str, StringRef const& matcherString ) { + handleExceptionMatchExpr( handler, Matchers::Equals( str ), matcherString ); + } + +} // namespace Catch +// end catch_assertionhandler.cpp +// start catch_assertionresult.cpp + +namespace Catch { + AssertionResultData::AssertionResultData(ResultWas::OfType _resultType, LazyExpression const & _lazyExpression): + lazyExpression(_lazyExpression), + resultType(_resultType) {} + + std::string AssertionResultData::reconstructExpression() const { + + if( reconstructedExpression.empty() ) { + if( lazyExpression ) { + ReusableStringStream rss; + rss << lazyExpression; + reconstructedExpression = rss.str(); + } + } + return reconstructedExpression; + } + + AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData const& data ) + : m_info( info ), + m_resultData( data ) + {} + + // Result was a success + bool AssertionResult::succeeded() const { + return Catch::isOk( m_resultData.resultType ); + } + + // Result was a success, or failure is suppressed + bool AssertionResult::isOk() const { + return Catch::isOk( m_resultData.resultType ) || shouldSuppressFailure( m_info.resultDisposition ); + } + + ResultWas::OfType AssertionResult::getResultType() const { + return m_resultData.resultType; + } + + bool AssertionResult::hasExpression() const { + return !m_info.capturedExpression.empty(); + } + + bool AssertionResult::hasMessage() const { + return !m_resultData.message.empty(); + } + + std::string AssertionResult::getExpression() const { + // Possibly overallocating by 3 characters should be basically free + std::string expr; expr.reserve(m_info.capturedExpression.size() + 3); + if (isFalseTest(m_info.resultDisposition)) { + expr += "!("; + } + expr += m_info.capturedExpression; + if (isFalseTest(m_info.resultDisposition)) { + expr += ')'; + } + return expr; + } + + std::string AssertionResult::getExpressionInMacro() const { + std::string expr; + if( m_info.macroName.empty() ) + expr = static_cast<std::string>(m_info.capturedExpression); + else { + expr.reserve( m_info.macroName.size() + m_info.capturedExpression.size() + 4 ); + expr += m_info.macroName; + expr += "( "; + expr += m_info.capturedExpression; + expr += " )"; + } + return expr; + } + + bool AssertionResult::hasExpandedExpression() const { + return hasExpression() && getExpandedExpression() != getExpression(); + } + + std::string AssertionResult::getExpandedExpression() const { + std::string expr = m_resultData.reconstructExpression(); + return expr.empty() + ? getExpression() + : expr; + } + + std::string AssertionResult::getMessage() const { + return m_resultData.message; + } + SourceLineInfo AssertionResult::getSourceInfo() const { + return m_info.lineInfo; + } + + StringRef AssertionResult::getTestMacroName() const { + return m_info.macroName; + } + +} // end namespace Catch +// end catch_assertionresult.cpp +// start catch_capture_matchers.cpp + +namespace Catch { + + using StringMatcher = Matchers::Impl::MatcherBase<std::string>; + + // This is the general overload that takes a any string matcher + // There is another overload, in catch_assertionhandler.h/.cpp, that only takes a string and infers + // the Equals matcher (so the header does not mention matchers) + void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef const& matcherString ) { + std::string exceptionMessage = Catch::translateActiveException(); + MatchExpr<std::string, StringMatcher const&> expr( exceptionMessage, matcher, matcherString ); + handler.handleExpr( expr ); + } + +} // namespace Catch +// end catch_capture_matchers.cpp +// start catch_commandline.cpp + +// start catch_commandline.h + +// start catch_clara.h + +// Use Catch's value for console width (store Clara's off to the side, if present) +#ifdef CLARA_CONFIG_CONSOLE_WIDTH +#define CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH +#undef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH +#endif +#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH-1 + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wweak-vtables" +#pragma clang diagnostic ignored "-Wexit-time-destructors" +#pragma clang diagnostic ignored "-Wshadow" +#endif + +// start clara.hpp +// Copyright 2017 Two Blue Cubes Ltd. All rights reserved. +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// See https://github.com/philsquared/Clara for more details + +// Clara v1.1.5 + + +#ifndef CATCH_CLARA_CONFIG_CONSOLE_WIDTH +#define CATCH_CLARA_CONFIG_CONSOLE_WIDTH 80 +#endif + +#ifndef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH +#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_CLARA_CONFIG_CONSOLE_WIDTH +#endif + +#ifndef CLARA_CONFIG_OPTIONAL_TYPE +#ifdef __has_include +#if __has_include(<optional>) && __cplusplus >= 201703L +#include <optional> +#define CLARA_CONFIG_OPTIONAL_TYPE std::optional +#endif +#endif +#endif + +// ----------- #included from clara_textflow.hpp ----------- + +// TextFlowCpp +// +// A single-header library for wrapping and laying out basic text, by Phil Nash +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// This project is hosted at https://github.com/philsquared/textflowcpp + + +#include <cassert> +#include <ostream> +#include <sstream> +#include <vector> + +#ifndef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH +#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH 80 +#endif + +namespace Catch { +namespace clara { +namespace TextFlow { + +inline auto isWhitespace(char c) -> bool { + static std::string chars = " \t\n\r"; + return chars.find(c) != std::string::npos; +} +inline auto isBreakableBefore(char c) -> bool { + static std::string chars = "[({<|"; + return chars.find(c) != std::string::npos; +} +inline auto isBreakableAfter(char c) -> bool { + static std::string chars = "])}>.,:;*+-=&/\\"; + return chars.find(c) != std::string::npos; +} + +class Columns; + +class Column { + std::vector<std::string> m_strings; + size_t m_width = CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH; + size_t m_indent = 0; + size_t m_initialIndent = std::string::npos; + +public: + class iterator { + friend Column; + + Column const& m_column; + size_t m_stringIndex = 0; + size_t m_pos = 0; + + size_t m_len = 0; + size_t m_end = 0; + bool m_suffix = false; + + iterator(Column const& column, size_t stringIndex) + : m_column(column), + m_stringIndex(stringIndex) {} + + auto line() const -> std::string const& { return m_column.m_strings[m_stringIndex]; } + + auto isBoundary(size_t at) const -> bool { + assert(at > 0); + assert(at <= line().size()); + + return at == line().size() || + (isWhitespace(line()[at]) && !isWhitespace(line()[at - 1])) || + isBreakableBefore(line()[at]) || + isBreakableAfter(line()[at - 1]); + } + + void calcLength() { + assert(m_stringIndex < m_column.m_strings.size()); + + m_suffix = false; + auto width = m_column.m_width - indent(); + m_end = m_pos; + if (line()[m_pos] == '\n') { + ++m_end; + } + while (m_end < line().size() && line()[m_end] != '\n') + ++m_end; + + if (m_end < m_pos + width) { + m_len = m_end - m_pos; + } else { + size_t len = width; + while (len > 0 && !isBoundary(m_pos + len)) + --len; + while (len > 0 && isWhitespace(line()[m_pos + len - 1])) + --len; + + if (len > 0) { + m_len = len; + } else { + m_suffix = true; + m_len = width - 1; + } + } + } + + auto indent() const -> size_t { + auto initial = m_pos == 0 && m_stringIndex == 0 ? m_column.m_initialIndent : std::string::npos; + return initial == std::string::npos ? m_column.m_indent : initial; + } + + auto addIndentAndSuffix(std::string const &plain) const -> std::string { + return std::string(indent(), ' ') + (m_suffix ? plain + "-" : plain); + } + + public: + using difference_type = std::ptrdiff_t; + using value_type = std::string; + using pointer = value_type * ; + using reference = value_type & ; + using iterator_category = std::forward_iterator_tag; + + explicit iterator(Column const& column) : m_column(column) { + assert(m_column.m_width > m_column.m_indent); + assert(m_column.m_initialIndent == std::string::npos || m_column.m_width > m_column.m_initialIndent); + calcLength(); + if (m_len == 0) + m_stringIndex++; // Empty string + } + + auto operator *() const -> std::string { + assert(m_stringIndex < m_column.m_strings.size()); + assert(m_pos <= m_end); + return addIndentAndSuffix(line().substr(m_pos, m_len)); + } + + auto operator ++() -> iterator& { + m_pos += m_len; + if (m_pos < line().size() && line()[m_pos] == '\n') + m_pos += 1; + else + while (m_pos < line().size() && isWhitespace(line()[m_pos])) + ++m_pos; + + if (m_pos == line().size()) { + m_pos = 0; + ++m_stringIndex; + } + if (m_stringIndex < m_column.m_strings.size()) + calcLength(); + return *this; + } + auto operator ++(int) -> iterator { + iterator prev(*this); + operator++(); + return prev; + } + + auto operator ==(iterator const& other) const -> bool { + return + m_pos == other.m_pos && + m_stringIndex == other.m_stringIndex && + &m_column == &other.m_column; + } + auto operator !=(iterator const& other) const -> bool { + return !operator==(other); + } + }; + using const_iterator = iterator; + + explicit Column(std::string const& text) { m_strings.push_back(text); } + + auto width(size_t newWidth) -> Column& { + assert(newWidth > 0); + m_width = newWidth; + return *this; + } + auto indent(size_t newIndent) -> Column& { + m_indent = newIndent; + return *this; + } + auto initialIndent(size_t newIndent) -> Column& { + m_initialIndent = newIndent; + return *this; + } + + auto width() const -> size_t { return m_width; } + auto begin() const -> iterator { return iterator(*this); } + auto end() const -> iterator { return { *this, m_strings.size() }; } + + inline friend std::ostream& operator << (std::ostream& os, Column const& col) { + bool first = true; + for (auto line : col) { + if (first) + first = false; + else + os << "\n"; + os << line; + } + return os; + } + + auto operator + (Column const& other)->Columns; + + auto toString() const -> std::string { + std::ostringstream oss; + oss << *this; + return oss.str(); + } +}; + +class Spacer : public Column { + +public: + explicit Spacer(size_t spaceWidth) : Column("") { + width(spaceWidth); + } +}; + +class Columns { + std::vector<Column> m_columns; + +public: + + class iterator { + friend Columns; + struct EndTag {}; + + std::vector<Column> const& m_columns; + std::vector<Column::iterator> m_iterators; + size_t m_activeIterators; + + iterator(Columns const& columns, EndTag) + : m_columns(columns.m_columns), + m_activeIterators(0) { + m_iterators.reserve(m_columns.size()); + + for (auto const& col : m_columns) + m_iterators.push_back(col.end()); + } + + public: + using difference_type = std::ptrdiff_t; + using value_type = std::string; + using pointer = value_type * ; + using reference = value_type & ; + using iterator_category = std::forward_iterator_tag; + + explicit iterator(Columns const& columns) + : m_columns(columns.m_columns), + m_activeIterators(m_columns.size()) { + m_iterators.reserve(m_columns.size()); + + for (auto const& col : m_columns) + m_iterators.push_back(col.begin()); + } + + auto operator ==(iterator const& other) const -> bool { + return m_iterators == other.m_iterators; + } + auto operator !=(iterator const& other) const -> bool { + return m_iterators != other.m_iterators; + } + auto operator *() const -> std::string { + std::string row, padding; + + for (size_t i = 0; i < m_columns.size(); ++i) { + auto width = m_columns[i].width(); + if (m_iterators[i] != m_columns[i].end()) { + std::string col = *m_iterators[i]; + row += padding + col; + if (col.size() < width) + padding = std::string(width - col.size(), ' '); + else + padding = ""; + } else { + padding += std::string(width, ' '); + } + } + return row; + } + auto operator ++() -> iterator& { + for (size_t i = 0; i < m_columns.size(); ++i) { + if (m_iterators[i] != m_columns[i].end()) + ++m_iterators[i]; + } + return *this; + } + auto operator ++(int) -> iterator { + iterator prev(*this); + operator++(); + return prev; + } + }; + using const_iterator = iterator; + + auto begin() const -> iterator { return iterator(*this); } + auto end() const -> iterator { return { *this, iterator::EndTag() }; } + + auto operator += (Column const& col) -> Columns& { + m_columns.push_back(col); + return *this; + } + auto operator + (Column const& col) -> Columns { + Columns combined = *this; + combined += col; + return combined; + } + + inline friend std::ostream& operator << (std::ostream& os, Columns const& cols) { + + bool first = true; + for (auto line : cols) { + if (first) + first = false; + else + os << "\n"; + os << line; + } + return os; + } + + auto toString() const -> std::string { + std::ostringstream oss; + oss << *this; + return oss.str(); + } +}; + +inline auto Column::operator + (Column const& other) -> Columns { + Columns cols; + cols += *this; + cols += other; + return cols; +} +} + +} +} + +// ----------- end of #include from clara_textflow.hpp ----------- +// ........... back in clara.hpp + +#include <cctype> +#include <string> +#include <memory> +#include <set> +#include <algorithm> + +#if !defined(CATCH_PLATFORM_WINDOWS) && ( defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) ) +#define CATCH_PLATFORM_WINDOWS +#endif + +namespace Catch { namespace clara { +namespace detail { + + // Traits for extracting arg and return type of lambdas (for single argument lambdas) + template<typename L> + struct UnaryLambdaTraits : UnaryLambdaTraits<decltype( &L::operator() )> {}; + + template<typename ClassT, typename ReturnT, typename... Args> + struct UnaryLambdaTraits<ReturnT( ClassT::* )( Args... ) const> { + static const bool isValid = false; + }; + + template<typename ClassT, typename ReturnT, typename ArgT> + struct UnaryLambdaTraits<ReturnT( ClassT::* )( ArgT ) const> { + static const bool isValid = true; + using ArgType = typename std::remove_const<typename std::remove_reference<ArgT>::type>::type; + using ReturnType = ReturnT; + }; + + class TokenStream; + + // Transport for raw args (copied from main args, or supplied via init list for testing) + class Args { + friend TokenStream; + std::string m_exeName; + std::vector<std::string> m_args; + + public: + Args( int argc, char const* const* argv ) + : m_exeName(argv[0]), + m_args(argv + 1, argv + argc) {} + + Args( std::initializer_list<std::string> args ) + : m_exeName( *args.begin() ), + m_args( args.begin()+1, args.end() ) + {} + + auto exeName() const -> std::string { + return m_exeName; + } + }; + + // Wraps a token coming from a token stream. These may not directly correspond to strings as a single string + // may encode an option + its argument if the : or = form is used + enum class TokenType { + Option, Argument + }; + struct Token { + TokenType type; + std::string token; + }; + + inline auto isOptPrefix( char c ) -> bool { + return c == '-' +#ifdef CATCH_PLATFORM_WINDOWS + || c == '/' +#endif + ; + } + + // Abstracts iterators into args as a stream of tokens, with option arguments uniformly handled + class TokenStream { + using Iterator = std::vector<std::string>::const_iterator; + Iterator it; + Iterator itEnd; + std::vector<Token> m_tokenBuffer; + + void loadBuffer() { + m_tokenBuffer.resize( 0 ); + + // Skip any empty strings + while( it != itEnd && it->empty() ) + ++it; + + if( it != itEnd ) { + auto const &next = *it; + if( isOptPrefix( next[0] ) ) { + auto delimiterPos = next.find_first_of( " :=" ); + if( delimiterPos != std::string::npos ) { + m_tokenBuffer.push_back( { TokenType::Option, next.substr( 0, delimiterPos ) } ); + m_tokenBuffer.push_back( { TokenType::Argument, next.substr( delimiterPos + 1 ) } ); + } else { + if( next[1] != '-' && next.size() > 2 ) { + std::string opt = "- "; + for( size_t i = 1; i < next.size(); ++i ) { + opt[1] = next[i]; + m_tokenBuffer.push_back( { TokenType::Option, opt } ); + } + } else { + m_tokenBuffer.push_back( { TokenType::Option, next } ); + } + } + } else { + m_tokenBuffer.push_back( { TokenType::Argument, next } ); + } + } + } + + public: + explicit TokenStream( Args const &args ) : TokenStream( args.m_args.begin(), args.m_args.end() ) {} + + TokenStream( Iterator it, Iterator itEnd ) : it( it ), itEnd( itEnd ) { + loadBuffer(); + } + + explicit operator bool() const { + return !m_tokenBuffer.empty() || it != itEnd; + } + + auto count() const -> size_t { return m_tokenBuffer.size() + (itEnd - it); } + + auto operator*() const -> Token { + assert( !m_tokenBuffer.empty() ); + return m_tokenBuffer.front(); + } + + auto operator->() const -> Token const * { + assert( !m_tokenBuffer.empty() ); + return &m_tokenBuffer.front(); + } + + auto operator++() -> TokenStream & { + if( m_tokenBuffer.size() >= 2 ) { + m_tokenBuffer.erase( m_tokenBuffer.begin() ); + } else { + if( it != itEnd ) + ++it; + loadBuffer(); + } + return *this; + } + }; + + class ResultBase { + public: + enum Type { + Ok, LogicError, RuntimeError + }; + + protected: + ResultBase( Type type ) : m_type( type ) {} + virtual ~ResultBase() = default; + + virtual void enforceOk() const = 0; + + Type m_type; + }; + + template<typename T> + class ResultValueBase : public ResultBase { + public: + auto value() const -> T const & { + enforceOk(); + return m_value; + } + + protected: + ResultValueBase( Type type ) : ResultBase( type ) {} + + ResultValueBase( ResultValueBase const &other ) : ResultBase( other ) { + if( m_type == ResultBase::Ok ) + new( &m_value ) T( other.m_value ); + } + + ResultValueBase( Type, T const &value ) : ResultBase( Ok ) { + new( &m_value ) T( value ); + } + + auto operator=( ResultValueBase const &other ) -> ResultValueBase & { + if( m_type == ResultBase::Ok ) + m_value.~T(); + ResultBase::operator=(other); + if( m_type == ResultBase::Ok ) + new( &m_value ) T( other.m_value ); + return *this; + } + + ~ResultValueBase() override { + if( m_type == Ok ) + m_value.~T(); + } + + union { + T m_value; + }; + }; + + template<> + class ResultValueBase<void> : public ResultBase { + protected: + using ResultBase::ResultBase; + }; + + template<typename T = void> + class BasicResult : public ResultValueBase<T> { + public: + template<typename U> + explicit BasicResult( BasicResult<U> const &other ) + : ResultValueBase<T>( other.type() ), + m_errorMessage( other.errorMessage() ) + { + assert( type() != ResultBase::Ok ); + } + + template<typename U> + static auto ok( U const &value ) -> BasicResult { return { ResultBase::Ok, value }; } + static auto ok() -> BasicResult { return { ResultBase::Ok }; } + static auto logicError( std::string const &message ) -> BasicResult { return { ResultBase::LogicError, message }; } + static auto runtimeError( std::string const &message ) -> BasicResult { return { ResultBase::RuntimeError, message }; } + + explicit operator bool() const { return m_type == ResultBase::Ok; } + auto type() const -> ResultBase::Type { return m_type; } + auto errorMessage() const -> std::string { return m_errorMessage; } + + protected: + void enforceOk() const override { + + // Errors shouldn't reach this point, but if they do + // the actual error message will be in m_errorMessage + assert( m_type != ResultBase::LogicError ); + assert( m_type != ResultBase::RuntimeError ); + if( m_type != ResultBase::Ok ) + std::abort(); + } + + std::string m_errorMessage; // Only populated if resultType is an error + + BasicResult( ResultBase::Type type, std::string const &message ) + : ResultValueBase<T>(type), + m_errorMessage(message) + { + assert( m_type != ResultBase::Ok ); + } + + using ResultValueBase<T>::ResultValueBase; + using ResultBase::m_type; + }; + + enum class ParseResultType { + Matched, NoMatch, ShortCircuitAll, ShortCircuitSame + }; + + class ParseState { + public: + + ParseState( ParseResultType type, TokenStream const &remainingTokens ) + : m_type(type), + m_remainingTokens( remainingTokens ) + {} + + auto type() const -> ParseResultType { return m_type; } + auto remainingTokens() const -> TokenStream { return m_remainingTokens; } + + private: + ParseResultType m_type; + TokenStream m_remainingTokens; + }; + + using Result = BasicResult<void>; + using ParserResult = BasicResult<ParseResultType>; + using InternalParseResult = BasicResult<ParseState>; + + struct HelpColumns { + std::string left; + std::string right; + }; + + template<typename T> + inline auto convertInto( std::string const &source, T& target ) -> ParserResult { + std::stringstream ss; + ss << source; + ss >> target; + if( ss.fail() ) + return ParserResult::runtimeError( "Unable to convert '" + source + "' to destination type" ); + else + return ParserResult::ok( ParseResultType::Matched ); + } + inline auto convertInto( std::string const &source, std::string& target ) -> ParserResult { + target = source; + return ParserResult::ok( ParseResultType::Matched ); + } + inline auto convertInto( std::string const &source, bool &target ) -> ParserResult { + std::string srcLC = source; + std::transform( srcLC.begin(), srcLC.end(), srcLC.begin(), []( unsigned char c ) { return static_cast<char>( std::tolower(c) ); } ); + if (srcLC == "y" || srcLC == "1" || srcLC == "true" || srcLC == "yes" || srcLC == "on") + target = true; + else if (srcLC == "n" || srcLC == "0" || srcLC == "false" || srcLC == "no" || srcLC == "off") + target = false; + else + return ParserResult::runtimeError( "Expected a boolean value but did not recognise: '" + source + "'" ); + return ParserResult::ok( ParseResultType::Matched ); + } +#ifdef CLARA_CONFIG_OPTIONAL_TYPE + template<typename T> + inline auto convertInto( std::string const &source, CLARA_CONFIG_OPTIONAL_TYPE<T>& target ) -> ParserResult { + T temp; + auto result = convertInto( source, temp ); + if( result ) + target = std::move(temp); + return result; + } +#endif // CLARA_CONFIG_OPTIONAL_TYPE + + struct NonCopyable { + NonCopyable() = default; + NonCopyable( NonCopyable const & ) = delete; + NonCopyable( NonCopyable && ) = delete; + NonCopyable &operator=( NonCopyable const & ) = delete; + NonCopyable &operator=( NonCopyable && ) = delete; + }; + + struct BoundRef : NonCopyable { + virtual ~BoundRef() = default; + virtual auto isContainer() const -> bool { return false; } + virtual auto isFlag() const -> bool { return false; } + }; + struct BoundValueRefBase : BoundRef { + virtual auto setValue( std::string const &arg ) -> ParserResult = 0; + }; + struct BoundFlagRefBase : BoundRef { + virtual auto setFlag( bool flag ) -> ParserResult = 0; + virtual auto isFlag() const -> bool { return true; } + }; + + template<typename T> + struct BoundValueRef : BoundValueRefBase { + T &m_ref; + + explicit BoundValueRef( T &ref ) : m_ref( ref ) {} + + auto setValue( std::string const &arg ) -> ParserResult override { + return convertInto( arg, m_ref ); + } + }; + + template<typename T> + struct BoundValueRef<std::vector<T>> : BoundValueRefBase { + std::vector<T> &m_ref; + + explicit BoundValueRef( std::vector<T> &ref ) : m_ref( ref ) {} + + auto isContainer() const -> bool override { return true; } + + auto setValue( std::string const &arg ) -> ParserResult override { + T temp; + auto result = convertInto( arg, temp ); + if( result ) + m_ref.push_back( temp ); + return result; + } + }; + + struct BoundFlagRef : BoundFlagRefBase { + bool &m_ref; + + explicit BoundFlagRef( bool &ref ) : m_ref( ref ) {} + + auto setFlag( bool flag ) -> ParserResult override { + m_ref = flag; + return ParserResult::ok( ParseResultType::Matched ); + } + }; + + template<typename ReturnType> + struct LambdaInvoker { + static_assert( std::is_same<ReturnType, ParserResult>::value, "Lambda must return void or clara::ParserResult" ); + + template<typename L, typename ArgType> + static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult { + return lambda( arg ); + } + }; + + template<> + struct LambdaInvoker<void> { + template<typename L, typename ArgType> + static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult { + lambda( arg ); + return ParserResult::ok( ParseResultType::Matched ); + } + }; + + template<typename ArgType, typename L> + inline auto invokeLambda( L const &lambda, std::string const &arg ) -> ParserResult { + ArgType temp{}; + auto result = convertInto( arg, temp ); + return !result + ? result + : LambdaInvoker<typename UnaryLambdaTraits<L>::ReturnType>::invoke( lambda, temp ); + } + + template<typename L> + struct BoundLambda : BoundValueRefBase { + L m_lambda; + + static_assert( UnaryLambdaTraits<L>::isValid, "Supplied lambda must take exactly one argument" ); + explicit BoundLambda( L const &lambda ) : m_lambda( lambda ) {} + + auto setValue( std::string const &arg ) -> ParserResult override { + return invokeLambda<typename UnaryLambdaTraits<L>::ArgType>( m_lambda, arg ); + } + }; + + template<typename L> + struct BoundFlagLambda : BoundFlagRefBase { + L m_lambda; + + static_assert( UnaryLambdaTraits<L>::isValid, "Supplied lambda must take exactly one argument" ); + static_assert( std::is_same<typename UnaryLambdaTraits<L>::ArgType, bool>::value, "flags must be boolean" ); + + explicit BoundFlagLambda( L const &lambda ) : m_lambda( lambda ) {} + + auto setFlag( bool flag ) -> ParserResult override { + return LambdaInvoker<typename UnaryLambdaTraits<L>::ReturnType>::invoke( m_lambda, flag ); + } + }; + + enum class Optionality { Optional, Required }; + + struct Parser; + + class ParserBase { + public: + virtual ~ParserBase() = default; + virtual auto validate() const -> Result { return Result::ok(); } + virtual auto parse( std::string const& exeName, TokenStream const &tokens) const -> InternalParseResult = 0; + virtual auto cardinality() const -> size_t { return 1; } + + auto parse( Args const &args ) const -> InternalParseResult { + return parse( args.exeName(), TokenStream( args ) ); + } + }; + + template<typename DerivedT> + class ComposableParserImpl : public ParserBase { + public: + template<typename T> + auto operator|( T const &other ) const -> Parser; + + template<typename T> + auto operator+( T const &other ) const -> Parser; + }; + + // Common code and state for Args and Opts + template<typename DerivedT> + class ParserRefImpl : public ComposableParserImpl<DerivedT> { + protected: + Optionality m_optionality = Optionality::Optional; + std::shared_ptr<BoundRef> m_ref; + std::string m_hint; + std::string m_description; + + explicit ParserRefImpl( std::shared_ptr<BoundRef> const &ref ) : m_ref( ref ) {} + + public: + template<typename T> + ParserRefImpl( T &ref, std::string const &hint ) + : m_ref( std::make_shared<BoundValueRef<T>>( ref ) ), + m_hint( hint ) + {} + + template<typename LambdaT> + ParserRefImpl( LambdaT const &ref, std::string const &hint ) + : m_ref( std::make_shared<BoundLambda<LambdaT>>( ref ) ), + m_hint(hint) + {} + + auto operator()( std::string const &description ) -> DerivedT & { + m_description = description; + return static_cast<DerivedT &>( *this ); + } + + auto optional() -> DerivedT & { + m_optionality = Optionality::Optional; + return static_cast<DerivedT &>( *this ); + }; + + auto required() -> DerivedT & { + m_optionality = Optionality::Required; + return static_cast<DerivedT &>( *this ); + }; + + auto isOptional() const -> bool { + return m_optionality == Optionality::Optional; + } + + auto cardinality() const -> size_t override { + if( m_ref->isContainer() ) + return 0; + else + return 1; + } + + auto hint() const -> std::string { return m_hint; } + }; + + class ExeName : public ComposableParserImpl<ExeName> { + std::shared_ptr<std::string> m_name; + std::shared_ptr<BoundValueRefBase> m_ref; + + template<typename LambdaT> + static auto makeRef(LambdaT const &lambda) -> std::shared_ptr<BoundValueRefBase> { + return std::make_shared<BoundLambda<LambdaT>>( lambda) ; + } + + public: + ExeName() : m_name( std::make_shared<std::string>( "<executable>" ) ) {} + + explicit ExeName( std::string &ref ) : ExeName() { + m_ref = std::make_shared<BoundValueRef<std::string>>( ref ); + } + + template<typename LambdaT> + explicit ExeName( LambdaT const& lambda ) : ExeName() { + m_ref = std::make_shared<BoundLambda<LambdaT>>( lambda ); + } + + // The exe name is not parsed out of the normal tokens, but is handled specially + auto parse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override { + return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) ); + } + + auto name() const -> std::string { return *m_name; } + auto set( std::string const& newName ) -> ParserResult { + + auto lastSlash = newName.find_last_of( "\\/" ); + auto filename = ( lastSlash == std::string::npos ) + ? newName + : newName.substr( lastSlash+1 ); + + *m_name = filename; + if( m_ref ) + return m_ref->setValue( filename ); + else + return ParserResult::ok( ParseResultType::Matched ); + } + }; + + class Arg : public ParserRefImpl<Arg> { + public: + using ParserRefImpl::ParserRefImpl; + + auto parse( std::string const &, TokenStream const &tokens ) const -> InternalParseResult override { + auto validationResult = validate(); + if( !validationResult ) + return InternalParseResult( validationResult ); + + auto remainingTokens = tokens; + auto const &token = *remainingTokens; + if( token.type != TokenType::Argument ) + return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) ); + + assert( !m_ref->isFlag() ); + auto valueRef = static_cast<detail::BoundValueRefBase*>( m_ref.get() ); + + auto result = valueRef->setValue( remainingTokens->token ); + if( !result ) + return InternalParseResult( result ); + else + return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) ); + } + }; + + inline auto normaliseOpt( std::string const &optName ) -> std::string { +#ifdef CATCH_PLATFORM_WINDOWS + if( optName[0] == '/' ) + return "-" + optName.substr( 1 ); + else +#endif + return optName; + } + + class Opt : public ParserRefImpl<Opt> { + protected: + std::vector<std::string> m_optNames; + + public: + template<typename LambdaT> + explicit Opt( LambdaT const &ref ) : ParserRefImpl( std::make_shared<BoundFlagLambda<LambdaT>>( ref ) ) {} + + explicit Opt( bool &ref ) : ParserRefImpl( std::make_shared<BoundFlagRef>( ref ) ) {} + + template<typename LambdaT> + Opt( LambdaT const &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {} + + template<typename T> + Opt( T &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {} + + auto operator[]( std::string const &optName ) -> Opt & { + m_optNames.push_back( optName ); + return *this; + } + + auto getHelpColumns() const -> std::vector<HelpColumns> { + std::ostringstream oss; + bool first = true; + for( auto const &opt : m_optNames ) { + if (first) + first = false; + else + oss << ", "; + oss << opt; + } + if( !m_hint.empty() ) + oss << " <" << m_hint << ">"; + return { { oss.str(), m_description } }; + } + + auto isMatch( std::string const &optToken ) const -> bool { + auto normalisedToken = normaliseOpt( optToken ); + for( auto const &name : m_optNames ) { + if( normaliseOpt( name ) == normalisedToken ) + return true; + } + return false; + } + + using ParserBase::parse; + + auto parse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override { + auto validationResult = validate(); + if( !validationResult ) + return InternalParseResult( validationResult ); + + auto remainingTokens = tokens; + if( remainingTokens && remainingTokens->type == TokenType::Option ) { + auto const &token = *remainingTokens; + if( isMatch(token.token ) ) { + if( m_ref->isFlag() ) { + auto flagRef = static_cast<detail::BoundFlagRefBase*>( m_ref.get() ); + auto result = flagRef->setFlag( true ); + if( !result ) + return InternalParseResult( result ); + if( result.value() == ParseResultType::ShortCircuitAll ) + return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) ); + } else { + auto valueRef = static_cast<detail::BoundValueRefBase*>( m_ref.get() ); + ++remainingTokens; + if( !remainingTokens ) + return InternalParseResult::runtimeError( "Expected argument following " + token.token ); + auto const &argToken = *remainingTokens; + if( argToken.type != TokenType::Argument ) + return InternalParseResult::runtimeError( "Expected argument following " + token.token ); + auto result = valueRef->setValue( argToken.token ); + if( !result ) + return InternalParseResult( result ); + if( result.value() == ParseResultType::ShortCircuitAll ) + return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) ); + } + return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) ); + } + } + return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) ); + } + + auto validate() const -> Result override { + if( m_optNames.empty() ) + return Result::logicError( "No options supplied to Opt" ); + for( auto const &name : m_optNames ) { + if( name.empty() ) + return Result::logicError( "Option name cannot be empty" ); +#ifdef CATCH_PLATFORM_WINDOWS + if( name[0] != '-' && name[0] != '/' ) + return Result::logicError( "Option name must begin with '-' or '/'" ); +#else + if( name[0] != '-' ) + return Result::logicError( "Option name must begin with '-'" ); +#endif + } + return ParserRefImpl::validate(); + } + }; + + struct Help : Opt { + Help( bool &showHelpFlag ) + : Opt([&]( bool flag ) { + showHelpFlag = flag; + return ParserResult::ok( ParseResultType::ShortCircuitAll ); + }) + { + static_cast<Opt &>( *this ) + ("display usage information") + ["-?"]["-h"]["--help"] + .optional(); + } + }; + + struct Parser : ParserBase { + + mutable ExeName m_exeName; + std::vector<Opt> m_options; + std::vector<Arg> m_args; + + auto operator|=( ExeName const &exeName ) -> Parser & { + m_exeName = exeName; + return *this; + } + + auto operator|=( Arg const &arg ) -> Parser & { + m_args.push_back(arg); + return *this; + } + + auto operator|=( Opt const &opt ) -> Parser & { + m_options.push_back(opt); + return *this; + } + + auto operator|=( Parser const &other ) -> Parser & { + m_options.insert(m_options.end(), other.m_options.begin(), other.m_options.end()); + m_args.insert(m_args.end(), other.m_args.begin(), other.m_args.end()); + return *this; + } + + template<typename T> + auto operator|( T const &other ) const -> Parser { + return Parser( *this ) |= other; + } + + // Forward deprecated interface with '+' instead of '|' + template<typename T> + auto operator+=( T const &other ) -> Parser & { return operator|=( other ); } + template<typename T> + auto operator+( T const &other ) const -> Parser { return operator|( other ); } + + auto getHelpColumns() const -> std::vector<HelpColumns> { + std::vector<HelpColumns> cols; + for (auto const &o : m_options) { + auto childCols = o.getHelpColumns(); + cols.insert( cols.end(), childCols.begin(), childCols.end() ); + } + return cols; + } + + void writeToStream( std::ostream &os ) const { + if (!m_exeName.name().empty()) { + os << "usage:\n" << " " << m_exeName.name() << " "; + bool required = true, first = true; + for( auto const &arg : m_args ) { + if (first) + first = false; + else + os << " "; + if( arg.isOptional() && required ) { + os << "["; + required = false; + } + os << "<" << arg.hint() << ">"; + if( arg.cardinality() == 0 ) + os << " ... "; + } + if( !required ) + os << "]"; + if( !m_options.empty() ) + os << " options"; + os << "\n\nwhere options are:" << std::endl; + } + + auto rows = getHelpColumns(); + size_t consoleWidth = CATCH_CLARA_CONFIG_CONSOLE_WIDTH; + size_t optWidth = 0; + for( auto const &cols : rows ) + optWidth = (std::max)(optWidth, cols.left.size() + 2); + + optWidth = (std::min)(optWidth, consoleWidth/2); + + for( auto const &cols : rows ) { + auto row = + TextFlow::Column( cols.left ).width( optWidth ).indent( 2 ) + + TextFlow::Spacer(4) + + TextFlow::Column( cols.right ).width( consoleWidth - 7 - optWidth ); + os << row << std::endl; + } + } + + friend auto operator<<( std::ostream &os, Parser const &parser ) -> std::ostream& { + parser.writeToStream( os ); + return os; + } + + auto validate() const -> Result override { + for( auto const &opt : m_options ) { + auto result = opt.validate(); + if( !result ) + return result; + } + for( auto const &arg : m_args ) { + auto result = arg.validate(); + if( !result ) + return result; + } + return Result::ok(); + } + + using ParserBase::parse; + + auto parse( std::string const& exeName, TokenStream const &tokens ) const -> InternalParseResult override { + + struct ParserInfo { + ParserBase const* parser = nullptr; + size_t count = 0; + }; + const size_t totalParsers = m_options.size() + m_args.size(); + assert( totalParsers < 512 ); + // ParserInfo parseInfos[totalParsers]; // <-- this is what we really want to do + ParserInfo parseInfos[512]; + + { + size_t i = 0; + for (auto const &opt : m_options) parseInfos[i++].parser = &opt; + for (auto const &arg : m_args) parseInfos[i++].parser = &arg; + } + + m_exeName.set( exeName ); + + auto result = InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) ); + while( result.value().remainingTokens() ) { + bool tokenParsed = false; + + for( size_t i = 0; i < totalParsers; ++i ) { + auto& parseInfo = parseInfos[i]; + if( parseInfo.parser->cardinality() == 0 || parseInfo.count < parseInfo.parser->cardinality() ) { + result = parseInfo.parser->parse(exeName, result.value().remainingTokens()); + if (!result) + return result; + if (result.value().type() != ParseResultType::NoMatch) { + tokenParsed = true; + ++parseInfo.count; + break; + } + } + } + + if( result.value().type() == ParseResultType::ShortCircuitAll ) + return result; + if( !tokenParsed ) + return InternalParseResult::runtimeError( "Unrecognised token: " + result.value().remainingTokens()->token ); + } + // !TBD Check missing required options + return result; + } + }; + + template<typename DerivedT> + template<typename T> + auto ComposableParserImpl<DerivedT>::operator|( T const &other ) const -> Parser { + return Parser() | static_cast<DerivedT const &>( *this ) | other; + } +} // namespace detail + +// A Combined parser +using detail::Parser; + +// A parser for options +using detail::Opt; + +// A parser for arguments +using detail::Arg; + +// Wrapper for argc, argv from main() +using detail::Args; + +// Specifies the name of the executable +using detail::ExeName; + +// Convenience wrapper for option parser that specifies the help option +using detail::Help; + +// enum of result types from a parse +using detail::ParseResultType; + +// Result type for parser operation +using detail::ParserResult; + +}} // namespace Catch::clara + +// end clara.hpp +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +// Restore Clara's value for console width, if present +#ifdef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH +#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH +#undef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH +#endif + +// end catch_clara.h +namespace Catch { + + clara::Parser makeCommandLineParser( ConfigData& config ); + +} // end namespace Catch + +// end catch_commandline.h +#include <fstream> +#include <ctime> + +namespace Catch { + + clara::Parser makeCommandLineParser( ConfigData& config ) { + + using namespace clara; + + auto const setWarning = [&]( std::string const& warning ) { + auto warningSet = [&]() { + if( warning == "NoAssertions" ) + return WarnAbout::NoAssertions; + + if ( warning == "NoTests" ) + return WarnAbout::NoTests; + + return WarnAbout::Nothing; + }(); + + if (warningSet == WarnAbout::Nothing) + return ParserResult::runtimeError( "Unrecognised warning: '" + warning + "'" ); + config.warnings = static_cast<WarnAbout::What>( config.warnings | warningSet ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const loadTestNamesFromFile = [&]( std::string const& filename ) { + std::ifstream f( filename.c_str() ); + if( !f.is_open() ) + return ParserResult::runtimeError( "Unable to load input file: '" + filename + "'" ); + + std::string line; + while( std::getline( f, line ) ) { + line = trim(line); + if( !line.empty() && !startsWith( line, '#' ) ) { + if( !startsWith( line, '"' ) ) + line = '"' + line + '"'; + config.testsOrTags.push_back( line ); + config.testsOrTags.emplace_back( "," ); + } + } + //Remove comma in the end + if(!config.testsOrTags.empty()) + config.testsOrTags.erase( config.testsOrTags.end()-1 ); + + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setTestOrder = [&]( std::string const& order ) { + if( startsWith( "declared", order ) ) + config.runOrder = RunTests::InDeclarationOrder; + else if( startsWith( "lexical", order ) ) + config.runOrder = RunTests::InLexicographicalOrder; + else if( startsWith( "random", order ) ) + config.runOrder = RunTests::InRandomOrder; + else + return clara::ParserResult::runtimeError( "Unrecognised ordering: '" + order + "'" ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setRngSeed = [&]( std::string const& seed ) { + if( seed != "time" ) + return clara::detail::convertInto( seed, config.rngSeed ); + config.rngSeed = static_cast<unsigned int>( std::time(nullptr) ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setColourUsage = [&]( std::string const& useColour ) { + auto mode = toLower( useColour ); + + if( mode == "yes" ) + config.useColour = UseColour::Yes; + else if( mode == "no" ) + config.useColour = UseColour::No; + else if( mode == "auto" ) + config.useColour = UseColour::Auto; + else + return ParserResult::runtimeError( "colour mode must be one of: auto, yes or no. '" + useColour + "' not recognised" ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setWaitForKeypress = [&]( std::string const& keypress ) { + auto keypressLc = toLower( keypress ); + if (keypressLc == "never") + config.waitForKeypress = WaitForKeypress::Never; + else if( keypressLc == "start" ) + config.waitForKeypress = WaitForKeypress::BeforeStart; + else if( keypressLc == "exit" ) + config.waitForKeypress = WaitForKeypress::BeforeExit; + else if( keypressLc == "both" ) + config.waitForKeypress = WaitForKeypress::BeforeStartAndExit; + else + return ParserResult::runtimeError( "keypress argument must be one of: never, start, exit or both. '" + keypress + "' not recognised" ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setVerbosity = [&]( std::string const& verbosity ) { + auto lcVerbosity = toLower( verbosity ); + if( lcVerbosity == "quiet" ) + config.verbosity = Verbosity::Quiet; + else if( lcVerbosity == "normal" ) + config.verbosity = Verbosity::Normal; + else if( lcVerbosity == "high" ) + config.verbosity = Verbosity::High; + else + return ParserResult::runtimeError( "Unrecognised verbosity, '" + verbosity + "'" ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setReporter = [&]( std::string const& reporter ) { + IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories(); + + auto lcReporter = toLower( reporter ); + auto result = factories.find( lcReporter ); + + if( factories.end() != result ) + config.reporterName = lcReporter; + else + return ParserResult::runtimeError( "Unrecognized reporter, '" + reporter + "'. Check available with --list-reporters" ); + return ParserResult::ok( ParseResultType::Matched ); + }; + + auto cli + = ExeName( config.processName ) + | Help( config.showHelp ) + | Opt( config.listTests ) + ["-l"]["--list-tests"] + ( "list all/matching test cases" ) + | Opt( config.listTags ) + ["-t"]["--list-tags"] + ( "list all/matching tags" ) + | Opt( config.showSuccessfulTests ) + ["-s"]["--success"] + ( "include successful tests in output" ) + | Opt( config.shouldDebugBreak ) + ["-b"]["--break"] + ( "break into debugger on failure" ) + | Opt( config.noThrow ) + ["-e"]["--nothrow"] + ( "skip exception tests" ) + | Opt( config.showInvisibles ) + ["-i"]["--invisibles"] + ( "show invisibles (tabs, newlines)" ) + | Opt( config.outputFilename, "filename" ) + ["-o"]["--out"] + ( "output filename" ) + | Opt( setReporter, "name" ) + ["-r"]["--reporter"] + ( "reporter to use (defaults to console)" ) + | Opt( config.name, "name" ) + ["-n"]["--name"] + ( "suite name" ) + | Opt( [&]( bool ){ config.abortAfter = 1; } ) + ["-a"]["--abort"] + ( "abort at first failure" ) + | Opt( [&]( int x ){ config.abortAfter = x; }, "no. failures" ) + ["-x"]["--abortx"] + ( "abort after x failures" ) + | Opt( setWarning, "warning name" ) + ["-w"]["--warn"] + ( "enable warnings" ) + | Opt( [&]( bool flag ) { config.showDurations = flag ? ShowDurations::Always : ShowDurations::Never; }, "yes|no" ) + ["-d"]["--durations"] + ( "show test durations" ) + | Opt( config.minDuration, "seconds" ) + ["-D"]["--min-duration"] + ( "show test durations for tests taking at least the given number of seconds" ) + | Opt( loadTestNamesFromFile, "filename" ) + ["-f"]["--input-file"] + ( "load test names to run from a file" ) + | Opt( config.filenamesAsTags ) + ["-#"]["--filenames-as-tags"] + ( "adds a tag for the filename" ) + | Opt( config.sectionsToRun, "section name" ) + ["-c"]["--section"] + ( "specify section to run" ) + | Opt( setVerbosity, "quiet|normal|high" ) + ["-v"]["--verbosity"] + ( "set output verbosity" ) + | Opt( config.listTestNamesOnly ) + ["--list-test-names-only"] + ( "list all/matching test cases names only" ) + | Opt( config.listReporters ) + ["--list-reporters"] + ( "list all reporters" ) + | Opt( setTestOrder, "decl|lex|rand" ) + ["--order"] + ( "test case order (defaults to decl)" ) + | Opt( setRngSeed, "'time'|number" ) + ["--rng-seed"] + ( "set a specific seed for random numbers" ) + | Opt( setColourUsage, "yes|no" ) + ["--use-colour"] + ( "should output be colourised" ) + | Opt( config.libIdentify ) + ["--libidentify"] + ( "report name and version according to libidentify standard" ) + | Opt( setWaitForKeypress, "never|start|exit|both" ) + ["--wait-for-keypress"] + ( "waits for a keypress before exiting" ) + | Opt( config.benchmarkSamples, "samples" ) + ["--benchmark-samples"] + ( "number of samples to collect (default: 100)" ) + | Opt( config.benchmarkResamples, "resamples" ) + ["--benchmark-resamples"] + ( "number of resamples for the bootstrap (default: 100000)" ) + | Opt( config.benchmarkConfidenceInterval, "confidence interval" ) + ["--benchmark-confidence-interval"] + ( "confidence interval for the bootstrap (between 0 and 1, default: 0.95)" ) + | Opt( config.benchmarkNoAnalysis ) + ["--benchmark-no-analysis"] + ( "perform only measurements; do not perform any analysis" ) + | Opt( config.benchmarkWarmupTime, "benchmarkWarmupTime" ) + ["--benchmark-warmup-time"] + ( "amount of time in milliseconds spent on warming up each test (default: 100)" ) + | Arg( config.testsOrTags, "test name|pattern|tags" ) + ( "which test or tests to use" ); + + return cli; + } + +} // end namespace Catch +// end catch_commandline.cpp +// start catch_common.cpp + +#include <cstring> +#include <ostream> + +namespace Catch { + + bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const noexcept { + return line == other.line && (file == other.file || std::strcmp(file, other.file) == 0); + } + bool SourceLineInfo::operator < ( SourceLineInfo const& other ) const noexcept { + // We can assume that the same file will usually have the same pointer. + // Thus, if the pointers are the same, there is no point in calling the strcmp + return line < other.line || ( line == other.line && file != other.file && (std::strcmp(file, other.file) < 0)); + } + + std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) { +#ifndef __GNUG__ + os << info.file << '(' << info.line << ')'; +#else + os << info.file << ':' << info.line; +#endif + return os; + } + + std::string StreamEndStop::operator+() const { + return std::string(); + } + + NonCopyable::NonCopyable() = default; + NonCopyable::~NonCopyable() = default; + +} +// end catch_common.cpp +// start catch_config.cpp + +namespace Catch { + + Config::Config( ConfigData const& data ) + : m_data( data ), + m_stream( openStream() ) + { + // We need to trim filter specs to avoid trouble with superfluous + // whitespace (esp. important for bdd macros, as those are manually + // aligned with whitespace). + + for (auto& elem : m_data.testsOrTags) { + elem = trim(elem); + } + for (auto& elem : m_data.sectionsToRun) { + elem = trim(elem); + } + + TestSpecParser parser(ITagAliasRegistry::get()); + if (!m_data.testsOrTags.empty()) { + m_hasTestFilters = true; + for (auto const& testOrTags : m_data.testsOrTags) { + parser.parse(testOrTags); + } + } + m_testSpec = parser.testSpec(); + } + + std::string const& Config::getFilename() const { + return m_data.outputFilename ; + } + + bool Config::listTests() const { return m_data.listTests; } + bool Config::listTestNamesOnly() const { return m_data.listTestNamesOnly; } + bool Config::listTags() const { return m_data.listTags; } + bool Config::listReporters() const { return m_data.listReporters; } + + std::string Config::getProcessName() const { return m_data.processName; } + std::string const& Config::getReporterName() const { return m_data.reporterName; } + + std::vector<std::string> const& Config::getTestsOrTags() const { return m_data.testsOrTags; } + std::vector<std::string> const& Config::getSectionsToRun() const { return m_data.sectionsToRun; } + + TestSpec const& Config::testSpec() const { return m_testSpec; } + bool Config::hasTestFilters() const { return m_hasTestFilters; } + + bool Config::showHelp() const { return m_data.showHelp; } + + // IConfig interface + bool Config::allowThrows() const { return !m_data.noThrow; } + std::ostream& Config::stream() const { return m_stream->stream(); } + std::string Config::name() const { return m_data.name.empty() ? m_data.processName : m_data.name; } + bool Config::includeSuccessfulResults() const { return m_data.showSuccessfulTests; } + bool Config::warnAboutMissingAssertions() const { return !!(m_data.warnings & WarnAbout::NoAssertions); } + bool Config::warnAboutNoTests() const { return !!(m_data.warnings & WarnAbout::NoTests); } + ShowDurations::OrNot Config::showDurations() const { return m_data.showDurations; } + double Config::minDuration() const { return m_data.minDuration; } + RunTests::InWhatOrder Config::runOrder() const { return m_data.runOrder; } + unsigned int Config::rngSeed() const { return m_data.rngSeed; } + UseColour::YesOrNo Config::useColour() const { return m_data.useColour; } + bool Config::shouldDebugBreak() const { return m_data.shouldDebugBreak; } + int Config::abortAfter() const { return m_data.abortAfter; } + bool Config::showInvisibles() const { return m_data.showInvisibles; } + Verbosity Config::verbosity() const { return m_data.verbosity; } + + bool Config::benchmarkNoAnalysis() const { return m_data.benchmarkNoAnalysis; } + int Config::benchmarkSamples() const { return m_data.benchmarkSamples; } + double Config::benchmarkConfidenceInterval() const { return m_data.benchmarkConfidenceInterval; } + unsigned int Config::benchmarkResamples() const { return m_data.benchmarkResamples; } + std::chrono::milliseconds Config::benchmarkWarmupTime() const { return std::chrono::milliseconds(m_data.benchmarkWarmupTime); } + + IStream const* Config::openStream() { + return Catch::makeStream(m_data.outputFilename); + } + +} // end namespace Catch +// end catch_config.cpp +// start catch_console_colour.cpp + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wexit-time-destructors" +#endif + +// start catch_errno_guard.h + +namespace Catch { + + class ErrnoGuard { + public: + ErrnoGuard(); + ~ErrnoGuard(); + private: + int m_oldErrno; + }; + +} + +// end catch_errno_guard.h +// start catch_windows_h_proxy.h + + +#if defined(CATCH_PLATFORM_WINDOWS) + +#if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX) +# define CATCH_DEFINED_NOMINMAX +# define NOMINMAX +#endif +#if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN) +# define CATCH_DEFINED_WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif + +#ifdef __AFXDLL +#include <AfxWin.h> +#else +#include <windows.h> +#endif + +#ifdef CATCH_DEFINED_NOMINMAX +# undef NOMINMAX +#endif +#ifdef CATCH_DEFINED_WIN32_LEAN_AND_MEAN +# undef WIN32_LEAN_AND_MEAN +#endif + +#endif // defined(CATCH_PLATFORM_WINDOWS) + +// end catch_windows_h_proxy.h +#include <sstream> + +namespace Catch { + namespace { + + struct IColourImpl { + virtual ~IColourImpl() = default; + virtual void use( Colour::Code _colourCode ) = 0; + }; + + struct NoColourImpl : IColourImpl { + void use( Colour::Code ) override {} + + static IColourImpl* instance() { + static NoColourImpl s_instance; + return &s_instance; + } + }; + + } // anon namespace +} // namespace Catch + +#if !defined( CATCH_CONFIG_COLOUR_NONE ) && !defined( CATCH_CONFIG_COLOUR_WINDOWS ) && !defined( CATCH_CONFIG_COLOUR_ANSI ) +# ifdef CATCH_PLATFORM_WINDOWS +# define CATCH_CONFIG_COLOUR_WINDOWS +# else +# define CATCH_CONFIG_COLOUR_ANSI +# endif +#endif + +#if defined ( CATCH_CONFIG_COLOUR_WINDOWS ) ///////////////////////////////////////// + +namespace Catch { +namespace { + + class Win32ColourImpl : public IColourImpl { + public: + Win32ColourImpl() : stdoutHandle( GetStdHandle(STD_OUTPUT_HANDLE) ) + { + CONSOLE_SCREEN_BUFFER_INFO csbiInfo; + GetConsoleScreenBufferInfo( stdoutHandle, &csbiInfo ); + originalForegroundAttributes = csbiInfo.wAttributes & ~( BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY ); + originalBackgroundAttributes = csbiInfo.wAttributes & ~( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY ); + } + + void use( Colour::Code _colourCode ) override { + switch( _colourCode ) { + case Colour::None: return setTextAttribute( originalForegroundAttributes ); + case Colour::White: return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); + case Colour::Red: return setTextAttribute( FOREGROUND_RED ); + case Colour::Green: return setTextAttribute( FOREGROUND_GREEN ); + case Colour::Blue: return setTextAttribute( FOREGROUND_BLUE ); + case Colour::Cyan: return setTextAttribute( FOREGROUND_BLUE | FOREGROUND_GREEN ); + case Colour::Yellow: return setTextAttribute( FOREGROUND_RED | FOREGROUND_GREEN ); + case Colour::Grey: return setTextAttribute( 0 ); + + case Colour::LightGrey: return setTextAttribute( FOREGROUND_INTENSITY ); + case Colour::BrightRed: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED ); + case Colour::BrightGreen: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN ); + case Colour::BrightWhite: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); + case Colour::BrightYellow: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN ); + + case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" ); + + default: + CATCH_ERROR( "Unknown colour requested" ); + } + } + + private: + void setTextAttribute( WORD _textAttribute ) { + SetConsoleTextAttribute( stdoutHandle, _textAttribute | originalBackgroundAttributes ); + } + HANDLE stdoutHandle; + WORD originalForegroundAttributes; + WORD originalBackgroundAttributes; + }; + + IColourImpl* platformColourInstance() { + static Win32ColourImpl s_instance; + + IConfigPtr config = getCurrentContext().getConfig(); + UseColour::YesOrNo colourMode = config + ? config->useColour() + : UseColour::Auto; + if( colourMode == UseColour::Auto ) + colourMode = UseColour::Yes; + return colourMode == UseColour::Yes + ? &s_instance + : NoColourImpl::instance(); + } + +} // end anon namespace +} // end namespace Catch + +#elif defined( CATCH_CONFIG_COLOUR_ANSI ) ////////////////////////////////////// + +#include <unistd.h> + +namespace Catch { +namespace { + + // use POSIX/ ANSI console terminal codes + // Thanks to Adam Strzelecki for original contribution + // (http://github.com/nanoant) + // https://github.com/philsquared/Catch/pull/131 + class PosixColourImpl : public IColourImpl { + public: + void use( Colour::Code _colourCode ) override { + switch( _colourCode ) { + case Colour::None: + case Colour::White: return setColour( "[0m" ); + case Colour::Red: return setColour( "[0;31m" ); + case Colour::Green: return setColour( "[0;32m" ); + case Colour::Blue: return setColour( "[0;34m" ); + case Colour::Cyan: return setColour( "[0;36m" ); + case Colour::Yellow: return setColour( "[0;33m" ); + case Colour::Grey: return setColour( "[1;30m" ); + + case Colour::LightGrey: return setColour( "[0;37m" ); + case Colour::BrightRed: return setColour( "[1;31m" ); + case Colour::BrightGreen: return setColour( "[1;32m" ); + case Colour::BrightWhite: return setColour( "[1;37m" ); + case Colour::BrightYellow: return setColour( "[1;33m" ); + + case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" ); + default: CATCH_INTERNAL_ERROR( "Unknown colour requested" ); + } + } + static IColourImpl* instance() { + static PosixColourImpl s_instance; + return &s_instance; + } + + private: + void setColour( const char* _escapeCode ) { + getCurrentContext().getConfig()->stream() + << '\033' << _escapeCode; + } + }; + + bool useColourOnPlatform() { + return +#if defined(CATCH_PLATFORM_MAC) || defined(CATCH_PLATFORM_IPHONE) + !isDebuggerActive() && +#endif +#if !(defined(__DJGPP__) && defined(__STRICT_ANSI__)) + isatty(STDOUT_FILENO) +#else + false +#endif + ; + } + IColourImpl* platformColourInstance() { + ErrnoGuard guard; + IConfigPtr config = getCurrentContext().getConfig(); + UseColour::YesOrNo colourMode = config + ? config->useColour() + : UseColour::Auto; + if( colourMode == UseColour::Auto ) + colourMode = useColourOnPlatform() + ? UseColour::Yes + : UseColour::No; + return colourMode == UseColour::Yes + ? PosixColourImpl::instance() + : NoColourImpl::instance(); + } + +} // end anon namespace +} // end namespace Catch + +#else // not Windows or ANSI /////////////////////////////////////////////// + +namespace Catch { + + static IColourImpl* platformColourInstance() { return NoColourImpl::instance(); } + +} // end namespace Catch + +#endif // Windows/ ANSI/ None + +namespace Catch { + + Colour::Colour( Code _colourCode ) { use( _colourCode ); } + Colour::Colour( Colour&& other ) noexcept { + m_moved = other.m_moved; + other.m_moved = true; + } + Colour& Colour::operator=( Colour&& other ) noexcept { + m_moved = other.m_moved; + other.m_moved = true; + return *this; + } + + Colour::~Colour(){ if( !m_moved ) use( None ); } + + void Colour::use( Code _colourCode ) { + static IColourImpl* impl = platformColourInstance(); + // Strictly speaking, this cannot possibly happen. + // However, under some conditions it does happen (see #1626), + // and this change is small enough that we can let practicality + // triumph over purity in this case. + if (impl != nullptr) { + impl->use( _colourCode ); + } + } + + std::ostream& operator << ( std::ostream& os, Colour const& ) { + return os; + } + +} // end namespace Catch + +#if defined(__clang__) +# pragma clang diagnostic pop +#endif + +// end catch_console_colour.cpp +// start catch_context.cpp + +namespace Catch { + + class Context : public IMutableContext, NonCopyable { + + public: // IContext + IResultCapture* getResultCapture() override { + return m_resultCapture; + } + IRunner* getRunner() override { + return m_runner; + } + + IConfigPtr const& getConfig() const override { + return m_config; + } + + ~Context() override; + + public: // IMutableContext + void setResultCapture( IResultCapture* resultCapture ) override { + m_resultCapture = resultCapture; + } + void setRunner( IRunner* runner ) override { + m_runner = runner; + } + void setConfig( IConfigPtr const& config ) override { + m_config = config; + } + + friend IMutableContext& getCurrentMutableContext(); + + private: + IConfigPtr m_config; + IRunner* m_runner = nullptr; + IResultCapture* m_resultCapture = nullptr; + }; + + IMutableContext *IMutableContext::currentContext = nullptr; + + void IMutableContext::createContext() + { + currentContext = new Context(); + } + + void cleanUpContext() { + delete IMutableContext::currentContext; + IMutableContext::currentContext = nullptr; + } + IContext::~IContext() = default; + IMutableContext::~IMutableContext() = default; + Context::~Context() = default; + + SimplePcg32& rng() { + static SimplePcg32 s_rng; + return s_rng; + } + +} +// end catch_context.cpp +// start catch_debug_console.cpp + +// start catch_debug_console.h + +#include <string> + +namespace Catch { + void writeToDebugConsole( std::string const& text ); +} + +// end catch_debug_console.h +#if defined(CATCH_CONFIG_ANDROID_LOGWRITE) +#include <android/log.h> + + namespace Catch { + void writeToDebugConsole( std::string const& text ) { + __android_log_write( ANDROID_LOG_DEBUG, "Catch", text.c_str() ); + } + } + +#elif defined(CATCH_PLATFORM_WINDOWS) + + namespace Catch { + void writeToDebugConsole( std::string const& text ) { + ::OutputDebugStringA( text.c_str() ); + } + } + +#else + + namespace Catch { + void writeToDebugConsole( std::string const& text ) { + // !TBD: Need a version for Mac/ XCode and other IDEs + Catch::cout() << text; + } + } + +#endif // Platform +// end catch_debug_console.cpp +// start catch_debugger.cpp + +#if defined(CATCH_PLATFORM_MAC) || defined(CATCH_PLATFORM_IPHONE) + +# include <cassert> +# include <sys/types.h> +# include <unistd.h> +# include <cstddef> +# include <ostream> + +#ifdef __apple_build_version__ + // These headers will only compile with AppleClang (XCode) + // For other compilers (Clang, GCC, ... ) we need to exclude them +# include <sys/sysctl.h> +#endif + + namespace Catch { + #ifdef __apple_build_version__ + // The following function is taken directly from the following technical note: + // https://developer.apple.com/library/archive/qa/qa1361/_index.html + + // Returns true if the current process is being debugged (either + // running under the debugger or has a debugger attached post facto). + bool isDebuggerActive(){ + int mib[4]; + struct kinfo_proc info; + std::size_t size; + + // Initialize the flags so that, if sysctl fails for some bizarre + // reason, we get a predictable result. + + info.kp_proc.p_flag = 0; + + // Initialize mib, which tells sysctl the info we want, in this case + // we're looking for information about a specific process ID. + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = getpid(); + + // Call sysctl. + + size = sizeof(info); + if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, nullptr, 0) != 0 ) { + Catch::cerr() << "\n** Call to sysctl failed - unable to determine if debugger is active **\n" << std::endl; + return false; + } + + // We're being debugged if the P_TRACED flag is set. + + return ( (info.kp_proc.p_flag & P_TRACED) != 0 ); + } + #else + bool isDebuggerActive() { + // We need to find another way to determine this for non-appleclang compilers on macOS + return false; + } + #endif + } // namespace Catch + +#elif defined(CATCH_PLATFORM_LINUX) + #include <fstream> + #include <string> + + namespace Catch{ + // The standard POSIX way of detecting a debugger is to attempt to + // ptrace() the process, but this needs to be done from a child and not + // this process itself to still allow attaching to this process later + // if wanted, so is rather heavy. Under Linux we have the PID of the + // "debugger" (which doesn't need to be gdb, of course, it could also + // be strace, for example) in /proc/$PID/status, so just get it from + // there instead. + bool isDebuggerActive(){ + // Libstdc++ has a bug, where std::ifstream sets errno to 0 + // This way our users can properly assert over errno values + ErrnoGuard guard; + std::ifstream in("/proc/self/status"); + for( std::string line; std::getline(in, line); ) { + static const int PREFIX_LEN = 11; + if( line.compare(0, PREFIX_LEN, "TracerPid:\t") == 0 ) { + // We're traced if the PID is not 0 and no other PID starts + // with 0 digit, so it's enough to check for just a single + // character. + return line.length() > PREFIX_LEN && line[PREFIX_LEN] != '0'; + } + } + + return false; + } + } // namespace Catch +#elif defined(_MSC_VER) + extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); + namespace Catch { + bool isDebuggerActive() { + return IsDebuggerPresent() != 0; + } + } +#elif defined(__MINGW32__) + extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); + namespace Catch { + bool isDebuggerActive() { + return IsDebuggerPresent() != 0; + } + } +#else + namespace Catch { + bool isDebuggerActive() { return false; } + } +#endif // Platform +// end catch_debugger.cpp +// start catch_decomposer.cpp + +namespace Catch { + + ITransientExpression::~ITransientExpression() = default; + + void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs ) { + if( lhs.size() + rhs.size() < 40 && + lhs.find('\n') == std::string::npos && + rhs.find('\n') == std::string::npos ) + os << lhs << " " << op << " " << rhs; + else + os << lhs << "\n" << op << "\n" << rhs; + } +} +// end catch_decomposer.cpp +// start catch_enforce.cpp + +#include <stdexcept> + +namespace Catch { +#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS_CUSTOM_HANDLER) + [[noreturn]] + void throw_exception(std::exception const& e) { + Catch::cerr() << "Catch will terminate because it needed to throw an exception.\n" + << "The message was: " << e.what() << '\n'; + std::terminate(); + } +#endif + + [[noreturn]] + void throw_logic_error(std::string const& msg) { + throw_exception(std::logic_error(msg)); + } + + [[noreturn]] + void throw_domain_error(std::string const& msg) { + throw_exception(std::domain_error(msg)); + } + + [[noreturn]] + void throw_runtime_error(std::string const& msg) { + throw_exception(std::runtime_error(msg)); + } + +} // namespace Catch; +// end catch_enforce.cpp +// start catch_enum_values_registry.cpp +// start catch_enum_values_registry.h + +#include <vector> +#include <memory> + +namespace Catch { + + namespace Detail { + + std::unique_ptr<EnumInfo> makeEnumInfo( StringRef enumName, StringRef allValueNames, std::vector<int> const& values ); + + class EnumValuesRegistry : public IMutableEnumValuesRegistry { + + std::vector<std::unique_ptr<EnumInfo>> m_enumInfos; + + EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::vector<int> const& values) override; + }; + + std::vector<StringRef> parseEnums( StringRef enums ); + + } // Detail + +} // Catch + +// end catch_enum_values_registry.h + +#include <map> +#include <cassert> + +namespace Catch { + + IMutableEnumValuesRegistry::~IMutableEnumValuesRegistry() {} + + namespace Detail { + + namespace { + // Extracts the actual name part of an enum instance + // In other words, it returns the Blue part of Bikeshed::Colour::Blue + StringRef extractInstanceName(StringRef enumInstance) { + // Find last occurrence of ":" + size_t name_start = enumInstance.size(); + while (name_start > 0 && enumInstance[name_start - 1] != ':') { + --name_start; + } + return enumInstance.substr(name_start, enumInstance.size() - name_start); + } + } + + std::vector<StringRef> parseEnums( StringRef enums ) { + auto enumValues = splitStringRef( enums, ',' ); + std::vector<StringRef> parsed; + parsed.reserve( enumValues.size() ); + for( auto const& enumValue : enumValues ) { + parsed.push_back(trim(extractInstanceName(enumValue))); + } + return parsed; + } + + EnumInfo::~EnumInfo() {} + + StringRef EnumInfo::lookup( int value ) const { + for( auto const& valueToName : m_values ) { + if( valueToName.first == value ) + return valueToName.second; + } + return "{** unexpected enum value **}"_sr; + } + + std::unique_ptr<EnumInfo> makeEnumInfo( StringRef enumName, StringRef allValueNames, std::vector<int> const& values ) { + std::unique_ptr<EnumInfo> enumInfo( new EnumInfo ); + enumInfo->m_name = enumName; + enumInfo->m_values.reserve( values.size() ); + + const auto valueNames = Catch::Detail::parseEnums( allValueNames ); + assert( valueNames.size() == values.size() ); + std::size_t i = 0; + for( auto value : values ) + enumInfo->m_values.emplace_back(value, valueNames[i++]); + + return enumInfo; + } + + EnumInfo const& EnumValuesRegistry::registerEnum( StringRef enumName, StringRef allValueNames, std::vector<int> const& values ) { + m_enumInfos.push_back(makeEnumInfo(enumName, allValueNames, values)); + return *m_enumInfos.back(); + } + + } // Detail +} // Catch + +// end catch_enum_values_registry.cpp +// start catch_errno_guard.cpp + +#include <cerrno> + +namespace Catch { + ErrnoGuard::ErrnoGuard():m_oldErrno(errno){} + ErrnoGuard::~ErrnoGuard() { errno = m_oldErrno; } +} +// end catch_errno_guard.cpp +// start catch_exception_translator_registry.cpp + +// start catch_exception_translator_registry.h + +#include <vector> +#include <string> +#include <memory> + +namespace Catch { + + class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry { + public: + ~ExceptionTranslatorRegistry(); + virtual void registerTranslator( const IExceptionTranslator* translator ); + std::string translateActiveException() const override; + std::string tryTranslators() const; + + private: + std::vector<std::unique_ptr<IExceptionTranslator const>> m_translators; + }; +} + +// end catch_exception_translator_registry.h +#ifdef __OBJC__ +#import "Foundation/Foundation.h" +#endif + +namespace Catch { + + ExceptionTranslatorRegistry::~ExceptionTranslatorRegistry() { + } + + void ExceptionTranslatorRegistry::registerTranslator( const IExceptionTranslator* translator ) { + m_translators.push_back( std::unique_ptr<const IExceptionTranslator>( translator ) ); + } + +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + std::string ExceptionTranslatorRegistry::translateActiveException() const { + try { +#ifdef __OBJC__ + // In Objective-C try objective-c exceptions first + @try { + return tryTranslators(); + } + @catch (NSException *exception) { + return Catch::Detail::stringify( [exception description] ); + } +#else + // Compiling a mixed mode project with MSVC means that CLR + // exceptions will be caught in (...) as well. However, these + // do not fill-in std::current_exception and thus lead to crash + // when attempting rethrow. + // /EHa switch also causes structured exceptions to be caught + // here, but they fill-in current_exception properly, so + // at worst the output should be a little weird, instead of + // causing a crash. + if (std::current_exception() == nullptr) { + return "Non C++ exception. Possibly a CLR exception."; + } + return tryTranslators(); +#endif + } + catch( TestFailureException& ) { + std::rethrow_exception(std::current_exception()); + } + catch( std::exception& ex ) { + return ex.what(); + } + catch( std::string& msg ) { + return msg; + } + catch( const char* msg ) { + return msg; + } + catch(...) { + return "Unknown exception"; + } + } + + std::string ExceptionTranslatorRegistry::tryTranslators() const { + if (m_translators.empty()) { + std::rethrow_exception(std::current_exception()); + } else { + return m_translators[0]->translate(m_translators.begin() + 1, m_translators.end()); + } + } + +#else // ^^ Exceptions are enabled // Exceptions are disabled vv + std::string ExceptionTranslatorRegistry::translateActiveException() const { + CATCH_INTERNAL_ERROR("Attempted to translate active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!"); + } + + std::string ExceptionTranslatorRegistry::tryTranslators() const { + CATCH_INTERNAL_ERROR("Attempted to use exception translators under CATCH_CONFIG_DISABLE_EXCEPTIONS!"); + } +#endif + +} +// end catch_exception_translator_registry.cpp +// start catch_fatal_condition.cpp + +#include <algorithm> + +#if !defined( CATCH_CONFIG_WINDOWS_SEH ) && !defined( CATCH_CONFIG_POSIX_SIGNALS ) + +namespace Catch { + + // If neither SEH nor signal handling is required, the handler impls + // do not have to do anything, and can be empty. + void FatalConditionHandler::engage_platform() {} + void FatalConditionHandler::disengage_platform() {} + FatalConditionHandler::FatalConditionHandler() = default; + FatalConditionHandler::~FatalConditionHandler() = default; + +} // end namespace Catch + +#endif // !CATCH_CONFIG_WINDOWS_SEH && !CATCH_CONFIG_POSIX_SIGNALS + +#if defined( CATCH_CONFIG_WINDOWS_SEH ) && defined( CATCH_CONFIG_POSIX_SIGNALS ) +#error "Inconsistent configuration: Windows' SEH handling and POSIX signals cannot be enabled at the same time" +#endif // CATCH_CONFIG_WINDOWS_SEH && CATCH_CONFIG_POSIX_SIGNALS + +#if defined( CATCH_CONFIG_WINDOWS_SEH ) || defined( CATCH_CONFIG_POSIX_SIGNALS ) + +namespace { + //! Signals fatal error message to the run context + void reportFatal( char const * const message ) { + Catch::getCurrentContext().getResultCapture()->handleFatalErrorCondition( message ); + } + + //! Minimal size Catch2 needs for its own fatal error handling. + //! Picked anecdotally, so it might not be sufficient on all + //! platforms, and for all configurations. + constexpr std::size_t minStackSizeForErrors = 32 * 1024; +} // end unnamed namespace + +#endif // CATCH_CONFIG_WINDOWS_SEH || CATCH_CONFIG_POSIX_SIGNALS + +#if defined( CATCH_CONFIG_WINDOWS_SEH ) + +namespace Catch { + + struct SignalDefs { DWORD id; const char* name; }; + + // There is no 1-1 mapping between signals and windows exceptions. + // Windows can easily distinguish between SO and SigSegV, + // but SigInt, SigTerm, etc are handled differently. + static SignalDefs signalDefs[] = { + { static_cast<DWORD>(EXCEPTION_ILLEGAL_INSTRUCTION), "SIGILL - Illegal instruction signal" }, + { static_cast<DWORD>(EXCEPTION_STACK_OVERFLOW), "SIGSEGV - Stack overflow" }, + { static_cast<DWORD>(EXCEPTION_ACCESS_VIOLATION), "SIGSEGV - Segmentation violation signal" }, + { static_cast<DWORD>(EXCEPTION_INT_DIVIDE_BY_ZERO), "Divide by zero error" }, + }; + + static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) { + for (auto const& def : signalDefs) { + if (ExceptionInfo->ExceptionRecord->ExceptionCode == def.id) { + reportFatal(def.name); + } + } + // If its not an exception we care about, pass it along. + // This stops us from eating debugger breaks etc. + return EXCEPTION_CONTINUE_SEARCH; + } + + // Since we do not support multiple instantiations, we put these + // into global variables and rely on cleaning them up in outlined + // constructors/destructors + static PVOID exceptionHandlerHandle = nullptr; + + // For MSVC, we reserve part of the stack memory for handling + // memory overflow structured exception. + FatalConditionHandler::FatalConditionHandler() { + ULONG guaranteeSize = static_cast<ULONG>(minStackSizeForErrors); + if (!SetThreadStackGuarantee(&guaranteeSize)) { + // We do not want to fully error out, because needing + // the stack reserve should be rare enough anyway. + Catch::cerr() + << "Failed to reserve piece of stack." + << " Stack overflows will not be reported successfully."; + } + } + + // We do not attempt to unset the stack guarantee, because + // Windows does not support lowering the stack size guarantee. + FatalConditionHandler::~FatalConditionHandler() = default; + + void FatalConditionHandler::engage_platform() { + // Register as first handler in current chain + exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException); + if (!exceptionHandlerHandle) { + CATCH_RUNTIME_ERROR("Could not register vectored exception handler"); + } + } + + void FatalConditionHandler::disengage_platform() { + if (!RemoveVectoredExceptionHandler(exceptionHandlerHandle)) { + CATCH_RUNTIME_ERROR("Could not unregister vectored exception handler"); + } + exceptionHandlerHandle = nullptr; + } + +} // end namespace Catch + +#endif // CATCH_CONFIG_WINDOWS_SEH + +#if defined( CATCH_CONFIG_POSIX_SIGNALS ) + +#include <signal.h> + +namespace Catch { + + struct SignalDefs { + int id; + const char* name; + }; + + static SignalDefs signalDefs[] = { + { SIGINT, "SIGINT - Terminal interrupt signal" }, + { SIGILL, "SIGILL - Illegal instruction signal" }, + { SIGFPE, "SIGFPE - Floating point error signal" }, + { SIGSEGV, "SIGSEGV - Segmentation violation signal" }, + { SIGTERM, "SIGTERM - Termination request signal" }, + { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" } + }; + +// Older GCCs trigger -Wmissing-field-initializers for T foo = {} +// which is zero initialization, but not explicit. We want to avoid +// that. +#if defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#endif + + static char* altStackMem = nullptr; + static std::size_t altStackSize = 0; + static stack_t oldSigStack{}; + static struct sigaction oldSigActions[sizeof(signalDefs) / sizeof(SignalDefs)]{}; + + static void restorePreviousSignalHandlers() { + // We set signal handlers back to the previous ones. Hopefully + // nobody overwrote them in the meantime, and doesn't expect + // their signal handlers to live past ours given that they + // installed them after ours.. + for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) { + sigaction(signalDefs[i].id, &oldSigActions[i], nullptr); + } + // Return the old stack + sigaltstack(&oldSigStack, nullptr); + } + + static void handleSignal( int sig ) { + char const * name = "<unknown signal>"; + for (auto const& def : signalDefs) { + if (sig == def.id) { + name = def.name; + break; + } + } + // We need to restore previous signal handlers and let them do + // their thing, so that the users can have the debugger break + // when a signal is raised, and so on. + restorePreviousSignalHandlers(); + reportFatal( name ); + raise( sig ); + } + + FatalConditionHandler::FatalConditionHandler() { + assert(!altStackMem && "Cannot initialize POSIX signal handler when one already exists"); + if (altStackSize == 0) { + altStackSize = std::max(static_cast<size_t>(SIGSTKSZ), minStackSizeForErrors); + } + altStackMem = new char[altStackSize](); + } + + FatalConditionHandler::~FatalConditionHandler() { + delete[] altStackMem; + // We signal that another instance can be constructed by zeroing + // out the pointer. + altStackMem = nullptr; + } + + void FatalConditionHandler::engage_platform() { + stack_t sigStack; + sigStack.ss_sp = altStackMem; + sigStack.ss_size = altStackSize; + sigStack.ss_flags = 0; + sigaltstack(&sigStack, &oldSigStack); + struct sigaction sa = { }; + + sa.sa_handler = handleSignal; + sa.sa_flags = SA_ONSTACK; + for (std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i) { + sigaction(signalDefs[i].id, &sa, &oldSigActions[i]); + } + } + +#if defined(__GNUC__) +# pragma GCC diagnostic pop +#endif + + void FatalConditionHandler::disengage_platform() { + restorePreviousSignalHandlers(); + } + +} // end namespace Catch + +#endif // CATCH_CONFIG_POSIX_SIGNALS +// end catch_fatal_condition.cpp +// start catch_generators.cpp + +#include <limits> +#include <set> + +namespace Catch { + +IGeneratorTracker::~IGeneratorTracker() {} + +const char* GeneratorException::what() const noexcept { + return m_msg; +} + +namespace Generators { + + GeneratorUntypedBase::~GeneratorUntypedBase() {} + + auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& { + return getResultCapture().acquireGeneratorTracker( generatorName, lineInfo ); + } + +} // namespace Generators +} // namespace Catch +// end catch_generators.cpp +// start catch_interfaces_capture.cpp + +namespace Catch { + IResultCapture::~IResultCapture() = default; +} +// end catch_interfaces_capture.cpp +// start catch_interfaces_config.cpp + +namespace Catch { + IConfig::~IConfig() = default; +} +// end catch_interfaces_config.cpp +// start catch_interfaces_exception.cpp + +namespace Catch { + IExceptionTranslator::~IExceptionTranslator() = default; + IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() = default; +} +// end catch_interfaces_exception.cpp +// start catch_interfaces_registry_hub.cpp + +namespace Catch { + IRegistryHub::~IRegistryHub() = default; + IMutableRegistryHub::~IMutableRegistryHub() = default; +} +// end catch_interfaces_registry_hub.cpp +// start catch_interfaces_reporter.cpp + +// start catch_reporter_listening.h + +namespace Catch { + + class ListeningReporter : public IStreamingReporter { + using Reporters = std::vector<IStreamingReporterPtr>; + Reporters m_listeners; + IStreamingReporterPtr m_reporter = nullptr; + ReporterPreferences m_preferences; + + public: + ListeningReporter(); + + void addListener( IStreamingReporterPtr&& listener ); + void addReporter( IStreamingReporterPtr&& reporter ); + + public: // IStreamingReporter + + ReporterPreferences getPreferences() const override; + + void noMatchingTestCases( std::string const& spec ) override; + + void reportInvalidArguments(std::string const&arg) override; + + static std::set<Verbosity> getSupportedVerbosities(); + +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) + void benchmarkPreparing(std::string const& name) override; + void benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) override; + void benchmarkEnded( BenchmarkStats<> const& benchmarkStats ) override; + void benchmarkFailed(std::string const&) override; +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING + + void testRunStarting( TestRunInfo const& testRunInfo ) override; + void testGroupStarting( GroupInfo const& groupInfo ) override; + void testCaseStarting( TestCaseInfo const& testInfo ) override; + void sectionStarting( SectionInfo const& sectionInfo ) override; + void assertionStarting( AssertionInfo const& assertionInfo ) override; + + // The return value indicates if the messages buffer should be cleared: + bool assertionEnded( AssertionStats const& assertionStats ) override; + void sectionEnded( SectionStats const& sectionStats ) override; + void testCaseEnded( TestCaseStats const& testCaseStats ) override; + void testGroupEnded( TestGroupStats const& testGroupStats ) override; + void testRunEnded( TestRunStats const& testRunStats ) override; + + void skipTest( TestCaseInfo const& testInfo ) override; + bool isMulti() const override; + + }; + +} // end namespace Catch + +// end catch_reporter_listening.h +namespace Catch { + + ReporterConfig::ReporterConfig( IConfigPtr const& _fullConfig ) + : m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {} + + ReporterConfig::ReporterConfig( IConfigPtr const& _fullConfig, std::ostream& _stream ) + : m_stream( &_stream ), m_fullConfig( _fullConfig ) {} + + std::ostream& ReporterConfig::stream() const { return *m_stream; } + IConfigPtr ReporterConfig::fullConfig() const { return m_fullConfig; } + + TestRunInfo::TestRunInfo( std::string const& _name ) : name( _name ) {} + + GroupInfo::GroupInfo( std::string const& _name, + std::size_t _groupIndex, + std::size_t _groupsCount ) + : name( _name ), + groupIndex( _groupIndex ), + groupsCounts( _groupsCount ) + {} + + AssertionStats::AssertionStats( AssertionResult const& _assertionResult, + std::vector<MessageInfo> const& _infoMessages, + Totals const& _totals ) + : assertionResult( _assertionResult ), + infoMessages( _infoMessages ), + totals( _totals ) + { + assertionResult.m_resultData.lazyExpression.m_transientExpression = _assertionResult.m_resultData.lazyExpression.m_transientExpression; + + if( assertionResult.hasMessage() ) { + // Copy message into messages list. + // !TBD This should have been done earlier, somewhere + MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() ); + builder << assertionResult.getMessage(); + builder.m_info.message = builder.m_stream.str(); + + infoMessages.push_back( builder.m_info ); + } + } + + AssertionStats::~AssertionStats() = default; + + SectionStats::SectionStats( SectionInfo const& _sectionInfo, + Counts const& _assertions, + double _durationInSeconds, + bool _missingAssertions ) + : sectionInfo( _sectionInfo ), + assertions( _assertions ), + durationInSeconds( _durationInSeconds ), + missingAssertions( _missingAssertions ) + {} + + SectionStats::~SectionStats() = default; + + TestCaseStats::TestCaseStats( TestCaseInfo const& _testInfo, + Totals const& _totals, + std::string const& _stdOut, + std::string const& _stdErr, + bool _aborting ) + : testInfo( _testInfo ), + totals( _totals ), + stdOut( _stdOut ), + stdErr( _stdErr ), + aborting( _aborting ) + {} + + TestCaseStats::~TestCaseStats() = default; + + TestGroupStats::TestGroupStats( GroupInfo const& _groupInfo, + Totals const& _totals, + bool _aborting ) + : groupInfo( _groupInfo ), + totals( _totals ), + aborting( _aborting ) + {} + + TestGroupStats::TestGroupStats( GroupInfo const& _groupInfo ) + : groupInfo( _groupInfo ), + aborting( false ) + {} + + TestGroupStats::~TestGroupStats() = default; + + TestRunStats::TestRunStats( TestRunInfo const& _runInfo, + Totals const& _totals, + bool _aborting ) + : runInfo( _runInfo ), + totals( _totals ), + aborting( _aborting ) + {} + + TestRunStats::~TestRunStats() = default; + + void IStreamingReporter::fatalErrorEncountered( StringRef ) {} + bool IStreamingReporter::isMulti() const { return false; } + + IReporterFactory::~IReporterFactory() = default; + IReporterRegistry::~IReporterRegistry() = default; + +} // end namespace Catch +// end catch_interfaces_reporter.cpp +// start catch_interfaces_runner.cpp + +namespace Catch { + IRunner::~IRunner() = default; +} +// end catch_interfaces_runner.cpp +// start catch_interfaces_testcase.cpp + +namespace Catch { + ITestInvoker::~ITestInvoker() = default; + ITestCaseRegistry::~ITestCaseRegistry() = default; +} +// end catch_interfaces_testcase.cpp +// start catch_leak_detector.cpp + +#ifdef CATCH_CONFIG_WINDOWS_CRTDBG +#include <crtdbg.h> + +namespace Catch { + + LeakDetector::LeakDetector() { + int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); + flag |= _CRTDBG_LEAK_CHECK_DF; + flag |= _CRTDBG_ALLOC_MEM_DF; + _CrtSetDbgFlag(flag); + _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); + _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); + // Change this to leaking allocation's number to break there + _CrtSetBreakAlloc(-1); + } +} + +#else + + Catch::LeakDetector::LeakDetector() {} + +#endif + +Catch::LeakDetector::~LeakDetector() { + Catch::cleanUp(); +} +// end catch_leak_detector.cpp +// start catch_list.cpp + +// start catch_list.h + +#include <set> + +namespace Catch { + + std::size_t listTests( Config const& config ); + + std::size_t listTestsNamesOnly( Config const& config ); + + struct TagInfo { + void add( std::string const& spelling ); + std::string all() const; + + std::set<std::string> spellings; + std::size_t count = 0; + }; + + std::size_t listTags( Config const& config ); + + std::size_t listReporters(); + + Option<std::size_t> list( std::shared_ptr<Config> const& config ); + +} // end namespace Catch + +// end catch_list.h +// start catch_text.h + +namespace Catch { + using namespace clara::TextFlow; +} + +// end catch_text.h +#include <limits> +#include <algorithm> +#include <iomanip> + +namespace Catch { + + std::size_t listTests( Config const& config ) { + TestSpec const& testSpec = config.testSpec(); + if( config.hasTestFilters() ) + Catch::cout() << "Matching test cases:\n"; + else { + Catch::cout() << "All available test cases:\n"; + } + + auto matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); + for( auto const& testCaseInfo : matchedTestCases ) { + Colour::Code colour = testCaseInfo.isHidden() + ? Colour::SecondaryText + : Colour::None; + Colour colourGuard( colour ); + + Catch::cout() << Column( testCaseInfo.name ).initialIndent( 2 ).indent( 4 ) << "\n"; + if( config.verbosity() >= Verbosity::High ) { + Catch::cout() << Column( Catch::Detail::stringify( testCaseInfo.lineInfo ) ).indent(4) << std::endl; + std::string description = testCaseInfo.description; + if( description.empty() ) + description = "(NO DESCRIPTION)"; + Catch::cout() << Column( description ).indent(4) << std::endl; + } + if( !testCaseInfo.tags.empty() ) + Catch::cout() << Column( testCaseInfo.tagsAsString() ).indent( 6 ) << "\n"; + } + + if( !config.hasTestFilters() ) + Catch::cout() << pluralise( matchedTestCases.size(), "test case" ) << '\n' << std::endl; + else + Catch::cout() << pluralise( matchedTestCases.size(), "matching test case" ) << '\n' << std::endl; + return matchedTestCases.size(); + } + + std::size_t listTestsNamesOnly( Config const& config ) { + TestSpec const& testSpec = config.testSpec(); + std::size_t matchedTests = 0; + std::vector<TestCase> matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); + for( auto const& testCaseInfo : matchedTestCases ) { + matchedTests++; + if( startsWith( testCaseInfo.name, '#' ) ) + Catch::cout() << '"' << testCaseInfo.name << '"'; + else + Catch::cout() << testCaseInfo.name; + if ( config.verbosity() >= Verbosity::High ) + Catch::cout() << "\t@" << testCaseInfo.lineInfo; + Catch::cout() << std::endl; + } + return matchedTests; + } + + void TagInfo::add( std::string const& spelling ) { + ++count; + spellings.insert( spelling ); + } + + std::string TagInfo::all() const { + size_t size = 0; + for (auto const& spelling : spellings) { + // Add 2 for the brackes + size += spelling.size() + 2; + } + + std::string out; out.reserve(size); + for (auto const& spelling : spellings) { + out += '['; + out += spelling; + out += ']'; + } + return out; + } + + std::size_t listTags( Config const& config ) { + TestSpec const& testSpec = config.testSpec(); + if( config.hasTestFilters() ) + Catch::cout() << "Tags for matching test cases:\n"; + else { + Catch::cout() << "All available tags:\n"; + } + + std::map<std::string, TagInfo> tagCounts; + + std::vector<TestCase> matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); + for( auto const& testCase : matchedTestCases ) { + for( auto const& tagName : testCase.getTestCaseInfo().tags ) { + std::string lcaseTagName = toLower( tagName ); + auto countIt = tagCounts.find( lcaseTagName ); + if( countIt == tagCounts.end() ) + countIt = tagCounts.insert( std::make_pair( lcaseTagName, TagInfo() ) ).first; + countIt->second.add( tagName ); + } + } + + for( auto const& tagCount : tagCounts ) { + ReusableStringStream rss; + rss << " " << std::setw(2) << tagCount.second.count << " "; + auto str = rss.str(); + auto wrapper = Column( tagCount.second.all() ) + .initialIndent( 0 ) + .indent( str.size() ) + .width( CATCH_CONFIG_CONSOLE_WIDTH-10 ); + Catch::cout() << str << wrapper << '\n'; + } + Catch::cout() << pluralise( tagCounts.size(), "tag" ) << '\n' << std::endl; + return tagCounts.size(); + } + + std::size_t listReporters() { + Catch::cout() << "Available reporters:\n"; + IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories(); + std::size_t maxNameLen = 0; + for( auto const& factoryKvp : factories ) + maxNameLen = (std::max)( maxNameLen, factoryKvp.first.size() ); + + for( auto const& factoryKvp : factories ) { + Catch::cout() + << Column( factoryKvp.first + ":" ) + .indent(2) + .width( 5+maxNameLen ) + + Column( factoryKvp.second->getDescription() ) + .initialIndent(0) + .indent(2) + .width( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen-8 ) + << "\n"; + } + Catch::cout() << std::endl; + return factories.size(); + } + + Option<std::size_t> list( std::shared_ptr<Config> const& config ) { + Option<std::size_t> listedCount; + getCurrentMutableContext().setConfig( config ); + if( config->listTests() ) + listedCount = listedCount.valueOr(0) + listTests( *config ); + if( config->listTestNamesOnly() ) + listedCount = listedCount.valueOr(0) + listTestsNamesOnly( *config ); + if( config->listTags() ) + listedCount = listedCount.valueOr(0) + listTags( *config ); + if( config->listReporters() ) + listedCount = listedCount.valueOr(0) + listReporters(); + return listedCount; + } + +} // end namespace Catch +// end catch_list.cpp +// start catch_matchers.cpp + +namespace Catch { +namespace Matchers { + namespace Impl { + + std::string MatcherUntypedBase::toString() const { + if( m_cachedToString.empty() ) + m_cachedToString = describe(); + return m_cachedToString; + } + + MatcherUntypedBase::~MatcherUntypedBase() = default; + + } // namespace Impl +} // namespace Matchers + +using namespace Matchers; +using Matchers::Impl::MatcherBase; + +} // namespace Catch +// end catch_matchers.cpp +// start catch_matchers_exception.cpp + +namespace Catch { +namespace Matchers { +namespace Exception { + +bool ExceptionMessageMatcher::match(std::exception const& ex) const { + return ex.what() == m_message; +} + +std::string ExceptionMessageMatcher::describe() const { + return "exception message matches \"" + m_message + "\""; +} + +} +Exception::ExceptionMessageMatcher Message(std::string const& message) { + return Exception::ExceptionMessageMatcher(message); +} + +// namespace Exception +} // namespace Matchers +} // namespace Catch +// end catch_matchers_exception.cpp +// start catch_matchers_floating.cpp + +// start catch_polyfills.hpp + +namespace Catch { + bool isnan(float f); + bool isnan(double d); +} + +// end catch_polyfills.hpp +// start catch_to_string.hpp + +#include <string> + +namespace Catch { + template <typename T> + std::string to_string(T const& t) { +#if defined(CATCH_CONFIG_CPP11_TO_STRING) + return std::to_string(t); +#else + ReusableStringStream rss; + rss << t; + return rss.str(); +#endif + } +} // end namespace Catch + +// end catch_to_string.hpp +#include <algorithm> +#include <cmath> +#include <cstdlib> +#include <cstdint> +#include <cstring> +#include <sstream> +#include <type_traits> +#include <iomanip> +#include <limits> + +namespace Catch { +namespace { + + int32_t convert(float f) { + static_assert(sizeof(float) == sizeof(int32_t), "Important ULP matcher assumption violated"); + int32_t i; + std::memcpy(&i, &f, sizeof(f)); + return i; + } + + int64_t convert(double d) { + static_assert(sizeof(double) == sizeof(int64_t), "Important ULP matcher assumption violated"); + int64_t i; + std::memcpy(&i, &d, sizeof(d)); + return i; + } + + template <typename FP> + bool almostEqualUlps(FP lhs, FP rhs, uint64_t maxUlpDiff) { + // Comparison with NaN should always be false. + // This way we can rule it out before getting into the ugly details + if (Catch::isnan(lhs) || Catch::isnan(rhs)) { + return false; + } + + auto lc = convert(lhs); + auto rc = convert(rhs); + + if ((lc < 0) != (rc < 0)) { + // Potentially we can have +0 and -0 + return lhs == rhs; + } + + // static cast as a workaround for IBM XLC + auto ulpDiff = std::abs(static_cast<FP>(lc - rc)); + return static_cast<uint64_t>(ulpDiff) <= maxUlpDiff; + } + +#if defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) + + float nextafter(float x, float y) { + return ::nextafterf(x, y); + } + + double nextafter(double x, double y) { + return ::nextafter(x, y); + } + +#endif // ^^^ CATCH_CONFIG_GLOBAL_NEXTAFTER ^^^ + +template <typename FP> +FP step(FP start, FP direction, uint64_t steps) { + for (uint64_t i = 0; i < steps; ++i) { +#if defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) + start = Catch::nextafter(start, direction); +#else + start = std::nextafter(start, direction); +#endif + } + return start; +} + +// Performs equivalent check of std::fabs(lhs - rhs) <= margin +// But without the subtraction to allow for INFINITY in comparison +bool marginComparison(double lhs, double rhs, double margin) { + return (lhs + margin >= rhs) && (rhs + margin >= lhs); +} + +template <typename FloatingPoint> +void write(std::ostream& out, FloatingPoint num) { + out << std::scientific + << std::setprecision(std::numeric_limits<FloatingPoint>::max_digits10 - 1) + << num; +} + +} // end anonymous namespace + +namespace Matchers { +namespace Floating { + + enum class FloatingPointKind : uint8_t { + Float, + Double + }; + + WithinAbsMatcher::WithinAbsMatcher(double target, double margin) + :m_target{ target }, m_margin{ margin } { + CATCH_ENFORCE(margin >= 0, "Invalid margin: " << margin << '.' + << " Margin has to be non-negative."); + } + + // Performs equivalent check of std::fabs(lhs - rhs) <= margin + // But without the subtraction to allow for INFINITY in comparison + bool WithinAbsMatcher::match(double const& matchee) const { + return (matchee + m_margin >= m_target) && (m_target + m_margin >= matchee); + } + + std::string WithinAbsMatcher::describe() const { + return "is within " + ::Catch::Detail::stringify(m_margin) + " of " + ::Catch::Detail::stringify(m_target); + } + + WithinUlpsMatcher::WithinUlpsMatcher(double target, uint64_t ulps, FloatingPointKind baseType) + :m_target{ target }, m_ulps{ ulps }, m_type{ baseType } { + CATCH_ENFORCE(m_type == FloatingPointKind::Double + || m_ulps < (std::numeric_limits<uint32_t>::max)(), + "Provided ULP is impossibly large for a float comparison."); + } + +#if defined(__clang__) +#pragma clang diagnostic push +// Clang <3.5 reports on the default branch in the switch below +#pragma clang diagnostic ignored "-Wunreachable-code" +#endif + + bool WithinUlpsMatcher::match(double const& matchee) const { + switch (m_type) { + case FloatingPointKind::Float: + return almostEqualUlps<float>(static_cast<float>(matchee), static_cast<float>(m_target), m_ulps); + case FloatingPointKind::Double: + return almostEqualUlps<double>(matchee, m_target, m_ulps); + default: + CATCH_INTERNAL_ERROR( "Unknown FloatingPointKind value" ); + } + } + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + + std::string WithinUlpsMatcher::describe() const { + std::stringstream ret; + + ret << "is within " << m_ulps << " ULPs of "; + + if (m_type == FloatingPointKind::Float) { + write(ret, static_cast<float>(m_target)); + ret << 'f'; + } else { + write(ret, m_target); + } + + ret << " (["; + if (m_type == FloatingPointKind::Double) { + write(ret, step(m_target, static_cast<double>(-INFINITY), m_ulps)); + ret << ", "; + write(ret, step(m_target, static_cast<double>( INFINITY), m_ulps)); + } else { + // We have to cast INFINITY to float because of MinGW, see #1782 + write(ret, step(static_cast<float>(m_target), static_cast<float>(-INFINITY), m_ulps)); + ret << ", "; + write(ret, step(static_cast<float>(m_target), static_cast<float>( INFINITY), m_ulps)); + } + ret << "])"; + + return ret.str(); + } + + WithinRelMatcher::WithinRelMatcher(double target, double epsilon): + m_target(target), + m_epsilon(epsilon){ + CATCH_ENFORCE(m_epsilon >= 0., "Relative comparison with epsilon < 0 does not make sense."); + CATCH_ENFORCE(m_epsilon < 1., "Relative comparison with epsilon >= 1 does not make sense."); + } + + bool WithinRelMatcher::match(double const& matchee) const { + const auto relMargin = m_epsilon * (std::max)(std::fabs(matchee), std::fabs(m_target)); + return marginComparison(matchee, m_target, + std::isinf(relMargin)? 0 : relMargin); + } + + std::string WithinRelMatcher::describe() const { + Catch::ReusableStringStream sstr; + sstr << "and " << m_target << " are within " << m_epsilon * 100. << "% of each other"; + return sstr.str(); + } + +}// namespace Floating + +Floating::WithinUlpsMatcher WithinULP(double target, uint64_t maxUlpDiff) { + return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Double); +} + +Floating::WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff) { + return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Float); +} + +Floating::WithinAbsMatcher WithinAbs(double target, double margin) { + return Floating::WithinAbsMatcher(target, margin); +} + +Floating::WithinRelMatcher WithinRel(double target, double eps) { + return Floating::WithinRelMatcher(target, eps); +} + +Floating::WithinRelMatcher WithinRel(double target) { + return Floating::WithinRelMatcher(target, std::numeric_limits<double>::epsilon() * 100); +} + +Floating::WithinRelMatcher WithinRel(float target, float eps) { + return Floating::WithinRelMatcher(target, eps); +} + +Floating::WithinRelMatcher WithinRel(float target) { + return Floating::WithinRelMatcher(target, std::numeric_limits<float>::epsilon() * 100); +} + +} // namespace Matchers +} // namespace Catch +// end catch_matchers_floating.cpp +// start catch_matchers_generic.cpp + +std::string Catch::Matchers::Generic::Detail::finalizeDescription(const std::string& desc) { + if (desc.empty()) { + return "matches undescribed predicate"; + } else { + return "matches predicate: \"" + desc + '"'; + } +} +// end catch_matchers_generic.cpp +// start catch_matchers_string.cpp + +#include <regex> + +namespace Catch { +namespace Matchers { + + namespace StdString { + + CasedString::CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity ) + : m_caseSensitivity( caseSensitivity ), + m_str( adjustString( str ) ) + {} + std::string CasedString::adjustString( std::string const& str ) const { + return m_caseSensitivity == CaseSensitive::No + ? toLower( str ) + : str; + } + std::string CasedString::caseSensitivitySuffix() const { + return m_caseSensitivity == CaseSensitive::No + ? " (case insensitive)" + : std::string(); + } + + StringMatcherBase::StringMatcherBase( std::string const& operation, CasedString const& comparator ) + : m_comparator( comparator ), + m_operation( operation ) { + } + + std::string StringMatcherBase::describe() const { + std::string description; + description.reserve(5 + m_operation.size() + m_comparator.m_str.size() + + m_comparator.caseSensitivitySuffix().size()); + description += m_operation; + description += ": \""; + description += m_comparator.m_str; + description += "\""; + description += m_comparator.caseSensitivitySuffix(); + return description; + } + + EqualsMatcher::EqualsMatcher( CasedString const& comparator ) : StringMatcherBase( "equals", comparator ) {} + + bool EqualsMatcher::match( std::string const& source ) const { + return m_comparator.adjustString( source ) == m_comparator.m_str; + } + + ContainsMatcher::ContainsMatcher( CasedString const& comparator ) : StringMatcherBase( "contains", comparator ) {} + + bool ContainsMatcher::match( std::string const& source ) const { + return contains( m_comparator.adjustString( source ), m_comparator.m_str ); + } + + StartsWithMatcher::StartsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "starts with", comparator ) {} + + bool StartsWithMatcher::match( std::string const& source ) const { + return startsWith( m_comparator.adjustString( source ), m_comparator.m_str ); + } + + EndsWithMatcher::EndsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "ends with", comparator ) {} + + bool EndsWithMatcher::match( std::string const& source ) const { + return endsWith( m_comparator.adjustString( source ), m_comparator.m_str ); + } + + RegexMatcher::RegexMatcher(std::string regex, CaseSensitive::Choice caseSensitivity): m_regex(std::move(regex)), m_caseSensitivity(caseSensitivity) {} + + bool RegexMatcher::match(std::string const& matchee) const { + auto flags = std::regex::ECMAScript; // ECMAScript is the default syntax option anyway + if (m_caseSensitivity == CaseSensitive::Choice::No) { + flags |= std::regex::icase; + } + auto reg = std::regex(m_regex, flags); + return std::regex_match(matchee, reg); + } + + std::string RegexMatcher::describe() const { + return "matches " + ::Catch::Detail::stringify(m_regex) + ((m_caseSensitivity == CaseSensitive::Choice::Yes)? " case sensitively" : " case insensitively"); + } + + } // namespace StdString + + StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity ) { + return StdString::EqualsMatcher( StdString::CasedString( str, caseSensitivity) ); + } + StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity ) { + return StdString::ContainsMatcher( StdString::CasedString( str, caseSensitivity) ); + } + StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) { + return StdString::EndsWithMatcher( StdString::CasedString( str, caseSensitivity) ); + } + StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) { + return StdString::StartsWithMatcher( StdString::CasedString( str, caseSensitivity) ); + } + + StdString::RegexMatcher Matches(std::string const& regex, CaseSensitive::Choice caseSensitivity) { + return StdString::RegexMatcher(regex, caseSensitivity); + } + +} // namespace Matchers +} // namespace Catch +// end catch_matchers_string.cpp +// start catch_message.cpp + +// start catch_uncaught_exceptions.h + +namespace Catch { + bool uncaught_exceptions(); +} // end namespace Catch + +// end catch_uncaught_exceptions.h +#include <cassert> +#include <stack> + +namespace Catch { + + MessageInfo::MessageInfo( StringRef const& _macroName, + SourceLineInfo const& _lineInfo, + ResultWas::OfType _type ) + : macroName( _macroName ), + lineInfo( _lineInfo ), + type( _type ), + sequence( ++globalCount ) + {} + + bool MessageInfo::operator==( MessageInfo const& other ) const { + return sequence == other.sequence; + } + + bool MessageInfo::operator<( MessageInfo const& other ) const { + return sequence < other.sequence; + } + + // This may need protecting if threading support is added + unsigned int MessageInfo::globalCount = 0; + + //////////////////////////////////////////////////////////////////////////// + + Catch::MessageBuilder::MessageBuilder( StringRef const& macroName, + SourceLineInfo const& lineInfo, + ResultWas::OfType type ) + :m_info(macroName, lineInfo, type) {} + + //////////////////////////////////////////////////////////////////////////// + + ScopedMessage::ScopedMessage( MessageBuilder const& builder ) + : m_info( builder.m_info ), m_moved() + { + m_info.message = builder.m_stream.str(); + getResultCapture().pushScopedMessage( m_info ); + } + + ScopedMessage::ScopedMessage( ScopedMessage&& old ) + : m_info( old.m_info ), m_moved() + { + old.m_moved = true; + } + + ScopedMessage::~ScopedMessage() { + if ( !uncaught_exceptions() && !m_moved ){ + getResultCapture().popScopedMessage(m_info); + } + } + + Capturer::Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names ) { + auto trimmed = [&] (size_t start, size_t end) { + while (names[start] == ',' || isspace(static_cast<unsigned char>(names[start]))) { + ++start; + } + while (names[end] == ',' || isspace(static_cast<unsigned char>(names[end]))) { + --end; + } + return names.substr(start, end - start + 1); + }; + auto skipq = [&] (size_t start, char quote) { + for (auto i = start + 1; i < names.size() ; ++i) { + if (names[i] == quote) + return i; + if (names[i] == '\\') + ++i; + } + CATCH_INTERNAL_ERROR("CAPTURE parsing encountered unmatched quote"); + }; + + size_t start = 0; + std::stack<char> openings; + for (size_t pos = 0; pos < names.size(); ++pos) { + char c = names[pos]; + switch (c) { + case '[': + case '{': + case '(': + // It is basically impossible to disambiguate between + // comparison and start of template args in this context +// case '<': + openings.push(c); + break; + case ']': + case '}': + case ')': +// case '>': + openings.pop(); + break; + case '"': + case '\'': + pos = skipq(pos, c); + break; + case ',': + if (start != pos && openings.empty()) { + m_messages.emplace_back(macroName, lineInfo, resultType); + m_messages.back().message = static_cast<std::string>(trimmed(start, pos)); + m_messages.back().message += " := "; + start = pos; + } + } + } + assert(openings.empty() && "Mismatched openings"); + m_messages.emplace_back(macroName, lineInfo, resultType); + m_messages.back().message = static_cast<std::string>(trimmed(start, names.size() - 1)); + m_messages.back().message += " := "; + } + Capturer::~Capturer() { + if ( !uncaught_exceptions() ){ + assert( m_captured == m_messages.size() ); + for( size_t i = 0; i < m_captured; ++i ) + m_resultCapture.popScopedMessage( m_messages[i] ); + } + } + + void Capturer::captureValue( size_t index, std::string const& value ) { + assert( index < m_messages.size() ); + m_messages[index].message += value; + m_resultCapture.pushScopedMessage( m_messages[index] ); + m_captured++; + } + +} // end namespace Catch +// end catch_message.cpp +// start catch_output_redirect.cpp + +// start catch_output_redirect.h +#ifndef TWOBLUECUBES_CATCH_OUTPUT_REDIRECT_H +#define TWOBLUECUBES_CATCH_OUTPUT_REDIRECT_H + +#include <cstdio> +#include <iosfwd> +#include <string> + +namespace Catch { + + class RedirectedStream { + std::ostream& m_originalStream; + std::ostream& m_redirectionStream; + std::streambuf* m_prevBuf; + + public: + RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream ); + ~RedirectedStream(); + }; + + class RedirectedStdOut { + ReusableStringStream m_rss; + RedirectedStream m_cout; + public: + RedirectedStdOut(); + auto str() const -> std::string; + }; + + // StdErr has two constituent streams in C++, std::cerr and std::clog + // This means that we need to redirect 2 streams into 1 to keep proper + // order of writes + class RedirectedStdErr { + ReusableStringStream m_rss; + RedirectedStream m_cerr; + RedirectedStream m_clog; + public: + RedirectedStdErr(); + auto str() const -> std::string; + }; + + class RedirectedStreams { + public: + RedirectedStreams(RedirectedStreams const&) = delete; + RedirectedStreams& operator=(RedirectedStreams const&) = delete; + RedirectedStreams(RedirectedStreams&&) = delete; + RedirectedStreams& operator=(RedirectedStreams&&) = delete; + + RedirectedStreams(std::string& redirectedCout, std::string& redirectedCerr); + ~RedirectedStreams(); + private: + std::string& m_redirectedCout; + std::string& m_redirectedCerr; + RedirectedStdOut m_redirectedStdOut; + RedirectedStdErr m_redirectedStdErr; + }; + +#if defined(CATCH_CONFIG_NEW_CAPTURE) + + // Windows's implementation of std::tmpfile is terrible (it tries + // to create a file inside system folder, thus requiring elevated + // privileges for the binary), so we have to use tmpnam(_s) and + // create the file ourselves there. + class TempFile { + public: + TempFile(TempFile const&) = delete; + TempFile& operator=(TempFile const&) = delete; + TempFile(TempFile&&) = delete; + TempFile& operator=(TempFile&&) = delete; + + TempFile(); + ~TempFile(); + + std::FILE* getFile(); + std::string getContents(); + + private: + std::FILE* m_file = nullptr; + #if defined(_MSC_VER) + char m_buffer[L_tmpnam] = { 0 }; + #endif + }; + + class OutputRedirect { + public: + OutputRedirect(OutputRedirect const&) = delete; + OutputRedirect& operator=(OutputRedirect const&) = delete; + OutputRedirect(OutputRedirect&&) = delete; + OutputRedirect& operator=(OutputRedirect&&) = delete; + + OutputRedirect(std::string& stdout_dest, std::string& stderr_dest); + ~OutputRedirect(); + + private: + int m_originalStdout = -1; + int m_originalStderr = -1; + TempFile m_stdoutFile; + TempFile m_stderrFile; + std::string& m_stdoutDest; + std::string& m_stderrDest; + }; + +#endif + +} // end namespace Catch + +#endif // TWOBLUECUBES_CATCH_OUTPUT_REDIRECT_H +// end catch_output_redirect.h +#include <cstdio> +#include <cstring> +#include <fstream> +#include <sstream> +#include <stdexcept> + +#if defined(CATCH_CONFIG_NEW_CAPTURE) + #if defined(_MSC_VER) + #include <io.h> //_dup and _dup2 + #define dup _dup + #define dup2 _dup2 + #define fileno _fileno + #else + #include <unistd.h> // dup and dup2 + #endif +#endif + +namespace Catch { + + RedirectedStream::RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream ) + : m_originalStream( originalStream ), + m_redirectionStream( redirectionStream ), + m_prevBuf( m_originalStream.rdbuf() ) + { + m_originalStream.rdbuf( m_redirectionStream.rdbuf() ); + } + + RedirectedStream::~RedirectedStream() { + m_originalStream.rdbuf( m_prevBuf ); + } + + RedirectedStdOut::RedirectedStdOut() : m_cout( Catch::cout(), m_rss.get() ) {} + auto RedirectedStdOut::str() const -> std::string { return m_rss.str(); } + + RedirectedStdErr::RedirectedStdErr() + : m_cerr( Catch::cerr(), m_rss.get() ), + m_clog( Catch::clog(), m_rss.get() ) + {} + auto RedirectedStdErr::str() const -> std::string { return m_rss.str(); } + + RedirectedStreams::RedirectedStreams(std::string& redirectedCout, std::string& redirectedCerr) + : m_redirectedCout(redirectedCout), + m_redirectedCerr(redirectedCerr) + {} + + RedirectedStreams::~RedirectedStreams() { + m_redirectedCout += m_redirectedStdOut.str(); + m_redirectedCerr += m_redirectedStdErr.str(); + } + +#if defined(CATCH_CONFIG_NEW_CAPTURE) + +#if defined(_MSC_VER) + TempFile::TempFile() { + if (tmpnam_s(m_buffer)) { + CATCH_RUNTIME_ERROR("Could not get a temp filename"); + } + if (fopen_s(&m_file, m_buffer, "w+")) { + char buffer[100]; + if (strerror_s(buffer, errno)) { + CATCH_RUNTIME_ERROR("Could not translate errno to a string"); + } + CATCH_RUNTIME_ERROR("Could not open the temp file: '" << m_buffer << "' because: " << buffer); + } + } +#else + TempFile::TempFile() { + m_file = std::tmpfile(); + if (!m_file) { + CATCH_RUNTIME_ERROR("Could not create a temp file."); + } + } + +#endif + + TempFile::~TempFile() { + // TBD: What to do about errors here? + std::fclose(m_file); + // We manually create the file on Windows only, on Linux + // it will be autodeleted +#if defined(_MSC_VER) + std::remove(m_buffer); +#endif + } + + FILE* TempFile::getFile() { + return m_file; + } + + std::string TempFile::getContents() { + std::stringstream sstr; + char buffer[100] = {}; + std::rewind(m_file); + while (std::fgets(buffer, sizeof(buffer), m_file)) { + sstr << buffer; + } + return sstr.str(); + } + + OutputRedirect::OutputRedirect(std::string& stdout_dest, std::string& stderr_dest) : + m_originalStdout(dup(1)), + m_originalStderr(dup(2)), + m_stdoutDest(stdout_dest), + m_stderrDest(stderr_dest) { + dup2(fileno(m_stdoutFile.getFile()), 1); + dup2(fileno(m_stderrFile.getFile()), 2); + } + + OutputRedirect::~OutputRedirect() { + Catch::cout() << std::flush; + fflush(stdout); + // Since we support overriding these streams, we flush cerr + // even though std::cerr is unbuffered + Catch::cerr() << std::flush; + Catch::clog() << std::flush; + fflush(stderr); + + dup2(m_originalStdout, 1); + dup2(m_originalStderr, 2); + + m_stdoutDest += m_stdoutFile.getContents(); + m_stderrDest += m_stderrFile.getContents(); + } + +#endif // CATCH_CONFIG_NEW_CAPTURE + +} // namespace Catch + +#if defined(CATCH_CONFIG_NEW_CAPTURE) + #if defined(_MSC_VER) + #undef dup + #undef dup2 + #undef fileno + #endif +#endif +// end catch_output_redirect.cpp +// start catch_polyfills.cpp + +#include <cmath> + +namespace Catch { + +#if !defined(CATCH_CONFIG_POLYFILL_ISNAN) + bool isnan(float f) { + return std::isnan(f); + } + bool isnan(double d) { + return std::isnan(d); + } +#else + // For now we only use this for embarcadero + bool isnan(float f) { + return std::_isnan(f); + } + bool isnan(double d) { + return std::_isnan(d); + } +#endif + +} // end namespace Catch +// end catch_polyfills.cpp +// start catch_random_number_generator.cpp + +namespace Catch { + +namespace { + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4146) // we negate uint32 during the rotate +#endif + // Safe rotr implementation thanks to John Regehr + uint32_t rotate_right(uint32_t val, uint32_t count) { + const uint32_t mask = 31; + count &= mask; + return (val >> count) | (val << (-count & mask)); + } + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +} + + SimplePcg32::SimplePcg32(result_type seed_) { + seed(seed_); + } + + void SimplePcg32::seed(result_type seed_) { + m_state = 0; + (*this)(); + m_state += seed_; + (*this)(); + } + + void SimplePcg32::discard(uint64_t skip) { + // We could implement this to run in O(log n) steps, but this + // should suffice for our use case. + for (uint64_t s = 0; s < skip; ++s) { + static_cast<void>((*this)()); + } + } + + SimplePcg32::result_type SimplePcg32::operator()() { + // prepare the output value + const uint32_t xorshifted = static_cast<uint32_t>(((m_state >> 18u) ^ m_state) >> 27u); + const auto output = rotate_right(xorshifted, m_state >> 59u); + + // advance state + m_state = m_state * 6364136223846793005ULL + s_inc; + + return output; + } + + bool operator==(SimplePcg32 const& lhs, SimplePcg32 const& rhs) { + return lhs.m_state == rhs.m_state; + } + + bool operator!=(SimplePcg32 const& lhs, SimplePcg32 const& rhs) { + return lhs.m_state != rhs.m_state; + } +} +// end catch_random_number_generator.cpp +// start catch_registry_hub.cpp + +// start catch_test_case_registry_impl.h + +#include <vector> +#include <set> +#include <algorithm> +#include <ios> + +namespace Catch { + + class TestCase; + struct IConfig; + + std::vector<TestCase> sortTests( IConfig const& config, std::vector<TestCase> const& unsortedTestCases ); + + bool isThrowSafe( TestCase const& testCase, IConfig const& config ); + bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); + + void enforceNoDuplicateTestCases( std::vector<TestCase> const& functions ); + + std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config ); + std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config ); + + class TestRegistry : public ITestCaseRegistry { + public: + virtual ~TestRegistry() = default; + + virtual void registerTest( TestCase const& testCase ); + + std::vector<TestCase> const& getAllTests() const override; + std::vector<TestCase> const& getAllTestsSorted( IConfig const& config ) const override; + + private: + std::vector<TestCase> m_functions; + mutable RunTests::InWhatOrder m_currentSortOrder = RunTests::InDeclarationOrder; + mutable std::vector<TestCase> m_sortedFunctions; + std::size_t m_unnamedCount = 0; + std::ios_base::Init m_ostreamInit; // Forces cout/ cerr to be initialised + }; + + /////////////////////////////////////////////////////////////////////////// + + class TestInvokerAsFunction : public ITestInvoker { + void(*m_testAsFunction)(); + public: + TestInvokerAsFunction( void(*testAsFunction)() ) noexcept; + + void invoke() const override; + }; + + std::string extractClassName( StringRef const& classOrQualifiedMethodName ); + + /////////////////////////////////////////////////////////////////////////// + +} // end namespace Catch + +// end catch_test_case_registry_impl.h +// start catch_reporter_registry.h + +#include <map> + +namespace Catch { + + class ReporterRegistry : public IReporterRegistry { + + public: + + ~ReporterRegistry() override; + + IStreamingReporterPtr create( std::string const& name, IConfigPtr const& config ) const override; + + void registerReporter( std::string const& name, IReporterFactoryPtr const& factory ); + void registerListener( IReporterFactoryPtr const& factory ); + + FactoryMap const& getFactories() const override; + Listeners const& getListeners() const override; + + private: + FactoryMap m_factories; + Listeners m_listeners; + }; +} + +// end catch_reporter_registry.h +// start catch_tag_alias_registry.h + +// start catch_tag_alias.h + +#include <string> + +namespace Catch { + + struct TagAlias { + TagAlias(std::string const& _tag, SourceLineInfo _lineInfo); + + std::string tag; + SourceLineInfo lineInfo; + }; + +} // end namespace Catch + +// end catch_tag_alias.h +#include <map> + +namespace Catch { + + class TagAliasRegistry : public ITagAliasRegistry { + public: + ~TagAliasRegistry() override; + TagAlias const* find( std::string const& alias ) const override; + std::string expandAliases( std::string const& unexpandedTestSpec ) const override; + void add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ); + + private: + std::map<std::string, TagAlias> m_registry; + }; + +} // end namespace Catch + +// end catch_tag_alias_registry.h +// start catch_startup_exception_registry.h + +#include <vector> +#include <exception> + +namespace Catch { + + class StartupExceptionRegistry { +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + public: + void add(std::exception_ptr const& exception) noexcept; + std::vector<std::exception_ptr> const& getExceptions() const noexcept; + private: + std::vector<std::exception_ptr> m_exceptions; +#endif + }; + +} // end namespace Catch + +// end catch_startup_exception_registry.h +// start catch_singletons.hpp + +namespace Catch { + + struct ISingleton { + virtual ~ISingleton(); + }; + + void addSingleton( ISingleton* singleton ); + void cleanupSingletons(); + + template<typename SingletonImplT, typename InterfaceT = SingletonImplT, typename MutableInterfaceT = InterfaceT> + class Singleton : SingletonImplT, public ISingleton { + + static auto getInternal() -> Singleton* { + static Singleton* s_instance = nullptr; + if( !s_instance ) { + s_instance = new Singleton; + addSingleton( s_instance ); + } + return s_instance; + } + + public: + static auto get() -> InterfaceT const& { + return *getInternal(); + } + static auto getMutable() -> MutableInterfaceT& { + return *getInternal(); + } + }; + +} // namespace Catch + +// end catch_singletons.hpp +namespace Catch { + + namespace { + + class RegistryHub : public IRegistryHub, public IMutableRegistryHub, + private NonCopyable { + + public: // IRegistryHub + RegistryHub() = default; + IReporterRegistry const& getReporterRegistry() const override { + return m_reporterRegistry; + } + ITestCaseRegistry const& getTestCaseRegistry() const override { + return m_testCaseRegistry; + } + IExceptionTranslatorRegistry const& getExceptionTranslatorRegistry() const override { + return m_exceptionTranslatorRegistry; + } + ITagAliasRegistry const& getTagAliasRegistry() const override { + return m_tagAliasRegistry; + } + StartupExceptionRegistry const& getStartupExceptionRegistry() const override { + return m_exceptionRegistry; + } + + public: // IMutableRegistryHub + void registerReporter( std::string const& name, IReporterFactoryPtr const& factory ) override { + m_reporterRegistry.registerReporter( name, factory ); + } + void registerListener( IReporterFactoryPtr const& factory ) override { + m_reporterRegistry.registerListener( factory ); + } + void registerTest( TestCase const& testInfo ) override { + m_testCaseRegistry.registerTest( testInfo ); + } + void registerTranslator( const IExceptionTranslator* translator ) override { + m_exceptionTranslatorRegistry.registerTranslator( translator ); + } + void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) override { + m_tagAliasRegistry.add( alias, tag, lineInfo ); + } + void registerStartupException() noexcept override { +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + m_exceptionRegistry.add(std::current_exception()); +#else + CATCH_INTERNAL_ERROR("Attempted to register active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!"); +#endif + } + IMutableEnumValuesRegistry& getMutableEnumValuesRegistry() override { + return m_enumValuesRegistry; + } + + private: + TestRegistry m_testCaseRegistry; + ReporterRegistry m_reporterRegistry; + ExceptionTranslatorRegistry m_exceptionTranslatorRegistry; + TagAliasRegistry m_tagAliasRegistry; + StartupExceptionRegistry m_exceptionRegistry; + Detail::EnumValuesRegistry m_enumValuesRegistry; + }; + } + + using RegistryHubSingleton = Singleton<RegistryHub, IRegistryHub, IMutableRegistryHub>; + + IRegistryHub const& getRegistryHub() { + return RegistryHubSingleton::get(); + } + IMutableRegistryHub& getMutableRegistryHub() { + return RegistryHubSingleton::getMutable(); + } + void cleanUp() { + cleanupSingletons(); + cleanUpContext(); + } + std::string translateActiveException() { + return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException(); + } + +} // end namespace Catch +// end catch_registry_hub.cpp +// start catch_reporter_registry.cpp + +namespace Catch { + + ReporterRegistry::~ReporterRegistry() = default; + + IStreamingReporterPtr ReporterRegistry::create( std::string const& name, IConfigPtr const& config ) const { + auto it = m_factories.find( name ); + if( it == m_factories.end() ) + return nullptr; + return it->second->create( ReporterConfig( config ) ); + } + + void ReporterRegistry::registerReporter( std::string const& name, IReporterFactoryPtr const& factory ) { + m_factories.emplace(name, factory); + } + void ReporterRegistry::registerListener( IReporterFactoryPtr const& factory ) { + m_listeners.push_back( factory ); + } + + IReporterRegistry::FactoryMap const& ReporterRegistry::getFactories() const { + return m_factories; + } + IReporterRegistry::Listeners const& ReporterRegistry::getListeners() const { + return m_listeners; + } + +} +// end catch_reporter_registry.cpp +// start catch_result_type.cpp + +namespace Catch { + + bool isOk( ResultWas::OfType resultType ) { + return ( resultType & ResultWas::FailureBit ) == 0; + } + bool isJustInfo( int flags ) { + return flags == ResultWas::Info; + } + + ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ) { + return static_cast<ResultDisposition::Flags>( static_cast<int>( lhs ) | static_cast<int>( rhs ) ); + } + + bool shouldContinueOnFailure( int flags ) { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; } + bool shouldSuppressFailure( int flags ) { return ( flags & ResultDisposition::SuppressFail ) != 0; } + +} // end namespace Catch +// end catch_result_type.cpp +// start catch_run_context.cpp + +#include <cassert> +#include <algorithm> +#include <sstream> + +namespace Catch { + + namespace Generators { + struct GeneratorTracker : TestCaseTracking::TrackerBase, IGeneratorTracker { + GeneratorBasePtr m_generator; + + GeneratorTracker( TestCaseTracking::NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) + : TrackerBase( nameAndLocation, ctx, parent ) + {} + ~GeneratorTracker(); + + static GeneratorTracker& acquire( TrackerContext& ctx, TestCaseTracking::NameAndLocation const& nameAndLocation ) { + std::shared_ptr<GeneratorTracker> tracker; + + ITracker& currentTracker = ctx.currentTracker(); + // Under specific circumstances, the generator we want + // to acquire is also the current tracker. If this is + // the case, we have to avoid looking through current + // tracker's children, and instead return the current + // tracker. + // A case where this check is important is e.g. + // for (int i = 0; i < 5; ++i) { + // int n = GENERATE(1, 2); + // } + // + // without it, the code above creates 5 nested generators. + if (currentTracker.nameAndLocation() == nameAndLocation) { + auto thisTracker = currentTracker.parent().findChild(nameAndLocation); + assert(thisTracker); + assert(thisTracker->isGeneratorTracker()); + tracker = std::static_pointer_cast<GeneratorTracker>(thisTracker); + } else if ( TestCaseTracking::ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) { + assert( childTracker ); + assert( childTracker->isGeneratorTracker() ); + tracker = std::static_pointer_cast<GeneratorTracker>( childTracker ); + } else { + tracker = std::make_shared<GeneratorTracker>( nameAndLocation, ctx, ¤tTracker ); + currentTracker.addChild( tracker ); + } + + if( !tracker->isComplete() ) { + tracker->open(); + } + + return *tracker; + } + + // TrackerBase interface + bool isGeneratorTracker() const override { return true; } + auto hasGenerator() const -> bool override { + return !!m_generator; + } + void close() override { + TrackerBase::close(); + // If a generator has a child (it is followed by a section) + // and none of its children have started, then we must wait + // until later to start consuming its values. + // This catches cases where `GENERATE` is placed between two + // `SECTION`s. + // **The check for m_children.empty cannot be removed**. + // doing so would break `GENERATE` _not_ followed by `SECTION`s. + const bool should_wait_for_child = [&]() { + // No children -> nobody to wait for + if ( m_children.empty() ) { + return false; + } + // If at least one child started executing, don't wait + if ( std::find_if( + m_children.begin(), + m_children.end(), + []( TestCaseTracking::ITrackerPtr tracker ) { + return tracker->hasStarted(); + } ) != m_children.end() ) { + return false; + } + + // No children have started. We need to check if they _can_ + // start, and thus we should wait for them, or they cannot + // start (due to filters), and we shouldn't wait for them + auto* parent = m_parent; + // This is safe: there is always at least one section + // tracker in a test case tracking tree + while ( !parent->isSectionTracker() ) { + parent = &( parent->parent() ); + } + assert( parent && + "Missing root (test case) level section" ); + + auto const& parentSection = + static_cast<SectionTracker&>( *parent ); + auto const& filters = parentSection.getFilters(); + // No filters -> no restrictions on running sections + if ( filters.empty() ) { + return true; + } + + for ( auto const& child : m_children ) { + if ( child->isSectionTracker() && + std::find( filters.begin(), + filters.end(), + static_cast<SectionTracker&>( *child ) + .trimmedName() ) != + filters.end() ) { + return true; + } + } + return false; + }(); + + // This check is a bit tricky, because m_generator->next() + // has a side-effect, where it consumes generator's current + // value, but we do not want to invoke the side-effect if + // this generator is still waiting for any child to start. + if ( should_wait_for_child || + ( m_runState == CompletedSuccessfully && + m_generator->next() ) ) { + m_children.clear(); + m_runState = Executing; + } + } + + // IGeneratorTracker interface + auto getGenerator() const -> GeneratorBasePtr const& override { + return m_generator; + } + void setGenerator( GeneratorBasePtr&& generator ) override { + m_generator = std::move( generator ); + } + }; + GeneratorTracker::~GeneratorTracker() {} + } + + RunContext::RunContext(IConfigPtr const& _config, IStreamingReporterPtr&& reporter) + : m_runInfo(_config->name()), + m_context(getCurrentMutableContext()), + m_config(_config), + m_reporter(std::move(reporter)), + m_lastAssertionInfo{ StringRef(), SourceLineInfo("",0), StringRef(), ResultDisposition::Normal }, + m_includeSuccessfulResults( m_config->includeSuccessfulResults() || m_reporter->getPreferences().shouldReportAllAssertions ) + { + m_context.setRunner(this); + m_context.setConfig(m_config); + m_context.setResultCapture(this); + m_reporter->testRunStarting(m_runInfo); + } + + RunContext::~RunContext() { + m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, aborting())); + } + + void RunContext::testGroupStarting(std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount) { + m_reporter->testGroupStarting(GroupInfo(testSpec, groupIndex, groupsCount)); + } + + void RunContext::testGroupEnded(std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount) { + m_reporter->testGroupEnded(TestGroupStats(GroupInfo(testSpec, groupIndex, groupsCount), totals, aborting())); + } + + Totals RunContext::runTest(TestCase const& testCase) { + Totals prevTotals = m_totals; + + std::string redirectedCout; + std::string redirectedCerr; + + auto const& testInfo = testCase.getTestCaseInfo(); + + m_reporter->testCaseStarting(testInfo); + + m_activeTestCase = &testCase; + + ITracker& rootTracker = m_trackerContext.startRun(); + assert(rootTracker.isSectionTracker()); + static_cast<SectionTracker&>(rootTracker).addInitialFilters(m_config->getSectionsToRun()); + do { + m_trackerContext.startCycle(); + m_testCaseTracker = &SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocation(testInfo.name, testInfo.lineInfo)); + runCurrentTest(redirectedCout, redirectedCerr); + } while (!m_testCaseTracker->isSuccessfullyCompleted() && !aborting()); + + Totals deltaTotals = m_totals.delta(prevTotals); + if (testInfo.expectedToFail() && deltaTotals.testCases.passed > 0) { + deltaTotals.assertions.failed++; + deltaTotals.testCases.passed--; + deltaTotals.testCases.failed++; + } + m_totals.testCases += deltaTotals.testCases; + m_reporter->testCaseEnded(TestCaseStats(testInfo, + deltaTotals, + redirectedCout, + redirectedCerr, + aborting())); + + m_activeTestCase = nullptr; + m_testCaseTracker = nullptr; + + return deltaTotals; + } + + IConfigPtr RunContext::config() const { + return m_config; + } + + IStreamingReporter& RunContext::reporter() const { + return *m_reporter; + } + + void RunContext::assertionEnded(AssertionResult const & result) { + if (result.getResultType() == ResultWas::Ok) { + m_totals.assertions.passed++; + m_lastAssertionPassed = true; + } else if (!result.isOk()) { + m_lastAssertionPassed = false; + if( m_activeTestCase->getTestCaseInfo().okToFail() ) + m_totals.assertions.failedButOk++; + else + m_totals.assertions.failed++; + } + else { + m_lastAssertionPassed = true; + } + + // We have no use for the return value (whether messages should be cleared), because messages were made scoped + // and should be let to clear themselves out. + static_cast<void>(m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals))); + + if (result.getResultType() != ResultWas::Warning) + m_messageScopes.clear(); + + // Reset working state + resetAssertionInfo(); + m_lastResult = result; + } + void RunContext::resetAssertionInfo() { + m_lastAssertionInfo.macroName = StringRef(); + m_lastAssertionInfo.capturedExpression = "{Unknown expression after the reported line}"_sr; + } + + bool RunContext::sectionStarted(SectionInfo const & sectionInfo, Counts & assertions) { + ITracker& sectionTracker = SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocation(sectionInfo.name, sectionInfo.lineInfo)); + if (!sectionTracker.isOpen()) + return false; + m_activeSections.push_back(§ionTracker); + + m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo; + + m_reporter->sectionStarting(sectionInfo); + + assertions = m_totals.assertions; + + return true; + } + auto RunContext::acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& { + using namespace Generators; + GeneratorTracker& tracker = GeneratorTracker::acquire(m_trackerContext, + TestCaseTracking::NameAndLocation( static_cast<std::string>(generatorName), lineInfo ) ); + m_lastAssertionInfo.lineInfo = lineInfo; + return tracker; + } + + bool RunContext::testForMissingAssertions(Counts& assertions) { + if (assertions.total() != 0) + return false; + if (!m_config->warnAboutMissingAssertions()) + return false; + if (m_trackerContext.currentTracker().hasChildren()) + return false; + m_totals.assertions.failed++; + assertions.failed++; + return true; + } + + void RunContext::sectionEnded(SectionEndInfo const & endInfo) { + Counts assertions = m_totals.assertions - endInfo.prevAssertions; + bool missingAssertions = testForMissingAssertions(assertions); + + if (!m_activeSections.empty()) { + m_activeSections.back()->close(); + m_activeSections.pop_back(); + } + + m_reporter->sectionEnded(SectionStats(endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions)); + m_messages.clear(); + m_messageScopes.clear(); + } + + void RunContext::sectionEndedEarly(SectionEndInfo const & endInfo) { + if (m_unfinishedSections.empty()) + m_activeSections.back()->fail(); + else + m_activeSections.back()->close(); + m_activeSections.pop_back(); + + m_unfinishedSections.push_back(endInfo); + } + +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) + void RunContext::benchmarkPreparing(std::string const& name) { + m_reporter->benchmarkPreparing(name); + } + void RunContext::benchmarkStarting( BenchmarkInfo const& info ) { + m_reporter->benchmarkStarting( info ); + } + void RunContext::benchmarkEnded( BenchmarkStats<> const& stats ) { + m_reporter->benchmarkEnded( stats ); + } + void RunContext::benchmarkFailed(std::string const & error) { + m_reporter->benchmarkFailed(error); + } +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING + + void RunContext::pushScopedMessage(MessageInfo const & message) { + m_messages.push_back(message); + } + + void RunContext::popScopedMessage(MessageInfo const & message) { + m_messages.erase(std::remove(m_messages.begin(), m_messages.end(), message), m_messages.end()); + } + + void RunContext::emplaceUnscopedMessage( MessageBuilder const& builder ) { + m_messageScopes.emplace_back( builder ); + } + + std::string RunContext::getCurrentTestName() const { + return m_activeTestCase + ? m_activeTestCase->getTestCaseInfo().name + : std::string(); + } + + const AssertionResult * RunContext::getLastResult() const { + return &(*m_lastResult); + } + + void RunContext::exceptionEarlyReported() { + m_shouldReportUnexpected = false; + } + + void RunContext::handleFatalErrorCondition( StringRef message ) { + // First notify reporter that bad things happened + m_reporter->fatalErrorEncountered(message); + + // Don't rebuild the result -- the stringification itself can cause more fatal errors + // Instead, fake a result data. + AssertionResultData tempResult( ResultWas::FatalErrorCondition, { false } ); + tempResult.message = static_cast<std::string>(message); + AssertionResult result(m_lastAssertionInfo, tempResult); + + assertionEnded(result); + + handleUnfinishedSections(); + + // Recreate section for test case (as we will lose the one that was in scope) + auto const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); + SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name); + + Counts assertions; + assertions.failed = 1; + SectionStats testCaseSectionStats(testCaseSection, assertions, 0, false); + m_reporter->sectionEnded(testCaseSectionStats); + + auto const& testInfo = m_activeTestCase->getTestCaseInfo(); + + Totals deltaTotals; + deltaTotals.testCases.failed = 1; + deltaTotals.assertions.failed = 1; + m_reporter->testCaseEnded(TestCaseStats(testInfo, + deltaTotals, + std::string(), + std::string(), + false)); + m_totals.testCases.failed++; + testGroupEnded(std::string(), m_totals, 1, 1); + m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, false)); + } + + bool RunContext::lastAssertionPassed() { + return m_lastAssertionPassed; + } + + void RunContext::assertionPassed() { + m_lastAssertionPassed = true; + ++m_totals.assertions.passed; + resetAssertionInfo(); + m_messageScopes.clear(); + } + + bool RunContext::aborting() const { + return m_totals.assertions.failed >= static_cast<std::size_t>(m_config->abortAfter()); + } + + void RunContext::runCurrentTest(std::string & redirectedCout, std::string & redirectedCerr) { + auto const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); + SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name); + m_reporter->sectionStarting(testCaseSection); + Counts prevAssertions = m_totals.assertions; + double duration = 0; + m_shouldReportUnexpected = true; + m_lastAssertionInfo = { "TEST_CASE"_sr, testCaseInfo.lineInfo, StringRef(), ResultDisposition::Normal }; + + seedRng(*m_config); + + Timer timer; + CATCH_TRY { + if (m_reporter->getPreferences().shouldRedirectStdOut) { +#if !defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) + RedirectedStreams redirectedStreams(redirectedCout, redirectedCerr); + + timer.start(); + invokeActiveTestCase(); +#else + OutputRedirect r(redirectedCout, redirectedCerr); + timer.start(); + invokeActiveTestCase(); +#endif + } else { + timer.start(); + invokeActiveTestCase(); + } + duration = timer.getElapsedSeconds(); + } CATCH_CATCH_ANON (TestFailureException&) { + // This just means the test was aborted due to failure + } CATCH_CATCH_ALL { + // Under CATCH_CONFIG_FAST_COMPILE, unexpected exceptions under REQUIRE assertions + // are reported without translation at the point of origin. + if( m_shouldReportUnexpected ) { + AssertionReaction dummyReaction; + handleUnexpectedInflightException( m_lastAssertionInfo, translateActiveException(), dummyReaction ); + } + } + Counts assertions = m_totals.assertions - prevAssertions; + bool missingAssertions = testForMissingAssertions(assertions); + + m_testCaseTracker->close(); + handleUnfinishedSections(); + m_messages.clear(); + m_messageScopes.clear(); + + SectionStats testCaseSectionStats(testCaseSection, assertions, duration, missingAssertions); + m_reporter->sectionEnded(testCaseSectionStats); + } + + void RunContext::invokeActiveTestCase() { + FatalConditionHandlerGuard _(&m_fatalConditionhandler); + m_activeTestCase->invoke(); + } + + void RunContext::handleUnfinishedSections() { + // If sections ended prematurely due to an exception we stored their + // infos here so we can tear them down outside the unwind process. + for (auto it = m_unfinishedSections.rbegin(), + itEnd = m_unfinishedSections.rend(); + it != itEnd; + ++it) + sectionEnded(*it); + m_unfinishedSections.clear(); + } + + void RunContext::handleExpr( + AssertionInfo const& info, + ITransientExpression const& expr, + AssertionReaction& reaction + ) { + m_reporter->assertionStarting( info ); + + bool negated = isFalseTest( info.resultDisposition ); + bool result = expr.getResult() != negated; + + if( result ) { + if (!m_includeSuccessfulResults) { + assertionPassed(); + } + else { + reportExpr(info, ResultWas::Ok, &expr, negated); + } + } + else { + reportExpr(info, ResultWas::ExpressionFailed, &expr, negated ); + populateReaction( reaction ); + } + } + void RunContext::reportExpr( + AssertionInfo const &info, + ResultWas::OfType resultType, + ITransientExpression const *expr, + bool negated ) { + + m_lastAssertionInfo = info; + AssertionResultData data( resultType, LazyExpression( negated ) ); + + AssertionResult assertionResult{ info, data }; + assertionResult.m_resultData.lazyExpression.m_transientExpression = expr; + + assertionEnded( assertionResult ); + } + + void RunContext::handleMessage( + AssertionInfo const& info, + ResultWas::OfType resultType, + StringRef const& message, + AssertionReaction& reaction + ) { + m_reporter->assertionStarting( info ); + + m_lastAssertionInfo = info; + + AssertionResultData data( resultType, LazyExpression( false ) ); + data.message = static_cast<std::string>(message); + AssertionResult assertionResult{ m_lastAssertionInfo, data }; + assertionEnded( assertionResult ); + if( !assertionResult.isOk() ) + populateReaction( reaction ); + } + void RunContext::handleUnexpectedExceptionNotThrown( + AssertionInfo const& info, + AssertionReaction& reaction + ) { + handleNonExpr(info, Catch::ResultWas::DidntThrowException, reaction); + } + + void RunContext::handleUnexpectedInflightException( + AssertionInfo const& info, + std::string const& message, + AssertionReaction& reaction + ) { + m_lastAssertionInfo = info; + + AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) ); + data.message = message; + AssertionResult assertionResult{ info, data }; + assertionEnded( assertionResult ); + populateReaction( reaction ); + } + + void RunContext::populateReaction( AssertionReaction& reaction ) { + reaction.shouldDebugBreak = m_config->shouldDebugBreak(); + reaction.shouldThrow = aborting() || (m_lastAssertionInfo.resultDisposition & ResultDisposition::Normal); + } + + void RunContext::handleIncomplete( + AssertionInfo const& info + ) { + m_lastAssertionInfo = info; + + AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) ); + data.message = "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE"; + AssertionResult assertionResult{ info, data }; + assertionEnded( assertionResult ); + } + void RunContext::handleNonExpr( + AssertionInfo const &info, + ResultWas::OfType resultType, + AssertionReaction &reaction + ) { + m_lastAssertionInfo = info; + + AssertionResultData data( resultType, LazyExpression( false ) ); + AssertionResult assertionResult{ info, data }; + assertionEnded( assertionResult ); + + if( !assertionResult.isOk() ) + populateReaction( reaction ); + } + + IResultCapture& getResultCapture() { + if (auto* capture = getCurrentContext().getResultCapture()) + return *capture; + else + CATCH_INTERNAL_ERROR("No result capture instance"); + } + + void seedRng(IConfig const& config) { + if (config.rngSeed() != 0) { + std::srand(config.rngSeed()); + rng().seed(config.rngSeed()); + } + } + + unsigned int rngSeed() { + return getCurrentContext().getConfig()->rngSeed(); + } + +} +// end catch_run_context.cpp +// start catch_section.cpp + +namespace Catch { + + Section::Section( SectionInfo const& info ) + : m_info( info ), + m_sectionIncluded( getResultCapture().sectionStarted( m_info, m_assertions ) ) + { + m_timer.start(); + } + + Section::~Section() { + if( m_sectionIncluded ) { + SectionEndInfo endInfo{ m_info, m_assertions, m_timer.getElapsedSeconds() }; + if( uncaught_exceptions() ) + getResultCapture().sectionEndedEarly( endInfo ); + else + getResultCapture().sectionEnded( endInfo ); + } + } + + // This indicates whether the section should be executed or not + Section::operator bool() const { + return m_sectionIncluded; + } + +} // end namespace Catch +// end catch_section.cpp +// start catch_section_info.cpp + +namespace Catch { + + SectionInfo::SectionInfo + ( SourceLineInfo const& _lineInfo, + std::string const& _name ) + : name( _name ), + lineInfo( _lineInfo ) + {} + +} // end namespace Catch +// end catch_section_info.cpp +// start catch_session.cpp + +// start catch_session.h + +#include <memory> + +namespace Catch { + + class Session : NonCopyable { + public: + + Session(); + ~Session() override; + + void showHelp() const; + void libIdentify(); + + int applyCommandLine( int argc, char const * const * argv ); + #if defined(CATCH_CONFIG_WCHAR) && defined(_WIN32) && defined(UNICODE) + int applyCommandLine( int argc, wchar_t const * const * argv ); + #endif + + void useConfigData( ConfigData const& configData ); + + template<typename CharT> + int run(int argc, CharT const * const argv[]) { + if (m_startupExceptions) + return 1; + int returnCode = applyCommandLine(argc, argv); + if (returnCode == 0) + returnCode = run(); + return returnCode; + } + + int run(); + + clara::Parser const& cli() const; + void cli( clara::Parser const& newParser ); + ConfigData& configData(); + Config& config(); + private: + int runInternal(); + + clara::Parser m_cli; + ConfigData m_configData; + std::shared_ptr<Config> m_config; + bool m_startupExceptions = false; + }; + +} // end namespace Catch + +// end catch_session.h +// start catch_version.h + +#include <iosfwd> + +namespace Catch { + + // Versioning information + struct Version { + Version( Version const& ) = delete; + Version& operator=( Version const& ) = delete; + Version( unsigned int _majorVersion, + unsigned int _minorVersion, + unsigned int _patchNumber, + char const * const _branchName, + unsigned int _buildNumber ); + + unsigned int const majorVersion; + unsigned int const minorVersion; + unsigned int const patchNumber; + + // buildNumber is only used if branchName is not null + char const * const branchName; + unsigned int const buildNumber; + + friend std::ostream& operator << ( std::ostream& os, Version const& version ); + }; + + Version const& libraryVersion(); +} + +// end catch_version.h +#include <cstdlib> +#include <iomanip> +#include <set> +#include <iterator> + +namespace Catch { + + namespace { + const int MaxExitCode = 255; + + IStreamingReporterPtr createReporter(std::string const& reporterName, IConfigPtr const& config) { + auto reporter = Catch::getRegistryHub().getReporterRegistry().create(reporterName, config); + CATCH_ENFORCE(reporter, "No reporter registered with name: '" << reporterName << "'"); + + return reporter; + } + + IStreamingReporterPtr makeReporter(std::shared_ptr<Config> const& config) { + if (Catch::getRegistryHub().getReporterRegistry().getListeners().empty()) { + return createReporter(config->getReporterName(), config); + } + + // On older platforms, returning std::unique_ptr<ListeningReporter> + // when the return type is std::unique_ptr<IStreamingReporter> + // doesn't compile without a std::move call. However, this causes + // a warning on newer platforms. Thus, we have to work around + // it a bit and downcast the pointer manually. + auto ret = std::unique_ptr<IStreamingReporter>(new ListeningReporter); + auto& multi = static_cast<ListeningReporter&>(*ret); + auto const& listeners = Catch::getRegistryHub().getReporterRegistry().getListeners(); + for (auto const& listener : listeners) { + multi.addListener(listener->create(Catch::ReporterConfig(config))); + } + multi.addReporter(createReporter(config->getReporterName(), config)); + return ret; + } + + class TestGroup { + public: + explicit TestGroup(std::shared_ptr<Config> const& config) + : m_config{config} + , m_context{config, makeReporter(config)} + { + auto const& allTestCases = getAllTestCasesSorted(*m_config); + m_matches = m_config->testSpec().matchesByFilter(allTestCases, *m_config); + auto const& invalidArgs = m_config->testSpec().getInvalidArgs(); + + if (m_matches.empty() && invalidArgs.empty()) { + for (auto const& test : allTestCases) + if (!test.isHidden()) + m_tests.emplace(&test); + } else { + for (auto const& match : m_matches) + m_tests.insert(match.tests.begin(), match.tests.end()); + } + } + + Totals execute() { + auto const& invalidArgs = m_config->testSpec().getInvalidArgs(); + Totals totals; + m_context.testGroupStarting(m_config->name(), 1, 1); + for (auto const& testCase : m_tests) { + if (!m_context.aborting()) + totals += m_context.runTest(*testCase); + else + m_context.reporter().skipTest(*testCase); + } + + for (auto const& match : m_matches) { + if (match.tests.empty()) { + m_context.reporter().noMatchingTestCases(match.name); + totals.error = -1; + } + } + + if (!invalidArgs.empty()) { + for (auto const& invalidArg: invalidArgs) + m_context.reporter().reportInvalidArguments(invalidArg); + } + + m_context.testGroupEnded(m_config->name(), totals, 1, 1); + return totals; + } + + private: + using Tests = std::set<TestCase const*>; + + std::shared_ptr<Config> m_config; + RunContext m_context; + Tests m_tests; + TestSpec::Matches m_matches; + }; + + void applyFilenamesAsTags(Catch::IConfig const& config) { + auto& tests = const_cast<std::vector<TestCase>&>(getAllTestCasesSorted(config)); + for (auto& testCase : tests) { + auto tags = testCase.tags; + + std::string filename = testCase.lineInfo.file; + auto lastSlash = filename.find_last_of("\\/"); + if (lastSlash != std::string::npos) { + filename.erase(0, lastSlash); + filename[0] = '#'; + } + else + { + filename.insert(0, "#"); + } + + auto lastDot = filename.find_last_of('.'); + if (lastDot != std::string::npos) { + filename.erase(lastDot); + } + + tags.push_back(std::move(filename)); + setTags(testCase, tags); + } + } + + } // anon namespace + + Session::Session() { + static bool alreadyInstantiated = false; + if( alreadyInstantiated ) { + CATCH_TRY { CATCH_INTERNAL_ERROR( "Only one instance of Catch::Session can ever be used" ); } + CATCH_CATCH_ALL { getMutableRegistryHub().registerStartupException(); } + } + + // There cannot be exceptions at startup in no-exception mode. +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + const auto& exceptions = getRegistryHub().getStartupExceptionRegistry().getExceptions(); + if ( !exceptions.empty() ) { + config(); + getCurrentMutableContext().setConfig(m_config); + + m_startupExceptions = true; + Colour colourGuard( Colour::Red ); + Catch::cerr() << "Errors occurred during startup!" << '\n'; + // iterate over all exceptions and notify user + for ( const auto& ex_ptr : exceptions ) { + try { + std::rethrow_exception(ex_ptr); + } catch ( std::exception const& ex ) { + Catch::cerr() << Column( ex.what() ).indent(2) << '\n'; + } + } + } +#endif + + alreadyInstantiated = true; + m_cli = makeCommandLineParser( m_configData ); + } + Session::~Session() { + Catch::cleanUp(); + } + + void Session::showHelp() const { + Catch::cout() + << "\nCatch v" << libraryVersion() << "\n" + << m_cli << std::endl + << "For more detailed usage please see the project docs\n" << std::endl; + } + void Session::libIdentify() { + Catch::cout() + << std::left << std::setw(16) << "description: " << "A Catch2 test executable\n" + << std::left << std::setw(16) << "category: " << "testframework\n" + << std::left << std::setw(16) << "framework: " << "Catch Test\n" + << std::left << std::setw(16) << "version: " << libraryVersion() << std::endl; + } + + int Session::applyCommandLine( int argc, char const * const * argv ) { + if( m_startupExceptions ) + return 1; + + auto result = m_cli.parse( clara::Args( argc, argv ) ); + if( !result ) { + config(); + getCurrentMutableContext().setConfig(m_config); + Catch::cerr() + << Colour( Colour::Red ) + << "\nError(s) in input:\n" + << Column( result.errorMessage() ).indent( 2 ) + << "\n\n"; + Catch::cerr() << "Run with -? for usage\n" << std::endl; + return MaxExitCode; + } + + if( m_configData.showHelp ) + showHelp(); + if( m_configData.libIdentify ) + libIdentify(); + m_config.reset(); + return 0; + } + +#if defined(CATCH_CONFIG_WCHAR) && defined(_WIN32) && defined(UNICODE) + int Session::applyCommandLine( int argc, wchar_t const * const * argv ) { + + char **utf8Argv = new char *[ argc ]; + + for ( int i = 0; i < argc; ++i ) { + int bufSize = WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, nullptr, 0, nullptr, nullptr ); + + utf8Argv[ i ] = new char[ bufSize ]; + + WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, utf8Argv[i], bufSize, nullptr, nullptr ); + } + + int returnCode = applyCommandLine( argc, utf8Argv ); + + for ( int i = 0; i < argc; ++i ) + delete [] utf8Argv[ i ]; + + delete [] utf8Argv; + + return returnCode; + } +#endif + + void Session::useConfigData( ConfigData const& configData ) { + m_configData = configData; + m_config.reset(); + } + + int Session::run() { + if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeStart ) != 0 ) { + Catch::cout() << "...waiting for enter/ return before starting" << std::endl; + static_cast<void>(std::getchar()); + } + int exitCode = runInternal(); + if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeExit ) != 0 ) { + Catch::cout() << "...waiting for enter/ return before exiting, with code: " << exitCode << std::endl; + static_cast<void>(std::getchar()); + } + return exitCode; + } + + clara::Parser const& Session::cli() const { + return m_cli; + } + void Session::cli( clara::Parser const& newParser ) { + m_cli = newParser; + } + ConfigData& Session::configData() { + return m_configData; + } + Config& Session::config() { + if( !m_config ) + m_config = std::make_shared<Config>( m_configData ); + return *m_config; + } + + int Session::runInternal() { + if( m_startupExceptions ) + return 1; + + if (m_configData.showHelp || m_configData.libIdentify) { + return 0; + } + + CATCH_TRY { + config(); // Force config to be constructed + + seedRng( *m_config ); + + if( m_configData.filenamesAsTags ) + applyFilenamesAsTags( *m_config ); + + // Handle list request + if( Option<std::size_t> listed = list( m_config ) ) + return (std::min) (MaxExitCode, static_cast<int>(*listed)); + + TestGroup tests { m_config }; + auto const totals = tests.execute(); + + if( m_config->warnAboutNoTests() && totals.error == -1 ) + return 2; + + // Note that on unices only the lower 8 bits are usually used, clamping + // the return value to 255 prevents false negative when some multiple + // of 256 tests has failed + return (std::min) (MaxExitCode, (std::max) (totals.error, static_cast<int>(totals.assertions.failed))); + } +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + catch( std::exception& ex ) { + Catch::cerr() << ex.what() << std::endl; + return MaxExitCode; + } +#endif + } + +} // end namespace Catch +// end catch_session.cpp +// start catch_singletons.cpp + +#include <vector> + +namespace Catch { + + namespace { + static auto getSingletons() -> std::vector<ISingleton*>*& { + static std::vector<ISingleton*>* g_singletons = nullptr; + if( !g_singletons ) + g_singletons = new std::vector<ISingleton*>(); + return g_singletons; + } + } + + ISingleton::~ISingleton() {} + + void addSingleton(ISingleton* singleton ) { + getSingletons()->push_back( singleton ); + } + void cleanupSingletons() { + auto& singletons = getSingletons(); + for( auto singleton : *singletons ) + delete singleton; + delete singletons; + singletons = nullptr; + } + +} // namespace Catch +// end catch_singletons.cpp +// start catch_startup_exception_registry.cpp + +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +namespace Catch { +void StartupExceptionRegistry::add( std::exception_ptr const& exception ) noexcept { + CATCH_TRY { + m_exceptions.push_back(exception); + } CATCH_CATCH_ALL { + // If we run out of memory during start-up there's really not a lot more we can do about it + std::terminate(); + } + } + + std::vector<std::exception_ptr> const& StartupExceptionRegistry::getExceptions() const noexcept { + return m_exceptions; + } + +} // end namespace Catch +#endif +// end catch_startup_exception_registry.cpp +// start catch_stream.cpp + +#include <cstdio> +#include <iostream> +#include <fstream> +#include <sstream> +#include <vector> +#include <memory> + +namespace Catch { + + Catch::IStream::~IStream() = default; + + namespace Detail { namespace { + template<typename WriterF, std::size_t bufferSize=256> + class StreamBufImpl : public std::streambuf { + char data[bufferSize]; + WriterF m_writer; + + public: + StreamBufImpl() { + setp( data, data + sizeof(data) ); + } + + ~StreamBufImpl() noexcept { + StreamBufImpl::sync(); + } + + private: + int overflow( int c ) override { + sync(); + + if( c != EOF ) { + if( pbase() == epptr() ) + m_writer( std::string( 1, static_cast<char>( c ) ) ); + else + sputc( static_cast<char>( c ) ); + } + return 0; + } + + int sync() override { + if( pbase() != pptr() ) { + m_writer( std::string( pbase(), static_cast<std::string::size_type>( pptr() - pbase() ) ) ); + setp( pbase(), epptr() ); + } + return 0; + } + }; + + /////////////////////////////////////////////////////////////////////////// + + struct OutputDebugWriter { + + void operator()( std::string const&str ) { + writeToDebugConsole( str ); + } + }; + + /////////////////////////////////////////////////////////////////////////// + + class FileStream : public IStream { + mutable std::ofstream m_ofs; + public: + FileStream( StringRef filename ) { + m_ofs.open( filename.c_str() ); + CATCH_ENFORCE( !m_ofs.fail(), "Unable to open file: '" << filename << "'" ); + } + ~FileStream() override = default; + public: // IStream + std::ostream& stream() const override { + return m_ofs; + } + }; + + /////////////////////////////////////////////////////////////////////////// + + class CoutStream : public IStream { + mutable std::ostream m_os; + public: + // Store the streambuf from cout up-front because + // cout may get redirected when running tests + CoutStream() : m_os( Catch::cout().rdbuf() ) {} + ~CoutStream() override = default; + + public: // IStream + std::ostream& stream() const override { return m_os; } + }; + + /////////////////////////////////////////////////////////////////////////// + + class DebugOutStream : public IStream { + std::unique_ptr<StreamBufImpl<OutputDebugWriter>> m_streamBuf; + mutable std::ostream m_os; + public: + DebugOutStream() + : m_streamBuf( new StreamBufImpl<OutputDebugWriter>() ), + m_os( m_streamBuf.get() ) + {} + + ~DebugOutStream() override = default; + + public: // IStream + std::ostream& stream() const override { return m_os; } + }; + + }} // namespace anon::detail + + /////////////////////////////////////////////////////////////////////////// + + auto makeStream( StringRef const &filename ) -> IStream const* { + if( filename.empty() ) + return new Detail::CoutStream(); + else if( filename[0] == '%' ) { + if( filename == "%debug" ) + return new Detail::DebugOutStream(); + else + CATCH_ERROR( "Unrecognised stream: '" << filename << "'" ); + } + else + return new Detail::FileStream( filename ); + } + + // This class encapsulates the idea of a pool of ostringstreams that can be reused. + struct StringStreams { + std::vector<std::unique_ptr<std::ostringstream>> m_streams; + std::vector<std::size_t> m_unused; + std::ostringstream m_referenceStream; // Used for copy state/ flags from + + auto add() -> std::size_t { + if( m_unused.empty() ) { + m_streams.push_back( std::unique_ptr<std::ostringstream>( new std::ostringstream ) ); + return m_streams.size()-1; + } + else { + auto index = m_unused.back(); + m_unused.pop_back(); + return index; + } + } + + void release( std::size_t index ) { + m_streams[index]->copyfmt( m_referenceStream ); // Restore initial flags and other state + m_unused.push_back(index); + } + }; + + ReusableStringStream::ReusableStringStream() + : m_index( Singleton<StringStreams>::getMutable().add() ), + m_oss( Singleton<StringStreams>::getMutable().m_streams[m_index].get() ) + {} + + ReusableStringStream::~ReusableStringStream() { + static_cast<std::ostringstream*>( m_oss )->str(""); + m_oss->clear(); + Singleton<StringStreams>::getMutable().release( m_index ); + } + + auto ReusableStringStream::str() const -> std::string { + return static_cast<std::ostringstream*>( m_oss )->str(); + } + + /////////////////////////////////////////////////////////////////////////// + +#ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement these functions + std::ostream& cout() { return std::cout; } + std::ostream& cerr() { return std::cerr; } + std::ostream& clog() { return std::clog; } +#endif +} +// end catch_stream.cpp +// start catch_string_manip.cpp + +#include <algorithm> +#include <ostream> +#include <cstring> +#include <cctype> +#include <vector> + +namespace Catch { + + namespace { + char toLowerCh(char c) { + return static_cast<char>( std::tolower( static_cast<unsigned char>(c) ) ); + } + } + + bool startsWith( std::string const& s, std::string const& prefix ) { + return s.size() >= prefix.size() && std::equal(prefix.begin(), prefix.end(), s.begin()); + } + bool startsWith( std::string const& s, char prefix ) { + return !s.empty() && s[0] == prefix; + } + bool endsWith( std::string const& s, std::string const& suffix ) { + return s.size() >= suffix.size() && std::equal(suffix.rbegin(), suffix.rend(), s.rbegin()); + } + bool endsWith( std::string const& s, char suffix ) { + return !s.empty() && s[s.size()-1] == suffix; + } + bool contains( std::string const& s, std::string const& infix ) { + return s.find( infix ) != std::string::npos; + } + void toLowerInPlace( std::string& s ) { + std::transform( s.begin(), s.end(), s.begin(), toLowerCh ); + } + std::string toLower( std::string const& s ) { + std::string lc = s; + toLowerInPlace( lc ); + return lc; + } + std::string trim( std::string const& str ) { + static char const* whitespaceChars = "\n\r\t "; + std::string::size_type start = str.find_first_not_of( whitespaceChars ); + std::string::size_type end = str.find_last_not_of( whitespaceChars ); + + return start != std::string::npos ? str.substr( start, 1+end-start ) : std::string(); + } + + StringRef trim(StringRef ref) { + const auto is_ws = [](char c) { + return c == ' ' || c == '\t' || c == '\n' || c == '\r'; + }; + size_t real_begin = 0; + while (real_begin < ref.size() && is_ws(ref[real_begin])) { ++real_begin; } + size_t real_end = ref.size(); + while (real_end > real_begin && is_ws(ref[real_end - 1])) { --real_end; } + + return ref.substr(real_begin, real_end - real_begin); + } + + bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) { + bool replaced = false; + std::size_t i = str.find( replaceThis ); + while( i != std::string::npos ) { + replaced = true; + str = str.substr( 0, i ) + withThis + str.substr( i+replaceThis.size() ); + if( i < str.size()-withThis.size() ) + i = str.find( replaceThis, i+withThis.size() ); + else + i = std::string::npos; + } + return replaced; + } + + std::vector<StringRef> splitStringRef( StringRef str, char delimiter ) { + std::vector<StringRef> subStrings; + std::size_t start = 0; + for(std::size_t pos = 0; pos < str.size(); ++pos ) { + if( str[pos] == delimiter ) { + if( pos - start > 1 ) + subStrings.push_back( str.substr( start, pos-start ) ); + start = pos+1; + } + } + if( start < str.size() ) + subStrings.push_back( str.substr( start, str.size()-start ) ); + return subStrings; + } + + pluralise::pluralise( std::size_t count, std::string const& label ) + : m_count( count ), + m_label( label ) + {} + + std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ) { + os << pluraliser.m_count << ' ' << pluraliser.m_label; + if( pluraliser.m_count != 1 ) + os << 's'; + return os; + } + +} +// end catch_string_manip.cpp +// start catch_stringref.cpp + +#include <algorithm> +#include <ostream> +#include <cstring> +#include <cstdint> + +namespace Catch { + StringRef::StringRef( char const* rawChars ) noexcept + : StringRef( rawChars, static_cast<StringRef::size_type>(std::strlen(rawChars) ) ) + {} + + auto StringRef::c_str() const -> char const* { + CATCH_ENFORCE(isNullTerminated(), "Called StringRef::c_str() on a non-null-terminated instance"); + return m_start; + } + auto StringRef::data() const noexcept -> char const* { + return m_start; + } + + auto StringRef::substr( size_type start, size_type size ) const noexcept -> StringRef { + if (start < m_size) { + return StringRef(m_start + start, (std::min)(m_size - start, size)); + } else { + return StringRef(); + } + } + auto StringRef::operator == ( StringRef const& other ) const noexcept -> bool { + return m_size == other.m_size + && (std::memcmp( m_start, other.m_start, m_size ) == 0); + } + + auto operator << ( std::ostream& os, StringRef const& str ) -> std::ostream& { + return os.write(str.data(), str.size()); + } + + auto operator+=( std::string& lhs, StringRef const& rhs ) -> std::string& { + lhs.append(rhs.data(), rhs.size()); + return lhs; + } + +} // namespace Catch +// end catch_stringref.cpp +// start catch_tag_alias.cpp + +namespace Catch { + TagAlias::TagAlias(std::string const & _tag, SourceLineInfo _lineInfo): tag(_tag), lineInfo(_lineInfo) {} +} +// end catch_tag_alias.cpp +// start catch_tag_alias_autoregistrar.cpp + +namespace Catch { + + RegistrarForTagAliases::RegistrarForTagAliases(char const* alias, char const* tag, SourceLineInfo const& lineInfo) { + CATCH_TRY { + getMutableRegistryHub().registerTagAlias(alias, tag, lineInfo); + } CATCH_CATCH_ALL { + // Do not throw when constructing global objects, instead register the exception to be processed later + getMutableRegistryHub().registerStartupException(); + } + } + +} +// end catch_tag_alias_autoregistrar.cpp +// start catch_tag_alias_registry.cpp + +#include <sstream> + +namespace Catch { + + TagAliasRegistry::~TagAliasRegistry() {} + + TagAlias const* TagAliasRegistry::find( std::string const& alias ) const { + auto it = m_registry.find( alias ); + if( it != m_registry.end() ) + return &(it->second); + else + return nullptr; + } + + std::string TagAliasRegistry::expandAliases( std::string const& unexpandedTestSpec ) const { + std::string expandedTestSpec = unexpandedTestSpec; + for( auto const& registryKvp : m_registry ) { + std::size_t pos = expandedTestSpec.find( registryKvp.first ); + if( pos != std::string::npos ) { + expandedTestSpec = expandedTestSpec.substr( 0, pos ) + + registryKvp.second.tag + + expandedTestSpec.substr( pos + registryKvp.first.size() ); + } + } + return expandedTestSpec; + } + + void TagAliasRegistry::add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) { + CATCH_ENFORCE( startsWith(alias, "[@") && endsWith(alias, ']'), + "error: tag alias, '" << alias << "' is not of the form [@alias name].\n" << lineInfo ); + + CATCH_ENFORCE( m_registry.insert(std::make_pair(alias, TagAlias(tag, lineInfo))).second, + "error: tag alias, '" << alias << "' already registered.\n" + << "\tFirst seen at: " << find(alias)->lineInfo << "\n" + << "\tRedefined at: " << lineInfo ); + } + + ITagAliasRegistry::~ITagAliasRegistry() {} + + ITagAliasRegistry const& ITagAliasRegistry::get() { + return getRegistryHub().getTagAliasRegistry(); + } + +} // end namespace Catch +// end catch_tag_alias_registry.cpp +// start catch_test_case_info.cpp + +#include <cctype> +#include <exception> +#include <algorithm> +#include <sstream> + +namespace Catch { + + namespace { + TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) { + if( startsWith( tag, '.' ) || + tag == "!hide" ) + return TestCaseInfo::IsHidden; + else if( tag == "!throws" ) + return TestCaseInfo::Throws; + else if( tag == "!shouldfail" ) + return TestCaseInfo::ShouldFail; + else if( tag == "!mayfail" ) + return TestCaseInfo::MayFail; + else if( tag == "!nonportable" ) + return TestCaseInfo::NonPortable; + else if( tag == "!benchmark" ) + return static_cast<TestCaseInfo::SpecialProperties>( TestCaseInfo::Benchmark | TestCaseInfo::IsHidden ); + else + return TestCaseInfo::None; + } + bool isReservedTag( std::string const& tag ) { + return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !std::isalnum( static_cast<unsigned char>(tag[0]) ); + } + void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) { + CATCH_ENFORCE( !isReservedTag(tag), + "Tag name: [" << tag << "] is not allowed.\n" + << "Tag names starting with non alphanumeric characters are reserved\n" + << _lineInfo ); + } + } + + TestCase makeTestCase( ITestInvoker* _testCase, + std::string const& _className, + NameAndTags const& nameAndTags, + SourceLineInfo const& _lineInfo ) + { + bool isHidden = false; + + // Parse out tags + std::vector<std::string> tags; + std::string desc, tag; + bool inTag = false; + for (char c : nameAndTags.tags) { + if( !inTag ) { + if( c == '[' ) + inTag = true; + else + desc += c; + } + else { + if( c == ']' ) { + TestCaseInfo::SpecialProperties prop = parseSpecialTag( tag ); + if( ( prop & TestCaseInfo::IsHidden ) != 0 ) + isHidden = true; + else if( prop == TestCaseInfo::None ) + enforceNotReservedTag( tag, _lineInfo ); + + // Merged hide tags like `[.approvals]` should be added as + // `[.][approvals]`. The `[.]` is added at later point, so + // we only strip the prefix + if (startsWith(tag, '.') && tag.size() > 1) { + tag.erase(0, 1); + } + tags.push_back( tag ); + tag.clear(); + inTag = false; + } + else + tag += c; + } + } + if( isHidden ) { + // Add all "hidden" tags to make them behave identically + tags.insert( tags.end(), { ".", "!hide" } ); + } + + TestCaseInfo info( static_cast<std::string>(nameAndTags.name), _className, desc, tags, _lineInfo ); + return TestCase( _testCase, std::move(info) ); + } + + void setTags( TestCaseInfo& testCaseInfo, std::vector<std::string> tags ) { + std::sort(begin(tags), end(tags)); + tags.erase(std::unique(begin(tags), end(tags)), end(tags)); + testCaseInfo.lcaseTags.clear(); + + for( auto const& tag : tags ) { + std::string lcaseTag = toLower( tag ); + testCaseInfo.properties = static_cast<TestCaseInfo::SpecialProperties>( testCaseInfo.properties | parseSpecialTag( lcaseTag ) ); + testCaseInfo.lcaseTags.push_back( lcaseTag ); + } + testCaseInfo.tags = std::move(tags); + } + + TestCaseInfo::TestCaseInfo( std::string const& _name, + std::string const& _className, + std::string const& _description, + std::vector<std::string> const& _tags, + SourceLineInfo const& _lineInfo ) + : name( _name ), + className( _className ), + description( _description ), + lineInfo( _lineInfo ), + properties( None ) + { + setTags( *this, _tags ); + } + + bool TestCaseInfo::isHidden() const { + return ( properties & IsHidden ) != 0; + } + bool TestCaseInfo::throws() const { + return ( properties & Throws ) != 0; + } + bool TestCaseInfo::okToFail() const { + return ( properties & (ShouldFail | MayFail ) ) != 0; + } + bool TestCaseInfo::expectedToFail() const { + return ( properties & (ShouldFail ) ) != 0; + } + + std::string TestCaseInfo::tagsAsString() const { + std::string ret; + // '[' and ']' per tag + std::size_t full_size = 2 * tags.size(); + for (const auto& tag : tags) { + full_size += tag.size(); + } + ret.reserve(full_size); + for (const auto& tag : tags) { + ret.push_back('['); + ret.append(tag); + ret.push_back(']'); + } + + return ret; + } + + TestCase::TestCase( ITestInvoker* testCase, TestCaseInfo&& info ) : TestCaseInfo( std::move(info) ), test( testCase ) {} + + TestCase TestCase::withName( std::string const& _newName ) const { + TestCase other( *this ); + other.name = _newName; + return other; + } + + void TestCase::invoke() const { + test->invoke(); + } + + bool TestCase::operator == ( TestCase const& other ) const { + return test.get() == other.test.get() && + name == other.name && + className == other.className; + } + + bool TestCase::operator < ( TestCase const& other ) const { + return name < other.name; + } + + TestCaseInfo const& TestCase::getTestCaseInfo() const + { + return *this; + } + +} // end namespace Catch +// end catch_test_case_info.cpp +// start catch_test_case_registry_impl.cpp + +#include <algorithm> +#include <sstream> + +namespace Catch { + + namespace { + struct TestHasher { + using hash_t = uint64_t; + + explicit TestHasher( hash_t hashSuffix ): + m_hashSuffix{ hashSuffix } {} + + uint32_t operator()( TestCase const& t ) const { + // FNV-1a hash with multiplication fold. + const hash_t prime = 1099511628211u; + hash_t hash = 14695981039346656037u; + for ( const char c : t.name ) { + hash ^= c; + hash *= prime; + } + hash ^= m_hashSuffix; + hash *= prime; + const uint32_t low{ static_cast<uint32_t>( hash ) }; + const uint32_t high{ static_cast<uint32_t>( hash >> 32 ) }; + return low * high; + } + + private: + hash_t m_hashSuffix; + }; + } // end unnamed namespace + + std::vector<TestCase> sortTests( IConfig const& config, std::vector<TestCase> const& unsortedTestCases ) { + switch( config.runOrder() ) { + case RunTests::InDeclarationOrder: + // already in declaration order + break; + + case RunTests::InLexicographicalOrder: { + std::vector<TestCase> sorted = unsortedTestCases; + std::sort( sorted.begin(), sorted.end() ); + return sorted; + } + + case RunTests::InRandomOrder: { + seedRng( config ); + TestHasher h{ config.rngSeed() }; + + using hashedTest = std::pair<TestHasher::hash_t, TestCase const*>; + std::vector<hashedTest> indexed_tests; + indexed_tests.reserve( unsortedTestCases.size() ); + + for (auto const& testCase : unsortedTestCases) { + indexed_tests.emplace_back(h(testCase), &testCase); + } + + std::sort(indexed_tests.begin(), indexed_tests.end(), + [](hashedTest const& lhs, hashedTest const& rhs) { + if (lhs.first == rhs.first) { + return lhs.second->name < rhs.second->name; + } + return lhs.first < rhs.first; + }); + + std::vector<TestCase> sorted; + sorted.reserve( indexed_tests.size() ); + + for (auto const& hashed : indexed_tests) { + sorted.emplace_back(*hashed.second); + } + + return sorted; + } + } + return unsortedTestCases; + } + + bool isThrowSafe( TestCase const& testCase, IConfig const& config ) { + return !testCase.throws() || config.allowThrows(); + } + + bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ) { + return testSpec.matches( testCase ) && isThrowSafe( testCase, config ); + } + + void enforceNoDuplicateTestCases( std::vector<TestCase> const& functions ) { + std::set<TestCase> seenFunctions; + for( auto const& function : functions ) { + auto prev = seenFunctions.insert( function ); + CATCH_ENFORCE( prev.second, + "error: TEST_CASE( \"" << function.name << "\" ) already defined.\n" + << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << "\n" + << "\tRedefined at " << function.getTestCaseInfo().lineInfo ); + } + } + + std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config ) { + std::vector<TestCase> filtered; + filtered.reserve( testCases.size() ); + for (auto const& testCase : testCases) { + if ((!testSpec.hasFilters() && !testCase.isHidden()) || + (testSpec.hasFilters() && matchTest(testCase, testSpec, config))) { + filtered.push_back(testCase); + } + } + return filtered; + } + std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config ) { + return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config ); + } + + void TestRegistry::registerTest( TestCase const& testCase ) { + std::string name = testCase.getTestCaseInfo().name; + if( name.empty() ) { + ReusableStringStream rss; + rss << "Anonymous test case " << ++m_unnamedCount; + return registerTest( testCase.withName( rss.str() ) ); + } + m_functions.push_back( testCase ); + } + + std::vector<TestCase> const& TestRegistry::getAllTests() const { + return m_functions; + } + std::vector<TestCase> const& TestRegistry::getAllTestsSorted( IConfig const& config ) const { + if( m_sortedFunctions.empty() ) + enforceNoDuplicateTestCases( m_functions ); + + if( m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty() ) { + m_sortedFunctions = sortTests( config, m_functions ); + m_currentSortOrder = config.runOrder(); + } + return m_sortedFunctions; + } + + /////////////////////////////////////////////////////////////////////////// + TestInvokerAsFunction::TestInvokerAsFunction( void(*testAsFunction)() ) noexcept : m_testAsFunction( testAsFunction ) {} + + void TestInvokerAsFunction::invoke() const { + m_testAsFunction(); + } + + std::string extractClassName( StringRef const& classOrQualifiedMethodName ) { + std::string className(classOrQualifiedMethodName); + if( startsWith( className, '&' ) ) + { + std::size_t lastColons = className.rfind( "::" ); + std::size_t penultimateColons = className.rfind( "::", lastColons-1 ); + if( penultimateColons == std::string::npos ) + penultimateColons = 1; + className = className.substr( penultimateColons, lastColons-penultimateColons ); + } + return className; + } + +} // end namespace Catch +// end catch_test_case_registry_impl.cpp +// start catch_test_case_tracker.cpp + +#include <algorithm> +#include <cassert> +#include <stdexcept> +#include <memory> +#include <sstream> + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wexit-time-destructors" +#endif + +namespace Catch { +namespace TestCaseTracking { + + NameAndLocation::NameAndLocation( std::string const& _name, SourceLineInfo const& _location ) + : name( _name ), + location( _location ) + {} + + ITracker::~ITracker() = default; + + ITracker& TrackerContext::startRun() { + m_rootTracker = std::make_shared<SectionTracker>( NameAndLocation( "{root}", CATCH_INTERNAL_LINEINFO ), *this, nullptr ); + m_currentTracker = nullptr; + m_runState = Executing; + return *m_rootTracker; + } + + void TrackerContext::endRun() { + m_rootTracker.reset(); + m_currentTracker = nullptr; + m_runState = NotStarted; + } + + void TrackerContext::startCycle() { + m_currentTracker = m_rootTracker.get(); + m_runState = Executing; + } + void TrackerContext::completeCycle() { + m_runState = CompletedCycle; + } + + bool TrackerContext::completedCycle() const { + return m_runState == CompletedCycle; + } + ITracker& TrackerContext::currentTracker() { + return *m_currentTracker; + } + void TrackerContext::setCurrentTracker( ITracker* tracker ) { + m_currentTracker = tracker; + } + + TrackerBase::TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ): + ITracker(nameAndLocation), + m_ctx( ctx ), + m_parent( parent ) + {} + + bool TrackerBase::isComplete() const { + return m_runState == CompletedSuccessfully || m_runState == Failed; + } + bool TrackerBase::isSuccessfullyCompleted() const { + return m_runState == CompletedSuccessfully; + } + bool TrackerBase::isOpen() const { + return m_runState != NotStarted && !isComplete(); + } + bool TrackerBase::hasChildren() const { + return !m_children.empty(); + } + + void TrackerBase::addChild( ITrackerPtr const& child ) { + m_children.push_back( child ); + } + + ITrackerPtr TrackerBase::findChild( NameAndLocation const& nameAndLocation ) { + auto it = std::find_if( m_children.begin(), m_children.end(), + [&nameAndLocation]( ITrackerPtr const& tracker ){ + return + tracker->nameAndLocation().location == nameAndLocation.location && + tracker->nameAndLocation().name == nameAndLocation.name; + } ); + return( it != m_children.end() ) + ? *it + : nullptr; + } + ITracker& TrackerBase::parent() { + assert( m_parent ); // Should always be non-null except for root + return *m_parent; + } + + void TrackerBase::openChild() { + if( m_runState != ExecutingChildren ) { + m_runState = ExecutingChildren; + if( m_parent ) + m_parent->openChild(); + } + } + + bool TrackerBase::isSectionTracker() const { return false; } + bool TrackerBase::isGeneratorTracker() const { return false; } + + void TrackerBase::open() { + m_runState = Executing; + moveToThis(); + if( m_parent ) + m_parent->openChild(); + } + + void TrackerBase::close() { + + // Close any still open children (e.g. generators) + while( &m_ctx.currentTracker() != this ) + m_ctx.currentTracker().close(); + + switch( m_runState ) { + case NeedsAnotherRun: + break; + + case Executing: + m_runState = CompletedSuccessfully; + break; + case ExecutingChildren: + if( std::all_of(m_children.begin(), m_children.end(), [](ITrackerPtr const& t){ return t->isComplete(); }) ) + m_runState = CompletedSuccessfully; + break; + + case NotStarted: + case CompletedSuccessfully: + case Failed: + CATCH_INTERNAL_ERROR( "Illogical state: " << m_runState ); + + default: + CATCH_INTERNAL_ERROR( "Unknown state: " << m_runState ); + } + moveToParent(); + m_ctx.completeCycle(); + } + void TrackerBase::fail() { + m_runState = Failed; + if( m_parent ) + m_parent->markAsNeedingAnotherRun(); + moveToParent(); + m_ctx.completeCycle(); + } + void TrackerBase::markAsNeedingAnotherRun() { + m_runState = NeedsAnotherRun; + } + + void TrackerBase::moveToParent() { + assert( m_parent ); + m_ctx.setCurrentTracker( m_parent ); + } + void TrackerBase::moveToThis() { + m_ctx.setCurrentTracker( this ); + } + + SectionTracker::SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) + : TrackerBase( nameAndLocation, ctx, parent ), + m_trimmed_name(trim(nameAndLocation.name)) + { + if( parent ) { + while( !parent->isSectionTracker() ) + parent = &parent->parent(); + + SectionTracker& parentSection = static_cast<SectionTracker&>( *parent ); + addNextFilters( parentSection.m_filters ); + } + } + + bool SectionTracker::isComplete() const { + bool complete = true; + + if (m_filters.empty() + || m_filters[0] == "" + || std::find(m_filters.begin(), m_filters.end(), m_trimmed_name) != m_filters.end()) { + complete = TrackerBase::isComplete(); + } + return complete; + } + + bool SectionTracker::isSectionTracker() const { return true; } + + SectionTracker& SectionTracker::acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) { + std::shared_ptr<SectionTracker> section; + + ITracker& currentTracker = ctx.currentTracker(); + if( ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) { + assert( childTracker ); + assert( childTracker->isSectionTracker() ); + section = std::static_pointer_cast<SectionTracker>( childTracker ); + } + else { + section = std::make_shared<SectionTracker>( nameAndLocation, ctx, ¤tTracker ); + currentTracker.addChild( section ); + } + if( !ctx.completedCycle() ) + section->tryOpen(); + return *section; + } + + void SectionTracker::tryOpen() { + if( !isComplete() ) + open(); + } + + void SectionTracker::addInitialFilters( std::vector<std::string> const& filters ) { + if( !filters.empty() ) { + m_filters.reserve( m_filters.size() + filters.size() + 2 ); + m_filters.emplace_back(""); // Root - should never be consulted + m_filters.emplace_back(""); // Test Case - not a section filter + m_filters.insert( m_filters.end(), filters.begin(), filters.end() ); + } + } + void SectionTracker::addNextFilters( std::vector<std::string> const& filters ) { + if( filters.size() > 1 ) + m_filters.insert( m_filters.end(), filters.begin()+1, filters.end() ); + } + + std::vector<std::string> const& SectionTracker::getFilters() const { + return m_filters; + } + + std::string const& SectionTracker::trimmedName() const { + return m_trimmed_name; + } + +} // namespace TestCaseTracking + +using TestCaseTracking::ITracker; +using TestCaseTracking::TrackerContext; +using TestCaseTracking::SectionTracker; + +} // namespace Catch + +#if defined(__clang__) +# pragma clang diagnostic pop +#endif +// end catch_test_case_tracker.cpp +// start catch_test_registry.cpp + +namespace Catch { + + auto makeTestInvoker( void(*testAsFunction)() ) noexcept -> ITestInvoker* { + return new(std::nothrow) TestInvokerAsFunction( testAsFunction ); + } + + NameAndTags::NameAndTags( StringRef const& name_ , StringRef const& tags_ ) noexcept : name( name_ ), tags( tags_ ) {} + + AutoReg::AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef const& classOrMethod, NameAndTags const& nameAndTags ) noexcept { + CATCH_TRY { + getMutableRegistryHub() + .registerTest( + makeTestCase( + invoker, + extractClassName( classOrMethod ), + nameAndTags, + lineInfo)); + } CATCH_CATCH_ALL { + // Do not throw when constructing global objects, instead register the exception to be processed later + getMutableRegistryHub().registerStartupException(); + } + } + + AutoReg::~AutoReg() = default; +} +// end catch_test_registry.cpp +// start catch_test_spec.cpp + +#include <algorithm> +#include <string> +#include <vector> +#include <memory> + +namespace Catch { + + TestSpec::Pattern::Pattern( std::string const& name ) + : m_name( name ) + {} + + TestSpec::Pattern::~Pattern() = default; + + std::string const& TestSpec::Pattern::name() const { + return m_name; + } + + TestSpec::NamePattern::NamePattern( std::string const& name, std::string const& filterString ) + : Pattern( filterString ) + , m_wildcardPattern( toLower( name ), CaseSensitive::No ) + {} + + bool TestSpec::NamePattern::matches( TestCaseInfo const& testCase ) const { + return m_wildcardPattern.matches( testCase.name ); + } + + TestSpec::TagPattern::TagPattern( std::string const& tag, std::string const& filterString ) + : Pattern( filterString ) + , m_tag( toLower( tag ) ) + {} + + bool TestSpec::TagPattern::matches( TestCaseInfo const& testCase ) const { + return std::find(begin(testCase.lcaseTags), + end(testCase.lcaseTags), + m_tag) != end(testCase.lcaseTags); + } + + TestSpec::ExcludedPattern::ExcludedPattern( PatternPtr const& underlyingPattern ) + : Pattern( underlyingPattern->name() ) + , m_underlyingPattern( underlyingPattern ) + {} + + bool TestSpec::ExcludedPattern::matches( TestCaseInfo const& testCase ) const { + return !m_underlyingPattern->matches( testCase ); + } + + bool TestSpec::Filter::matches( TestCaseInfo const& testCase ) const { + return std::all_of( m_patterns.begin(), m_patterns.end(), [&]( PatternPtr const& p ){ return p->matches( testCase ); } ); + } + + std::string TestSpec::Filter::name() const { + std::string name; + for( auto const& p : m_patterns ) + name += p->name(); + return name; + } + + bool TestSpec::hasFilters() const { + return !m_filters.empty(); + } + + bool TestSpec::matches( TestCaseInfo const& testCase ) const { + return std::any_of( m_filters.begin(), m_filters.end(), [&]( Filter const& f ){ return f.matches( testCase ); } ); + } + + TestSpec::Matches TestSpec::matchesByFilter( std::vector<TestCase> const& testCases, IConfig const& config ) const + { + Matches matches( m_filters.size() ); + std::transform( m_filters.begin(), m_filters.end(), matches.begin(), [&]( Filter const& filter ){ + std::vector<TestCase const*> currentMatches; + for( auto const& test : testCases ) + if( isThrowSafe( test, config ) && filter.matches( test ) ) + currentMatches.emplace_back( &test ); + return FilterMatch{ filter.name(), currentMatches }; + } ); + return matches; + } + + const TestSpec::vectorStrings& TestSpec::getInvalidArgs() const{ + return (m_invalidArgs); + } + +} +// end catch_test_spec.cpp +// start catch_test_spec_parser.cpp + +namespace Catch { + + TestSpecParser::TestSpecParser( ITagAliasRegistry const& tagAliases ) : m_tagAliases( &tagAliases ) {} + + TestSpecParser& TestSpecParser::parse( std::string const& arg ) { + m_mode = None; + m_exclusion = false; + m_arg = m_tagAliases->expandAliases( arg ); + m_escapeChars.clear(); + m_substring.reserve(m_arg.size()); + m_patternName.reserve(m_arg.size()); + m_realPatternPos = 0; + + for( m_pos = 0; m_pos < m_arg.size(); ++m_pos ) + //if visitChar fails + if( !visitChar( m_arg[m_pos] ) ){ + m_testSpec.m_invalidArgs.push_back(arg); + break; + } + endMode(); + return *this; + } + TestSpec TestSpecParser::testSpec() { + addFilter(); + return m_testSpec; + } + bool TestSpecParser::visitChar( char c ) { + if( (m_mode != EscapedName) && (c == '\\') ) { + escape(); + addCharToPattern(c); + return true; + }else if((m_mode != EscapedName) && (c == ',') ) { + return separate(); + } + + switch( m_mode ) { + case None: + if( processNoneChar( c ) ) + return true; + break; + case Name: + processNameChar( c ); + break; + case EscapedName: + endMode(); + addCharToPattern(c); + return true; + default: + case Tag: + case QuotedName: + if( processOtherChar( c ) ) + return true; + break; + } + + m_substring += c; + if( !isControlChar( c ) ) { + m_patternName += c; + m_realPatternPos++; + } + return true; + } + // Two of the processing methods return true to signal the caller to return + // without adding the given character to the current pattern strings + bool TestSpecParser::processNoneChar( char c ) { + switch( c ) { + case ' ': + return true; + case '~': + m_exclusion = true; + return false; + case '[': + startNewMode( Tag ); + return false; + case '"': + startNewMode( QuotedName ); + return false; + default: + startNewMode( Name ); + return false; + } + } + void TestSpecParser::processNameChar( char c ) { + if( c == '[' ) { + if( m_substring == "exclude:" ) + m_exclusion = true; + else + endMode(); + startNewMode( Tag ); + } + } + bool TestSpecParser::processOtherChar( char c ) { + if( !isControlChar( c ) ) + return false; + m_substring += c; + endMode(); + return true; + } + void TestSpecParser::startNewMode( Mode mode ) { + m_mode = mode; + } + void TestSpecParser::endMode() { + switch( m_mode ) { + case Name: + case QuotedName: + return addNamePattern(); + case Tag: + return addTagPattern(); + case EscapedName: + revertBackToLastMode(); + return; + case None: + default: + return startNewMode( None ); + } + } + void TestSpecParser::escape() { + saveLastMode(); + m_mode = EscapedName; + m_escapeChars.push_back(m_realPatternPos); + } + bool TestSpecParser::isControlChar( char c ) const { + switch( m_mode ) { + default: + return false; + case None: + return c == '~'; + case Name: + return c == '['; + case EscapedName: + return true; + case QuotedName: + return c == '"'; + case Tag: + return c == '[' || c == ']'; + } + } + + void TestSpecParser::addFilter() { + if( !m_currentFilter.m_patterns.empty() ) { + m_testSpec.m_filters.push_back( m_currentFilter ); + m_currentFilter = TestSpec::Filter(); + } + } + + void TestSpecParser::saveLastMode() { + lastMode = m_mode; + } + + void TestSpecParser::revertBackToLastMode() { + m_mode = lastMode; + } + + bool TestSpecParser::separate() { + if( (m_mode==QuotedName) || (m_mode==Tag) ){ + //invalid argument, signal failure to previous scope. + m_mode = None; + m_pos = m_arg.size(); + m_substring.clear(); + m_patternName.clear(); + m_realPatternPos = 0; + return false; + } + endMode(); + addFilter(); + return true; //success + } + + std::string TestSpecParser::preprocessPattern() { + std::string token = m_patternName; + for (std::size_t i = 0; i < m_escapeChars.size(); ++i) + token = token.substr(0, m_escapeChars[i] - i) + token.substr(m_escapeChars[i] - i + 1); + m_escapeChars.clear(); + if (startsWith(token, "exclude:")) { + m_exclusion = true; + token = token.substr(8); + } + + m_patternName.clear(); + m_realPatternPos = 0; + + return token; + } + + void TestSpecParser::addNamePattern() { + auto token = preprocessPattern(); + + if (!token.empty()) { + TestSpec::PatternPtr pattern = std::make_shared<TestSpec::NamePattern>(token, m_substring); + if (m_exclusion) + pattern = std::make_shared<TestSpec::ExcludedPattern>(pattern); + m_currentFilter.m_patterns.push_back(pattern); + } + m_substring.clear(); + m_exclusion = false; + m_mode = None; + } + + void TestSpecParser::addTagPattern() { + auto token = preprocessPattern(); + + if (!token.empty()) { + // If the tag pattern is the "hide and tag" shorthand (e.g. [.foo]) + // we have to create a separate hide tag and shorten the real one + if (token.size() > 1 && token[0] == '.') { + token.erase(token.begin()); + TestSpec::PatternPtr pattern = std::make_shared<TestSpec::TagPattern>(".", m_substring); + if (m_exclusion) { + pattern = std::make_shared<TestSpec::ExcludedPattern>(pattern); + } + m_currentFilter.m_patterns.push_back(pattern); + } + + TestSpec::PatternPtr pattern = std::make_shared<TestSpec::TagPattern>(token, m_substring); + + if (m_exclusion) { + pattern = std::make_shared<TestSpec::ExcludedPattern>(pattern); + } + m_currentFilter.m_patterns.push_back(pattern); + } + m_substring.clear(); + m_exclusion = false; + m_mode = None; + } + + TestSpec parseTestSpec( std::string const& arg ) { + return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec(); + } + +} // namespace Catch +// end catch_test_spec_parser.cpp +// start catch_timer.cpp + +#include <chrono> + +static const uint64_t nanosecondsInSecond = 1000000000; + +namespace Catch { + + auto getCurrentNanosecondsSinceEpoch() -> uint64_t { + return std::chrono::duration_cast<std::chrono::nanoseconds>( std::chrono::high_resolution_clock::now().time_since_epoch() ).count(); + } + + namespace { + auto estimateClockResolution() -> uint64_t { + uint64_t sum = 0; + static const uint64_t iterations = 1000000; + + auto startTime = getCurrentNanosecondsSinceEpoch(); + + for( std::size_t i = 0; i < iterations; ++i ) { + + uint64_t ticks; + uint64_t baseTicks = getCurrentNanosecondsSinceEpoch(); + do { + ticks = getCurrentNanosecondsSinceEpoch(); + } while( ticks == baseTicks ); + + auto delta = ticks - baseTicks; + sum += delta; + + // If we have been calibrating for over 3 seconds -- the clock + // is terrible and we should move on. + // TBD: How to signal that the measured resolution is probably wrong? + if (ticks > startTime + 3 * nanosecondsInSecond) { + return sum / ( i + 1u ); + } + } + + // We're just taking the mean, here. To do better we could take the std. dev and exclude outliers + // - and potentially do more iterations if there's a high variance. + return sum/iterations; + } + } + auto getEstimatedClockResolution() -> uint64_t { + static auto s_resolution = estimateClockResolution(); + return s_resolution; + } + + void Timer::start() { + m_nanoseconds = getCurrentNanosecondsSinceEpoch(); + } + auto Timer::getElapsedNanoseconds() const -> uint64_t { + return getCurrentNanosecondsSinceEpoch() - m_nanoseconds; + } + auto Timer::getElapsedMicroseconds() const -> uint64_t { + return getElapsedNanoseconds()/1000; + } + auto Timer::getElapsedMilliseconds() const -> unsigned int { + return static_cast<unsigned int>(getElapsedMicroseconds()/1000); + } + auto Timer::getElapsedSeconds() const -> double { + return getElapsedMicroseconds()/1000000.0; + } + +} // namespace Catch +// end catch_timer.cpp +// start catch_tostring.cpp + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wexit-time-destructors" +# pragma clang diagnostic ignored "-Wglobal-constructors" +#endif + +// Enable specific decls locally +#if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) +#define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +#endif + +#include <cmath> +#include <iomanip> + +namespace Catch { + +namespace Detail { + + const std::string unprintableString = "{?}"; + + namespace { + const int hexThreshold = 255; + + struct Endianness { + enum Arch { Big, Little }; + + static Arch which() { + int one = 1; + // If the lowest byte we read is non-zero, we can assume + // that little endian format is used. + auto value = *reinterpret_cast<char*>(&one); + return value ? Little : Big; + } + }; + } + + std::string rawMemoryToString( const void *object, std::size_t size ) { + // Reverse order for little endian architectures + int i = 0, end = static_cast<int>( size ), inc = 1; + if( Endianness::which() == Endianness::Little ) { + i = end-1; + end = inc = -1; + } + + unsigned char const *bytes = static_cast<unsigned char const *>(object); + ReusableStringStream rss; + rss << "0x" << std::setfill('0') << std::hex; + for( ; i != end; i += inc ) + rss << std::setw(2) << static_cast<unsigned>(bytes[i]); + return rss.str(); + } +} + +template<typename T> +std::string fpToString( T value, int precision ) { + if (Catch::isnan(value)) { + return "nan"; + } + + ReusableStringStream rss; + rss << std::setprecision( precision ) + << std::fixed + << value; + std::string d = rss.str(); + std::size_t i = d.find_last_not_of( '0' ); + if( i != std::string::npos && i != d.size()-1 ) { + if( d[i] == '.' ) + i++; + d = d.substr( 0, i+1 ); + } + return d; +} + +//// ======================================================= //// +// +// Out-of-line defs for full specialization of StringMaker +// +//// ======================================================= //// + +std::string StringMaker<std::string>::convert(const std::string& str) { + if (!getCurrentContext().getConfig()->showInvisibles()) { + return '"' + str + '"'; + } + + std::string s("\""); + for (char c : str) { + switch (c) { + case '\n': + s.append("\\n"); + break; + case '\t': + s.append("\\t"); + break; + default: + s.push_back(c); + break; + } + } + s.append("\""); + return s; +} + +#ifdef CATCH_CONFIG_CPP17_STRING_VIEW +std::string StringMaker<std::string_view>::convert(std::string_view str) { + return ::Catch::Detail::stringify(std::string{ str }); +} +#endif + +std::string StringMaker<char const*>::convert(char const* str) { + if (str) { + return ::Catch::Detail::stringify(std::string{ str }); + } else { + return{ "{null string}" }; + } +} +std::string StringMaker<char*>::convert(char* str) { + if (str) { + return ::Catch::Detail::stringify(std::string{ str }); + } else { + return{ "{null string}" }; + } +} + +#ifdef CATCH_CONFIG_WCHAR +std::string StringMaker<std::wstring>::convert(const std::wstring& wstr) { + std::string s; + s.reserve(wstr.size()); + for (auto c : wstr) { + s += (c <= 0xff) ? static_cast<char>(c) : '?'; + } + return ::Catch::Detail::stringify(s); +} + +# ifdef CATCH_CONFIG_CPP17_STRING_VIEW +std::string StringMaker<std::wstring_view>::convert(std::wstring_view str) { + return StringMaker<std::wstring>::convert(std::wstring(str)); +} +# endif + +std::string StringMaker<wchar_t const*>::convert(wchar_t const * str) { + if (str) { + return ::Catch::Detail::stringify(std::wstring{ str }); + } else { + return{ "{null string}" }; + } +} +std::string StringMaker<wchar_t *>::convert(wchar_t * str) { + if (str) { + return ::Catch::Detail::stringify(std::wstring{ str }); + } else { + return{ "{null string}" }; + } +} +#endif + +#if defined(CATCH_CONFIG_CPP17_BYTE) +#include <cstddef> +std::string StringMaker<std::byte>::convert(std::byte value) { + return ::Catch::Detail::stringify(std::to_integer<unsigned long long>(value)); +} +#endif // defined(CATCH_CONFIG_CPP17_BYTE) + +std::string StringMaker<int>::convert(int value) { + return ::Catch::Detail::stringify(static_cast<long long>(value)); +} +std::string StringMaker<long>::convert(long value) { + return ::Catch::Detail::stringify(static_cast<long long>(value)); +} +std::string StringMaker<long long>::convert(long long value) { + ReusableStringStream rss; + rss << value; + if (value > Detail::hexThreshold) { + rss << " (0x" << std::hex << value << ')'; + } + return rss.str(); +} + +std::string StringMaker<unsigned int>::convert(unsigned int value) { + return ::Catch::Detail::stringify(static_cast<unsigned long long>(value)); +} +std::string StringMaker<unsigned long>::convert(unsigned long value) { + return ::Catch::Detail::stringify(static_cast<unsigned long long>(value)); +} +std::string StringMaker<unsigned long long>::convert(unsigned long long value) { + ReusableStringStream rss; + rss << value; + if (value > Detail::hexThreshold) { + rss << " (0x" << std::hex << value << ')'; + } + return rss.str(); +} + +std::string StringMaker<bool>::convert(bool b) { + return b ? "true" : "false"; +} + +std::string StringMaker<signed char>::convert(signed char value) { + if (value == '\r') { + return "'\\r'"; + } else if (value == '\f') { + return "'\\f'"; + } else if (value == '\n') { + return "'\\n'"; + } else if (value == '\t') { + return "'\\t'"; + } else if ('\0' <= value && value < ' ') { + return ::Catch::Detail::stringify(static_cast<unsigned int>(value)); + } else { + char chstr[] = "' '"; + chstr[1] = value; + return chstr; + } +} +std::string StringMaker<char>::convert(char c) { + return ::Catch::Detail::stringify(static_cast<signed char>(c)); +} +std::string StringMaker<unsigned char>::convert(unsigned char c) { + return ::Catch::Detail::stringify(static_cast<char>(c)); +} + +std::string StringMaker<std::nullptr_t>::convert(std::nullptr_t) { + return "nullptr"; +} + +int StringMaker<float>::precision = 5; + +std::string StringMaker<float>::convert(float value) { + return fpToString(value, precision) + 'f'; +} + +int StringMaker<double>::precision = 10; + +std::string StringMaker<double>::convert(double value) { + return fpToString(value, precision); +} + +std::string ratio_string<std::atto>::symbol() { return "a"; } +std::string ratio_string<std::femto>::symbol() { return "f"; } +std::string ratio_string<std::pico>::symbol() { return "p"; } +std::string ratio_string<std::nano>::symbol() { return "n"; } +std::string ratio_string<std::micro>::symbol() { return "u"; } +std::string ratio_string<std::milli>::symbol() { return "m"; } + +} // end namespace Catch + +#if defined(__clang__) +# pragma clang diagnostic pop +#endif + +// end catch_tostring.cpp +// start catch_totals.cpp + +namespace Catch { + + Counts Counts::operator - ( Counts const& other ) const { + Counts diff; + diff.passed = passed - other.passed; + diff.failed = failed - other.failed; + diff.failedButOk = failedButOk - other.failedButOk; + return diff; + } + + Counts& Counts::operator += ( Counts const& other ) { + passed += other.passed; + failed += other.failed; + failedButOk += other.failedButOk; + return *this; + } + + std::size_t Counts::total() const { + return passed + failed + failedButOk; + } + bool Counts::allPassed() const { + return failed == 0 && failedButOk == 0; + } + bool Counts::allOk() const { + return failed == 0; + } + + Totals Totals::operator - ( Totals const& other ) const { + Totals diff; + diff.assertions = assertions - other.assertions; + diff.testCases = testCases - other.testCases; + return diff; + } + + Totals& Totals::operator += ( Totals const& other ) { + assertions += other.assertions; + testCases += other.testCases; + return *this; + } + + Totals Totals::delta( Totals const& prevTotals ) const { + Totals diff = *this - prevTotals; + if( diff.assertions.failed > 0 ) + ++diff.testCases.failed; + else if( diff.assertions.failedButOk > 0 ) + ++diff.testCases.failedButOk; + else + ++diff.testCases.passed; + return diff; + } + +} +// end catch_totals.cpp +// start catch_uncaught_exceptions.cpp + +// start catch_config_uncaught_exceptions.hpp + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + +#ifndef CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP +#define CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP + +#if defined(_MSC_VER) +# if _MSC_VER >= 1900 // Visual Studio 2015 or newer +# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +# endif +#endif + +#include <exception> + +#if defined(__cpp_lib_uncaught_exceptions) \ + && !defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) + +# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +#endif // __cpp_lib_uncaught_exceptions + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) \ + && !defined(CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS) \ + && !defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) + +# define CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +#endif + +#endif // CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP +// end catch_config_uncaught_exceptions.hpp +#include <exception> + +namespace Catch { + bool uncaught_exceptions() { +#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + return false; +#elif defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) + return std::uncaught_exceptions() > 0; +#else + return std::uncaught_exception(); +#endif + } +} // end namespace Catch +// end catch_uncaught_exceptions.cpp +// start catch_version.cpp + +#include <ostream> + +namespace Catch { + + Version::Version + ( unsigned int _majorVersion, + unsigned int _minorVersion, + unsigned int _patchNumber, + char const * const _branchName, + unsigned int _buildNumber ) + : majorVersion( _majorVersion ), + minorVersion( _minorVersion ), + patchNumber( _patchNumber ), + branchName( _branchName ), + buildNumber( _buildNumber ) + {} + + std::ostream& operator << ( std::ostream& os, Version const& version ) { + os << version.majorVersion << '.' + << version.minorVersion << '.' + << version.patchNumber; + // branchName is never null -> 0th char is \0 if it is empty + if (version.branchName[0]) { + os << '-' << version.branchName + << '.' << version.buildNumber; + } + return os; + } + + Version const& libraryVersion() { + static Version version( 2, 13, 10, "", 0 ); + return version; + } + +} +// end catch_version.cpp +// start catch_wildcard_pattern.cpp + +namespace Catch { + + WildcardPattern::WildcardPattern( std::string const& pattern, + CaseSensitive::Choice caseSensitivity ) + : m_caseSensitivity( caseSensitivity ), + m_pattern( normaliseString( pattern ) ) + { + if( startsWith( m_pattern, '*' ) ) { + m_pattern = m_pattern.substr( 1 ); + m_wildcard = WildcardAtStart; + } + if( endsWith( m_pattern, '*' ) ) { + m_pattern = m_pattern.substr( 0, m_pattern.size()-1 ); + m_wildcard = static_cast<WildcardPosition>( m_wildcard | WildcardAtEnd ); + } + } + + bool WildcardPattern::matches( std::string const& str ) const { + switch( m_wildcard ) { + case NoWildcard: + return m_pattern == normaliseString( str ); + case WildcardAtStart: + return endsWith( normaliseString( str ), m_pattern ); + case WildcardAtEnd: + return startsWith( normaliseString( str ), m_pattern ); + case WildcardAtBothEnds: + return contains( normaliseString( str ), m_pattern ); + default: + CATCH_INTERNAL_ERROR( "Unknown enum" ); + } + } + + std::string WildcardPattern::normaliseString( std::string const& str ) const { + return trim( m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str ); + } +} +// end catch_wildcard_pattern.cpp +// start catch_xmlwriter.cpp + +#include <iomanip> +#include <type_traits> + +namespace Catch { + +namespace { + + size_t trailingBytes(unsigned char c) { + if ((c & 0xE0) == 0xC0) { + return 2; + } + if ((c & 0xF0) == 0xE0) { + return 3; + } + if ((c & 0xF8) == 0xF0) { + return 4; + } + CATCH_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered"); + } + + uint32_t headerValue(unsigned char c) { + if ((c & 0xE0) == 0xC0) { + return c & 0x1F; + } + if ((c & 0xF0) == 0xE0) { + return c & 0x0F; + } + if ((c & 0xF8) == 0xF0) { + return c & 0x07; + } + CATCH_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered"); + } + + void hexEscapeChar(std::ostream& os, unsigned char c) { + std::ios_base::fmtflags f(os.flags()); + os << "\\x" + << std::uppercase << std::hex << std::setfill('0') << std::setw(2) + << static_cast<int>(c); + os.flags(f); + } + + bool shouldNewline(XmlFormatting fmt) { + return !!(static_cast<std::underlying_type<XmlFormatting>::type>(fmt & XmlFormatting::Newline)); + } + + bool shouldIndent(XmlFormatting fmt) { + return !!(static_cast<std::underlying_type<XmlFormatting>::type>(fmt & XmlFormatting::Indent)); + } + +} // anonymous namespace + + XmlFormatting operator | (XmlFormatting lhs, XmlFormatting rhs) { + return static_cast<XmlFormatting>( + static_cast<std::underlying_type<XmlFormatting>::type>(lhs) | + static_cast<std::underlying_type<XmlFormatting>::type>(rhs) + ); + } + + XmlFormatting operator & (XmlFormatting lhs, XmlFormatting rhs) { + return static_cast<XmlFormatting>( + static_cast<std::underlying_type<XmlFormatting>::type>(lhs) & + static_cast<std::underlying_type<XmlFormatting>::type>(rhs) + ); + } + + XmlEncode::XmlEncode( std::string const& str, ForWhat forWhat ) + : m_str( str ), + m_forWhat( forWhat ) + {} + + void XmlEncode::encodeTo( std::ostream& os ) const { + // Apostrophe escaping not necessary if we always use " to write attributes + // (see: http://www.w3.org/TR/xml/#syntax) + + for( std::size_t idx = 0; idx < m_str.size(); ++ idx ) { + unsigned char c = m_str[idx]; + switch (c) { + case '<': os << "<"; break; + case '&': os << "&"; break; + + case '>': + // See: http://www.w3.org/TR/xml/#syntax + if (idx > 2 && m_str[idx - 1] == ']' && m_str[idx - 2] == ']') + os << ">"; + else + os << c; + break; + + case '\"': + if (m_forWhat == ForAttributes) + os << """; + else + os << c; + break; + + default: + // Check for control characters and invalid utf-8 + + // Escape control characters in standard ascii + // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0 + if (c < 0x09 || (c > 0x0D && c < 0x20) || c == 0x7F) { + hexEscapeChar(os, c); + break; + } + + // Plain ASCII: Write it to stream + if (c < 0x7F) { + os << c; + break; + } + + // UTF-8 territory + // Check if the encoding is valid and if it is not, hex escape bytes. + // Important: We do not check the exact decoded values for validity, only the encoding format + // First check that this bytes is a valid lead byte: + // This means that it is not encoded as 1111 1XXX + // Or as 10XX XXXX + if (c < 0xC0 || + c >= 0xF8) { + hexEscapeChar(os, c); + break; + } + + auto encBytes = trailingBytes(c); + // Are there enough bytes left to avoid accessing out-of-bounds memory? + if (idx + encBytes - 1 >= m_str.size()) { + hexEscapeChar(os, c); + break; + } + // The header is valid, check data + // The next encBytes bytes must together be a valid utf-8 + // This means: bitpattern 10XX XXXX and the extracted value is sane (ish) + bool valid = true; + uint32_t value = headerValue(c); + for (std::size_t n = 1; n < encBytes; ++n) { + unsigned char nc = m_str[idx + n]; + valid &= ((nc & 0xC0) == 0x80); + value = (value << 6) | (nc & 0x3F); + } + + if ( + // Wrong bit pattern of following bytes + (!valid) || + // Overlong encodings + (value < 0x80) || + (0x80 <= value && value < 0x800 && encBytes > 2) || + (0x800 < value && value < 0x10000 && encBytes > 3) || + // Encoded value out of range + (value >= 0x110000) + ) { + hexEscapeChar(os, c); + break; + } + + // If we got here, this is in fact a valid(ish) utf-8 sequence + for (std::size_t n = 0; n < encBytes; ++n) { + os << m_str[idx + n]; + } + idx += encBytes - 1; + break; + } + } + } + + std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) { + xmlEncode.encodeTo( os ); + return os; + } + + XmlWriter::ScopedElement::ScopedElement( XmlWriter* writer, XmlFormatting fmt ) + : m_writer( writer ), + m_fmt(fmt) + {} + + XmlWriter::ScopedElement::ScopedElement( ScopedElement&& other ) noexcept + : m_writer( other.m_writer ), + m_fmt(other.m_fmt) + { + other.m_writer = nullptr; + other.m_fmt = XmlFormatting::None; + } + XmlWriter::ScopedElement& XmlWriter::ScopedElement::operator=( ScopedElement&& other ) noexcept { + if ( m_writer ) { + m_writer->endElement(); + } + m_writer = other.m_writer; + other.m_writer = nullptr; + m_fmt = other.m_fmt; + other.m_fmt = XmlFormatting::None; + return *this; + } + + XmlWriter::ScopedElement::~ScopedElement() { + if (m_writer) { + m_writer->endElement(m_fmt); + } + } + + XmlWriter::ScopedElement& XmlWriter::ScopedElement::writeText( std::string const& text, XmlFormatting fmt ) { + m_writer->writeText( text, fmt ); + return *this; + } + + XmlWriter::XmlWriter( std::ostream& os ) : m_os( os ) + { + writeDeclaration(); + } + + XmlWriter::~XmlWriter() { + while (!m_tags.empty()) { + endElement(); + } + newlineIfNecessary(); + } + + XmlWriter& XmlWriter::startElement( std::string const& name, XmlFormatting fmt ) { + ensureTagClosed(); + newlineIfNecessary(); + if (shouldIndent(fmt)) { + m_os << m_indent; + m_indent += " "; + } + m_os << '<' << name; + m_tags.push_back( name ); + m_tagIsOpen = true; + applyFormatting(fmt); + return *this; + } + + XmlWriter::ScopedElement XmlWriter::scopedElement( std::string const& name, XmlFormatting fmt ) { + ScopedElement scoped( this, fmt ); + startElement( name, fmt ); + return scoped; + } + + XmlWriter& XmlWriter::endElement(XmlFormatting fmt) { + m_indent = m_indent.substr(0, m_indent.size() - 2); + + if( m_tagIsOpen ) { + m_os << "/>"; + m_tagIsOpen = false; + } else { + newlineIfNecessary(); + if (shouldIndent(fmt)) { + m_os << m_indent; + } + m_os << "</" << m_tags.back() << ">"; + } + m_os << std::flush; + applyFormatting(fmt); + m_tags.pop_back(); + return *this; + } + + XmlWriter& XmlWriter::writeAttribute( std::string const& name, std::string const& attribute ) { + if( !name.empty() && !attribute.empty() ) + m_os << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"'; + return *this; + } + + XmlWriter& XmlWriter::writeAttribute( std::string const& name, bool attribute ) { + m_os << ' ' << name << "=\"" << ( attribute ? "true" : "false" ) << '"'; + return *this; + } + + XmlWriter& XmlWriter::writeText( std::string const& text, XmlFormatting fmt) { + if( !text.empty() ){ + bool tagWasOpen = m_tagIsOpen; + ensureTagClosed(); + if (tagWasOpen && shouldIndent(fmt)) { + m_os << m_indent; + } + m_os << XmlEncode( text ); + applyFormatting(fmt); + } + return *this; + } + + XmlWriter& XmlWriter::writeComment( std::string const& text, XmlFormatting fmt) { + ensureTagClosed(); + if (shouldIndent(fmt)) { + m_os << m_indent; + } + m_os << "<!--" << text << "-->"; + applyFormatting(fmt); + return *this; + } + + void XmlWriter::writeStylesheetRef( std::string const& url ) { + m_os << "<?xml-stylesheet type=\"text/xsl\" href=\"" << url << "\"?>\n"; + } + + XmlWriter& XmlWriter::writeBlankLine() { + ensureTagClosed(); + m_os << '\n'; + return *this; + } + + void XmlWriter::ensureTagClosed() { + if( m_tagIsOpen ) { + m_os << '>' << std::flush; + newlineIfNecessary(); + m_tagIsOpen = false; + } + } + + void XmlWriter::applyFormatting(XmlFormatting fmt) { + m_needsNewline = shouldNewline(fmt); + } + + void XmlWriter::writeDeclaration() { + m_os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; + } + + void XmlWriter::newlineIfNecessary() { + if( m_needsNewline ) { + m_os << std::endl; + m_needsNewline = false; + } + } +} +// end catch_xmlwriter.cpp +// start catch_reporter_bases.cpp + +#include <cstring> +#include <cfloat> +#include <cstdio> +#include <cassert> +#include <memory> + +namespace Catch { + void prepareExpandedExpression(AssertionResult& result) { + result.getExpandedExpression(); + } + + // Because formatting using c++ streams is stateful, drop down to C is required + // Alternatively we could use stringstream, but its performance is... not good. + std::string getFormattedDuration( double duration ) { + // Max exponent + 1 is required to represent the whole part + // + 1 for decimal point + // + 3 for the 3 decimal places + // + 1 for null terminator + const std::size_t maxDoubleSize = DBL_MAX_10_EXP + 1 + 1 + 3 + 1; + char buffer[maxDoubleSize]; + + // Save previous errno, to prevent sprintf from overwriting it + ErrnoGuard guard; +#ifdef _MSC_VER + sprintf_s(buffer, "%.3f", duration); +#else + std::sprintf(buffer, "%.3f", duration); +#endif + return std::string(buffer); + } + + bool shouldShowDuration( IConfig const& config, double duration ) { + if ( config.showDurations() == ShowDurations::Always ) { + return true; + } + if ( config.showDurations() == ShowDurations::Never ) { + return false; + } + const double min = config.minDuration(); + return min >= 0 && duration >= min; + } + + std::string serializeFilters( std::vector<std::string> const& container ) { + ReusableStringStream oss; + bool first = true; + for (auto&& filter : container) + { + if (!first) + oss << ' '; + else + first = false; + + oss << filter; + } + return oss.str(); + } + + TestEventListenerBase::TestEventListenerBase(ReporterConfig const & _config) + :StreamingReporterBase(_config) {} + + std::set<Verbosity> TestEventListenerBase::getSupportedVerbosities() { + return { Verbosity::Quiet, Verbosity::Normal, Verbosity::High }; + } + + void TestEventListenerBase::assertionStarting(AssertionInfo const &) {} + + bool TestEventListenerBase::assertionEnded(AssertionStats const &) { + return false; + } + +} // end namespace Catch +// end catch_reporter_bases.cpp +// start catch_reporter_compact.cpp + +namespace { + +#ifdef CATCH_PLATFORM_MAC + const char* failedString() { return "FAILED"; } + const char* passedString() { return "PASSED"; } +#else + const char* failedString() { return "failed"; } + const char* passedString() { return "passed"; } +#endif + + // Colour::LightGrey + Catch::Colour::Code dimColour() { return Catch::Colour::FileName; } + + std::string bothOrAll( std::size_t count ) { + return count == 1 ? std::string() : + count == 2 ? "both " : "all " ; + } + +} // anon namespace + +namespace Catch { +namespace { +// Colour, message variants: +// - white: No tests ran. +// - red: Failed [both/all] N test cases, failed [both/all] M assertions. +// - white: Passed [both/all] N test cases (no assertions). +// - red: Failed N tests cases, failed M assertions. +// - green: Passed [both/all] N tests cases with M assertions. +void printTotals(std::ostream& out, const Totals& totals) { + if (totals.testCases.total() == 0) { + out << "No tests ran."; + } else if (totals.testCases.failed == totals.testCases.total()) { + Colour colour(Colour::ResultError); + const std::string qualify_assertions_failed = + totals.assertions.failed == totals.assertions.total() ? + bothOrAll(totals.assertions.failed) : std::string(); + out << + "Failed " << bothOrAll(totals.testCases.failed) + << pluralise(totals.testCases.failed, "test case") << ", " + "failed " << qualify_assertions_failed << + pluralise(totals.assertions.failed, "assertion") << '.'; + } else if (totals.assertions.total() == 0) { + out << + "Passed " << bothOrAll(totals.testCases.total()) + << pluralise(totals.testCases.total(), "test case") + << " (no assertions)."; + } else if (totals.assertions.failed) { + Colour colour(Colour::ResultError); + out << + "Failed " << pluralise(totals.testCases.failed, "test case") << ", " + "failed " << pluralise(totals.assertions.failed, "assertion") << '.'; + } else { + Colour colour(Colour::ResultSuccess); + out << + "Passed " << bothOrAll(totals.testCases.passed) + << pluralise(totals.testCases.passed, "test case") << + " with " << pluralise(totals.assertions.passed, "assertion") << '.'; + } +} + +// Implementation of CompactReporter formatting +class AssertionPrinter { +public: + AssertionPrinter& operator= (AssertionPrinter const&) = delete; + AssertionPrinter(AssertionPrinter const&) = delete; + AssertionPrinter(std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages) + : stream(_stream) + , result(_stats.assertionResult) + , messages(_stats.infoMessages) + , itMessage(_stats.infoMessages.begin()) + , printInfoMessages(_printInfoMessages) {} + + void print() { + printSourceInfo(); + + itMessage = messages.begin(); + + switch (result.getResultType()) { + case ResultWas::Ok: + printResultType(Colour::ResultSuccess, passedString()); + printOriginalExpression(); + printReconstructedExpression(); + if (!result.hasExpression()) + printRemainingMessages(Colour::None); + else + printRemainingMessages(); + break; + case ResultWas::ExpressionFailed: + if (result.isOk()) + printResultType(Colour::ResultSuccess, failedString() + std::string(" - but was ok")); + else + printResultType(Colour::Error, failedString()); + printOriginalExpression(); + printReconstructedExpression(); + printRemainingMessages(); + break; + case ResultWas::ThrewException: + printResultType(Colour::Error, failedString()); + printIssue("unexpected exception with message:"); + printMessage(); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::FatalErrorCondition: + printResultType(Colour::Error, failedString()); + printIssue("fatal error condition with message:"); + printMessage(); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::DidntThrowException: + printResultType(Colour::Error, failedString()); + printIssue("expected exception, got none"); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::Info: + printResultType(Colour::None, "info"); + printMessage(); + printRemainingMessages(); + break; + case ResultWas::Warning: + printResultType(Colour::None, "warning"); + printMessage(); + printRemainingMessages(); + break; + case ResultWas::ExplicitFailure: + printResultType(Colour::Error, failedString()); + printIssue("explicitly"); + printRemainingMessages(Colour::None); + break; + // These cases are here to prevent compiler warnings + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + printResultType(Colour::Error, "** internal error **"); + break; + } + } + +private: + void printSourceInfo() const { + Colour colourGuard(Colour::FileName); + stream << result.getSourceInfo() << ':'; + } + + void printResultType(Colour::Code colour, std::string const& passOrFail) const { + if (!passOrFail.empty()) { + { + Colour colourGuard(colour); + stream << ' ' << passOrFail; + } + stream << ':'; + } + } + + void printIssue(std::string const& issue) const { + stream << ' ' << issue; + } + + void printExpressionWas() { + if (result.hasExpression()) { + stream << ';'; + { + Colour colour(dimColour()); + stream << " expression was:"; + } + printOriginalExpression(); + } + } + + void printOriginalExpression() const { + if (result.hasExpression()) { + stream << ' ' << result.getExpression(); + } + } + + void printReconstructedExpression() const { + if (result.hasExpandedExpression()) { + { + Colour colour(dimColour()); + stream << " for: "; + } + stream << result.getExpandedExpression(); + } + } + + void printMessage() { + if (itMessage != messages.end()) { + stream << " '" << itMessage->message << '\''; + ++itMessage; + } + } + + void printRemainingMessages(Colour::Code colour = dimColour()) { + if (itMessage == messages.end()) + return; + + const auto itEnd = messages.cend(); + const auto N = static_cast<std::size_t>(std::distance(itMessage, itEnd)); + + { + Colour colourGuard(colour); + stream << " with " << pluralise(N, "message") << ':'; + } + + while (itMessage != itEnd) { + // If this assertion is a warning ignore any INFO messages + if (printInfoMessages || itMessage->type != ResultWas::Info) { + printMessage(); + if (itMessage != itEnd) { + Colour colourGuard(dimColour()); + stream << " and"; + } + continue; + } + ++itMessage; + } + } + +private: + std::ostream& stream; + AssertionResult const& result; + std::vector<MessageInfo> messages; + std::vector<MessageInfo>::const_iterator itMessage; + bool printInfoMessages; +}; + +} // anon namespace + + std::string CompactReporter::getDescription() { + return "Reports test results on a single line, suitable for IDEs"; + } + + void CompactReporter::noMatchingTestCases( std::string const& spec ) { + stream << "No test cases matched '" << spec << '\'' << std::endl; + } + + void CompactReporter::assertionStarting( AssertionInfo const& ) {} + + bool CompactReporter::assertionEnded( AssertionStats const& _assertionStats ) { + AssertionResult const& result = _assertionStats.assertionResult; + + bool printInfoMessages = true; + + // Drop out if result was successful and we're not printing those + if( !m_config->includeSuccessfulResults() && result.isOk() ) { + if( result.getResultType() != ResultWas::Warning ) + return false; + printInfoMessages = false; + } + + AssertionPrinter printer( stream, _assertionStats, printInfoMessages ); + printer.print(); + + stream << std::endl; + return true; + } + + void CompactReporter::sectionEnded(SectionStats const& _sectionStats) { + double dur = _sectionStats.durationInSeconds; + if ( shouldShowDuration( *m_config, dur ) ) { + stream << getFormattedDuration( dur ) << " s: " << _sectionStats.sectionInfo.name << std::endl; + } + } + + void CompactReporter::testRunEnded( TestRunStats const& _testRunStats ) { + printTotals( stream, _testRunStats.totals ); + stream << '\n' << std::endl; + StreamingReporterBase::testRunEnded( _testRunStats ); + } + + CompactReporter::~CompactReporter() {} + + CATCH_REGISTER_REPORTER( "compact", CompactReporter ) + +} // end namespace Catch +// end catch_reporter_compact.cpp +// start catch_reporter_console.cpp + +#include <cfloat> +#include <cstdio> + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch + // Note that 4062 (not all labels are handled and default is missing) is enabled +#endif + +#if defined(__clang__) +# pragma clang diagnostic push +// For simplicity, benchmarking-only helpers are always enabled +# pragma clang diagnostic ignored "-Wunused-function" +#endif + +namespace Catch { + +namespace { + +// Formatter impl for ConsoleReporter +class ConsoleAssertionPrinter { +public: + ConsoleAssertionPrinter& operator= (ConsoleAssertionPrinter const&) = delete; + ConsoleAssertionPrinter(ConsoleAssertionPrinter const&) = delete; + ConsoleAssertionPrinter(std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages) + : stream(_stream), + stats(_stats), + result(_stats.assertionResult), + colour(Colour::None), + message(result.getMessage()), + messages(_stats.infoMessages), + printInfoMessages(_printInfoMessages) { + switch (result.getResultType()) { + case ResultWas::Ok: + colour = Colour::Success; + passOrFail = "PASSED"; + //if( result.hasMessage() ) + if (_stats.infoMessages.size() == 1) + messageLabel = "with message"; + if (_stats.infoMessages.size() > 1) + messageLabel = "with messages"; + break; + case ResultWas::ExpressionFailed: + if (result.isOk()) { + colour = Colour::Success; + passOrFail = "FAILED - but was ok"; + } else { + colour = Colour::Error; + passOrFail = "FAILED"; + } + if (_stats.infoMessages.size() == 1) + messageLabel = "with message"; + if (_stats.infoMessages.size() > 1) + messageLabel = "with messages"; + break; + case ResultWas::ThrewException: + colour = Colour::Error; + passOrFail = "FAILED"; + messageLabel = "due to unexpected exception with "; + if (_stats.infoMessages.size() == 1) + messageLabel += "message"; + if (_stats.infoMessages.size() > 1) + messageLabel += "messages"; + break; + case ResultWas::FatalErrorCondition: + colour = Colour::Error; + passOrFail = "FAILED"; + messageLabel = "due to a fatal error condition"; + break; + case ResultWas::DidntThrowException: + colour = Colour::Error; + passOrFail = "FAILED"; + messageLabel = "because no exception was thrown where one was expected"; + break; + case ResultWas::Info: + messageLabel = "info"; + break; + case ResultWas::Warning: + messageLabel = "warning"; + break; + case ResultWas::ExplicitFailure: + passOrFail = "FAILED"; + colour = Colour::Error; + if (_stats.infoMessages.size() == 1) + messageLabel = "explicitly with message"; + if (_stats.infoMessages.size() > 1) + messageLabel = "explicitly with messages"; + break; + // These cases are here to prevent compiler warnings + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + passOrFail = "** internal error **"; + colour = Colour::Error; + break; + } + } + + void print() const { + printSourceInfo(); + if (stats.totals.assertions.total() > 0) { + printResultType(); + printOriginalExpression(); + printReconstructedExpression(); + } else { + stream << '\n'; + } + printMessage(); + } + +private: + void printResultType() const { + if (!passOrFail.empty()) { + Colour colourGuard(colour); + stream << passOrFail << ":\n"; + } + } + void printOriginalExpression() const { + if (result.hasExpression()) { + Colour colourGuard(Colour::OriginalExpression); + stream << " "; + stream << result.getExpressionInMacro(); + stream << '\n'; + } + } + void printReconstructedExpression() const { + if (result.hasExpandedExpression()) { + stream << "with expansion:\n"; + Colour colourGuard(Colour::ReconstructedExpression); + stream << Column(result.getExpandedExpression()).indent(2) << '\n'; + } + } + void printMessage() const { + if (!messageLabel.empty()) + stream << messageLabel << ':' << '\n'; + for (auto const& msg : messages) { + // If this assertion is a warning ignore any INFO messages + if (printInfoMessages || msg.type != ResultWas::Info) + stream << Column(msg.message).indent(2) << '\n'; + } + } + void printSourceInfo() const { + Colour colourGuard(Colour::FileName); + stream << result.getSourceInfo() << ": "; + } + + std::ostream& stream; + AssertionStats const& stats; + AssertionResult const& result; + Colour::Code colour; + std::string passOrFail; + std::string messageLabel; + std::string message; + std::vector<MessageInfo> messages; + bool printInfoMessages; +}; + +std::size_t makeRatio(std::size_t number, std::size_t total) { + std::size_t ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number / total : 0; + return (ratio == 0 && number > 0) ? 1 : ratio; +} + +std::size_t& findMax(std::size_t& i, std::size_t& j, std::size_t& k) { + if (i > j && i > k) + return i; + else if (j > k) + return j; + else + return k; +} + +struct ColumnInfo { + enum Justification { Left, Right }; + std::string name; + int width; + Justification justification; +}; +struct ColumnBreak {}; +struct RowBreak {}; + +class Duration { + enum class Unit { + Auto, + Nanoseconds, + Microseconds, + Milliseconds, + Seconds, + Minutes + }; + static const uint64_t s_nanosecondsInAMicrosecond = 1000; + static const uint64_t s_nanosecondsInAMillisecond = 1000 * s_nanosecondsInAMicrosecond; + static const uint64_t s_nanosecondsInASecond = 1000 * s_nanosecondsInAMillisecond; + static const uint64_t s_nanosecondsInAMinute = 60 * s_nanosecondsInASecond; + + double m_inNanoseconds; + Unit m_units; + +public: + explicit Duration(double inNanoseconds, Unit units = Unit::Auto) + : m_inNanoseconds(inNanoseconds), + m_units(units) { + if (m_units == Unit::Auto) { + if (m_inNanoseconds < s_nanosecondsInAMicrosecond) + m_units = Unit::Nanoseconds; + else if (m_inNanoseconds < s_nanosecondsInAMillisecond) + m_units = Unit::Microseconds; + else if (m_inNanoseconds < s_nanosecondsInASecond) + m_units = Unit::Milliseconds; + else if (m_inNanoseconds < s_nanosecondsInAMinute) + m_units = Unit::Seconds; + else + m_units = Unit::Minutes; + } + + } + + auto value() const -> double { + switch (m_units) { + case Unit::Microseconds: + return m_inNanoseconds / static_cast<double>(s_nanosecondsInAMicrosecond); + case Unit::Milliseconds: + return m_inNanoseconds / static_cast<double>(s_nanosecondsInAMillisecond); + case Unit::Seconds: + return m_inNanoseconds / static_cast<double>(s_nanosecondsInASecond); + case Unit::Minutes: + return m_inNanoseconds / static_cast<double>(s_nanosecondsInAMinute); + default: + return m_inNanoseconds; + } + } + auto unitsAsString() const -> std::string { + switch (m_units) { + case Unit::Nanoseconds: + return "ns"; + case Unit::Microseconds: + return "us"; + case Unit::Milliseconds: + return "ms"; + case Unit::Seconds: + return "s"; + case Unit::Minutes: + return "m"; + default: + return "** internal error **"; + } + + } + friend auto operator << (std::ostream& os, Duration const& duration) -> std::ostream& { + return os << duration.value() << ' ' << duration.unitsAsString(); + } +}; +} // end anon namespace + +class TablePrinter { + std::ostream& m_os; + std::vector<ColumnInfo> m_columnInfos; + std::ostringstream m_oss; + int m_currentColumn = -1; + bool m_isOpen = false; + +public: + TablePrinter( std::ostream& os, std::vector<ColumnInfo> columnInfos ) + : m_os( os ), + m_columnInfos( std::move( columnInfos ) ) {} + + auto columnInfos() const -> std::vector<ColumnInfo> const& { + return m_columnInfos; + } + + void open() { + if (!m_isOpen) { + m_isOpen = true; + *this << RowBreak(); + + Columns headerCols; + Spacer spacer(2); + for (auto const& info : m_columnInfos) { + headerCols += Column(info.name).width(static_cast<std::size_t>(info.width - 2)); + headerCols += spacer; + } + m_os << headerCols << '\n'; + + m_os << Catch::getLineOfChars<'-'>() << '\n'; + } + } + void close() { + if (m_isOpen) { + *this << RowBreak(); + m_os << std::endl; + m_isOpen = false; + } + } + + template<typename T> + friend TablePrinter& operator << (TablePrinter& tp, T const& value) { + tp.m_oss << value; + return tp; + } + + friend TablePrinter& operator << (TablePrinter& tp, ColumnBreak) { + auto colStr = tp.m_oss.str(); + const auto strSize = colStr.size(); + tp.m_oss.str(""); + tp.open(); + if (tp.m_currentColumn == static_cast<int>(tp.m_columnInfos.size() - 1)) { + tp.m_currentColumn = -1; + tp.m_os << '\n'; + } + tp.m_currentColumn++; + + auto colInfo = tp.m_columnInfos[tp.m_currentColumn]; + auto padding = (strSize + 1 < static_cast<std::size_t>(colInfo.width)) + ? std::string(colInfo.width - (strSize + 1), ' ') + : std::string(); + if (colInfo.justification == ColumnInfo::Left) + tp.m_os << colStr << padding << ' '; + else + tp.m_os << padding << colStr << ' '; + return tp; + } + + friend TablePrinter& operator << (TablePrinter& tp, RowBreak) { + if (tp.m_currentColumn > 0) { + tp.m_os << '\n'; + tp.m_currentColumn = -1; + } + return tp; + } +}; + +ConsoleReporter::ConsoleReporter(ReporterConfig const& config) + : StreamingReporterBase(config), + m_tablePrinter(new TablePrinter(config.stream(), + [&config]() -> std::vector<ColumnInfo> { + if (config.fullConfig()->benchmarkNoAnalysis()) + { + return{ + { "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 43, ColumnInfo::Left }, + { " samples", 14, ColumnInfo::Right }, + { " iterations", 14, ColumnInfo::Right }, + { " mean", 14, ColumnInfo::Right } + }; + } + else + { + return{ + { "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 43, ColumnInfo::Left }, + { "samples mean std dev", 14, ColumnInfo::Right }, + { "iterations low mean low std dev", 14, ColumnInfo::Right }, + { "estimated high mean high std dev", 14, ColumnInfo::Right } + }; + } + }())) {} +ConsoleReporter::~ConsoleReporter() = default; + +std::string ConsoleReporter::getDescription() { + return "Reports test results as plain lines of text"; +} + +void ConsoleReporter::noMatchingTestCases(std::string const& spec) { + stream << "No test cases matched '" << spec << '\'' << std::endl; +} + +void ConsoleReporter::reportInvalidArguments(std::string const&arg){ + stream << "Invalid Filter: " << arg << std::endl; +} + +void ConsoleReporter::assertionStarting(AssertionInfo const&) {} + +bool ConsoleReporter::assertionEnded(AssertionStats const& _assertionStats) { + AssertionResult const& result = _assertionStats.assertionResult; + + bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); + + // Drop out if result was successful but we're not printing them. + if (!includeResults && result.getResultType() != ResultWas::Warning) + return false; + + lazyPrint(); + + ConsoleAssertionPrinter printer(stream, _assertionStats, includeResults); + printer.print(); + stream << std::endl; + return true; +} + +void ConsoleReporter::sectionStarting(SectionInfo const& _sectionInfo) { + m_tablePrinter->close(); + m_headerPrinted = false; + StreamingReporterBase::sectionStarting(_sectionInfo); +} +void ConsoleReporter::sectionEnded(SectionStats const& _sectionStats) { + m_tablePrinter->close(); + if (_sectionStats.missingAssertions) { + lazyPrint(); + Colour colour(Colour::ResultError); + if (m_sectionStack.size() > 1) + stream << "\nNo assertions in section"; + else + stream << "\nNo assertions in test case"; + stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl; + } + double dur = _sectionStats.durationInSeconds; + if (shouldShowDuration(*m_config, dur)) { + stream << getFormattedDuration(dur) << " s: " << _sectionStats.sectionInfo.name << std::endl; + } + if (m_headerPrinted) { + m_headerPrinted = false; + } + StreamingReporterBase::sectionEnded(_sectionStats); +} + +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) +void ConsoleReporter::benchmarkPreparing(std::string const& name) { + lazyPrintWithoutClosingBenchmarkTable(); + + auto nameCol = Column(name).width(static_cast<std::size_t>(m_tablePrinter->columnInfos()[0].width - 2)); + + bool firstLine = true; + for (auto line : nameCol) { + if (!firstLine) + (*m_tablePrinter) << ColumnBreak() << ColumnBreak() << ColumnBreak(); + else + firstLine = false; + + (*m_tablePrinter) << line << ColumnBreak(); + } +} + +void ConsoleReporter::benchmarkStarting(BenchmarkInfo const& info) { + (*m_tablePrinter) << info.samples << ColumnBreak() + << info.iterations << ColumnBreak(); + if (!m_config->benchmarkNoAnalysis()) + (*m_tablePrinter) << Duration(info.estimatedDuration) << ColumnBreak(); +} +void ConsoleReporter::benchmarkEnded(BenchmarkStats<> const& stats) { + if (m_config->benchmarkNoAnalysis()) + { + (*m_tablePrinter) << Duration(stats.mean.point.count()) << ColumnBreak(); + } + else + { + (*m_tablePrinter) << ColumnBreak() + << Duration(stats.mean.point.count()) << ColumnBreak() + << Duration(stats.mean.lower_bound.count()) << ColumnBreak() + << Duration(stats.mean.upper_bound.count()) << ColumnBreak() << ColumnBreak() + << Duration(stats.standardDeviation.point.count()) << ColumnBreak() + << Duration(stats.standardDeviation.lower_bound.count()) << ColumnBreak() + << Duration(stats.standardDeviation.upper_bound.count()) << ColumnBreak() << ColumnBreak() << ColumnBreak() << ColumnBreak() << ColumnBreak(); + } +} + +void ConsoleReporter::benchmarkFailed(std::string const& error) { + Colour colour(Colour::Red); + (*m_tablePrinter) + << "Benchmark failed (" << error << ')' + << ColumnBreak() << RowBreak(); +} +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING + +void ConsoleReporter::testCaseEnded(TestCaseStats const& _testCaseStats) { + m_tablePrinter->close(); + StreamingReporterBase::testCaseEnded(_testCaseStats); + m_headerPrinted = false; +} +void ConsoleReporter::testGroupEnded(TestGroupStats const& _testGroupStats) { + if (currentGroupInfo.used) { + printSummaryDivider(); + stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n"; + printTotals(_testGroupStats.totals); + stream << '\n' << std::endl; + } + StreamingReporterBase::testGroupEnded(_testGroupStats); +} +void ConsoleReporter::testRunEnded(TestRunStats const& _testRunStats) { + printTotalsDivider(_testRunStats.totals); + printTotals(_testRunStats.totals); + stream << std::endl; + StreamingReporterBase::testRunEnded(_testRunStats); +} +void ConsoleReporter::testRunStarting(TestRunInfo const& _testInfo) { + StreamingReporterBase::testRunStarting(_testInfo); + printTestFilters(); +} + +void ConsoleReporter::lazyPrint() { + + m_tablePrinter->close(); + lazyPrintWithoutClosingBenchmarkTable(); +} + +void ConsoleReporter::lazyPrintWithoutClosingBenchmarkTable() { + + if (!currentTestRunInfo.used) + lazyPrintRunInfo(); + if (!currentGroupInfo.used) + lazyPrintGroupInfo(); + + if (!m_headerPrinted) { + printTestCaseAndSectionHeader(); + m_headerPrinted = true; + } +} +void ConsoleReporter::lazyPrintRunInfo() { + stream << '\n' << getLineOfChars<'~'>() << '\n'; + Colour colour(Colour::SecondaryText); + stream << currentTestRunInfo->name + << " is a Catch v" << libraryVersion() << " host application.\n" + << "Run with -? for options\n\n"; + + if (m_config->rngSeed() != 0) + stream << "Randomness seeded to: " << m_config->rngSeed() << "\n\n"; + + currentTestRunInfo.used = true; +} +void ConsoleReporter::lazyPrintGroupInfo() { + if (!currentGroupInfo->name.empty() && currentGroupInfo->groupsCounts > 1) { + printClosedHeader("Group: " + currentGroupInfo->name); + currentGroupInfo.used = true; + } +} +void ConsoleReporter::printTestCaseAndSectionHeader() { + assert(!m_sectionStack.empty()); + printOpenHeader(currentTestCaseInfo->name); + + if (m_sectionStack.size() > 1) { + Colour colourGuard(Colour::Headers); + + auto + it = m_sectionStack.begin() + 1, // Skip first section (test case) + itEnd = m_sectionStack.end(); + for (; it != itEnd; ++it) + printHeaderString(it->name, 2); + } + + SourceLineInfo lineInfo = m_sectionStack.back().lineInfo; + + stream << getLineOfChars<'-'>() << '\n'; + Colour colourGuard(Colour::FileName); + stream << lineInfo << '\n'; + stream << getLineOfChars<'.'>() << '\n' << std::endl; +} + +void ConsoleReporter::printClosedHeader(std::string const& _name) { + printOpenHeader(_name); + stream << getLineOfChars<'.'>() << '\n'; +} +void ConsoleReporter::printOpenHeader(std::string const& _name) { + stream << getLineOfChars<'-'>() << '\n'; + { + Colour colourGuard(Colour::Headers); + printHeaderString(_name); + } +} + +// if string has a : in first line will set indent to follow it on +// subsequent lines +void ConsoleReporter::printHeaderString(std::string const& _string, std::size_t indent) { + std::size_t i = _string.find(": "); + if (i != std::string::npos) + i += 2; + else + i = 0; + stream << Column(_string).indent(indent + i).initialIndent(indent) << '\n'; +} + +struct SummaryColumn { + + SummaryColumn( std::string _label, Colour::Code _colour ) + : label( std::move( _label ) ), + colour( _colour ) {} + SummaryColumn addRow( std::size_t count ) { + ReusableStringStream rss; + rss << count; + std::string row = rss.str(); + for (auto& oldRow : rows) { + while (oldRow.size() < row.size()) + oldRow = ' ' + oldRow; + while (oldRow.size() > row.size()) + row = ' ' + row; + } + rows.push_back(row); + return *this; + } + + std::string label; + Colour::Code colour; + std::vector<std::string> rows; + +}; + +void ConsoleReporter::printTotals( Totals const& totals ) { + if (totals.testCases.total() == 0) { + stream << Colour(Colour::Warning) << "No tests ran\n"; + } else if (totals.assertions.total() > 0 && totals.testCases.allPassed()) { + stream << Colour(Colour::ResultSuccess) << "All tests passed"; + stream << " (" + << pluralise(totals.assertions.passed, "assertion") << " in " + << pluralise(totals.testCases.passed, "test case") << ')' + << '\n'; + } else { + + std::vector<SummaryColumn> columns; + columns.push_back(SummaryColumn("", Colour::None) + .addRow(totals.testCases.total()) + .addRow(totals.assertions.total())); + columns.push_back(SummaryColumn("passed", Colour::Success) + .addRow(totals.testCases.passed) + .addRow(totals.assertions.passed)); + columns.push_back(SummaryColumn("failed", Colour::ResultError) + .addRow(totals.testCases.failed) + .addRow(totals.assertions.failed)); + columns.push_back(SummaryColumn("failed as expected", Colour::ResultExpectedFailure) + .addRow(totals.testCases.failedButOk) + .addRow(totals.assertions.failedButOk)); + + printSummaryRow("test cases", columns, 0); + printSummaryRow("assertions", columns, 1); + } +} +void ConsoleReporter::printSummaryRow(std::string const& label, std::vector<SummaryColumn> const& cols, std::size_t row) { + for (auto col : cols) { + std::string value = col.rows[row]; + if (col.label.empty()) { + stream << label << ": "; + if (value != "0") + stream << value; + else + stream << Colour(Colour::Warning) << "- none -"; + } else if (value != "0") { + stream << Colour(Colour::LightGrey) << " | "; + stream << Colour(col.colour) + << value << ' ' << col.label; + } + } + stream << '\n'; +} + +void ConsoleReporter::printTotalsDivider(Totals const& totals) { + if (totals.testCases.total() > 0) { + std::size_t failedRatio = makeRatio(totals.testCases.failed, totals.testCases.total()); + std::size_t failedButOkRatio = makeRatio(totals.testCases.failedButOk, totals.testCases.total()); + std::size_t passedRatio = makeRatio(totals.testCases.passed, totals.testCases.total()); + while (failedRatio + failedButOkRatio + passedRatio < CATCH_CONFIG_CONSOLE_WIDTH - 1) + findMax(failedRatio, failedButOkRatio, passedRatio)++; + while (failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH - 1) + findMax(failedRatio, failedButOkRatio, passedRatio)--; + + stream << Colour(Colour::Error) << std::string(failedRatio, '='); + stream << Colour(Colour::ResultExpectedFailure) << std::string(failedButOkRatio, '='); + if (totals.testCases.allPassed()) + stream << Colour(Colour::ResultSuccess) << std::string(passedRatio, '='); + else + stream << Colour(Colour::Success) << std::string(passedRatio, '='); + } else { + stream << Colour(Colour::Warning) << std::string(CATCH_CONFIG_CONSOLE_WIDTH - 1, '='); + } + stream << '\n'; +} +void ConsoleReporter::printSummaryDivider() { + stream << getLineOfChars<'-'>() << '\n'; +} + +void ConsoleReporter::printTestFilters() { + if (m_config->testSpec().hasFilters()) { + Colour guard(Colour::BrightYellow); + stream << "Filters: " << serializeFilters(m_config->getTestsOrTags()) << '\n'; + } +} + +CATCH_REGISTER_REPORTER("console", ConsoleReporter) + +} // end namespace Catch + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +#if defined(__clang__) +# pragma clang diagnostic pop +#endif +// end catch_reporter_console.cpp +// start catch_reporter_junit.cpp + +#include <cassert> +#include <sstream> +#include <ctime> +#include <algorithm> +#include <iomanip> + +namespace Catch { + + namespace { + std::string getCurrentTimestamp() { + // Beware, this is not reentrant because of backward compatibility issues + // Also, UTC only, again because of backward compatibility (%z is C++11) + time_t rawtime; + std::time(&rawtime); + auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); + +#ifdef _MSC_VER + std::tm timeInfo = {}; + gmtime_s(&timeInfo, &rawtime); +#else + std::tm* timeInfo; + timeInfo = std::gmtime(&rawtime); +#endif + + char timeStamp[timeStampSize]; + const char * const fmt = "%Y-%m-%dT%H:%M:%SZ"; + +#ifdef _MSC_VER + std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); +#else + std::strftime(timeStamp, timeStampSize, fmt, timeInfo); +#endif + return std::string(timeStamp, timeStampSize-1); + } + + std::string fileNameTag(const std::vector<std::string> &tags) { + auto it = std::find_if(begin(tags), + end(tags), + [] (std::string const& tag) {return tag.front() == '#'; }); + if (it != tags.end()) + return it->substr(1); + return std::string(); + } + + // Formats the duration in seconds to 3 decimal places. + // This is done because some genius defined Maven Surefire schema + // in a way that only accepts 3 decimal places, and tools like + // Jenkins use that schema for validation JUnit reporter output. + std::string formatDuration( double seconds ) { + ReusableStringStream rss; + rss << std::fixed << std::setprecision( 3 ) << seconds; + return rss.str(); + } + + } // anonymous namespace + + JunitReporter::JunitReporter( ReporterConfig const& _config ) + : CumulativeReporterBase( _config ), + xml( _config.stream() ) + { + m_reporterPrefs.shouldRedirectStdOut = true; + m_reporterPrefs.shouldReportAllAssertions = true; + } + + JunitReporter::~JunitReporter() {} + + std::string JunitReporter::getDescription() { + return "Reports test results in an XML format that looks like Ant's junitreport target"; + } + + void JunitReporter::noMatchingTestCases( std::string const& /*spec*/ ) {} + + void JunitReporter::testRunStarting( TestRunInfo const& runInfo ) { + CumulativeReporterBase::testRunStarting( runInfo ); + xml.startElement( "testsuites" ); + } + + void JunitReporter::testGroupStarting( GroupInfo const& groupInfo ) { + suiteTimer.start(); + stdOutForSuite.clear(); + stdErrForSuite.clear(); + unexpectedExceptions = 0; + CumulativeReporterBase::testGroupStarting( groupInfo ); + } + + void JunitReporter::testCaseStarting( TestCaseInfo const& testCaseInfo ) { + m_okToFail = testCaseInfo.okToFail(); + } + + bool JunitReporter::assertionEnded( AssertionStats const& assertionStats ) { + if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException && !m_okToFail ) + unexpectedExceptions++; + return CumulativeReporterBase::assertionEnded( assertionStats ); + } + + void JunitReporter::testCaseEnded( TestCaseStats const& testCaseStats ) { + stdOutForSuite += testCaseStats.stdOut; + stdErrForSuite += testCaseStats.stdErr; + CumulativeReporterBase::testCaseEnded( testCaseStats ); + } + + void JunitReporter::testGroupEnded( TestGroupStats const& testGroupStats ) { + double suiteTime = suiteTimer.getElapsedSeconds(); + CumulativeReporterBase::testGroupEnded( testGroupStats ); + writeGroup( *m_testGroups.back(), suiteTime ); + } + + void JunitReporter::testRunEndedCumulative() { + xml.endElement(); + } + + void JunitReporter::writeGroup( TestGroupNode const& groupNode, double suiteTime ) { + XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" ); + + TestGroupStats const& stats = groupNode.value; + xml.writeAttribute( "name", stats.groupInfo.name ); + xml.writeAttribute( "errors", unexpectedExceptions ); + xml.writeAttribute( "failures", stats.totals.assertions.failed-unexpectedExceptions ); + xml.writeAttribute( "tests", stats.totals.assertions.total() ); + xml.writeAttribute( "hostname", "tbd" ); // !TBD + if( m_config->showDurations() == ShowDurations::Never ) + xml.writeAttribute( "time", "" ); + else + xml.writeAttribute( "time", formatDuration( suiteTime ) ); + xml.writeAttribute( "timestamp", getCurrentTimestamp() ); + + // Write properties if there are any + if (m_config->hasTestFilters() || m_config->rngSeed() != 0) { + auto properties = xml.scopedElement("properties"); + if (m_config->hasTestFilters()) { + xml.scopedElement("property") + .writeAttribute("name", "filters") + .writeAttribute("value", serializeFilters(m_config->getTestsOrTags())); + } + if (m_config->rngSeed() != 0) { + xml.scopedElement("property") + .writeAttribute("name", "random-seed") + .writeAttribute("value", m_config->rngSeed()); + } + } + + // Write test cases + for( auto const& child : groupNode.children ) + writeTestCase( *child ); + + xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite ), XmlFormatting::Newline ); + xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite ), XmlFormatting::Newline ); + } + + void JunitReporter::writeTestCase( TestCaseNode const& testCaseNode ) { + TestCaseStats const& stats = testCaseNode.value; + + // All test cases have exactly one section - which represents the + // test case itself. That section may have 0-n nested sections + assert( testCaseNode.children.size() == 1 ); + SectionNode const& rootSection = *testCaseNode.children.front(); + + std::string className = stats.testInfo.className; + + if( className.empty() ) { + className = fileNameTag(stats.testInfo.tags); + if ( className.empty() ) + className = "global"; + } + + if ( !m_config->name().empty() ) + className = m_config->name() + "." + className; + + writeSection( className, "", rootSection, stats.testInfo.okToFail() ); + } + + void JunitReporter::writeSection( std::string const& className, + std::string const& rootName, + SectionNode const& sectionNode, + bool testOkToFail) { + std::string name = trim( sectionNode.stats.sectionInfo.name ); + if( !rootName.empty() ) + name = rootName + '/' + name; + + if( !sectionNode.assertions.empty() || + !sectionNode.stdOut.empty() || + !sectionNode.stdErr.empty() ) { + XmlWriter::ScopedElement e = xml.scopedElement( "testcase" ); + if( className.empty() ) { + xml.writeAttribute( "classname", name ); + xml.writeAttribute( "name", "root" ); + } + else { + xml.writeAttribute( "classname", className ); + xml.writeAttribute( "name", name ); + } + xml.writeAttribute( "time", formatDuration( sectionNode.stats.durationInSeconds ) ); + // This is not ideal, but it should be enough to mimic gtest's + // junit output. + // Ideally the JUnit reporter would also handle `skipTest` + // events and write those out appropriately. + xml.writeAttribute( "status", "run" ); + + if (sectionNode.stats.assertions.failedButOk) { + xml.scopedElement("skipped") + .writeAttribute("message", "TEST_CASE tagged with !mayfail"); + } + + writeAssertions( sectionNode ); + + if( !sectionNode.stdOut.empty() ) + xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), XmlFormatting::Newline ); + if( !sectionNode.stdErr.empty() ) + xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), XmlFormatting::Newline ); + } + for( auto const& childNode : sectionNode.childSections ) + if( className.empty() ) + writeSection( name, "", *childNode, testOkToFail ); + else + writeSection( className, name, *childNode, testOkToFail ); + } + + void JunitReporter::writeAssertions( SectionNode const& sectionNode ) { + for( auto const& assertion : sectionNode.assertions ) + writeAssertion( assertion ); + } + + void JunitReporter::writeAssertion( AssertionStats const& stats ) { + AssertionResult const& result = stats.assertionResult; + if( !result.isOk() ) { + std::string elementName; + switch( result.getResultType() ) { + case ResultWas::ThrewException: + case ResultWas::FatalErrorCondition: + elementName = "error"; + break; + case ResultWas::ExplicitFailure: + case ResultWas::ExpressionFailed: + case ResultWas::DidntThrowException: + elementName = "failure"; + break; + + // We should never see these here: + case ResultWas::Info: + case ResultWas::Warning: + case ResultWas::Ok: + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + elementName = "internalError"; + break; + } + + XmlWriter::ScopedElement e = xml.scopedElement( elementName ); + + xml.writeAttribute( "message", result.getExpression() ); + xml.writeAttribute( "type", result.getTestMacroName() ); + + ReusableStringStream rss; + if (stats.totals.assertions.total() > 0) { + rss << "FAILED" << ":\n"; + if (result.hasExpression()) { + rss << " "; + rss << result.getExpressionInMacro(); + rss << '\n'; + } + if (result.hasExpandedExpression()) { + rss << "with expansion:\n"; + rss << Column(result.getExpandedExpression()).indent(2) << '\n'; + } + } else { + rss << '\n'; + } + + if( !result.getMessage().empty() ) + rss << result.getMessage() << '\n'; + for( auto const& msg : stats.infoMessages ) + if( msg.type == ResultWas::Info ) + rss << msg.message << '\n'; + + rss << "at " << result.getSourceInfo(); + xml.writeText( rss.str(), XmlFormatting::Newline ); + } + } + + CATCH_REGISTER_REPORTER( "junit", JunitReporter ) + +} // end namespace Catch +// end catch_reporter_junit.cpp +// start catch_reporter_listening.cpp + +#include <cassert> + +namespace Catch { + + ListeningReporter::ListeningReporter() { + // We will assume that listeners will always want all assertions + m_preferences.shouldReportAllAssertions = true; + } + + void ListeningReporter::addListener( IStreamingReporterPtr&& listener ) { + m_listeners.push_back( std::move( listener ) ); + } + + void ListeningReporter::addReporter(IStreamingReporterPtr&& reporter) { + assert(!m_reporter && "Listening reporter can wrap only 1 real reporter"); + m_reporter = std::move( reporter ); + m_preferences.shouldRedirectStdOut = m_reporter->getPreferences().shouldRedirectStdOut; + } + + ReporterPreferences ListeningReporter::getPreferences() const { + return m_preferences; + } + + std::set<Verbosity> ListeningReporter::getSupportedVerbosities() { + return std::set<Verbosity>{ }; + } + + void ListeningReporter::noMatchingTestCases( std::string const& spec ) { + for ( auto const& listener : m_listeners ) { + listener->noMatchingTestCases( spec ); + } + m_reporter->noMatchingTestCases( spec ); + } + + void ListeningReporter::reportInvalidArguments(std::string const&arg){ + for ( auto const& listener : m_listeners ) { + listener->reportInvalidArguments( arg ); + } + m_reporter->reportInvalidArguments( arg ); + } + +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) + void ListeningReporter::benchmarkPreparing( std::string const& name ) { + for (auto const& listener : m_listeners) { + listener->benchmarkPreparing(name); + } + m_reporter->benchmarkPreparing(name); + } + void ListeningReporter::benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) { + for ( auto const& listener : m_listeners ) { + listener->benchmarkStarting( benchmarkInfo ); + } + m_reporter->benchmarkStarting( benchmarkInfo ); + } + void ListeningReporter::benchmarkEnded( BenchmarkStats<> const& benchmarkStats ) { + for ( auto const& listener : m_listeners ) { + listener->benchmarkEnded( benchmarkStats ); + } + m_reporter->benchmarkEnded( benchmarkStats ); + } + + void ListeningReporter::benchmarkFailed( std::string const& error ) { + for (auto const& listener : m_listeners) { + listener->benchmarkFailed(error); + } + m_reporter->benchmarkFailed(error); + } +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING + + void ListeningReporter::testRunStarting( TestRunInfo const& testRunInfo ) { + for ( auto const& listener : m_listeners ) { + listener->testRunStarting( testRunInfo ); + } + m_reporter->testRunStarting( testRunInfo ); + } + + void ListeningReporter::testGroupStarting( GroupInfo const& groupInfo ) { + for ( auto const& listener : m_listeners ) { + listener->testGroupStarting( groupInfo ); + } + m_reporter->testGroupStarting( groupInfo ); + } + + void ListeningReporter::testCaseStarting( TestCaseInfo const& testInfo ) { + for ( auto const& listener : m_listeners ) { + listener->testCaseStarting( testInfo ); + } + m_reporter->testCaseStarting( testInfo ); + } + + void ListeningReporter::sectionStarting( SectionInfo const& sectionInfo ) { + for ( auto const& listener : m_listeners ) { + listener->sectionStarting( sectionInfo ); + } + m_reporter->sectionStarting( sectionInfo ); + } + + void ListeningReporter::assertionStarting( AssertionInfo const& assertionInfo ) { + for ( auto const& listener : m_listeners ) { + listener->assertionStarting( assertionInfo ); + } + m_reporter->assertionStarting( assertionInfo ); + } + + // The return value indicates if the messages buffer should be cleared: + bool ListeningReporter::assertionEnded( AssertionStats const& assertionStats ) { + for( auto const& listener : m_listeners ) { + static_cast<void>( listener->assertionEnded( assertionStats ) ); + } + return m_reporter->assertionEnded( assertionStats ); + } + + void ListeningReporter::sectionEnded( SectionStats const& sectionStats ) { + for ( auto const& listener : m_listeners ) { + listener->sectionEnded( sectionStats ); + } + m_reporter->sectionEnded( sectionStats ); + } + + void ListeningReporter::testCaseEnded( TestCaseStats const& testCaseStats ) { + for ( auto const& listener : m_listeners ) { + listener->testCaseEnded( testCaseStats ); + } + m_reporter->testCaseEnded( testCaseStats ); + } + + void ListeningReporter::testGroupEnded( TestGroupStats const& testGroupStats ) { + for ( auto const& listener : m_listeners ) { + listener->testGroupEnded( testGroupStats ); + } + m_reporter->testGroupEnded( testGroupStats ); + } + + void ListeningReporter::testRunEnded( TestRunStats const& testRunStats ) { + for ( auto const& listener : m_listeners ) { + listener->testRunEnded( testRunStats ); + } + m_reporter->testRunEnded( testRunStats ); + } + + void ListeningReporter::skipTest( TestCaseInfo const& testInfo ) { + for ( auto const& listener : m_listeners ) { + listener->skipTest( testInfo ); + } + m_reporter->skipTest( testInfo ); + } + + bool ListeningReporter::isMulti() const { + return true; + } + +} // end namespace Catch +// end catch_reporter_listening.cpp +// start catch_reporter_xml.cpp + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch + // Note that 4062 (not all labels are handled + // and default is missing) is enabled +#endif + +namespace Catch { + XmlReporter::XmlReporter( ReporterConfig const& _config ) + : StreamingReporterBase( _config ), + m_xml(_config.stream()) + { + m_reporterPrefs.shouldRedirectStdOut = true; + m_reporterPrefs.shouldReportAllAssertions = true; + } + + XmlReporter::~XmlReporter() = default; + + std::string XmlReporter::getDescription() { + return "Reports test results as an XML document"; + } + + std::string XmlReporter::getStylesheetRef() const { + return std::string(); + } + + void XmlReporter::writeSourceInfo( SourceLineInfo const& sourceInfo ) { + m_xml + .writeAttribute( "filename", sourceInfo.file ) + .writeAttribute( "line", sourceInfo.line ); + } + + void XmlReporter::noMatchingTestCases( std::string const& s ) { + StreamingReporterBase::noMatchingTestCases( s ); + } + + void XmlReporter::testRunStarting( TestRunInfo const& testInfo ) { + StreamingReporterBase::testRunStarting( testInfo ); + std::string stylesheetRef = getStylesheetRef(); + if( !stylesheetRef.empty() ) + m_xml.writeStylesheetRef( stylesheetRef ); + m_xml.startElement( "Catch" ); + if( !m_config->name().empty() ) + m_xml.writeAttribute( "name", m_config->name() ); + if (m_config->testSpec().hasFilters()) + m_xml.writeAttribute( "filters", serializeFilters( m_config->getTestsOrTags() ) ); + if( m_config->rngSeed() != 0 ) + m_xml.scopedElement( "Randomness" ) + .writeAttribute( "seed", m_config->rngSeed() ); + } + + void XmlReporter::testGroupStarting( GroupInfo const& groupInfo ) { + StreamingReporterBase::testGroupStarting( groupInfo ); + m_xml.startElement( "Group" ) + .writeAttribute( "name", groupInfo.name ); + } + + void XmlReporter::testCaseStarting( TestCaseInfo const& testInfo ) { + StreamingReporterBase::testCaseStarting(testInfo); + m_xml.startElement( "TestCase" ) + .writeAttribute( "name", trim( testInfo.name ) ) + .writeAttribute( "description", testInfo.description ) + .writeAttribute( "tags", testInfo.tagsAsString() ); + + writeSourceInfo( testInfo.lineInfo ); + + if ( m_config->showDurations() == ShowDurations::Always ) + m_testCaseTimer.start(); + m_xml.ensureTagClosed(); + } + + void XmlReporter::sectionStarting( SectionInfo const& sectionInfo ) { + StreamingReporterBase::sectionStarting( sectionInfo ); + if( m_sectionDepth++ > 0 ) { + m_xml.startElement( "Section" ) + .writeAttribute( "name", trim( sectionInfo.name ) ); + writeSourceInfo( sectionInfo.lineInfo ); + m_xml.ensureTagClosed(); + } + } + + void XmlReporter::assertionStarting( AssertionInfo const& ) { } + + bool XmlReporter::assertionEnded( AssertionStats const& assertionStats ) { + + AssertionResult const& result = assertionStats.assertionResult; + + bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); + + if( includeResults || result.getResultType() == ResultWas::Warning ) { + // Print any info messages in <Info> tags. + for( auto const& msg : assertionStats.infoMessages ) { + if( msg.type == ResultWas::Info && includeResults ) { + m_xml.scopedElement( "Info" ) + .writeText( msg.message ); + } else if ( msg.type == ResultWas::Warning ) { + m_xml.scopedElement( "Warning" ) + .writeText( msg.message ); + } + } + } + + // Drop out if result was successful but we're not printing them. + if( !includeResults && result.getResultType() != ResultWas::Warning ) + return true; + + // Print the expression if there is one. + if( result.hasExpression() ) { + m_xml.startElement( "Expression" ) + .writeAttribute( "success", result.succeeded() ) + .writeAttribute( "type", result.getTestMacroName() ); + + writeSourceInfo( result.getSourceInfo() ); + + m_xml.scopedElement( "Original" ) + .writeText( result.getExpression() ); + m_xml.scopedElement( "Expanded" ) + .writeText( result.getExpandedExpression() ); + } + + // And... Print a result applicable to each result type. + switch( result.getResultType() ) { + case ResultWas::ThrewException: + m_xml.startElement( "Exception" ); + writeSourceInfo( result.getSourceInfo() ); + m_xml.writeText( result.getMessage() ); + m_xml.endElement(); + break; + case ResultWas::FatalErrorCondition: + m_xml.startElement( "FatalErrorCondition" ); + writeSourceInfo( result.getSourceInfo() ); + m_xml.writeText( result.getMessage() ); + m_xml.endElement(); + break; + case ResultWas::Info: + m_xml.scopedElement( "Info" ) + .writeText( result.getMessage() ); + break; + case ResultWas::Warning: + // Warning will already have been written + break; + case ResultWas::ExplicitFailure: + m_xml.startElement( "Failure" ); + writeSourceInfo( result.getSourceInfo() ); + m_xml.writeText( result.getMessage() ); + m_xml.endElement(); + break; + default: + break; + } + + if( result.hasExpression() ) + m_xml.endElement(); + + return true; + } + + void XmlReporter::sectionEnded( SectionStats const& sectionStats ) { + StreamingReporterBase::sectionEnded( sectionStats ); + if( --m_sectionDepth > 0 ) { + XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResults" ); + e.writeAttribute( "successes", sectionStats.assertions.passed ); + e.writeAttribute( "failures", sectionStats.assertions.failed ); + e.writeAttribute( "expectedFailures", sectionStats.assertions.failedButOk ); + + if ( m_config->showDurations() == ShowDurations::Always ) + e.writeAttribute( "durationInSeconds", sectionStats.durationInSeconds ); + + m_xml.endElement(); + } + } + + void XmlReporter::testCaseEnded( TestCaseStats const& testCaseStats ) { + StreamingReporterBase::testCaseEnded( testCaseStats ); + XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResult" ); + e.writeAttribute( "success", testCaseStats.totals.assertions.allOk() ); + + if ( m_config->showDurations() == ShowDurations::Always ) + e.writeAttribute( "durationInSeconds", m_testCaseTimer.getElapsedSeconds() ); + + if( !testCaseStats.stdOut.empty() ) + m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), XmlFormatting::Newline ); + if( !testCaseStats.stdErr.empty() ) + m_xml.scopedElement( "StdErr" ).writeText( trim( testCaseStats.stdErr ), XmlFormatting::Newline ); + + m_xml.endElement(); + } + + void XmlReporter::testGroupEnded( TestGroupStats const& testGroupStats ) { + StreamingReporterBase::testGroupEnded( testGroupStats ); + // TODO: Check testGroupStats.aborting and act accordingly. + m_xml.scopedElement( "OverallResults" ) + .writeAttribute( "successes", testGroupStats.totals.assertions.passed ) + .writeAttribute( "failures", testGroupStats.totals.assertions.failed ) + .writeAttribute( "expectedFailures", testGroupStats.totals.assertions.failedButOk ); + m_xml.scopedElement( "OverallResultsCases") + .writeAttribute( "successes", testGroupStats.totals.testCases.passed ) + .writeAttribute( "failures", testGroupStats.totals.testCases.failed ) + .writeAttribute( "expectedFailures", testGroupStats.totals.testCases.failedButOk ); + m_xml.endElement(); + } + + void XmlReporter::testRunEnded( TestRunStats const& testRunStats ) { + StreamingReporterBase::testRunEnded( testRunStats ); + m_xml.scopedElement( "OverallResults" ) + .writeAttribute( "successes", testRunStats.totals.assertions.passed ) + .writeAttribute( "failures", testRunStats.totals.assertions.failed ) + .writeAttribute( "expectedFailures", testRunStats.totals.assertions.failedButOk ); + m_xml.scopedElement( "OverallResultsCases") + .writeAttribute( "successes", testRunStats.totals.testCases.passed ) + .writeAttribute( "failures", testRunStats.totals.testCases.failed ) + .writeAttribute( "expectedFailures", testRunStats.totals.testCases.failedButOk ); + m_xml.endElement(); + } + +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) + void XmlReporter::benchmarkPreparing(std::string const& name) { + m_xml.startElement("BenchmarkResults") + .writeAttribute("name", name); + } + + void XmlReporter::benchmarkStarting(BenchmarkInfo const &info) { + m_xml.writeAttribute("samples", info.samples) + .writeAttribute("resamples", info.resamples) + .writeAttribute("iterations", info.iterations) + .writeAttribute("clockResolution", info.clockResolution) + .writeAttribute("estimatedDuration", info.estimatedDuration) + .writeComment("All values in nano seconds"); + } + + void XmlReporter::benchmarkEnded(BenchmarkStats<> const& benchmarkStats) { + m_xml.startElement("mean") + .writeAttribute("value", benchmarkStats.mean.point.count()) + .writeAttribute("lowerBound", benchmarkStats.mean.lower_bound.count()) + .writeAttribute("upperBound", benchmarkStats.mean.upper_bound.count()) + .writeAttribute("ci", benchmarkStats.mean.confidence_interval); + m_xml.endElement(); + m_xml.startElement("standardDeviation") + .writeAttribute("value", benchmarkStats.standardDeviation.point.count()) + .writeAttribute("lowerBound", benchmarkStats.standardDeviation.lower_bound.count()) + .writeAttribute("upperBound", benchmarkStats.standardDeviation.upper_bound.count()) + .writeAttribute("ci", benchmarkStats.standardDeviation.confidence_interval); + m_xml.endElement(); + m_xml.startElement("outliers") + .writeAttribute("variance", benchmarkStats.outlierVariance) + .writeAttribute("lowMild", benchmarkStats.outliers.low_mild) + .writeAttribute("lowSevere", benchmarkStats.outliers.low_severe) + .writeAttribute("highMild", benchmarkStats.outliers.high_mild) + .writeAttribute("highSevere", benchmarkStats.outliers.high_severe); + m_xml.endElement(); + m_xml.endElement(); + } + + void XmlReporter::benchmarkFailed(std::string const &error) { + m_xml.scopedElement("failed"). + writeAttribute("message", error); + m_xml.endElement(); + } +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING + + CATCH_REGISTER_REPORTER( "xml", XmlReporter ) + +} // end namespace Catch + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif +// end catch_reporter_xml.cpp + +namespace Catch { + LeakDetector leakDetector; +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +// end catch_impl.hpp +#endif + +#ifdef CATCH_CONFIG_MAIN +// start catch_default_main.hpp + +#ifndef __OBJC__ + +#ifndef CATCH_INTERNAL_CDECL +#ifdef _MSC_VER +#define CATCH_INTERNAL_CDECL __cdecl +#else +#define CATCH_INTERNAL_CDECL +#endif +#endif + +#if defined(CATCH_CONFIG_WCHAR) && defined(CATCH_PLATFORM_WINDOWS) && defined(_UNICODE) && !defined(DO_NOT_USE_WMAIN) +// Standard C/C++ Win32 Unicode wmain entry point +extern "C" int CATCH_INTERNAL_CDECL wmain (int argc, wchar_t * argv[], wchar_t * []) { +#else +// Standard C/C++ main entry point +int CATCH_INTERNAL_CDECL main (int argc, char * argv[]) { +#endif + + return Catch::Session().run( argc, argv ); +} + +#else // __OBJC__ + +// Objective-C entry point +int main (int argc, char * const argv[]) { +#if !CATCH_ARC_ENABLED + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; +#endif + + Catch::registerTestMethods(); + int result = Catch::Session().run( argc, (char**)argv ); + +#if !CATCH_ARC_ENABLED + [pool drain]; +#endif + + return result; +} + +#endif // __OBJC__ + +// end catch_default_main.hpp +#endif + +#if !defined(CATCH_CONFIG_IMPL_ONLY) + +#ifdef CLARA_CONFIG_MAIN_NOT_DEFINED +# undef CLARA_CONFIG_MAIN +#endif + +#if !defined(CATCH_CONFIG_DISABLE) +////// +// If this config identifier is defined then all CATCH macros are prefixed with CATCH_ +#ifdef CATCH_CONFIG_PREFIX_ALL + +#define CATCH_REQUIRE( ... ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE", Catch::ResultDisposition::Normal, __VA_ARGS__ ) +#define CATCH_REQUIRE_FALSE( ... ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) + +#define CATCH_REQUIRE_THROWS( ... ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS", Catch::ResultDisposition::Normal, __VA_ARGS__ ) +#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr ) +#define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CATCH_REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr ) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CATCH_REQUIRE_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::Normal, matcher, expr ) +#endif// CATCH_CONFIG_DISABLE_MATCHERS +#define CATCH_REQUIRE_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CATCH_REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, __VA_ARGS__ ) + +#define CATCH_CHECK( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CATCH_CHECK_FALSE( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) +#define CATCH_CHECKED_IF( ... ) INTERNAL_CATCH_IF( "CATCH_CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CATCH_CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CATCH_CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CATCH_CHECK_NOFAIL( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ ) + +#define CATCH_CHECK_THROWS( ... ) INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr ) +#define CATCH_CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CATCH_CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CATCH_CHECK_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define CATCH_CHECK_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CATCH_CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) + +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg ) + +#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) +#endif // CATCH_CONFIG_DISABLE_MATCHERS + +#define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg ) +#define CATCH_UNSCOPED_INFO( msg ) INTERNAL_CATCH_UNSCOPED_INFO( "CATCH_UNSCOPED_INFO", msg ) +#define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( "CATCH_WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) +#define CATCH_CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CATCH_CAPTURE",__VA_ARGS__ ) + +#define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) +#define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) +#define CATCH_REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) +#define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) +#define CATCH_DYNAMIC_SECTION( ... ) INTERNAL_CATCH_DYNAMIC_SECTION( __VA_ARGS__ ) +#define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ ) +#define CATCH_FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( "CATCH_SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) + +#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE() + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) +#define CATCH_TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG( __VA_ARGS__ ) +#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( __VA_ARGS__ ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) +#else +#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) ) +#define CATCH_TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG( __VA_ARGS__ ) ) +#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) ) +#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ ) ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( __VA_ARGS__ ) ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ ) ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) ) +#endif + +#if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE) +#define CATCH_STATIC_REQUIRE( ... ) static_assert( __VA_ARGS__ , #__VA_ARGS__ ); CATCH_SUCCEED( #__VA_ARGS__ ) +#define CATCH_STATIC_REQUIRE_FALSE( ... ) static_assert( !(__VA_ARGS__), "!(" #__VA_ARGS__ ")" ); CATCH_SUCCEED( #__VA_ARGS__ ) +#else +#define CATCH_STATIC_REQUIRE( ... ) CATCH_REQUIRE( __VA_ARGS__ ) +#define CATCH_STATIC_REQUIRE_FALSE( ... ) CATCH_REQUIRE_FALSE( __VA_ARGS__ ) +#endif + +// "BDD-style" convenience wrappers +#define CATCH_SCENARIO( ... ) CATCH_TEST_CASE( "Scenario: " __VA_ARGS__ ) +#define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) +#define CATCH_GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Given: " << desc ) +#define CATCH_AND_GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( "And given: " << desc ) +#define CATCH_WHEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " When: " << desc ) +#define CATCH_AND_WHEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And when: " << desc ) +#define CATCH_THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Then: " << desc ) +#define CATCH_AND_THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And: " << desc ) + +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) +#define CATCH_BENCHMARK(...) \ + INTERNAL_CATCH_BENCHMARK(INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_B_E_N_C_H_), INTERNAL_CATCH_GET_1_ARG(__VA_ARGS__,,), INTERNAL_CATCH_GET_2_ARG(__VA_ARGS__,,)) +#define CATCH_BENCHMARK_ADVANCED(name) \ + INTERNAL_CATCH_BENCHMARK_ADVANCED(INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_B_E_N_C_H_), name) +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING + +// If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required +#else + +#define REQUIRE( ... ) INTERNAL_CATCH_TEST( "REQUIRE", Catch::ResultDisposition::Normal, __VA_ARGS__ ) +#define REQUIRE_FALSE( ... ) INTERNAL_CATCH_TEST( "REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) + +#define REQUIRE_THROWS( ... ) INTERNAL_CATCH_THROWS( "REQUIRE_THROWS", Catch::ResultDisposition::Normal, __VA_ARGS__ ) +#define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr ) +#define REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr ) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "REQUIRE_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::Normal, matcher, expr ) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define REQUIRE_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, __VA_ARGS__ ) + +#define CHECK( ... ) INTERNAL_CATCH_TEST( "CHECK", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CHECK_FALSE( ... ) INTERNAL_CATCH_TEST( "CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) +#define CHECKED_IF( ... ) INTERNAL_CATCH_IF( "CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CHECK_NOFAIL( ... ) INTERNAL_CATCH_TEST( "CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ ) + +#define CHECK_THROWS( ... ) INTERNAL_CATCH_THROWS( "CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr ) +#define CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CHECK_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define CHECK_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) + +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg ) + +#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) +#endif // CATCH_CONFIG_DISABLE_MATCHERS + +#define INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg ) +#define UNSCOPED_INFO( msg ) INTERNAL_CATCH_UNSCOPED_INFO( "UNSCOPED_INFO", msg ) +#define WARN( msg ) INTERNAL_CATCH_MSG( "WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) +#define CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CAPTURE",__VA_ARGS__ ) + +#define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) +#define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) +#define REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) +#define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) +#define DYNAMIC_SECTION( ... ) INTERNAL_CATCH_DYNAMIC_SECTION( __VA_ARGS__ ) +#define FAIL( ... ) INTERNAL_CATCH_MSG( "FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ ) +#define FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define SUCCEED( ... ) INTERNAL_CATCH_MSG( "SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE() + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) +#define TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG( __VA_ARGS__ ) +#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#define TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) +#define TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ ) +#define TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( __VA_ARGS__ ) +#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) +#define TEMPLATE_LIST_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE(__VA_ARGS__) +#define TEMPLATE_LIST_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#else +#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) ) +#define TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG( __VA_ARGS__ ) ) +#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) ) +#define TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) ) +#define TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ ) ) +#define TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( __VA_ARGS__ ) ) +#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ ) ) +#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) ) +#define TEMPLATE_LIST_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE( __VA_ARGS__ ) ) +#define TEMPLATE_LIST_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD( className, __VA_ARGS__ ) ) +#endif + +#if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE) +#define STATIC_REQUIRE( ... ) static_assert( __VA_ARGS__, #__VA_ARGS__ ); SUCCEED( #__VA_ARGS__ ) +#define STATIC_REQUIRE_FALSE( ... ) static_assert( !(__VA_ARGS__), "!(" #__VA_ARGS__ ")" ); SUCCEED( "!(" #__VA_ARGS__ ")" ) +#else +#define STATIC_REQUIRE( ... ) REQUIRE( __VA_ARGS__ ) +#define STATIC_REQUIRE_FALSE( ... ) REQUIRE_FALSE( __VA_ARGS__ ) +#endif + +#endif + +#define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) + +// "BDD-style" convenience wrappers +#define SCENARIO( ... ) TEST_CASE( "Scenario: " __VA_ARGS__ ) +#define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) + +#define GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Given: " << desc ) +#define AND_GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( "And given: " << desc ) +#define WHEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " When: " << desc ) +#define AND_WHEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And when: " << desc ) +#define THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Then: " << desc ) +#define AND_THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And: " << desc ) + +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) +#define BENCHMARK(...) \ + INTERNAL_CATCH_BENCHMARK(INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_B_E_N_C_H_), INTERNAL_CATCH_GET_1_ARG(__VA_ARGS__,,), INTERNAL_CATCH_GET_2_ARG(__VA_ARGS__,,)) +#define BENCHMARK_ADVANCED(name) \ + INTERNAL_CATCH_BENCHMARK_ADVANCED(INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_B_E_N_C_H_), name) +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING + +using Catch::Detail::Approx; + +#else // CATCH_CONFIG_DISABLE + +////// +// If this config identifier is defined then all CATCH macros are prefixed with CATCH_ +#ifdef CATCH_CONFIG_PREFIX_ALL + +#define CATCH_REQUIRE( ... ) (void)(0) +#define CATCH_REQUIRE_FALSE( ... ) (void)(0) + +#define CATCH_REQUIRE_THROWS( ... ) (void)(0) +#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) (void)(0) +#define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) (void)(0) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0) +#endif// CATCH_CONFIG_DISABLE_MATCHERS +#define CATCH_REQUIRE_NOTHROW( ... ) (void)(0) + +#define CATCH_CHECK( ... ) (void)(0) +#define CATCH_CHECK_FALSE( ... ) (void)(0) +#define CATCH_CHECKED_IF( ... ) if (__VA_ARGS__) +#define CATCH_CHECKED_ELSE( ... ) if (!(__VA_ARGS__)) +#define CATCH_CHECK_NOFAIL( ... ) (void)(0) + +#define CATCH_CHECK_THROWS( ... ) (void)(0) +#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) (void)(0) +#define CATCH_CHECK_THROWS_WITH( expr, matcher ) (void)(0) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define CATCH_CHECK_NOTHROW( ... ) (void)(0) + +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_CHECK_THAT( arg, matcher ) (void)(0) + +#define CATCH_REQUIRE_THAT( arg, matcher ) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS + +#define CATCH_INFO( msg ) (void)(0) +#define CATCH_UNSCOPED_INFO( msg ) (void)(0) +#define CATCH_WARN( msg ) (void)(0) +#define CATCH_CAPTURE( msg ) (void)(0) + +#define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ )) +#define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ )) +#define CATCH_METHOD_AS_TEST_CASE( method, ... ) +#define CATCH_REGISTER_TEST_CASE( Function, ... ) (void)(0) +#define CATCH_SECTION( ... ) +#define CATCH_DYNAMIC_SECTION( ... ) +#define CATCH_FAIL( ... ) (void)(0) +#define CATCH_FAIL_CHECK( ... ) (void)(0) +#define CATCH_SUCCEED( ... ) (void)(0) + +#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ )) + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__) +#define CATCH_TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__) +#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__) +#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__ ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#else +#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__) ) +#define CATCH_TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__) ) +#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__ ) ) +#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__ ) ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#endif + +// "BDD-style" convenience wrappers +#define CATCH_SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ )) +#define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ), className ) +#define CATCH_GIVEN( desc ) +#define CATCH_AND_GIVEN( desc ) +#define CATCH_WHEN( desc ) +#define CATCH_AND_WHEN( desc ) +#define CATCH_THEN( desc ) +#define CATCH_AND_THEN( desc ) + +#define CATCH_STATIC_REQUIRE( ... ) (void)(0) +#define CATCH_STATIC_REQUIRE_FALSE( ... ) (void)(0) + +// If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required +#else + +#define REQUIRE( ... ) (void)(0) +#define REQUIRE_FALSE( ... ) (void)(0) + +#define REQUIRE_THROWS( ... ) (void)(0) +#define REQUIRE_THROWS_AS( expr, exceptionType ) (void)(0) +#define REQUIRE_THROWS_WITH( expr, matcher ) (void)(0) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define REQUIRE_NOTHROW( ... ) (void)(0) + +#define CHECK( ... ) (void)(0) +#define CHECK_FALSE( ... ) (void)(0) +#define CHECKED_IF( ... ) if (__VA_ARGS__) +#define CHECKED_ELSE( ... ) if (!(__VA_ARGS__)) +#define CHECK_NOFAIL( ... ) (void)(0) + +#define CHECK_THROWS( ... ) (void)(0) +#define CHECK_THROWS_AS( expr, exceptionType ) (void)(0) +#define CHECK_THROWS_WITH( expr, matcher ) (void)(0) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define CHECK_NOTHROW( ... ) (void)(0) + +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CHECK_THAT( arg, matcher ) (void)(0) + +#define REQUIRE_THAT( arg, matcher ) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS + +#define INFO( msg ) (void)(0) +#define UNSCOPED_INFO( msg ) (void)(0) +#define WARN( msg ) (void)(0) +#define CAPTURE( ... ) (void)(0) + +#define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ )) +#define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ )) +#define METHOD_AS_TEST_CASE( method, ... ) +#define REGISTER_TEST_CASE( Function, ... ) (void)(0) +#define SECTION( ... ) +#define DYNAMIC_SECTION( ... ) +#define FAIL( ... ) (void)(0) +#define FAIL_CHECK( ... ) (void)(0) +#define SUCCEED( ... ) (void)(0) +#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ )) + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__) +#define TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__) +#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__) +#define TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__ ) +#define TEMPLATE_PRODUCT_TEST_CASE( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ ) +#define TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ ) +#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#else +#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__) ) +#define TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__) ) +#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__ ) ) +#define TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__ ) ) +#define TEMPLATE_PRODUCT_TEST_CASE( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ ) +#define TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ ) +#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#endif + +#define STATIC_REQUIRE( ... ) (void)(0) +#define STATIC_REQUIRE_FALSE( ... ) (void)(0) + +#endif + +#define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION_NO_REG( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature ) + +// "BDD-style" convenience wrappers +#define SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ) ) +#define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ), className ) + +#define GIVEN( desc ) +#define AND_GIVEN( desc ) +#define WHEN( desc ) +#define AND_WHEN( desc ) +#define THEN( desc ) +#define AND_THEN( desc ) + +using Catch::Detail::Approx; + +#endif + +#endif // ! CATCH_CONFIG_IMPL_ONLY + +// start catch_reenable_warnings.h + + +#ifdef __clang__ +# ifdef __ICC // icpc defines the __clang__ macro +# pragma warning(pop) +# else +# pragma clang diagnostic pop +# endif +#elif defined __GNUC__ +# pragma GCC diagnostic pop +#endif + +// end catch_reenable_warnings.h +// end catch.hpp +#endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED + diff --git a/externals/biscuit/tests/src/assembler_bfloat_tests.cpp b/externals/biscuit/tests/src/assembler_bfloat_tests.cpp new file mode 100644 index 00000000..3f0df44f --- /dev/null +++ b/externals/biscuit/tests/src/assembler_bfloat_tests.cpp @@ -0,0 +1,95 @@ +#include <catch/catch.hpp> + +#include <biscuit/assembler.hpp> + +#include "assembler_test_utils.hpp" + +using namespace biscuit; + +TEST_CASE("FCVT.BF16.S", "[Zfbfmin]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FCVT_BF16_S(f31, f7, RMode::RNE); + REQUIRE(value == 0x44838FD3); + + as.RewindBuffer(); + + as.FCVT_BF16_S(f31, f7, RMode::RMM); + REQUIRE(value == 0x4483CFD3); + + as.RewindBuffer(); + + as.FCVT_BF16_S(f31, f7, RMode::DYN); + REQUIRE(value == 0x4483FFD3); +} + +TEST_CASE("FCVT.S.BF16", "[Zfbfmin]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FCVT_S_BF16(f31, f7, RMode::RNE); + REQUIRE(value == 0x40638FD3); + + as.RewindBuffer(); + + as.FCVT_S_BF16(f31, f7, RMode::RMM); + REQUIRE(value == 0x4063CFD3); + + as.RewindBuffer(); + + as.FCVT_S_BF16(f31, f7, RMode::DYN); + REQUIRE(value == 0x4063FFD3); +} + +TEST_CASE("VFNCVTBF16.F.F.W", "[Zvfbfmin]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.VFNCVTBF16_F_F_W(v31, v7, VecMask::Yes); + REQUIRE(value == 0x487E9FD7); + + as.RewindBuffer(); + + as.VFNCVTBF16_F_F_W(v31, v7, VecMask::No); + REQUIRE(value == 0x4A7E9FD7); +} + +TEST_CASE("VFWCVTBF16.F.F.V", "[Zvfbfmin]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.VFWCVTBF16_F_F_V(v31, v7, VecMask::Yes); + REQUIRE(value == 0x48769FD7); + + as.RewindBuffer(); + + as.VFWCVTBF16_F_F_V(v31, v7, VecMask::No); + REQUIRE(value == 0x4A769FD7); +} + +TEST_CASE("VFWMACCBF16.VF", "[Zvfbfwma]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.VFWMACCBF16(v31, f7, v20, VecMask::Yes); + REQUIRE(value == 0xED43DFD7); + + as.RewindBuffer(); + + as.VFWMACCBF16(v31, f7, v20, VecMask::No); + REQUIRE(value == 0xEF43DFD7); +} + +TEST_CASE("VFWMACCBF16.VV", "[Zvfbfwma]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.VFWMACCBF16(v31, v7, v20, VecMask::Yes); + REQUIRE(value == 0xED439FD7); + + as.RewindBuffer(); + + as.VFWMACCBF16(v31, v7, v20, VecMask::No); + REQUIRE(value == 0xEF439FD7); +} diff --git a/externals/biscuit/tests/src/assembler_branch_tests.cpp b/externals/biscuit/tests/src/assembler_branch_tests.cpp new file mode 100644 index 00000000..ed0c7f23 --- /dev/null +++ b/externals/biscuit/tests/src/assembler_branch_tests.cpp @@ -0,0 +1,105 @@ +#include <catch/catch.hpp> + +#include <array> +#include <biscuit/assembler.hpp> + +#include "assembler_test_utils.hpp" + +using namespace biscuit; + +TEST_CASE("Branch to Self", "[branch]") { + uint32_t data; + auto as = MakeAssembler32(data); + + // Simple branch to self with a jump instruction. + { + Label label; + as.Bind(&label); + as.J(&label); + REQUIRE(data == 0x0000006F); + } + + as.RewindBuffer(); + + // Simple branch to self with a compressed jump instruction. + { + Label label; + as.Bind(&label); + as.C_J(&label); + REQUIRE((data & 0xFFFF) == 0xA001); + } + + as.RewindBuffer(); + + // Simple branch to self with a conditional branch instruction. + { + Label label; + as.Bind(&label); + as.BNE(x3, x4, &label); + REQUIRE(data == 0x00419063); + } + + as.RewindBuffer(); + + // Simple branch to self with a compressed branch instruction. + { + Label label; + as.Bind(&label); + as.C_BNEZ(x15, &label); + REQUIRE((data & 0xFFFF) == 0xE381); + } +} + +TEST_CASE("Branch with Instructions Between", "[branch]") { + std::array<uint32_t, 20> data{}; + auto as = MakeAssembler32(data); + + // Simple branch backward + { + Label label; + as.Bind(&label); + as.ADD(x1, x2, x3); + as.SUB(x2, x4, x3); + as.J(&label); + REQUIRE(data[2] == 0xFF9FF06F); + } + + as.RewindBuffer(); + data.fill(0); + + // Simple branch forward + { + Label label; + as.J(&label); + as.ADD(x1, x2, x3); + as.SUB(x2, x4, x3); + as.Bind(&label); + REQUIRE(data[0] == 0x00C0006F); + } + + as.RewindBuffer(); + data.fill(0); + + // Simple branch backward (compressed) + { + Label label; + as.Bind(&label); + as.ADD(x1, x2, x3); + as.SUB(x2, x4, x3); + as.C_J(&label); + REQUIRE((data[2] & 0xFFFF) == 0xBFC5); + } + + as.RewindBuffer(); + data.fill(0); + + // Simple branch forward (compressed) + { + Label label; + as.C_J(&label); + as.ADD(x1, x2, x3); + as.SUB(x2, x4, x3); + as.Bind(&label); + REQUIRE((data[0] & 0xFFFF) == 0xA0A1); + } +} diff --git a/externals/biscuit/tests/src/assembler_cmo_tests.cpp b/externals/biscuit/tests/src/assembler_cmo_tests.cpp new file mode 100644 index 00000000..0b4cffdc --- /dev/null +++ b/externals/biscuit/tests/src/assembler_cmo_tests.cpp @@ -0,0 +1,113 @@ +#include <catch/catch.hpp> + +#include <biscuit/assembler.hpp> + +#include "assembler_test_utils.hpp" + +using namespace biscuit; + +TEST_CASE("CBO.CLEAN", "[cmo]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.CBO_CLEAN(x0); + REQUIRE(value == 0x0010200F); + + as.RewindBuffer(); + + as.CBO_CLEAN(x31); + REQUIRE(value == 0x001FA00F); +} + +TEST_CASE("CBO.FLUSH", "[cmo]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.CBO_FLUSH(x0); + REQUIRE(value == 0x0020200F); + + as.RewindBuffer(); + + as.CBO_FLUSH(x31); + REQUIRE(value == 0x002FA00F); +} + +TEST_CASE("CBO.INVAL", "[cmo]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.CBO_INVAL(x0); + REQUIRE(value == 0x0000200F); + + as.RewindBuffer(); + + as.CBO_INVAL(x31); + REQUIRE(value == 0x000FA00F); +} + +TEST_CASE("CBO.ZERO", "[cmo]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.CBO_ZERO(x0); + REQUIRE(value == 0x0040200F); + + as.RewindBuffer(); + + as.CBO_ZERO(x31); + REQUIRE(value == 0x004FA00F); +} + +TEST_CASE("PREFETCH.I", "[cmo]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.PREFETCH_I(x0); + REQUIRE(value == 0x00006013); + + as.RewindBuffer(); + + as.PREFETCH_I(x31, 2016); + REQUIRE(value == 0x7E0FE013); + + as.RewindBuffer(); + + as.PREFETCH_I(x31, -2016); + REQUIRE(value == 0x820FE013); +} + +TEST_CASE("PREFETCH.R", "[cmo]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.PREFETCH_R(x0); + REQUIRE(value == 0x00106013); + + as.RewindBuffer(); + + as.PREFETCH_R(x31, 2016); + REQUIRE(value == 0x7E1FE013); + + as.RewindBuffer(); + + as.PREFETCH_R(x31, -2016); + REQUIRE(value == 0x821FE013); +} + +TEST_CASE("PREFETCH.W", "[cmo]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.PREFETCH_W(x0); + REQUIRE(value == 0x00306013); + + as.RewindBuffer(); + + as.PREFETCH_W(x31, 2016); + REQUIRE(value == 0x7E3FE013); + + as.RewindBuffer(); + + as.PREFETCH_W(x31, -2016); + REQUIRE(value == 0x823FE013); +} diff --git a/externals/biscuit/tests/src/assembler_privileged_tests.cpp b/externals/biscuit/tests/src/assembler_privileged_tests.cpp new file mode 100644 index 00000000..0f7d8129 --- /dev/null +++ b/externals/biscuit/tests/src/assembler_privileged_tests.cpp @@ -0,0 +1,302 @@ +#include <catch/catch.hpp> + +#include <biscuit/assembler.hpp> + +#include "assembler_test_utils.hpp" + +using namespace biscuit; + +TEST_CASE("HFENCE.VVMA", "[rvpriv]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.HFENCE_VVMA(x0, x0); + REQUIRE(value == 0x22000073); + + as.RewindBuffer(); + + as.HFENCE_VVMA(x15, x15); + REQUIRE(value == 0x22F78073); +} + +TEST_CASE("HFENCE.GVMA", "[rvpriv]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.HFENCE_GVMA(x0, x0); + REQUIRE(value == 0x62000073); + + as.RewindBuffer(); + + as.HFENCE_GVMA(x15, x15); + REQUIRE(value == 0x62F78073); +} + +TEST_CASE("HINVAL.VVMA", "[rvpriv]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.HINVAL_VVMA(x0, x0); + REQUIRE(value == 0x26000073); + + as.RewindBuffer(); + + as.HINVAL_VVMA(x15, x15); + REQUIRE(value == 0x26F78073); +} + +TEST_CASE("HINVAL.GVMA", "[rvpriv]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.HINVAL_GVMA(x0, x0); + REQUIRE(value == 0x66000073); + + as.RewindBuffer(); + + as.HINVAL_GVMA(x15, x15); + REQUIRE(value == 0x66F78073); +} + +TEST_CASE("HLV.B", "[rvpriv]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.HLV_B(x0, x0); + REQUIRE(value == 0x60004073); + + as.RewindBuffer(); + + as.HLV_B(x15, x14); + REQUIRE(value == 0x600747F3); +} + +TEST_CASE("HLV.BU", "[rvpriv]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.HLV_BU(x0, x0); + REQUIRE(value == 0x60104073); + + as.RewindBuffer(); + + as.HLV_BU(x15, x14); + REQUIRE(value == 0x601747F3); +} + +TEST_CASE("HLV.D", "[rvpriv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.HLV_D(x0, x0); + REQUIRE(value == 0x6C004073); + + as.RewindBuffer(); + + as.HLV_D(x15, x14); + REQUIRE(value == 0x6C0747F3); +} + +TEST_CASE("HLV.H", "[rvpriv]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.HLV_H(x0, x0); + REQUIRE(value == 0x64004073); + + as.RewindBuffer(); + + as.HLV_H(x15, x14); + REQUIRE(value == 0x640747F3); +} + +TEST_CASE("HLV.HU", "[rvpriv]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.HLV_HU(x0, x0); + REQUIRE(value == 0x64104073); + + as.RewindBuffer(); + + as.HLV_HU(x15, x14); + REQUIRE(value == 0x641747F3); +} + +TEST_CASE("HLV.W", "[rvpriv]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.HLV_W(x0, x0); + REQUIRE(value == 0x68004073); + + as.RewindBuffer(); + + as.HLV_W(x15, x14); + REQUIRE(value == 0x680747F3); +} + +TEST_CASE("HLV.WU", "[rvpriv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.HLV_WU(x0, x0); + REQUIRE(value == 0x68104073); + + as.RewindBuffer(); + + as.HLV_WU(x15, x14); + REQUIRE(value == 0x681747F3); +} + +TEST_CASE("HLVX.HU", "[rvpriv]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.HLVX_HU(x0, x0); + REQUIRE(value == 0x64304073); + + as.RewindBuffer(); + + as.HLVX_HU(x15, x14); + REQUIRE(value == 0x643747F3); +} + +TEST_CASE("HLVX.WU", "[rvpriv]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.HLVX_WU(x0, x0); + REQUIRE(value == 0x68304073); + + as.RewindBuffer(); + + as.HLVX_WU(x15, x14); + REQUIRE(value == 0x683747F3); +} + +TEST_CASE("HSV.B", "[rvpriv]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.HSV_B(x0, x0); + REQUIRE(value == 0x62004073); + + as.RewindBuffer(); + + as.HSV_B(x15, x14); + REQUIRE(value == 0x62F74073); +} + +TEST_CASE("HSV.D", "[rvpriv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.HSV_D(x0, x0); + REQUIRE(value == 0x6E004073); + + as.RewindBuffer(); + + as.HSV_D(x15, x14); + REQUIRE(value == 0x6EF74073); +} + +TEST_CASE("HSV.H", "[rvpriv]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.HSV_H(x0, x0); + REQUIRE(value == 0x66004073); + + as.RewindBuffer(); + + as.HSV_H(x15, x14); + REQUIRE(value == 0x66F74073); +} + +TEST_CASE("HSV.W", "[rvpriv]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.HSV_W(x0, x0); + REQUIRE(value == 0x6A004073); + + as.RewindBuffer(); + + as.HSV_W(x15, x14); + REQUIRE(value == 0x6AF74073); +} + +TEST_CASE("MRET", "[rvpriv]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.MRET(); + REQUIRE(value == 0x30200073); +} + +TEST_CASE("SFENCE.INVAL.IR", "[rvpriv]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.SFENCE_INVAL_IR(); + REQUIRE(value == 0x18100073); +} + +TEST_CASE("SFENCE.VMA", "[rvpriv]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.SFENCE_VMA(x0, x0); + REQUIRE(value == 0x12000073); + + as.RewindBuffer(); + + as.SFENCE_VMA(x15, x15); + REQUIRE(value == 0x12F78073); +} + +TEST_CASE("SFENCE.W.INVAL", "[rvpriv]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.SFENCE_W_INVAL(); + REQUIRE(value == 0x18000073); +} + +TEST_CASE("SINVAL.VMA", "[rvpriv]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.SINVAL_VMA(x0, x0); + REQUIRE(value == 0x16000073); + + as.RewindBuffer(); + + as.SINVAL_VMA(x15, x15); + REQUIRE(value == 0x16F78073); +} + +TEST_CASE("SRET", "[rvpriv]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.SRET(); + REQUIRE(value == 0x10200073); +} + +TEST_CASE("URET", "[rvpriv]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.URET(); + REQUIRE(value == 0x00200073); +} + +TEST_CASE("WFI", "[rvpriv]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.WFI(); + REQUIRE(value == 0x10500073); +} diff --git a/externals/biscuit/tests/src/assembler_rv32i_tests.cpp b/externals/biscuit/tests/src/assembler_rv32i_tests.cpp new file mode 100644 index 00000000..a3dec818 --- /dev/null +++ b/externals/biscuit/tests/src/assembler_rv32i_tests.cpp @@ -0,0 +1,769 @@ +#include <catch/catch.hpp> + +#include <array> +#include <biscuit/assembler.hpp> + +#include "assembler_test_utils.hpp" + +using namespace biscuit; + +TEST_CASE("ADD", "[rv32i]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.ADD(x7, x15, x31); + REQUIRE(value == 0x01F783B3); + + as.RewindBuffer(); + + as.ADD(x31, x31, x31); + REQUIRE(value == 0x01FF8FB3); + + as.RewindBuffer(); + + as.ADD(x0, x0, x0); + REQUIRE(value == 0x00000033); +} + +TEST_CASE("ADDI", "[rv32i]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.ADDI(x15, x31, 1024); + REQUIRE(value == 0x400F8793); + + as.RewindBuffer(); + + as.ADDI(x15, x31, 2048); + REQUIRE(value == 0x800F8793); + + as.RewindBuffer(); + + as.ADDI(x15, x31, 4095); + REQUIRE(value == 0xFFFF8793); +} + +TEST_CASE("AND", "[rv32i]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.AND(x7, x15, x31); + REQUIRE(value == 0x01F7F3B3); + + as.RewindBuffer(); + + as.AND(x31, x31, x31); + REQUIRE(value == 0x01FFFFB3); + + as.RewindBuffer(); + + as.AND(x0, x0, x0); + REQUIRE(value == 0x00007033); +} + +TEST_CASE("ANDI", "[rv32i]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.ANDI(x15, x31, 1024); + REQUIRE(value == 0x400FF793); + + as.RewindBuffer(); + + as.ANDI(x15, x31, 2048); + REQUIRE(value == 0x800FF793); + + as.RewindBuffer(); + + as.ANDI(x15, x31, 4095); + REQUIRE(value == 0xFFFFF793); +} + +TEST_CASE("AUIPC", "[rv32i]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.AUIPC(x31, -1); + REQUIRE(value == 0xFFFFFF97); + + as.RewindBuffer(); + + as.AUIPC(x31, 0); + REQUIRE(value == 0x00000F97); + + as.RewindBuffer(); + + as.AUIPC(x31, 0x00FF00FF); + REQUIRE(value == 0xF00FFF97); +} + +TEST_CASE("BEQ", "[rv32i]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.BEQ(x15, x31, 2000); + REQUIRE(value == 0x7DF78863); + + as.RewindBuffer(); + + as.BEQ(x15, x31, -2); + REQUIRE(value == 0xFFF78FE3); +} + +TEST_CASE("BGE", "[rv32i]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.BGE(x15, x31, 2000); + REQUIRE(value == 0x7DF7D863); + + as.RewindBuffer(); + + as.BGE(x15, x31, -2); + REQUIRE(value == 0xFFF7DFE3); +} + +TEST_CASE("BGEU", "[rv32i]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.BGEU(x15, x31, 2000); + REQUIRE(value == 0x7DF7F863); + + as.RewindBuffer(); + + as.BGEU(x15, x31, -2); + REQUIRE(value == 0xFFF7FFE3); +} + +TEST_CASE("BNE", "[rv32i]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.BNE(x15, x31, 2000); + REQUIRE(value == 0x7DF79863); + + as.RewindBuffer(); + + as.BNE(x15, x31, -2); + REQUIRE(value == 0xFFF79FE3); +} + +TEST_CASE("BLT", "[rv32i]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.BLT(x15, x31, 2000); + REQUIRE(value == 0x7DF7C863); + + as.RewindBuffer(); + + as.BLT(x15, x31, -2); + REQUIRE(value == 0xFFF7CFE3); +} + +TEST_CASE("BLTU", "[rv32i]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.BLTU(x15, x31, 2000); + REQUIRE(value == 0x7DF7E863); + + as.RewindBuffer(); + + as.BLTU(x15, x31, -2); + REQUIRE(value == 0xFFF7EFE3); +} + +TEST_CASE("CALL", "[rv32i]") { + std::array<uint32_t, 2> vals{}; + auto as = MakeAssembler32(vals); + + const auto compare_vals = [&vals](uint32_t val_1, uint32_t val_2) { + REQUIRE(vals[0] == val_1); + REQUIRE(vals[1] == val_2); + }; + + as.CALL(-1); + compare_vals(0x00000097, 0xFFF080E7); +} + +TEST_CASE("EBREAK", "[rv32i]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.EBREAK(); + REQUIRE(value == 0x00100073); +} + +TEST_CASE("ECALL", "[rv32i]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.ECALL(); + REQUIRE(value == 0x00000073); +} + +TEST_CASE("FENCE", "[rv32i]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FENCE(FenceOrder::IORW, FenceOrder::IORW); + REQUIRE(value == 0x0FF0000F); + + as.RewindBuffer(); + + as.FENCETSO(); + REQUIRE(value == 0x8330000F); + + as.RewindBuffer(); + + as.FENCEI(); + REQUIRE(value == 0x0000100F); +} + +TEST_CASE("JAL", "[rv32i]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.JAL(x31, 0xFFFFFFFF); + REQUIRE(value == 0xFFFFFFEF); + + as.RewindBuffer(); + + as.JAL(x31, 2000); + REQUIRE(value == 0x7D000FEF); + + as.RewindBuffer(); + + as.JAL(x31, 100000); + REQUIRE(value == 0x6A018FEF); +} + +TEST_CASE("JALR", "[rv32i]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.JALR(x15, 1024, x31); + REQUIRE(value == 0x400F87E7); + + as.RewindBuffer(); + + as.JALR(x15, 1536, x31); + REQUIRE(value == 0x600F87E7); + + as.RewindBuffer(); + + as.JALR(x15, -1, x31); + REQUIRE(value == 0xFFFF87E7); +} + +TEST_CASE("LB", "[rv32i]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.LB(x15, 1024, x31); + REQUIRE(value == 0x400F8783); + + as.RewindBuffer(); + + as.LB(x15, 1536, x31); + REQUIRE(value == 0x600F8783); + + as.RewindBuffer(); + + as.LB(x15, -1, x31); + REQUIRE(value == 0xFFFF8783); +} + +TEST_CASE("LBU", "[rv32i]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.LBU(x15, 1024, x31); + REQUIRE(value == 0x400FC783); + + as.RewindBuffer(); + + as.LBU(x15, 1536, x31); + REQUIRE(value == 0x600FC783); + + as.RewindBuffer(); + + as.LBU(x15, -1, x31); + REQUIRE(value == 0xFFFFC783); +} + +TEST_CASE("LH", "[rv32i]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.LH(x15, 1024, x31); + REQUIRE(value == 0x400F9783); + + as.RewindBuffer(); + + as.LH(x15, 1536, x31); + REQUIRE(value == 0x600F9783); + + as.RewindBuffer(); + + as.LH(x15, -1, x31); + REQUIRE(value == 0xFFFF9783); +} + +TEST_CASE("LHU", "[rv32i]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.LHU(x15, 1024, x31); + REQUIRE(value == 0x400FD783); + + as.RewindBuffer(); + + as.LHU(x15, 1536, x31); + REQUIRE(value == 0x600FD783); + + as.RewindBuffer(); + + as.LHU(x15, -1, x31); + REQUIRE(value == 0xFFFFD783); +} + +TEST_CASE("LI", "[rv32i]") { + std::array<uint32_t, 2> vals{}; + auto as = MakeAssembler32(vals); + + const auto compare_vals = [&vals](uint32_t val_1, uint32_t val_2) { + REQUIRE(vals[0] == val_1); + REQUIRE(vals[1] == val_2); + }; + + ///////// Single ADDI cases + + as.LI(x1, 0); + // addi x1, x0, 0 + compare_vals(0x00000093, 0x00000000); + as.RewindBuffer(); + vals = {}; + + as.LI(x1, -1); + // addi x1, x0, -1 + compare_vals(0xFFF00093, 0x00000000); + as.RewindBuffer(); + vals = {}; + + as.LI(x1, 42); + // addi x1, x0, 42 + compare_vals(0x02A00093, 0x000000000); + as.RewindBuffer(); + vals = {}; + + as.LI(x1, 0x7ff); + // addi x1, x0, 2047 + compare_vals(0x7FF00093, 0x00000000); + as.RewindBuffer(); + vals = {}; + + ///////// Single LUI cases + + as.LI(x1, 0x2A000); + // lui x1, 42 + compare_vals(0x0002A0B7, 0x00000000); + as.RewindBuffer(); + vals = {}; + + as.LI(x1, ~0xFFF); + // lui x1, -1 + compare_vals(0xFFFFF0B7, 0x00000000); + as.RewindBuffer(); + vals = {}; + + as.LI(x1, INT32_MIN); + // lui x1, -524288 + compare_vals(0x800000B7, 0x00000000); + as.RewindBuffer(); + vals = {}; + + ///////// Full LUI+ADDI cases + + as.LI(x1, 0x11111111); + // lui x1, 69905 + // addi x1, x1, 273 + compare_vals(0x111110B7, 0x11108093); + as.RewindBuffer(); + vals = {}; + + as.LI(x1, INT32_MAX); + // lui x1, -524288 + // addi x1, x1, -1 + compare_vals(0x800000B7, 0xFFF08093); +} + +TEST_CASE("LUI", "[rv32i]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.LUI(x10, 0xFFFFFFFF); + REQUIRE(value == 0xFFFFF537); + + as.RewindBuffer(); + + as.LUI(x10, 0xFFF7FFFF); + REQUIRE(value == 0x7FFFF537); + + as.RewindBuffer(); + + as.LUI(x31, 0xFFFFFFFF); + REQUIRE(value == 0xFFFFFFB7); +} + +TEST_CASE("LW", "[rv32i]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.LW(x15, 1024, x31); + REQUIRE(value == 0x400FA783); + + as.RewindBuffer(); + + as.LW(x15, 1536, x31); + REQUIRE(value == 0x600FA783); + + as.RewindBuffer(); + + as.LW(x15, -1, x31); + REQUIRE(value == 0xFFFFA783); +} + +TEST_CASE("OR", "[rv32i]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.OR(x7, x15, x31); + REQUIRE(value == 0x01F7E3B3); + + as.RewindBuffer(); + + as.OR(x31, x31, x31); + REQUIRE(value == 0x01FFEFB3); + + as.RewindBuffer(); + + as.OR(x0, x0, x0); + REQUIRE(value == 0x00006033); +} + +TEST_CASE("ORI", "[rv32i]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.ORI(x15, x31, 1024); + REQUIRE(value == 0x400FE793); + + as.RewindBuffer(); + + as.ORI(x15, x31, 2048); + REQUIRE(value == 0x800FE793); + + as.RewindBuffer(); + + as.ORI(x15, x31, 4095); + REQUIRE(value == 0xFFFFE793); +} + +TEST_CASE("PAUSE", "[rv32i]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.PAUSE(); + REQUIRE(value == 0x0100000F); +} + +TEST_CASE("SB", "[rv32i]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.SB(x31, 1024, x15); + REQUIRE(value == 0x41F78023); + + as.RewindBuffer(); + + as.SB(x31, 1536, x15); + REQUIRE(value == 0x61F78023); + + as.RewindBuffer(); + + as.SB(x31, -1, x15); + REQUIRE(value == 0xFFF78FA3); +} + +TEST_CASE("SH", "[rv32i]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.SH(x31, 1024, x15); + REQUIRE(value == 0x41F79023); + + as.RewindBuffer(); + + as.SH(x31, 1536, x15); + REQUIRE(value == 0x61F79023); + + as.RewindBuffer(); + + as.SH(x31, -1, x15); + REQUIRE(value == 0xFFF79FA3); +} + +TEST_CASE("SLL", "[rv32i]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.SLL(x7, x15, x31); + REQUIRE(value == 0x01F793B3); + + as.RewindBuffer(); + + as.SLL(x31, x31, x31); + REQUIRE(value == 0x01FF9FB3); + + as.RewindBuffer(); + + as.SLL(x0, x0, x0); + REQUIRE(value == 0x00001033); +} + +TEST_CASE("SLLI", "[rv32i]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.SLLI(x31, x15, 10); + REQUIRE(value == 0x00A79F93); + + as.RewindBuffer(); + + as.SLLI(x31, x15, 20); + REQUIRE(value == 0x01479F93); + + as.RewindBuffer(); + + as.SLLI(x31, x15, 31); + REQUIRE(value == 0x01F79F93); +} + +TEST_CASE("SLT", "[rv32i]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.SLT(x7, x15, x31); + REQUIRE(value == 0x01F7A3B3); + + as.RewindBuffer(); + + as.SLT(x31, x31, x31); + REQUIRE(value == 0x01FFAFB3); + + as.RewindBuffer(); + + as.SLT(x0, x0, x0); + REQUIRE(value == 0x00002033); +} + +TEST_CASE("SLTI", "[rv32i]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.SLTI(x15, x31, 1024); + REQUIRE(value == 0x400FA793); + + as.RewindBuffer(); + + as.SLTI(x15, x31, -2048); + REQUIRE(value == 0x800FA793); + + as.RewindBuffer(); + + as.SLTI(x15, x31, -1); + REQUIRE(value == 0xFFFFA793); +} + +TEST_CASE("SLTIU", "[rv32i]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.SLTIU(x15, x31, 1024); + REQUIRE(value == 0x400FB793); + + as.RewindBuffer(); + + as.SLTIU(x15, x31, -2048); + REQUIRE(value == 0x800FB793); + + as.RewindBuffer(); + + as.SLTIU(x15, x31, -1); + REQUIRE(value == 0xFFFFB793); +} + +TEST_CASE("SLTU", "[rv32i]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.SLTU(x7, x15, x31); + REQUIRE(value == 0x01F7B3B3); + + as.RewindBuffer(); + + as.SLTU(x31, x31, x31); + REQUIRE(value == 0x01FFBFB3); + + as.RewindBuffer(); + + as.SLTU(x0, x0, x0); + REQUIRE(value == 0x00003033); +} + +TEST_CASE("SRA", "[rv32i]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.SRA(x7, x15, x31); + REQUIRE(value == 0x41F7D3B3); + + as.RewindBuffer(); + + as.SRA(x31, x31, x31); + REQUIRE(value == 0x41FFDFB3); + + as.RewindBuffer(); + + as.SRA(x0, x0, x0); + REQUIRE(value == 0x40005033); +} + +TEST_CASE("SRAI", "[rv32i]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.SRAI(x31, x15, 10); + REQUIRE(value == 0x40A7DF93); + + as.RewindBuffer(); + + as.SRAI(x31, x15, 20); + REQUIRE(value == 0x4147DF93); + + as.RewindBuffer(); + + as.SRAI(x31, x15, 31); + REQUIRE(value == 0x41F7DF93); +} + +TEST_CASE("SRL", "[rv32i]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.SRL(x7, x15, x31); + REQUIRE(value == 0x01F7D3B3); + + as.RewindBuffer(); + + as.SRL(x31, x31, x31); + REQUIRE(value == 0x01FFDFB3); + + as.RewindBuffer(); + + as.SRL(x0, x0, x0); + REQUIRE(value == 0x00005033); +} + +TEST_CASE("SRLI", "[rv32i]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.SRLI(x31, x15, 10); + REQUIRE(value == 0x00A7DF93); + + as.RewindBuffer(); + + as.SRLI(x31, x15, 20); + REQUIRE(value == 0x0147DF93); + + as.RewindBuffer(); + + as.SRLI(x31, x15, 31); + REQUIRE(value == 0x01F7DF93); +} + +TEST_CASE("SUB", "[rv32i]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.SUB(x7, x15, x31); + REQUIRE(value == 0x41F783B3); + + as.RewindBuffer(); + + as.SUB(x31, x31, x31); + REQUIRE(value == 0x41FF8FB3); + + as.RewindBuffer(); + + as.SUB(x0, x0, x0); + REQUIRE(value == 0x40000033); +} + +TEST_CASE("SW", "[rv32i]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.SW(x31, 1024, x15); + REQUIRE(value == 0x41F7A023); + + as.RewindBuffer(); + + as.SW(x31, 1536, x15); + REQUIRE(value == 0x61F7A023); + + as.RewindBuffer(); + + as.SW(x31, -1, x15); + REQUIRE(value == 0xFFF7AFA3); +} + +TEST_CASE("XOR", "[rv32i]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.XOR(x7, x15, x31); + REQUIRE(value == 0x01F7C3B3); + + as.RewindBuffer(); + + as.XOR(x31, x31, x31); + REQUIRE(value == 0x01FFCFB3); + + as.RewindBuffer(); + + as.XOR(x0, x0, x0); + REQUIRE(value == 0x00004033); +} + +TEST_CASE("XORI", "[rv32i]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.XORI(x15, x31, 1024); + REQUIRE(value == 0x400FC793); + + as.RewindBuffer(); + + as.XORI(x15, x31, 2048); + REQUIRE(value == 0x800FC793); + + as.RewindBuffer(); + + as.XORI(x15, x31, 4095); + REQUIRE(value == 0xFFFFC793); +} diff --git a/externals/biscuit/tests/src/assembler_rv64i_tests.cpp b/externals/biscuit/tests/src/assembler_rv64i_tests.cpp new file mode 100644 index 00000000..e019873b --- /dev/null +++ b/externals/biscuit/tests/src/assembler_rv64i_tests.cpp @@ -0,0 +1,436 @@ +#include <catch/catch.hpp> + +#include <array> +#include <biscuit/assembler.hpp> + +#include "assembler_test_utils.hpp" + +using namespace biscuit; + +TEST_CASE("ADDIW", "[rv64i]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.ADDIW(x31, x15, 1024); + REQUIRE(value == 0x40078F9B); + + as.RewindBuffer(); + + as.ADDIW(x31, x15, 2048); + REQUIRE(value == 0x80078F9B); + + as.RewindBuffer(); + + as.ADDIW(x31, x15, 4095); + REQUIRE(value == 0xFFF78F9B); +} + +TEST_CASE("ADDW", "[rv64i]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.ADDW(x7, x15, x31); + REQUIRE(value == 0x01F783BB); + + as.RewindBuffer(); + + as.ADDW(x31, x31, x31); + REQUIRE(value == 0x01FF8FBB); + + as.RewindBuffer(); + + as.ADDW(x0, x0, x0); + REQUIRE(value == 0x0000003B); +} + +TEST_CASE("LWU", "[rv64i]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.LWU(x15, 1024, x31); + REQUIRE(value == 0x400FE783); + + as.RewindBuffer(); + + as.LWU(x15, 1536, x31); + REQUIRE(value == 0x600FE783); + + as.RewindBuffer(); + + as.LWU(x15, -1, x31); + REQUIRE(value == 0xFFFFE783); +} + +TEST_CASE("LD", "[rv64i]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.LD(x15, 1024, x31); + REQUIRE(value == 0x400FB783); + + as.RewindBuffer(); + + as.LD(x15, 1536, x31); + REQUIRE(value == 0x600FB783); + + as.RewindBuffer(); + + as.LD(x15, -1, x31); + REQUIRE(value == 0xFFFFB783); +} + +TEST_CASE("LI (RV64)", "[rv64i]") { + // Up to 8 instructions can be generated + std::array<uint32_t, 8> vals{}; + auto as = MakeAssembler64(vals); + + const auto compare_vals = [&vals]<typename... Args>(const Args&... args) { + static_assert(sizeof...(args) <= vals.size()); + + size_t i = 0; + for (const auto arg : {args...}) { + REQUIRE(vals[i] == arg); + i++; + } + }; + + ///////// Single ADDIW cases + + as.LI(x1, 0); + // addiw x1, x0, 0 + compare_vals(0x0000009BU, 0x00000000U); + as.RewindBuffer(); + vals = {}; + + as.LI(x1, -1); + // addiw x1, x0, -1 + compare_vals(0xFFF0009BU, 0x00000000U); + as.RewindBuffer(); + vals = {}; + + as.LI(x1, 42); + // addiw x1, x0, 42 + compare_vals(0x02A0009BU, 0x000000000U); + as.RewindBuffer(); + vals = {}; + + as.LI(x1, 0x7ff); + // addiw x1, x0, 2047 + compare_vals(0x7FF0009BU, 0x00000000U); + as.RewindBuffer(); + vals = {}; + + ///////// Single LUI cases + + as.LI(x1, 0x2A000); + // lui x1, 42 + compare_vals(0x0002A0B7U, 0x00000000U); + as.RewindBuffer(); + vals = {}; + + as.LI(x1, ~0xFFF); + // lui x1, -1 + compare_vals(0xFFFFF0B7U, 0x00000000U); + as.RewindBuffer(); + vals = {}; + + as.LI(x1, INT32_MIN); + // lui x1, -524288 + compare_vals(0x800000B7U, 0x00000000U); + as.RewindBuffer(); + vals = {}; + + ///////// LUI+ADDIW cases + + as.LI(x1, 0x11111111); + // lui x1, 69905 + // addiw x1, x1, 273 + compare_vals(0x111110B7U, 0x1110809BU, 0x00000000U); + as.RewindBuffer(); + vals = {}; + + as.LI(x1, INT32_MAX); + // lui x1, -524288 + // addiw x1, x1, -1 + compare_vals(0x800000B7U, 0xFFF0809BU, 0x00000000U); + as.RewindBuffer(); + vals = {}; + + ///////// ADDIW+SLLI cases + + as.LI(x1, 0x7FF0000000ULL); + // addiw x1, x0, 2047 + // slli x1, x1, 28 + compare_vals(0x7FF0009BU, 0x01C09093U, 0x000000000U); + as.RewindBuffer(); + vals = {}; + + as.LI(x1, 0xABC00000ULL); + // addiw x1, x0, 687 + // slli x1, x1, 22 + compare_vals(0x2AF0009BU, 0x01609093U, 0x000000000U); + as.RewindBuffer(); + vals = {}; + + ///////// LUI+ADDIW+SLLI cases + + as.LI(x1, 0x7FFFFFFF0000ULL); + // lui x1, -524288 + // addiw x1, x1, -1 + // slli x1, x1, 16 + compare_vals(0x800000B7U, 0xFFF0809BU, 0x01009093U, 0x000000000U); + as.RewindBuffer(); + vals = {}; + + ///////// LUI+ADDIW+SLLI+ADDI cases + + as.LI(x1, 0x7FFFFFFF0123); + // lui x1, -524288 + // addiw x1, x1, -1 + // slli x1, x1, 16 + // addi x1, x1, 291 + compare_vals(0x800000B7U, 0xfff0809BU, 0x01009093U, 0x12308093U, + 0x000000000U); + as.RewindBuffer(); + vals = {}; + + ///////// ADDIW+SLLI+ADDI+SLLI+ADDI cases + + as.LI(x1, 0x8000000080000001ULL); + // addiw x1, x0, -1 + // slli x1, x1, 32 + // addi x1, x1, 1 + // slli x1, x1, 31 + // addi x1, x1, 1 + compare_vals(0xFFF0009BU, 0x02009093U, 0x00108093U, 0x01F09093U, + 0x00108093U, 0x000000000U); + as.RewindBuffer(); + vals = {}; + + ///////// Full LUI+ADDIW+SLLI+ADDI+SLLI+ADDI+SLLI+ADDI cases + + as.LI(x1, 0x80808000808080F1ULL); + // lui x1, -16 + // addiw x1, x1, 257 + // slli x1, x1, 16 + // addi x1, x1, 1 + // slli x1, x1, 16 + // addi x1, x1, 257 + // slli x1, x1, 15 + // addi x1, x1, 241 + compare_vals(0xFFFF00B7U, 0x1010809BU, 0x01009093U, 0x00108093U, + 0x01009093U, 0x10108093U, 0x00F09093U, 0x0F108093U); +} + +TEST_CASE("SD", "[rv64i]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.SD(x15, 1024, x31); + REQUIRE(value == 0x40FFB023); + + as.RewindBuffer(); + + as.SD(x15, 1536, x31); + REQUIRE(value == 0x60FFB023); + + as.RewindBuffer(); + + as.SD(x15, -1, x31); + REQUIRE(value == 0xFEFFBFA3); +} + +TEST_CASE("SLLI (RV64)", "[rv64i]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.SLLI(x31, x15, 10); + REQUIRE(value == 0x00A79F93); + + as.RewindBuffer(); + + as.SLLI(x31, x15, 20); + REQUIRE(value == 0x01479F93); + + as.RewindBuffer(); + + as.SLLI(x31, x15, 31); + REQUIRE(value == 0x01F79F93); + + as.RewindBuffer(); + + as.SLLI(x31, x15, 63); + REQUIRE(value == 0x03F79F93); +} + +TEST_CASE("SLLIW", "[rv64i]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.SLLIW(x31, x15, 10); + REQUIRE(value == 0x00A79F9B); + + as.RewindBuffer(); + + as.SLLIW(x31, x15, 20); + REQUIRE(value == 0x01479F9B); + + as.RewindBuffer(); + + as.SLLIW(x31, x15, 31); + REQUIRE(value == 0x01F79F9B); +} + +TEST_CASE("SLLW", "[rv64i]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.SLLW(x7, x15, x31); + REQUIRE(value == 0x01F793BB); + + as.RewindBuffer(); + + as.SLLW(x31, x31, x31); + REQUIRE(value == 0x01FF9FBB); + + as.RewindBuffer(); + + as.SLLW(x0, x0, x0); + REQUIRE(value == 0x0000103B); +} + +TEST_CASE("SRAI (RV64)", "[rv64i]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.SRAI(x31, x15, 10); + REQUIRE(value == 0x40A7DF93); + + as.RewindBuffer(); + + as.SRAI(x31, x15, 20); + REQUIRE(value == 0x4147DF93); + + as.RewindBuffer(); + + as.SRAI(x31, x15, 31); + REQUIRE(value == 0x41F7DF93); + + as.RewindBuffer(); + + as.SRAI(x31, x15, 63); + REQUIRE(value == 0x43F7DF93); +} + +TEST_CASE("SRAIW", "[rv64i]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.SRAIW(x31, x15, 10); + REQUIRE(value == 0x40A7DF9B); + + as.RewindBuffer(); + + as.SRAIW(x31, x15, 20); + REQUIRE(value == 0x4147DF9B); + + as.RewindBuffer(); + + as.SRAIW(x31, x15, 31); + REQUIRE(value == 0x41F7DF9B); +} + +TEST_CASE("SRAW", "[rv64i]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.SRAW(x7, x15, x31); + REQUIRE(value == 0x41F7D3BB); + + as.RewindBuffer(); + + as.SRAW(x31, x31, x31); + REQUIRE(value == 0x41FFDFBB); + + as.RewindBuffer(); + + as.SRAW(x0, x0, x0); + REQUIRE(value == 0x4000503B); +} + +TEST_CASE("SRLI (RV64)", "[rv64i]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.SRLI(x31, x15, 10); + REQUIRE(value == 0x00A7DF93); + + as.RewindBuffer(); + + as.SRLI(x31, x15, 20); + REQUIRE(value == 0x0147DF93); + + as.RewindBuffer(); + + as.SRLI(x31, x15, 31); + REQUIRE(value == 0x01F7DF93); + + as.RewindBuffer(); + + as.SRLI(x31, x15, 63); + REQUIRE(value == 0x03F7DF93); +} + +TEST_CASE("SRLIW", "[rv64i]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.SRLIW(x31, x15, 10); + REQUIRE(value == 0x00A7DF9B); + + as.RewindBuffer(); + + as.SRLIW(x31, x15, 20); + REQUIRE(value == 0x0147DF9B); + + as.RewindBuffer(); + + as.SRLIW(x31, x15, 31); + REQUIRE(value == 0x01F7DF9B); +} + +TEST_CASE("SRLW", "[rv64i]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.SRLW(x7, x15, x31); + REQUIRE(value == 0x01F7D3BB); + + as.RewindBuffer(); + + as.SRLW(x31, x31, x31); + REQUIRE(value == 0x01FFDFBB); + + as.RewindBuffer(); + + as.SRLW(x0, x0, x0); + REQUIRE(value == 0x0000503B); +} + +TEST_CASE("SUBW", "[rv64i]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.SUBW(x7, x15, x31); + REQUIRE(value == 0x41F783BB); + + as.RewindBuffer(); + + as.SUBW(x31, x31, x31); + REQUIRE(value == 0x41FF8FBB); + + as.RewindBuffer(); + + as.SUBW(x0, x0, x0); + REQUIRE(value == 0x4000003B); +} diff --git a/externals/biscuit/tests/src/assembler_rva_tests.cpp b/externals/biscuit/tests/src/assembler_rva_tests.cpp new file mode 100644 index 00000000..b972a666 --- /dev/null +++ b/externals/biscuit/tests/src/assembler_rva_tests.cpp @@ -0,0 +1,513 @@ +#include <catch/catch.hpp> + +#include <biscuit/assembler.hpp> + +#include "assembler_test_utils.hpp" + +using namespace biscuit; + +TEST_CASE("AMOADD.D", "[rv64a]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.AMOADD_D(Ordering::None, x31, x7, x15); + REQUIRE(value == 0x0077BFAF); + + as.RewindBuffer(); + + as.AMOADD_D(Ordering::AQ, x31, x7, x15); + REQUIRE(value == 0x0477BFAF); + + as.RewindBuffer(); + + as.AMOADD_D(Ordering::RL, x31, x7, x15); + REQUIRE(value == 0x0277BFAF); + + as.RewindBuffer(); + + as.AMOADD_D(Ordering::AQRL, x31, x7, x15); + REQUIRE(value == 0x0677BFAF); +} + +TEST_CASE("AMOADD.W", "[rv32a]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.AMOADD_W(Ordering::None, x31, x7, x15); + REQUIRE(value == 0x0077AFAF); + + as.RewindBuffer(); + + as.AMOADD_W(Ordering::AQ, x31, x7, x15); + REQUIRE(value == 0x0477AFAF); + + as.RewindBuffer(); + + as.AMOADD_W(Ordering::RL, x31, x7, x15); + REQUIRE(value == 0x0277AFAF); + + as.RewindBuffer(); + + as.AMOADD_W(Ordering::AQRL, x31, x7, x15); + REQUIRE(value == 0x0677AFAF); +} + +TEST_CASE("AMOAND.D", "[rv64a]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.AMOAND_D(Ordering::None, x31, x7, x15); + REQUIRE(value == 0x6077BFAF); + + as.RewindBuffer(); + + as.AMOAND_D(Ordering::AQ, x31, x7, x15); + REQUIRE(value == 0x6477BFAF); + + as.RewindBuffer(); + + as.AMOAND_D(Ordering::RL, x31, x7, x15); + REQUIRE(value == 0x6277BFAF); + + as.RewindBuffer(); + + as.AMOAND_D(Ordering::AQRL, x31, x7, x15); + REQUIRE(value == 0x6677BFAF); +} + +TEST_CASE("AMOAND.W", "[rv32a]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.AMOAND_W(Ordering::None, x31, x7, x15); + REQUIRE(value == 0x6077AFAF); + + as.RewindBuffer(); + + as.AMOAND_W(Ordering::AQ, x31, x7, x15); + REQUIRE(value == 0x6477AFAF); + + as.RewindBuffer(); + + as.AMOAND_W(Ordering::RL, x31, x7, x15); + REQUIRE(value == 0x6277AFAF); + + as.RewindBuffer(); + + as.AMOAND_W(Ordering::AQRL, x31, x7, x15); + REQUIRE(value == 0x6677AFAF); +} + +TEST_CASE("AMOMAX.D", "[rv64a]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.AMOMAX_D(Ordering::None, x31, x7, x15); + REQUIRE(value == 0xA077BFAF); + + as.RewindBuffer(); + + as.AMOMAX_D(Ordering::AQ, x31, x7, x15); + REQUIRE(value == 0xA477BFAF); + + as.RewindBuffer(); + + as.AMOMAX_D(Ordering::RL, x31, x7, x15); + REQUIRE(value == 0xA277BFAF); + + as.RewindBuffer(); + + as.AMOMAX_D(Ordering::AQRL, x31, x7, x15); + REQUIRE(value == 0xA677BFAF); +} + +TEST_CASE("AMOMAX.W", "[rv32a]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.AMOMAX_W(Ordering::None, x31, x7, x15); + REQUIRE(value == 0xA077AFAF); + + as.RewindBuffer(); + + as.AMOMAX_W(Ordering::AQ, x31, x7, x15); + REQUIRE(value == 0xA477AFAF); + + as.RewindBuffer(); + + as.AMOMAX_W(Ordering::RL, x31, x7, x15); + REQUIRE(value == 0xA277AFAF); + + as.RewindBuffer(); + + as.AMOMAX_W(Ordering::AQRL, x31, x7, x15); + REQUIRE(value == 0xA677AFAF); +} + +TEST_CASE("AMOMAXU.D", "[rv64a]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.AMOMAXU_D(Ordering::None, x31, x7, x15); + REQUIRE(value == 0xE077BFAF); + + as.RewindBuffer(); + + as.AMOMAXU_D(Ordering::AQ, x31, x7, x15); + REQUIRE(value == 0xE477BFAF); + + as.RewindBuffer(); + + as.AMOMAXU_D(Ordering::RL, x31, x7, x15); + REQUIRE(value == 0xE277BFAF); + + as.RewindBuffer(); + + as.AMOMAXU_D(Ordering::AQRL, x31, x7, x15); + REQUIRE(value == 0xE677BFAF); +} + +TEST_CASE("AMOMAXU.W", "[rv32a]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.AMOMAXU_W(Ordering::None, x31, x7, x15); + REQUIRE(value == 0xE077AFAF); + + as.RewindBuffer(); + + as.AMOMAXU_W(Ordering::AQ, x31, x7, x15); + REQUIRE(value == 0xE477AFAF); + + as.RewindBuffer(); + + as.AMOMAXU_W(Ordering::RL, x31, x7, x15); + REQUIRE(value == 0xE277AFAF); + + as.RewindBuffer(); + + as.AMOMAXU_W(Ordering::AQRL, x31, x7, x15); + REQUIRE(value == 0xE677AFAF); +} + +TEST_CASE("AMOMIN.D", "[rv64a]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.AMOMIN_D(Ordering::None, x31, x7, x15); + REQUIRE(value == 0x8077BFAF); + + as.RewindBuffer(); + + as.AMOMIN_D(Ordering::AQ, x31, x7, x15); + REQUIRE(value == 0x8477BFAF); + + as.RewindBuffer(); + + as.AMOMIN_D(Ordering::RL, x31, x7, x15); + REQUIRE(value == 0x8277BFAF); + + as.RewindBuffer(); + + as.AMOMIN_D(Ordering::AQRL, x31, x7, x15); + REQUIRE(value == 0x8677BFAF); +} + +TEST_CASE("AMOMIN.W", "[rv32a]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.AMOMIN_W(Ordering::None, x31, x7, x15); + REQUIRE(value == 0x8077AFAF); + + as.RewindBuffer(); + + as.AMOMIN_W(Ordering::AQ, x31, x7, x15); + REQUIRE(value == 0x8477AFAF); + + as.RewindBuffer(); + + as.AMOMIN_W(Ordering::RL, x31, x7, x15); + REQUIRE(value == 0x8277AFAF); + + as.RewindBuffer(); + + as.AMOMIN_W(Ordering::AQRL, x31, x7, x15); + REQUIRE(value == 0x8677AFAF); +} + +TEST_CASE("AMOMINU.D", "[rv64a]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.AMOMINU_D(Ordering::None, x31, x7, x15); + REQUIRE(value == 0xC077BFAF); + + as.RewindBuffer(); + + as.AMOMINU_D(Ordering::AQ, x31, x7, x15); + REQUIRE(value == 0xC477BFAF); + + as.RewindBuffer(); + + as.AMOMINU_D(Ordering::RL, x31, x7, x15); + REQUIRE(value == 0xC277BFAF); + + as.RewindBuffer(); + + as.AMOMINU_D(Ordering::AQRL, x31, x7, x15); + REQUIRE(value == 0xC677BFAF); +} + +TEST_CASE("AMOMINU.W", "[rv32a]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.AMOMINU_W(Ordering::None, x31, x7, x15); + REQUIRE(value == 0xC077AFAF); + + as.RewindBuffer(); + + as.AMOMINU_W(Ordering::AQ, x31, x7, x15); + REQUIRE(value == 0xC477AFAF); + + as.RewindBuffer(); + + as.AMOMINU_W(Ordering::RL, x31, x7, x15); + REQUIRE(value == 0xC277AFAF); + + as.RewindBuffer(); + + as.AMOMINU_W(Ordering::AQRL, x31, x7, x15); + REQUIRE(value == 0xC677AFAF); +} + +TEST_CASE("AMOOR.D", "[rv64a]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.AMOOR_D(Ordering::None, x31, x7, x15); + REQUIRE(value == 0x4077BFAF); + + as.RewindBuffer(); + + as.AMOOR_D(Ordering::AQ, x31, x7, x15); + REQUIRE(value == 0x4477BFAF); + + as.RewindBuffer(); + + as.AMOOR_D(Ordering::RL, x31, x7, x15); + REQUIRE(value == 0x4277BFAF); + + as.RewindBuffer(); + + as.AMOOR_D(Ordering::AQRL, x31, x7, x15); + REQUIRE(value == 0x4677BFAF); +} + +TEST_CASE("AMOOR.W", "[rv32a]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.AMOOR_W(Ordering::None, x31, x7, x15); + REQUIRE(value == 0x4077AFAF); + + as.RewindBuffer(); + + as.AMOOR_W(Ordering::AQ, x31, x7, x15); + REQUIRE(value == 0x4477AFAF); + + as.RewindBuffer(); + + as.AMOOR_W(Ordering::RL, x31, x7, x15); + REQUIRE(value == 0x4277AFAF); + + as.RewindBuffer(); + + as.AMOOR_W(Ordering::AQRL, x31, x7, x15); + REQUIRE(value == 0x4677AFAF); +} + +TEST_CASE("AMOSWAP.D", "[rv64a]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.AMOSWAP_D(Ordering::None, x31, x7, x15); + REQUIRE(value == 0x0877BFAF); + + as.RewindBuffer(); + + as.AMOSWAP_D(Ordering::AQ, x31, x7, x15); + REQUIRE(value == 0x0C77BFAF); + + as.RewindBuffer(); + + as.AMOSWAP_D(Ordering::RL, x31, x7, x15); + REQUIRE(value == 0x0A77BFAF); + + as.RewindBuffer(); + + as.AMOSWAP_D(Ordering::AQRL, x31, x7, x15); + REQUIRE(value == 0x0E77BFAF); +} + +TEST_CASE("AMOSWAP.W", "[rv32a]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.AMOSWAP_W(Ordering::None, x31, x7, x15); + REQUIRE(value == 0x0877AFAF); + + as.RewindBuffer(); + + as.AMOSWAP_W(Ordering::AQ, x31, x7, x15); + REQUIRE(value == 0x0C77AFAF); + + as.RewindBuffer(); + + as.AMOSWAP_W(Ordering::RL, x31, x7, x15); + REQUIRE(value == 0x0A77AFAF); + + as.RewindBuffer(); + + as.AMOSWAP_W(Ordering::AQRL, x31, x7, x15); + REQUIRE(value == 0x0E77AFAF); +} + +TEST_CASE("AMOXOR.D", "[rv64a]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.AMOXOR_D(Ordering::None, x31, x7, x15); + REQUIRE(value == 0x2077BFAF); + + as.RewindBuffer(); + + as.AMOXOR_D(Ordering::AQ, x31, x7, x15); + REQUIRE(value == 0x2477BFAF); + + as.RewindBuffer(); + + as.AMOXOR_D(Ordering::RL, x31, x7, x15); + REQUIRE(value == 0x2277BFAF); + + as.RewindBuffer(); + + as.AMOXOR_D(Ordering::AQRL, x31, x7, x15); + REQUIRE(value == 0x2677BFAF); +} + +TEST_CASE("AMOXOR.W", "[rv32a]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.AMOXOR_W(Ordering::None, x31, x7, x15); + REQUIRE(value == 0x2077AFAF); + + as.RewindBuffer(); + + as.AMOXOR_W(Ordering::AQ, x31, x7, x15); + REQUIRE(value == 0x2477AFAF); + + as.RewindBuffer(); + + as.AMOXOR_W(Ordering::RL, x31, x7, x15); + REQUIRE(value == 0x2277AFAF); + + as.RewindBuffer(); + + as.AMOXOR_W(Ordering::AQRL, x31, x7, x15); + REQUIRE(value == 0x2677AFAF); +} + +TEST_CASE("LR.D", "[rv64a]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.LR_D(Ordering::None, x31, x15); + REQUIRE(value == 0x1007BFAF); + + as.RewindBuffer(); + + as.LR_D(Ordering::AQ, x31, x15); + REQUIRE(value == 0x1407BFAF); + + as.RewindBuffer(); + + as.LR_D(Ordering::RL, x31, x15); + REQUIRE(value == 0x1207BFAF); + + as.RewindBuffer(); + + as.LR_D(Ordering::AQRL, x31, x15); + REQUIRE(value == 0x1607BFAF); +} + +TEST_CASE("LR.W", "[rv32a]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.LR_W(Ordering::None, x31, x15); + REQUIRE(value == 0x1007AFAF); + + as.RewindBuffer(); + + as.LR_W(Ordering::AQ, x31, x15); + REQUIRE(value == 0x1407AFAF); + + as.RewindBuffer(); + + as.LR_W(Ordering::RL, x31, x15); + REQUIRE(value == 0x1207AFAF); + + as.RewindBuffer(); + + as.LR_W(Ordering::AQRL, x31, x15); + REQUIRE(value == 0x1607AFAF); +} + +TEST_CASE("SC.D", "[rv64a]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.SC_D(Ordering::None, x31, x7, x15); + REQUIRE(value == 0x1877BFAF); + + as.RewindBuffer(); + + as.SC_D(Ordering::AQ, x31, x7, x15); + REQUIRE(value == 0x1C77BFAF); + + as.RewindBuffer(); + + as.SC_D(Ordering::RL, x31, x7, x15); + REQUIRE(value == 0x1A77BFAF); + + as.RewindBuffer(); + + as.SC_D(Ordering::AQRL, x31, x7, x15); + REQUIRE(value == 0x1E77BFAF); +} + +TEST_CASE("SC.W", "[rv32a]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.SC_W(Ordering::None, x31, x7, x15); + REQUIRE(value == 0x1877AFAF); + + as.RewindBuffer(); + + as.SC_W(Ordering::AQ, x31, x7, x15); + REQUIRE(value == 0x1C77AFAF); + + as.RewindBuffer(); + + as.SC_W(Ordering::RL, x31, x7, x15); + REQUIRE(value == 0x1A77AFAF); + + as.RewindBuffer(); + + as.SC_W(Ordering::AQRL, x31, x7, x15); + REQUIRE(value == 0x1E77AFAF); +} diff --git a/externals/biscuit/tests/src/assembler_rvb_tests.cpp b/externals/biscuit/tests/src/assembler_rvb_tests.cpp new file mode 100644 index 00000000..ba39f002 --- /dev/null +++ b/externals/biscuit/tests/src/assembler_rvb_tests.cpp @@ -0,0 +1,610 @@ +#include <catch/catch.hpp> + +#include <biscuit/assembler.hpp> + +#include "assembler_test_utils.hpp" + +using namespace biscuit; + +TEST_CASE("ADD.UW", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.ADDUW(x31, x7, x15); + REQUIRE(value == 0x08F38FBB); + + as.RewindBuffer(); + + // Pseudo instruction + + as.ZEXTW(x31, x7); + REQUIRE(value == 0x08038FBB); +} + +TEST_CASE("ANDN", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.ANDN(x31, x7, x15); + REQUIRE(value == 0x40F3FFB3); +} + +TEST_CASE("BCLR", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.BCLR(x31, x7, x15); + REQUIRE(value == 0x48F39FB3); +} + +TEST_CASE("BCLRI", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.BCLRI(x31, x7, 0); + REQUIRE(value == 0x48039F93); + + as.RewindBuffer(); + + as.BCLRI(x31, x7, 15); + REQUIRE(value == 0x48F39F93); + + as.RewindBuffer(); + + as.BCLRI(x31, x7, 31); + REQUIRE(value == 0x49F39F93); +} + +TEST_CASE("BCLRI (RV64)", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.BCLRI(x31, x7, 0); + REQUIRE(value == 0x48039F93); + + as.RewindBuffer(); + + as.BCLRI(x31, x7, 15); + REQUIRE(value == 0x48F39F93); + + as.RewindBuffer(); + + as.BCLRI(x31, x7, 31); + REQUIRE(value == 0x49F39F93); + + as.RewindBuffer(); + + as.BCLRI(x31, x7, 63); + REQUIRE(value == 0x4BF39F93); +} + +TEST_CASE("BEXT", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.BEXT(x31, x7, x15); + REQUIRE(value == 0x48F3DFB3); +} + +TEST_CASE("BEXTI", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.BEXTI(x31, x7, 0); + REQUIRE(value == 0x4803DF93); + + as.RewindBuffer(); + + as.BEXTI(x31, x7, 15); + REQUIRE(value == 0x48F3DF93); + + as.RewindBuffer(); + + as.BEXTI(x31, x7, 31); + REQUIRE(value == 0x49F3DF93); +} + +TEST_CASE("BEXTI (RV64)", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.BEXTI(x31, x7, 0); + REQUIRE(value == 0x4803DF93); + + as.RewindBuffer(); + + as.BEXTI(x31, x7, 15); + REQUIRE(value == 0x48F3DF93); + + as.RewindBuffer(); + + as.BEXTI(x31, x7, 31); + REQUIRE(value == 0x49F3DF93); + + as.RewindBuffer(); + + as.BEXTI(x31, x7, 63); + REQUIRE(value == 0x4BF3DF93); +} + +TEST_CASE("BINV", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.BINV(x31, x7, x15); + REQUIRE(value == 0x68F39FB3); +} + +TEST_CASE("BINVI", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.BINVI(x31, x7, 0); + REQUIRE(value == 0x68039F93); + + as.RewindBuffer(); + + as.BINVI(x31, x7, 15); + REQUIRE(value == 0x68F39F93); + + as.RewindBuffer(); + + as.BINVI(x31, x7, 31); + REQUIRE(value == 0x69F39F93); +} + +TEST_CASE("BINVI (RV64)", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.BINVI(x31, x7, 0); + REQUIRE(value == 0x68039F93); + + as.RewindBuffer(); + + as.BINVI(x31, x7, 15); + REQUIRE(value == 0x68F39F93); + + as.RewindBuffer(); + + as.BINVI(x31, x7, 31); + REQUIRE(value == 0x69F39F93); + + as.RewindBuffer(); + + as.BINVI(x31, x7, 63); + REQUIRE(value == 0x6BF39F93); +} + +TEST_CASE("BREV8", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.BREV8(x31, x31); + REQUIRE(value == 0x687FDF93); + + as.RewindBuffer(); + + as.BREV8(x1, x2); + REQUIRE(value == 0x68715093); +} + +TEST_CASE("BSET", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.BSET(x31, x7, x15); + REQUIRE(value == 0x28F39FB3); +} + +TEST_CASE("BSETI", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.BSETI(x31, x7, 0); + REQUIRE(value == 0x28039FB3); + + as.RewindBuffer(); + + as.BSETI(x31, x7, 15); + REQUIRE(value == 0x28F39FB3); + + as.RewindBuffer(); + + as.BSETI(x31, x7, 31); + REQUIRE(value == 0x29F39FB3); +} + +TEST_CASE("BSETI (RV64)", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.BSETI(x31, x7, 0); + REQUIRE(value == 0x28039FB3); + + as.RewindBuffer(); + + as.BSETI(x31, x7, 15); + REQUIRE(value == 0x28F39FB3); + + as.RewindBuffer(); + + as.BSETI(x31, x7, 31); + REQUIRE(value == 0x29F39FB3); + + as.RewindBuffer(); + + as.BSETI(x31, x7, 63); + REQUIRE(value == 0x2BF39FB3); +} + +TEST_CASE("CLMUL", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.CLMUL(x31, x7, x15); + REQUIRE(value == 0x0AF39FB3); +} + +TEST_CASE("CLMULH", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.CLMULH(x31, x7, x15); + REQUIRE(value == 0x0AF3BFB3); +} + +TEST_CASE("CLMULR", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.CLMULR(x31, x7, x15); + REQUIRE(value == 0x0AF3AFB3); +} + +TEST_CASE("CLZ", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.CLZ(x31, x7); + REQUIRE(value == 0x60039F93); +} + +TEST_CASE("CLZW", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.CLZW(x31, x7); + REQUIRE(value == 0x60039F9B); +} + +TEST_CASE("CPOP", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.CPOP(x31, x7); + REQUIRE(value == 0x60239F93); +} + +TEST_CASE("CPOPW", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.CPOPW(x31, x7); + REQUIRE(value == 0x60239F9B); +} + +TEST_CASE("CTZ", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.CTZ(x31, x7); + REQUIRE(value == 0x60139F93); +} + +TEST_CASE("CTZW", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.CTZW(x31, x7); + REQUIRE(value == 0x60139F9B); +} + +TEST_CASE("MAX", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.MAX(x31, x7, x15); + REQUIRE(value == 0x0AF3EFB3); +} + +TEST_CASE("MAXU", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.MAXU(x31, x7, x15); + REQUIRE(value == 0x0AF3FFB3); +} + +TEST_CASE("MIN", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.MIN(x31, x7, x15); + REQUIRE(value == 0x0AF3CFB3); +} + +TEST_CASE("MINU", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.MINU(x31, x7, x15); + REQUIRE(value == 0x0AF3DFB3); +} + +TEST_CASE("ORC.B", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.ORCB(x31, x7); + REQUIRE(value == 0x2873DF93); +} + +TEST_CASE("ORN", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.ORN(x31, x7, x15); + REQUIRE(value == 0x40F3EFB3); +} + +TEST_CASE("PACK", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.PACK(x31, x7, x2); + REQUIRE(value == 0x0823CFB3); +} + +TEST_CASE("PACKH", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.PACKH(x31, x7, x2); + REQUIRE(value == 0x0823FFB3); +} + +TEST_CASE("PACKW", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.PACKW(x31, x7, x2); + REQUIRE(value == 0x0823CFBB); +} + +TEST_CASE("REV8", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.REV8(x31, x7); + REQUIRE(value == 0x6983DF93); +} + +TEST_CASE("REV8 (RV64)", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.REV8(x31, x7); + REQUIRE(value == 0x6B83DF93); +} + +TEST_CASE("ROL", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.ROL(x31, x7, x15); + REQUIRE(value == 0x60F39FB3); +} + +TEST_CASE("ROLW", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.ROLW(x31, x7, x15); + REQUIRE(value == 0x60F39FBB); +} + +TEST_CASE("ROR", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.ROR(x31, x7, x15); + REQUIRE(value == 0x60F3DFB3); +} + +TEST_CASE("RORW", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.RORW(x31, x7, x15); + REQUIRE(value == 0x60F3DFBB); +} + +TEST_CASE("RORI", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.RORI(x31, x7, 0); + REQUIRE(value == 0x6003DF93); + + as.RewindBuffer(); + + as.RORI(x31, x7, 63); + REQUIRE(value == 0x63F3DF93); +} + +TEST_CASE("RORIW", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.RORIW(x31, x7, 0); + REQUIRE(value == 0x6003DF9B); + + as.RewindBuffer(); + + as.RORIW(x31, x7, 63); + REQUIRE(value == 0x63F3DF9B); +} + +TEST_CASE("SEXT.B", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.SEXTB(x31, x7); + REQUIRE(value == 0x60439F93); +} + +TEST_CASE("SEXT.H", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.SEXTH(x31, x7); + REQUIRE(value == 0x60539F93); +} + +TEST_CASE("SH1ADD", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.SH1ADD(x31, x7, x15); + REQUIRE(value == 0x20F3AFB3); +} + +TEST_CASE("SH1ADD.UW", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.SH1ADDUW(x31, x7, x15); + REQUIRE(value == 0x20F3AFBB); +} + +TEST_CASE("SH2ADD", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.SH2ADD(x31, x7, x15); + REQUIRE(value == 0x20F3CFB3); +} + +TEST_CASE("SH2ADD.UW", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.SH2ADDUW(x31, x7, x15); + REQUIRE(value == 0x20F3CFBB); +} + +TEST_CASE("SH3ADD", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.SH3ADD(x31, x7, x15); + REQUIRE(value == 0x20F3EFB3); +} + +TEST_CASE("SH3ADD.UW", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.SH3ADDUW(x31, x7, x15); + REQUIRE(value == 0x20F3EFBB); +} + +TEST_CASE("SLLI.UW", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.SLLIUW(x31, x7, 0); + REQUIRE(value == 0x08039F9B); + + as.RewindBuffer(); + + as.SLLIUW(x31, x7, 63); + REQUIRE(value == 0x0BF39F9B); +} + +TEST_CASE("UNZIP", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.UNZIP(x31, x31); + REQUIRE(value == 0x09FFDF93); + + as.RewindBuffer(); + + as.UNZIP(x1, x2); + REQUIRE(value == 0x09F15093); +} + +TEST_CASE("XNOR", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.XNOR(x31, x7, x15); + REQUIRE(value == 0x40F3CFB3); +} + +TEST_CASE("XPERM4", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.XPERM4(x31, x31, x31); + REQUIRE(value == 0x29FFAFB3); + + as.RewindBuffer(); + + as.XPERM4(x1, x2, x3); + REQUIRE(value == 0x283120B3); +} + +TEST_CASE("XPERM8", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.XPERM8(x31, x31, x31); + REQUIRE(value == 0x29FFCFB3); + + as.RewindBuffer(); + + as.XPERM8(x1, x2, x3); + REQUIRE(value == 0x283140B3); +} + +TEST_CASE("ZEXT.H", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.ZEXTH(x31, x7); + REQUIRE(value == 0x0803CFB3); +} + +TEST_CASE("ZEXT.H (RV64)", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.ZEXTH(x31, x7); + REQUIRE(value == 0x0803CFBB); +} + +TEST_CASE("ZIP", "[rvb]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.ZIP(x31, x31); + REQUIRE(value == 0x09EF9F93); + + as.RewindBuffer(); + + as.ZIP(x1, x2); + REQUIRE(value == 0x09E11093); +} diff --git a/externals/biscuit/tests/src/assembler_rvc_tests.cpp b/externals/biscuit/tests/src/assembler_rvc_tests.cpp new file mode 100644 index 00000000..da2b0c00 --- /dev/null +++ b/externals/biscuit/tests/src/assembler_rvc_tests.cpp @@ -0,0 +1,595 @@ +#include <catch/catch.hpp> + +#include <biscuit/assembler.hpp> + +#include "assembler_test_utils.hpp" + +using namespace biscuit; + +TEST_CASE("C.ADD", "[rvc]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.C_ADD(x31, x31); + REQUIRE(value == 0x9FFE); + + as.RewindBuffer(); + + as.C_ADD(x15, x8); + REQUIRE(value == 0x97A2); +} + +TEST_CASE("C.ADDI", "[rvc]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.C_ADDI(x15, -1); + REQUIRE(value == 0x17FD); + + as.RewindBuffer(); + + as.C_ADDI(x15, -32); + REQUIRE(value == 0x1781); + + as.RewindBuffer(); + + as.C_ADDI(x15, 31); + REQUIRE(value == 0x07FD); +} + +TEST_CASE("C.ADDIW", "[rvc]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.C_ADDIW(x15, -1); + REQUIRE(value == 0x37FD); + + as.RewindBuffer(); + + as.C_ADDIW(x15, -32); + REQUIRE(value == 0x3781); + + as.RewindBuffer(); + + as.C_ADDIW(x15, 31); + REQUIRE(value == 0x27FD); +} + +TEST_CASE("C.ADDI4SPN", "[rvc]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.C_ADDI4SPN(x15, 252); + REQUIRE(value == 0x19FC); + + as.RewindBuffer(); + + as.C_ADDI4SPN(x8, 1020); + REQUIRE(value == 0x1FE0); + + as.RewindBuffer(); + + as.C_ADDI4SPN(x15, 1020); + REQUIRE(value == 0x1FFC); +} + +TEST_CASE("C.ADDI16SP", "[rvc]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.C_ADDI16SP(16); + REQUIRE(value == 0x6141); + + as.RewindBuffer(); + + as.C_ADDI16SP(64); + REQUIRE(value == 0x6121); +} + +TEST_CASE("C.ADDW", "[rvc]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.C_ADDW(x15, x15); + REQUIRE(value == 0x9FBD); + + as.RewindBuffer(); + + as.C_ADDW(x15, x8); + REQUIRE(value == 0x9FA1); +} + +TEST_CASE("C.AND", "[rvc]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.C_AND(x15, x15); + REQUIRE(value == 0x8FFD); + + as.RewindBuffer(); + + as.C_AND(x15, x8); + REQUIRE(value == 0x8FE1); +} + +TEST_CASE("C.ANDI", "[rvc]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.C_ANDI(x15, 16); + REQUIRE(value == 0x8BC1); + + as.RewindBuffer(); + + as.C_ANDI(x15, 31); + REQUIRE(value == 0x8BFD); +} + +TEST_CASE("C.EBREAK", "[rvc]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.C_EBREAK(); + REQUIRE(value == 0x9002); +} + +TEST_CASE("C.FLD", "[rvc]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.C_FLD(f15, 8, x15); + REQUIRE(value == 0x279C); + + as.RewindBuffer(); + + as.C_FLD(f15, 24, x15); + REQUIRE(value == 0x2F9C); +} + +TEST_CASE("C.FLDSP", "[rvc]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.C_FLDSP(f15, 8); + REQUIRE(value == 0x27A2); + + as.RewindBuffer(); + + as.C_FLDSP(f15, 24); + REQUIRE(value == 0x27E2); +} + +TEST_CASE("C.FLW", "[rvc]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.C_FLW(f15, 16, x15); + REQUIRE(value == 0x6B9C); + + as.RewindBuffer(); + + as.C_FLW(f15, 24, x15); + REQUIRE(value == 0x6F9C); +} + +TEST_CASE("C.FLWSP", "[rvc]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.C_FLWSP(f15, 16); + REQUIRE(value == 0x67C2); + + as.RewindBuffer(); + + as.C_FLWSP(f15, 24); + REQUIRE(value == 0x67E2); +} + +TEST_CASE("C.FSD", "[rvc]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.C_FSD(f15, 8, x15); + REQUIRE(value == 0xA79C); + + as.RewindBuffer(); + + as.C_FSD(f15, 24, x15); + REQUIRE(value == 0xAF9C); +} + +TEST_CASE("C.FSDSP", "[rvc]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.C_FSDSP(f15, 8); + REQUIRE(value == 0xA43E); + + as.RewindBuffer(); + + as.C_FSDSP(f15, 24); + REQUIRE(value == 0xAC3E); +} + +TEST_CASE("C.FSW", "[rvc]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.C_FSW(f15, 16, x15); + REQUIRE(value == 0xEB9C); + + as.RewindBuffer(); + + as.C_FSW(f15, 24, x15); + REQUIRE(value == 0xEF9C); +} + +TEST_CASE("C.FSWSP", "[rvc]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.C_FSWSP(f15, 16); + REQUIRE(value == 0xE83E); + + as.RewindBuffer(); + + as.C_FSWSP(f15, 24); + REQUIRE(value == 0xEC3E); +} + +TEST_CASE("C.JALR", "[rvc]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.C_JALR(x31); + REQUIRE(value == 0x9F82); + + as.RewindBuffer(); + + as.C_JALR(x15); + REQUIRE(value == 0x9782); +} + +TEST_CASE("C.JR", "[rvc]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.C_JR(x31); + REQUIRE(value == 0x8F82); + + as.RewindBuffer(); + + as.C_JR(x15); + REQUIRE(value == 0x8782); +} + +TEST_CASE("C.LD", "[rvc]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.C_LD(x15, 8, x15); + REQUIRE(value == 0x679C); + + as.RewindBuffer(); + + as.C_LD(x15, 24, x15); + REQUIRE(value == 0x6F9C); +} + +TEST_CASE("C.LDSP", "[rvc]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.C_LDSP(x15, 8); + REQUIRE(value == 0x67A2); + + as.RewindBuffer(); + + as.C_LDSP(x15, 24); + REQUIRE(value == 0x67E2); +} + +TEST_CASE("C.LI", "[rvc]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.C_LI(x15, -1); + REQUIRE(value == 0x57FD); + + as.RewindBuffer(); + + as.C_LI(x15, -32); + REQUIRE(value == 0x5781); + + as.RewindBuffer(); + + as.C_LI(x15, 31); + REQUIRE(value == 0x47FD); +} + +TEST_CASE("C.LQ", "[rvc]") { + uint32_t value = 0; + auto as = MakeAssembler128(value); + + as.C_LQ(x15, 16, x15); + REQUIRE(value == 0x2B9C); + + as.RewindBuffer(); + + as.C_LQ(x15, 256, x15); + REQUIRE(value == 0x279C); +} + +TEST_CASE("C.LQSP", "[rvc]") { + uint32_t value = 0; + auto as = MakeAssembler128(value); + + as.C_LQSP(x15, 16); + REQUIRE(value == 0x27C2); + + as.RewindBuffer(); + + as.C_LQSP(x15, 256); + REQUIRE(value == 0x2792); +} + +TEST_CASE("C.LUI", "[rvc]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.C_LUI(x15, 0x3F000); + REQUIRE(value == 0x77FD); + + as.RewindBuffer(); + + as.C_LUI(x15, 0x0F000); + REQUIRE(value == 0x67BD); +} + +TEST_CASE("C.LW", "[rvc]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.C_LW(x15, 16, x15); + REQUIRE(value == 0x4B9C); + + as.RewindBuffer(); + + as.C_LW(x15, 24, x15); + REQUIRE(value == 0x4F9C); +} + +TEST_CASE("C.LWSP", "[rvc]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.C_LWSP(x15, 16); + REQUIRE(value == 0x47C2); + + as.RewindBuffer(); + + as.C_LWSP(x15, 24); + REQUIRE(value == 0x47E2); +} + +TEST_CASE("C.MV", "[rvc]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.C_MV(x31, x31); + REQUIRE(value == 0x8FFE); + + as.RewindBuffer(); + + as.C_MV(x15, x8); + REQUIRE(value == 0x87A2); +} + +TEST_CASE("C.NOP", "[rvc]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.C_NOP(); + REQUIRE(value == 0x0001); +} + +TEST_CASE("C.OR", "[rvc]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.C_OR(x15, x15); + REQUIRE(value == 0x8FDD); + + as.RewindBuffer(); + + as.C_OR(x15, x8); + REQUIRE(value == 0x8FC1); +} + +TEST_CASE("C.SD", "[rvc]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.C_SD(x15, 8, x15); + REQUIRE(value == 0xE79C); + + as.RewindBuffer(); + + as.C_SD(x15, 24, x15); + REQUIRE(value == 0xEF9C); +} + +TEST_CASE("C.SDSP", "[rvc]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.C_SDSP(x15, 8); + REQUIRE(value == 0xE43E); + + as.RewindBuffer(); + + as.C_SDSP(x15, 24); + REQUIRE(value == 0xEC3E); +} + +TEST_CASE("C.SLLI", "[rvc]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.C_SLLI(x15, 15); + REQUIRE(value == 0x07BE); + + as.RewindBuffer(); + + as.C_SLLI(x15, 31); + REQUIRE(value == 0x07FE); +} + +TEST_CASE("C.SLLI (RV128)", "[rv128c]") { + uint32_t value = 0; + auto as = MakeAssembler128(value); + + as.C_SLLI(x15, 64); + REQUIRE(value == 0x0782); +} + +TEST_CASE("C.SQ", "[rvc]") { + uint32_t value = 0; + auto as = MakeAssembler128(value); + + as.C_SQ(x15, 16, x15); + REQUIRE(value == 0xAB9C); + + as.RewindBuffer(); + + as.C_SQ(x15, 256, x15); + REQUIRE(value == 0xA79C); +} + +TEST_CASE("C.SQSP", "[rvc]") { + uint32_t value = 0; + auto as = MakeAssembler128(value); + + as.C_SQSP(x15, 16); + REQUIRE(value == 0xA83E); + + as.RewindBuffer(); + + as.C_SQSP(x15, 256); + REQUIRE(value == 0xA23E); +} + +TEST_CASE("C.SRAI", "[rvc]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.C_SRAI(x15, 16); + REQUIRE(value == 0x87C1); + + as.RewindBuffer(); + + as.C_SRAI(x15, 31); + REQUIRE(value == 0x87FD); +} + +TEST_CASE("C.SRAI (RV128)", "[rv128c]") { + uint32_t value = 0; + auto as = MakeAssembler128(value); + + as.C_SRAI(x15, 64); + REQUIRE(value == 0x8781); +} + +TEST_CASE("C.SRLI", "[rvc]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.C_SRLI(x15, 16); + REQUIRE(value == 0x83C1); + + as.RewindBuffer(); + + as.C_SRLI(x15, 31); + REQUIRE(value == 0x83FD); +} + +TEST_CASE("C.SRLI (RV128)", "[rv128c]") { + uint32_t value = 0; + auto as = MakeAssembler128(value); + + as.C_SRLI(x15, 64); + REQUIRE(value == 0x8381); +} + +TEST_CASE("C.SUB", "[rvc]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.C_SUB(x15, x15); + REQUIRE(value == 0x8F9D); + + as.RewindBuffer(); + + as.C_SUB(x15, x8); + REQUIRE(value == 0x8F81); +} + +TEST_CASE("C.SUBW", "[rvc]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.C_SUBW(x15, x15); + REQUIRE(value == 0x9F9D); + + as.RewindBuffer(); + + as.C_SUBW(x15, x8); + REQUIRE(value == 0x9F81); +} + +TEST_CASE("C.SW", "[rvc]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.C_SW(x15, 16, x15); + REQUIRE(value == 0xCB9C); + + as.RewindBuffer(); + + as.C_SW(x15, 24, x15); + REQUIRE(value == 0xCF9C); +} + +TEST_CASE("C.SWSP", "[rvc]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.C_SWSP(x15, 16); + REQUIRE(value == 0xC83E); + + as.RewindBuffer(); + + as.C_SWSP(x15, 24); + REQUIRE(value == 0xCC3E); +} + +TEST_CASE("C.UNDEF", "[rvc]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.C_UNDEF(); + REQUIRE(value == 0); +} + +TEST_CASE("C.XOR", "[rvc]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.C_XOR(x15, x15); + REQUIRE(value == 0x8FBD); + + as.RewindBuffer(); + + as.C_XOR(x15, x8); + REQUIRE(value == 0x8FA1); +} diff --git a/externals/biscuit/tests/src/assembler_rvd_tests.cpp b/externals/biscuit/tests/src/assembler_rvd_tests.cpp new file mode 100644 index 00000000..789df61d --- /dev/null +++ b/externals/biscuit/tests/src/assembler_rvd_tests.cpp @@ -0,0 +1,528 @@ +#include <catch/catch.hpp> + +#include <biscuit/assembler.hpp> + +#include "assembler_test_utils.hpp" + +using namespace biscuit; + +TEST_CASE("FADD.D", "[rv32d]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FADD_D(f31, f7, f26, RMode::RNE); + REQUIRE(value == 0x03A38FD3); + + as.RewindBuffer(); + + as.FADD_D(f31, f7, f26, RMode::RMM); + REQUIRE(value == 0x03A3CFD3); + + as.RewindBuffer(); + + as.FADD_D(f31, f7, f26, RMode::DYN); + REQUIRE(value == 0x03A3FFD3); +} + +TEST_CASE("FCLASS.D", "[rv32d]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FCLASS_D(x31, f7); + REQUIRE(value == 0xE2039FD3); + + as.RewindBuffer(); + + as.FCLASS_D(x7, f31); + REQUIRE(value == 0xE20F93D3); +} + +TEST_CASE("FCVT.D.S", "[rv32d]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FCVT_D_S(f31, f7, RMode::RNE); + REQUIRE(value == 0x42038FD3); + + as.RewindBuffer(); + + as.FCVT_D_S(f31, f7, RMode::RMM); + REQUIRE(value == 0x4203CFD3); + + as.RewindBuffer(); + + as.FCVT_D_S(f31, f7, RMode::DYN); + REQUIRE(value == 0x4203FFD3); +} + +TEST_CASE("FCVT.D.W", "[rv32d]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FCVT_D_W(f31, x7, RMode::RNE); + REQUIRE(value == 0xD2038FD3); + + as.RewindBuffer(); + + as.FCVT_D_W(f31, x7, RMode::RMM); + REQUIRE(value == 0xD203CFD3); + + as.RewindBuffer(); + + as.FCVT_D_W(f31, x7, RMode::DYN); + REQUIRE(value == 0xD203FFD3); +} + +TEST_CASE("FCVT.D.WU", "[rv32d]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FCVT_D_WU(f31, x7, RMode::RNE); + REQUIRE(value == 0xD2138FD3); + + as.RewindBuffer(); + + as.FCVT_D_WU(f31, x7, RMode::RMM); + REQUIRE(value == 0xD213CFD3); + + as.RewindBuffer(); + + as.FCVT_D_WU(f31, x7, RMode::DYN); + REQUIRE(value == 0xD213FFD3); +} + +TEST_CASE("FCVT.L.D", "[rv64d]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.FCVT_L_D(x31, f7, RMode::RNE); + REQUIRE(value == 0xC2238FD3); + + as.RewindBuffer(); + + as.FCVT_L_D(x31, f7, RMode::RMM); + REQUIRE(value == 0xC223CFD3); + + as.RewindBuffer(); + + as.FCVT_L_D(x31, f7, RMode::DYN); + REQUIRE(value == 0xC223FFD3); +} + +TEST_CASE("FCVT.LU.D", "[rv64d]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.FCVT_LU_D(x31, f7, RMode::RNE); + REQUIRE(value == 0xC2338FD3); + + as.RewindBuffer(); + + as.FCVT_LU_D(x31, f7, RMode::RMM); + REQUIRE(value == 0xC233CFD3); + + as.RewindBuffer(); + + as.FCVT_LU_D(x31, f7, RMode::DYN); + REQUIRE(value == 0xC233FFD3); +} + +TEST_CASE("FCVT.D.L", "[rv64d]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.FCVT_D_L(f31, x7, RMode::RNE); + REQUIRE(value == 0xD2238FD3); + + as.RewindBuffer(); + + as.FCVT_D_L(f31, x7, RMode::RMM); + REQUIRE(value == 0xD223CFD3); + + as.RewindBuffer(); + + as.FCVT_D_L(f31, x7, RMode::DYN); + REQUIRE(value == 0xD223FFD3); +} + +TEST_CASE("FCVT.D.LU", "[rv64d]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.FCVT_D_LU(f31, x7, RMode::RNE); + REQUIRE(value == 0xD2338FD3); + + as.RewindBuffer(); + + as.FCVT_D_LU(f31, x7, RMode::RMM); + REQUIRE(value == 0xD233CFD3); + + as.RewindBuffer(); + + as.FCVT_D_LU(f31, x7, RMode::DYN); + REQUIRE(value == 0xD233FFD3); +} + +TEST_CASE("FCVT.W.D", "[rv32d]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FCVT_W_D(x31, f7, RMode::RNE); + REQUIRE(value == 0xC2038FD3); + + as.RewindBuffer(); + + as.FCVT_W_D(x31, f7, RMode::RMM); + REQUIRE(value == 0xC203CFD3); + + as.RewindBuffer(); + + as.FCVT_W_D(x31, f7, RMode::DYN); + REQUIRE(value == 0xC203FFD3); +} + +TEST_CASE("FCVT.WU.D", "[rv32d]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FCVT_WU_D(x31, f7, RMode::RNE); + REQUIRE(value == 0xC2138FD3); + + as.RewindBuffer(); + + as.FCVT_WU_D(x31, f7, RMode::RMM); + REQUIRE(value == 0xC213CFD3); + + as.RewindBuffer(); + + as.FCVT_WU_D(x31, f7, RMode::DYN); + REQUIRE(value == 0xC213FFD3); +} + +TEST_CASE("FCVT.S.D", "[rv32d]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FCVT_S_D(f31, f7, RMode::RNE); + REQUIRE(value == 0x40138FD3); + + as.RewindBuffer(); + + as.FCVT_S_D(f31, f7, RMode::RMM); + REQUIRE(value == 0x4013CFD3); + + as.RewindBuffer(); + + as.FCVT_S_D(f31, f7, RMode::DYN); + REQUIRE(value == 0x4013FFD3); +} + +TEST_CASE("FDIV.D", "[rv32d]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FDIV_D(f31, f7, f26, RMode::RNE); + REQUIRE(value == 0x1BA38FD3); + + as.RewindBuffer(); + + as.FDIV_D(f31, f7, f26, RMode::RMM); + REQUIRE(value == 0x1BA3CFD3); + + as.RewindBuffer(); + + as.FDIV_D(f31, f7, f26, RMode::DYN); + REQUIRE(value == 0x1BA3FFD3); +} + +TEST_CASE("FEQ.D", "[rv32d]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FEQ_D(x31, f7, f26); + REQUIRE(value == 0xA3A3AFD3); + + as.RewindBuffer(); + + as.FEQ_D(x31, f26, f7); + REQUIRE(value == 0xA27D2FD3); +} + +TEST_CASE("FLE.D", "[rv32d]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FLE_D(x31, f7, f26); + REQUIRE(value == 0xA3A38FD3); + + as.RewindBuffer(); + + as.FLE_D(x31, f26, f7); + REQUIRE(value == 0xA27D0FD3); +} + +TEST_CASE("FLT.D", "[rv32d]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FLT_D(x31, f7, f26); + REQUIRE(value == 0xA3A39FD3); + + as.RewindBuffer(); + + as.FLT_D(x31, f26, f7); + REQUIRE(value == 0xA27D1FD3); +} + +TEST_CASE("FLD", "[rv32d]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FLD(f15, 1024, x31); + REQUIRE(value == 0x400FB787); + + as.RewindBuffer(); + + as.FLD(f15, 1536, x31); + REQUIRE(value == 0x600FB787); + + as.RewindBuffer(); + + as.FLD(f15, -1, x31); + REQUIRE(value == 0xFFFFB787); +} + +TEST_CASE("FMADD.D", "[rv32d]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FMADD_D(f15, f31, f7, f26, RMode::RNE); + REQUIRE(value == 0xD27F87C3); + + as.RewindBuffer(); + + as.FMADD_D(f15, f31, f7, f26, RMode::RMM); + REQUIRE(value == 0xD27FC7C3); + + as.RewindBuffer(); + + as.FMADD_D(f15, f31, f7, f26, RMode::DYN); + REQUIRE(value == 0xD27FF7C3); +} + +TEST_CASE("FMAX.D", "[rv32d]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FMAX_D(f31, f7, f26); + REQUIRE(value == 0x2BA39FD3); + + as.RewindBuffer(); + + as.FMAX_D(f31, f31, f31); + REQUIRE(value == 0x2BFF9FD3); +} + +TEST_CASE("FMIN.D", "[rv32d]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FMIN_D(f31, f7, f26); + REQUIRE(value == 0x2BA38FD3); + + as.RewindBuffer(); + + as.FMIN_D(f31, f31, f31); + REQUIRE(value == 0x2BFF8FD3); +} + +TEST_CASE("FMSUB.D", "[rv32d]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FMSUB_D(f15, f31, f7, f26, RMode::RNE); + REQUIRE(value == 0xD27F87C7); + + as.RewindBuffer(); + + as.FMSUB_D(f15, f31, f7, f26, RMode::RMM); + REQUIRE(value == 0xD27FC7C7); + + as.RewindBuffer(); + + as.FMSUB_D(f15, f31, f7, f26, RMode::DYN); + REQUIRE(value == 0xD27FF7C7); +} + +TEST_CASE("FMUL.D", "[rv32d]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FMUL_D(f31, f7, f26, RMode::RNE); + REQUIRE(value == 0x13A38FD3); + + as.RewindBuffer(); + + as.FMUL_D(f31, f7, f26, RMode::RMM); + REQUIRE(value == 0x13A3CFD3); + + as.RewindBuffer(); + + as.FMUL_D(f31, f7, f26, RMode::DYN); + REQUIRE(value == 0x13A3FFD3); +} + +TEST_CASE("FMV.D.X", "[rv64d]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.FMV_D_X(f31, x7); + REQUIRE(value == 0xF2038FD3); + + as.RewindBuffer(); + + as.FMV_D_X(f7, x31); + REQUIRE(value == 0xF20F83D3); +} + +TEST_CASE("FMV.X.D", "[rv64d]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.FMV_X_D(x31, f7); + REQUIRE(value == 0xE2038FD3); + + as.RewindBuffer(); + + as.FMV_X_D(x7, f31); + REQUIRE(value == 0xE20F83D3); +} + +TEST_CASE("FNMADD.D", "[rv32d]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FNMADD_D(f15, f31, f7, f26, RMode::RNE); + REQUIRE(value == 0xD27F87CF); + + as.RewindBuffer(); + + as.FNMADD_D(f15, f31, f7, f26, RMode::RMM); + REQUIRE(value == 0xD27FC7CF); + + as.RewindBuffer(); + + as.FNMADD_D(f15, f31, f7, f26, RMode::DYN); + REQUIRE(value == 0xD27FF7CF); +} + +TEST_CASE("FNMSUB.D", "[rv32d]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FNMSUB_D(f15, f31, f7, f26, RMode::RNE); + REQUIRE(value == 0xD27F87CB); + + as.RewindBuffer(); + + as.FNMSUB_D(f15, f31, f7, f26, RMode::RMM); + REQUIRE(value == 0xD27FC7CB); + + as.RewindBuffer(); + + as.FNMSUB_D(f15, f31, f7, f26, RMode::DYN); + REQUIRE(value == 0xD27FF7CB); +} + +TEST_CASE("FSGNJ.D", "[rv32d]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FSGNJ_D(f31, f7, f26); + REQUIRE(value == 0x23A38FD3); + + as.RewindBuffer(); + + as.FSGNJ_D(f31, f31, f31); + REQUIRE(value == 0x23FF8FD3); +} + +TEST_CASE("FSGNJN.D", "[rv32d]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FSGNJN_D(f31, f7, f26); + REQUIRE(value == 0x23A39FD3); + + as.RewindBuffer(); + + as.FSGNJN_D(f31, f31, f31); + REQUIRE(value == 0x23FF9FD3); +} + +TEST_CASE("FSGNJX.D", "[rv32d]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FSGNJX_D(f31, f7, f26); + REQUIRE(value == 0x23A3AFD3); + + as.RewindBuffer(); + + as.FSGNJX_D(f31, f31, f31); + REQUIRE(value == 0x23FFAFD3); +} + +TEST_CASE("FSQRT.D", "[rv32d]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FSQRT_D(f31, f7, RMode::RNE); + REQUIRE(value == 0x5A038FD3); + + as.RewindBuffer(); + + as.FSQRT_D(f31, f7, RMode::RMM); + REQUIRE(value == 0x5A03CFD3); + + as.RewindBuffer(); + + as.FSQRT_D(f31, f7, RMode::DYN); + REQUIRE(value == 0x5A03FFD3); +} + +TEST_CASE("FSUB.D", "[rv32d]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FSUB_D(f31, f7, f26, RMode::RNE); + REQUIRE(value == 0x0BA38FD3); + + as.RewindBuffer(); + + as.FSUB_D(f31, f7, f26, RMode::RMM); + REQUIRE(value == 0x0BA3CFD3); + + as.RewindBuffer(); + + as.FSUB_D(f31, f7, f26, RMode::DYN); + REQUIRE(value == 0x0BA3FFD3); +} + +TEST_CASE("FSD", "[rv32d]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FSD(f31, 1024, x15); + REQUIRE(value == 0x41F7B027); + + as.RewindBuffer(); + + as.FSD(f31, 1536, x15); + REQUIRE(value == 0x61F7B027); + + as.RewindBuffer(); + + as.FSD(f31, -1, x15); + REQUIRE(value == 0xFFF7BFA7); +} diff --git a/externals/biscuit/tests/src/assembler_rvf_tests.cpp b/externals/biscuit/tests/src/assembler_rvf_tests.cpp new file mode 100644 index 00000000..1fe177a8 --- /dev/null +++ b/externals/biscuit/tests/src/assembler_rvf_tests.cpp @@ -0,0 +1,1085 @@ +#include <catch/catch.hpp> + +#include <biscuit/assembler.hpp> + +#include "assembler_test_utils.hpp" + +using namespace biscuit; + +TEST_CASE("FADD.H", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FADD_H(f31, f7, f26, RMode::RNE); + REQUIRE(value == 0x05A38FD3); + + as.RewindBuffer(); + + as.FADD_H(f31, f7, f26, RMode::RMM); + REQUIRE(value == 0x05A3CFD3); + + as.RewindBuffer(); + + as.FADD_H(f31, f7, f26, RMode::DYN); + REQUIRE(value == 0x05A3FFD3); +} + +TEST_CASE("FADD.S", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FADD_S(f31, f7, f26, RMode::RNE); + REQUIRE(value == 0x01A38FD3); + + as.RewindBuffer(); + + as.FADD_S(f31, f7, f26, RMode::RMM); + REQUIRE(value == 0x01A3CFD3); + + as.RewindBuffer(); + + as.FADD_S(f31, f7, f26, RMode::DYN); + REQUIRE(value == 0x01A3FFD3); +} + +TEST_CASE("FCLASS.H", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FCLASS_H(x31, f7); + REQUIRE(value == 0xE4039FD3); + + as.RewindBuffer(); + + as.FCLASS_H(x7, f31); + REQUIRE(value == 0xE40F93D3); +} + +TEST_CASE("FCLASS.S", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FCLASS_S(x31, f7); + REQUIRE(value == 0xE0039FD3); + + as.RewindBuffer(); + + as.FCLASS_S(x7, f31); + REQUIRE(value == 0xE00F93D3); +} + +TEST_CASE("FCVT.D.H", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FCVT_D_H(f31, f7, RMode::RNE); + REQUIRE(value == 0x42238FD3); + + as.RewindBuffer(); + + as.FCVT_D_H(f31, f7, RMode::RMM); + REQUIRE(value == 0x4223CFD3); + + as.RewindBuffer(); + + as.FCVT_D_H(f31, f7, RMode::DYN); + REQUIRE(value == 0x4223FFD3); +} + +TEST_CASE("FCVT.H.D", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FCVT_H_D(f31, f7, RMode::RNE); + REQUIRE(value == 0x44138FD3); + + as.RewindBuffer(); + + as.FCVT_H_D(f31, f7, RMode::RMM); + REQUIRE(value == 0x4413CFD3); + + as.RewindBuffer(); + + as.FCVT_H_D(f31, f7, RMode::DYN); + REQUIRE(value == 0x4413FFD3); +} + +TEST_CASE("FCVT.H.Q", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FCVT_H_Q(f31, f7, RMode::RNE); + REQUIRE(value == 0x44338FD3); + + as.RewindBuffer(); + + as.FCVT_H_Q(f31, f7, RMode::RMM); + REQUIRE(value == 0x4433CFD3); + + as.RewindBuffer(); + + as.FCVT_H_Q(f31, f7, RMode::DYN); + REQUIRE(value == 0x4433FFD3); +} + +TEST_CASE("FCVT.H.S", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FCVT_H_S(f31, f7, RMode::RNE); + REQUIRE(value == 0x44038FD3); + + as.RewindBuffer(); + + as.FCVT_H_S(f31, f7, RMode::RMM); + REQUIRE(value == 0x4403CFD3); + + as.RewindBuffer(); + + as.FCVT_H_S(f31, f7, RMode::DYN); + REQUIRE(value == 0x4403FFD3); +} + +TEST_CASE("FCVT.Q.H", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FCVT_Q_H(f31, f7, RMode::RNE); + REQUIRE(value == 0x46238FD3); + + as.RewindBuffer(); + + as.FCVT_Q_H(f31, f7, RMode::RMM); + REQUIRE(value == 0x4623CFD3); + + as.RewindBuffer(); + + as.FCVT_Q_H(f31, f7, RMode::DYN); + REQUIRE(value == 0x4623FFD3); +} + +TEST_CASE("FCVT.S.H", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FCVT_S_H(f31, f7, RMode::RNE); + REQUIRE(value == 0x40238FD3); + + as.RewindBuffer(); + + as.FCVT_S_H(f31, f7, RMode::RMM); + REQUIRE(value == 0x4023CFD3); + + as.RewindBuffer(); + + as.FCVT_S_H(f31, f7, RMode::DYN); + REQUIRE(value == 0x4023FFD3); +} + +TEST_CASE("FCVT.H.W", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FCVT_H_W(f31, x7, RMode::RNE); + REQUIRE(value == 0xD4038FD3); + + as.RewindBuffer(); + + as.FCVT_H_W(f31, x7, RMode::RMM); + REQUIRE(value == 0xD403CFD3); + + as.RewindBuffer(); + + as.FCVT_H_W(f31, x7, RMode::DYN); + REQUIRE(value == 0xD403FFD3); +} + +TEST_CASE("FCVT.S.W", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FCVT_S_W(f31, x7, RMode::RNE); + REQUIRE(value == 0xD0038FD3); + + as.RewindBuffer(); + + as.FCVT_S_W(f31, x7, RMode::RMM); + REQUIRE(value == 0xD003CFD3); + + as.RewindBuffer(); + + as.FCVT_S_W(f31, x7, RMode::DYN); + REQUIRE(value == 0xD003FFD3); +} + +TEST_CASE("FCVT.H.WU", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FCVT_H_WU(f31, x7, RMode::RNE); + REQUIRE(value == 0xD4138FD3); + + as.RewindBuffer(); + + as.FCVT_H_WU(f31, x7, RMode::RMM); + REQUIRE(value == 0xD413CFD3); + + as.RewindBuffer(); + + as.FCVT_H_WU(f31, x7, RMode::DYN); + REQUIRE(value == 0xD413FFD3); +} + +TEST_CASE("FCVT.S.WU", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FCVT_S_WU(f31, x7, RMode::RNE); + REQUIRE(value == 0xD0138FD3); + + as.RewindBuffer(); + + as.FCVT_S_WU(f31, x7, RMode::RMM); + REQUIRE(value == 0xD013CFD3); + + as.RewindBuffer(); + + as.FCVT_S_WU(f31, x7, RMode::DYN); + REQUIRE(value == 0xD013FFD3); +} + +TEST_CASE("FCVT.L.H", "[rv64f]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.FCVT_L_H(x31, f7, RMode::RNE); + REQUIRE(value == 0xC4238FD3); + + as.RewindBuffer(); + + as.FCVT_L_H(x31, f7, RMode::RMM); + REQUIRE(value == 0xC423CFD3); + + as.RewindBuffer(); + + as.FCVT_L_H(x31, f7, RMode::DYN); + REQUIRE(value == 0xC423FFD3); +} + +TEST_CASE("FCVT.L.S", "[rv64f]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.FCVT_L_S(x31, f7, RMode::RNE); + REQUIRE(value == 0xC0238FD3); + + as.RewindBuffer(); + + as.FCVT_L_S(x31, f7, RMode::RMM); + REQUIRE(value == 0xC023CFD3); + + as.RewindBuffer(); + + as.FCVT_L_S(x31, f7, RMode::DYN); + REQUIRE(value == 0xC023FFD3); +} + +TEST_CASE("FCVT.LU.H", "[rv64f]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.FCVT_LU_H(x31, f7, RMode::RNE); + REQUIRE(value == 0xC4338FD3); + + as.RewindBuffer(); + + as.FCVT_LU_H(x31, f7, RMode::RMM); + REQUIRE(value == 0xC433CFD3); + + as.RewindBuffer(); + + as.FCVT_LU_H(x31, f7, RMode::DYN); + REQUIRE(value == 0xC433FFD3); +} + +TEST_CASE("FCVT.LU.S", "[rv64f]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.FCVT_LU_S(x31, f7, RMode::RNE); + REQUIRE(value == 0xC0338FD3); + + as.RewindBuffer(); + + as.FCVT_LU_S(x31, f7, RMode::RMM); + REQUIRE(value == 0xC033CFD3); + + as.RewindBuffer(); + + as.FCVT_LU_S(x31, f7, RMode::DYN); + REQUIRE(value == 0xC033FFD3); +} + +TEST_CASE("FCVT.H.L", "[rv64f]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.FCVT_H_L(f31, x7, RMode::RNE); + REQUIRE(value == 0xD4238FD3); + + as.RewindBuffer(); + + as.FCVT_H_L(f31, x7, RMode::RMM); + REQUIRE(value == 0xD423CFD3); + + as.RewindBuffer(); + + as.FCVT_H_L(f31, x7, RMode::DYN); + REQUIRE(value == 0xD423FFD3); +} + +TEST_CASE("FCVT.S.L", "[rv64f]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.FCVT_S_L(f31, x7, RMode::RNE); + REQUIRE(value == 0xD0238FD3); + + as.RewindBuffer(); + + as.FCVT_S_L(f31, x7, RMode::RMM); + REQUIRE(value == 0xD023CFD3); + + as.RewindBuffer(); + + as.FCVT_S_L(f31, x7, RMode::DYN); + REQUIRE(value == 0xD023FFD3); +} + +TEST_CASE("FCVT.H.LU", "[rv64f]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.FCVT_H_LU(f31, x7, RMode::RNE); + REQUIRE(value == 0xD4338FD3); + + as.RewindBuffer(); + + as.FCVT_H_LU(f31, x7, RMode::RMM); + REQUIRE(value == 0xD433CFD3); + + as.RewindBuffer(); + + as.FCVT_H_LU(f31, x7, RMode::DYN); + REQUIRE(value == 0xD433FFD3); +} + +TEST_CASE("FCVT.S.LU", "[rv64f]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.FCVT_S_LU(f31, x7, RMode::RNE); + REQUIRE(value == 0xD0338FD3); + + as.RewindBuffer(); + + as.FCVT_S_LU(f31, x7, RMode::RMM); + REQUIRE(value == 0xD033CFD3); + + as.RewindBuffer(); + + as.FCVT_S_LU(f31, x7, RMode::DYN); + REQUIRE(value == 0xD033FFD3); +} + +TEST_CASE("FCVT.W.H", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FCVT_W_H(x31, f7, RMode::RNE); + REQUIRE(value == 0xC4038FD3); + + as.RewindBuffer(); + + as.FCVT_W_H(x31, f7, RMode::RMM); + REQUIRE(value == 0xC403CFD3); + + as.RewindBuffer(); + + as.FCVT_W_H(x31, f7, RMode::DYN); + REQUIRE(value == 0xC403FFD3); +} + +TEST_CASE("FCVT.W.S", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FCVT_W_S(x31, f7, RMode::RNE); + REQUIRE(value == 0xC0038FD3); + + as.RewindBuffer(); + + as.FCVT_W_S(x31, f7, RMode::RMM); + REQUIRE(value == 0xC003CFD3); + + as.RewindBuffer(); + + as.FCVT_W_S(x31, f7, RMode::DYN); + REQUIRE(value == 0xC003FFD3); +} + +TEST_CASE("FCVT.WU.H", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FCVT_WU_H(x31, f7, RMode::RNE); + REQUIRE(value == 0xC4138FD3); + + as.RewindBuffer(); + + as.FCVT_WU_H(x31, f7, RMode::RMM); + REQUIRE(value == 0xC413CFD3); + + as.RewindBuffer(); + + as.FCVT_WU_H(x31, f7, RMode::DYN); + REQUIRE(value == 0xC413FFD3); +} + +TEST_CASE("FCVT.WU.S", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FCVT_WU_S(x31, f7, RMode::RNE); + REQUIRE(value == 0xC0138FD3); + + as.RewindBuffer(); + + as.FCVT_WU_S(x31, f7, RMode::RMM); + REQUIRE(value == 0xC013CFD3); + + as.RewindBuffer(); + + as.FCVT_WU_S(x31, f7, RMode::DYN); + REQUIRE(value == 0xC013FFD3); +} + +TEST_CASE("FDIV.H", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FDIV_H(f31, f7, f26, RMode::RNE); + REQUIRE(value == 0x1DA38FD3); + + as.RewindBuffer(); + + as.FDIV_H(f31, f7, f26, RMode::RMM); + REQUIRE(value == 0x1DA3CFD3); + + as.RewindBuffer(); + + as.FDIV_H(f31, f7, f26, RMode::DYN); + REQUIRE(value == 0x1DA3FFD3); +} + +TEST_CASE("FDIV.S", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FDIV_S(f31, f7, f26, RMode::RNE); + REQUIRE(value == 0x19A38FD3); + + as.RewindBuffer(); + + as.FDIV_S(f31, f7, f26, RMode::RMM); + REQUIRE(value == 0x19A3CFD3); + + as.RewindBuffer(); + + as.FDIV_S(f31, f7, f26, RMode::DYN); + REQUIRE(value == 0x19A3FFD3); +} + +TEST_CASE("FEQ.H", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FEQ_H(x31, f7, f26); + REQUIRE(value == 0xA5A3AFD3); + + as.RewindBuffer(); + + as.FEQ_H(x31, f26, f7); + REQUIRE(value == 0xA47D2FD3); +} + +TEST_CASE("FEQ.S", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FEQ_S(x31, f7, f26); + REQUIRE(value == 0xA1A3AFD3); + + as.RewindBuffer(); + + as.FEQ_S(x31, f26, f7); + REQUIRE(value == 0xA07D2FD3); +} + +TEST_CASE("FLE.H", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FLE_H(x31, f7, f26); + REQUIRE(value == 0xA5A38FD3); + + as.RewindBuffer(); + + as.FLE_H(x31, f26, f7); + REQUIRE(value == 0xA47D0FD3); +} + +TEST_CASE("FLE.S", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FLE_S(x31, f7, f26); + REQUIRE(value == 0xA1A38FD3); + + as.RewindBuffer(); + + as.FLE_S(x31, f26, f7); + REQUIRE(value == 0xA07D0FD3); +} + +TEST_CASE("FLH", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FLH(f15, 1024, x31); + REQUIRE(value == 0x400F9787); + + as.RewindBuffer(); + + as.FLH(f15, 1536, x31); + REQUIRE(value == 0x600F9787); + + as.RewindBuffer(); + + as.FLH(f15, -1, x31); + REQUIRE(value == 0xFFFF9787); +} + +TEST_CASE("FLT.H", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FLT_H(x31, f7, f26); + REQUIRE(value == 0xA5A39FD3); + + as.RewindBuffer(); + + as.FLT_H(x31, f26, f7); + REQUIRE(value == 0xA47D1FD3); +} + +TEST_CASE("FLT.S", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FLT_S(x31, f7, f26); + REQUIRE(value == 0xA1A39FD3); + + as.RewindBuffer(); + + as.FLT_S(x31, f26, f7); + REQUIRE(value == 0xA07D1FD3); +} + +TEST_CASE("FLW", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FLW(f15, 1024, x31); + REQUIRE(value == 0x400FA787); + + as.RewindBuffer(); + + as.FLW(f15, 1536, x31); + REQUIRE(value == 0x600FA787); + + as.RewindBuffer(); + + as.FLW(f15, -1, x31); + REQUIRE(value == 0xFFFFA787); +} + +TEST_CASE("FMADD.H", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FMADD_H(f15, f31, f7, f26, RMode::RNE); + REQUIRE(value == 0xD47F87C3); + + as.RewindBuffer(); + + as.FMADD_H(f15, f31, f7, f26, RMode::RMM); + REQUIRE(value == 0xD47FC7C3); + + as.RewindBuffer(); + + as.FMADD_H(f15, f31, f7, f26, RMode::DYN); + REQUIRE(value == 0xD47FF7C3); +} + +TEST_CASE("FMADD.S", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FMADD_S(f15, f31, f7, f26, RMode::RNE); + REQUIRE(value == 0xD07F87C3); + + as.RewindBuffer(); + + as.FMADD_S(f15, f31, f7, f26, RMode::RMM); + REQUIRE(value == 0xD07FC7C3); + + as.RewindBuffer(); + + as.FMADD_S(f15, f31, f7, f26, RMode::DYN); + REQUIRE(value == 0xD07FF7C3); +} + +TEST_CASE("FMAX.H", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FMAX_H(f31, f7, f26); + REQUIRE(value == 0x2DA39FD3); + + as.RewindBuffer(); + + as.FMAX_H(f31, f31, f31); + REQUIRE(value == 0x2DFF9FD3); +} + +TEST_CASE("FMAX.S", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FMAX_S(f31, f7, f26); + REQUIRE(value == 0x29A39FD3); + + as.RewindBuffer(); + + as.FMAX_S(f31, f31, f31); + REQUIRE(value == 0x29FF9FD3); +} + +TEST_CASE("FMIN.H", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FMIN_H(f31, f7, f26); + REQUIRE(value == 0x2DA38FD3); + + as.RewindBuffer(); + + as.FMIN_H(f31, f31, f31); + REQUIRE(value == 0x2DFF8FD3); +} + +TEST_CASE("FMIN.S", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FMIN_S(f31, f7, f26); + REQUIRE(value == 0x29A38FD3); + + as.RewindBuffer(); + + as.FMIN_S(f31, f31, f31); + REQUIRE(value == 0x29FF8FD3); +} + +TEST_CASE("FMSUB.H", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FMSUB_H(f15, f31, f7, f26, RMode::RNE); + REQUIRE(value == 0xD47F87C7); + + as.RewindBuffer(); + + as.FMSUB_H(f15, f31, f7, f26, RMode::RMM); + REQUIRE(value == 0xD47FC7C7); + + as.RewindBuffer(); + + as.FMSUB_H(f15, f31, f7, f26, RMode::DYN); + REQUIRE(value == 0xD47FF7C7); +} + +TEST_CASE("FMSUB.S", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FMSUB_S(f15, f31, f7, f26, RMode::RNE); + REQUIRE(value == 0xD07F87C7); + + as.RewindBuffer(); + + as.FMSUB_S(f15, f31, f7, f26, RMode::RMM); + REQUIRE(value == 0xD07FC7C7); + + as.RewindBuffer(); + + as.FMSUB_S(f15, f31, f7, f26, RMode::DYN); + REQUIRE(value == 0xD07FF7C7); +} + +TEST_CASE("FMUL.H", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FMUL_H(f31, f7, f26, RMode::RNE); + REQUIRE(value == 0x15A38FD3); + + as.RewindBuffer(); + + as.FMUL_H(f31, f7, f26, RMode::RMM); + REQUIRE(value == 0x15A3CFD3); + + as.RewindBuffer(); + + as.FMUL_H(f31, f7, f26, RMode::DYN); + REQUIRE(value == 0x15A3FFD3); +} + +TEST_CASE("FMUL.S", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FMUL_S(f31, f7, f26, RMode::RNE); + REQUIRE(value == 0x11A38FD3); + + as.RewindBuffer(); + + as.FMUL_S(f31, f7, f26, RMode::RMM); + REQUIRE(value == 0x11A3CFD3); + + as.RewindBuffer(); + + as.FMUL_S(f31, f7, f26, RMode::DYN); + REQUIRE(value == 0x11A3FFD3); +} + +TEST_CASE("FMV.H.X", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FMV_H_X(f31, x7); + REQUIRE(value == 0xF4038FD3); + + as.RewindBuffer(); + + as.FMV_H_X(f7, x31); + REQUIRE(value == 0xF40F83D3); +} + +TEST_CASE("FMV.W.X", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FMV_W_X(f31, x7); + REQUIRE(value == 0xF0038FD3); + + as.RewindBuffer(); + + as.FMV_W_X(f7, x31); + REQUIRE(value == 0xF00F83D3); +} + +TEST_CASE("FMV.X.H", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FMV_X_H(x31, f7); + REQUIRE(value == 0xE4038FD3); + + as.RewindBuffer(); + + as.FMV_X_H(x7, f31); + REQUIRE(value == 0xE40F83D3); +} + +TEST_CASE("FMV.X.W", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FMV_X_W(x31, f7); + REQUIRE(value == 0xE0038FD3); + + as.RewindBuffer(); + + as.FMV_X_W(x7, f31); + REQUIRE(value == 0xE00F83D3); +} + +TEST_CASE("FNMADD.H", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FNMADD_H(f15, f31, f7, f26, RMode::RNE); + REQUIRE(value == 0xD47F87CF); + + as.RewindBuffer(); + + as.FNMADD_H(f15, f31, f7, f26, RMode::RMM); + REQUIRE(value == 0xD47FC7CF); + + as.RewindBuffer(); + + as.FNMADD_H(f15, f31, f7, f26, RMode::DYN); + REQUIRE(value == 0xD47FF7CF); +} + +TEST_CASE("FNMADD.S", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FNMADD_S(f15, f31, f7, f26, RMode::RNE); + REQUIRE(value == 0xD07F87CF); + + as.RewindBuffer(); + + as.FNMADD_S(f15, f31, f7, f26, RMode::RMM); + REQUIRE(value == 0xD07FC7CF); + + as.RewindBuffer(); + + as.FNMADD_S(f15, f31, f7, f26, RMode::DYN); + REQUIRE(value == 0xD07FF7CF); +} + +TEST_CASE("FNMSUB.H", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FNMSUB_H(f15, f31, f7, f26, RMode::RNE); + REQUIRE(value == 0xD47F87CB); + + as.RewindBuffer(); + + as.FNMSUB_H(f15, f31, f7, f26, RMode::RMM); + REQUIRE(value == 0xD47FC7CB); + + as.RewindBuffer(); + + as.FNMSUB_H(f15, f31, f7, f26, RMode::DYN); + REQUIRE(value == 0xD47FF7CB); +} + +TEST_CASE("FNMSUB.S", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FNMSUB_S(f15, f31, f7, f26, RMode::RNE); + REQUIRE(value == 0xD07F87CB); + + as.RewindBuffer(); + + as.FNMSUB_S(f15, f31, f7, f26, RMode::RMM); + REQUIRE(value == 0xD07FC7CB); + + as.RewindBuffer(); + + as.FNMSUB_S(f15, f31, f7, f26, RMode::DYN); + REQUIRE(value == 0xD07FF7CB); +} + +TEST_CASE("FSGNJ.H", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FSGNJ_H(f31, f7, f26); + REQUIRE(value == 0x25A38FD3); + + as.RewindBuffer(); + + as.FSGNJ_H(f31, f31, f31); + REQUIRE(value == 0x25FF8FD3); +} + +TEST_CASE("FSGNJ.S", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FSGNJ_S(f31, f7, f26); + REQUIRE(value == 0x21A38FD3); + + as.RewindBuffer(); + + as.FSGNJ_S(f31, f31, f31); + REQUIRE(value == 0x21FF8FD3); +} + +TEST_CASE("FSGNJN.H", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FSGNJN_H(f31, f7, f26); + REQUIRE(value == 0x25A39FD3); + + as.RewindBuffer(); + + as.FSGNJN_H(f31, f31, f31); + REQUIRE(value == 0x25FF9FD3); +} + +TEST_CASE("FSGNJN.S", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FSGNJN_S(f31, f7, f26); + REQUIRE(value == 0x21A39FD3); + + as.RewindBuffer(); + + as.FSGNJN_S(f31, f31, f31); + REQUIRE(value == 0x21FF9FD3); +} + +TEST_CASE("FSGNJX.H", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FSGNJX_H(f31, f7, f26); + REQUIRE(value == 0x25A3AFD3); + + as.RewindBuffer(); + + as.FSGNJX_H(f31, f31, f31); + REQUIRE(value == 0x25FFAFD3); +} + +TEST_CASE("FSGNJX.S", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FSGNJX_S(f31, f7, f26); + REQUIRE(value == 0x21A3AFD3); + + as.RewindBuffer(); + + as.FSGNJX_S(f31, f31, f31); + REQUIRE(value == 0x21FFAFD3); +} + +TEST_CASE("FSH", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FSH(f31, 1024, x15); + REQUIRE(value == 0x41F79027); + + as.RewindBuffer(); + + as.FSH(f31, 1536, x15); + REQUIRE(value == 0x61F79027); + + as.RewindBuffer(); + + as.FSH(f31, -1, x15); + REQUIRE(value == 0xFFF79FA7); +} + +TEST_CASE("FSQRT.H", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FSQRT_H(f31, f7, RMode::RNE); + REQUIRE(value == 0x5C038FD3); + + as.RewindBuffer(); + + as.FSQRT_H(f31, f7, RMode::RMM); + REQUIRE(value == 0x5C03CFD3); + + as.RewindBuffer(); + + as.FSQRT_H(f31, f7, RMode::DYN); + REQUIRE(value == 0x5C03FFD3); +} + +TEST_CASE("FSQRT.S", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FSQRT_S(f31, f7, RMode::RNE); + REQUIRE(value == 0x58038FD3); + + as.RewindBuffer(); + + as.FSQRT_S(f31, f7, RMode::RMM); + REQUIRE(value == 0x5803CFD3); + + as.RewindBuffer(); + + as.FSQRT_S(f31, f7, RMode::DYN); + REQUIRE(value == 0x5803FFD3); +} + +TEST_CASE("FSUB.H", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FSUB_H(f31, f7, f26, RMode::RNE); + REQUIRE(value == 0x0DA38FD3); + + as.RewindBuffer(); + + as.FSUB_H(f31, f7, f26, RMode::RMM); + REQUIRE(value == 0x0DA3CFD3); + + as.RewindBuffer(); + + as.FSUB_H(f31, f7, f26, RMode::DYN); + REQUIRE(value == 0x0DA3FFD3); +} + +TEST_CASE("FSUB.S", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FSUB_S(f31, f7, f26, RMode::RNE); + REQUIRE(value == 0x09A38FD3); + + as.RewindBuffer(); + + as.FSUB_S(f31, f7, f26, RMode::RMM); + REQUIRE(value == 0x09A3CFD3); + + as.RewindBuffer(); + + as.FSUB_S(f31, f7, f26, RMode::DYN); + REQUIRE(value == 0x09A3FFD3); +} + +TEST_CASE("FSW", "[rv32f]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FSW(f31, 1024, x15); + REQUIRE(value == 0x41F7A027); + + as.RewindBuffer(); + + as.FSW(f31, 1536, x15); + REQUIRE(value == 0x61F7A027); + + as.RewindBuffer(); + + as.FSW(f31, -1, x15); + REQUIRE(value == 0xFFF7AFA7); +} diff --git a/externals/biscuit/tests/src/assembler_rvk_tests.cpp b/externals/biscuit/tests/src/assembler_rvk_tests.cpp new file mode 100644 index 00000000..ccb8c2bd --- /dev/null +++ b/externals/biscuit/tests/src/assembler_rvk_tests.cpp @@ -0,0 +1,384 @@ +#include <catch/catch.hpp> + +#include <biscuit/assembler.hpp> + +#include "assembler_test_utils.hpp" + +using namespace biscuit; + +TEST_CASE("AES32DSI", "[rvk]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.AES32DSI(x31, x31, x31, 0b11); + REQUIRE(value == 0xEBFF8FB3); + + as.RewindBuffer(); + + as.AES32DSI(x1, x2, x3, 0b10); + REQUIRE(value == 0xAA3100B3); +} + +TEST_CASE("AES32DSMI", "[rvk]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.AES32DSMI(x31, x31, x31, 0b11); + REQUIRE(value == 0xEFFF8FB3); + + as.RewindBuffer(); + + as.AES32DSMI(x1, x2, x3, 0b10); + REQUIRE(value == 0xAE3100B3); +} + +TEST_CASE("AES32ESI", "[rvk]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.AES32ESI(x31, x31, x31, 0b11); + REQUIRE(value == 0xE3FF8FB3); + + as.RewindBuffer(); + + as.AES32ESI(x1, x2, x3, 0b10); + REQUIRE(value == 0xA23100B3); +} + +TEST_CASE("AES32ESMI", "[rvk]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.AES32ESMI(x31, x31, x31, 0b11); + REQUIRE(value == 0xE7FF8FB3); + + as.RewindBuffer(); + + as.AES32ESMI(x1, x2, x3, 0b10); + REQUIRE(value == 0xA63100B3); +} + +TEST_CASE("AES64DS", "[rvk]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.AES64DS(x31, x31, x31); + REQUIRE(value == 0x3BFF8FB3); + + as.RewindBuffer(); + + as.AES64DS(x1, x2, x3); + REQUIRE(value == 0x3A3100B3); +} + +TEST_CASE("AES64DSM", "[rvk]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.AES64DSM(x31, x31, x31); + REQUIRE(value == 0x3FFF8FB3); + + as.RewindBuffer(); + + as.AES64DSM(x1, x2, x3); + REQUIRE(value == 0x3E3100B3); +} + +TEST_CASE("AES64ES", "[rvk]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.AES64ES(x31, x31, x31); + REQUIRE(value == 0x33FF8FB3); + + as.RewindBuffer(); + + as.AES64ES(x1, x2, x3); + REQUIRE(value == 0x323100B3); +} + +TEST_CASE("AES64ESM", "[rvk]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.AES64ESM(x31, x31, x31); + REQUIRE(value == 0x37FF8FB3); + + as.RewindBuffer(); + + as.AES64ESM(x1, x2, x3); + REQUIRE(value == 0x363100B3); +} + +TEST_CASE("AES64IM", "[rvk]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.AES64IM(x31, x31); + REQUIRE(value == 0x300F9F93); + + as.RewindBuffer(); + + as.AES64IM(x1, x2); + REQUIRE(value == 0x30011093); +} + +TEST_CASE("AES64KS1I", "[rvk]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.AES64KS1I(x31, x31, 0xA); + REQUIRE(value == 0x31AF9F93); + + as.RewindBuffer(); + + as.AES64KS1I(x1, x2, 0x5); + REQUIRE(value == 0x31511093); +} + +TEST_CASE("AES64KS2", "[rvk]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.AES64KS2(x31, x31, x31); + REQUIRE(value == 0x7FFF8FB3); + + as.RewindBuffer(); + + as.AES64KS2(x1, x2, x3); + REQUIRE(value == 0x7E3100B3); +} + +TEST_CASE("SHA256SIG0", "[rvk]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.SHA256SIG0(x31, x31); + REQUIRE(value == 0x102F9F93); + + as.RewindBuffer(); + + as.SHA256SIG0(x1, x2); + REQUIRE(value == 0x10211093); +} + +TEST_CASE("SHA256SIG1", "[rvk]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.SHA256SIG1(x31, x31); + REQUIRE(value == 0x103F9F93); + + as.RewindBuffer(); + + as.SHA256SIG1(x1, x2); + REQUIRE(value == 0x10311093); +} + +TEST_CASE("SHA256SUM0", "[rvk]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.SHA256SUM0(x31, x31); + REQUIRE(value == 0x100F9F93); + + as.RewindBuffer(); + + as.SHA256SUM0(x1, x2); + REQUIRE(value == 0x10011093); +} + +TEST_CASE("SHA256SUM1", "[rvk]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.SHA256SUM1(x31, x31); + REQUIRE(value == 0x101F9F93); + + as.RewindBuffer(); + + as.SHA256SUM1(x1, x2); + REQUIRE(value == 0x10111093); +} + +TEST_CASE("SHA512SIG0", "[rvk]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.SHA512SIG0(x31, x31); + REQUIRE(value == 0x106F9F93); + + as.RewindBuffer(); + + as.SHA512SIG0(x1, x2); + REQUIRE(value == 0x10611093); +} + +TEST_CASE("SHA512SIG0H", "[rvk]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.SHA512SIG0H(x31, x31, x31); + REQUIRE(value == 0x5DFF8FB3); + + as.RewindBuffer(); + + as.SHA512SIG0H(x1, x2, x3); + REQUIRE(value == 0x5C3100B3); +} + +TEST_CASE("SHA512SIG0L", "[rvk]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.SHA512SIG0L(x31, x31, x31); + REQUIRE(value == 0x55FF8FB3); + + as.RewindBuffer(); + + as.SHA512SIG0L(x1, x2, x3); + REQUIRE(value == 0x543100B3); +} + +TEST_CASE("SHA512SIG1", "[rvk]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.SHA512SIG1(x31, x31); + REQUIRE(value == 0x107F9F93); + + as.RewindBuffer(); + + as.SHA512SIG1(x1, x2); + REQUIRE(value == 0x10711093); +} + +TEST_CASE("SHA512SIG1H", "[rvk]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.SHA512SIG1H(x31, x31, x31); + REQUIRE(value == 0x5FFF8FB3); + + as.RewindBuffer(); + + as.SHA512SIG1H(x1, x2, x3); + REQUIRE(value == 0x5E3100B3); +} + +TEST_CASE("SHA512SIG1L", "[rvk]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.SHA512SIG1L(x31, x31, x31); + REQUIRE(value == 0x57FF8FB3); + + as.RewindBuffer(); + + as.SHA512SIG1L(x1, x2, x3); + REQUIRE(value == 0x563100B3); +} + +TEST_CASE("SHA512SUM0", "[rvk]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.SHA512SUM0(x31, x31); + REQUIRE(value == 0x104F9F93); + + as.RewindBuffer(); + + as.SHA512SUM0(x1, x2); + REQUIRE(value == 0x10411093); +} + +TEST_CASE("SHA512SUM0R", "[rvk]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.SHA512SUM0R(x31, x31, x31); + REQUIRE(value == 0x51FF8FB3); + + as.RewindBuffer(); + + as.SHA512SUM0R(x1, x2, x3); + REQUIRE(value == 0x503100B3); +} + +TEST_CASE("SHA512SUM1", "[rvk]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.SHA512SUM1(x31, x31); + REQUIRE(value == 0x105F9F93); + + as.RewindBuffer(); + + as.SHA512SUM1(x1, x2); + REQUIRE(value == 0x10511093); +} + +TEST_CASE("SHA512SUM1R", "[rvk]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.SHA512SUM1R(x31, x31, x31); + REQUIRE(value == 0x53FF8FB3); + + as.RewindBuffer(); + + as.SHA512SUM1R(x1, x2, x3); + REQUIRE(value == 0x523100B3); +} + +TEST_CASE("SM3P0", "[rvk]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.SM3P0(x31, x31); + REQUIRE(value == 0x108F9F93); + + as.RewindBuffer(); + + as.SM3P0(x1, x2); + REQUIRE(value == 0x10811093); +} + +TEST_CASE("SM3P1", "[rvk]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.SM3P1(x31, x31); + REQUIRE(value == 0x109F9F93); + + as.RewindBuffer(); + + as.SM3P1(x1, x2); + REQUIRE(value == 0x10911093); +} + +TEST_CASE("SM4ED", "[rvk]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.SM4ED(x31, x31, x31, 0b11); + REQUIRE(value == 0xF1FF8FB3); + + as.RewindBuffer(); + + as.SM4ED(x1, x2, x3, 0b10); + REQUIRE(value == 0xB03100B3); +} + +TEST_CASE("SM4KS", "[rvk]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.SM4KS(x31, x31, x31, 0b11); + REQUIRE(value == 0xF5FF8FB3); + + as.RewindBuffer(); + + as.SM4KS(x1, x2, x3, 0b10); + REQUIRE(value == 0xB43100B3); +} diff --git a/externals/biscuit/tests/src/assembler_rvm_tests.cpp b/externals/biscuit/tests/src/assembler_rvm_tests.cpp new file mode 100644 index 00000000..ba9ad32d --- /dev/null +++ b/externals/biscuit/tests/src/assembler_rvm_tests.cpp @@ -0,0 +1,241 @@ +#include <catch/catch.hpp> + +#include <biscuit/assembler.hpp> + +#include "assembler_test_utils.hpp" + +using namespace biscuit; + +TEST_CASE("DIV", "[rv32m]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.DIV(x31, x15, x20); + REQUIRE(value == 0x0347CFB3); + + as.RewindBuffer(); + + as.DIV(x31, x20, x15); + REQUIRE(value == 0x02FA4FB3); + + as.RewindBuffer(); + + as.DIV(x20, x31, x15); + REQUIRE(value == 0x02FFCA33); +} + +TEST_CASE("DIVW", "[rv64m]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.DIVW(x31, x15, x20); + REQUIRE(value == 0x0347CFBB); + + as.RewindBuffer(); + + as.DIVW(x31, x20, x15); + REQUIRE(value == 0x02FA4FBB); + + as.RewindBuffer(); + + as.DIVW(x20, x31, x15); + REQUIRE(value == 0x02FFCA3B); +} + +TEST_CASE("DIVU", "[rv32m]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.DIVU(x31, x15, x20); + REQUIRE(value == 0x0347DFB3); + + as.RewindBuffer(); + + as.DIVU(x31, x20, x15); + REQUIRE(value == 0x02FA5FB3); + + as.RewindBuffer(); + + as.DIVU(x20, x31, x15); + REQUIRE(value == 0x02FFDA33); +} + +TEST_CASE("DIVUW", "[rv64m]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.DIVUW(x31, x15, x20); + REQUIRE(value == 0x0347DFBB); + + as.RewindBuffer(); + + as.DIVUW(x31, x20, x15); + REQUIRE(value == 0x02FA5FBB); + + as.RewindBuffer(); + + as.DIVUW(x20, x31, x15); + REQUIRE(value == 0x02FFDA3B); +} + +TEST_CASE("MUL", "[rv32m]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.MUL(x31, x15, x20); + REQUIRE(value == 0x03478FB3); + + as.RewindBuffer(); + + as.MUL(x31, x20, x15); + REQUIRE(value == 0x02FA0FB3); + + as.RewindBuffer(); + + as.MUL(x20, x31, x15); + REQUIRE(value == 0x02FF8A33); +} + +TEST_CASE("MULH", "[rv32m]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.MULH(x31, x15, x20); + REQUIRE(value == 0x03479FB3); + + as.RewindBuffer(); + + as.MULH(x31, x20, x15); + REQUIRE(value == 0x02FA1FB3); + + as.RewindBuffer(); + + as.MULH(x20, x31, x15); + REQUIRE(value == 0x02FF9A33); +} + +TEST_CASE("MULW", "[rv64m]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.MULW(x31, x15, x20); + REQUIRE(value == 0x03478FBB); + + as.RewindBuffer(); + + as.MULW(x31, x20, x15); + REQUIRE(value == 0x02FA0FBB); + + as.RewindBuffer(); + + as.MULW(x20, x31, x15); + REQUIRE(value == 0x02FF8A3B); +} + +TEST_CASE("MULHSU", "[rv32m]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.MULHSU(x31, x15, x20); + REQUIRE(value == 0x0347AFB3); + + as.RewindBuffer(); + + as.MULHSU(x31, x20, x15); + REQUIRE(value == 0x02FA2FB3); + + as.RewindBuffer(); + + as.MULHSU(x20, x31, x15); + REQUIRE(value == 0x02FFAA33); +} + +TEST_CASE("MULHU", "[rv32m]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.MULHU(x31, x15, x20); + REQUIRE(value == 0x0347BFB3); + + as.RewindBuffer(); + + as.MULHU(x31, x20, x15); + REQUIRE(value == 0x02FA3FB3); + + as.RewindBuffer(); + + as.MULHU(x20, x31, x15); + REQUIRE(value == 0x02FFBA33); +} + +TEST_CASE("REM", "[rv32m]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.REM(x31, x15, x20); + REQUIRE(value == 0x0347EFB3); + + as.RewindBuffer(); + + as.REM(x31, x20, x15); + REQUIRE(value == 0x02FA6FB3); + + as.RewindBuffer(); + + as.REM(x20, x31, x15); + REQUIRE(value == 0x02FFEA33); +} + +TEST_CASE("REMW", "[rv64m]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.REMW(x31, x15, x20); + REQUIRE(value == 0x0347EFBB); + + as.RewindBuffer(); + + as.REMW(x31, x20, x15); + REQUIRE(value == 0x02FA6FBB); + + as.RewindBuffer(); + + as.REMW(x20, x31, x15); + REQUIRE(value == 0x02FFEA3B); +} + +TEST_CASE("REMU", "[rv32m]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.REMU(x31, x15, x20); + REQUIRE(value == 0x0347FFB3); + + as.RewindBuffer(); + + as.REMU(x31, x20, x15); + REQUIRE(value == 0x02FA7FB3); + + as.RewindBuffer(); + + as.REMU(x20, x31, x15); + REQUIRE(value == 0x02FFFA33); +} + +TEST_CASE("REMUW", "[rv64m]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.REMUW(x31, x15, x20); + REQUIRE(value == 0x0347FFBB); + + as.RewindBuffer(); + + as.REMUW(x31, x20, x15); + REQUIRE(value == 0x02FA7FBB); + + as.RewindBuffer(); + + as.REMUW(x20, x31, x15); + REQUIRE(value == 0x02FFFA3B); +} diff --git a/externals/biscuit/tests/src/assembler_rvq_tests.cpp b/externals/biscuit/tests/src/assembler_rvq_tests.cpp new file mode 100644 index 00000000..2c2e8bd3 --- /dev/null +++ b/externals/biscuit/tests/src/assembler_rvq_tests.cpp @@ -0,0 +1,538 @@ +#include <catch/catch.hpp> + +#include <biscuit/assembler.hpp> + +#include "assembler_test_utils.hpp" + +using namespace biscuit; + +TEST_CASE("FADD.Q", "[rv32q]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FADD_Q(f31, f7, f26, RMode::RNE); + REQUIRE(value == 0x07A38FD3); + + as.RewindBuffer(); + + as.FADD_Q(f31, f7, f26, RMode::RMM); + REQUIRE(value == 0x07A3CFD3); + + as.RewindBuffer(); + + as.FADD_Q(f31, f7, f26, RMode::DYN); + REQUIRE(value == 0x07A3FFD3); +} + +TEST_CASE("FCLASS.Q", "[rv32q]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FCLASS_Q(x31, f7); + REQUIRE(value == 0xE6039FD3); + + as.RewindBuffer(); + + as.FCLASS_Q(x7, f31); + REQUIRE(value == 0xE60F93D3); +} + +TEST_CASE("FCVT.Q.D", "[rv32q]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FCVT_Q_D(f31, f7, RMode::RNE); + REQUIRE(value == 0x46138FD3); + + as.RewindBuffer(); + + as.FCVT_Q_D(f31, f7, RMode::RMM); + REQUIRE(value == 0x4613CFD3); + + as.RewindBuffer(); + + as.FCVT_Q_D(f31, f7, RMode::DYN); + REQUIRE(value == 0x4613FFD3); +} + +TEST_CASE("FCVT.Q.S", "[rv32q]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FCVT_Q_S(f31, f7, RMode::RNE); + REQUIRE(value == 0x46038FD3); + + as.RewindBuffer(); + + as.FCVT_Q_S(f31, f7, RMode::RMM); + REQUIRE(value == 0x4603CFD3); + + as.RewindBuffer(); + + as.FCVT_Q_S(f31, f7, RMode::DYN); + REQUIRE(value == 0x4603FFD3); +} + +TEST_CASE("FCVT.Q.W", "[rv32q]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FCVT_Q_W(f31, x7, RMode::RNE); + REQUIRE(value == 0xD6038FD3); + + as.RewindBuffer(); + + as.FCVT_Q_W(f31, x7, RMode::RMM); + REQUIRE(value == 0xD603CFD3); + + as.RewindBuffer(); + + as.FCVT_Q_W(f31, x7, RMode::DYN); + REQUIRE(value == 0xD603FFD3); +} + +TEST_CASE("FCVT.Q.WU", "[rv32q]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FCVT_Q_WU(f31, x7, RMode::RNE); + REQUIRE(value == 0xD6138FD3); + + as.RewindBuffer(); + + as.FCVT_Q_WU(f31, x7, RMode::RMM); + REQUIRE(value == 0xD613CFD3); + + as.RewindBuffer(); + + as.FCVT_Q_WU(f31, x7, RMode::DYN); + REQUIRE(value == 0xD613FFD3); +} + +TEST_CASE("FCVT.L.Q", "[rv64q]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.FCVT_L_Q(x31, f7, RMode::RNE); + REQUIRE(value == 0xC6238FD3); + + as.RewindBuffer(); + + as.FCVT_L_Q(x31, f7, RMode::RMM); + REQUIRE(value == 0xC623CFD3); + + as.RewindBuffer(); + + as.FCVT_L_Q(x31, f7, RMode::DYN); + REQUIRE(value == 0xC623FFD3); +} + +TEST_CASE("FCVT.LU.Q", "[rv64q]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.FCVT_LU_Q(x31, f7, RMode::RNE); + REQUIRE(value == 0xC6338FD3); + + as.RewindBuffer(); + + as.FCVT_LU_Q(x31, f7, RMode::RMM); + REQUIRE(value == 0xC633CFD3); + + as.RewindBuffer(); + + as.FCVT_LU_Q(x31, f7, RMode::DYN); + REQUIRE(value == 0xC633FFD3); +} + +TEST_CASE("FCVT.Q.L", "[rv64q]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.FCVT_Q_L(f31, x7, RMode::RNE); + REQUIRE(value == 0xD6238FD3); + + as.RewindBuffer(); + + as.FCVT_Q_L(f31, x7, RMode::RMM); + REQUIRE(value == 0xD623CFD3); + + as.RewindBuffer(); + + as.FCVT_Q_L(f31, x7, RMode::DYN); + REQUIRE(value == 0xD623FFD3); +} + +TEST_CASE("FCVT.Q.LU", "[rv64q]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.FCVT_Q_LU(f31, x7, RMode::RNE); + REQUIRE(value == 0xD6338FD3); + + as.RewindBuffer(); + + as.FCVT_Q_LU(f31, x7, RMode::RMM); + REQUIRE(value == 0xD633CFD3); + + as.RewindBuffer(); + + as.FCVT_Q_LU(f31, x7, RMode::DYN); + REQUIRE(value == 0xD633FFD3); +} + +TEST_CASE("FCVT.W.Q", "[rv32q]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FCVT_W_Q(x31, f7, RMode::RNE); + REQUIRE(value == 0xC6038FD3); + + as.RewindBuffer(); + + as.FCVT_W_Q(x31, f7, RMode::RMM); + REQUIRE(value == 0xC603CFD3); + + as.RewindBuffer(); + + as.FCVT_W_Q(x31, f7, RMode::DYN); + REQUIRE(value == 0xC603FFD3); +} + +TEST_CASE("FCVT.WU.Q", "[rv32q]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FCVT_WU_Q(x31, f7, RMode::RNE); + REQUIRE(value == 0xC6138FD3); + + as.RewindBuffer(); + + as.FCVT_WU_Q(x31, f7, RMode::RMM); + REQUIRE(value == 0xC613CFD3); + + as.RewindBuffer(); + + as.FCVT_WU_Q(x31, f7, RMode::DYN); + REQUIRE(value == 0xC613FFD3); +} + +TEST_CASE("FCVT.D.Q", "[rv32q]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FCVT_D_Q(f31, f7, RMode::RNE); + REQUIRE(value == 0x42338FD3); + + as.RewindBuffer(); + + as.FCVT_D_Q(f31, f7, RMode::RMM); + REQUIRE(value == 0x4233CFD3); + + as.RewindBuffer(); + + as.FCVT_D_Q(f31, f7, RMode::DYN); + REQUIRE(value == 0x4233FFD3); +} + +TEST_CASE("FCVT.S.Q", "[rv32q]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FCVT_S_Q(f31, f7, RMode::RNE); + REQUIRE(value == 0x40338FD3); + + as.RewindBuffer(); + + as.FCVT_S_Q(f31, f7, RMode::RMM); + REQUIRE(value == 0x4033CFD3); + + as.RewindBuffer(); + + as.FCVT_S_Q(f31, f7, RMode::DYN); + REQUIRE(value == 0x4033FFD3); +} + +TEST_CASE("FDIV.Q", "[rv32q]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FDIV_Q(f31, f7, f26, RMode::RNE); + REQUIRE(value == 0x1FA38FD3); + + as.RewindBuffer(); + + as.FDIV_Q(f31, f7, f26, RMode::RMM); + REQUIRE(value == 0x1FA3CFD3); + + as.RewindBuffer(); + + as.FDIV_Q(f31, f7, f26, RMode::DYN); + REQUIRE(value == 0x1FA3FFD3); +} + +TEST_CASE("FEQ.Q", "[rv32q]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FEQ_Q(x31, f7, f26); + REQUIRE(value == 0xA7A3AFD3); + + as.RewindBuffer(); + + as.FEQ_Q(x31, f26, f7); + REQUIRE(value == 0xA67D2FD3); +} + +TEST_CASE("FLE.Q", "[rv32q]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FLE_Q(x31, f7, f26); + REQUIRE(value == 0xA7A38FD3); + + as.RewindBuffer(); + + as.FLE_Q(x31, f26, f7); + REQUIRE(value == 0xA67D0FD3); +} + +TEST_CASE("FLT.Q", "[rv32q]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FLT_Q(x31, f7, f26); + REQUIRE(value == 0xA7A39FD3); + + as.RewindBuffer(); + + as.FLT_Q(x31, f26, f7); + REQUIRE(value == 0xA67D1FD3); +} + +TEST_CASE("FLQ", "[rv32q]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FLQ(f15, 1024, x31); + REQUIRE(value == 0x400FC787); + + as.RewindBuffer(); + + as.FLQ(f15, 1536, x31); + REQUIRE(value == 0x600FC787); + + as.RewindBuffer(); + + as.FLQ(f15, -1, x31); + REQUIRE(value == 0xFFFFC787); +} + +TEST_CASE("FMADD.Q", "[rv32q]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FMADD_Q(f15, f31, f7, f26, RMode::RNE); + REQUIRE(value == 0xD67F87C3); + + as.RewindBuffer(); + + as.FMADD_Q(f15, f31, f7, f26, RMode::RMM); + REQUIRE(value == 0xD67FC7C3); + + as.RewindBuffer(); + + as.FMADD_Q(f15, f31, f7, f26, RMode::DYN); + REQUIRE(value == 0xD67FF7C3); +} + +TEST_CASE("FMAX.Q", "[rv32q]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FMAX_Q(f31, f7, f26); + REQUIRE(value == 0x2FA39FD3); + + as.RewindBuffer(); + + as.FMAX_Q(f31, f31, f31); + REQUIRE(value == 0x2FFF9FD3); +} + +TEST_CASE("FMIN.Q", "[rv32q]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FMIN_Q(f31, f7, f26); + REQUIRE(value == 0x2FA38FD3); + + as.RewindBuffer(); + + as.FMIN_Q(f31, f31, f31); + REQUIRE(value == 0x2FFF8FD3); +} + +TEST_CASE("FMSUB.Q", "[rv32q]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FMSUB_Q(f15, f31, f7, f26, RMode::RNE); + REQUIRE(value == 0xD67F87C7); + + as.RewindBuffer(); + + as.FMSUB_Q(f15, f31, f7, f26, RMode::RMM); + REQUIRE(value == 0xD67FC7C7); + + as.RewindBuffer(); + + as.FMSUB_Q(f15, f31, f7, f26, RMode::DYN); + REQUIRE(value == 0xD67FF7C7); +} + +TEST_CASE("FMUL.Q", "[rv32q]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FMUL_Q(f31, f7, f26, RMode::RNE); + REQUIRE(value == 0x17A38FD3); + + as.RewindBuffer(); + + as.FMUL_Q(f31, f7, f26, RMode::RMM); + REQUIRE(value == 0x17A3CFD3); + + as.RewindBuffer(); + + as.FMUL_Q(f31, f7, f26, RMode::DYN); + REQUIRE(value == 0x17A3FFD3); +} + +TEST_CASE("FNMADD.Q", "[rv32q]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FNMADD_Q(f15, f31, f7, f26, RMode::RNE); + REQUIRE(value == 0xD67F87CF); + + as.RewindBuffer(); + + as.FNMADD_Q(f15, f31, f7, f26, RMode::RMM); + REQUIRE(value == 0xD67FC7CF); + + as.RewindBuffer(); + + as.FNMADD_Q(f15, f31, f7, f26, RMode::DYN); + REQUIRE(value == 0xD67FF7CF); +} + +TEST_CASE("FNMSUB.Q", "[rv32q]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FNMSUB_Q(f15, f31, f7, f26, RMode::RNE); + REQUIRE(value == 0xD67F87CB); + + as.RewindBuffer(); + + as.FNMSUB_Q(f15, f31, f7, f26, RMode::RMM); + REQUIRE(value == 0xD67FC7CB); + + as.RewindBuffer(); + + as.FNMSUB_Q(f15, f31, f7, f26, RMode::DYN); + REQUIRE(value == 0xD67FF7CB); +} + +TEST_CASE("FSGNJ.Q", "[rv32q]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FSGNJ_Q(f31, f7, f26); + REQUIRE(value == 0x27A38FD3); + + as.RewindBuffer(); + + as.FSGNJ_Q(f31, f31, f31); + REQUIRE(value == 0x27FF8FD3); +} + +TEST_CASE("FSGNJN.Q", "[rv32q]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FSGNJN_Q(f31, f7, f26); + REQUIRE(value == 0x27A39FD3); + + as.RewindBuffer(); + + as.FSGNJN_Q(f31, f31, f31); + REQUIRE(value == 0x27FF9FD3); +} + +TEST_CASE("FSGNJX.Q", "[rv32q]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FSGNJX_Q(f31, f7, f26); + REQUIRE(value == 0x27A3AFD3); + + as.RewindBuffer(); + + as.FSGNJX_Q(f31, f31, f31); + REQUIRE(value == 0x27FFAFD3); +} + +TEST_CASE("FSQRT.Q", "[rv32q]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FSQRT_Q(f31, f7, RMode::RNE); + REQUIRE(value == 0x5E038FD3); + + as.RewindBuffer(); + + as.FSQRT_Q(f31, f7, RMode::RMM); + REQUIRE(value == 0x5E03CFD3); + + as.RewindBuffer(); + + as.FSQRT_Q(f31, f7, RMode::DYN); + REQUIRE(value == 0x5E03FFD3); +} + +TEST_CASE("FSUB.Q", "[rv32q]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FSUB_Q(f31, f7, f26, RMode::RNE); + REQUIRE(value == 0x0FA38FD3); + + as.RewindBuffer(); + + as.FSUB_Q(f31, f7, f26, RMode::RMM); + REQUIRE(value == 0x0FA3CFD3); + + as.RewindBuffer(); + + as.FSUB_Q(f31, f7, f26, RMode::DYN); + REQUIRE(value == 0x0FA3FFD3); +} + +TEST_CASE("FSQ", "[rv32q]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FSQ(f31, 1024, x15); + REQUIRE(value == 0x41F7C027); + + as.RewindBuffer(); + + as.FSQ(f31, 1536, x15); + REQUIRE(value == 0x61F7C027); + + as.RewindBuffer(); + + as.FSQ(f31, -1, x15); + REQUIRE(value == 0xFFF7CFA7); +} diff --git a/externals/biscuit/tests/src/assembler_rvv_tests.cpp b/externals/biscuit/tests/src/assembler_rvv_tests.cpp new file mode 100644 index 00000000..eb3508af --- /dev/null +++ b/externals/biscuit/tests/src/assembler_rvv_tests.cpp @@ -0,0 +1,5334 @@ +#include <catch/catch.hpp> + +#include <biscuit/assembler.hpp> + +#include "assembler_test_utils.hpp" + +using namespace biscuit; + +TEST_CASE("VAADD.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VAADD(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x26862257); + + as.RewindBuffer(); + + as.VAADD(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x24862257); +} + +TEST_CASE("VAADD.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VAADD(v4, v8, x11, VecMask::No); + REQUIRE(value == 0x2685E257); + + as.RewindBuffer(); + + as.VAADD(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0x2485E257); +} + +TEST_CASE("VAADDU.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VAADDU(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x22862257); + + as.RewindBuffer(); + + as.VAADDU(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x20862257); +} + +TEST_CASE("VAADDU.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VAADDU(v4, v8, x11, VecMask::No); + REQUIRE(value == 0x2285E257); + + as.RewindBuffer(); + + as.VAADDU(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0x2085E257); +} + +TEST_CASE("VADC.VVM", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VADC(v4, v8, v12); + REQUIRE(value == 0x40860257); +} + +TEST_CASE("VADC.VXM", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VADC(v4, v8, x11); + REQUIRE(value == 0x4085C257); +} + +TEST_CASE("VADC.VIM", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VADC(v4, v8, 15); + REQUIRE(value == 0x4087B257); + + as.RewindBuffer(); + + as.VADC(v4, v8, -16); + REQUIRE(value == 0x40883257); +} + +TEST_CASE("VADD.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VADD(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x02860257); + + as.RewindBuffer(); + + as.VADD(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x00860257); +} + +TEST_CASE("VADD.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VADD(v4, v8, x11, VecMask::No); + REQUIRE(value == 0x0285C257); + + as.RewindBuffer(); + + as.VADD(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0x0085C257); +} + +TEST_CASE("VADD.VI", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VADD(v4, v8, 15, VecMask::No); + REQUIRE(value == 0x0287B257); + + as.RewindBuffer(); + + as.VADD(v4, v8, -16, VecMask::No); + REQUIRE(value == 0x02883257); + + as.RewindBuffer(); + + as.VADD(v4, v8, 15, VecMask::Yes); + REQUIRE(value == 0x0087B257); + + as.RewindBuffer(); + + as.VADD(v4, v8, -16, VecMask::Yes); + REQUIRE(value == 0x00883257); +} + +TEST_CASE("VAND.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VAND(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x26860257); + + as.RewindBuffer(); + + as.VAND(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x24860257); +} + +TEST_CASE("VAND.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VAND(v4, v8, x11, VecMask::No); + REQUIRE(value == 0x2685C257); + + as.RewindBuffer(); + + as.VAND(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0x2485C257); +} + +TEST_CASE("VAND.VI", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VAND(v4, v8, 15, VecMask::No); + REQUIRE(value == 0x2687B257); + + as.RewindBuffer(); + + as.VAND(v4, v8, -16, VecMask::No); + REQUIRE(value == 0x26883257); + + as.RewindBuffer(); + + as.VAND(v4, v8, 15, VecMask::Yes); + REQUIRE(value == 0x2487B257); + + as.RewindBuffer(); + + as.VAND(v4, v8, -16, VecMask::Yes); + REQUIRE(value == 0x24883257); +} + +TEST_CASE("VASUB.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VASUB(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x2E862257); + + as.RewindBuffer(); + + as.VASUB(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x2C862257); +} + +TEST_CASE("VASUB.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VASUB(v4, v8, x11, VecMask::No); + REQUIRE(value == 0x2E85E257); + + as.RewindBuffer(); + + as.VASUB(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0x2C85E257); +} + +TEST_CASE("VASUBU.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VASUBU(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x2A862257); + + as.RewindBuffer(); + + as.VASUBU(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x28862257); +} + +TEST_CASE("VASUBU.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VASUBU(v4, v8, x11, VecMask::No); + REQUIRE(value == 0x2A85E257); + + as.RewindBuffer(); + + as.VASUBU(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0x2885E257); +} + +TEST_CASE("VCOMPRESS.VM", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VCOMPRESS(v4, v8, v12); + REQUIRE(value == 0x5E862257); +} + +TEST_CASE("VDIV.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VDIV(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x86862257); + + as.RewindBuffer(); + + as.VDIV(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x84862257); +} + +TEST_CASE("VDIV.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VDIV(v4, v8, x11, VecMask::No); + REQUIRE(value == 0x8685E257); + + as.RewindBuffer(); + + as.VDIV(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0x8485E257); +} + +TEST_CASE("VDIVU.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VDIVU(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x82862257); + + as.RewindBuffer(); + + as.VDIVU(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x80862257); +} + +TEST_CASE("VDIVU.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VDIVU(v4, v8, x11, VecMask::No); + REQUIRE(value == 0x8285E257); + + as.RewindBuffer(); + + as.VDIVU(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0x8085E257); +} + +TEST_CASE("VFADD.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFADD(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x02861257); + + as.RewindBuffer(); + + as.VFADD(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x00861257); +} + +TEST_CASE("VFADD.VF", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFADD(v4, v8, f12, VecMask::No); + REQUIRE(value == 0x02865257); + + as.RewindBuffer(); + + as.VFADD(v4, v8, f12, VecMask::Yes); + REQUIRE(value == 0x00865257); +} + +TEST_CASE("VFCLASS.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFCLASS(v4, v8, VecMask::No); + REQUIRE(value == 0x4E881257); + + as.RewindBuffer(); + + as.VFCLASS(v4, v8, VecMask::Yes); + REQUIRE(value == 0x4C881257); +} + +TEST_CASE("VFCVT.F.X.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFCVT_F_X(v4, v8, VecMask::No); + REQUIRE(value == 0x4A819257); + + as.RewindBuffer(); + + as.VFCVT_F_X(v4, v8, VecMask::Yes); + REQUIRE(value == 0x48819257); +} + +TEST_CASE("VFCVT.F.XU.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFCVT_F_XU(v4, v8, VecMask::No); + REQUIRE(value == 0x4A811257); + + as.RewindBuffer(); + + as.VFCVT_F_XU(v4, v8, VecMask::Yes); + REQUIRE(value == 0x48811257); +} + +TEST_CASE("VFCVT.RTZ.X.F.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFCVT_RTZ_X_F(v4, v8, VecMask::No); + REQUIRE(value == 0x4A839257); + + as.RewindBuffer(); + + as.VFCVT_RTZ_X_F(v4, v8, VecMask::Yes); + REQUIRE(value == 0x48839257); +} + +TEST_CASE("VFCVT.RTZ.XU.F.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFCVT_RTZ_XU_F(v4, v8, VecMask::No); + REQUIRE(value == 0x4A831257); + + as.RewindBuffer(); + + as.VFCVT_RTZ_XU_F(v4, v8, VecMask::Yes); + REQUIRE(value == 0x48831257); +} + +TEST_CASE("VFCVT.X.F.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFCVT_X_F(v4, v8, VecMask::No); + REQUIRE(value == 0x4A809257); + + as.RewindBuffer(); + + as.VFCVT_X_F(v4, v8, VecMask::Yes); + REQUIRE(value == 0x48809257); +} + +TEST_CASE("VFCVT.XU.F.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFCVT_XU_F(v4, v8, VecMask::No); + REQUIRE(value == 0x4A801257); + + as.RewindBuffer(); + + as.VFCVT_XU_F(v4, v8, VecMask::Yes); + REQUIRE(value == 0x48801257); +} + +TEST_CASE("VFNCVT.F.F.W", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFNCVT_F_F(v4, v8, VecMask::No); + REQUIRE(value == 0x4A8A1257); + + as.RewindBuffer(); + + as.VFNCVT_F_F(v4, v8, VecMask::Yes); + REQUIRE(value == 0x488A1257); +} + +TEST_CASE("VFNCVT.F.X.W", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFNCVT_F_X(v4, v8, VecMask::No); + REQUIRE(value == 0x4A899257); + + as.RewindBuffer(); + + as.VFNCVT_F_X(v4, v8, VecMask::Yes); + REQUIRE(value == 0x48899257); +} + +TEST_CASE("VFNCVT.F.XU.W", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFNCVT_F_XU(v4, v8, VecMask::No); + REQUIRE(value == 0x4A891257); + + as.RewindBuffer(); + + as.VFNCVT_F_XU(v4, v8, VecMask::Yes); + REQUIRE(value == 0x48891257); +} + +TEST_CASE("VFNCVT.ROD.F.F.W", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFNCVT_ROD_F_F(v4, v8, VecMask::No); + REQUIRE(value == 0x4A8A9257); + + as.RewindBuffer(); + + as.VFNCVT_ROD_F_F(v4, v8, VecMask::Yes); + REQUIRE(value == 0x488A9257); +} + +TEST_CASE("VFNCVT.RTZ.X.F.W", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFNCVT_RTZ_X_F(v4, v8, VecMask::No); + REQUIRE(value == 0x4A8B9257); + + as.RewindBuffer(); + + as.VFNCVT_RTZ_X_F(v4, v8, VecMask::Yes); + REQUIRE(value == 0x488B9257); +} + +TEST_CASE("VFNCVT.RTZ.XU.F.W", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFNCVT_RTZ_XU_F(v4, v8, VecMask::No); + REQUIRE(value == 0x4A8B1257); + + as.RewindBuffer(); + + as.VFNCVT_RTZ_XU_F(v4, v8, VecMask::Yes); + REQUIRE(value == 0x488B1257); +} + +TEST_CASE("VFNCVT.X.F.W", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFNCVT_X_F(v4, v8, VecMask::No); + REQUIRE(value == 0x4A889257); + + as.RewindBuffer(); + + as.VFNCVT_X_F(v4, v8, VecMask::Yes); + REQUIRE(value == 0x48889257); +} + +TEST_CASE("VFNCVT.XU.F.W", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFNCVT_XU_F(v4, v8, VecMask::No); + REQUIRE(value == 0x4A881257); + + as.RewindBuffer(); + + as.VFNCVT_XU_F(v4, v8, VecMask::Yes); + REQUIRE(value == 0x48881257); +} + +TEST_CASE("VFWCVT.F.F.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFWCVT_F_F(v4, v8, VecMask::No); + REQUIRE(value == 0x4A861257); + + as.RewindBuffer(); + + as.VFWCVT_F_F(v4, v8, VecMask::Yes); + REQUIRE(value == 0x48861257); +} + +TEST_CASE("VFWCVT.F.X.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFWCVT_F_X(v4, v8, VecMask::No); + REQUIRE(value == 0x4A859257); + + as.RewindBuffer(); + + as.VFWCVT_F_X(v4, v8, VecMask::Yes); + REQUIRE(value == 0x48859257); +} + +TEST_CASE("VFWCVT.F.XU.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFWCVT_F_XU(v4, v8, VecMask::No); + REQUIRE(value == 0x4A851257); + + as.RewindBuffer(); + + as.VFWCVT_F_XU(v4, v8, VecMask::Yes); + REQUIRE(value == 0x48851257); +} + +TEST_CASE("VFWCVT.RTZ.X.F.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFWCVT_RTZ_X_F(v4, v8, VecMask::No); + REQUIRE(value == 0x4A879257); + + as.RewindBuffer(); + + as.VFWCVT_RTZ_X_F(v4, v8, VecMask::Yes); + REQUIRE(value == 0x48879257); +} + +TEST_CASE("VFWCVT.RTZ.XU.F.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFWCVT_RTZ_XU_F(v4, v8, VecMask::No); + REQUIRE(value == 0x4A871257); + + as.RewindBuffer(); + + as.VFWCVT_RTZ_XU_F(v4, v8, VecMask::Yes); + REQUIRE(value == 0x48871257); +} + +TEST_CASE("VFWCVT.X.F.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFWCVT_X_F(v4, v8, VecMask::No); + REQUIRE(value == 0x4A849257); + + as.RewindBuffer(); + + as.VFWCVT_X_F(v4, v8, VecMask::Yes); + REQUIRE(value == 0x48849257); +} + +TEST_CASE("VFWCVT.XU.F.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFWCVT_XU_F(v4, v8, VecMask::No); + REQUIRE(value == 0x4A841257); + + as.RewindBuffer(); + + as.VFWCVT_XU_F(v4, v8, VecMask::Yes); + REQUIRE(value == 0x48841257); +} + +TEST_CASE("VFDIV.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFDIV(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x82861257); + + as.RewindBuffer(); + + as.VFDIV(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x80861257); +} + +TEST_CASE("VFDIV.VF", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFDIV(v4, v8, f12, VecMask::No); + REQUIRE(value == 0x82865257); + + as.RewindBuffer(); + + as.VFDIV(v4, v8, f12, VecMask::Yes); + REQUIRE(value == 0x80865257); +} + +TEST_CASE("VFRDIV.VF", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFRDIV(v4, v8, f12, VecMask::No); + REQUIRE(value == 0x86865257); + + as.RewindBuffer(); + + as.VFRDIV(v4, v8, f12, VecMask::Yes); + REQUIRE(value == 0x84865257); +} + +TEST_CASE("VFIRST.M", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFIRST(x10, v12, VecMask::No); + REQUIRE(value == 0x42C8A557); + + as.RewindBuffer(); + + as.VFIRST(x10, v12, VecMask::Yes); + REQUIRE(value == 0x40C8A557); +} + +TEST_CASE("VFREDMAX.VS", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFREDMAX(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x1E861257); + + as.RewindBuffer(); + + as.VFREDMAX(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x1C861257); +} + +TEST_CASE("VFREDMIN.VS", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFREDMIN(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x16861257); + + as.RewindBuffer(); + + as.VFREDMIN(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x14861257); +} + +TEST_CASE("VFREDSUM.VS", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFREDSUM(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x06861257); + + as.RewindBuffer(); + + as.VFREDSUM(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x04861257); +} + +TEST_CASE("VFREDOSUM.VS", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFREDOSUM(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x0E861257); + + as.RewindBuffer(); + + as.VFREDOSUM(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x0C861257); +} + +TEST_CASE("VFMACC.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFMACC(v4, v12, v8, VecMask::No); + REQUIRE(value == 0xB2861257); + + as.RewindBuffer(); + + as.VFMACC(v4, v12, v8, VecMask::Yes); + REQUIRE(value == 0xB0861257); +} + +TEST_CASE("VFMACC.VF", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFMACC(v4, f12, v8, VecMask::No); + REQUIRE(value == 0xB2865257); + + as.RewindBuffer(); + + as.VFMACC(v4, f12, v8, VecMask::Yes); + REQUIRE(value == 0xB0865257); +} + +TEST_CASE("VFMADD.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFMADD(v4, v12, v8, VecMask::No); + REQUIRE(value == 0xA2861257); + + as.RewindBuffer(); + + as.VFMADD(v4, v12, v8, VecMask::Yes); + REQUIRE(value == 0xA0861257); +} + +TEST_CASE("VFMADD.VF", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFMADD(v4, f12, v8, VecMask::No); + REQUIRE(value == 0xA2865257); + + as.RewindBuffer(); + + as.VFMADD(v4, f12, v8, VecMask::Yes); + REQUIRE(value == 0xA0865257); +} + +TEST_CASE("VFMAX.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFMAX(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x1A861257); + + as.RewindBuffer(); + + as.VFMAX(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x18861257); +} + +TEST_CASE("VFMAX.VF", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFMAX(v4, v8, f12, VecMask::No); + REQUIRE(value == 0x1A865257); + + as.RewindBuffer(); + + as.VFMAX(v4, v8, f12, VecMask::Yes); + REQUIRE(value == 0x18865257); +} + +TEST_CASE("VFMERGE.VFM", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFMERGE(v4, v8, f12); + REQUIRE(value == 0x5C865257); +} + +TEST_CASE("VFMIN.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFMIN(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x12861257); + + as.RewindBuffer(); + + as.VFMIN(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x10861257); +} + +TEST_CASE("VFMIN.VF", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFMIN(v4, v8, f12, VecMask::No); + REQUIRE(value == 0x12865257); + + as.RewindBuffer(); + + as.VFMIN(v4, v8, f12, VecMask::Yes); + REQUIRE(value == 0x10865257); +} + +TEST_CASE("VFMSAC.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFMSAC(v4, v12, v8, VecMask::No); + REQUIRE(value == 0xBA861257); + + as.RewindBuffer(); + + as.VFMSAC(v4, v12, v8, VecMask::Yes); + REQUIRE(value == 0xB8861257); +} + +TEST_CASE("VFMSAC.VF", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFMSAC(v4, f12, v8, VecMask::No); + REQUIRE(value == 0xBA865257); + + as.RewindBuffer(); + + as.VFMSAC(v4, f12, v8, VecMask::Yes); + REQUIRE(value == 0xB8865257); +} + +TEST_CASE("VFMSUB.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFMSUB(v4, v12, v8, VecMask::No); + REQUIRE(value == 0xAA861257); + + as.RewindBuffer(); + + as.VFMSUB(v4, v12, v8, VecMask::Yes); + REQUIRE(value == 0xA8861257); +} + +TEST_CASE("VFMSUB.VF", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFMSUB(v4, f12, v8, VecMask::No); + REQUIRE(value == 0xAA865257); + + as.RewindBuffer(); + + as.VFMSUB(v4, f12, v8, VecMask::Yes); + REQUIRE(value == 0xA8865257); +} + +TEST_CASE("VFMUL.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFMUL(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x92861257); + + as.RewindBuffer(); + + as.VFMUL(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x90861257); +} + +TEST_CASE("VFMUL.VF", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFMUL(v4, v8, f12, VecMask::No); + REQUIRE(value == 0x92865257); + + as.RewindBuffer(); + + as.VFMUL(v4, v8, f12, VecMask::Yes); + REQUIRE(value == 0x90865257); +} + +TEST_CASE("VFMV.F.S", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFMV_FS(f10, v8); + REQUIRE(value == 0x42801557); +} + +TEST_CASE("VFMV.S.F", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFMV_SF(v4, f11); + REQUIRE(value == 0x4205D257); +} + +TEST_CASE("VFMV.V.F", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFMV(v4, f11); + REQUIRE(value == 0x5E05D257); +} + +TEST_CASE("VFNMACC.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFNMACC(v4, v12, v8, VecMask::No); + REQUIRE(value == 0xB6861257); + + as.RewindBuffer(); + + as.VFNMACC(v4, v12, v8, VecMask::Yes); + REQUIRE(value == 0xB4861257); +} + +TEST_CASE("VFNMACC.VF", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFNMACC(v4, f12, v8, VecMask::No); + REQUIRE(value == 0xB6865257); + + as.RewindBuffer(); + + as.VFNMACC(v4, f12, v8, VecMask::Yes); + REQUIRE(value == 0xB4865257); +} + +TEST_CASE("VFNMADD.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFNMADD(v4, v12, v8, VecMask::No); + REQUIRE(value == 0xA6861257); + + as.RewindBuffer(); + + as.VFNMADD(v4, v12, v8, VecMask::Yes); + REQUIRE(value == 0xA4861257); +} + +TEST_CASE("VFNMADD.VF", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFNMADD(v4, f12, v8, VecMask::No); + REQUIRE(value == 0xA6865257); + + as.RewindBuffer(); + + as.VFNMADD(v4, f12, v8, VecMask::Yes); + REQUIRE(value == 0xA4865257); +} + +TEST_CASE("VFNMSAC.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFNMSAC(v4, v12, v8, VecMask::No); + REQUIRE(value == 0xBE861257); + + as.RewindBuffer(); + + as.VFNMSAC(v4, v12, v8, VecMask::Yes); + REQUIRE(value == 0xBC861257); +} + +TEST_CASE("VFNMSAC.VF", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFNMSAC(v4, f12, v8, VecMask::No); + REQUIRE(value == 0xBE865257); + + as.RewindBuffer(); + + as.VFNMSAC(v4, f12, v8, VecMask::Yes); + REQUIRE(value == 0xBC865257); +} + +TEST_CASE("VFNMSUB.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFNMSUB(v4, v12, v8, VecMask::No); + REQUIRE(value == 0xAE861257); + + as.RewindBuffer(); + + as.VFNMSUB(v4, v12, v8, VecMask::Yes); + REQUIRE(value == 0xAC861257); +} + +TEST_CASE("VFNMSUB.VF", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFNMSUB(v4, f12, v8, VecMask::No); + REQUIRE(value == 0xAE865257); + + as.RewindBuffer(); + + as.VFNMSUB(v4, f12, v8, VecMask::Yes); + REQUIRE(value == 0xAC865257); +} + +TEST_CASE("VFREC7.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFREC7(v4, v8, VecMask::No); + REQUIRE(value == 0x4E829257); + + as.RewindBuffer(); + + as.VFREC7(v4, v8, VecMask::Yes); + REQUIRE(value == 0x4C829257); +} + +TEST_CASE("VFSGNJ.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFSGNJ(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x22861257); + + as.RewindBuffer(); + + as.VFSGNJ(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x20861257); +} + +TEST_CASE("VFSGNJ.VF", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFSGNJ(v4, v8, f12, VecMask::No); + REQUIRE(value == 0x22865257); + + as.RewindBuffer(); + + as.VFSGNJ(v4, v8, f12, VecMask::Yes); + REQUIRE(value == 0x20865257); +} + +TEST_CASE("VFSGNJN.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFSGNJN(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x26861257); + + as.RewindBuffer(); + + as.VFSGNJN(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x24861257); +} + +TEST_CASE("VFSGNJN.VF", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFSGNJN(v4, v8, f12, VecMask::No); + REQUIRE(value == 0x26865257); + + as.RewindBuffer(); + + as.VFSGNJN(v4, v8, f12, VecMask::Yes); + REQUIRE(value == 0x24865257); +} + +TEST_CASE("VFSGNJX.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFSGNJX(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x2A861257); + + as.RewindBuffer(); + + as.VFSGNJX(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x28861257); +} + +TEST_CASE("VFSGNJX.VF", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFSGNJX(v4, v8, f12, VecMask::No); + REQUIRE(value == 0x2A865257); + + as.RewindBuffer(); + + as.VFSGNJX(v4, v8, f12, VecMask::Yes); + REQUIRE(value == 0x28865257); +} + +TEST_CASE("VFSQRT.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFSQRT(v4, v8, VecMask::No); + REQUIRE(value == 0x4E801257); + + as.RewindBuffer(); + + as.VFSQRT(v4, v8, VecMask::Yes); + REQUIRE(value == 0x4C801257); +} + +TEST_CASE("VFRSQRT7.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFRSQRT7(v4, v8, VecMask::No); + REQUIRE(value == 0x4E821257); + + as.RewindBuffer(); + + as.VFRSQRT7(v4, v8, VecMask::Yes); + REQUIRE(value == 0x4C821257); +} + +TEST_CASE("VFSLIDE1DOWN.VF", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFSLIDE1DOWN(v4, v8, f12, VecMask::No); + REQUIRE(value == 0x3E865257); + + as.RewindBuffer(); + + as.VFSLIDE1DOWN(v4, v8, f12, VecMask::Yes); + REQUIRE(value == 0x3C865257); +} + +TEST_CASE("VFSLIDE1UP.VF", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFSLIDE1UP(v4, v8, f12, VecMask::No); + REQUIRE(value == 0x3A865257); + + as.RewindBuffer(); + + as.VFSLIDE1UP(v4, v8, f12, VecMask::Yes); + REQUIRE(value == 0x38865257); +} + +TEST_CASE("VFSUB.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFSUB(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x0A861257); + + as.RewindBuffer(); + + as.VFSUB(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x08861257); +} + +TEST_CASE("VFSUB.VF", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFSUB(v4, v8, f12, VecMask::No); + REQUIRE(value == 0x0A865257); + + as.RewindBuffer(); + + as.VFSUB(v4, v8, f12, VecMask::Yes); + REQUIRE(value == 0x08865257); +} + +TEST_CASE("VFRSUB.VF", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFRSUB(v4, v8, f12, VecMask::No); + REQUIRE(value == 0x9E865257); + + as.RewindBuffer(); + + as.VFRSUB(v4, v8, f12, VecMask::Yes); + REQUIRE(value == 0x9C865257); +} + +TEST_CASE("VFWADD.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFWADD(v4, v8, v12, VecMask::No); + REQUIRE(value == 0xC2861257); + + as.RewindBuffer(); + + as.VFWADD(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0xC0861257); +} + +TEST_CASE("VFWADD.VF", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFWADD(v4, v8, f12, VecMask::No); + REQUIRE(value == 0xC2865257); + + as.RewindBuffer(); + + as.VFWADD(v4, v8, f12, VecMask::Yes); + REQUIRE(value == 0xC0865257); +} + +TEST_CASE("VFWADD.WV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFWADDW(v4, v8, v12, VecMask::No); + REQUIRE(value == 0xD2861257); + + as.RewindBuffer(); + + as.VFWADDW(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0xD0861257); +} + +TEST_CASE("VFWADD.WF", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFWADDW(v4, v8, f12, VecMask::No); + REQUIRE(value == 0xD2865257); + + as.RewindBuffer(); + + as.VFWADDW(v4, v8, f12, VecMask::Yes); + REQUIRE(value == 0xD0865257); +} + +TEST_CASE("VFWMACC.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFWMACC(v4, v12, v8, VecMask::No); + REQUIRE(value == 0xF2861257); + + as.RewindBuffer(); + + as.VFWMACC(v4, v12, v8, VecMask::Yes); + REQUIRE(value == 0xF0861257); +} + +TEST_CASE("VFWMACC.VF", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFWMACC(v4, f12, v8, VecMask::No); + REQUIRE(value == 0xF2865257); + + as.RewindBuffer(); + + as.VFWMACC(v4, f12, v8, VecMask::Yes); + REQUIRE(value == 0xF0865257); +} + +TEST_CASE("VFWMUL.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFWMUL(v4, v8, v12, VecMask::No); + REQUIRE(value == 0xE2861257); + + as.RewindBuffer(); + + as.VFWMUL(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0xE0861257); +} + +TEST_CASE("VFWMUL.VF", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFWMUL(v4, v8, f12, VecMask::No); + REQUIRE(value == 0xE2865257); + + as.RewindBuffer(); + + as.VFWMUL(v4, v8, f12, VecMask::Yes); + REQUIRE(value == 0xE0865257); +} + +TEST_CASE("VFWNMACC.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFWNMACC(v4, v12, v8, VecMask::No); + REQUIRE(value == 0xF6861257); + + as.RewindBuffer(); + + as.VFWNMACC(v4, v12, v8, VecMask::Yes); + REQUIRE(value == 0xF4861257); +} + +TEST_CASE("VFWNMACC.VF", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFWNMACC(v4, f12, v8, VecMask::No); + REQUIRE(value == 0xF6865257); + + as.RewindBuffer(); + + as.VFWNMACC(v4, f12, v8, VecMask::Yes); + REQUIRE(value == 0xF4865257); +} + +TEST_CASE("VFWNMSAC.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFWNMSAC(v4, v12, v8, VecMask::No); + REQUIRE(value == 0xFE861257); + + as.RewindBuffer(); + + as.VFWNMSAC(v4, v12, v8, VecMask::Yes); + REQUIRE(value == 0xFC861257); +} + +TEST_CASE("VFWNMSAC.VF", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFWNMSAC(v4, f12, v8, VecMask::No); + REQUIRE(value == 0xFE865257); + + as.RewindBuffer(); + + as.VFWNMSAC(v4, f12, v8, VecMask::Yes); + REQUIRE(value == 0xFC865257); +} + +TEST_CASE("VFWREDSUM.VS", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFWREDSUM(v4, v8, v12, VecMask::No); + REQUIRE(value == 0xC6861257); + + as.RewindBuffer(); + + as.VFWREDSUM(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0xC4861257); +} + +TEST_CASE("VFWREDOSUM.VS", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFWREDOSUM(v4, v8, v12, VecMask::No); + REQUIRE(value == 0xCE861257); + + as.RewindBuffer(); + + as.VFWREDOSUM(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0xCC861257); +} + +TEST_CASE("VFWMSAC.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFWMSAC(v4, v12, v8, VecMask::No); + REQUIRE(value == 0xFA861257); + + as.RewindBuffer(); + + as.VFWMSAC(v4, v12, v8, VecMask::Yes); + REQUIRE(value == 0xF8861257); +} + +TEST_CASE("VFWMSAC.VF", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFWMSAC(v4, f12, v8, VecMask::No); + REQUIRE(value == 0xFA865257); + + as.RewindBuffer(); + + as.VFWMSAC(v4, f12, v8, VecMask::Yes); + REQUIRE(value == 0xF8865257); +} + +TEST_CASE("VFWSUB.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFWSUB(v4, v8, v12, VecMask::No); + REQUIRE(value == 0xCA861257); + + as.RewindBuffer(); + + as.VFWSUB(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0xC8861257); +} + +TEST_CASE("VFWSUB.VF", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFWSUB(v4, v8, f12, VecMask::No); + REQUIRE(value == 0xCA865257); + + as.RewindBuffer(); + + as.VFWSUB(v4, v8, f12, VecMask::Yes); + REQUIRE(value == 0xC8865257); +} + +TEST_CASE("VFWSUB.WV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFWSUBW(v4, v8, v12, VecMask::No); + REQUIRE(value == 0xDA861257); + + as.RewindBuffer(); + + as.VFWSUBW(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0xD8861257); +} + +TEST_CASE("VFWSUB.WF", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VFWSUBW(v4, v8, f12, VecMask::No); + REQUIRE(value == 0xDA865257); + + as.RewindBuffer(); + + as.VFWSUBW(v4, v8, f12, VecMask::Yes); + REQUIRE(value == 0xD8865257); +} + +TEST_CASE("VID.M", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VID(v4, VecMask::No); + REQUIRE(value == 0x5208A257); + + as.RewindBuffer(); + + as.VID(v4, VecMask::Yes); + REQUIRE(value == 0x5008A257); +} + +TEST_CASE("VIOTA.M", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VIOTA(v4, v8, VecMask::No); + REQUIRE(value == 0x52882257); + + as.RewindBuffer(); + + as.VIOTA(v4, v8, VecMask::Yes); + REQUIRE(value == 0x50882257); +} + +TEST_CASE("VMACC.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMACC(v4, v12, v8, VecMask::No); + REQUIRE(value == 0xB6862257); + + as.RewindBuffer(); + + as.VMACC(v4, v12, v8, VecMask::Yes); + REQUIRE(value == 0xB4862257); +} + +TEST_CASE("VMACC.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMACC(v4, x11, v8, VecMask::No); + REQUIRE(value == 0xB685E257); + + as.RewindBuffer(); + + as.VMACC(v4, x11, v8, VecMask::Yes); + REQUIRE(value == 0xB485E257); +} + +TEST_CASE("VMADC.VV(M)", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMADC(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x46860257); + + as.RewindBuffer(); + + as.VMADC(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x44860257); +} + +TEST_CASE("VMADC.VX(M)", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMADC(v4, v8, x11, VecMask::No); + REQUIRE(value == 0x4685C257); + + as.RewindBuffer(); + + as.VMADC(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0x4485C257); +} + +TEST_CASE("VMADC.VI(M)", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMADC(v4, v8, 15, VecMask::No); + REQUIRE(value == 0x4687B257); + + as.RewindBuffer(); + + as.VMADC(v4, v8, -16, VecMask::No); + REQUIRE(value == 0x46883257); + + as.RewindBuffer(); + + as.VMADC(v4, v8, 15, VecMask::Yes); + REQUIRE(value == 0x4487B257); + + as.RewindBuffer(); + + as.VMADC(v4, v8, -16, VecMask::Yes); + REQUIRE(value == 0x44883257); +} + +TEST_CASE("VMADD.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMADD(v4, v12, v8, VecMask::No); + REQUIRE(value == 0xA6862257); + + as.RewindBuffer(); + + as.VMADD(v4, v12, v8, VecMask::Yes); + REQUIRE(value == 0xA4862257); +} + +TEST_CASE("VMADD.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMADD(v4, x11, v8, VecMask::No); + REQUIRE(value == 0xA685E257); + + as.RewindBuffer(); + + as.VMADD(v4, x11, v8, VecMask::Yes); + REQUIRE(value == 0xA485E257); +} + +TEST_CASE("VMAND.MM", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMAND(v4, v8, v12); + REQUIRE(value == 0x66862257); +} + +TEST_CASE("VMANDNOT.MM", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMANDNOT(v4, v8, v12); + REQUIRE(value == 0x62862257); +} + +TEST_CASE("VMFEQ.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMFEQ(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x62861257); + + as.RewindBuffer(); + + as.VMFEQ(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x60861257); +} + +TEST_CASE("VMFEQ.VF", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMFEQ(v4, v8, f12, VecMask::No); + REQUIRE(value == 0x62865257); + + as.RewindBuffer(); + + as.VMFEQ(v4, v8, f12, VecMask::Yes); + REQUIRE(value == 0x60865257); +} + +TEST_CASE("VMFGE.VF", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMFGE(v4, v8, f12, VecMask::No); + REQUIRE(value == 0x7E865257); + + as.RewindBuffer(); + + as.VMFGE(v4, v8, f12, VecMask::Yes); + REQUIRE(value == 0x7C865257); +} + +TEST_CASE("VMFGT.VF", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMFGT(v4, v8, f12, VecMask::No); + REQUIRE(value == 0x76865257); + + as.RewindBuffer(); + + as.VMFGT(v4, v8, f12, VecMask::Yes); + REQUIRE(value == 0x74865257); +} + +TEST_CASE("VMFLE.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMFLE(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x66861257); + + as.RewindBuffer(); + + as.VMFLE(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x64861257); +} + +TEST_CASE("VMFLE.VF", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMFLE(v4, v8, f12, VecMask::No); + REQUIRE(value == 0x66865257); + + as.RewindBuffer(); + + as.VMFLE(v4, v8, f12, VecMask::Yes); + REQUIRE(value == 0x64865257); +} + +TEST_CASE("VMFLT.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMFLT(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x6E861257); + + as.RewindBuffer(); + + as.VMFLT(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x6C861257); +} + +TEST_CASE("VMFLT.VF", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMFLT(v4, v8, f12, VecMask::No); + REQUIRE(value == 0x6E865257); + + as.RewindBuffer(); + + as.VMFLT(v4, v8, f12, VecMask::Yes); + REQUIRE(value == 0x6C865257); +} + +TEST_CASE("VMFNE.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMFNE(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x72861257); + + as.RewindBuffer(); + + as.VMFNE(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x70861257); +} + +TEST_CASE("VMFNE.VF", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMFNE(v4, v8, f12, VecMask::No); + REQUIRE(value == 0x72865257); + + as.RewindBuffer(); + + as.VMFNE(v4, v8, f12, VecMask::Yes); + REQUIRE(value == 0x70865257); +} + +TEST_CASE("VMNAND.MM", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMNAND(v4, v8, v12); + REQUIRE(value == 0x76862257); +} + +TEST_CASE("VMNOR.MM", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMNOR(v4, v8, v12); + REQUIRE(value == 0x7A862257); +} + +TEST_CASE("VMOR.MM", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMOR(v4, v8, v12); + REQUIRE(value == 0x6A862257); +} + +TEST_CASE("VMORNOT.MM", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMORNOT(v4, v8, v12); + REQUIRE(value == 0x72862257); +} + +TEST_CASE("VMXNOR.MM", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMXNOR(v4, v8, v12); + REQUIRE(value == 0x7E862257); +} + +TEST_CASE("VMXOR.MM", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMXOR(v4, v8, v12); + REQUIRE(value == 0x6E862257); +} + +TEST_CASE("VMAX.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMAX(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x1E860257); + + as.RewindBuffer(); + + as.VMAX(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x1C860257); +} + +TEST_CASE("VMAX.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMAX(v4, v8, x11, VecMask::No); + REQUIRE(value == 0x1E85C257); + + as.RewindBuffer(); + + as.VMAX(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0x1C85C257); +} + +TEST_CASE("VMAXU.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMAXU(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x1A860257); + + as.RewindBuffer(); + + as.VMAXU(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x18860257); +} + +TEST_CASE("VMAXU.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMAXU(v4, v8, x11, VecMask::No); + REQUIRE(value == 0x1A85C257); + + as.RewindBuffer(); + + as.VMAXU(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0x1885C257); +} + +TEST_CASE("VMERGE.VVM", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMERGE(v4, v8, v12); + REQUIRE(value == 0x5C860257); +} + +TEST_CASE("VMERGE.VXM", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMERGE(v4, v8, x11); + REQUIRE(value == 0x5C85C257); +} + +TEST_CASE("VMERGE.VIM", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMERGE(v4, v8, 15); + REQUIRE(value == 0x5C87B257); + + as.RewindBuffer(); + + as.VMERGE(v4, v8, -16); + REQUIRE(value == 0x5C883257); +} + +TEST_CASE("VMIN.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMIN(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x16860257); + + as.RewindBuffer(); + + as.VMIN(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x14860257); +} + +TEST_CASE("VMIN.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMIN(v4, v8, x11, VecMask::No); + REQUIRE(value == 0x1685C257); + + as.RewindBuffer(); + + as.VMIN(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0x1485C257); +} + +TEST_CASE("VMINU.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMINU(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x12860257); + + as.RewindBuffer(); + + as.VMINU(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x10860257); +} + +TEST_CASE("VMINU.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMINU(v4, v8, x11, VecMask::No); + REQUIRE(value == 0x1285C257); + + as.RewindBuffer(); + + as.VMINU(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0x1085C257); +} + +TEST_CASE("VMSBC.VV(M)", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMSBC(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x4E860257); + + as.RewindBuffer(); + + as.VMSBC(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x4C860257); +} + +TEST_CASE("VMSBC.VX(M)", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMSBC(v4, v8, x11, VecMask::No); + REQUIRE(value == 0x4E85C257); + + as.RewindBuffer(); + + as.VMSBC(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0x4C85C257); +} + +TEST_CASE("VMSBF.M", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMSBF(v4, v8, VecMask::No); + REQUIRE(value == 0x5280A257); + + as.RewindBuffer(); + + as.VMSBF(v4, v8, VecMask::Yes); + REQUIRE(value == 0x5080A257); +} + +TEST_CASE("VMSIF.M", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMSIF(v4, v8, VecMask::No); + REQUIRE(value == 0x5281A257); + + as.RewindBuffer(); + + as.VMSIF(v4, v8, VecMask::Yes); + REQUIRE(value == 0x5081A257); +} + +TEST_CASE("VMSOF.M", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMSOF(v4, v8, VecMask::No); + REQUIRE(value == 0x52812257); + + as.RewindBuffer(); + + as.VMSOF(v4, v8, VecMask::Yes); + REQUIRE(value == 0x50812257); +} + +TEST_CASE("VMSEQ.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMSEQ(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x62860257); + + as.RewindBuffer(); + + as.VMSEQ(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x60860257); +} + +TEST_CASE("VMSEQ.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMSEQ(v4, v8, x11, VecMask::No); + REQUIRE(value == 0x6285C257); + + as.RewindBuffer(); + + as.VMSEQ(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0x6085C257); +} + +TEST_CASE("VMSEQ.VI", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMSEQ(v4, v8, 15, VecMask::No); + REQUIRE(value == 0x6287B257); + + as.RewindBuffer(); + + as.VMSEQ(v4, v8, -16, VecMask::No); + REQUIRE(value == 0x62883257); + + as.RewindBuffer(); + + as.VMSEQ(v4, v8, 15, VecMask::Yes); + REQUIRE(value == 0x6087B257); + + as.RewindBuffer(); + + as.VMSEQ(v4, v8, -16, VecMask::Yes); + REQUIRE(value == 0x60883257); +} + +TEST_CASE("VMSGT.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMSGT(v4, v8, x11, VecMask::No); + REQUIRE(value == 0x7E85C257); + + as.RewindBuffer(); + + as.VMSGT(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0x7C85C257); +} + +TEST_CASE("VMSGT.VI", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMSGT(v4, v8, 15, VecMask::No); + REQUIRE(value == 0x7E87B257); + + as.RewindBuffer(); + + as.VMSGT(v4, v8, -16, VecMask::No); + REQUIRE(value == 0x7E883257); + + as.RewindBuffer(); + + as.VMSGT(v4, v8, 15, VecMask::Yes); + REQUIRE(value == 0x7C87B257); + + as.RewindBuffer(); + + as.VMSGT(v4, v8, -16, VecMask::Yes); + REQUIRE(value == 0x7C883257); +} + +TEST_CASE("VMSGTU.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMSGTU(v4, v8, x11, VecMask::No); + REQUIRE(value == 0x7A85C257); + + as.RewindBuffer(); + + as.VMSGTU(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0x7885C257); +} + +TEST_CASE("VMSGTU.VI", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMSGTU(v4, v8, 15, VecMask::No); + REQUIRE(value == 0x7A87B257); + + as.RewindBuffer(); + + as.VMSGTU(v4, v8, -16, VecMask::No); + REQUIRE(value == 0x7A883257); + + as.RewindBuffer(); + + as.VMSGTU(v4, v8, 15, VecMask::Yes); + REQUIRE(value == 0x7887B257); + + as.RewindBuffer(); + + as.VMSGTU(v4, v8, -16, VecMask::Yes); + REQUIRE(value == 0x78883257); +} + +TEST_CASE("VMSLE.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMSLE(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x76860257); + + as.RewindBuffer(); + + as.VMSLE(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x74860257); +} + +TEST_CASE("VMSLE.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMSLE(v4, v8, x11, VecMask::No); + REQUIRE(value == 0x7685C257); + + as.RewindBuffer(); + + as.VMSLE(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0x7485C257); +} + +TEST_CASE("VMSLE.VI", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMSLE(v4, v8, 15, VecMask::No); + REQUIRE(value == 0x7687B257); + + as.RewindBuffer(); + + as.VMSLE(v4, v8, -16, VecMask::No); + REQUIRE(value == 0x76883257); + + as.RewindBuffer(); + + as.VMSLE(v4, v8, 15, VecMask::Yes); + REQUIRE(value == 0x7487B257); + + as.RewindBuffer(); + + as.VMSLE(v4, v8, -16, VecMask::Yes); + REQUIRE(value == 0x74883257); +} + +TEST_CASE("VMSLEU.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMSLEU(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x72860257); + + as.RewindBuffer(); + + as.VMSLEU(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x70860257); +} + +TEST_CASE("VMSLEU.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMSLEU(v4, v8, x11, VecMask::No); + REQUIRE(value == 0x7285C257); + + as.RewindBuffer(); + + as.VMSLEU(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0x7085C257); +} + +TEST_CASE("VMSLEU.VI", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMSLEU(v4, v8, 15, VecMask::No); + REQUIRE(value == 0x7287B257); + + as.RewindBuffer(); + + as.VMSLEU(v4, v8, -16, VecMask::No); + REQUIRE(value == 0x72883257); + + as.RewindBuffer(); + + as.VMSLEU(v4, v8, 15, VecMask::Yes); + REQUIRE(value == 0x7087B257); + + as.RewindBuffer(); + + as.VMSLEU(v4, v8, -16, VecMask::Yes); + REQUIRE(value == 0x70883257); +} + +TEST_CASE("VMSLT.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMSLT(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x6E860257); + + as.RewindBuffer(); + + as.VMSLT(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x6C860257); +} + +TEST_CASE("VMSLT.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMSLT(v4, v8, x11, VecMask::No); + REQUIRE(value == 0x6E85C257); + + as.RewindBuffer(); + + as.VMSLT(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0x6C85C257); +} + +TEST_CASE("VMSLTU.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMSLTU(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x6A860257); + + as.RewindBuffer(); + + as.VMSLTU(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x68860257); +} + +TEST_CASE("VMSLTU.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMSLTU(v4, v8, x11, VecMask::No); + REQUIRE(value == 0x6A85C257); + + as.RewindBuffer(); + + as.VMSLTU(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0x6885C257); +} + +TEST_CASE("VMSNE.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMSNE(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x66860257); + + as.RewindBuffer(); + + as.VMSNE(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x64860257); +} + +TEST_CASE("VMSNE.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMSNE(v4, v8, x11, VecMask::No); + REQUIRE(value == 0x6685C257); + + as.RewindBuffer(); + + as.VMSNE(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0x6485C257); +} + +TEST_CASE("VMSNE.VI", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMSNE(v4, v8, 15, VecMask::No); + REQUIRE(value == 0x6687B257); + + as.RewindBuffer(); + + as.VMSNE(v4, v8, -16, VecMask::No); + REQUIRE(value == 0x66883257); + + as.RewindBuffer(); + + as.VMSNE(v4, v8, 15, VecMask::Yes); + REQUIRE(value == 0x6487B257); + + as.RewindBuffer(); + + as.VMSNE(v4, v8, -16, VecMask::Yes); + REQUIRE(value == 0x64883257); +} + +TEST_CASE("VMUL.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMUL(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x96862257); + + as.RewindBuffer(); + + as.VMUL(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x94862257); +} + +TEST_CASE("VMUL.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMUL(v4, v8, x11, VecMask::No); + REQUIRE(value == 0x9685E257); + + as.RewindBuffer(); + + as.VMUL(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0x9485E257); +} + +TEST_CASE("VMULH.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMULH(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x9E862257); + + as.RewindBuffer(); + + as.VMULH(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x9C862257); +} + +TEST_CASE("VMULH.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMULH(v4, v8, x11, VecMask::No); + REQUIRE(value == 0x9E85E257); + + as.RewindBuffer(); + + as.VMULH(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0x9C85E257); +} + +TEST_CASE("VMULHSU.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMULHSU(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x9A862257); + + as.RewindBuffer(); + + as.VMULHSU(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x98862257); +} + +TEST_CASE("VMULHSU.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMULHSU(v4, v8, x11, VecMask::No); + REQUIRE(value == 0x9A85E257); + + as.RewindBuffer(); + + as.VMULHSU(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0x9885E257); +} + +TEST_CASE("VMULHU.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMULHU(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x92862257); + + as.RewindBuffer(); + + as.VMULHU(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x90862257); +} + +TEST_CASE("VMULHU.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMULHU(v4, v8, x11, VecMask::No); + REQUIRE(value == 0x9285E257); + + as.RewindBuffer(); + + as.VMULHU(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0x9085E257); +} + +TEST_CASE("VMV.S.X", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMV_SX(v4, x10); + REQUIRE(value == 0x42056257); +} + +TEST_CASE("VMV.X.S", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMV_XS(x10, v12); + REQUIRE(value == 0x42C02557); +} + +TEST_CASE("VMV.V.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMV(v8, v12); + REQUIRE(value == 0x5E060457); +} + +TEST_CASE("VMV.V.X", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMV(v8, x11); + REQUIRE(value == 0x5E05C457); +} + +TEST_CASE("VMV.V.I", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMV(v8, 15); + REQUIRE(value == 0x5E07B457); + + as.RewindBuffer(); + + as.VMV(v8, -16); + REQUIRE(value == 0x5E083457); +} + +TEST_CASE("VMV1R.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMV1R(v1, v2); + REQUIRE(value == 0x9E2030D7); +} + +TEST_CASE("VMV2R.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMV2R(v2, v4); + REQUIRE(value == 0x9E40B157); +} + +TEST_CASE("VMV4R.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMV4R(v4, v8); + REQUIRE(value == 0x9E81B257); +} + +TEST_CASE("VMV8R.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VMV8R(v0, v8); + REQUIRE(value == 0x9E83B057); +} + +TEST_CASE("VNCLIP.WV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VNCLIP(v4, v8, v12, VecMask::No); + REQUIRE(value == 0xBE860257); + + as.RewindBuffer(); + + as.VNCLIP(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0xBC860257); +} + +TEST_CASE("VNCLIP.WX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VNCLIP(v4, v8, x11, VecMask::No); + REQUIRE(value == 0xBE85C257); + + as.RewindBuffer(); + + as.VNCLIP(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0xBC85C257); +} + +TEST_CASE("VNCLIP.WI", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VNCLIP(v4, v8, 1, VecMask::No); + REQUIRE(value == 0xBE80B257); + + as.RewindBuffer(); + + as.VNCLIP(v4, v8, 31, VecMask::No); + REQUIRE(value == 0xBE8FB257); + + as.RewindBuffer(); + + as.VNCLIP(v4, v8, 1, VecMask::Yes); + REQUIRE(value == 0xBC80B257); + + as.RewindBuffer(); + + as.VNCLIP(v4, v8, 31, VecMask::Yes); + REQUIRE(value == 0xBC8FB257); +} + +TEST_CASE("VNCLIPU.WV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VNCLIPU(v4, v8, v12, VecMask::No); + REQUIRE(value == 0xBA860257); + + as.RewindBuffer(); + + as.VNCLIPU(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0xB8860257); +} + +TEST_CASE("VNCLIPU.WX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VNCLIPU(v4, v8, x11, VecMask::No); + REQUIRE(value == 0xBA85C257); + + as.RewindBuffer(); + + as.VNCLIPU(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0xB885C257); +} + +TEST_CASE("VNCLIPU.WI", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VNCLIPU(v4, v8, 1, VecMask::No); + REQUIRE(value == 0xBA80B257); + + as.RewindBuffer(); + + as.VNCLIPU(v4, v8, 31, VecMask::No); + REQUIRE(value == 0xBA8FB257); + + as.RewindBuffer(); + + as.VNCLIPU(v4, v8, 1, VecMask::Yes); + REQUIRE(value == 0xB880B257); + + as.RewindBuffer(); + + as.VNCLIPU(v4, v8, 31, VecMask::Yes); + REQUIRE(value == 0xB88FB257); +} + +TEST_CASE("VNMSAC.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VNMSAC(v4, v12, v8, VecMask::No); + REQUIRE(value == 0xBE862257); + + as.RewindBuffer(); + + as.VNMSAC(v4, v12, v8, VecMask::Yes); + REQUIRE(value == 0xBC862257); +} + +TEST_CASE("VNMSAC.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VNMSAC(v4, x11, v8, VecMask::No); + REQUIRE(value == 0xBE85E257); + + as.RewindBuffer(); + + as.VNMSAC(v4, x11, v8, VecMask::Yes); + REQUIRE(value == 0xBC85E257); +} + +TEST_CASE("VNMSUB.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VNMSUB(v4, v12, v8, VecMask::No); + REQUIRE(value == 0xAE862257); + + as.RewindBuffer(); + + as.VNMSUB(v4, v12, v8, VecMask::Yes); + REQUIRE(value == 0xAC862257); +} + +TEST_CASE("VNMSUB.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VNMSUB(v4, x11, v8, VecMask::No); + REQUIRE(value == 0xAE85E257); + + as.RewindBuffer(); + + as.VNMSUB(v4, x11, v8, VecMask::Yes); + REQUIRE(value == 0xAC85E257); +} + +TEST_CASE("VNSRA.WV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VNSRA(v4, v8, v12, VecMask::No); + REQUIRE(value == 0xB6860257); + + as.RewindBuffer(); + + as.VNSRA(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0xB4860257); +} + +TEST_CASE("VNSRA.WX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VNSRA(v4, v8, x11, VecMask::No); + REQUIRE(value == 0xB685C257); + + as.RewindBuffer(); + + as.VNSRA(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0xB485C257); +} + +TEST_CASE("VNSRA.WI", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VNSRA(v4, v8, 1, VecMask::No); + REQUIRE(value == 0xB680B257); + + as.RewindBuffer(); + + as.VNSRA(v4, v8, 31, VecMask::No); + REQUIRE(value == 0xB68FB257); + + as.RewindBuffer(); + + as.VNSRA(v4, v8, 1, VecMask::Yes); + REQUIRE(value == 0xB480B257); + + as.RewindBuffer(); + + as.VNSRA(v4, v8, 31, VecMask::Yes); + REQUIRE(value == 0xB48FB257); +} + +TEST_CASE("VNSRL.WV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VNSRL(v4, v8, v12, VecMask::No); + REQUIRE(value == 0xB2860257); + + as.RewindBuffer(); + + as.VNSRL(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0xB0860257); +} + +TEST_CASE("VNSRL.WX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VNSRL(v4, v8, x11, VecMask::No); + REQUIRE(value == 0xB285C257); + + as.RewindBuffer(); + + as.VNSRL(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0xB085C257); +} + +TEST_CASE("VNSRL.WI", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VNSRL(v4, v8, 1, VecMask::No); + REQUIRE(value == 0xB280B257); + + as.RewindBuffer(); + + as.VNSRL(v4, v8, 31, VecMask::No); + REQUIRE(value == 0xB28FB257); + + as.RewindBuffer(); + + as.VNSRL(v4, v8, 1, VecMask::Yes); + REQUIRE(value == 0xB080B257); + + as.RewindBuffer(); + + as.VNSRL(v4, v8, 31, VecMask::Yes); + REQUIRE(value == 0xB08FB257); +} + +TEST_CASE("VOR.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VOR(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x2A860257); + + as.RewindBuffer(); + + as.VOR(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x28860257); +} + +TEST_CASE("VOR.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VOR(v4, v8, x11, VecMask::No); + REQUIRE(value == 0x2A85C257); + + as.RewindBuffer(); + + as.VOR(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0x2885C257); +} + +TEST_CASE("VOR.VI", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VOR(v4, v8, 15, VecMask::No); + REQUIRE(value == 0x2A87B257); + + as.RewindBuffer(); + + as.VOR(v4, v8, -16, VecMask::No); + REQUIRE(value == 0x2A883257); + + as.RewindBuffer(); + + as.VOR(v4, v8, 15, VecMask::Yes); + REQUIRE(value == 0x2887B257); + + as.RewindBuffer(); + + as.VOR(v4, v8, -16, VecMask::Yes); + REQUIRE(value == 0x28883257); +} + +TEST_CASE("VPOPC.M", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VPOPC(x10, v12, VecMask::No); + REQUIRE(value == 0x42C82557); + + as.RewindBuffer(); + + as.VPOPC(x10, v12, VecMask::Yes); + REQUIRE(value == 0x40C82557); +} + +TEST_CASE("VREDAND.VS", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VREDAND(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x06862257); + + as.RewindBuffer(); + + as.VREDAND(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x04862257); +} + +TEST_CASE("VREDMAX.VS", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VREDMAX(v4, v8, v8, VecMask::No); + REQUIRE(value == 0x1E842257); + + as.RewindBuffer(); + + as.VREDMAX(v4, v8, v8, VecMask::Yes); + REQUIRE(value == 0x1C842257); +} + +TEST_CASE("VREDMAXU.VS", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VREDMAXU(v4, v8, v8, VecMask::No); + REQUIRE(value == 0x1A842257); + + as.RewindBuffer(); + + as.VREDMAXU(v4, v8, v8, VecMask::Yes); + REQUIRE(value == 0x18842257); +} + +TEST_CASE("VREDMIN.VS", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VREDMIN(v4, v8, v8, VecMask::No); + REQUIRE(value == 0x16842257); + + as.RewindBuffer(); + + as.VREDMIN(v4, v8, v8, VecMask::Yes); + REQUIRE(value == 0x14842257); +} + +TEST_CASE("VREDMINU.VS", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VREDMINU(v4, v8, v8, VecMask::No); + REQUIRE(value == 0x12842257); + + as.RewindBuffer(); + + as.VREDMINU(v4, v8, v8, VecMask::Yes); + REQUIRE(value == 0x10842257); +} + +TEST_CASE("VREDOR.VS", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VREDOR(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x0A862257); + + as.RewindBuffer(); + + as.VREDOR(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x08862257); +} + +TEST_CASE("VREDSUM.VS", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VREDSUM(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x02862257); + + as.RewindBuffer(); + + as.VREDSUM(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x00862257); +} + +TEST_CASE("VREDXOR.VS", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VREDXOR(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x0E862257); + + as.RewindBuffer(); + + as.VREDXOR(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x0C862257); +} + +TEST_CASE("VREM.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VREM(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x8E862257); + + as.RewindBuffer(); + + as.VREM(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x8C862257); +} + +TEST_CASE("VREM.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VREM(v4, v8, x11, VecMask::No); + REQUIRE(value == 0x8E85E257); + + as.RewindBuffer(); + + as.VREM(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0x8C85E257); +} + +TEST_CASE("VREMU.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VREMU(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x8A862257); + + as.RewindBuffer(); + + as.VREMU(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x88862257); +} + +TEST_CASE("VREMU.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VREMU(v4, v8, x11, VecMask::No); + REQUIRE(value == 0x8A85E257); + + as.RewindBuffer(); + + as.VREMU(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0x8885E257); +} + +TEST_CASE("VRGATHER.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VRGATHER(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x32860257); + + as.RewindBuffer(); + + as.VRGATHER(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x30860257); +} + +TEST_CASE("VRGATHER.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VRGATHER(v4, v8, x11, VecMask::No); + REQUIRE(value == 0x3285C257); + + as.RewindBuffer(); + + as.VRGATHER(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0x3085C257); +} + +TEST_CASE("VRGATHER.VI", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VRGATHER(v4, v8, 0, VecMask::No); + REQUIRE(value == 0x32803257); + + as.RewindBuffer(); + + as.VRGATHER(v4, v8, 31, VecMask::No); + REQUIRE(value == 0x328FB257); + + as.RewindBuffer(); + + as.VRGATHER(v4, v8, 0, VecMask::Yes); + REQUIRE(value == 0x30803257); + + as.RewindBuffer(); + + as.VRGATHER(v4, v8, 31, VecMask::Yes); + REQUIRE(value == 0x308FB257); +} + +TEST_CASE("VRGATHEREI16.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VRGATHEREI16(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x3A860257); + + as.RewindBuffer(); + + as.VRGATHEREI16(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x38860257); +} + +TEST_CASE("VRSUB.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VRSUB(v4, v8, x11, VecMask::No); + REQUIRE(value == 0x0E85C257); + + as.RewindBuffer(); + + as.VRSUB(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0x0C85C257); +} + +TEST_CASE("VRSUB.VI", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VRSUB(v4, v8, 15, VecMask::No); + REQUIRE(value == 0x0E87B257); + + as.RewindBuffer(); + + as.VRSUB(v4, v8, -16, VecMask::No); + REQUIRE(value == 0x0E883257); + + as.RewindBuffer(); + + as.VRSUB(v4, v8, 15, VecMask::Yes); + REQUIRE(value == 0x0C87B257); + + as.RewindBuffer(); + + as.VRSUB(v4, v8, -16, VecMask::Yes); + REQUIRE(value == 0x0C883257); +} + +TEST_CASE("VSADD.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSADD(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x86860257); + + as.RewindBuffer(); + + as.VSADD(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x84860257); +} + +TEST_CASE("VSADD.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSADD(v4, v8, x11, VecMask::No); + REQUIRE(value == 0x8685C257); + + as.RewindBuffer(); + + as.VSADD(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0x8485C257); +} + +TEST_CASE("VSADD.VI", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSADD(v4, v8, 15, VecMask::No); + REQUIRE(value == 0x8687B257); + + as.RewindBuffer(); + + as.VSADD(v4, v8, -16, VecMask::No); + REQUIRE(value == 0x86883257); + + as.RewindBuffer(); + + as.VSADD(v4, v8, 15, VecMask::Yes); + REQUIRE(value == 0x8487B257); + + as.RewindBuffer(); + + as.VSADD(v4, v8, -16, VecMask::Yes); + REQUIRE(value == 0x84883257); +} + +TEST_CASE("VSADDU.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSADDU(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x82860257); + + as.RewindBuffer(); + + as.VSADDU(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x80860257); +} + +TEST_CASE("VSADDU.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSADDU(v4, v8, x11, VecMask::No); + REQUIRE(value == 0x8285C257); + + as.RewindBuffer(); + + as.VSADDU(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0x8085C257); +} + +TEST_CASE("VSADDU.VI", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSADDU(v4, v8, 15, VecMask::No); + REQUIRE(value == 0x8287B257); + + as.RewindBuffer(); + + as.VSADDU(v4, v8, -16, VecMask::No); + REQUIRE(value == 0x82883257); + + as.RewindBuffer(); + + as.VSADDU(v4, v8, 15, VecMask::Yes); + REQUIRE(value == 0x8087B257); + + as.RewindBuffer(); + + as.VSADDU(v4, v8, -16, VecMask::Yes); + REQUIRE(value == 0x80883257); +} + +TEST_CASE("VSBC.VVM", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSBC(v4, v8, v12); + REQUIRE(value == 0x48860257); +} + +TEST_CASE("VSBC.VXM", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSBC(v4, v8, x11); + REQUIRE(value == 0x4885C257); +} + +TEST_CASE("VSEXT.VF2", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSEXTVF2(v4, v8, VecMask::No); + REQUIRE(value == 0x4A83A257); + + as.RewindBuffer(); + + as.VSEXTVF2(v4, v8, VecMask::Yes); + REQUIRE(value == 0x4883A257); +} + +TEST_CASE("VSEXT.VF4", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSEXTVF4(v4, v8, VecMask::No); + REQUIRE(value == 0x4A82A257); + + as.RewindBuffer(); + + as.VSEXTVF4(v4, v8, VecMask::Yes); + REQUIRE(value == 0x4882A257); +} + +TEST_CASE("VSEXT.VF8", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSEXTVF8(v4, v8, VecMask::No); + REQUIRE(value == 0x4A81A257); + + as.RewindBuffer(); + + as.VSEXTVF8(v4, v8, VecMask::Yes); + REQUIRE(value == 0x4881A257); +} + +TEST_CASE("VSLIDE1DOWN.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSLIDE1DOWN(v4, v8, x11, VecMask::No); + REQUIRE(value == 0x3E85E257); + + as.RewindBuffer(); + + as.VSLIDE1DOWN(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0x3C85E257); +} + +TEST_CASE("VSLIDEDOWN.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSLIDEDOWN(v4, v8, x11, VecMask::No); + REQUIRE(value == 0x3E85C257); + + as.RewindBuffer(); + + as.VSLIDEDOWN(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0x3C85C257); +} + +TEST_CASE("VSLIDEDOWN.VI", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSLIDEDOWN(v4, v8, 0, VecMask::No); + REQUIRE(value == 0x3E803257); + + as.RewindBuffer(); + + as.VSLIDEDOWN(v4, v8, 31, VecMask::No); + REQUIRE(value == 0x3E8FB257); + + as.RewindBuffer(); + + as.VSLIDEDOWN(v4, v8, 0, VecMask::Yes); + REQUIRE(value == 0x3C803257); + + as.RewindBuffer(); + + as.VSLIDEDOWN(v4, v8, 31, VecMask::Yes); + REQUIRE(value == 0x3C8FB257); +} + +TEST_CASE("VSLIDE1UP.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSLIDE1UP(v4, v8, x11, VecMask::No); + REQUIRE(value == 0x3A85E257); + + as.RewindBuffer(); + + as.VSLIDE1UP(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0x3885E257); +} + +TEST_CASE("VSLIDEUP.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSLIDEUP(v4, v8, x11, VecMask::No); + REQUIRE(value == 0x3A85C257); + + as.RewindBuffer(); + + as.VSLIDEUP(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0x3885C257); +} + +TEST_CASE("VSLIDEUP.VI", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSLIDEUP(v4, v8, 0, VecMask::No); + REQUIRE(value == 0x3A803257); + + as.RewindBuffer(); + + as.VSLIDEUP(v4, v8, 31, VecMask::No); + REQUIRE(value == 0x3A8FB257); + + as.RewindBuffer(); + + as.VSLIDEUP(v4, v8, 0, VecMask::Yes); + REQUIRE(value == 0x38803257); + + as.RewindBuffer(); + + as.VSLIDEUP(v4, v8, 31, VecMask::Yes); + REQUIRE(value == 0x388FB257); +} + +TEST_CASE("VSLL.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSLL(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x96860257); + + as.RewindBuffer(); + + as.VSLL(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x94860257); +} + +TEST_CASE("VSLL.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSLL(v4, v8, x11, VecMask::No); + REQUIRE(value == 0x9685C257); + + as.RewindBuffer(); + + as.VSLL(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0x9485C257); +} + +TEST_CASE("VSLL.VI", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSLL(v4, v8, 1, VecMask::No); + REQUIRE(value == 0x9680B257); + + as.RewindBuffer(); + + as.VSLL(v4, v8, 31, VecMask::No); + REQUIRE(value == 0x968FB257); + + as.RewindBuffer(); + + as.VSLL(v4, v8, 1, VecMask::Yes); + REQUIRE(value == 0x9480B257); + + as.RewindBuffer(); + + as.VSLL(v4, v8, 31, VecMask::Yes); + REQUIRE(value == 0x948FB257); +} + +TEST_CASE("VSMUL.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSMUL(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x9E860257); + + as.RewindBuffer(); + + as.VSMUL(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x9C860257); +} + +TEST_CASE("VSMUL.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSMUL(v4, v8, x11, VecMask::No); + REQUIRE(value == 0x9E85C257); + + as.RewindBuffer(); + + as.VSMUL(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0x9C85C257); +} + +TEST_CASE("VSRA.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSRA(v4, v8, v12, VecMask::No); + REQUIRE(value == 0xA6860257); + + as.RewindBuffer(); + + as.VSRA(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0xA4860257); +} + +TEST_CASE("VSRA.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSRA(v4, v8, x11, VecMask::No); + REQUIRE(value == 0xA685C257); + + as.RewindBuffer(); + + as.VSRA(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0xA485C257); +} + +TEST_CASE("VSRA.VI", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSRA(v4, v8, 1, VecMask::No); + REQUIRE(value == 0xA680B257); + + as.RewindBuffer(); + + as.VSRA(v4, v8, 31, VecMask::No); + REQUIRE(value == 0xA68FB257); + + as.RewindBuffer(); + + as.VSRA(v4, v8, 1, VecMask::Yes); + REQUIRE(value == 0xA480B257); + + as.RewindBuffer(); + + as.VSRA(v4, v8, 31, VecMask::Yes); + REQUIRE(value == 0xA48FB257); +} + +TEST_CASE("VSRL.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSRL(v4, v8, v12, VecMask::No); + REQUIRE(value == 0xA2860257); + + as.RewindBuffer(); + + as.VSRL(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0xA0860257); +} + +TEST_CASE("VSRL.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSRL(v4, v8, x11, VecMask::No); + REQUIRE(value == 0xA285C257); + + as.RewindBuffer(); + + as.VSRL(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0xA085C257); +} + +TEST_CASE("VSRL.VI", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSRL(v4, v8, 1, VecMask::No); + REQUIRE(value == 0xA280B257); + + as.RewindBuffer(); + + as.VSRL(v4, v8, 31, VecMask::No); + REQUIRE(value == 0xA28FB257); + + as.RewindBuffer(); + + as.VSRL(v4, v8, 1, VecMask::Yes); + REQUIRE(value == 0xA080B257); + + as.RewindBuffer(); + + as.VSRL(v4, v8, 31, VecMask::Yes); + REQUIRE(value == 0xA08FB257); +} + +TEST_CASE("VSSRA.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSSRA(v4, v8, v12, VecMask::No); + REQUIRE(value == 0xAE860257); + + as.RewindBuffer(); + + as.VSSRA(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0xAC860257); +} + +TEST_CASE("VSSRA.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSSRA(v4, v8, x11, VecMask::No); + REQUIRE(value == 0xAE85C257); + + as.RewindBuffer(); + + as.VSSRA(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0xAC85C257); +} + +TEST_CASE("VSSRA.VI", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSSRA(v4, v8, 1, VecMask::No); + REQUIRE(value == 0xAE80B257); + + as.RewindBuffer(); + + as.VSSRA(v4, v8, 31, VecMask::No); + REQUIRE(value == 0xAE8FB257); + + as.RewindBuffer(); + + as.VSSRA(v4, v8, 1, VecMask::Yes); + REQUIRE(value == 0xAC80B257); + + as.RewindBuffer(); + + as.VSSRA(v4, v8, 31, VecMask::Yes); + REQUIRE(value == 0xAC8FB257); +} + +TEST_CASE("VSSRL.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSSRL(v4, v8, v12, VecMask::No); + REQUIRE(value == 0xAA860257); + + as.RewindBuffer(); + + as.VSSRL(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0xA8860257); +} + +TEST_CASE("VSSRL.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSSRL(v4, v8, x11, VecMask::No); + REQUIRE(value == 0xAA85C257); + + as.RewindBuffer(); + + as.VSSRL(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0xA885C257); +} + +TEST_CASE("VSSRL.VI", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSSRL(v4, v8, 1, VecMask::No); + REQUIRE(value == 0xAA80B257); + + as.RewindBuffer(); + + as.VSSRL(v4, v8, 31, VecMask::No); + REQUIRE(value == 0xAA8FB257); + + as.RewindBuffer(); + + as.VSSRL(v4, v8, 1, VecMask::Yes); + REQUIRE(value == 0xA880B257); + + as.RewindBuffer(); + + as.VSSRL(v4, v8, 31, VecMask::Yes); + REQUIRE(value == 0xA88FB257); +} + +TEST_CASE("VSSUB.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSSUB(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x8E860257); + + as.RewindBuffer(); + + as.VSSUB(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x8C860257); +} + +TEST_CASE("VSSUB.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSSUB(v4, v8, x11, VecMask::No); + REQUIRE(value == 0x8E85C257); + + as.RewindBuffer(); + + as.VSSUB(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0x8C85C257); +} + +TEST_CASE("VSSUBU.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSSUBU(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x8A860257); + + as.RewindBuffer(); + + as.VSSUBU(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x88860257); +} + +TEST_CASE("VSSUBU.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSSUBU(v4, v8, x11, VecMask::No); + REQUIRE(value == 0x8A85C257); + + as.RewindBuffer(); + + as.VSSUBU(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0x8885C257); +} + +TEST_CASE("VSUB.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSUB(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x0A860257); + + as.RewindBuffer(); + + as.VSUB(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x08860257); +} + +TEST_CASE("VSUB.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSUB(v4, v8, x11, VecMask::No); + REQUIRE(value == 0x0A85C257); + + as.RewindBuffer(); + + as.VSUB(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0x0885C257); +} + +TEST_CASE("VWADD.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VWADD(v4, v8, v12, VecMask::No); + REQUIRE(value == 0xC6862257); + + as.RewindBuffer(); + + as.VWADD(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0xC4862257); +} + +TEST_CASE("VWADD.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VWADD(v4, v8, x11, VecMask::No); + REQUIRE(value == 0xC685E257); + + as.RewindBuffer(); + + as.VWADD(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0xC485E257); +} + +TEST_CASE("VWADD.WV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VWADDW(v4, v8, v12, VecMask::No); + REQUIRE(value == 0xD6862257); + + as.RewindBuffer(); + + as.VWADDW(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0xD4862257); +} + +TEST_CASE("VWADD.WX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VWADDW(v4, v8, x11, VecMask::No); + REQUIRE(value == 0xD685E257); + + as.RewindBuffer(); + + as.VWADDW(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0xD485E257); +} + +TEST_CASE("VWADDU.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VWADDU(v4, v8, v12, VecMask::No); + REQUIRE(value == 0xC2862257); + + as.RewindBuffer(); + + as.VWADDU(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0xC0862257); +} + +TEST_CASE("VWADDU.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VWADDU(v4, v8, x11, VecMask::No); + REQUIRE(value == 0xC285E257); + + as.RewindBuffer(); + + as.VWADDU(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0xC085E257); +} + +TEST_CASE("VWADDU.WV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VWADDUW(v4, v8, v12, VecMask::No); + REQUIRE(value == 0xD2862257); + + as.RewindBuffer(); + + as.VWADDUW(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0xD0862257); +} + +TEST_CASE("VWADDU.WX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VWADDUW(v4, v8, x11, VecMask::No); + REQUIRE(value == 0xD285E257); + + as.RewindBuffer(); + + as.VWADDUW(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0xD085E257); +} + +TEST_CASE("VWMACC.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VWMACC(v4, v12, v8, VecMask::No); + REQUIRE(value == 0xF6862257); + + as.RewindBuffer(); + + as.VWMACC(v4, v12, v8, VecMask::Yes); + REQUIRE(value == 0xF4862257); +} + +TEST_CASE("VWMACC.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VWMACC(v4, x11, v8, VecMask::No); + REQUIRE(value == 0xF685E257); + + as.RewindBuffer(); + + as.VWMACC(v4, x11, v8, VecMask::Yes); + REQUIRE(value == 0xF485E257); +} + +TEST_CASE("VWMACCSU.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VWMACCSU(v4, v12, v8, VecMask::No); + REQUIRE(value == 0xFE862257); + + as.RewindBuffer(); + + as.VWMACCSU(v4, v12, v8, VecMask::Yes); + REQUIRE(value == 0xFC862257); +} + +TEST_CASE("VWMACCSU.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VWMACCSU(v4, x11, v8, VecMask::No); + REQUIRE(value == 0xFE85E257); + + as.RewindBuffer(); + + as.VWMACCSU(v4, x11, v8, VecMask::Yes); + REQUIRE(value == 0xFC85E257); +} + +TEST_CASE("VWMACCU.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VWMACCU(v4, v12, v8, VecMask::No); + REQUIRE(value == 0xF2862257); + + as.RewindBuffer(); + + as.VWMACCU(v4, v12, v8, VecMask::Yes); + REQUIRE(value == 0xF0862257); +} + +TEST_CASE("VWMACCU.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VWMACCU(v4, x11, v8, VecMask::No); + REQUIRE(value == 0xF285E257); + + as.RewindBuffer(); + + as.VWMACCU(v4, x11, v8, VecMask::Yes); + REQUIRE(value == 0xF085E257); +} + +TEST_CASE("VWMACCUS.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VWMACCUS(v4, x11, v8, VecMask::No); + REQUIRE(value == 0xFA85E257); + + as.RewindBuffer(); + + as.VWMACCUS(v4, x11, v8, VecMask::Yes); + REQUIRE(value == 0xF885E257); +} + +TEST_CASE("VWMUL.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VWMUL(v4, v8, v12, VecMask::No); + REQUIRE(value == 0xEE862257); + + as.RewindBuffer(); + + as.VWMUL(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0xEC862257); +} + +TEST_CASE("VWMUL.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VWMUL(v4, v8, x11, VecMask::No); + REQUIRE(value == 0xEE85E257); + + as.RewindBuffer(); + + as.VWMUL(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0xEC85E257); +} + +TEST_CASE("VWMULSU.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VWMULSU(v4, v8, v12, VecMask::No); + REQUIRE(value == 0xEA862257); + + as.RewindBuffer(); + + as.VWMULSU(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0xE8862257); +} + +TEST_CASE("VWMULSU.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VWMULSU(v4, v8, x11, VecMask::No); + REQUIRE(value == 0xEA85E257); + + as.RewindBuffer(); + + as.VWMULSU(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0xE885E257); +} + +TEST_CASE("VWMULU.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VWMULU(v4, v8, v12, VecMask::No); + REQUIRE(value == 0xE2862257); + + as.RewindBuffer(); + + as.VWMULU(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0xE0862257); +} + +TEST_CASE("VWMULU.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VWMULU(v4, v8, x11, VecMask::No); + REQUIRE(value == 0xE285E257); + + as.RewindBuffer(); + + as.VWMULU(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0xE085E257); +} + +TEST_CASE("VWREDSUM.VS", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VWREDSUM(v4, v8, v12, VecMask::No); + REQUIRE(value == 0xC6860257); + + as.RewindBuffer(); + + as.VWREDSUM(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0xC4860257); +} + +TEST_CASE("VWREDSUMU.VS", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VWREDSUMU(v4, v8, v12, VecMask::No); + REQUIRE(value == 0xC2860257); + + as.RewindBuffer(); + + as.VWREDSUMU(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0xC0860257); +} + +TEST_CASE("VWSUB.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VWSUB(v4, v8, v12, VecMask::No); + REQUIRE(value == 0xCE862257); + + as.RewindBuffer(); + + as.VWSUB(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0xCC862257); +} + +TEST_CASE("VWSUB.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VWSUB(v4, v8, x11, VecMask::No); + REQUIRE(value == 0xCE85E257); + + as.RewindBuffer(); + + as.VWSUB(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0xCC85E257); +} + +TEST_CASE("VWSUB.WV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VWSUBW(v4, v8, v12, VecMask::No); + REQUIRE(value == 0xDE862257); + + as.RewindBuffer(); + + as.VWSUBW(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0xDC862257); +} + +TEST_CASE("VWSUB.WX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VWSUBW(v4, v8, x11, VecMask::No); + REQUIRE(value == 0xDE85E257); + + as.RewindBuffer(); + + as.VWSUBW(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0xDC85E257); +} + +TEST_CASE("VWSUBU.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VWSUBU(v4, v8, v12, VecMask::No); + REQUIRE(value == 0xCA862257); + + as.RewindBuffer(); + + as.VWSUBU(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0xC8862257); +} + +TEST_CASE("VWSUBU.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VWSUBU(v4, v8, x11, VecMask::No); + REQUIRE(value == 0xCA85E257); + + as.RewindBuffer(); + + as.VWSUBU(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0xC885E257); +} + +TEST_CASE("VWSUBU.WV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VWSUBUW(v4, v8, v12, VecMask::No); + REQUIRE(value == 0xDA862257); + + as.RewindBuffer(); + + as.VWSUBUW(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0xD8862257); +} + +TEST_CASE("VWSUBU.WX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VWSUBUW(v4, v8, x11, VecMask::No); + REQUIRE(value == 0xDA85E257); + + as.RewindBuffer(); + + as.VWSUBUW(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0xD885E257); +} + +TEST_CASE("VXOR.VV", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VXOR(v4, v8, v12, VecMask::No); + REQUIRE(value == 0x2E860257); + + as.RewindBuffer(); + + as.VXOR(v4, v8, v12, VecMask::Yes); + REQUIRE(value == 0x2C860257); +} + +TEST_CASE("VXOR.VX", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VXOR(v4, v8, x11, VecMask::No); + REQUIRE(value == 0x2E85C257); + + as.RewindBuffer(); + + as.VXOR(v4, v8, x11, VecMask::Yes); + REQUIRE(value == 0x2C85C257); +} + +TEST_CASE("VXOR.VI", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VXOR(v4, v8, 15, VecMask::No); + REQUIRE(value == 0x2E87B257); + + as.RewindBuffer(); + + as.VXOR(v4, v8, -16, VecMask::No); + REQUIRE(value == 0x2E883257); + + as.RewindBuffer(); + + as.VXOR(v4, v8, 15, VecMask::Yes); + REQUIRE(value == 0x2C87B257); + + as.RewindBuffer(); + + as.VXOR(v4, v8, -16, VecMask::Yes); + REQUIRE(value == 0x2C883257); +} + +TEST_CASE("VZEXT.VF2", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VZEXTVF2(v4, v8, VecMask::No); + REQUIRE(value == 0x4A832257); + + as.RewindBuffer(); + + as.VZEXTVF2(v4, v8, VecMask::Yes); + REQUIRE(value == 0x48832257); +} + +TEST_CASE("VZEXT.VF4", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VZEXTVF4(v4, v8, VecMask::No); + REQUIRE(value == 0x4A822257); + + as.RewindBuffer(); + + as.VZEXTVF4(v4, v8, VecMask::Yes); + REQUIRE(value == 0x48822257); +} + +TEST_CASE("VZEXT.VF8", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VZEXTVF8(v4, v8, VecMask::No); + REQUIRE(value == 0x4A812257); + + as.RewindBuffer(); + + as.VZEXTVF8(v4, v8, VecMask::Yes); + REQUIRE(value == 0x48812257); +} + +TEST_CASE("VLE8.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VLE8(v0, x11, VecMask::No); + REQUIRE(value == 0x02058007); + + as.RewindBuffer(); + + as.VLE8(v0, x11, VecMask::Yes); + REQUIRE(value == 0x00058007); +} + +TEST_CASE("VLE16.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VLE16(v0, x11, VecMask::No); + REQUIRE(value == 0x0205D007); + + as.RewindBuffer(); + + as.VLE16(v0, x11, VecMask::Yes); + REQUIRE(value == 0x0005D007); +} + +TEST_CASE("VLE32.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VLE32(v0, x11, VecMask::No); + REQUIRE(value == 0x0205E007); + + as.RewindBuffer(); + + as.VLE32(v0, x11, VecMask::Yes); + REQUIRE(value == 0x0005E007); +} + +TEST_CASE("VLE64.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VLE64(v0, x11, VecMask::No); + REQUIRE(value == 0x0205F007); + + as.RewindBuffer(); + + as.VLE64(v0, x11, VecMask::Yes); + REQUIRE(value == 0x0005F007); +} + +TEST_CASE("VLM.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VLM(v0, x11); + REQUIRE(value == 0x02B58007); +} + +TEST_CASE("VLSE8.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VLSE8(v4, x10, x11, VecMask::No); + REQUIRE(value == 0x0AB50207); + + as.RewindBuffer(); + + as.VLSE8(v4, x10, x11, VecMask::Yes); + REQUIRE(value == 0x08B50207); +} + +TEST_CASE("VLSE16.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VLSE16(v4, x10, x11, VecMask::No); + REQUIRE(value == 0x0AB55207); + + as.RewindBuffer(); + + as.VLSE16(v4, x10, x11, VecMask::Yes); + REQUIRE(value == 0x08B55207); +} + +TEST_CASE("VLSE32.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VLSE32(v4, x10, x11, VecMask::No); + REQUIRE(value == 0x0AB56207); + + as.RewindBuffer(); + + as.VLSE32(v4, x10, x11, VecMask::Yes); + REQUIRE(value == 0x08B56207); +} + +TEST_CASE("VLSE64.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VLSE64(v4, x10, x11, VecMask::No); + REQUIRE(value == 0x0AB57207); + + as.RewindBuffer(); + + as.VLSE64(v4, x10, x11, VecMask::Yes); + REQUIRE(value == 0x08B57207); +} + +TEST_CASE("VLOXEI8.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VLOXEI8(v4, x10, v12, VecMask::No); + REQUIRE(value == 0x0EC50207); + + as.RewindBuffer(); + + as.VLOXEI8(v4, x10, v12, VecMask::Yes); + REQUIRE(value == 0x0CC50207); +} + +TEST_CASE("VLOXEI16.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VLOXEI16(v4, x10, v12, VecMask::No); + REQUIRE(value == 0x0EC55207); + + as.RewindBuffer(); + + as.VLOXEI16(v4, x10, v12, VecMask::Yes); + REQUIRE(value == 0x0CC55207); +} + +TEST_CASE("VLOXEI32.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VLOXEI32(v4, x10, v12, VecMask::No); + REQUIRE(value == 0x0EC56207); + + as.RewindBuffer(); + + as.VLOXEI32(v4, x10, v12, VecMask::Yes); + REQUIRE(value == 0x0CC56207); +} + +TEST_CASE("VLOXEI64.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VLOXEI64(v4, x10, v12, VecMask::No); + REQUIRE(value == 0x0EC57207); + + as.RewindBuffer(); + + as.VLOXEI64(v4, x10, v12, VecMask::Yes); + REQUIRE(value == 0x0CC57207); +} + +TEST_CASE("VLUXEI8.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VLUXEI8(v4, x10, v12, VecMask::No); + REQUIRE(value == 0x06C50207); + + as.RewindBuffer(); + + as.VLUXEI8(v4, x10, v12, VecMask::Yes); + REQUIRE(value == 0x04C50207); +} + +TEST_CASE("VLUXEI16.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VLUXEI16(v4, x10, v12, VecMask::No); + REQUIRE(value == 0x06C55207); + + as.RewindBuffer(); + + as.VLUXEI16(v4, x10, v12, VecMask::Yes); + REQUIRE(value == 0x04C55207); +} + +TEST_CASE("VLUXEI32.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VLUXEI32(v4, x10, v12, VecMask::No); + REQUIRE(value == 0x06C56207); + + as.RewindBuffer(); + + as.VLUXEI32(v4, x10, v12, VecMask::Yes); + REQUIRE(value == 0x04C56207); +} + +TEST_CASE("VLUXEI64.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VLUXEI64(v4, x10, v12, VecMask::No); + REQUIRE(value == 0x06C57207); + + as.RewindBuffer(); + + as.VLUXEI64(v4, x10, v12, VecMask::Yes); + REQUIRE(value == 0x04C57207); +} + +TEST_CASE("VLE8FF.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VLE8FF(v4, x10, VecMask::No); + REQUIRE(value == 0x03050207); + + as.RewindBuffer(); + + as.VLE8FF(v4, x10, VecMask::Yes); + REQUIRE(value == 0x01050207); +} + +TEST_CASE("VLE16FF.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VLE16FF(v4, x10, VecMask::No); + REQUIRE(value == 0x03055207); + + as.RewindBuffer(); + + as.VLE16FF(v4, x10, VecMask::Yes); + REQUIRE(value == 0x01055207); +} + +TEST_CASE("VLE32FF.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VLE32FF(v4, x10, VecMask::No); + REQUIRE(value == 0x03056207); + + as.RewindBuffer(); + + as.VLE32FF(v4, x10, VecMask::Yes); + REQUIRE(value == 0x01056207); +} + +TEST_CASE("VLE64FF.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VLE64FF(v4, x10, VecMask::No); + REQUIRE(value == 0x03057207); + + as.RewindBuffer(); + + as.VLE64FF(v4, x10, VecMask::Yes); + REQUIRE(value == 0x01057207); +} + +TEST_CASE("8-bit segmented unit-stride loads", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + for (uint32_t i = 2; i <= 8; i++) { + as.VLSEGE8(i, v4, x10, VecMask::No); + REQUIRE(value == (0x02050207 | ((i - 1) << 29))); + + as.RewindBuffer(); + + as.VLSEGE8(i, v4, x10, VecMask::Yes); + REQUIRE(value == (0x00050207 | ((i - 1) << 29))); + + as.RewindBuffer(); + } +} + +TEST_CASE("16-bit segmented unit-stride loads", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + for (uint32_t i = 2; i <= 8; i++) { + as.VLSEGE16(i, v4, x10, VecMask::No); + REQUIRE(value == (0x02055207 | ((i - 1) << 29))); + + as.RewindBuffer(); + + as.VLSEGE16(i, v4, x10, VecMask::Yes); + REQUIRE(value == (0x00055207 | ((i - 1) << 29))); + + as.RewindBuffer(); + } +} + +TEST_CASE("32-bit segmented unit-stride loads", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + for (uint32_t i = 2; i <= 8; i++) { + as.VLSEGE32(i, v4, x10, VecMask::No); + REQUIRE(value == (0x02056207 | ((i - 1) << 29))); + + as.RewindBuffer(); + + as.VLSEGE32(i, v4, x10, VecMask::Yes); + REQUIRE(value == (0x00056207 | ((i - 1) << 29))); + + as.RewindBuffer(); + } +} + +TEST_CASE("64-bit segmented unit-stride loads", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + for (uint32_t i = 2; i <= 8; i++) { + as.VLSEGE64(i, v4, x10, VecMask::No); + REQUIRE(value == (0x02057207 | ((i - 1) << 29))); + + as.RewindBuffer(); + + as.VLSEGE64(i, v4, x10, VecMask::Yes); + REQUIRE(value == (0x00057207 | ((i - 1) << 29))); + + as.RewindBuffer(); + } +} + +TEST_CASE("8-bit segmented strided loads", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + for (uint32_t i = 2; i <= 8; i++) { + as.VLSSEGE8(i, v4, x10, x11, VecMask::No); + REQUIRE(value == (0x0AB50207 | ((i - 1) << 29))); + + as.RewindBuffer(); + + as.VLSSEGE8(i, v4, x10, x11, VecMask::Yes); + REQUIRE(value == (0x08B50207 | ((i - 1) << 29))); + + as.RewindBuffer(); + } +} + +TEST_CASE("16-bit segmented strided loads", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + for (uint32_t i = 2; i <= 8; i++) { + as.VLSSEGE16(i, v4, x10, x11, VecMask::No); + REQUIRE(value == (0x0AB55207 | ((i - 1) << 29))); + + as.RewindBuffer(); + + as.VLSSEGE16(i, v4, x10, x11, VecMask::Yes); + REQUIRE(value == (0x08B55207 | ((i - 1) << 29))); + + as.RewindBuffer(); + } +} + +TEST_CASE("32-bit segmented strided loads", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + for (uint32_t i = 2; i <= 8; i++) { + as.VLSSEGE32(i, v4, x10, x11, VecMask::No); + REQUIRE(value == (0x0AB56207 | ((i - 1) << 29))); + + as.RewindBuffer(); + + as.VLSSEGE32(i, v4, x10, x11, VecMask::Yes); + REQUIRE(value == (0x08B56207 | ((i - 1) << 29))); + + as.RewindBuffer(); + } +} + +TEST_CASE("64-bit segmented strided loads", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + for (uint32_t i = 2; i <= 8; i++) { + as.VLSSEGE64(i, v4, x10, x11, VecMask::No); + REQUIRE(value == (0x0AB57207 | ((i - 1) << 29))); + + as.RewindBuffer(); + + as.VLSSEGE64(i, v4, x10, x11, VecMask::Yes); + REQUIRE(value == (0x08B57207 | ((i - 1) << 29))); + + as.RewindBuffer(); + } +} + +TEST_CASE("8-bit vector indexed-ordered segment loads", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + for (uint32_t i = 2; i <= 8; i++) { + as.VLOXSEGEI8(i, v4, x10, v12, VecMask::No); + REQUIRE(value == (0x0EC50207 | ((i - 1) << 29))); + + as.RewindBuffer(); + + as.VLOXSEGEI8(i, v4, x10, v12, VecMask::Yes); + REQUIRE(value == (0x0CC50207 | ((i - 1) << 29))); + + as.RewindBuffer(); + } +} + +TEST_CASE("16-bit vector indexed-ordered segment loads", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + for (uint32_t i = 2; i <= 8; i++) { + as.VLOXSEGEI16(i, v4, x10, v12, VecMask::No); + REQUIRE(value == (0x0EC55207 | ((i - 1) << 29))); + + as.RewindBuffer(); + + as.VLOXSEGEI16(i, v4, x10, v12, VecMask::Yes); + REQUIRE(value == (0x0CC55207 | ((i - 1) << 29))); + + as.RewindBuffer(); + } +} + +TEST_CASE("32-bit vector indexed-ordered segment loads", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + for (uint32_t i = 2; i <= 8; i++) { + as.VLOXSEGEI32(i, v4, x10, v12, VecMask::No); + REQUIRE(value == (0x0EC56207 | ((i - 1) << 29))); + + as.RewindBuffer(); + + as.VLOXSEGEI32(i, v4, x10, v12, VecMask::Yes); + REQUIRE(value == (0x0CC56207 | ((i - 1) << 29))); + + as.RewindBuffer(); + } +} + +TEST_CASE("64-bit vector indexed-ordered segment loads", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + for (uint32_t i = 2; i <= 8; i++) { + as.VLOXSEGEI64(i, v4, x10, v12, VecMask::No); + REQUIRE(value == (0x0EC57207 | ((i - 1) << 29))); + + as.RewindBuffer(); + + as.VLOXSEGEI64(i, v4, x10, v12, VecMask::Yes); + REQUIRE(value == (0x0CC57207 | ((i - 1) << 29))); + + as.RewindBuffer(); + } +} + +TEST_CASE("8-bit vector indexed-unordered segment loads", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + for (uint32_t i = 2; i <= 8; i++) { + as.VLUXSEGEI8(i, v4, x10, v12, VecMask::No); + REQUIRE(value == (0x06C50207 | ((i - 1) << 29))); + + as.RewindBuffer(); + + as.VLUXSEGEI8(i, v4, x10, v12, VecMask::Yes); + REQUIRE(value == (0x04C50207 | ((i - 1) << 29))); + + as.RewindBuffer(); + } +} + +TEST_CASE("16-bit vector indexed-unordered segment loads", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + for (uint32_t i = 2; i <= 8; i++) { + as.VLUXSEGEI16(i, v4, x10, v12, VecMask::No); + REQUIRE(value == (0x06C55207 | ((i - 1) << 29))); + + as.RewindBuffer(); + + as.VLUXSEGEI16(i, v4, x10, v12, VecMask::Yes); + REQUIRE(value == (0x04C55207 | ((i - 1) << 29))); + + as.RewindBuffer(); + } +} + +TEST_CASE("32-bit vector indexed-unordered segment loads", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + for (uint32_t i = 2; i <= 8; i++) { + as.VLUXSEGEI32(i, v4, x10, v12, VecMask::No); + REQUIRE(value == (0x06C56207 | ((i - 1) << 29))); + + as.RewindBuffer(); + + as.VLUXSEGEI32(i, v4, x10, v12, VecMask::Yes); + REQUIRE(value == (0x04C56207 | ((i - 1) << 29))); + + as.RewindBuffer(); + } +} + +TEST_CASE("64-bit vector indexed-unordered segment loads", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + for (uint32_t i = 2; i <= 8; i++) { + as.VLUXSEGEI64(i, v4, x10, v12, VecMask::No); + REQUIRE(value == (0x06C57207 | ((i - 1) << 29))); + + as.RewindBuffer(); + + as.VLUXSEGEI64(i, v4, x10, v12, VecMask::Yes); + REQUIRE(value == (0x04C57207 | ((i - 1) << 29))); + + as.RewindBuffer(); + } +} + +TEST_CASE("8-bit vector whole register loads", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VLRE8(1, v3, x10); + REQUIRE(value == 0x02850187); + as.RewindBuffer(); + + as.VLRE8(2, v2, x10); + REQUIRE(value == 0x22850107); + as.RewindBuffer(); + + as.VLRE8(4, v4, x10); + REQUIRE(value == 0x62850207); + as.RewindBuffer(); + + as.VLRE8(8, v8, x10); + REQUIRE(value == 0xE2850407); +} + +TEST_CASE("16-bit vector whole register loads", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VLRE16(1, v3, x10); + REQUIRE(value == 0x02855187); + as.RewindBuffer(); + + as.VLRE16(2, v2, x10); + REQUIRE(value == 0x22855107); + as.RewindBuffer(); + + as.VLRE16(4, v4, x10); + REQUIRE(value == 0x62855207); + as.RewindBuffer(); + + as.VLRE16(8, v8, x10); + REQUIRE(value == 0xE2855407); +} + +TEST_CASE("32-bit vector whole register loads", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VLRE32(1, v3, x10); + REQUIRE(value == 0x02856187); + as.RewindBuffer(); + + as.VLRE32(2, v2, x10); + REQUIRE(value == 0x22856107); + as.RewindBuffer(); + + as.VLRE32(4, v4, x10); + REQUIRE(value == 0x62856207); + as.RewindBuffer(); + + as.VLRE32(8, v8, x10); + REQUIRE(value == 0xE2856407); +} + +TEST_CASE("64-bit vector whole register loads", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VLRE64(1, v3, x10); + REQUIRE(value == 0x02857187); + as.RewindBuffer(); + + as.VLRE64(2, v2, x10); + REQUIRE(value == 0x22857107); + as.RewindBuffer(); + + as.VLRE64(4, v4, x10); + REQUIRE(value == 0x62857207); + as.RewindBuffer(); + + as.VLRE64(8, v8, x10); + REQUIRE(value == 0xE2857407); +} + +TEST_CASE("VSE8.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSE8(v0, x13, VecMask::No); + REQUIRE(value == 0x02068027); + + as.RewindBuffer(); + + as.VSE8(v0, x13, VecMask::Yes); + REQUIRE(value == 0x00068027); +} + +TEST_CASE("VSE16.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSE16(v0, x13, VecMask::No); + REQUIRE(value == 0x0206D027); + + as.RewindBuffer(); + + as.VSE16(v0, x13, VecMask::Yes); + REQUIRE(value == 0x0006D027); +} + +TEST_CASE("VSE32.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSE32(v0, x13, VecMask::No); + REQUIRE(value == 0x0206E027); + + as.RewindBuffer(); + + as.VSE32(v0, x13, VecMask::Yes); + REQUIRE(value == 0x0006E027); +} + +TEST_CASE("VSE64.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSE64(v0, x13, VecMask::No); + REQUIRE(value == 0x0206F027); + + as.RewindBuffer(); + + as.VSE64(v0, x13, VecMask::Yes); + REQUIRE(value == 0x0006F027); +} + +TEST_CASE("VSM.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSM(v0, x13); + REQUIRE(value == 0x02B68027); +} + +TEST_CASE("VSSE8.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSSE8(v4, x10, x11, VecMask::No); + REQUIRE(value == 0x0AB50227); + + as.RewindBuffer(); + + as.VSSE8(v4, x10, x11, VecMask::Yes); + REQUIRE(value == 0x08B50227); +} + +TEST_CASE("VSSE16.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSSE16(v4, x10, x11, VecMask::No); + REQUIRE(value == 0x0AB55227); + + as.RewindBuffer(); + + as.VSSE16(v4, x10, x11, VecMask::Yes); + REQUIRE(value == 0x08B55227); +} + +TEST_CASE("VSSE32.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSSE32(v4, x10, x11, VecMask::No); + REQUIRE(value == 0x0AB56227); + + as.RewindBuffer(); + + as.VSSE32(v4, x10, x11, VecMask::Yes); + REQUIRE(value == 0x08B56227); +} + +TEST_CASE("VSSE64.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSSE64(v4, x10, x11, VecMask::No); + REQUIRE(value == 0x0AB57227); + + as.RewindBuffer(); + + as.VSSE64(v4, x10, x11, VecMask::Yes); + REQUIRE(value == 0x08B57227); +} + +TEST_CASE("VSOXEI8.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSOXEI8(v4, x10, v12, VecMask::No); + REQUIRE(value == 0x0EC50227); + + as.RewindBuffer(); + + as.VSOXEI8(v4, x10, v12, VecMask::Yes); + REQUIRE(value == 0x0CC50227); +} + +TEST_CASE("VSOXEI16.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSOXEI16(v4, x10, v12, VecMask::No); + REQUIRE(value == 0x0EC55227); + + as.RewindBuffer(); + + as.VSOXEI16(v4, x10, v12, VecMask::Yes); + REQUIRE(value == 0x0CC55227); +} + +TEST_CASE("VSOXEI32.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSOXEI32(v4, x10, v12, VecMask::No); + REQUIRE(value == 0x0EC56227); + + as.RewindBuffer(); + + as.VSOXEI32(v4, x10, v12, VecMask::Yes); + REQUIRE(value == 0x0CC56227); +} + +TEST_CASE("VSOXEI64.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSOXEI64(v4, x10, v12, VecMask::No); + REQUIRE(value == 0x0EC57227); + + as.RewindBuffer(); + + as.VSOXEI64(v4, x10, v12, VecMask::Yes); + REQUIRE(value == 0x0CC57227); +} + +TEST_CASE("VSUXEI8.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSUXEI8(v4, x10, v12, VecMask::No); + REQUIRE(value == 0x06C50227); + + as.RewindBuffer(); + + as.VSUXEI8(v4, x10, v12, VecMask::Yes); + REQUIRE(value == 0x04C50227); +} + +TEST_CASE("VSUXEI16.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSUXEI16(v4, x10, v12, VecMask::No); + REQUIRE(value == 0x06C55227); + + as.RewindBuffer(); + + as.VSUXEI16(v4, x10, v12, VecMask::Yes); + REQUIRE(value == 0x04C55227); +} + +TEST_CASE("VSUXEI32.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSUXEI32(v4, x10, v12, VecMask::No); + REQUIRE(value == 0x06C56227); + + as.RewindBuffer(); + + as.VSUXEI32(v4, x10, v12, VecMask::Yes); + REQUIRE(value == 0x04C56227); +} + +TEST_CASE("VSUXEI64.V", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSUXEI64(v4, x10, v12, VecMask::No); + REQUIRE(value == 0x06C57227); + + as.RewindBuffer(); + + as.VSUXEI64(v4, x10, v12, VecMask::Yes); + REQUIRE(value == 0x04C57227); +} + +TEST_CASE("8-bit segmented unit-stride stores", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + for (uint32_t i = 2; i <= 8; i++) { + as.VSSEGE8(i, v4, x10, VecMask::No); + REQUIRE(value == (0x02050227 | ((i - 1) << 29))); + + as.RewindBuffer(); + + as.VSSEGE8(i, v4, x10, VecMask::Yes); + REQUIRE(value == (0x00050227 | ((i - 1) << 29))); + + as.RewindBuffer(); + } +} + +TEST_CASE("16-bit segmented unit-stride stores", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + for (uint32_t i = 2; i <= 8; i++) { + as.VSSEGE16(i, v4, x10, VecMask::No); + REQUIRE(value == (0x02055227 | ((i - 1) << 29))); + + as.RewindBuffer(); + + as.VSSEGE16(i, v4, x10, VecMask::Yes); + REQUIRE(value == (0x00055227 | ((i - 1) << 29))); + + as.RewindBuffer(); + } +} + +TEST_CASE("32-bit segmented unit-stride stores", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + for (uint32_t i = 2; i <= 8; i++) { + as.VSSEGE32(i, v4, x10, VecMask::No); + REQUIRE(value == (0x02056227 | ((i - 1) << 29))); + + as.RewindBuffer(); + + as.VSSEGE32(i, v4, x10, VecMask::Yes); + REQUIRE(value == (0x00056227 | ((i - 1) << 29))); + + as.RewindBuffer(); + } +} + +TEST_CASE("64-bit segmented unit-stride stores", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + for (uint32_t i = 2; i <= 8; i++) { + as.VSSEGE64(i, v4, x10, VecMask::No); + REQUIRE(value == (0x02057227 | ((i - 1) << 29))); + + as.RewindBuffer(); + + as.VSSEGE64(i, v4, x10, VecMask::Yes); + REQUIRE(value == (0x00057227 | ((i - 1) << 29))); + + as.RewindBuffer(); + } +} + +TEST_CASE("8-bit segmented strided stores", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + for (uint32_t i = 2; i <= 8; i++) { + as.VSSSEGE8(i, v4, x10, x11, VecMask::No); + REQUIRE(value == (0x0AB50227 | ((i - 1) << 29))); + + as.RewindBuffer(); + + as.VSSSEGE8(i, v4, x10, x11, VecMask::Yes); + REQUIRE(value == (0x08B50227 | ((i - 1) << 29))); + + as.RewindBuffer(); + } +} + +TEST_CASE("16-bit segmented strided stores", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + for (uint32_t i = 2; i <= 8; i++) { + as.VSSSEGE16(i, v4, x10, x11, VecMask::No); + REQUIRE(value == (0x0AB55227 | ((i - 1) << 29))); + + as.RewindBuffer(); + + as.VSSSEGE16(i, v4, x10, x11, VecMask::Yes); + REQUIRE(value == (0x08B55227 | ((i - 1) << 29))); + + as.RewindBuffer(); + } +} + +TEST_CASE("32-bit segmented strided stores", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + for (uint32_t i = 2; i <= 8; i++) { + as.VSSSEGE32(i, v4, x10, x11, VecMask::No); + REQUIRE(value == (0x0AB56227 | ((i - 1) << 29))); + + as.RewindBuffer(); + + as.VSSSEGE32(i, v4, x10, x11, VecMask::Yes); + REQUIRE(value == (0x08B56227 | ((i - 1) << 29))); + + as.RewindBuffer(); + } +} + +TEST_CASE("64-bit segmented strided stores", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + for (uint32_t i = 2; i <= 8; i++) { + as.VSSSEGE64(i, v4, x10, x11, VecMask::No); + REQUIRE(value == (0x0AB57227 | ((i - 1) << 29))); + + as.RewindBuffer(); + + as.VSSSEGE64(i, v4, x10, x11, VecMask::Yes); + REQUIRE(value == (0x08B57227 | ((i - 1) << 29))); + + as.RewindBuffer(); + } +} + +TEST_CASE("8-bit segmented vector indexed-ordered stores", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + for (uint32_t i = 2; i <= 8; i++) { + as.VSOXSEGEI8(i, v4, x10, v12, VecMask::No); + REQUIRE(value == (0x0EC50227 | ((i - 1) << 29))); + + as.RewindBuffer(); + + as.VSOXSEGEI8(i, v4, x10, v12, VecMask::Yes); + REQUIRE(value == (0x0CC50227 | ((i - 1) << 29))); + + as.RewindBuffer(); + } +} + +TEST_CASE("16-bit segmented vector indexed-ordered stores", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + for (uint32_t i = 2; i <= 8; i++) { + as.VSOXSEGEI16(i, v4, x10, v12, VecMask::No); + REQUIRE(value == (0x0EC55227 | ((i - 1) << 29))); + + as.RewindBuffer(); + + as.VSOXSEGEI16(i, v4, x10, v12, VecMask::Yes); + REQUIRE(value == (0x0CC55227 | ((i - 1) << 29))); + + as.RewindBuffer(); + } +} + +TEST_CASE("32-bit segmented vector indexed-ordered stores", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + for (uint32_t i = 2; i <= 8; i++) { + as.VSOXSEGEI32(i, v4, x10, v12, VecMask::No); + REQUIRE(value == (0x0EC56227 | ((i - 1) << 29))); + + as.RewindBuffer(); + + as.VSOXSEGEI32(i, v4, x10, v12, VecMask::Yes); + REQUIRE(value == (0x0CC56227 | ((i - 1) << 29))); + + as.RewindBuffer(); + } +} + +TEST_CASE("64-bit segmented vector indexed-ordered stores", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + for (uint32_t i = 2; i <= 8; i++) { + as.VSOXSEGEI64(i, v4, x10, v12, VecMask::No); + REQUIRE(value == (0x0EC57227 | ((i - 1) << 29))); + + as.RewindBuffer(); + + as.VSOXSEGEI64(i, v4, x10, v12, VecMask::Yes); + REQUIRE(value == (0x0CC57227 | ((i - 1) << 29))); + + as.RewindBuffer(); + } +} + +TEST_CASE("8-bit segmented vector indexed-unordered stores", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + for (uint32_t i = 2; i <= 8; i++) { + as.VSUXSEGEI8(i, v4, x10, v12, VecMask::No); + REQUIRE(value == (0x06C50227 | ((i - 1) << 29))); + + as.RewindBuffer(); + + as.VSUXSEGEI8(i, v4, x10, v12, VecMask::Yes); + REQUIRE(value == (0x04C50227 | ((i - 1) << 29))); + + as.RewindBuffer(); + } +} + +TEST_CASE("16-bit segmented vector indexed-unordered stores", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + for (uint32_t i = 2; i <= 8; i++) { + as.VSUXSEGEI16(i, v4, x10, v12, VecMask::No); + REQUIRE(value == (0x06C55227 | ((i - 1) << 29))); + + as.RewindBuffer(); + + as.VSUXSEGEI16(i, v4, x10, v12, VecMask::Yes); + REQUIRE(value == (0x04C55227 | ((i - 1) << 29))); + + as.RewindBuffer(); + } +} + +TEST_CASE("32-bit segmented vector indexed-unordered stores", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + for (uint32_t i = 2; i <= 8; i++) { + as.VSUXSEGEI32(i, v4, x10, v12, VecMask::No); + REQUIRE(value == (0x06C56227 | ((i - 1) << 29))); + + as.RewindBuffer(); + + as.VSUXSEGEI32(i, v4, x10, v12, VecMask::Yes); + REQUIRE(value == (0x04C56227 | ((i - 1) << 29))); + + as.RewindBuffer(); + } +} + +TEST_CASE("64-bit segmented vector indexed-unordered stores", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + for (uint32_t i = 2; i <= 8; i++) { + as.VSUXSEGEI64(i, v4, x10, v12, VecMask::No); + REQUIRE(value == (0x06C57227 | ((i - 1) << 29))); + + as.RewindBuffer(); + + as.VSUXSEGEI64(i, v4, x10, v12, VecMask::Yes); + REQUIRE(value == (0x04C57227 | ((i - 1) << 29))); + + as.RewindBuffer(); + } +} + +TEST_CASE("Vector whole register stores", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSR(1, v3, x11); + REQUIRE(value == 0x028581A7); + + as.RewindBuffer(); + + as.VSR(2, v2, x11); + REQUIRE(value == 0x22858127); + + as.RewindBuffer(); + + as.VSR(4, v4, x11); + REQUIRE(value == 0x62858227); + + as.RewindBuffer(); + + as.VSR(8, v8, x11); + REQUIRE(value == 0xE2858427); +} + +TEST_CASE("VSETIVLI", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSETIVLI(x10, 11, SEW::E8, LMUL::M1, VTA::No, VMA::No); + REQUIRE(value == 0xC005F557); + + as.RewindBuffer(); + + as.VSETIVLI(x10, 11, SEW::E16, LMUL::M2, VTA::No, VMA::No); + REQUIRE(value == 0xC095F557); + + as.RewindBuffer(); + + as.VSETIVLI(x10, 11, SEW::E256, LMUL::M2, VTA::Yes, VMA::No); + REQUIRE(value == 0xC695F557); + + as.RewindBuffer(); + + as.VSETIVLI(x10, 11, SEW::E256, LMUL::M2, VTA::Yes, VMA::Yes); + REQUIRE(value == 0xCE95F557); +} + +TEST_CASE("VSETVL", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSETVL(x10, x11, x12); + REQUIRE(value == 0x80C5F557); +} + +TEST_CASE("VSETVLI", "[rvv]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSETVLI(x31, x6, SEW::E64, LMUL::MF2, VTA::Yes, VMA::Yes); + REQUIRE(value == 0x0DF37FD7); + + as.RewindBuffer(); + + as.VSETVLI(x31, x6, SEW::E64, LMUL::MF2, VTA::No, VMA::No); + REQUIRE(value == 0x01F37FD7); + + as.RewindBuffer(); + + as.VSETVLI(x12, x18, SEW::E8, LMUL::M1, VTA::No, VMA::No); + REQUIRE(value == 0x00097657); + + as.RewindBuffer(); + + as.VSETVLI(x15, x12, SEW::E32, LMUL::M4, VTA::No, VMA::No); + REQUIRE(value == 0x012677D7); +} diff --git a/externals/biscuit/tests/src/assembler_test_utils.hpp b/externals/biscuit/tests/src/assembler_test_utils.hpp new file mode 100644 index 00000000..a20737dc --- /dev/null +++ b/externals/biscuit/tests/src/assembler_test_utils.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include <biscuit/assembler.hpp> +#include <cstdint> + +namespace biscuit { + +template <typename T> +inline Assembler MakeAssembler32(T& buffer) { + return Assembler{reinterpret_cast<uint8_t*>(&buffer), sizeof(buffer), ArchFeature::RV32}; +} + +template <typename T> +inline Assembler MakeAssembler64(T& buffer) { + return Assembler{reinterpret_cast<uint8_t*>(&buffer), sizeof(buffer), ArchFeature::RV64}; +} + +template <typename T> +inline Assembler MakeAssembler128(T& buffer) { + return Assembler{reinterpret_cast<uint8_t*>(&buffer), sizeof(buffer), ArchFeature::RV128}; +} + +} // namespace biscuit diff --git a/externals/biscuit/tests/src/assembler_vector_crypto_tests.cpp b/externals/biscuit/tests/src/assembler_vector_crypto_tests.cpp new file mode 100644 index 00000000..fbfe0fa1 --- /dev/null +++ b/externals/biscuit/tests/src/assembler_vector_crypto_tests.cpp @@ -0,0 +1,495 @@ +#include <catch/catch.hpp> + +#include <biscuit/assembler.hpp> + +#include "assembler_test_utils.hpp" + +using namespace biscuit; + +TEST_CASE("VANDN.VV", "[Zvbb]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VANDN(v20, v12, v10, VecMask::Yes); + REQUIRE(value == 0x04C50A57); + + as.RewindBuffer(); + + as.VANDN(v20, v12, v10, VecMask::No); + REQUIRE(value == 0x06C50A57); +} + +TEST_CASE("VANDN.VX", "[Zvbb]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VANDN(v20, v12, x10, VecMask::Yes); + REQUIRE(value == 0x04C54A57); + + as.RewindBuffer(); + + as.VANDN(v20, v12, x10, VecMask::No); + REQUIRE(value == 0x06C54A57); +} + +TEST_CASE("VBREV.V", "[Zvbb]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VBREV(v20, v12, VecMask::Yes); + REQUIRE(value == 0x48C52A57); + + as.RewindBuffer(); + + as.VBREV(v20, v12, VecMask::No); + REQUIRE(value == 0x4AC52A57); +} + +TEST_CASE("VBREV8.V", "[Zvbb]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VBREV8(v20, v12, VecMask::Yes); + REQUIRE(value == 0x48C42A57); + + as.RewindBuffer(); + + as.VBREV8(v20, v12, VecMask::No); + REQUIRE(value == 0x4AC42A57); +} + +TEST_CASE("VREV8.V", "[Zvbb]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VREV8(v20, v12, VecMask::Yes); + REQUIRE(value == 0x48C4AA57); + + as.RewindBuffer(); + + as.VREV8(v20, v12, VecMask::No); + REQUIRE(value == 0x4AC4AA57); +} + +TEST_CASE("VCLZ.V", "[Zvbb]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VCLZ(v20, v12, VecMask::Yes); + REQUIRE(value == 0x48C62A57); + + as.RewindBuffer(); + + as.VCLZ(v20, v12, VecMask::No); + REQUIRE(value == 0x4AC62A57); +} + +TEST_CASE("VCTZ.V", "[Zvbb]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VCTZ(v20, v12, VecMask::Yes); + REQUIRE(value == 0x48C6AA57); + + as.RewindBuffer(); + + as.VCTZ(v20, v12, VecMask::No); + REQUIRE(value == 0x4AC6AA57); +} + +TEST_CASE("VCPOP.V", "[Zvbb]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VCPOP(v20, v12, VecMask::Yes); + REQUIRE(value == 0x48C72A57); + + as.RewindBuffer(); + + as.VCPOP(v20, v12, VecMask::No); + REQUIRE(value == 0x4AC72A57); +} + +TEST_CASE("VROL.VV", "[Zvbb]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VROL(v20, v12, v10, VecMask::Yes); + REQUIRE(value == 0x54C50A57); + + as.RewindBuffer(); + + as.VROL(v20, v12, v10, VecMask::No); + REQUIRE(value == 0x56C50A57); +} + +TEST_CASE("VROL.VX", "[Zvbb]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VROL(v20, v12, x10, VecMask::Yes); + REQUIRE(value == 0x54C54A57); + + as.RewindBuffer(); + + as.VROL(v20, v12, x10, VecMask::No); + REQUIRE(value == 0x56C54A57); +} + +TEST_CASE("VROR.VV", "[Zvbb]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VROR(v20, v12, v10, VecMask::Yes); + REQUIRE(value == 0x50C50A57); + + as.RewindBuffer(); + + as.VROR(v20, v12, v10, VecMask::No); + REQUIRE(value == 0x52C50A57); +} + +TEST_CASE("VROR.VX", "[Zvbb]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VROR(v20, v12, x10, VecMask::Yes); + REQUIRE(value == 0x50C54A57); + + as.RewindBuffer(); + + as.VROR(v20, v12, x10, VecMask::No); + REQUIRE(value == 0x52C54A57); +} + +TEST_CASE("VROR.VI", "[Zvbb]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VROR(v20, v12, 63, VecMask::Yes); + REQUIRE(value == 0x54CFBA57); + + as.RewindBuffer(); + + as.VROR(v20, v12, 31, VecMask::Yes); + REQUIRE(value == 0x50CFBA57); + + as.RewindBuffer(); + + as.VROR(v20, v12, 63, VecMask::No); + REQUIRE(value == 0x56CFBA57); + + as.RewindBuffer(); + + as.VROR(v20, v12, 31, VecMask::No); + REQUIRE(value == 0x52CFBA57); +} + +TEST_CASE("VWSLL.VV", "[Zvbb]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VWSLL(v20, v12, v10, VecMask::Yes); + REQUIRE(value == 0xD4C50A57); + + as.RewindBuffer(); + + as.VWSLL(v20, v12, v10, VecMask::No); + REQUIRE(value == 0xD6C50A57); +} + +TEST_CASE("VWSLL.VX", "[Zvbb]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VWSLL(v20, v12, x10, VecMask::Yes); + REQUIRE(value == 0xD4C54A57); + + as.RewindBuffer(); + + as.VWSLL(v20, v12, x10, VecMask::No); + REQUIRE(value == 0xD6C54A57); +} + +TEST_CASE("VWSLL.VI", "[Zvbb]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VWSLL(v20, v12, 31, VecMask::Yes); + REQUIRE(value == 0xD4CFBA57); + + as.RewindBuffer(); + + as.VWSLL(v20, v12, 15, VecMask::Yes); + REQUIRE(value == 0xD4C7BA57); + + as.RewindBuffer(); + + as.VWSLL(v20, v12, 31, VecMask::No); + REQUIRE(value == 0xD6CFBA57); + + as.RewindBuffer(); + + as.VWSLL(v20, v12, 15, VecMask::No); + REQUIRE(value == 0xD6C7BA57); +} + +TEST_CASE("VCLMUL.VV", "[Zvbc]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VCLMUL(v20, v12, v10, VecMask::Yes); + REQUIRE(value == 0x30C52A57); + + as.RewindBuffer(); + + as.VCLMUL(v20, v12, v10, VecMask::No); + REQUIRE(value == 0x32C52A57); +} + +TEST_CASE("VCLMUL.VX", "[Zvbc]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VCLMUL(v20, v12, x10, VecMask::Yes); + REQUIRE(value == 0x30C56A57); + + as.RewindBuffer(); + + as.VCLMUL(v20, v12, x10, VecMask::No); + REQUIRE(value == 0x32C56A57); +} + +TEST_CASE("VCLMULH.VV", "[Zvbc]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VCLMULH(v20, v12, v10, VecMask::Yes); + REQUIRE(value == 0x34C52A57); + + as.RewindBuffer(); + + as.VCLMULH(v20, v12, v10, VecMask::No); + REQUIRE(value == 0x36C52A57); +} + +TEST_CASE("VCLMULH.VX", "[Zvbc]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VCLMULH(v20, v12, x10, VecMask::Yes); + REQUIRE(value == 0x34C56A57); + + as.RewindBuffer(); + + as.VCLMULH(v20, v12, x10, VecMask::No); + REQUIRE(value == 0x36C56A57); +} + +TEST_CASE("VGHSH.VV", "[Zvkg]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VGHSH(v20, v12, v10); + REQUIRE(value == 0xB2C52A77); +} + +TEST_CASE("VGMUL.VV", "[Zvkg]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VGMUL(v20, v12); + REQUIRE(value == 0xA2C8AA77); +} + +TEST_CASE("VAESDF.VV", "[Zvkned]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VAESDF_VV(v20, v12); + REQUIRE(value == 0xA2C0AA77); +} + +TEST_CASE("VAESDF.VS", "[Zvkned]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VAESDF_VS(v20, v12); + REQUIRE(value == 0xA6C0AA77); +} + +TEST_CASE("VAESDM.VV", "[Zvkned]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VAESDM_VV(v20, v12); + REQUIRE(value == 0xA2C02A77); +} + +TEST_CASE("VAESDM.VS", "[Zvkned]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VAESDM_VS(v20, v12); + REQUIRE(value == 0xA6C02A77); +} + +TEST_CASE("VAESEF.VV", "[Zvkned]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VAESEF_VV(v20, v12); + REQUIRE(value == 0xA2C1AA77); +} + +TEST_CASE("VAESEF.VS", "[Zvkned]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VAESEF_VS(v20, v12); + REQUIRE(value == 0xA6C1AA77); +} + +TEST_CASE("VAESEM.VV", "[Zvkned]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VAESEM_VV(v20, v12); + REQUIRE(value == 0xA2C12A77); +} + +TEST_CASE("VAESEM.VS", "[Zvkned]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VAESEM_VS(v20, v12); + REQUIRE(value == 0xA6C12A77); +} + +TEST_CASE("VAESKF1.VI", "[Zvkned]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + // Test mapping of out of range indices + for (const uint32_t idx : {0U, 11U, 12U, 13U, 14U, 15U}) { + as.VAESKF1(v20, v12, idx); + + const auto op_base = 0x8AC02A77U; + const auto inverted_b3 = idx ^ 0b1000; + const auto verify = op_base | (inverted_b3 << 15); + + REQUIRE(value == verify); + + as.RewindBuffer(); + } + + as.VAESKF1(v20, v12, 8); + REQUIRE(value == 0x8AC42A77); +} + +TEST_CASE("VAESKF2.VI", "[Zvkned]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + // Test mapping of out of range indices + for (const uint32_t idx : {0U, 1U, 15U}) { + as.VAESKF2(v20, v12, idx); + + const auto op_base = 0xAAC02A77; + const auto inverted_b3 = idx ^ 0b1000; + const auto verify = op_base | (inverted_b3 << 15); + + REQUIRE(value == verify); + + as.RewindBuffer(); + } + + as.VAESKF2(v20, v12, 8); + REQUIRE(value == 0xAAC42A77); +} + +TEST_CASE("VAESZ.VS", "[Zvkned]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VAESZ(v20, v12); + REQUIRE(value == 0xA6C3AA77); +} + +TEST_CASE("VSHA2MS.VV", "[Zvknhb]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSHA2MS(v20, v12, v10); + REQUIRE(value == 0xB6C52A77); +} + +TEST_CASE("VSHA2CH.VV", "[Zvknhb]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSHA2CH(v20, v12, v10); + REQUIRE(value == 0xBAC52A77); +} + +TEST_CASE("VSHA2CL.VV", "[Zvknhb]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSHA2CL(v20, v12, v10); + REQUIRE(value == 0xBEC52A77); +} + +TEST_CASE("VSM4K.VI", "[Zvksed]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + for (uint32_t i = 0; i <= 7; i++) { + as.VSM4K(v20, v12, i); + + const auto op_base = 0x86C02A77U; + const auto verify = op_base | (i << 15); + REQUIRE(value == verify); + + as.RewindBuffer(); + } +} + +TEST_CASE("VSM4R.VV", "[Zvksed]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSM4R_VV(v20, v12); + REQUIRE(value == 0xA2C82A77); +} + +TEST_CASE("VSM4R.VS", "[Zvksed]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSM4R_VS(v20, v12); + REQUIRE(value == 0xA6C82A77); +} + +TEST_CASE("VSM3C.VI", "[Zvksh]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + for (uint32_t i = 0; i <= 31; i++) { + as.VSM3C(v20, v12, i); + + const auto op_base = 0xAEC02A77U; + const auto verify = op_base | (i << 15); + REQUIRE(value == verify); + + as.RewindBuffer(); + } +} + +TEST_CASE("VSM3ME.VV", "[Zvksh]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.VSM3ME(v20, v12, v10); + REQUIRE(value == 0x82C52A77); +} diff --git a/externals/biscuit/tests/src/assembler_zacas_tests.cpp b/externals/biscuit/tests/src/assembler_zacas_tests.cpp new file mode 100644 index 00000000..f0891fba --- /dev/null +++ b/externals/biscuit/tests/src/assembler_zacas_tests.cpp @@ -0,0 +1,76 @@ +#include <catch/catch.hpp> + +#include <biscuit/assembler.hpp> + +#include "assembler_test_utils.hpp" + +using namespace biscuit; + +TEST_CASE("AMOCAS.D", "[Zacas]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.AMOCAS_D(Ordering::None, x31, x7, x15); + REQUIRE(value == 0x2877BFAF); + + as.RewindBuffer(); + + as.AMOCAS_D(Ordering::AQ, x31, x7, x15); + REQUIRE(value == 0x2C77BFAF); + + as.RewindBuffer(); + + as.AMOCAS_D(Ordering::RL, x31, x7, x15); + REQUIRE(value == 0x2A77BFAF); + + as.RewindBuffer(); + + as.AMOCAS_D(Ordering::AQRL, x31, x7, x15); + REQUIRE(value == 0x2E77BFAF); +} + +TEST_CASE("AMOCAS.Q", "[Zacas]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.AMOCAS_Q(Ordering::None, x30, x6, x14); + REQUIRE(value == 0x28674F2F); + + as.RewindBuffer(); + + as.AMOCAS_Q(Ordering::AQ, x30, x6, x14); + REQUIRE(value == 0x2C674F2F); + + as.RewindBuffer(); + + as.AMOCAS_Q(Ordering::RL, x30, x6, x14); + REQUIRE(value == 0x2A674F2F); + + as.RewindBuffer(); + + as.AMOCAS_Q(Ordering::AQRL, x30, x6, x14); + REQUIRE(value == 0x2E674F2F); +} + +TEST_CASE("AMOCAS.W", "[Zacas]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.AMOCAS_W(Ordering::None, x31, x7, x15); + REQUIRE(value == 0x2877AFAF); + + as.RewindBuffer(); + + as.AMOCAS_W(Ordering::AQ, x31, x7, x15); + REQUIRE(value == 0x2C77AFAF); + + as.RewindBuffer(); + + as.AMOCAS_W(Ordering::RL, x31, x7, x15); + REQUIRE(value == 0x2A77AFAF); + + as.RewindBuffer(); + + as.AMOCAS_W(Ordering::AQRL, x31, x7, x15); + REQUIRE(value == 0x2E77AFAF); +} diff --git a/externals/biscuit/tests/src/assembler_zawrs_tests.cpp b/externals/biscuit/tests/src/assembler_zawrs_tests.cpp new file mode 100644 index 00000000..ed49bfe5 --- /dev/null +++ b/externals/biscuit/tests/src/assembler_zawrs_tests.cpp @@ -0,0 +1,23 @@ +#include <catch/catch.hpp> + +#include <biscuit/assembler.hpp> + +#include "assembler_test_utils.hpp" + +using namespace biscuit; + +TEST_CASE("WRS.NTO", "[Zawrs]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.WRS_NTO(); + REQUIRE(value == 0x00D00073); +} + +TEST_CASE("WRS.STO", "[Zawrs]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.WRS_STO(); + REQUIRE(value == 0x01D00073); +} diff --git a/externals/biscuit/tests/src/assembler_zc_tests.cpp b/externals/biscuit/tests/src/assembler_zc_tests.cpp new file mode 100644 index 00000000..1c7d9d95 --- /dev/null +++ b/externals/biscuit/tests/src/assembler_zc_tests.cpp @@ -0,0 +1,457 @@ +#include <catch/catch.hpp> + +#include <array> +#include <biscuit/assembler.hpp> + +#include "assembler_test_utils.hpp" + +using namespace biscuit; + +TEST_CASE("C.LBU", "[Zc]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.C_LBU(x12, 0, x15); + REQUIRE(value == 0x8390U); + + as.RewindBuffer(); + + as.C_LBU(x12, 1, x15); + REQUIRE(value == 0x83D0U); + + as.RewindBuffer(); + + as.C_LBU(x12, 2, x15); + REQUIRE(value == 0x83B0U); + + as.RewindBuffer(); + + as.C_LBU(x12, 3, x15); + REQUIRE(value == 0x83F0U); +} + +TEST_CASE("C.LH", "[Zc]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.C_LH(x12, 0, x15); + REQUIRE(value == 0x87D0U); + + as.RewindBuffer(); + + as.C_LH(x12, 2, x15); + REQUIRE(value == 0x87F0U); +} + +TEST_CASE("C.LHU", "[Zc]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.C_LHU(x12, 0, x15); + REQUIRE(value == 0x8790U); + + as.RewindBuffer(); + + as.C_LHU(x12, 2, x15); + REQUIRE(value == 0x87B0U); +} + +TEST_CASE("C.SB", "[Zc]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.C_SB(x12, 0, x15); + REQUIRE(value == 0x8B90U); + + as.RewindBuffer(); + + as.C_SB(x12, 1, x15); + REQUIRE(value == 0x8BD0U); + + as.RewindBuffer(); + + as.C_SB(x12, 2, x15); + REQUIRE(value == 0x8BB0U); + + as.RewindBuffer(); + + as.C_SB(x12, 3, x15); + REQUIRE(value == 0x8BF0U); +} + +TEST_CASE("C.SH", "[Zc]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.C_SH(x12, 0, x15); + REQUIRE(value == 0x8F90U); + + as.RewindBuffer(); + + as.C_SH(x12, 2, x15); + REQUIRE(value == 0x8FB0U); +} + +TEST_CASE("C.SEXT.B", "[Zc]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.C_SEXT_B(x12); + REQUIRE(value == 0x9E65); + + as.RewindBuffer(); + + as.C_SEXT_B(x15); + REQUIRE(value == 0x9FE5); +} + +TEST_CASE("C.SEXT.H", "[Zc]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.C_SEXT_H(x12); + REQUIRE(value == 0x9E6D); + + as.RewindBuffer(); + + as.C_SEXT_H(x15); + REQUIRE(value == 0x9FED); +} + +TEST_CASE("C.ZEXT.B", "[Zc]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.C_ZEXT_B(x12); + REQUIRE(value == 0x9E61); + + as.RewindBuffer(); + + as.C_ZEXT_B(x15); + REQUIRE(value == 0x9FE1); +} + +TEST_CASE("C.ZEXT.H", "[Zc]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.C_ZEXT_H(x12); + REQUIRE(value == 0x9E69); + + as.RewindBuffer(); + + as.C_ZEXT_H(x15); + REQUIRE(value == 0x9FE9); +} + +TEST_CASE("C.ZEXT.W", "[Zc]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.C_ZEXT_W(x12); + REQUIRE(value == 0x9E71); + + as.RewindBuffer(); + + as.C_ZEXT_W(x15); + REQUIRE(value == 0x9FF1); +} + +TEST_CASE("C.MUL", "[Zc]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.C_MUL(x12, x15); + REQUIRE(value == 0x9E5D); + + as.RewindBuffer(); + + as.C_MUL(x15, x12); + REQUIRE(value == 0x9FD1); +} + +TEST_CASE("C.NOT", "[Zc]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.C_NOT(x12); + REQUIRE(value == 0x9E75); + + as.RewindBuffer(); + + as.C_NOT(x15); + REQUIRE(value == 0x9FF5); +} + +TEST_CASE("CM.MVA01S", "[Zc]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.CM_MVA01S(s7, s6); + REQUIRE(value == 0xAFFA); + + as.RewindBuffer(); + + as.CM_MVA01S(s3, s4); + REQUIRE(value == 0xADF2); +} + +TEST_CASE("CM.MVSA01", "[Zc]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.CM_MVSA01(s7, s6); + REQUIRE(value == 0xAFBA); + + as.RewindBuffer(); + + as.CM_MVSA01(s3, s4); + REQUIRE(value == 0xADB2); +} + +TEST_CASE("CM.JALT", "[Zc]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + for (uint32_t i = 32; i <= 255; i++) { + const uint32_t op_base = 0xA002; + const uint32_t op = op_base | (i << 2); + + as.CM_JALT(i); + REQUIRE(value == op); + + as.RewindBuffer(); + } +} + +TEST_CASE("CM.JT", "[Zc]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + for (uint32_t i = 0; i <= 31; i++) { + const uint32_t op_base = 0xA002; + const uint32_t op = op_base | (i << 2); + + as.CM_JT(i); + REQUIRE(value == op); + + as.RewindBuffer(); + } +} + +constexpr std::array stack_adj_bases_rv32{ + 0, 0, 0, 0, 16, 16, 16, 16, + 32, 32, 32, 32, 48, 48, 48, 64, +}; +constexpr std::array stack_adj_bases_rv64{ + 0, 0, 0, 0, 16, 16, 32, 32, + 48, 48, 64, 64, 80, 80, 96, 112, +}; + +TEST_CASE("CM.POP", "[Zc]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.CM_POP({ra}, 16); + REQUIRE(value == 0xBA42); + as.RewindBuffer(); + + // s10 intentionally omitted, since no direct encoding for it exists. + uint32_t rlist = 5; + for (const GPR sreg : {s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s11}) { + const auto op_base = 0xBA02U; + const auto stack_adj_base = stack_adj_bases_rv64[rlist]; + + for (int32_t i = 0; i <= 3; i++) { + const auto op = op_base | (rlist << 4) | uint32_t(i) << 2; + + as.CM_POP({ra, {s0, sreg}}, stack_adj_base + (16 * i)); + REQUIRE(value == op); + as.RewindBuffer(); + } + + rlist++; + } +} + +TEST_CASE("CM.POP (RV32)", "[Zc]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.CM_POP({ra}, 16); + REQUIRE(value == 0xBA42); + as.RewindBuffer(); + + // s10 intentionally omitted, since no direct encoding for it exists. + uint32_t rlist = 5; + for (const GPR sreg : {s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s11}) { + const auto op_base = 0xBA02U; + const auto stack_adj_base = stack_adj_bases_rv32[rlist]; + + for (int32_t i = 0; i <= 3; i++) { + const auto op = op_base | (rlist << 4) | uint32_t(i) << 2; + + as.CM_POP({ra, {s0, sreg}}, stack_adj_base + (16 * i)); + REQUIRE(value == op); + as.RewindBuffer(); + } + + rlist++; + } +} + +TEST_CASE("CM.POPRET", "[Zc]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.CM_POPRET({ra}, 16); + REQUIRE(value == 0xBE42); + as.RewindBuffer(); + + // s10 intentionally omitted, since no direct encoding for it exists. + uint32_t rlist = 5; + for (const GPR sreg : {s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s11}) { + const auto op_base = 0xBE02U; + const auto stack_adj_base = stack_adj_bases_rv64[rlist]; + + for (int32_t i = 0; i <= 3; i++) { + const auto op = op_base | (rlist << 4) | uint32_t(i) << 2; + + as.CM_POPRET({ra, {s0, sreg}}, stack_adj_base + (16 * i)); + REQUIRE(value == op); + as.RewindBuffer(); + } + + rlist++; + } +} + +TEST_CASE("CM.POPRET (RV32)", "[Zc]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.CM_POPRET({ra}, 16); + REQUIRE(value == 0xBE42); + as.RewindBuffer(); + + // s10 intentionally omitted, since no direct encoding for it exists. + uint32_t rlist = 5; + for (const GPR sreg : {s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s11}) { + const auto op_base = 0xBE02U; + const auto stack_adj_base = stack_adj_bases_rv32[rlist]; + + for (int32_t i = 0; i <= 3; i++) { + const auto op = op_base | (rlist << 4) | uint32_t(i) << 2; + + as.CM_POPRET({ra, {s0, sreg}}, stack_adj_base + (16 * i)); + REQUIRE(value == op); + as.RewindBuffer(); + } + + rlist++; + } +} + +TEST_CASE("CM.POPRETZ", "[Zc]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.CM_POPRETZ({ra}, 16); + REQUIRE(value == 0xBC42); + as.RewindBuffer(); + + // s10 intentionally omitted, since no direct encoding for it exists. + uint32_t rlist = 5; + for (const GPR sreg : {s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s11}) { + const auto op_base = 0xBC02U; + const auto stack_adj_base = stack_adj_bases_rv64[rlist]; + + for (int32_t i = 0; i <= 3; i++) { + const auto op = op_base | (rlist << 4) | uint32_t(i) << 2; + + as.CM_POPRETZ({ra, {s0, sreg}}, stack_adj_base + (16 * i)); + REQUIRE(value == op); + as.RewindBuffer(); + } + + rlist++; + } +} + +TEST_CASE("CM.POPRETZ (RV32)", "[Zc]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.CM_POPRETZ({ra}, 16); + REQUIRE(value == 0xBC42); + as.RewindBuffer(); + + // s10 intentionally omitted, since no direct encoding for it exists. + uint32_t rlist = 5; + for (const GPR sreg : {s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s11}) { + const auto op_base = 0xBC02U; + const auto stack_adj_base = stack_adj_bases_rv32[rlist]; + + for (int32_t i = 0; i <= 3; i++) { + const auto op = op_base | (rlist << 4) | uint32_t(i) << 2; + + as.CM_POPRETZ({ra, {s0, sreg}}, stack_adj_base + (16 * i)); + REQUIRE(value == op); + as.RewindBuffer(); + } + + rlist++; + } +} + +TEST_CASE("CM.PUSH", "[Zc]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.CM_PUSH({ra}, -16); + REQUIRE(value == 0xB842); + as.RewindBuffer(); + + // s10 intentionally omitted, since no direct encoding for it exists. + uint32_t rlist = 5; + for (const GPR sreg : {s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s11}) { + const auto op_base = 0xB802U; + const auto stack_adj_base = stack_adj_bases_rv64[rlist]; + + for (int32_t i = 0; i <= 3; i++) { + const auto op = op_base | (rlist << 4) | uint32_t(i) << 2; + + as.CM_PUSH({ra, {s0, sreg}}, -stack_adj_base + (-16 * i)); + REQUIRE(value == op); + as.RewindBuffer(); + } + + rlist++; + } +} + +TEST_CASE("CM.PUSH (RV32)", "[Zc]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.CM_PUSH({ra}, -16); + REQUIRE(value == 0xB842); + as.RewindBuffer(); + + // s10 intentionally omitted, since no direct encoding for it exists. + uint32_t rlist = 5; + for (const GPR sreg : {s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s11}) { + const auto op_base = 0xB802U; + const auto stack_adj_base = stack_adj_bases_rv32[rlist]; + + for (int32_t i = 0; i <= 3; i++) { + const auto op = op_base | (rlist << 4) | uint32_t(i) << 2; + + as.CM_PUSH({ra, {s0, sreg}}, -stack_adj_base + (-16 * i)); + REQUIRE(value == op); + as.RewindBuffer(); + } + + rlist++; + } +} diff --git a/externals/biscuit/tests/src/assembler_zfa_tests.cpp b/externals/biscuit/tests/src/assembler_zfa_tests.cpp new file mode 100644 index 00000000..d73297d4 --- /dev/null +++ b/externals/biscuit/tests/src/assembler_zfa_tests.cpp @@ -0,0 +1,414 @@ +#include <catch/catch.hpp> + +#include <array> +#include <cstring> + +#include <biscuit/assembler.hpp> + +#include "assembler_test_utils.hpp" + +using namespace biscuit; + +static constexpr std::array fli_constants{ + 0xBFF0000000000000ULL, // -1.0 + 0x0010000000000000ULL, // Minimum positive normal + 0x3EF0000000000000ULL, // 1.0 * 2^-16 + 0x3F00000000000000ULL, // 1.0 * 2^-15 + 0x3F70000000000000ULL, // 1.0 * 2^-8 + 0x3F80000000000000ULL, // 1.0 * 2^-7 + 0x3FB0000000000000ULL, // 1.0 * 2^-4 + 0x3FC0000000000000ULL, // 1.0 * 2^-3 + 0x3FD0000000000000ULL, // 0.25 + 0x3FD4000000000000ULL, // 0.3125 + 0x3FD8000000000000ULL, // 0.375 + 0x3FDC000000000000ULL, // 0.4375 + 0x3FE0000000000000ULL, // 0.5 + 0x3FE4000000000000ULL, // 0.625 + 0x3FE8000000000000ULL, // 0.75 + 0x3FEC000000000000ULL, // 0.875 + 0x3FF0000000000000ULL, // 1.0 + 0x3FF4000000000000ULL, // 1.25 + 0x3FF8000000000000ULL, // 1.5 + 0x3FFC000000000000ULL, // 1.75 + 0x4000000000000000ULL, // 2.0 + 0x4004000000000000ULL, // 2.5 + 0x4008000000000000ULL, // 3 + 0x4010000000000000ULL, // 4 + 0x4020000000000000ULL, // 8 + 0x4030000000000000ULL, // 16 + 0x4060000000000000ULL, // 2^7 + 0x4070000000000000ULL, // 2^8 + 0x40E0000000000000ULL, // 2^15 + 0x40F0000000000000ULL, // 2^16 + 0x7FF0000000000000ULL, // +inf + 0x7FF8000000000000ULL, // Canonical NaN +}; + +TEST_CASE("FLI.D", "[Zfa]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + for (size_t i = 0; i < fli_constants.size(); i++) { + const auto constant = fli_constants[i]; + double fconstant{}; + std::memcpy(&fconstant, &constant, sizeof(fconstant)); + + as.FLI_D(f10, fconstant); + + const auto op_base = 0xF2100553; + const auto verify = op_base | (i << 15); + REQUIRE(value == verify); + + as.RewindBuffer(); + } +} + +TEST_CASE("FLI.H", "[Zfa]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + for (size_t i = 0; i < fli_constants.size(); i++) { + const auto constant = fli_constants[i]; + double fconstant{}; + std::memcpy(&fconstant, &constant, sizeof(fconstant)); + + as.FLI_H(f10, fconstant); + + const auto op_base = 0xF4100553; + const auto verify = op_base | (i << 15); + REQUIRE(value == verify); + + as.RewindBuffer(); + } +} + +TEST_CASE("FLI.S", "[Zfa]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + for (size_t i = 0; i < fli_constants.size(); i++) { + const auto constant = fli_constants[i]; + double fconstant{}; + std::memcpy(&fconstant, &constant, sizeof(fconstant)); + + as.FLI_S(f10, fconstant); + + const auto op_base = 0xF0100553; + const auto verify = op_base | (i << 15); + REQUIRE(value == verify); + + as.RewindBuffer(); + } +} + +TEST_CASE("FMINM.D", "[Zfa]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.FMINM_D(f20, f12, f10); + REQUIRE(value == 0x2AA62A53); +} + +TEST_CASE("FMINM.H", "[Zfa]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.FMINM_H(f20, f12, f10); + REQUIRE(value == 0x2CA62A53); +} + +TEST_CASE("FMINM.Q", "[Zfa]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.FMINM_Q(f20, f12, f10); + REQUIRE(value == 0x2EA62A53); +} + +TEST_CASE("FMINM.S", "[Zfa]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.FMINM_S(f20, f12, f10); + REQUIRE(value == 0x28A62A53); +} + +TEST_CASE("FMAXM.D", "[Zfa]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.FMAXM_D(f20, f12, f10); + REQUIRE(value == 0x2AA63A53); +} + +TEST_CASE("FMAXM.H", "[Zfa]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.FMAXM_H(f20, f12, f10); + REQUIRE(value == 0x2CA63A53); +} + +TEST_CASE("FMAXM.Q", "[Zfa]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.FMAXM_Q(f20, f12, f10); + REQUIRE(value == 0x2EA63A53); +} + +TEST_CASE("FMAXM.S", "[Zfa]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.FMAXM_S(f20, f12, f10); + REQUIRE(value == 0x28A63A53); +} + +TEST_CASE("FROUND.D", "[Zfa]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.FROUND_D(f31, f7, RMode::RNE); + REQUIRE(value == 0x42438FD3); + + as.RewindBuffer(); + + as.FROUND_D(f31, f7, RMode::RMM); + REQUIRE(value == 0x4243CFD3); + + as.RewindBuffer(); + + as.FROUND_D(f31, f7, RMode::DYN); + REQUIRE(value == 0x4243FFD3); +} + +TEST_CASE("FROUND.H", "[Zfa]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.FROUND_H(f31, f7, RMode::RNE); + REQUIRE(value == 0x44438FD3); + + as.RewindBuffer(); + + as.FROUND_H(f31, f7, RMode::RMM); + REQUIRE(value == 0x4443CFD3); + + as.RewindBuffer(); + + as.FROUND_H(f31, f7, RMode::DYN); + REQUIRE(value == 0x4443FFD3); +} + +TEST_CASE("FROUND.Q", "[Zfa]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.FROUND_Q(f31, f7, RMode::RNE); + REQUIRE(value == 0x46438FD3); + + as.RewindBuffer(); + + as.FROUND_Q(f31, f7, RMode::RMM); + REQUIRE(value == 0x4643CFD3); + + as.RewindBuffer(); + + as.FROUND_Q(f31, f7, RMode::DYN); + REQUIRE(value == 0x4643FFD3); +} + +TEST_CASE("FROUND.S", "[Zfa]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.FROUND_S(f31, f7, RMode::RNE); + REQUIRE(value == 0x40438FD3); + + as.RewindBuffer(); + + as.FROUND_S(f31, f7, RMode::RMM); + REQUIRE(value == 0x4043CFD3); + + as.RewindBuffer(); + + as.FROUND_S(f31, f7, RMode::DYN); + REQUIRE(value == 0x4043FFD3); +} + +TEST_CASE("FROUNDNX.D", "[Zfa]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.FROUNDNX_D(f31, f7, RMode::RNE); + REQUIRE(value == 0x42538FD3); + + as.RewindBuffer(); + + as.FROUNDNX_D(f31, f7, RMode::RMM); + REQUIRE(value == 0x4253CFD3); + + as.RewindBuffer(); + + as.FROUNDNX_D(f31, f7, RMode::DYN); + REQUIRE(value == 0x4253FFD3); +} + +TEST_CASE("FROUNDNX.H", "[Zfa]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.FROUNDNX_H(f31, f7, RMode::RNE); + REQUIRE(value == 0x44538FD3); + + as.RewindBuffer(); + + as.FROUNDNX_H(f31, f7, RMode::RMM); + REQUIRE(value == 0x4453CFD3); + + as.RewindBuffer(); + + as.FROUNDNX_H(f31, f7, RMode::DYN); + REQUIRE(value == 0x4453FFD3); +} + +TEST_CASE("FROUNDNX.Q", "[Zfa]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.FROUNDNX_Q(f31, f7, RMode::RNE); + REQUIRE(value == 0x46538FD3); + + as.RewindBuffer(); + + as.FROUNDNX_Q(f31, f7, RMode::RMM); + REQUIRE(value == 0x4653CFD3); + + as.RewindBuffer(); + + as.FROUNDNX_Q(f31, f7, RMode::DYN); + REQUIRE(value == 0x4653FFD3); +} + +TEST_CASE("FROUNDNX.S", "[Zfa]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.FROUNDNX_S(f31, f7, RMode::RNE); + REQUIRE(value == 0x40538FD3); + + as.RewindBuffer(); + + as.FROUNDNX_S(f31, f7, RMode::RMM); + REQUIRE(value == 0x4053CFD3); + + as.RewindBuffer(); + + as.FROUNDNX_S(f31, f7, RMode::DYN); + REQUIRE(value == 0x4053FFD3); +} + +TEST_CASE("FCVTMOD.W.D", "[Zfa]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.FCVTMOD_W_D(x31, f7); + REQUIRE(value == 0xC2839FD3); +} + +TEST_CASE("FMVH.X.D", "[Zfa]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FMVH_X_D(x31, f7); + REQUIRE(value == 0xE2138FD3); +} + +TEST_CASE("FMVH.X.Q", "[Zfa]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.FMVH_X_Q(x31, f7); + REQUIRE(value == 0xE6138FD3); +} + +TEST_CASE("FMVP.D.X", "[Zfa]") { + uint32_t value = 0; + auto as = MakeAssembler32(value); + + as.FMVP_D_X(f31, x7, x8); + REQUIRE(value == 0xB2838FD3); +} + +TEST_CASE("FMVP.Q.X", "[Zfa]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.FMVP_Q_X(f31, x7, x8); + REQUIRE(value == 0xB6838FD3); +} + +TEST_CASE("FLEQ.D", "[Zfa]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.FLEQ_D(x31, f7, f15); + REQUIRE(value == 0xA2F3CFD3); +} + +TEST_CASE("FLTQ.D", "[Zfa]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.FLTQ_D(x31, f7, f15); + REQUIRE(value == 0xA2F3DFD3); +} + +TEST_CASE("FLEQ.H", "[Zfa]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.FLEQ_H(x31, f7, f15); + REQUIRE(value == 0xA4F3CFD3); +} + +TEST_CASE("FLTQ.H", "[Zfa]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.FLTQ_H(x31, f7, f15); + REQUIRE(value == 0xA4F3DFD3); +} + +TEST_CASE("FLEQ.Q", "[Zfa]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.FLEQ_Q(x31, f7, f15); + REQUIRE(value == 0xA6F3CFD3); +} + +TEST_CASE("FLTQ.Q", "[Zfa]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.FLTQ_Q(x31, f7, f15); + REQUIRE(value == 0xA6F3DFD3); +} + +TEST_CASE("FLEQ.S", "[Zfa]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.FLEQ_S(x31, f7, f15); + REQUIRE(value == 0xA0F3CFD3); +} + +TEST_CASE("FLTQ.S", "[Zfa]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.FLTQ_S(x31, f7, f15); + REQUIRE(value == 0xA0F3DFD3); +} diff --git a/externals/biscuit/tests/src/assembler_zicond_tests.cpp b/externals/biscuit/tests/src/assembler_zicond_tests.cpp new file mode 100644 index 00000000..68dc11b9 --- /dev/null +++ b/externals/biscuit/tests/src/assembler_zicond_tests.cpp @@ -0,0 +1,33 @@ +#include <catch/catch.hpp> + +#include <biscuit/assembler.hpp> + +#include "assembler_test_utils.hpp" + +using namespace biscuit; + +TEST_CASE("CZERO.EQZ", "[Zicond]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.CZERO_EQZ(x31, x30, x29); + REQUIRE(value == 0x0FDF5FB3); + + as.RewindBuffer(); + + as.CZERO_EQZ(x1, x2, x3); + REQUIRE(value == 0x0E3150B3); +} + +TEST_CASE("CZERO.NEZ", "[Zicond]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.CZERO_NEZ(x31, x30, x29); + REQUIRE(value == 0x0FDF7FB3); + + as.RewindBuffer(); + + as.CZERO_NEZ(x1, x2, x3); + REQUIRE(value == 0x0E3170B3); +} diff --git a/externals/biscuit/tests/src/assembler_zicsr_tests.cpp b/externals/biscuit/tests/src/assembler_zicsr_tests.cpp new file mode 100644 index 00000000..b3f5ad6d --- /dev/null +++ b/externals/biscuit/tests/src/assembler_zicsr_tests.cpp @@ -0,0 +1,130 @@ +#include <catch/catch.hpp> + +#include <biscuit/assembler.hpp> + +#include "assembler_test_utils.hpp" + +using namespace biscuit; + +TEST_CASE("CSRRC", "[Zicsr]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.CSRRC(x31, CSR::Cycle, x15); + REQUIRE(value == 0xC007BFF3); + + as.RewindBuffer(); + + as.CSRRC(x31, CSR::FFlags, x15); + REQUIRE(value == 0x0017BFF3); + + as.RewindBuffer(); + + as.CSRRC(x31, CSR::FRM, x15); + REQUIRE(value == 0x0027BFF3); + + as.RewindBuffer(); + + as.CSRRC(x31, CSR::FCSR, x15); + REQUIRE(value == 0x0037BFF3); +} + +TEST_CASE("CSRRCI", "[Zicsr]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.CSRRCI(x31, CSR::Cycle, 0); + REQUIRE(value == 0xC0007FF3); + + as.RewindBuffer(); + + as.CSRRCI(x31, CSR::FFlags, 0x1F); + REQUIRE(value == 0x001FFFF3); + + as.RewindBuffer(); + + as.CSRRCI(x31, CSR::FRM, 0x7); + REQUIRE(value == 0x0023FFF3); +} + +TEST_CASE("CSRRS", "[Zicsr]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.CSRRS(x31, CSR::Cycle, x15); + REQUIRE(value == 0xC007AFF3); + + as.RewindBuffer(); + + as.CSRRS(x31, CSR::FFlags, x15); + REQUIRE(value == 0x0017AFF3); + + as.RewindBuffer(); + + as.CSRRS(x31, CSR::FRM, x15); + REQUIRE(value == 0x0027AFF3); + + as.RewindBuffer(); + + as.CSRRS(x31, CSR::FCSR, x15); + REQUIRE(value == 0x0037AFF3); +} + +TEST_CASE("CSRRSI", "[Zicsr]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.CSRRSI(x31, CSR::Cycle, 0); + REQUIRE(value == 0xC0006FF3); + + as.RewindBuffer(); + + as.CSRRSI(x31, CSR::FFlags, 0x1F); + REQUIRE(value == 0x001FEFF3); + + as.RewindBuffer(); + + as.CSRRSI(x31, CSR::FRM, 0x7); + REQUIRE(value == 0x0023EFF3); +} + +TEST_CASE("CSRRW", "[Zicsr]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.CSRRW(x31, CSR::Cycle, x15); + REQUIRE(value == 0xC0079FF3); + + as.RewindBuffer(); + + as.CSRRW(x31, CSR::FFlags, x15); + REQUIRE(value == 0x00179FF3); + + as.RewindBuffer(); + + as.CSRRW(x31, CSR::FRM, x15); + REQUIRE(value == 0x00279FF3); + + as.RewindBuffer(); + + as.CSRRW(x31, CSR::FCSR, x15); + REQUIRE(value == 0x00379FF3); +} + +TEST_CASE("CSRRWI", "[Zicsr]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.CSRRWI(x31, CSR::Cycle, 0); + REQUIRE(value == 0xC0005FF3); + + as.RewindBuffer(); + + as.CSRRWI(x31, CSR::FFlags, 0x1F); + REQUIRE(value == 0x001FDFF3); + + as.RewindBuffer(); + + as.CSRRWI(x31, CSR::FRM, 0x7); + REQUIRE(value == 0x0023DFF3); +} diff --git a/externals/biscuit/tests/src/assembler_zihintntl_tests.cpp b/externals/biscuit/tests/src/assembler_zihintntl_tests.cpp new file mode 100644 index 00000000..5e81fb8c --- /dev/null +++ b/externals/biscuit/tests/src/assembler_zihintntl_tests.cpp @@ -0,0 +1,71 @@ +#include <catch/catch.hpp> + +#include <biscuit/assembler.hpp> + +#include "assembler_test_utils.hpp" + +using namespace biscuit; + +TEST_CASE("C.NTL.ALL", "[Zihintntl]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.C_NTL_ALL(); + REQUIRE(value == 0x9016); +} + +TEST_CASE("C.NTL.S1", "[Zihintntl]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.C_NTL_S1(); + REQUIRE(value == 0x9012); +} + +TEST_CASE("C.NTL.P1", "[Zihintntl]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.C_NTL_P1(); + REQUIRE(value == 0x900A); +} + +TEST_CASE("C.NTL.PALL", "[Zihintntl]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.C_NTL_PALL(); + REQUIRE(value == 0x900E); +} + +TEST_CASE("NTL.ALL", "[Zihintntl]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.NTL_ALL(); + REQUIRE(value == 0x00500033); +} + +TEST_CASE("NTL.S1", "[Zihintntl]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.NTL_S1(); + REQUIRE(value == 0x00400033); +} + +TEST_CASE("NTL.P1", "[Zihintntl]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.NTL_P1(); + REQUIRE(value == 0x00200033); +} + +TEST_CASE("NTL.PALL", "[Zihintntl]") { + uint32_t value = 0; + auto as = MakeAssembler64(value); + + as.NTL_PALL(); + REQUIRE(value == 0x00300033); +} diff --git a/externals/biscuit/tests/src/main.cpp b/externals/biscuit/tests/src/main.cpp new file mode 100644 index 00000000..4c506ba0 --- /dev/null +++ b/externals/biscuit/tests/src/main.cpp @@ -0,0 +1,2 @@ +#define CATCH_CONFIG_MAIN +#include <catch/catch.hpp> |