aboutsummaryrefslogtreecommitdiff
path: root/backend/unittests/Target
diff options
context:
space:
mode:
Diffstat (limited to 'backend/unittests/Target')
-rw-r--r--backend/unittests/Target/CMakeLists.txt21
-rw-r--r--backend/unittests/Target/DescriptionGeneratorTest.cpp118
-rw-r--r--backend/unittests/Target/RISCVTargetTest.cpp109
-rw-r--r--backend/unittests/Target/TestTarget.cpp3
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