From f89d528efc0228c049c2867dcba886a56bac8619 Mon Sep 17 00:00:00 2001 From: Richard-Qin-X Date: Fri, 28 Aug 2026 18:51:10 +0800 Subject: [CodeGen] Add target machine registry --- backend/lib/MC/CMakeLists.txt | 4 +- backend/lib/MC/TargetRegistry.cpp | 73 +++++++++++ backend/lib/Target/CMakeLists.txt | 36 ++++++ backend/lib/Target/RISCV/CMakeLists.txt | 3 + backend/lib/Target/RISCV/RISCVPipeline.cpp | 13 +- backend/lib/Target/RISCV/RISCVSubtarget.cpp | 13 +- backend/lib/Target/RISCV/RISCVTargetMachine.cpp | 161 ++++++++++++++++++++++++ backend/lib/Target/TargetMachine.cpp | 18 +++ backend/lib/Tooling/BackendDriver.cpp | 23 ++-- backend/lib/Tooling/BackendInvocation.cpp | 72 ++++++++--- backend/lib/Tooling/CMakeLists.txt | 3 +- backend/lib/Tooling/MIRFuzzer.cpp | 20 ++- backend/lib/Tooling/PipelineDriver.cpp | 137 +++++++------------- 13 files changed, 426 insertions(+), 150 deletions(-) create mode 100644 backend/lib/MC/TargetRegistry.cpp create mode 100644 backend/lib/Target/RISCV/RISCVTargetMachine.cpp create mode 100644 backend/lib/Target/TargetMachine.cpp (limited to 'backend/lib') diff --git a/backend/lib/MC/CMakeLists.txt b/backend/lib/MC/CMakeLists.txt index 330ede3..b314c58 100644 --- a/backend/lib/MC/CMakeLists.txt +++ b/backend/lib/MC/CMakeLists.txt @@ -14,6 +14,7 @@ set( MCObjectStreamer.cpp MCSection.cpp MCStreamer.cpp + TargetRegistry.cpp ) set( RCCBackendMCHeaders @@ -33,6 +34,7 @@ set( "${RCC_BACKEND_SOURCE_DIR}/include/rcc/MC/MCSection.h" "${RCC_BACKEND_SOURCE_DIR}/include/rcc/MC/MCStreamer.h" "${RCC_BACKEND_SOURCE_DIR}/include/rcc/MC/MCSymbol.h" + "${RCC_BACKEND_SOURCE_DIR}/include/rcc/MC/TargetRegistry.h" ) add_library(RCCBackendMC ${RCCBackendMCSources}) @@ -46,7 +48,7 @@ target_include_directories( "$" "$" ) -target_link_libraries(RCCBackendMC PUBLIC RCCBackend::Support) +target_link_libraries(RCCBackendMC PUBLIC RCCBackend::Support RCCBackend::Target) set_target_properties( RCCBackendMC diff --git a/backend/lib/MC/TargetRegistry.cpp b/backend/lib/MC/TargetRegistry.cpp new file mode 100644 index 0000000..217f59b --- /dev/null +++ b/backend/lib/MC/TargetRegistry.cpp @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "rcc/MC/TargetRegistry.h" + +#include "rcc/Target/TargetMachine.h" + +#include +#include +#include +#include + +namespace rcc { +namespace { + +[[nodiscard]] std::vector ®isteredTargets() { + static std::vector Targets; + return Targets; +} + +} // namespace + +TargetMachineOptions Target::defaultMachineOptions() const { + return DefaultOptions(); +} + +Expected> +Target::createTargetMachine(TargetMachineOptions Options) const { + return TargetMachineCtor(*this, std::move(Options)); +} + +void TargetRegistry::registerTarget(const Target &RegisteredTarget) { + std::vector &Targets = registeredTargets(); + if (std::ranges::find(Targets, &RegisteredTarget) == Targets.end()) + Targets.push_back(&RegisteredTarget); +} + +Expected TargetRegistry::lookupTarget(std::string_view Triple) { + const Target *Best = nullptr; + unsigned BestQuality = 0; + for (const Target *Candidate : registeredTargets()) { + const unsigned Quality = Candidate->matchQuality(Triple); + if (Quality > BestQuality) { + Best = Candidate; + BestQuality = Quality; + } else if (Quality != 0 && Quality == BestQuality) { + return makeUnexpected(ErrorCode::InvalidInvocation, + "target triple matches multiple registered targets"); + } + } + if (Best == nullptr) { + return makeUnexpected(ErrorCode::Unsupported, + "no registered target supports triple '" + std::string(Triple) + "'"); + } + return Best; +} + +Expected TargetRegistry::defaultTarget() { + const Target *Default = nullptr; + for (const Target *Candidate : registeredTargets()) { + if (!Candidate->isDefault()) + continue; + if (Default != nullptr) { + return makeUnexpected(ErrorCode::InternalError, + "multiple targets are registered as the default target"); + } + Default = Candidate; + } + if (Default == nullptr) + return makeUnexpected(ErrorCode::InternalError, "no default target is registered"); + return Default; +} + +} // namespace rcc diff --git a/backend/lib/Target/CMakeLists.txt b/backend/lib/Target/CMakeLists.txt index 341d98d..0c61f57 100644 --- a/backend/lib/Target/CMakeLists.txt +++ b/backend/lib/Target/CMakeLists.txt @@ -1,3 +1,39 @@ # SPDX-License-Identifier: GPL-3.0-or-later +add_library(RCCBackendTarget TargetMachine.cpp) +add_library(RCCBackend::Target ALIAS RCCBackendTarget) + +rcc_backend_configure_target(RCCBackendTarget) + +target_include_directories( + RCCBackendTarget + PUBLIC + "$" + "$" +) +target_link_libraries(RCCBackendTarget PUBLIC RCCBackend::CodeGen) + +set_target_properties( + RCCBackendTarget + PROPERTIES + EXPORT_NAME Target + OUTPUT_NAME RCCBackendTarget +) + +install( + TARGETS RCCBackendTarget + EXPORT RCCBackendTargets + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" +) + +rcc_backend_register_format_files( + "${CMAKE_CURRENT_SOURCE_DIR}/TargetMachine.cpp" + "${RCC_BACKEND_SOURCE_DIR}/include/rcc/Target/TargetMachine.h" + "${RCC_BACKEND_SOURCE_DIR}/include/rcc/Target/TargetOptions.h" + "${RCC_BACKEND_SOURCE_DIR}/include/rcc/Support/TargetSelect.h" +) +rcc_backend_register_tidy_files("${CMAKE_CURRENT_SOURCE_DIR}/TargetMachine.cpp") + add_subdirectory(RISCV) diff --git a/backend/lib/Target/RISCV/CMakeLists.txt b/backend/lib/Target/RISCV/CMakeLists.txt index aca8ad4..2b9c023 100644 --- a/backend/lib/Target/RISCV/CMakeLists.txt +++ b/backend/lib/Target/RISCV/CMakeLists.txt @@ -139,6 +139,7 @@ add_library( RISCVScheduling.cpp RISCVSubtarget.cpp RISCVTarget.cpp + RISCVTargetMachine.cpp RISCVTargetConstant.cpp RISCVTargetObjectFile.cpp "${RCCBackendRISCVGeneratedRegisterHeader}" @@ -170,6 +171,7 @@ target_link_libraries( RCCBackend::AsmPrinter RCCBackend::CodeGen RCCBackend::MC + RCCBackend::Target ) set_target_properties( @@ -232,6 +234,7 @@ rcc_backend_register_format_files( "${CMAKE_CURRENT_SOURCE_DIR}/RISCVScheduling.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/RISCVSubtarget.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/RISCVTarget.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/RISCVTargetMachine.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/RISCVTargetConstant.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/RISCVTargetObjectFile.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/RISCVTargetObjectFile.h" diff --git a/backend/lib/Target/RISCV/RISCVPipeline.cpp b/backend/lib/Target/RISCV/RISCVPipeline.cpp index 61b3888..c34a590 100644 --- a/backend/lib/Target/RISCV/RISCVPipeline.cpp +++ b/backend/lib/Target/RISCV/RISCVPipeline.cpp @@ -47,7 +47,7 @@ template [[nodiscard]] Expected addInitialPipeline(MachineFunctionPassManager &Pipeline, const RISCVSubtarget &Subtarget, - RISCVPipelineOptions Options) { + TargetPipelineOptions Options) { auto Added = addPass( Pipeline, giselCombinerTable(), giselCombinerExecutor(), "riscv-prelegalizer-combiner"); if (!Added) @@ -67,7 +67,7 @@ template [[nodiscard]] Expected addSSAExitPipeline(MachineFunctionPassManager &Pipeline, const RISCVSubtarget &Subtarget, - RISCVPipelineOptions Options) { + TargetPipelineOptions Options) { auto Added = addSelectedSSAOptimizationPipeline( Pipeline, optimizationPipelineInfo(Subtarget, Options.DebugMachineScheduler, Options.DebugPrologueEpilogue)); @@ -84,7 +84,7 @@ template [[nodiscard]] Expected addRegisterAllocationPipeline(MachineFunctionPassManager &Pipeline, const RISCVSubtarget &Subtarget, - RISCVPipelineOptions Options) { + TargetPipelineOptions Options) { auto Added = addPass(Pipeline, pseudoExpansionInfo()); if (!Added) return std::unexpected(Added.error()); @@ -104,7 +104,7 @@ template [[nodiscard]] Expected addCompleteRegistry(MachineFunctionPassManager &Pipeline, const RISCVSubtarget &Subtarget, - RISCVPipelineOptions Options) { + TargetPipelineOptions Options) { auto Added = addInitialPipeline(Pipeline, Subtarget, Options); if (!Added) return std::unexpected(Added.error()); @@ -139,7 +139,8 @@ template Expected addCanonicalPipeline(MachineFunctionPassManager &Pipeline, const MachineFunction &Function, - const RISCVSubtarget &Subtarget, RISCVPipelineOptions Options) { + const RISCVSubtarget &Subtarget, + TargetPipelineOptions Options) { if (!Function.properties().has(MachineFunctionProperty::Legalized)) { auto Added = addPass( Pipeline, giselCombinerTable(), giselCombinerExecutor(), "riscv-prelegalizer-combiner"); @@ -211,7 +212,7 @@ Expected addCanonicalPipeline(MachineFunctionPassManager &Pipeline, Expected> createCanonicalPipelinePass(std::string_view Name, const RISCVSubtarget &Subtarget, - RISCVPipelineOptions Options) { + TargetPipelineOptions Options) { if (Name.empty()) return makeUnexpected(ErrorCode::InvalidArgument, "pipeline pass name cannot be empty"); MachineFunctionPassManager Registry; diff --git a/backend/lib/Target/RISCV/RISCVSubtarget.cpp b/backend/lib/Target/RISCV/RISCVSubtarget.cpp index c97d3cd..5e9efb6 100644 --- a/backend/lib/Target/RISCV/RISCVSubtarget.cpp +++ b/backend/lib/Target/RISCV/RISCVSubtarget.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -199,6 +200,10 @@ RISCVSubtarget::RISCVSubtarget(RISCVSubtargetFeatures Features, RelocationModel : Features(Features), Relocation(Relocation), Code(Code), ABI(ABI), SchedulingHooks(createSchedulingHooks(Features)) { FrameLoweringInfo = createFrameLowering(*this); + CallLoweringInfo = std::make_unique(); + RegBankInfo = std::make_unique(); + InstSelector.reset(createRISCVInstructionSelector(*this, *RegBankInfo)); + Legalizer = std::make_unique(*this); auto Model = createSchedulingModel(*SchedulingHooks); if (!Model) std::abort(); @@ -208,26 +213,18 @@ RISCVSubtarget::RISCVSubtarget(RISCVSubtargetFeatures Features, RelocationModel RISCVSubtarget::~RISCVSubtarget() = default; const TargetCallLowering *RISCVSubtarget::getCallLowering() const { - if (!CallLoweringInfo) - CallLoweringInfo = std::make_unique(); return CallLoweringInfo.get(); } InstructionSelector *RISCVSubtarget::getInstructionSelector() const { - if (!InstSelector) - InstSelector.reset(createRISCVInstructionSelector(*this, *getRegBankInfo())); return InstSelector.get(); } const LegalizerInfo *RISCVSubtarget::getLegalizerInfo() const { - if (!Legalizer) - Legalizer = std::make_unique(*this); return Legalizer.get(); } const RISCVRegisterBankInfo *RISCVSubtarget::getRegBankInfo() const { - if (!RegBankInfo) - RegBankInfo = std::make_unique(); return RegBankInfo.get(); } diff --git a/backend/lib/Target/RISCV/RISCVTargetMachine.cpp b/backend/lib/Target/RISCV/RISCVTargetMachine.cpp new file mode 100644 index 0000000..6f82ce8 --- /dev/null +++ b/backend/lib/Target/RISCV/RISCVTargetMachine.cpp @@ -0,0 +1,161 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "rcc/MC/TargetRegistry.h" +#include "rcc/Target/RISCV/RISCVAssemblyPrinter.h" +#include "rcc/Target/RISCV/RISCVObjectFile.h" +#include "rcc/Target/RISCV/RISCVPipeline.h" +#include "rcc/Target/RISCV/RISCVTarget.h" +#include "rcc/Target/TargetMachine.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace rcc { +namespace { + +constexpr std::string_view RISCV64Triple = "riscv64-unknown-linux-gnu"; +constexpr std::string_view RISCV64CPU = "generic-rv64gc"; +constexpr std::string_view RISCV64DataLayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128"; +constexpr std::string_view RISCV64ABILoweringVersion = "rcc-rv64gc-lp64d-v1"; +constexpr std::array RISCV64BaselineFeatures{"+i"}; + +[[nodiscard]] unsigned matchRISCV64Triple(std::string_view Triple) noexcept { + if (Triple == "riscv64" || Triple == "riscv64-linux-gnu" || Triple == RISCV64Triple) + return 20; + return Triple.starts_with("riscv64-") ? 10U : 0U; +} + +[[nodiscard]] std::string normalizeRISCV64Triple(std::string_view Triple) { + if (Triple == "riscv64" || Triple == "riscv64-linux-gnu") + return std::string(RISCV64Triple); + return std::string(Triple); +} + +[[nodiscard]] std::string defaultRISCV64ABI(std::span Features) { + return std::ranges::find(Features, "+d") == Features.end() ? "lp64" : "lp64d"; +} + +[[nodiscard]] TargetMachineOptions defaultRISCV64Options() { + return TargetMachineOptions{ + .Triple = std::string(RISCV64Triple), + .CPU = std::string(RISCV64CPU), + .TuneCPU = std::string(RISCV64CPU), + .Features = {"+i", "+m", "+a", "+f", "+d", "+c", "+zicsr", "+zifencei"}, + .ABI = "lp64d", + .DataLayout = std::string(RISCV64DataLayout), + .Relocation = RelocationModel::Static, + .Code = CodeModel::Small, + .Optimization = OptimizationLevel::O2, + .Options = {{"function-sections", "false"}, + {"omit-frame-pointer", "true"}, + {"stack-realignment", "false"}}, + .ABILoweringVersion = std::string(RISCV64ABILoweringVersion), + .AssemblerFlavor = "rcc-integrated-gnu", + .AssemblerVersion = "rcc-mc-v1", + .AssemblerOptions = {{"relax", "true"}}, + }; +} + +[[nodiscard]] bool booleanOption(std::span Options, + std::string_view Name) noexcept { + const auto Position = std::ranges::find(Options, Name, &TargetOption::Name); + return Position != Options.end() && Position->Value == "true"; +} + +class RISCVTargetMachine final : public TargetMachine { +public: + RISCVTargetMachine(const Target &RegisteredTarget, TargetMachineOptions Options, + TargetDescription Description, riscv::RISCVSubtargetFeatures Features, + RelocationModel Relocation, CodeModel Code, riscv::RISCVABI ABI) + : TargetMachine(RegisteredTarget, std::move(Options), std::move(Description)), + Subtarget(Features, Relocation, Code, ABI) {} + + [[nodiscard]] Expected + addCodeGenPasses(MachineFunctionPassManager &Pipeline, const MachineFunction &Function, + TargetPipelineOptions PipelineOptions) const override { + return riscv::addCanonicalPipeline(Pipeline, Function, Subtarget, PipelineOptions); + } + + [[nodiscard]] Expected> + createMachinePass(std::string_view Name, TargetPipelineOptions PipelineOptions) const override { + return riscv::createCanonicalPipelinePass(Name, Subtarget, PipelineOptions); + } + + [[nodiscard]] Expected> + codeGenPassNames(const MachineFunction &Function) const override { + return riscv::canonicalPipelinePassNames(Function, Subtarget); + } + + [[nodiscard]] Expected> + createOutlinerInfo() const override { + return riscv::createMachineOutlinerInfo( + booleanOption(options().Options, "function-sections"), + booleanOption(options().Options, "riscv-outliner-regsave")); + } + + [[nodiscard]] Expected emitAssembly(const CompilationUnit &Unit) const override { + return riscv::emitAssembly(Unit, targetDescription()); + } + + [[nodiscard]] Expected> + emitObject(const CompilationUnit &Unit) const override { + return riscv::emitObject(Unit, targetDescription()); + } + +private: + riscv::RISCVSubtarget Subtarget; +}; + +[[nodiscard]] Expected> +createRISCVTargetMachine(const Target &RegisteredTarget, TargetMachineOptions Options) { + Options.Triple = RegisteredTarget.normalizeTriple(Options.Triple); + const TargetMachineOptions Defaults = defaultRISCV64Options(); + if (Options.Triple != Defaults.Triple || Options.CPU != Defaults.CPU || + Options.TuneCPU != Defaults.TuneCPU || Options.DataLayout != Defaults.DataLayout) { + return makeUnexpected(ErrorCode::InvalidInvocation, + "backend invocation does not select a supported RV64 target"); + } + if (Options.ABILoweringVersion != RISCV64ABILoweringVersion) { + return makeUnexpected(ErrorCode::InvalidInvocation, + "RISC-V ABI lowering version does not match this build"); + } + auto Features = riscv::RISCVSubtargetFeatures::fromFeatureList(Options.Features); + if (!Features) + return std::unexpected(Features.error()); + auto ABI = riscv::parseRISCVABI(Options.ABI); + if (!ABI) + return std::unexpected(ABI.error()); + if (*ABI == riscv::RISCVABI::LP64D && !Features->has(riscv::RISCVExtension::D)) { + return makeUnexpected(ErrorCode::InvalidInvocation, "RISC-V LP64D requires the D extension"); + } + auto Description = riscv::createTargetDescription(); + if (!Description) + return std::unexpected(Description.error()); + const RelocationModel Relocation = Options.Relocation; + const CodeModel Code = Options.Code; + return std::make_unique(RegisteredTarget, std::move(Options), + std::move(*Description), *Features, Relocation, Code, + *ABI); +} + +const Target &getRISCV64Target() { + static const Target RegisteredTarget( + "riscv64", "64-bit RISC-V", true, matchRISCV64Triple, normalizeRISCV64Triple, + defaultRISCV64Options, defaultRISCV64ABI, RISCV64BaselineFeatures, createRISCVTargetMachine); + return RegisteredTarget; +} + +} // namespace + +void initializeRISCVTarget() { + TargetRegistry::registerTarget(getRISCV64Target()); +} + +} // namespace rcc diff --git a/backend/lib/Target/TargetMachine.cpp b/backend/lib/Target/TargetMachine.cpp new file mode 100644 index 0000000..bc6b704 --- /dev/null +++ b/backend/lib/Target/TargetMachine.cpp @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "rcc/Target/TargetMachine.h" + +#include "rcc/MC/TargetRegistry.h" + +#include + +namespace rcc { + +TargetMachine::TargetMachine(const Target &RegisteredTarget, TargetMachineOptions Options, + TargetDescription Description) + : RegisteredTarget(RegisteredTarget), Options(std::move(Options)), + Description(std::move(Description)) {} + +TargetMachine::~TargetMachine() = default; + +} // namespace rcc diff --git a/backend/lib/Tooling/BackendDriver.cpp b/backend/lib/Tooling/BackendDriver.cpp index fa82c0c..5932335 100644 --- a/backend/lib/Tooling/BackendDriver.cpp +++ b/backend/lib/Tooling/BackendDriver.cpp @@ -4,8 +4,7 @@ #include "rcc/CodeGen/MIRPrinter.h" #include "rcc/CodeGen/MachineFunction.h" -#include "rcc/Target/RISCV/RISCVAssemblyPrinter.h" -#include "rcc/Target/RISCV/RISCVObjectFile.h" +#include "rcc/Target/TargetMachine.h" #include #include @@ -61,13 +60,13 @@ std::string_view BackendRunResult::text() const noexcept { return {reinterpret_cast(Output.data()), Output.size()}; } -Expected runBackend(CanonicalMIR Input, const TargetDescription &Target, - const BackendInvocation &Invocation, ArtifactKind Output, - PipelineControls Controls, +Expected runBackend(CanonicalMIR Input, const BackendInvocation &Invocation, + ArtifactKind Output, PipelineControls Controls, MachinePassInstrumentation *Instrumentation) { - auto InvocationVerification = verifyRV64GCInvocation(Invocation); - if (!InvocationVerification) - return std::unexpected(InvocationVerification.error()); + auto TargetMachine = createTargetMachine(Invocation); + if (!TargetMachine) + return std::unexpected(TargetMachine.error()); + const TargetDescription &Target = (*TargetMachine)->targetDescription(); if (Input.Document.unit().functionCount() == 0) { return makeUnexpected(ErrorCode::InvalidArgument, "backend input must contain at least one machine function"); @@ -101,8 +100,8 @@ Expected runBackend(CanonicalMIR Input, const TargetDescriptio MachinePassInstrumentation LocalInstrumentation; MachinePassInstrumentation *ActiveInstrumentation = Instrumentation == nullptr ? &LocalInstrumentation : Instrumentation; - auto Pipeline = runRV64GCPipeline(Input.Document, Target, Invocation, std::move(Controls), - ActiveInstrumentation); + auto Pipeline = runTargetPipeline(Input.Document, **TargetMachine, Invocation, + std::move(Controls), ActiveInstrumentation); if (!Pipeline) return std::unexpected(Pipeline.error()); @@ -113,12 +112,12 @@ Expected runBackend(CanonicalMIR Input, const TargetDescriptio return std::unexpected(Printed.error()); Bytes = std::move(*Printed); } else if (Output == ArtifactKind::Assembly) { - auto Assembly = riscv::emitAssembly(Input.Document.unit(), Target); + auto Assembly = (*TargetMachine)->emitAssembly(Input.Document.unit()); if (!Assembly) return std::unexpected(Assembly.error()); Bytes.assign(Assembly->begin(), Assembly->end()); } else { - auto Object = riscv::emitObject(Input.Document.unit(), Target); + auto Object = (*TargetMachine)->emitObject(Input.Document.unit()); if (!Object) return std::unexpected(Object.error()); Bytes = std::move(*Object); diff --git a/backend/lib/Tooling/BackendInvocation.cpp b/backend/lib/Tooling/BackendInvocation.cpp index 232d34f..8fbc4d6 100644 --- a/backend/lib/Tooling/BackendInvocation.cpp +++ b/backend/lib/Tooling/BackendInvocation.cpp @@ -7,7 +7,9 @@ #include "rcc/CodeGen/TargetDescription.h" #include "rcc/CodeGen/TargetMIRTextInfo.h" #include "rcc/CodeGen/TargetRegisterInfo.h" +#include "rcc/MC/TargetRegistry.h" #include "rcc/Support/SHA256.h" +#include "rcc/Target/TargetMachine.h" #include #include @@ -374,27 +376,65 @@ std::string BackendInvocation::canonicalText() const { return Output; } -BackendInvocation defaultRV64GCInvocation() { +Expected defaultBackendInvocation(const Target &Target) { + TargetMachineOptions Defaults = Target.defaultMachineOptions(); BackendInvocation Result; - Result.Triple = "riscv64-unknown-linux-gnu"; - Result.CPU = "generic-rv64gc"; - Result.TuneCPU = "generic-rv64gc"; - Result.Features = {"+i", "+m", "+a", "+f", "+d", "+c", "+zicsr", "+zifencei"}; - Result.ABI = "lp64d"; - Result.DataLayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128"; - Result.TargetOptions = { - {"function-sections", "false"}, - {"omit-frame-pointer", "true"}, - {"stack-realignment", "false"}, - }; - Result.AssemblerFlavor = "rcc-integrated-gnu"; - Result.AssemblerVersion = "rcc-mc-v1"; - Result.AssemblerOptions = {{"relax", "true"}}; + Result.Triple = std::move(Defaults.Triple); + Result.CPU = std::move(Defaults.CPU); + Result.TuneCPU = std::move(Defaults.TuneCPU); + Result.Features = std::move(Defaults.Features); + Result.ABI = std::move(Defaults.ABI); + Result.DataLayout = std::move(Defaults.DataLayout); + Result.Relocation = Defaults.Relocation; + Result.Code = Defaults.Code; + Result.Optimization = Defaults.Optimization; + Result.TargetOptions = std::move(Defaults.Options); + Result.TargetABILoweringVersion = std::move(Defaults.ABILoweringVersion); + Result.AssemblerFlavor = std::move(Defaults.AssemblerFlavor); + Result.AssemblerVersion = std::move(Defaults.AssemblerVersion); + Result.AssemblerOptions = std::move(Defaults.AssemblerOptions); auto Verified = Result.canonicalizeAndVerify(); - (void)Verified; + if (!Verified) + return std::unexpected(Verified.error()); return Result; } +Expected> createTargetMachine(const BackendInvocation &Invocation) { + BackendInvocation Canonical = Invocation; + auto Verified = Canonical.canonicalizeAndVerify(); + if (!Verified) + return std::unexpected(Verified.error()); + if (Canonical != Invocation) { + return makeUnexpected(ErrorCode::InvalidInvocation, + "backend invocation must be canonicalized before execution"); + } + if (Invocation.MIRReferenceRevision != LLVMReferenceRevision || + Invocation.GenericOpcodeVersion != GenericOpcodeReferenceVersion) { + return makeUnexpected(ErrorCode::InvalidInvocation, + "backend invocation reference versions do not match this build"); + } + auto RegisteredTarget = TargetRegistry::lookupTarget(Invocation.Triple); + if (!RegisteredTarget) + return std::unexpected(RegisteredTarget.error()); + TargetMachineOptions Options{ + .Triple = Invocation.Triple, + .CPU = Invocation.CPU, + .TuneCPU = Invocation.TuneCPU, + .Features = Invocation.Features, + .ABI = Invocation.ABI, + .DataLayout = Invocation.DataLayout, + .Relocation = Invocation.Relocation, + .Code = Invocation.Code, + .Optimization = Invocation.Optimization, + .Options = Invocation.TargetOptions, + .ABILoweringVersion = Invocation.TargetABILoweringVersion, + .AssemblerFlavor = Invocation.AssemblerFlavor, + .AssemblerVersion = Invocation.AssemblerVersion, + .AssemblerOptions = Invocation.AssemblerOptions, + }; + return (*RegisteredTarget)->createTargetMachine(std::move(Options)); +} + Expected computeTargetDescriptionFingerprint(const TargetDescription &Target) { std::string Canonical; appendField(Canonical, "fingerprint-schema", "rcc-target-fingerprint-v1"); diff --git a/backend/lib/Tooling/CMakeLists.txt b/backend/lib/Tooling/CMakeLists.txt index bf20e37..361a776 100644 --- a/backend/lib/Tooling/CMakeLists.txt +++ b/backend/lib/Tooling/CMakeLists.txt @@ -36,8 +36,9 @@ target_link_libraries( RCCBackendTooling PUBLIC RCCBackend::MIR - RCCBackend::RISCV + RCCBackend::MC RCCBackend::Support + RCCBackend::Target ) set_target_properties( diff --git a/backend/lib/Tooling/MIRFuzzer.cpp b/backend/lib/Tooling/MIRFuzzer.cpp index 79e1b50..2ab3b6f 100644 --- a/backend/lib/Tooling/MIRFuzzer.cpp +++ b/backend/lib/Tooling/MIRFuzzer.cpp @@ -14,7 +14,7 @@ #include "rcc/CodeGen/MachineRegisterInfo.h" #include "rcc/CodeGen/MachineVerifier.h" #include "rcc/CodeGen/TargetDescription.h" -#include "rcc/Target/RISCV/RISCVPipeline.h" +#include "rcc/Target/TargetMachine.h" #include "rcc/Tooling/ArtifactStore.h" #include "rcc/Tooling/BackendDriver.h" @@ -262,7 +262,7 @@ private: if (Function == nullptr || isEarlierStage(Stage, stageForFunction(*Function))) { return false; } - auto Run = runBackend(std::move(*Parsed), Target, Invocation, outputForStage(Stage)); + auto Run = runBackend(std::move(*Parsed), Invocation, outputForStage(Stage)); if (!Run) return std::unexpected(Run.error()); auto RoundTrip = verifyCanonicalRoundTrip(std::string(Run->text()), Target, Invocation); @@ -295,14 +295,10 @@ private: const MachineFunction *Function = Parsed->Document.unit().functionAt(0); if (Function == nullptr) return std::unexpected(Error(ErrorCode::InternalError, "pipeline fuzz input has no function")); - auto Features = riscv::RISCVSubtargetFeatures::fromFeatureList(Invocation.Features); - if (!Features) - return std::unexpected(Features.error()); - auto ABI = riscv::parseRISCVABI(Invocation.ABI); - if (!ABI) - return std::unexpected(ABI.error()); - const riscv::RISCVSubtarget Subtarget(*Features, Invocation.Relocation, Invocation.Code, *ABI); - auto Names = riscv::canonicalPipelinePassNames(*Function, Subtarget); + auto TargetMachine = createTargetMachine(Invocation); + if (!TargetMachine) + return std::unexpected(TargetMachine.error()); + auto Names = (*TargetMachine)->codeGenPassNames(*Function); if (!Names) return std::unexpected(Names.error()); PipelineControls Controls; @@ -312,7 +308,7 @@ private: Controls.DetectUnreportedChanges = true; Invocation.Determinism.ThreadCount = std::array{1, 2, 4}[Random.index(3U)]; const std::string Stop = Controls.StopAfter ? *Controls.StopAfter : "end"; - auto Run = runBackend(std::move(*Parsed), Target, Invocation, ArtifactKind::FinalMIR, Controls); + auto Run = runBackend(std::move(*Parsed), Invocation, ArtifactKind::FinalMIR, Controls); if (!Run) return std::unexpected(Run.error()); auto RoundTrip = verifyCanonicalRoundTrip(std::string(Run->text()), Target, Invocation); @@ -486,7 +482,7 @@ using RegisterValues = std::unordered_map; parseAndCanonicalizeMIR("semantic-pipeline.mir", std::string(Canonical), Target, Invocation); if (!CompiledInput) return std::unexpected(CompiledInput.error()); - auto Compiled = runBackend(std::move(*CompiledInput), Target, Invocation, ArtifactKind::FinalMIR); + auto Compiled = runBackend(std::move(*CompiledInput), Invocation, ArtifactKind::FinalMIR); if (!Compiled) return std::unexpected(Compiled.error()); auto Final = parseAndCanonicalizeMIR("semantic-final.mir", std::string(Compiled->text()), Target, diff --git a/backend/lib/Tooling/PipelineDriver.cpp b/backend/lib/Tooling/PipelineDriver.cpp index 72c7dd2..6020979 100644 --- a/backend/lib/Tooling/PipelineDriver.cpp +++ b/backend/lib/Tooling/PipelineDriver.cpp @@ -10,7 +10,7 @@ #include "rcc/CodeGen/MachineStripDebug.h" #include "rcc/CodeGen/MachineVerifier.h" #include "rcc/MIR/ReachingDefPrinter.h" -#include "rcc/Target/RISCV/RISCVPipeline.h" +#include "rcc/Target/TargetMachine.h" #include #include @@ -100,12 +100,6 @@ struct FunctionRunResult final { return false; } -[[nodiscard]] bool booleanTargetOption(const BackendInvocation &Invocation, - std::string_view Name) noexcept { - const auto Option = std::ranges::find(Invocation.TargetOptions, Name, &BackendOption::Name); - return Option != Invocation.TargetOptions.end() && Option->Value == "true"; -} - [[nodiscard]] std::optional stageAfterPass(std::string_view Pass) noexcept { if (Pass == "legalizer") return BackendStage::LegalizedMIR; @@ -123,16 +117,16 @@ struct FunctionRunResult final { } [[nodiscard]] Expected> -createNamedMachinePass(std::string_view Name, const riscv::RISCVSubtarget &Subtarget, - riscv::RISCVPipelineOptions Options = {}) { +createNamedMachinePass(std::string_view Name, const TargetMachine &TargetMachine, + TargetPipelineOptions Options = {}) { if (Name == mir::ReachingDefPrinterPass::name()) return std::make_unique(); - return riscv::createCanonicalPipelinePass(Name, Subtarget, Options); + return TargetMachine.createMachinePass(Name, Options); } [[nodiscard]] Expected createPasses(const MachineFunction &Function, const BackendInvocation &Invocation, - const riscv::RISCVSubtarget &Subtarget, MachinePassManagerOptions Options) { + const TargetMachine &TargetMachine, MachinePassManagerOptions Options) { const auto GlobalISelAbortOption = std::ranges::find( Invocation.TargetOptions, std::string_view{"global-isel-abort"}, &BackendOption::Name); GlobalISelAbortMode GlobalISelAbort = GlobalISelAbortMode::Enable; @@ -146,13 +140,13 @@ createPasses(const MachineFunction &Function, const BackendInvocation &Invocatio const bool DebugMachineScheduler = hasDebugType(Invocation, "machine-scheduler"); const bool DebugPrologueEpilogue = hasDebugType(Invocation, "prologepilog"); if (Invocation.PassPipeline.size() == 1 && Invocation.PassPipeline.front() == "default") { - auto Added = riscv::addCanonicalPipeline( - Pipeline, Function, Subtarget, - riscv::RISCVPipelineOptions{.Optimization = Invocation.Optimization, - .UseTargetCopyInstructions = true, - .GlobalISelAbort = GlobalISelAbort, - .DebugMachineScheduler = DebugMachineScheduler, - .DebugPrologueEpilogue = DebugPrologueEpilogue}); + auto Added = TargetMachine.addCodeGenPasses( + Pipeline, Function, + TargetPipelineOptions{.Optimization = Invocation.Optimization, + .UseTargetCopyInstructions = true, + .GlobalISelAbort = GlobalISelAbort, + .DebugMachineScheduler = DebugMachineScheduler, + .DebugPrologueEpilogue = DebugPrologueEpilogue}); if (!Added) return std::unexpected(Added.error()); } else { @@ -166,12 +160,12 @@ createPasses(const MachineFunction &Function, const BackendInvocation &Invocatio TargetCopyOption != Invocation.TargetOptions.end() && TargetCopyOption->Value == "true"; for (const std::string &Name : Invocation.PassPipeline) { auto Pass = createNamedMachinePass( - Name, Subtarget, - riscv::RISCVPipelineOptions{.Optimization = Invocation.Optimization, - .UseTargetCopyInstructions = UseTargetCopyInstructions, - .GlobalISelAbort = GlobalISelAbort, - .DebugMachineScheduler = DebugMachineScheduler, - .DebugPrologueEpilogue = DebugPrologueEpilogue}); + Name, TargetMachine, + TargetPipelineOptions{.Optimization = Invocation.Optimization, + .UseTargetCopyInstructions = UseTargetCopyInstructions, + .GlobalISelAbort = GlobalISelAbort, + .DebugMachineScheduler = DebugMachineScheduler, + .DebugPrologueEpilogue = DebugPrologueEpilogue}); if (!Pass) return std::unexpected(Pass.error()); auto Added = Pipeline.addPass(std::move(*Pass)); @@ -183,7 +177,7 @@ createPasses(const MachineFunction &Function, const BackendInvocation &Invocatio } [[nodiscard]] Expected validateSelector(std::string_view Selector, bool AllowAll, - const riscv::RISCVSubtarget &Subtarget) { + const TargetMachine &TargetMachine) { if (Selector == "all" && AllowAll) return {}; const std::size_t Comma = Selector.find(','); @@ -192,7 +186,7 @@ createPasses(const MachineFunction &Function, const BackendInvocation &Invocatio if (Name.empty()) return makeUnexpected(ErrorCode::InvalidInvocation, "empty pipeline pass selector"); if (Name != "machine-outliner") { - auto Pass = createNamedMachinePass(Name, Subtarget); + auto Pass = createNamedMachinePass(Name, TargetMachine); if (!Pass) return std::unexpected(Pass.error()); } @@ -211,8 +205,7 @@ createPasses(const MachineFunction &Function, const BackendInvocation &Invocatio } [[nodiscard]] Expected validateControls(const PipelineControls &Controls, - riscv::RISCVSubtargetFeatures Features) { - const riscv::RISCVSubtarget Subtarget(Features); + const TargetMachine &TargetMachine) { if (Controls.StartBefore && Controls.StartAfter) { return makeUnexpected(ErrorCode::InvalidInvocation, "-start-before and -start-after cannot be combined"); @@ -222,32 +215,32 @@ createPasses(const MachineFunction &Function, const BackendInvocation &Invocatio "-stop-before and -stop-after cannot be combined"); } if (Controls.StartBefore) { - auto Result = validateSelector(*Controls.StartBefore, false, Subtarget); + auto Result = validateSelector(*Controls.StartBefore, false, TargetMachine); if (!Result) return Result; } if (Controls.StartAfter) { - auto Result = validateSelector(*Controls.StartAfter, false, Subtarget); + auto Result = validateSelector(*Controls.StartAfter, false, TargetMachine); if (!Result) return Result; } if (Controls.StopBefore) { - auto Result = validateSelector(*Controls.StopBefore, false, Subtarget); + auto Result = validateSelector(*Controls.StopBefore, false, TargetMachine); if (!Result) return Result; } if (Controls.StopAfter) { - auto Result = validateSelector(*Controls.StopAfter, false, Subtarget); + auto Result = validateSelector(*Controls.StopAfter, false, TargetMachine); if (!Result) return Result; } for (const std::string &Selector : Controls.PrintBefore) { - auto Result = validateSelector(Selector, true, Subtarget); + auto Result = validateSelector(Selector, true, TargetMachine); if (!Result) return Result; } for (const std::string &Selector : Controls.PrintAfter) { - auto Result = validateSelector(Selector, true, Subtarget); + auto Result = validateSelector(Selector, true, TargetMachine); if (!Result) return Result; } @@ -278,21 +271,18 @@ captureSnapshot(FunctionRunResult &Result, const mir::MIRDocument &Document, } [[nodiscard]] Expected -runFunction(MachineFunction &Function, mir::MIRDocument &Document, const TargetDescription &Target, - const BackendInvocation &Invocation, const PipelineControls &Controls, - riscv::RISCVSubtargetFeatures Features, bool AllowSnapshots, +runFunction(MachineFunction &Function, mir::MIRDocument &Document, + const TargetMachine &TargetMachine, const BackendInvocation &Invocation, + const PipelineControls &Controls, bool AllowSnapshots, MachinePassInstrumentation *Instrumentation) { FunctionRunResult Result; - auto ABI = riscv::parseRISCVABI(Invocation.ABI); - if (!ABI) - return std::unexpected(ABI.error()); - const riscv::RISCVSubtarget Subtarget(Features, Invocation.Relocation, Invocation.Code, *ABI); + const TargetDescription &Target = TargetMachine.targetDescription(); MachinePassManagerOptions ManagerOptions{ .VerifyBefore = true, .VerifyEach = Controls.VerifyEach, .DetectUnreportedChanges = Controls.DetectUnreportedChanges, }; - auto Passes = createPasses(Function, Invocation, Subtarget, ManagerOptions); + auto Passes = createPasses(Function, Invocation, TargetMachine, ManagerOptions); if (!Passes) return std::unexpected(Passes.error()); MachineFunctionAnalysisManager Analyses; @@ -402,12 +392,9 @@ runFunction(MachineFunction &Function, mir::MIRDocument &Document, const TargetD } [[nodiscard]] Expected -runMachineOutliner(mir::MIRDocument &Document, const TargetDescription &Target, - const BackendInvocation &Invocation, const PipelineControls &Controls, - MachinePassInstrumentation *Instrumentation) { - auto TargetInfo = - riscv::createMachineOutlinerInfo(booleanTargetOption(Invocation, "function-sections"), - booleanTargetOption(Invocation, "riscv-outliner-regsave")); +runMachineOutliner(mir::MIRDocument &Document, const TargetMachine &TargetMachine, + const PipelineControls &Controls, MachinePassInstrumentation *Instrumentation) { + auto TargetInfo = TargetMachine.createOutlinerInfo(); if (!TargetInfo) return std::unexpected(TargetInfo.error()); @@ -434,7 +421,8 @@ runMachineOutliner(mir::MIRDocument &Document, const TargetDescription &Target, auto Registered = registerMachineOptimizationAnalyses(FunctionAnalyses); if (!Registered) return std::unexpected(Registered.error()); - auto Run = Pipeline.run(Document.unit(), Target, UnitAnalyses, FunctionAnalyses, Instrumentation); + auto Run = Pipeline.run(Document.unit(), TargetMachine.targetDescription(), UnitAnalyses, + FunctionAnalyses, Instrumentation); if (!Run) return std::unexpected(Run.error()); PipelineRunResult Result; @@ -507,51 +495,12 @@ Expected parseBackendStage(std::string_view Value) { return makeUnexpected(ErrorCode::InvalidInvocation, "unknown backend stage"); } -Expected verifyRV64GCInvocation(const BackendInvocation &Invocation) { - BackendInvocation Canonical = Invocation; - auto Verified = Canonical.canonicalizeAndVerify(); - if (!Verified) - return std::unexpected(Verified.error()); - if (Canonical != Invocation) { - return makeUnexpected(ErrorCode::InvalidInvocation, - "RV64GC backend invocation must be canonicalized before execution"); - } - const BackendInvocation Expected = defaultRV64GCInvocation(); - auto Features = riscv::RISCVSubtargetFeatures::fromFeatureList(Invocation.Features); - if (!Features) - return std::unexpected(Features.error()); - auto ABI = riscv::parseRISCVABI(Invocation.ABI); - if (!ABI) - return std::unexpected(ABI.error()); - if (*ABI == riscv::RISCVABI::LP64D && !Features->has(riscv::RISCVExtension::D)) { - return makeUnexpected(ErrorCode::InvalidInvocation, "RISC-V LP64D requires the D extension"); - } - if (Invocation.Triple != Expected.Triple || Invocation.CPU != Expected.CPU || - Invocation.TuneCPU != Expected.TuneCPU || Invocation.DataLayout != Expected.DataLayout) { - return makeUnexpected(ErrorCode::InvalidInvocation, - "backend invocation does not select a supported RV64 target"); - } - if (Invocation.MIRReferenceRevision != LLVMReferenceRevision || - Invocation.GenericOpcodeVersion != GenericOpcodeReferenceVersion || - Invocation.TargetABILoweringVersion != RV64GCTargetABILoweringVersion) { - return makeUnexpected(ErrorCode::InvalidInvocation, - "backend invocation reference versions do not match this build"); - } - return {}; -} - -Expected runRV64GCPipeline(mir::MIRDocument &Document, - const TargetDescription &Target, +Expected runTargetPipeline(mir::MIRDocument &Document, + const TargetMachine &TargetMachine, const BackendInvocation &Invocation, PipelineControls Controls, MachinePassInstrumentation *Instrumentation) { - auto InvocationVerification = verifyRV64GCInvocation(Invocation); - if (!InvocationVerification) - return std::unexpected(InvocationVerification.error()); - auto Features = riscv::RISCVSubtargetFeatures::fromFeatureList(Invocation.Features); - if (!Features) - return std::unexpected(Features.error()); - auto ControlVerification = validateControls(Controls, *Features); + auto ControlVerification = validateControls(Controls, TargetMachine); if (!ControlVerification) return std::unexpected(ControlVerification.error()); auto UnitVerification = Document.unit().verify(); @@ -583,7 +532,7 @@ Expected runRV64GCPipeline(mir::MIRDocument &Document, return makeUnexpected(ErrorCode::InvalidInvocation, "machine-outliner cannot be mixed with per-function passes"); } - return runMachineOutliner(Document, Target, Invocation, Controls, ActiveInstrumentation); + return runMachineOutliner(Document, TargetMachine, Controls, ActiveInstrumentation); } const std::size_t FunctionCount = Document.unit().functionCount(); @@ -596,7 +545,7 @@ Expected runRV64GCPipeline(mir::MIRDocument &Document, if (Function == nullptr) return makeUnexpected(ErrorCode::InternalError, "compilation unit contains a null function"); - auto Run = runFunction(*Function, Document, Target, Invocation, Controls, *Features, true, + auto Run = runFunction(*Function, Document, TargetMachine, Invocation, Controls, true, ActiveInstrumentation); if (!Run) return std::unexpected(Run.error()); @@ -617,7 +566,7 @@ Expected runRV64GCPipeline(mir::MIRDocument &Document, Error(ErrorCode::InternalError, "compilation unit contains a null function"); continue; } - auto Run = runFunction(*Function, Document, Target, Invocation, Controls, *Features, false, + auto Run = runFunction(*Function, Document, TargetMachine, Invocation, Controls, false, ActiveInstrumentation); if (!Run) { FunctionResults[Index].Failure = Run.error(); -- cgit v1.2.3