From b7716aba458fb3d49742f7a3c169deee7345396e Mon Sep 17 00:00:00 2001 From: Richard-Qin-X Date: Tue, 11 Aug 2026 19:36:06 +0800 Subject: [RISCV] Honor invocation subtarget features --- backend/lib/Target/RISCV/CMakeLists.txt | 4 ++ backend/lib/Target/RISCV/RISCVCodeGen.cpp | 15 ++++- backend/lib/Target/RISCV/RISCVPipeline.cpp | 24 ++++++-- backend/lib/Target/RISCV/RISCVSubtarget.cpp | 94 +++++++++++++++++++++++++++++ backend/lib/Tooling/PipelineDriver.cpp | 27 ++++++--- 5 files changed, 147 insertions(+), 17 deletions(-) create mode 100644 backend/lib/Target/RISCV/RISCVSubtarget.cpp (limited to 'backend/lib') diff --git a/backend/lib/Target/RISCV/CMakeLists.txt b/backend/lib/Target/RISCV/CMakeLists.txt index eae426c..68bd444 100644 --- a/backend/lib/Target/RISCV/CMakeLists.txt +++ b/backend/lib/Target/RISCV/CMakeLists.txt @@ -66,6 +66,7 @@ add_library( RISCVPseudoExpansion.cpp RISCVRegisterAllocation.cpp RISCVScheduling.cpp + RISCVSubtarget.cpp RISCVTarget.cpp RISCVTargetConstant.cpp "${RCCBackendRISCVGeneratedRegisterHeader}" @@ -135,6 +136,7 @@ rcc_backend_register_format_files( "${CMAKE_CURRENT_SOURCE_DIR}/RISCVPseudoExpansion.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/RISCVRegisterAllocation.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/RISCVScheduling.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/RISCVSubtarget.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/RISCVTarget.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/RISCVTargetConstant.cpp" "${RCC_BACKEND_SOURCE_DIR}/include/rcc/Target/RISCV/RISCVCodeGen.h" @@ -147,6 +149,7 @@ rcc_backend_register_format_files( "${RCC_BACKEND_SOURCE_DIR}/include/rcc/Target/RISCV/RISCVMCCodeEmitter.h" "${RCC_BACKEND_SOURCE_DIR}/include/rcc/Target/RISCV/RISCVObjectFile.h" "${RCC_BACKEND_SOURCE_DIR}/include/rcc/Target/RISCV/RISCVPipeline.h" + "${RCC_BACKEND_SOURCE_DIR}/include/rcc/Target/RISCV/RISCVSubtarget.h" "${RCC_BACKEND_SOURCE_DIR}/include/rcc/Target/RISCV/RISCVMCTargetLowering.h" "${RCC_BACKEND_SOURCE_DIR}/include/rcc/Target/RISCV/RISCVTarget.h" "${RCC_BACKEND_SOURCE_DIR}/include/rcc/Target/RISCV/RISCVTargetConstant.h" @@ -172,6 +175,7 @@ rcc_backend_register_tidy_files( "${CMAKE_CURRENT_SOURCE_DIR}/RISCVPseudoExpansion.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/RISCVRegisterAllocation.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/RISCVScheduling.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/RISCVSubtarget.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/RISCVTarget.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/RISCVTargetConstant.cpp" ) diff --git a/backend/lib/Target/RISCV/RISCVCodeGen.cpp b/backend/lib/Target/RISCV/RISCVCodeGen.cpp index 8676a2b..0bd3298 100644 --- a/backend/lib/Target/RISCV/RISCVCodeGen.cpp +++ b/backend/lib/Target/RISCV/RISCVCodeGen.cpp @@ -2879,9 +2879,15 @@ buildIncomingConversion(MachineFunction &Function, const TargetDescription &Targ class RISCVLegalizerInfo final : public TargetLegalizerInfo { public: + explicit RISCVLegalizerInfo(RISCVSubtargetFeatures Features) : Features(Features) {} + [[nodiscard]] Expected classify(const MachineInstr &Instruction, const MachineFunction &Function, const TargetDescription &) const override { + if (!Features.has(RISCVExtension::I)) { + return makeUnexpected(ErrorCode::InternalError, + "RISC-V legalizer requires the base I extension"); + } if (!isGenericOpcode(Instruction.opcode())) return LegalizationAction::Legal; @@ -3560,6 +3566,9 @@ public: return makeUnexpected(ErrorCode::Unsupported, "RISC-V legalizer has no lowering rule for this instruction"); } + +private: + RISCVSubtargetFeatures Features; }; class RISCVRegisterBankInfo final : public TargetRegisterBankInfo { @@ -6284,10 +6293,14 @@ private: } // namespace const TargetLegalizerInfo &legalizerInfo() noexcept { - static const RISCVLegalizerInfo Value; + static const RISCVLegalizerInfo Value(RISCVSubtargetFeatures::rv64gc()); return Value; } +std::unique_ptr createLegalizerInfo(RISCVSubtargetFeatures Features) { + return std::make_unique(Features); +} + const TargetRegisterBankInfo ®isterBankInfo() noexcept { static const RISCVRegisterBankInfo Value; return Value; diff --git a/backend/lib/Target/RISCV/RISCVPipeline.cpp b/backend/lib/Target/RISCV/RISCVPipeline.cpp index 8d05bba..05d26b3 100644 --- a/backend/lib/Target/RISCV/RISCVPipeline.cpp +++ b/backend/lib/Target/RISCV/RISCVPipeline.cpp @@ -25,8 +25,9 @@ template return Pipeline.addPass(std::make_unique(std::forward(Arguments)...)); } -[[nodiscard]] Expected addInitialPipeline(MachinePassManager &Pipeline) { - auto Added = addPass(Pipeline, legalizerInfo()); +[[nodiscard]] Expected addInitialPipeline(MachinePassManager &Pipeline, + RISCVSubtargetFeatures Features) { + auto Added = addPass(Pipeline, createLegalizerInfo(Features)); if (!Added) return std::unexpected(Added.error()); Added = addPass(Pipeline, registerBankInfo()); @@ -52,8 +53,9 @@ template return addPass(Pipeline, registerAllocationInfo()); } -[[nodiscard]] Expected addCompleteRegistry(MachinePassManager &Pipeline) { - auto Added = addInitialPipeline(Pipeline); +[[nodiscard]] Expected addCompleteRegistry(MachinePassManager &Pipeline, + RISCVSubtargetFeatures Features) { + auto Added = addInitialPipeline(Pipeline, Features); if (!Added) return std::unexpected(Added.error()); Added = addSSAExitPipeline(Pipeline); @@ -68,8 +70,13 @@ template } // namespace Expected addCanonicalPipeline(MachinePassManager &Pipeline, const MachineFunction &Function) { + return addCanonicalPipeline(Pipeline, Function, RISCVSubtargetFeatures::rv64gc()); +} + +Expected addCanonicalPipeline(MachinePassManager &Pipeline, const MachineFunction &Function, + RISCVSubtargetFeatures Features) { if (!Function.properties().has(MachineFunctionProperty::Legalized)) { - auto Added = addPass(Pipeline, legalizerInfo()); + auto Added = addPass(Pipeline, createLegalizerInfo(Features)); if (!Added) return std::unexpected(Added.error()); } @@ -112,10 +119,15 @@ Expected addCanonicalPipeline(MachinePassManager &Pipeline, const MachineF } Expected> createCanonicalPipelinePass(std::string_view Name) { + return createCanonicalPipelinePass(Name, RISCVSubtargetFeatures::rv64gc()); +} + +Expected> +createCanonicalPipelinePass(std::string_view Name, RISCVSubtargetFeatures Features) { if (Name.empty()) return makeUnexpected(ErrorCode::InvalidArgument, "pipeline pass name cannot be empty"); MachinePassManager Registry; - auto Added = addCompleteRegistry(Registry); + auto Added = addCompleteRegistry(Registry, Features); if (!Added) return std::unexpected(Added.error()); MachinePassManager::PassList Passes = std::move(Registry).takePasses(); diff --git a/backend/lib/Target/RISCV/RISCVSubtarget.cpp b/backend/lib/Target/RISCV/RISCVSubtarget.cpp new file mode 100644 index 0000000..3a3d9e2 --- /dev/null +++ b/backend/lib/Target/RISCV/RISCVSubtarget.cpp @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "rcc/Target/RISCV/RISCVSubtarget.h" + +#include +#include +#include + +namespace rcc::riscv { +namespace { + +struct ExtensionName final { + std::string_view Name; + RISCVExtension Extension; +}; + +constexpr std::array ExtensionNames{ + ExtensionName{"i", RISCVExtension::I}, + ExtensionName{"m", RISCVExtension::M}, + ExtensionName{"a", RISCVExtension::A}, + ExtensionName{"f", RISCVExtension::F}, + ExtensionName{"d", RISCVExtension::D}, + ExtensionName{"c", RISCVExtension::C}, + ExtensionName{"zicsr", RISCVExtension::Zicsr}, + ExtensionName{"zifencei", RISCVExtension::Zifencei}, + ExtensionName{"zacas", RISCVExtension::Zacas}, + ExtensionName{"zabha", RISCVExtension::Zabha}, +}; + +[[nodiscard]] constexpr std::uint16_t extensionBit(RISCVExtension Extension) noexcept { + return static_cast(std::uint16_t{1} << static_cast(Extension)); +} + +[[nodiscard]] const ExtensionName *findExtension(std::string_view Name) noexcept { + for (const ExtensionName &Entry : ExtensionNames) { + if (Entry.Name == Name) + return &Entry; + } + return nullptr; +} + +} // namespace + +Expected +RISCVSubtargetFeatures::fromFeatureList(std::span Features) { + std::uint16_t Enabled = extensionBit(RISCVExtension::I); + for (const std::string &Feature : Features) { + if (Feature.size() < 2 || (Feature.front() != '+' && Feature.front() != '-')) { + return makeUnexpected(ErrorCode::InvalidInvocation, + "RISC-V target feature must use canonical +/- spelling"); + } + const ExtensionName *Extension = findExtension(std::string_view(Feature).substr(1)); + if (Extension == nullptr) { + return makeUnexpected(ErrorCode::InvalidInvocation, + "RISC-V backend does not support target feature '" + Feature + "'"); + } + if (Feature.front() == '+') + Enabled |= extensionBit(Extension->Extension); + else + Enabled &= static_cast(~extensionBit(Extension->Extension)); + } + + RISCVSubtargetFeatures Result(Enabled); + if (!Result.has(RISCVExtension::I)) { + return makeUnexpected(ErrorCode::InvalidInvocation, + "RV64 target feature configuration cannot disable the base I extension"); + } + if (Result.has(RISCVExtension::D) && !Result.has(RISCVExtension::F)) { + return makeUnexpected(ErrorCode::InvalidInvocation, + "RISC-V D extension requires the F extension"); + } + if ((Result.has(RISCVExtension::Zacas) || Result.has(RISCVExtension::Zabha)) && + !Result.has(RISCVExtension::A)) { + return makeUnexpected(ErrorCode::InvalidInvocation, + "RISC-V Zacas and Zabha extensions require the A extension"); + } + return Result; +} + +RISCVSubtargetFeatures RISCVSubtargetFeatures::rv64gc() noexcept { + return RISCVSubtargetFeatures(extensionBit(RISCVExtension::I) | extensionBit(RISCVExtension::M) | + extensionBit(RISCVExtension::A) | extensionBit(RISCVExtension::F) | + extensionBit(RISCVExtension::D) | extensionBit(RISCVExtension::C) | + extensionBit(RISCVExtension::Zicsr) | + extensionBit(RISCVExtension::Zifencei)); +} + +bool RISCVSubtargetFeatures::has(RISCVExtension Extension) const noexcept { + if (Extension >= RISCVExtension::Count) + return false; + return (Enabled & extensionBit(Extension)) != 0; +} + +} // namespace rcc::riscv diff --git a/backend/lib/Tooling/PipelineDriver.cpp b/backend/lib/Tooling/PipelineDriver.cpp index e9a3d9d..b7844cd 100644 --- a/backend/lib/Tooling/PipelineDriver.cpp +++ b/backend/lib/Tooling/PipelineDriver.cpp @@ -72,10 +72,10 @@ struct FunctionRunResult final { [[nodiscard]] Expected createPasses(const MachineFunction &Function, const BackendInvocation &Invocation, - MachinePassManagerOptions Options) { + riscv::RISCVSubtargetFeatures Features, MachinePassManagerOptions Options) { MachinePassManager Pipeline(Options); if (Invocation.PassPipeline.size() == 1 && Invocation.PassPipeline.front() == "default") { - auto Added = riscv::addCanonicalPipeline(Pipeline, Function); + auto Added = riscv::addCanonicalPipeline(Pipeline, Function, Features); if (!Added) return std::unexpected(Added.error()); } else { @@ -86,7 +86,7 @@ createPasses(const MachineFunction &Function, const BackendInvocation &Invocatio for (const std::string &Name : Invocation.PassPipeline) { if (Name == "none") continue; - auto Pass = riscv::createCanonicalPipelinePass(Name); + auto Pass = riscv::createCanonicalPipelinePass(Name, Features); if (!Pass) return std::unexpected(Pass.error()); auto Added = Pipeline.addPass(std::move(*Pass)); @@ -171,14 +171,15 @@ captureSnapshot(FunctionRunResult &Result, const mir::MIRDocument &Document, [[nodiscard]] Expected runFunction(MachineFunction &Function, mir::MIRDocument &Document, const TargetDescription &Target, const BackendInvocation &Invocation, const PipelineControls &Controls, - bool AllowSnapshots, MachinePassInstrumentation *Instrumentation) { + riscv::RISCVSubtargetFeatures Features, bool AllowSnapshots, + MachinePassInstrumentation *Instrumentation) { FunctionRunResult Result; MachinePassManagerOptions ManagerOptions{ .VerifyBefore = true, .VerifyEach = Controls.VerifyEach, .DetectUnreportedChanges = Controls.DetectUnreportedChanges, }; - auto Passes = createPasses(Function, Invocation, ManagerOptions); + auto Passes = createPasses(Function, Invocation, Features, ManagerOptions); if (!Passes) return std::unexpected(Passes.error()); MachineAnalysisManager Analyses; @@ -326,11 +327,14 @@ Expected verifyRV64GCInvocation(const BackendInvocation &Invocation) { "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()); if (Invocation.Triple != Expected.Triple || Invocation.CPU != Expected.CPU || - Invocation.TuneCPU != Expected.TuneCPU || Invocation.Features != Expected.Features || - Invocation.ABI != Expected.ABI || Invocation.DataLayout != Expected.DataLayout) { + Invocation.TuneCPU != Expected.TuneCPU || Invocation.ABI != Expected.ABI || + Invocation.DataLayout != Expected.DataLayout) { return makeUnexpected(ErrorCode::InvalidInvocation, - "backend invocation does not select the supported RV64GC LP64D target"); + "backend invocation does not select the supported RV64 LP64D target"); } if (Invocation.MIRReferenceRevision != LLVMReferenceRevision || Invocation.GenericOpcodeVersion != GenericOpcodeReferenceVersion || @@ -349,6 +353,9 @@ Expected runRV64GCPipeline(mir::MIRDocument &Document, 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); if (!ControlVerification) return std::unexpected(ControlVerification.error()); @@ -383,7 +390,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, true, + auto Run = runFunction(*Function, Document, Target, Invocation, Controls, *Features, true, ActiveInstrumentation); if (!Run) return std::unexpected(Run.error()); @@ -404,7 +411,7 @@ Expected runRV64GCPipeline(mir::MIRDocument &Document, Error(ErrorCode::InternalError, "compilation unit contains a null function"); continue; } - auto Run = runFunction(*Function, Document, Target, Invocation, Controls, false, + auto Run = runFunction(*Function, Document, Target, Invocation, Controls, *Features, false, ActiveInstrumentation); if (!Run) { FunctionResults[Index].Failure = Run.error(); -- cgit v1.2.3