aboutsummaryrefslogtreecommitdiff
path: root/backend/unittests/Tooling/BackendManifestTest.cpp
diff options
context:
space:
mode:
authorRichard-Qin-X <richard.qin.001@gmail.com>2026-08-10 20:36:50 +0800
committerRichard-Qin-X <richard.qin.001@gmail.com>2026-08-10 20:36:50 +0800
commitadcf8c9b1bd1b47e00bf859693112cea1f7e8c9f (patch)
tree59067aa71befab31678946dbf5e19f57131dd6f2 /backend/unittests/Tooling/BackendManifestTest.cpp
parent3a57627e59ba18b6f9f9aae7bb60c390c0d3575a (diff)
downloadrcc-project-adcf8c9b1bd1b47e00bf859693112cea1f7e8c9f.tar.gz
rcc-project-adcf8c9b1bd1b47e00bf859693112cea1f7e8c9f.tar.xz
[Tooling] Complete backend tooling and validation
Diffstat (limited to 'backend/unittests/Tooling/BackendManifestTest.cpp')
-rw-r--r--backend/unittests/Tooling/BackendManifestTest.cpp210
1 files changed, 210 insertions, 0 deletions
diff --git a/backend/unittests/Tooling/BackendManifestTest.cpp b/backend/unittests/Tooling/BackendManifestTest.cpp
new file mode 100644
index 0000000..5a6a0e9
--- /dev/null
+++ b/backend/unittests/Tooling/BackendManifestTest.cpp
@@ -0,0 +1,210 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "rcc/Tooling/BackendManifest.h"
+
+#include "ToolingTestSupport.h"
+#include "rcc/Target/RISCV/RISCVTarget.h"
+
+#include <algorithm>
+#include <cstdint>
+#include <optional>
+#include <string>
+#include <string_view>
+#include <utility>
+#include <vector>
+
+namespace {
+
+using rcc::tooling::BackendInvocation;
+
+[[nodiscard]] bool keyDiffers(const BackendInvocation &Base, BackendInvocation Changed,
+ std::string_view Input, std::string_view Fingerprint) {
+ auto Canonicalized = Changed.canonicalizeAndVerify();
+ if (!Canonicalized)
+ return false;
+ auto BaseKey = rcc::tooling::computeCacheKey(Input, Base, Fingerprint);
+ auto ChangedKey = rcc::tooling::computeCacheKey(Input, Changed, Fingerprint);
+ return BaseKey && ChangedKey && *BaseKey != *ChangedKey;
+}
+
+} // namespace
+
+int main() {
+ auto Target = rcc::riscv::createTargetDescription();
+ if (!Target)
+ return tooling_test::fail(Target.error().message());
+ const BackendInvocation Base = rcc::tooling::defaultRV64GCInvocation();
+ auto Fingerprint = rcc::tooling::computeTargetDescriptionFingerprint(*Target);
+ if (!Fingerprint || Fingerprint->size() != 64)
+ return tooling_test::fail("target description fingerprint must be a SHA-256 digest");
+ auto RepeatedFingerprint = rcc::tooling::computeTargetDescriptionFingerprint(*Target);
+ if (!RepeatedFingerprint || *RepeatedFingerprint != *Fingerprint)
+ return tooling_test::fail("target description fingerprint must be deterministic");
+
+ BackendInvocation Unordered = Base;
+ std::ranges::reverse(Unordered.Features);
+ std::ranges::reverse(Unordered.TargetOptions);
+ Unordered.EnabledActions = {"spill", "coalesce"};
+ auto Canonicalized = Unordered.canonicalizeAndVerify();
+ if (!Canonicalized || !std::ranges::is_sorted(Unordered.Features) ||
+ !std::ranges::is_sorted(Unordered.TargetOptions, {}, &rcc::tooling::BackendOption::Name) ||
+ Unordered.EnabledActions != std::vector<std::string>({"coalesce", "spill"})) {
+ return tooling_test::fail("backend invocation canonicalization must sort explicit identities");
+ }
+ BackendInvocation DuplicateFeature = Base;
+ DuplicateFeature.Features.push_back("+i");
+ if (DuplicateFeature.canonicalizeAndVerify())
+ return tooling_test::fail("backend invocation must reject duplicate target features");
+ BackendInvocation ZeroThreads = Base;
+ ZeroThreads.Determinism.ThreadCount = 0;
+ if (ZeroThreads.canonicalizeAndVerify())
+ return tooling_test::fail("backend invocation must reject a zero thread count");
+
+ constexpr std::string_view Input = "canonical MIR";
+ auto BaseKey = rcc::tooling::computeCacheKey(Input, Base, *Fingerprint);
+ if (!BaseKey || BaseKey->size() != 64)
+ return tooling_test::fail("cache key must be a SHA-256 digest");
+ auto DifferentInput = rcc::tooling::computeCacheKey("different MIR", Base, *Fingerprint);
+ if (!DifferentInput || *DifferentInput == *BaseKey)
+ return tooling_test::fail("cache key must include canonical MIR");
+ std::string DifferentFingerprint(64, '0');
+ auto DifferentTarget = rcc::tooling::computeCacheKey(Input, Base, DifferentFingerprint);
+ if (!DifferentTarget || *DifferentTarget == *BaseKey)
+ return tooling_test::fail("cache key must include the target description fingerprint");
+
+ BackendInvocation Changed = Base;
+ Changed.Triple += "-variant";
+ if (!keyDiffers(Base, Changed, Input, *Fingerprint))
+ return tooling_test::fail("cache key must include the target triple");
+ Changed = Base;
+ Changed.CPU += "-variant";
+ if (!keyDiffers(Base, Changed, Input, *Fingerprint))
+ return tooling_test::fail("cache key must include the target CPU");
+ Changed = Base;
+ Changed.TuneCPU += "-variant";
+ if (!keyDiffers(Base, Changed, Input, *Fingerprint))
+ return tooling_test::fail("cache key must include the tuning CPU");
+ Changed = Base;
+ Changed.Features.front() = "+variant";
+ if (!keyDiffers(Base, Changed, Input, *Fingerprint))
+ return tooling_test::fail("cache key must include target features");
+ Changed = Base;
+ Changed.ABI += "-variant";
+ if (!keyDiffers(Base, Changed, Input, *Fingerprint))
+ return tooling_test::fail("cache key must include the ABI");
+ Changed = Base;
+ Changed.DataLayout += "-variant";
+ if (!keyDiffers(Base, Changed, Input, *Fingerprint))
+ return tooling_test::fail("cache key must include the data layout");
+ Changed = Base;
+ Changed.Relocation = rcc::tooling::RelocationModel::Static;
+ if (!keyDiffers(Base, Changed, Input, *Fingerprint))
+ return tooling_test::fail("cache key must include the relocation model");
+ Changed = Base;
+ Changed.Code = rcc::tooling::CodeModel::Medium;
+ if (!keyDiffers(Base, Changed, Input, *Fingerprint))
+ return tooling_test::fail("cache key must include the code model");
+ Changed = Base;
+ Changed.TargetOptions.front().Value = "variant";
+ if (!keyDiffers(Base, Changed, Input, *Fingerprint))
+ return tooling_test::fail("cache key must include target options");
+ Changed = Base;
+ Changed.Layout = rcc::tooling::LayoutOwnership::CompilerFinalLayout;
+ if (!keyDiffers(Base, Changed, Input, *Fingerprint))
+ return tooling_test::fail("cache key must include layout ownership");
+ Changed = Base;
+ Changed.AssemblerVersion += "-variant";
+ if (!keyDiffers(Base, Changed, Input, *Fingerprint))
+ return tooling_test::fail("cache key must include assembler identity");
+ Changed = Base;
+ Changed.AssemblerOptions.front().Value = "false";
+ if (!keyDiffers(Base, Changed, Input, *Fingerprint))
+ return tooling_test::fail("cache key must include assembler options");
+ Changed = Base;
+ Changed.Optimization = rcc::tooling::OptimizationLevel::O3;
+ if (!keyDiffers(Base, Changed, Input, *Fingerprint))
+ return tooling_test::fail("cache key must include the optimization level");
+ Changed = Base;
+ Changed.PassPipeline = {"Legalizer"};
+ if (!keyDiffers(Base, Changed, Input, *Fingerprint))
+ return tooling_test::fail("cache key must include the pass pipeline");
+ Changed = Base;
+ Changed.Determinism.RandomSeed = 1;
+ if (!keyDiffers(Base, Changed, Input, *Fingerprint))
+ return tooling_test::fail("cache key must include the random seed");
+ Changed = Base;
+ Changed.Determinism.ThreadCount = 2;
+ if (!keyDiffers(Base, Changed, Input, *Fingerprint))
+ return tooling_test::fail("cache key must include the thread count");
+ Changed = Base;
+ Changed.Determinism.StableScheduling = false;
+ if (!keyDiffers(Base, Changed, Input, *Fingerprint))
+ return tooling_test::fail("cache key must include stable scheduling");
+ Changed = Base;
+ Changed.ActionFuel = 7;
+ if (!keyDiffers(Base, Changed, Input, *Fingerprint))
+ return tooling_test::fail("cache key must include Action fuel");
+ Changed = Base;
+ Changed.EnabledActions = {"Legalizer"};
+ if (!keyDiffers(Base, Changed, Input, *Fingerprint))
+ return tooling_test::fail("cache key must include enabled Actions");
+ Changed = Base;
+ Changed.BuildFingerprint += "-variant";
+ if (!keyDiffers(Base, Changed, Input, *Fingerprint))
+ return tooling_test::fail("cache key must include the backend build fingerprint");
+
+ rcc::tooling::StructuredDiagnostic Diagnostic;
+ Diagnostic.Category = "verification error";
+ Diagnostic.Stage = "selected-mir";
+ Diagnostic.Pass = "test-pass";
+ Diagnostic.Function = "test-function";
+ Diagnostic.Block = 3;
+ Diagnostic.Instruction = 7;
+ Diagnostic.Operand = 1;
+ Diagnostic.SourceLocation = "input.mir:4:5";
+ Diagnostic.TargetConstraint = "GPR";
+ Diagnostic.Rule = "test-rule";
+ Diagnostic.Message = "quoted \"message\"\nline";
+ auto Manifest = rcc::tooling::createBackendManifest(rcc::tooling::ManifestKind::Reproducer,
+ rcc::tooling::ArtifactKind::Assembly, Base,
+ *Target, Input, std::nullopt, {Diagnostic});
+ if (!Manifest)
+ return tooling_test::fail(Manifest.error().message());
+ auto Printed = rcc::tooling::printBackendManifest(*Manifest);
+ if (!Printed)
+ return tooling_test::fail(Printed.error().message());
+ auto Parsed = rcc::tooling::parseBackendManifest("manifest.yaml", *Printed);
+ if (!Parsed || *Parsed != *Manifest)
+ return tooling_test::fail("manifest printer and strict parser must round-trip all fields");
+
+ BackendInvocation EmptyCollections = Base;
+ EmptyCollections.TargetOptions.clear();
+ EmptyCollections.AssemblerOptions.clear();
+ auto EmptyManifest = rcc::tooling::createBackendManifest(rcc::tooling::ManifestKind::Reproducer,
+ rcc::tooling::ArtifactKind::FinalMIR,
+ EmptyCollections, *Target, Input);
+ if (!EmptyManifest)
+ return tooling_test::fail(EmptyManifest.error().message());
+ auto EmptyPrinted = rcc::tooling::printBackendManifest(*EmptyManifest);
+ if (!EmptyPrinted)
+ return tooling_test::fail(EmptyPrinted.error().message());
+ auto EmptyParsed = rcc::tooling::parseBackendManifest("empty-manifest.yaml", *EmptyPrinted);
+ if (!EmptyParsed || *EmptyParsed != *EmptyManifest)
+ return tooling_test::fail("manifest must preserve explicitly empty collections");
+
+ std::string Unknown = *Printed;
+ Unknown.insert(Unknown.find("schema:"), "unknown: 1\n");
+ if (rcc::tooling::parseBackendManifest("unknown.yaml", std::move(Unknown)))
+ return tooling_test::fail("strict manifest parsing must reject unknown keys");
+ std::string Duplicate = *Printed;
+ Duplicate.insert(Duplicate.find("schema:"), "schema: 1\n");
+ if (rcc::tooling::parseBackendManifest("duplicate.yaml", std::move(Duplicate)))
+ return tooling_test::fail("strict manifest parsing must reject duplicate keys");
+ std::string Missing = *Printed;
+ const std::size_t ABI = Missing.find(" abi:");
+ Missing.erase(ABI, Missing.find('\n', ABI) - ABI + 1U);
+ if (rcc::tooling::parseBackendManifest("missing.yaml", std::move(Missing)))
+ return tooling_test::fail("strict manifest parsing must reject missing invocation facts");
+
+ return 0;
+}