aboutsummaryrefslogtreecommitdiff
path: root/backend
diff options
context:
space:
mode:
authorRichard-Qin-X <richard.qin.001@gmail.com>2026-08-11 19:36:06 +0800
committerRichard-Qin-X <richard.qin.001@gmail.com>2026-08-11 19:36:06 +0800
commitb7716aba458fb3d49742f7a3c169deee7345396e (patch)
tree85a8f99a8bd06e9dfbe6e7973c3440f2db7110af /backend
parente7bf0d93d40ad86902d9a8636d24420b5abd2937 (diff)
downloadrcc-project-b7716aba458fb3d49742f7a3c169deee7345396e.tar.gz
rcc-project-b7716aba458fb3d49742f7a3c169deee7345396e.tar.xz
[RISCV] Honor invocation subtarget features
Diffstat (limited to 'backend')
-rw-r--r--backend/include/rcc/CodeGen/MachinePipelinePasses.h7
-rw-r--r--backend/include/rcc/Target/RISCV/RISCVCodeGen.h5
-rw-r--r--backend/include/rcc/Target/RISCV/RISCVPipeline.h6
-rw-r--r--backend/include/rcc/Target/RISCV/RISCVSubtarget.h47
-rw-r--r--backend/lib/Target/RISCV/CMakeLists.txt4
-rw-r--r--backend/lib/Target/RISCV/RISCVCodeGen.cpp15
-rw-r--r--backend/lib/Target/RISCV/RISCVPipeline.cpp24
-rw-r--r--backend/lib/Target/RISCV/RISCVSubtarget.cpp94
-rw-r--r--backend/lib/Tooling/PipelineDriver.cpp27
-rw-r--r--backend/unittests/Tooling/PipelineDriverTest.cpp19
10 files changed, 230 insertions, 18 deletions
diff --git a/backend/include/rcc/CodeGen/MachinePipelinePasses.h b/backend/include/rcc/CodeGen/MachinePipelinePasses.h
index 08f0dac..1187e4a 100644
--- a/backend/include/rcc/CodeGen/MachinePipelinePasses.h
+++ b/backend/include/rcc/CodeGen/MachinePipelinePasses.h
@@ -6,12 +6,16 @@
#include "rcc/CodeGen/MachinePass.h"
#include "rcc/CodeGen/TargetCodeGen.h"
+#include <memory>
+
namespace rcc {
class LegalizerPass final : public MachineFunctionPass {
public:
explicit LegalizerPass(const TargetLegalizerInfo *Info) : Info(Info) {}
explicit LegalizerPass(const TargetLegalizerInfo &Info) : Info(&Info) {}
+ explicit LegalizerPass(std::unique_ptr<TargetLegalizerInfo> Info)
+ : OwnedInfo(std::move(Info)), Info(OwnedInfo.get()) {}
[[nodiscard]] const MachinePassContract &contract() const noexcept override;
[[nodiscard]] Expected<MachinePassResult> run(MachineFunction &Function,
@@ -23,7 +27,8 @@ private:
const TargetDescription &Target,
MachineActionReplay &Actions);
- const TargetLegalizerInfo *Info;
+ std::unique_ptr<TargetLegalizerInfo> OwnedInfo;
+ const TargetLegalizerInfo *Info = nullptr;
};
class RegisterBankSelectPass final : public MachineFunctionPass {
diff --git a/backend/include/rcc/Target/RISCV/RISCVCodeGen.h b/backend/include/rcc/Target/RISCV/RISCVCodeGen.h
index b6b207c..a39a3fa 100644
--- a/backend/include/rcc/Target/RISCV/RISCVCodeGen.h
+++ b/backend/include/rcc/Target/RISCV/RISCVCodeGen.h
@@ -12,10 +12,15 @@
#include "rcc/CodeGen/TargetPseudoExpansion.h"
#include "rcc/CodeGen/TargetRegisterAllocationInfo.h"
#include "rcc/CodeGen/TargetSchedulingModel.h"
+#include "rcc/Target/RISCV/RISCVSubtarget.h"
+
+#include <memory>
namespace rcc::riscv {
[[nodiscard]] const TargetLegalizerInfo &legalizerInfo() noexcept;
+[[nodiscard]] std::unique_ptr<TargetLegalizerInfo>
+createLegalizerInfo(RISCVSubtargetFeatures Features);
[[nodiscard]] const TargetRegisterBankInfo &registerBankInfo() noexcept;
[[nodiscard]] const TargetInstructionSelector &instructionSelector() noexcept;
[[nodiscard]] const TargetSSAInfo &ssaInfo() noexcept;
diff --git a/backend/include/rcc/Target/RISCV/RISCVPipeline.h b/backend/include/rcc/Target/RISCV/RISCVPipeline.h
index 8bd79e9..fa2cb45 100644
--- a/backend/include/rcc/Target/RISCV/RISCVPipeline.h
+++ b/backend/include/rcc/Target/RISCV/RISCVPipeline.h
@@ -5,6 +5,7 @@
#include "rcc/CodeGen/MachinePass.h"
#include "rcc/Support/Error.h"
+#include "rcc/Target/RISCV/RISCVSubtarget.h"
#include <memory>
#include <string>
@@ -20,8 +21,13 @@ namespace riscv {
[[nodiscard]] Expected<void> addCanonicalPipeline(MachinePassManager &Pipeline,
const MachineFunction &Function);
+[[nodiscard]] Expected<void> addCanonicalPipeline(MachinePassManager &Pipeline,
+ const MachineFunction &Function,
+ RISCVSubtargetFeatures Features);
[[nodiscard]] Expected<std::unique_ptr<MachineFunctionPass>>
createCanonicalPipelinePass(std::string_view Name);
+[[nodiscard]] Expected<std::unique_ptr<MachineFunctionPass>>
+createCanonicalPipelinePass(std::string_view Name, RISCVSubtargetFeatures Features);
[[nodiscard]] Expected<std::vector<std::string>>
canonicalPipelinePassNames(const MachineFunction &Function);
[[nodiscard]] Expected<void>
diff --git a/backend/include/rcc/Target/RISCV/RISCVSubtarget.h b/backend/include/rcc/Target/RISCV/RISCVSubtarget.h
new file mode 100644
index 0000000..d5f088f
--- /dev/null
+++ b/backend/include/rcc/Target/RISCV/RISCVSubtarget.h
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#ifndef RCC_TARGET_RISCV_RISCVSUBTARGET_H
+#define RCC_TARGET_RISCV_RISCVSUBTARGET_H
+
+#include "rcc/Support/Error.h"
+
+#include <cstdint>
+#include <span>
+#include <string>
+
+namespace rcc::riscv {
+
+enum class RISCVExtension : std::uint8_t {
+ I,
+ M,
+ A,
+ F,
+ D,
+ C,
+ Zicsr,
+ Zifencei,
+ Zacas,
+ Zabha,
+ Count,
+};
+
+class RISCVSubtargetFeatures final {
+public:
+ [[nodiscard]] static Expected<RISCVSubtargetFeatures>
+ fromFeatureList(std::span<const std::string> Features);
+ [[nodiscard]] static RISCVSubtargetFeatures rv64gc() noexcept;
+
+ [[nodiscard]] bool has(RISCVExtension Extension) const noexcept;
+
+ friend bool operator==(const RISCVSubtargetFeatures &Left,
+ const RISCVSubtargetFeatures &Right) = default;
+
+private:
+ explicit constexpr RISCVSubtargetFeatures(std::uint16_t Enabled) noexcept : Enabled(Enabled) {}
+
+ std::uint16_t Enabled = 0;
+};
+
+} // namespace rcc::riscv
+
+#endif // RCC_TARGET_RISCV_RISCVSUBTARGET_H
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<LegalizationAction> 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<TargetLegalizerInfo> createLegalizerInfo(RISCVSubtargetFeatures Features) {
+ return std::make_unique<RISCVLegalizerInfo>(Features);
+}
+
const TargetRegisterBankInfo &registerBankInfo() 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 <typename PassT, typename... ArgumentT>
return Pipeline.addPass(std::make_unique<PassT>(std::forward<ArgumentT>(Arguments)...));
}
-[[nodiscard]] Expected<void> addInitialPipeline(MachinePassManager &Pipeline) {
- auto Added = addPass<LegalizerPass>(Pipeline, legalizerInfo());
+[[nodiscard]] Expected<void> addInitialPipeline(MachinePassManager &Pipeline,
+ RISCVSubtargetFeatures Features) {
+ auto Added = addPass<LegalizerPass>(Pipeline, createLegalizerInfo(Features));
if (!Added)
return std::unexpected(Added.error());
Added = addPass<RegisterBankSelectPass>(Pipeline, registerBankInfo());
@@ -52,8 +53,9 @@ template <typename PassT, typename... ArgumentT>
return addPass<PBQPAllocatorPass>(Pipeline, registerAllocationInfo());
}
-[[nodiscard]] Expected<void> addCompleteRegistry(MachinePassManager &Pipeline) {
- auto Added = addInitialPipeline(Pipeline);
+[[nodiscard]] Expected<void> 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 <typename PassT, typename... ArgumentT>
} // namespace
Expected<void> addCanonicalPipeline(MachinePassManager &Pipeline, const MachineFunction &Function) {
+ return addCanonicalPipeline(Pipeline, Function, RISCVSubtargetFeatures::rv64gc());
+}
+
+Expected<void> addCanonicalPipeline(MachinePassManager &Pipeline, const MachineFunction &Function,
+ RISCVSubtargetFeatures Features) {
if (!Function.properties().has(MachineFunctionProperty::Legalized)) {
- auto Added = addPass<LegalizerPass>(Pipeline, legalizerInfo());
+ auto Added = addPass<LegalizerPass>(Pipeline, createLegalizerInfo(Features));
if (!Added)
return std::unexpected(Added.error());
}
@@ -112,10 +119,15 @@ Expected<void> addCanonicalPipeline(MachinePassManager &Pipeline, const MachineF
}
Expected<std::unique_ptr<MachineFunctionPass>> createCanonicalPipelinePass(std::string_view Name) {
+ return createCanonicalPipelinePass(Name, RISCVSubtargetFeatures::rv64gc());
+}
+
+Expected<std::unique_ptr<MachineFunctionPass>>
+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 <array>
+#include <cstddef>
+#include <string_view>
+
+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>(std::uint16_t{1} << static_cast<std::uint8_t>(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>
+RISCVSubtargetFeatures::fromFeatureList(std::span<const std::string> 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<std::uint16_t>(~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<MachinePassManager::PassList>
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<FunctionRunResult>
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<void> 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<PipelineRunResult> 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<PipelineRunResult> 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<PipelineRunResult> 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();
diff --git a/backend/unittests/Tooling/PipelineDriverTest.cpp b/backend/unittests/Tooling/PipelineDriverTest.cpp
index b9180ce..5815712 100644
--- a/backend/unittests/Tooling/PipelineDriverTest.cpp
+++ b/backend/unittests/Tooling/PipelineDriverTest.cpp
@@ -102,6 +102,25 @@ int main() {
if (!Source)
return tooling_test::fail(Source.error().message());
+ rcc::tooling::BackendInvocation AtomicOnlyInvocation = Invocation;
+ AtomicOnlyInvocation.Features = {"+a", "+zacas", "+zabha"};
+ AtomicOnlyInvocation.PassPipeline = {"Legalizer"};
+ auto CanonicalAtomicOnly = AtomicOnlyInvocation.canonicalizeAndVerify();
+ if (!CanonicalAtomicOnly)
+ return tooling_test::fail(CanonicalAtomicOnly.error().message());
+ auto AtomicOnlyRun = runInput("atomic-only.mir", *Source, *Target, AtomicOnlyInvocation,
+ rcc::tooling::BackendOutputKind::LegalizedMIR);
+ if (!AtomicOnlyRun)
+ return tooling_test::fail(AtomicOnlyRun.error().message());
+
+ rcc::tooling::BackendInvocation UnsupportedFeatureInvocation = Invocation;
+ UnsupportedFeatureInvocation.Features = {"+v"};
+ auto CanonicalUnsupported = UnsupportedFeatureInvocation.canonicalizeAndVerify();
+ if (!CanonicalUnsupported)
+ return tooling_test::fail(CanonicalUnsupported.error().message());
+ if (rcc::tooling::verifyRV64GCInvocation(UnsupportedFeatureInvocation))
+ return tooling_test::fail("RV64 backend invocation must reject unsupported target features");
+
constexpr std::array StageCases{
std::pair{rcc::tooling::BackendOutputKind::PreLegalizerMIR,
rcc::tooling::BackendStage::PreLegalizerMIR},