diff options
Diffstat (limited to 'backend/unittests/Target')
| -rw-r--r-- | backend/unittests/Target/CMakeLists.txt | 21 | ||||
| -rw-r--r-- | backend/unittests/Target/DescriptionGeneratorTest.cpp | 118 | ||||
| -rw-r--r-- | backend/unittests/Target/RISCVTargetTest.cpp | 109 | ||||
| -rw-r--r-- | backend/unittests/Target/TestTarget.cpp | 3 |
4 files changed, 249 insertions, 2 deletions
diff --git a/backend/unittests/Target/CMakeLists.txt b/backend/unittests/Target/CMakeLists.txt index 514cb97..1ceb1ef 100644 --- a/backend/unittests/Target/CMakeLists.txt +++ b/backend/unittests/Target/CMakeLists.txt @@ -10,8 +10,27 @@ target_include_directories( ) target_link_libraries(RCCBackendTestTarget PUBLIC RCCBackend::CodeGen) +add_executable(RCCBackendDescriptionGeneratorTests DescriptionGeneratorTest.cpp) +rcc_backend_configure_target(RCCBackendDescriptionGeneratorTests) +target_link_libraries( + RCCBackendDescriptionGeneratorTests + PRIVATE RCCBackendDescriptionGeneratorCore +) +add_test(NAME RCCBackendDescriptionGeneratorTests COMMAND RCCBackendDescriptionGeneratorTests) + +add_executable(RCCBackendRISCVTargetTests RISCVTargetTest.cpp) +rcc_backend_configure_target(RCCBackendRISCVTargetTests) +target_link_libraries(RCCBackendRISCVTargetTests PRIVATE RCCBackend::RISCV) +add_test(NAME RCCBackendRISCVTargetTests COMMAND RCCBackendRISCVTargetTests) + rcc_backend_register_format_files( + "${CMAKE_CURRENT_SOURCE_DIR}/DescriptionGeneratorTest.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/RISCVTargetTest.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/TestTarget.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/TestTarget.h" ) -rcc_backend_register_tidy_files("${CMAKE_CURRENT_SOURCE_DIR}/TestTarget.cpp") +rcc_backend_register_tidy_files( + "${CMAKE_CURRENT_SOURCE_DIR}/DescriptionGeneratorTest.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/RISCVTargetTest.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/TestTarget.cpp" +) diff --git a/backend/unittests/Target/DescriptionGeneratorTest.cpp b/backend/unittests/Target/DescriptionGeneratorTest.cpp new file mode 100644 index 0000000..0ba52b2 --- /dev/null +++ b/backend/unittests/Target/DescriptionGeneratorTest.cpp @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "TargetDescriptionGenerator.h" + +#include <iostream> +#include <string> +#include <string_view> + +namespace { + +class TestContext final { +public: + void expect(bool Condition, std::string_view Message) { + if (Condition) + return; + std::cerr << Message << '\n'; + ++FailureCount; + } + + [[nodiscard]] int result() const noexcept { + return FailureCount == 0 ? 0 : 1; + } + +private: + int FailureCount = 0; +}; + +[[nodiscard]] std::string validDescription(std::string_view Aliases = "[acc]") { + return std::string(R"(schema: rcc-target-description-v1 +target: + namespace: tiny + name: Tiny + triple: tiny-unknown-none + cpu: tiny-cpu + abi: tiny-abi + data_layout: e-p:64:64 + features: [base] +register_units: + - { id: 1, symbol: U_R1, name: r1.unit } +registers: + - { id: 1, symbol: R1, name: r1, encoding: 0, dwarf_register: 0, units: [{ unit: U_R1, lanes: 1 }], aliases: )") + + std::string(Aliases) + R"( } +register_banks: + - { id: 1, symbol: GPR, name: GPR, maximum_size_bits: 64 } +register_classes: + - { id: 1, symbol: GPR64, name: GPR64, bank: GPR, size_bits: 64, spill_size_bits: 64, spill_alignment_bits: 64, lanes: 1, registers: [R1], allocation_order: [R1] } +register_masks: + - { name: CSR_TINY, preserved: [R1] } +)"; +} + +void testGeneration(TestContext &Context) { + const auto First = rcc::targetgen::generateDescription("tiny.rccdesc", validDescription()); + const auto Second = rcc::targetgen::generateDescription("tiny.rccdesc", validDescription()); + if (!First.succeeded()) { + for (const rcc::TextDiagnostic &Diagnostic : First.Diagnostics) + std::cerr << Diagnostic.Message << '\n'; + } + Context.expect(First.succeeded() && Second.succeeded(), + "a complete restricted target description must generate successfully"); + if (!First.succeeded() || !Second.succeeded()) + return; + + Context.expect(First.Output->RegisterHeader == Second.Output->RegisterHeader && + First.Output->TargetTables == Second.Output->TargetTables, + "target description generation must be deterministic"); + Context.expect( + First.Output->RegisterHeader.find("enum class PhysicalRegisterID") != std::string::npos && + First.Output->RegisterHeader.find("R1 = 1") != std::string::npos && + First.Output->TargetTables.find("appendGeneratedRegisterInfo") != std::string::npos && + First.Output->TargetTables.find("makePreservedMask") != std::string::npos, + "generated output must contain typed register IDs and static target tables"); +} + +void testSemanticRejection(TestContext &Context) { + const auto CollidingAlias = + rcc::targetgen::generateDescription("bad-alias.rccdesc", validDescription("[r1]")); + if (CollidingAlias.succeeded()) + std::cerr << "colliding alias description unexpectedly generated\n"; + Context.expect(!CollidingAlias.succeeded() && !CollidingAlias.Diagnostics.empty() && + CollidingAlias.Diagnostics.front().Message.find("aliases") != + std::string::npos, + "an ABI alias that collides with a canonical name must be rejected"); + + const std::string UnknownUnit = R"(schema: rcc-target-description-v1 +target: + namespace: tiny + name: Tiny + triple: tiny-unknown-none + cpu: tiny-cpu + abi: tiny-abi + data_layout: e-p:64:64 + features: [base] +register_units: + - { id: 1, symbol: U_R1, name: r1.unit } +registers: + - { id: 1, symbol: R1, name: r1, encoding: 0, dwarf_register: 0, units: [{ unit: U_MISSING, lanes: 1 }] } +register_banks: + - { id: 1, symbol: GPR, name: GPR, maximum_size_bits: 64 } +register_classes: + - { id: 1, symbol: GPR64, name: GPR64, bank: GPR, size_bits: 64, spill_size_bits: 64, spill_alignment_bits: 64, lanes: 1, registers: [R1], allocation_order: [R1] } +register_masks: [] +)"; + const auto BadUnit = rcc::targetgen::generateDescription("bad-unit.rccdesc", UnknownUnit); + Context.expect(!BadUnit.succeeded() && !BadUnit.Diagnostics.empty() && + BadUnit.Diagnostics.front().Message.find("declared units") != + std::string::npos, + "a register unit reference must resolve during semantic validation"); +} + +} // namespace + +int main() { + TestContext Context; + testGeneration(Context); + testSemanticRejection(Context); + return Context.result(); +} diff --git a/backend/unittests/Target/RISCVTargetTest.cpp b/backend/unittests/Target/RISCVTargetTest.cpp new file mode 100644 index 0000000..27a862d --- /dev/null +++ b/backend/unittests/Target/RISCVTargetTest.cpp @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "rcc/Target/RISCV/RISCVTarget.h" + +#include <cstdint> +#include <iostream> +#include <ranges> +#include <string_view> + +namespace { + +class TestContext final { +public: + void expect(bool Condition, std::string_view Message) { + if (Condition) + return; + std::cerr << Message << '\n'; + ++FailureCount; + } + + [[nodiscard]] int result() const noexcept { + return FailureCount == 0 ? 0 : 1; + } + +private: + int FailureCount = 0; +}; + +[[nodiscard]] rcc::PhysicalRegister registerID(rcc::riscv::PhysicalRegisterID ID) { + const auto Register = rcc::riscv::physicalRegister(ID); + if (!Register) + std::terminate(); + return *Register; +} + +void testRegisterFacts(TestContext &Context) { + auto Registers = rcc::riscv::createRegisterInfo(); + Context.expect(Registers.has_value(), "RV64GC register description must be valid"); + if (!Registers) + return; + + const rcc::PhysicalRegister X0 = registerID(rcc::riscv::PhysicalRegisterID::X0); + const rcc::PhysicalRegister X2 = registerID(rcc::riscv::PhysicalRegisterID::X2); + const rcc::PhysicalRegister X5 = registerID(rcc::riscv::PhysicalRegisterID::X5); + const rcc::PhysicalRegister X8 = registerID(rcc::riscv::PhysicalRegisterID::X8); + const rcc::PhysicalRegister F31 = registerID(rcc::riscv::PhysicalRegisterID::F31); + + Context.expect( + Registers->physicalRegisters().size() == 64 && Registers->registerUnits().size() == 64 && + Registers->registerClasses().size() == 6 && Registers->registerBanks().size() == 2, + "RV64GC must expose the complete scalar integer and floating register files"); + Context.expect(Registers->physicalRegister("x0") != nullptr && + Registers->physicalRegister("zero") == nullptr && + Registers->physicalRegisterByAlternativeName("ZERO") != nullptr && + Registers->physicalRegisterByAlternativeName("zero")->Reg == X0 && + Registers->physicalRegisterByAlternativeName("Sp") != nullptr && + Registers->physicalRegisterByAlternativeName("sp")->Reg == X2 && + Registers->physicalRegisterMIRName(X8) == "x8", + "canonical MIR names and case-insensitive ABI aliases must use separate lookups"); + Context.expect( + Registers->isReservedRegister(X0) && Registers->isReservedRegister(X2) && + !Registers->isReservedRegister(X5), + "zero, stack, global, and thread register constraints must be recorded statically"); + Context.expect(Registers->physicalRegister(F31) != nullptr && + Registers->physicalRegister(F31)->Encoding == 31, + "floating register physical IDs must remain distinct from their ISA encodings"); + + const auto *GPR = Registers->registerClass("gpr"); + Context.expect( + GPR != nullptr && std::ranges::find(GPR->AllocationOrder, X0) == GPR->AllocationOrder.end() && + std::ranges::find(GPR->AllocationOrder, X2) == GPR->AllocationOrder.end() && + std::ranges::find(GPR->AllocationOrder, X5) != GPR->AllocationOrder.end(), + "the general-register allocation order must exclude statically reserved registers"); +} + +void testABIAndDebugFacts(TestContext &Context) { + const rcc::PhysicalRegister X1 = registerID(rcc::riscv::PhysicalRegisterID::X1); + const rcc::PhysicalRegister X10 = registerID(rcc::riscv::PhysicalRegisterID::X10); + const rcc::PhysicalRegister F8 = registerID(rcc::riscv::PhysicalRegisterID::F8); + const rcc::PhysicalRegister F10 = registerID(rcc::riscv::PhysicalRegisterID::F10); + const rcc::PhysicalRegister F31 = registerID(rcc::riscv::PhysicalRegisterID::F31); + + const auto Mask = rcc::riscv::createCallPreservedMask(); + Context.expect( + Mask != nullptr && Mask->preserves(X1) && Mask->preserves(F8) && Mask->clobbers(X10) && + Mask->clobbers(F10), + "LP64D preserved-mask facts must distinguish saved and caller-clobbered registers"); + + auto MIRText = rcc::riscv::createMIRTextInfo(); + Context.expect(MIRText.has_value(), "RV64GC MIR text facts must be valid"); + if (!MIRText) + return; + Context.expect(MIRText->registerMasks().size() == 1 && + MIRText->registerMasks().front().Name == "csr_ilp32d_lp64d", + "RV64GC must expose its named LP64D call-preserved mask"); + Context.expect(MIRText->dwarfRegister(X1) == 1, + "the return-address register must retain its DWARF register number"); + Context.expect(MIRText->physicalRegister(63) == F31, + "the final floating register DWARF number must map to f31"); +} + +} // namespace + +int main() { + TestContext Context; + testRegisterFacts(Context); + testABIAndDebugFacts(Context); + return Context.result(); +} diff --git a/backend/unittests/Target/TestTarget.cpp b/backend/unittests/Target/TestTarget.cpp index 4d31fb9..a367454 100644 --- a/backend/unittests/Target/TestTarget.cpp +++ b/backend/unittests/Target/TestTarget.cpp @@ -25,7 +25,8 @@ namespace { [[nodiscard]] PhysicalRegisterDesc physicalRegister(std::uint32_t Number, const char *Name, std::uint32_t Encoding, std::vector<RegisterUnitLane> Units) { - return PhysicalRegisterDesc{phys(Number), Name, Encoding, std::move(Units)}; + return PhysicalRegisterDesc{phys(Number), Name, Encoding, std::move(Units), + false, false, false, {}}; } [[nodiscard]] RegisterClassDesc |
