From adcf8c9b1bd1b47e00bf859693112cea1f7e8c9f Mon Sep 17 00:00:00 2001 From: Richard-Qin-X Date: Mon, 10 Aug 2026 20:36:50 +0800 Subject: [Tooling] Complete backend tooling and validation --- .../include/rcc/CodeGen/MachineBlockPlacement.h | 7 +- .../include/rcc/CodeGen/MachineBranchRelaxation.h | 5 +- backend/include/rcc/CodeGen/MachineCombiner.h | 5 +- .../include/rcc/CodeGen/MachineCopyPropagation.h | 5 +- backend/include/rcc/CodeGen/MachinePass.h | 72 ++++++++++++- .../include/rcc/CodeGen/MachinePipelinePasses.h | 4 +- backend/include/rcc/CodeGen/MachineSSAExit.h | 4 +- backend/include/rcc/CodeGen/PBQP/Solver.h | 49 ++++++++- backend/include/rcc/CodeGen/RegisterPressure.h | 2 +- backend/include/rcc/CodeGen/Spiller.h | 8 +- backend/include/rcc/Support/Error.h | 6 ++ backend/include/rcc/Support/SHA256.h | 24 +++++ backend/include/rcc/Target/RISCV/RISCVPipeline.h | 35 ++++++ backend/include/rcc/Tooling/ArtifactStore.h | 73 +++++++++++++ backend/include/rcc/Tooling/BackendDriver.h | 56 ++++++++++ backend/include/rcc/Tooling/BackendInvocation.h | 119 +++++++++++++++++++++ backend/include/rcc/Tooling/BackendManifest.h | 75 +++++++++++++ backend/include/rcc/Tooling/MIRFuzzer.h | 66 ++++++++++++ backend/include/rcc/Tooling/MIRReducer.h | 81 ++++++++++++++ backend/include/rcc/Tooling/PipelineDriver.h | 83 ++++++++++++++ 20 files changed, 754 insertions(+), 25 deletions(-) create mode 100644 backend/include/rcc/Support/SHA256.h create mode 100644 backend/include/rcc/Target/RISCV/RISCVPipeline.h create mode 100644 backend/include/rcc/Tooling/ArtifactStore.h create mode 100644 backend/include/rcc/Tooling/BackendDriver.h create mode 100644 backend/include/rcc/Tooling/BackendInvocation.h create mode 100644 backend/include/rcc/Tooling/BackendManifest.h create mode 100644 backend/include/rcc/Tooling/MIRFuzzer.h create mode 100644 backend/include/rcc/Tooling/MIRReducer.h create mode 100644 backend/include/rcc/Tooling/PipelineDriver.h (limited to 'backend/include') diff --git a/backend/include/rcc/CodeGen/MachineBlockPlacement.h b/backend/include/rcc/CodeGen/MachineBlockPlacement.h index 27a06f4..52ddafd 100644 --- a/backend/include/rcc/CodeGen/MachineBlockPlacement.h +++ b/backend/include/rcc/CodeGen/MachineBlockPlacement.h @@ -15,9 +15,10 @@ public: MachineAnalysisManager &Analyses) override; private: - [[nodiscard]] Expected - runImplementation(MachineFunction &Function, const TargetDescription &Target, - MachineAnalysisManager &Analyses) const; + [[nodiscard]] Expected runImplementation(MachineFunction &Function, + const TargetDescription &Target, + MachineAnalysisManager &Analyses, + MachineActionReplay &Actions) const; }; } // namespace rcc diff --git a/backend/include/rcc/CodeGen/MachineBranchRelaxation.h b/backend/include/rcc/CodeGen/MachineBranchRelaxation.h index 1793a54..c88c1d5 100644 --- a/backend/include/rcc/CodeGen/MachineBranchRelaxation.h +++ b/backend/include/rcc/CodeGen/MachineBranchRelaxation.h @@ -27,8 +27,9 @@ public: MachineAnalysisManager &Analyses) override; private: - [[nodiscard]] Expected - runImplementation(MachineFunction &Function, const TargetDescription &Target) const; + [[nodiscard]] Expected runImplementation(MachineFunction &Function, + const TargetDescription &Target, + MachineActionReplay &Actions) const; const TargetBranchInfo *Info; MachineBranchRelaxationOptions Options; diff --git a/backend/include/rcc/CodeGen/MachineCombiner.h b/backend/include/rcc/CodeGen/MachineCombiner.h index 943d6ad..ef7f432 100644 --- a/backend/include/rcc/CodeGen/MachineCombiner.h +++ b/backend/include/rcc/CodeGen/MachineCombiner.h @@ -27,8 +27,9 @@ public: MachineAnalysisManager &Analyses) override; private: - [[nodiscard]] Expected - runImplementation(MachineFunction &Function, const TargetDescription &Target) const; + [[nodiscard]] Expected runImplementation(MachineFunction &Function, + const TargetDescription &Target, + MachineActionReplay &Actions) const; const TargetMachineCombinerInfo *Info; MachineCombinerStage Stage; diff --git a/backend/include/rcc/CodeGen/MachineCopyPropagation.h b/backend/include/rcc/CodeGen/MachineCopyPropagation.h index 5f84ac6..e8ee788 100644 --- a/backend/include/rcc/CodeGen/MachineCopyPropagation.h +++ b/backend/include/rcc/CodeGen/MachineCopyPropagation.h @@ -33,8 +33,9 @@ public: MachineAnalysisManager &Analyses) override; private: - [[nodiscard]] Expected - runImplementation(MachineFunction &Function, const TargetDescription &Target) const; + [[nodiscard]] Expected runImplementation(MachineFunction &Function, + const TargetDescription &Target, + MachineActionReplay &Actions) const; const TargetSSAInfo *Info; MachineCopyPropagationStage Stage; diff --git a/backend/include/rcc/CodeGen/MachinePass.h b/backend/include/rcc/CodeGen/MachinePass.h index 1637954..ee67924 100644 --- a/backend/include/rcc/CodeGen/MachinePass.h +++ b/backend/include/rcc/CodeGen/MachinePass.h @@ -5,6 +5,7 @@ #include "rcc/CodeGen/InstructionDescriptor.h" #include "rcc/CodeGen/MachineFunctionProperties.h" +#include "rcc/CodeGen/StableID.h" #include "rcc/Support/Error.h" #include @@ -279,6 +280,9 @@ public: [[nodiscard]] Expected> forkEmpty() const; [[nodiscard]] Expected requestAction(std::string_view Action, const MachineFunction &Function, std::uint64_t Cost = 1); + [[nodiscard]] Expected requestAction(std::string_view Rule, const MachineFunction &Function, + MachineInstrStableID Instruction, + std::uint64_t Cost = 1); private: using ErasedRunner = std::function>( @@ -315,6 +319,7 @@ private: std::unordered_map> Cache; std::unordered_map> ActiveComputations; MachinePassInstrumentation *Instrumentation = nullptr; + std::string ActivePass; friend class MachinePassManager; friend class CompilationUnitPassManager; @@ -473,6 +478,58 @@ struct InstrumentationStatistic final { std::uint64_t TotalNanoseconds = 0; }; +struct MachineActionID final { + std::string Pass; + std::string Rule; + MachineFunctionStableID Function; + MachineInstrStableID Instruction; + + [[nodiscard]] std::string toString() const; + + friend bool operator==(const MachineActionID &, const MachineActionID &) = default; +}; + +struct MachineActionEvent final { + MachineActionID ID; + std::uint64_t Cost = 0; + bool Accepted = false; + + friend bool operator==(const MachineActionEvent &, const MachineActionEvent &) = default; +}; + +class MachineActionReplay final { +public: + explicit MachineActionReplay(MachineAnalysisManager &Analyses) noexcept : Analyses(&Analyses) {} + MachineActionReplay( + MachineAnalysisManager &Analyses, + const std::unordered_map &SourceToClone) + : Analyses(&Analyses) { + for (const auto &[Source, Clone] : SourceToClone) + RecordingInstructionIDs.try_emplace(Clone, Source); + } + + [[nodiscard]] Expected request(std::string_view Rule, const MachineFunction &Function, + MachineInstrStableID Instruction = {}, + std::uint64_t Cost = 1); + void beginReplay() noexcept; + [[nodiscard]] Expected finishReplay() const; + +private: + struct Decision final { + std::string Rule; + MachineFunctionStableID Function; + MachineInstrStableID Instruction; + std::uint64_t Cost = 0; + bool Accepted = false; + }; + + MachineAnalysisManager *Analyses = nullptr; + std::unordered_map RecordingInstructionIDs; + std::vector Decisions; + std::size_t NextDecision = 0; + bool IsReplaying = false; +}; + class MachinePassInstrumentation final { public: using BeforeCallback = std::function; @@ -489,8 +546,7 @@ public: std::function; using ChangeCallback = std::function; - using ActionFilter = - std::function; + using ActionFilter = std::function; using BeforeUnitCallback = std::function; using AfterUnitCallback = std::function statistics() const; + [[nodiscard]] std::vector actionEvents() const; void clearStatistics(); private: @@ -548,7 +605,7 @@ private: const Error &ErrorValue, std::uint64_t Nanoseconds) const; void change(std::string_view Name, const MachineFunction &Function, MachinePassChanges Changes, bool FingerprintChanged) const; - [[nodiscard]] Expected requestAction(std::string_view Action, + [[nodiscard]] Expected requestAction(const MachineActionID &Action, const MachineFunction &Function, std::uint64_t Cost); void beforeUnit(std::string_view Name, const CompilationUnit &Unit) const; @@ -577,6 +634,7 @@ private: ActionFilter Filter; std::optional PerFunctionFuel; std::unordered_map ConsumedFuel; + std::vector ActionEvents; std::vector BeforeUnit; std::vector AfterUnit; @@ -602,6 +660,8 @@ struct MachinePassManagerOptions final { class MachinePassManager final { public: + using PassList = std::vector>; + explicit MachinePassManager(MachinePassManagerOptions Options = {}) : Options(Options) {} MachinePassManager(const MachinePassManager &) = delete; @@ -620,11 +680,15 @@ public: return Passes.size(); } + [[nodiscard]] PassList takePasses() && noexcept { + return std::move(Passes); + } + private: [[nodiscard]] Expected validateContract(const MachinePassContract &Contract) const; MachinePassManagerOptions Options; - std::vector> Passes; + PassList Passes; }; struct CompilationUnitPassManagerOptions final { diff --git a/backend/include/rcc/CodeGen/MachinePipelinePasses.h b/backend/include/rcc/CodeGen/MachinePipelinePasses.h index c5bdac0..08f0dac 100644 --- a/backend/include/rcc/CodeGen/MachinePipelinePasses.h +++ b/backend/include/rcc/CodeGen/MachinePipelinePasses.h @@ -21,7 +21,7 @@ public: private: [[nodiscard]] Expected runImplementation(MachineFunction &Function, const TargetDescription &Target, - MachineAnalysisManager &Analyses); + MachineActionReplay &Actions); const TargetLegalizerInfo *Info; }; @@ -53,7 +53,7 @@ public: private: [[nodiscard]] Expected runImplementation(MachineFunction &Function, const TargetDescription &Target, - MachineAnalysisManager &Analyses); + MachineActionReplay &Actions); const TargetInstructionSelector *Info; }; diff --git a/backend/include/rcc/CodeGen/MachineSSAExit.h b/backend/include/rcc/CodeGen/MachineSSAExit.h index d98c0b9..fdf74f0 100644 --- a/backend/include/rcc/CodeGen/MachineSSAExit.h +++ b/backend/include/rcc/CodeGen/MachineSSAExit.h @@ -21,7 +21,7 @@ public: private: [[nodiscard]] Expected runImplementation(MachineFunction &Function, const TargetDescription &Target, - MachineAnalysisManager &Analyses); + MachineActionReplay &Actions); const TargetSSAInfo *Info = nullptr; }; @@ -39,7 +39,7 @@ public: private: [[nodiscard]] Expected runImplementation(MachineFunction &Function, const TargetDescription &Target, - MachineAnalysisManager &Analyses); + MachineActionReplay &Actions); const TargetSSAInfo *Info = nullptr; }; diff --git a/backend/include/rcc/CodeGen/PBQP/Solver.h b/backend/include/rcc/CodeGen/PBQP/Solver.h index ff14857..f010fd1 100644 --- a/backend/include/rcc/CodeGen/PBQP/Solver.h +++ b/backend/include/rcc/CodeGen/PBQP/Solver.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -18,9 +19,24 @@ namespace rcc::pbqp { +enum class SolverActionKind : std::uint8_t { + Reduction, + Choice, +}; + +struct SolverAction final { + SolverActionKind Kind = SolverActionKind::Reduction; + ReductionKind Reduction = ReductionKind::DegreeZero; + NodeID Node; + std::size_t Value = 0; +}; + class DeterministicSolver final { public: - [[nodiscard]] Expected solve(const Graph &Problem) const { + using ActionCallback = std::function(const SolverAction &)>; + + [[nodiscard]] Expected solve(const Graph &Problem, + const ActionCallback &Action = {}) const { auto Verified = Problem.verify(); if (!Verified) return std::unexpected(Verified.error()); @@ -31,10 +47,24 @@ public: while (Work.activeNodeCount() != 0) { const NodeID Selected = Work.selectReductionNode(); const std::size_t Degree = Work.degree(Selected); - Expected Reduced = Degree == 0 ? Work.reduceDegreeZero(Selected) - : Degree == 1 ? Work.reduceDegreeOne(Selected) - : Degree == 2 ? Work.reduceDegreeTwo(Selected) - : Work.reduceHeuristically(Selected); + const ReductionKind Kind = Degree == 0 ? ReductionKind::DegreeZero + : Degree == 1 ? ReductionKind::DegreeOne + : Degree == 2 ? ReductionKind::DegreeTwo + : ReductionKind::Heuristic; + if (Action) { + auto Accepted = Action({SolverActionKind::Reduction, Kind, Selected, Degree}); + if (!Accepted) + return std::unexpected(Accepted.error()); + if (!*Accepted) { + return makeUnexpected(ErrorCode::RegisterAllocationFailed, + "PBQP reduction action was rejected"); + } + } + Expected Reduced = + Kind == ReductionKind::DegreeZero ? Work.reduceDegreeZero(Selected) + : Kind == ReductionKind::DegreeOne ? Work.reduceDegreeOne(Selected) + : Kind == ReductionKind::DegreeTwo ? Work.reduceDegreeTwo(Selected) + : Work.reduceHeuristically(Selected); if (!Reduced) return std::unexpected(Reduced.error()); Reductions.push_back(std::move(*Reduced)); @@ -71,6 +101,15 @@ public: return makeUnexpected(ErrorCode::RegisterAllocationFailed, "PBQP reductions produced no finite option"); } + if (Action) { + auto Accepted = Action({SolverActionKind::Choice, Record.Kind, Record.Node, Choice}); + if (!Accepted) + return std::unexpected(Accepted.error()); + if (!*Accepted) { + return makeUnexpected(ErrorCode::RegisterAllocationFailed, + "PBQP choice action was rejected"); + } + } auto Set = Result.setSelection(Record.Node, Choice); if (!Set) return std::unexpected(Set.error()); diff --git a/backend/include/rcc/CodeGen/RegisterPressure.h b/backend/include/rcc/CodeGen/RegisterPressure.h index fa1209a..107bc29 100644 --- a/backend/include/rcc/CodeGen/RegisterPressure.h +++ b/backend/include/rcc/CodeGen/RegisterPressure.h @@ -22,7 +22,7 @@ struct MachineBlockLiveness; namespace detail { struct RegisterPressureBuilder; -} +} // namespace detail struct RegisterPressureValue final { RegisterPressureSetID Set; diff --git a/backend/include/rcc/CodeGen/Spiller.h b/backend/include/rcc/CodeGen/Spiller.h index 3d1fc66..f63158d 100644 --- a/backend/include/rcc/CodeGen/Spiller.h +++ b/backend/include/rcc/CodeGen/Spiller.h @@ -11,13 +11,16 @@ namespace rcc { +class MachineActionReplay; + class Spiller final { public: Spiller(MachineFunction &Function, const TargetDescription &Target, const TargetRegisterAllocationInfo &TargetInfo, VirtRegMap &Mapping, - bool EnableRematerialization = true) + bool EnableRematerialization = true, MachineActionReplay *Actions = nullptr) : Function(&Function), Target(&Target), TargetInfo(&TargetInfo), Mapping(&Mapping), - Edit(Function, Target, Mapping), EnableRematerialization(EnableRematerialization) {} + Edit(Function, Target, Mapping), EnableRematerialization(EnableRematerialization), + Actions(Actions) {} [[nodiscard]] Expected> spill(Register VirtualReg); @@ -34,6 +37,7 @@ private: VirtRegMap *Mapping; LiveRangeEdit Edit; bool EnableRematerialization; + MachineActionReplay *Actions; }; } // namespace rcc diff --git a/backend/include/rcc/Support/Error.h b/backend/include/rcc/Support/Error.h index 63c0b2f..0429cc0 100644 --- a/backend/include/rcc/Support/Error.h +++ b/backend/include/rcc/Support/Error.h @@ -30,6 +30,12 @@ enum class ErrorCode : std::uint8_t { FixupApplicationFailed, RelocationFailed, ObjectWritingFailed, + InvalidInvocation, + ManifestError, + CacheMiss, + ReproducerError, + ReductionFailed, + FuzzingFailure, }; [[nodiscard]] std::string_view getErrorCodeName(ErrorCode Code) noexcept; diff --git a/backend/include/rcc/Support/SHA256.h b/backend/include/rcc/Support/SHA256.h new file mode 100644 index 0000000..ff7b50b --- /dev/null +++ b/backend/include/rcc/Support/SHA256.h @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_SUPPORT_SHA256_H +#define RCC_SUPPORT_SHA256_H + +#include +#include +#include +#include +#include +#include + +namespace rcc { + +using SHA256Digest = std::array; + +[[nodiscard]] SHA256Digest sha256(std::span Input); +[[nodiscard]] SHA256Digest sha256(std::string_view Input); +[[nodiscard]] std::string sha256Hex(std::span Input); +[[nodiscard]] std::string sha256Hex(std::string_view Input); + +} // namespace rcc + +#endif // RCC_SUPPORT_SHA256_H diff --git a/backend/include/rcc/Target/RISCV/RISCVPipeline.h b/backend/include/rcc/Target/RISCV/RISCVPipeline.h new file mode 100644 index 0000000..8bd79e9 --- /dev/null +++ b/backend/include/rcc/Target/RISCV/RISCVPipeline.h @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_TARGET_RISCV_RISCVPIPELINE_H +#define RCC_TARGET_RISCV_RISCVPIPELINE_H + +#include "rcc/CodeGen/MachinePass.h" +#include "rcc/Support/Error.h" + +#include +#include +#include +#include + +namespace rcc { + +class MachineFunction; +class TargetDescription; + +namespace riscv { + +[[nodiscard]] Expected addCanonicalPipeline(MachinePassManager &Pipeline, + const MachineFunction &Function); +[[nodiscard]] Expected> +createCanonicalPipelinePass(std::string_view Name); +[[nodiscard]] Expected> +canonicalPipelinePassNames(const MachineFunction &Function); +[[nodiscard]] Expected +runCanonicalPipeline(MachineFunction &Function, const TargetDescription &Target, + MachinePassManagerOptions Options = {}, + MachinePassInstrumentation *Instrumentation = nullptr); + +} // namespace riscv +} // namespace rcc + +#endif // RCC_TARGET_RISCV_RISCVPIPELINE_H diff --git a/backend/include/rcc/Tooling/ArtifactStore.h b/backend/include/rcc/Tooling/ArtifactStore.h new file mode 100644 index 0000000..a30f572 --- /dev/null +++ b/backend/include/rcc/Tooling/ArtifactStore.h @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_TOOLING_ARTIFACTSTORE_H +#define RCC_TOOLING_ARTIFACTSTORE_H + +#include "rcc/MIR/MIRDocument.h" +#include "rcc/Support/Error.h" +#include "rcc/Tooling/BackendManifest.h" + +#include +#include +#include +#include +#include +#include + +namespace rcc { + +class TargetDescription; + +namespace tooling { + +struct CanonicalMIR final { + mir::MIRDocument Document; + std::string Text; +}; + +struct CacheEntry final { + BackendManifest Manifest; + std::string InputMIR; + std::vector Artifact; + std::optional ParsedArtifact; +}; + +struct ReproducerPackage final { + BackendManifest Manifest; + CanonicalMIR Input; +}; + +[[nodiscard]] Expected parseAndCanonicalizeMIR(std::string SourceName, + std::string SourceText, + const TargetDescription &Target, + const BackendInvocation &Invocation); + +[[nodiscard]] Expected +createBackendManifest(ManifestKind Kind, ArtifactKind Artifact, const BackendInvocation &Invocation, + const TargetDescription &Target, std::string_view CanonicalInputMIR, + std::optional> ArtifactBytes = std::nullopt, + std::vector Diagnostics = {}); + +[[nodiscard]] Expected storeCacheEntry(const std::filesystem::path &CacheRoot, + const BackendManifest &Manifest, + std::string_view CanonicalInputMIR, + std::span Artifact); +[[nodiscard]] Expected loadCacheEntry(const std::filesystem::path &CacheRoot, + ArtifactKind Artifact, + const BackendInvocation &Invocation, + const TargetDescription &Target, + std::string_view CanonicalInputMIR); + +[[nodiscard]] Expected writeReproducer(const std::filesystem::path &PackagePath, + const BackendManifest &Manifest, + std::string_view CanonicalInputMIR); +[[nodiscard]] Expected +updateReproducerDiagnostics(const std::filesystem::path &PackagePath, + std::vector Diagnostics); +[[nodiscard]] Expected loadReproducer(const std::filesystem::path &PackagePath, + const TargetDescription &Target); + +} // namespace tooling +} // namespace rcc + +#endif // RCC_TOOLING_ARTIFACTSTORE_H diff --git a/backend/include/rcc/Tooling/BackendDriver.h b/backend/include/rcc/Tooling/BackendDriver.h new file mode 100644 index 0000000..f762c1e --- /dev/null +++ b/backend/include/rcc/Tooling/BackendDriver.h @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_TOOLING_BACKENDDRIVER_H +#define RCC_TOOLING_BACKENDDRIVER_H + +#include "rcc/CodeGen/MachinePass.h" +#include "rcc/Support/Error.h" +#include "rcc/Tooling/ArtifactStore.h" +#include "rcc/Tooling/PipelineDriver.h" + +#include +#include +#include +#include + +namespace rcc { + +class TargetDescription; + +namespace tooling { + +enum class BackendOutputKind : std::uint8_t { + PreLegalizerMIR, + LegalizedMIR, + RegisterBankMIR, + SelectedMIR, + PostSSAMIR, + PostRegisterAllocationMIR, + FinalMIR, + Assembly, + RelocatableObject, +}; + +struct BackendRunResult final { + BackendOutputKind Kind = BackendOutputKind::FinalMIR; + std::vector Output; + PipelineRunResult Pipeline; + std::vector Actions; + + [[nodiscard]] std::string_view text() const noexcept; +}; + +[[nodiscard]] Expected +runBackend(CanonicalMIR Input, const TargetDescription &Target, const BackendInvocation &Invocation, + BackendOutputKind Output, PipelineControls Controls = {}, + MachinePassInstrumentation *Instrumentation = nullptr); + +[[nodiscard]] std::string_view spelling(BackendOutputKind Output) noexcept; +[[nodiscard]] Expected parseBackendOutputKind(std::string_view Value); +[[nodiscard]] ArtifactKind manifestArtifactKind(BackendOutputKind Output) noexcept; +[[nodiscard]] Expected cacheArtifactKind(BackendOutputKind Output); + +} // namespace tooling +} // namespace rcc + +#endif // RCC_TOOLING_BACKENDDRIVER_H diff --git a/backend/include/rcc/Tooling/BackendInvocation.h b/backend/include/rcc/Tooling/BackendInvocation.h new file mode 100644 index 0000000..db1c5d2 --- /dev/null +++ b/backend/include/rcc/Tooling/BackendInvocation.h @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_TOOLING_BACKENDINVOCATION_H +#define RCC_TOOLING_BACKENDINVOCATION_H + +#include "rcc/Support/Error.h" + +#include +#include +#include +#include +#include +#include + +namespace rcc { + +class TargetDescription; + +namespace tooling { + +inline constexpr std::uint32_t BackendInvocationSchemaVersion = 1; +inline constexpr std::string_view LLVMReferenceRevision = + "2b40c303e79f64c5c7642032554eda026594d604"; +inline constexpr std::string_view GenericOpcodeReferenceVersion = "rcc-generic-opcode-v1"; +inline constexpr std::string_view RV64GCTargetABILoweringVersion = "rcc-rv64gc-lp64d-v1"; +inline constexpr std::string_view CompilerBuildFingerprint = "rcc-backend-0.1.0-stage17"; + +enum class RelocationModel : std::uint8_t { + Static, + PositionIndependent, +}; + +enum class CodeModel : std::uint8_t { + Small, + Medium, + Large, +}; + +enum class OptimizationLevel : std::uint8_t { + O0, + O1, + O2, + O3, + Os, + Oz, +}; + +enum class LayoutOwnership : std::uint8_t { + NativeObject, + CompilerFinalLayout, + ExternalRelaxation, +}; + +struct BackendOption final { + std::string Name; + std::string Value; + + friend bool operator==(const BackendOption &, const BackendOption &) = default; +}; + +struct DeterminismOptions final { + std::uint64_t RandomSeed = 0; + std::uint32_t ThreadCount = 1; + bool StableScheduling = true; + + friend bool operator==(const DeterminismOptions &, const DeterminismOptions &) = default; +}; + +struct BackendInvocation final { + std::uint32_t SchemaVersion = BackendInvocationSchemaVersion; + std::string Triple; + std::string CPU; + std::string TuneCPU; + std::vector Features; + std::string ABI; + std::string DataLayout; + RelocationModel Relocation = RelocationModel::PositionIndependent; + CodeModel Code = CodeModel::Small; + std::vector TargetOptions; + LayoutOwnership Layout = LayoutOwnership::NativeObject; + std::string AssemblerFlavor; + std::string AssemblerVersion; + std::vector AssemblerOptions; + OptimizationLevel Optimization = OptimizationLevel::O2; + std::vector PassPipeline{"default"}; + DeterminismOptions Determinism; + std::optional ActionFuel; + std::vector EnabledActions; + std::string MIRReferenceRevision{LLVMReferenceRevision}; + std::string GenericOpcodeVersion{GenericOpcodeReferenceVersion}; + std::string TargetABILoweringVersion{RV64GCTargetABILoweringVersion}; + std::string BuildFingerprint{CompilerBuildFingerprint}; + + [[nodiscard]] Expected canonicalizeAndVerify(); + [[nodiscard]] std::string canonicalText() const; + + friend bool operator==(const BackendInvocation &, const BackendInvocation &) = default; +}; + +[[nodiscard]] BackendInvocation defaultRV64GCInvocation(); +[[nodiscard]] Expected +computeTargetDescriptionFingerprint(const TargetDescription &Target); +[[nodiscard]] Expected computeCacheKey(std::string_view CanonicalMIR, + const BackendInvocation &Invocation, + std::string_view TargetFingerprint); + +[[nodiscard]] std::string_view spelling(RelocationModel Value) noexcept; +[[nodiscard]] std::string_view spelling(CodeModel Value) noexcept; +[[nodiscard]] std::string_view spelling(OptimizationLevel Value) noexcept; +[[nodiscard]] std::string_view spelling(LayoutOwnership Value) noexcept; +[[nodiscard]] Expected parseRelocationModel(std::string_view Value); +[[nodiscard]] Expected parseCodeModel(std::string_view Value); +[[nodiscard]] Expected parseOptimizationLevel(std::string_view Value); +[[nodiscard]] Expected parseLayoutOwnership(std::string_view Value); + +} // namespace tooling +} // namespace rcc + +#endif // RCC_TOOLING_BACKENDINVOCATION_H diff --git a/backend/include/rcc/Tooling/BackendManifest.h b/backend/include/rcc/Tooling/BackendManifest.h new file mode 100644 index 0000000..d5d5733 --- /dev/null +++ b/backend/include/rcc/Tooling/BackendManifest.h @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_TOOLING_BACKENDMANIFEST_H +#define RCC_TOOLING_BACKENDMANIFEST_H + +#include "rcc/Tooling/BackendInvocation.h" + +#include +#include +#include +#include +#include + +namespace rcc::tooling { + +inline constexpr std::uint32_t BackendManifestSchemaVersion = 1; + +enum class ManifestKind : std::uint8_t { + Cache, + Reproducer, +}; + +enum class ArtifactKind : std::uint8_t { + PreLegalizerMIR, + LegalizedMIR, + RegisterBankMIR, + SelectedMIR, + PostSSAMIR, + PostRegisterAllocationMIR, + FinalMIR, + Assembly, + RelocatableObject, +}; + +struct StructuredDiagnostic final { + std::string Category; + std::string Stage; + std::string Pass; + std::string Function; + std::optional Block; + std::optional Instruction; + std::optional Operand; + std::string SourceLocation; + std::string TargetConstraint; + std::string Rule; + std::string Message; + + friend bool operator==(const StructuredDiagnostic &, const StructuredDiagnostic &) = default; +}; + +struct BackendManifest final { + std::uint32_t SchemaVersion = BackendManifestSchemaVersion; + ManifestKind Kind = ManifestKind::Reproducer; + ArtifactKind Artifact = ArtifactKind::FinalMIR; + BackendInvocation Invocation; + std::string InputMIRHash; + std::string TargetFingerprint; + std::string CacheKey; + std::optional ArtifactHash; + std::vector Diagnostics; + + [[nodiscard]] Expected verify() const; + + friend bool operator==(const BackendManifest &, const BackendManifest &) = default; +}; + +[[nodiscard]] Expected printBackendManifest(const BackendManifest &Manifest); +[[nodiscard]] Expected parseBackendManifest(std::string SourceName, + std::string SourceText); +[[nodiscard]] std::string_view spelling(ManifestKind Value) noexcept; +[[nodiscard]] std::string_view spelling(ArtifactKind Value) noexcept; + +} // namespace rcc::tooling + +#endif // RCC_TOOLING_BACKENDMANIFEST_H diff --git a/backend/include/rcc/Tooling/MIRFuzzer.h b/backend/include/rcc/Tooling/MIRFuzzer.h new file mode 100644 index 0000000..7fccb6d --- /dev/null +++ b/backend/include/rcc/Tooling/MIRFuzzer.h @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_TOOLING_MIRFUZZER_H +#define RCC_TOOLING_MIRFUZZER_H + +#include "rcc/Support/Error.h" +#include "rcc/Tooling/BackendInvocation.h" + +#include +#include +#include +#include +#include + +namespace rcc { + +class TargetDescription; + +namespace tooling { + +enum class MIRFuzzLayer : std::uint8_t { + Parser, + BuilderStructural, + PropertyStage, + Mutation, + Pipeline, + RestrictedSemantics, +}; + +struct MIRFuzzOptions final { + std::uint64_t Seed = 0; + std::size_t Iterations = 100; + std::size_t MaximumInputSize = 1U << 20U; + std::vector Layers; +}; + +struct MIRFuzzLayerResult final { + MIRFuzzLayer Layer = MIRFuzzLayer::Parser; + std::size_t Executed = 0; + std::size_t Accepted = 0; + std::size_t Rejected = 0; + + friend bool operator==(const MIRFuzzLayerResult &, const MIRFuzzLayerResult &) = default; +}; + +struct MIRFuzzResult final { + std::uint64_t Seed = 0; + std::vector Layers; + + [[nodiscard]] std::size_t executed() const noexcept; + [[nodiscard]] std::size_t accepted() const noexcept; + [[nodiscard]] std::size_t rejected() const noexcept; +}; + +[[nodiscard]] Expected fuzzMIR(std::string SourceName, std::string SourceText, + BackendInvocation Invocation, + const TargetDescription &Target, + MIRFuzzOptions Options = {}); + +[[nodiscard]] std::string_view spelling(MIRFuzzLayer Layer) noexcept; +[[nodiscard]] Expected parseMIRFuzzLayer(std::string_view Value); + +} // namespace tooling +} // namespace rcc + +#endif // RCC_TOOLING_MIRFUZZER_H diff --git a/backend/include/rcc/Tooling/MIRReducer.h b/backend/include/rcc/Tooling/MIRReducer.h new file mode 100644 index 0000000..35ef9c0 --- /dev/null +++ b/backend/include/rcc/Tooling/MIRReducer.h @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_TOOLING_MIRREDUCER_H +#define RCC_TOOLING_MIRREDUCER_H + +#include "rcc/Support/Error.h" +#include "rcc/Tooling/BackendInvocation.h" + +#include +#include +#include +#include +#include +#include + +namespace rcc { + +class TargetDescription; + +namespace tooling { + +enum class ReductionMode : std::uint8_t { + PreserveValidity, + PreserveFailure, +}; + +enum class ReductionGranularity : std::uint8_t { + CompilationUnit, + MachineFunction, + BasicBlock, + CFGEdge, + Instruction, + Operand, + RegisterSubgraph, + FrameObject, + ConstantPool, + JumpTable, + TargetFeature, + PassPipeline, +}; + +struct MIRReducerOptions final { + ReductionMode Mode = ReductionMode::PreserveFailure; + std::size_t MaximumAttempts = 100'000; + std::vector Granularities; +}; + +using MIRInterestingnessTest = + std::function(std::string_view, const BackendInvocation &)>; + +// PreserveFailure requires and evaluates the interestingness test. PreserveValidity accepts every +// smaller candidate that reparses, canonicalizes, and verifies; its interestingness test is +// ignored. + +struct MIRReductionStep final { + ReductionGranularity Granularity = ReductionGranularity::Instruction; + std::string Description; + std::size_t PreviousSize = 0; + std::size_t ReducedSize = 0; +}; + +struct MIRReductionResult final { + std::string MIR; + BackendInvocation Invocation; + std::vector Steps; + std::size_t Attempts = 0; + bool Exhausted = false; +}; + +[[nodiscard]] Expected reduceMIR(std::string SourceName, std::string SourceText, + BackendInvocation Invocation, + const TargetDescription &Target, + const MIRInterestingnessTest &Interesting, + MIRReducerOptions Options = {}); + +[[nodiscard]] std::string_view spelling(ReductionGranularity Granularity) noexcept; + +} // namespace tooling +} // namespace rcc + +#endif // RCC_TOOLING_MIRREDUCER_H diff --git a/backend/include/rcc/Tooling/PipelineDriver.h b/backend/include/rcc/Tooling/PipelineDriver.h new file mode 100644 index 0000000..2de341c --- /dev/null +++ b/backend/include/rcc/Tooling/PipelineDriver.h @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_TOOLING_PIPELINEDRIVER_H +#define RCC_TOOLING_PIPELINEDRIVER_H + +#include "rcc/CodeGen/MachinePass.h" +#include "rcc/MIR/MIRDocument.h" +#include "rcc/Tooling/BackendInvocation.h" + +#include +#include +#include +#include +#include + +namespace rcc { + +class TargetDescription; + +namespace tooling { + +enum class BackendStage : std::uint8_t { + PreLegalizerMIR, + LegalizedMIR, + RegisterBankMIR, + SelectedMIR, + PostSSAMIR, + PostRegisterAllocationMIR, + FinalMIR, +}; + +enum class SnapshotPhase : std::uint8_t { + Stage, + BeforePass, + AfterPass, +}; + +struct PipelineControls final { + std::optional StopBefore; + std::optional StopAfter; + std::vector PrintBefore; + std::vector PrintAfter; + std::optional StopAtStage; + bool CaptureStageSnapshots = false; + bool VerifyEach = true; + bool DetectUnreportedChanges = true; +}; + +struct PipelineSnapshot final { + SnapshotPhase Phase = SnapshotPhase::Stage; + BackendStage Stage = BackendStage::PreLegalizerMIR; + std::string Function; + std::string Pass; + std::uint32_t Occurrence = 0; + std::string MIR; +}; + +struct ExecutedPass final { + std::string Function; + std::string Pass; + std::uint32_t Occurrence = 0; + + friend bool operator==(const ExecutedPass &, const ExecutedPass &) = default; +}; + +struct PipelineRunResult final { + std::vector ExecutedPasses; + std::vector Snapshots; +}; + +[[nodiscard]] Expected verifyRV64GCInvocation(const BackendInvocation &Invocation); +[[nodiscard]] Expected +runRV64GCPipeline(mir::MIRDocument &Document, const TargetDescription &Target, + const BackendInvocation &Invocation, PipelineControls Controls = {}, + MachinePassInstrumentation *Instrumentation = nullptr); +[[nodiscard]] BackendStage stageForFunction(const MachineFunction &Function) noexcept; +[[nodiscard]] std::string_view spelling(BackendStage Stage) noexcept; +[[nodiscard]] Expected parseBackendStage(std::string_view Value); + +} // namespace tooling +} // namespace rcc + +#endif // RCC_TOOLING_PIPELINEDRIVER_H -- cgit v1.2.3