aboutsummaryrefslogtreecommitdiff
path: root/backend/lib
diff options
context:
space:
mode:
authorRichard-Qin-X <richard.qin.001@gmail.com>2026-08-12 19:45:09 +0800
committerRichard-Qin-X <richard.qin.001@gmail.com>2026-08-12 19:45:09 +0800
commit09e8ef3e0dae29be7aa21a6e22a118a3f545945f (patch)
treecfc5a7148aca9e768df16743c2c637e8c00a9d44 /backend/lib
parent1775d50ba02dab033c639f5c8fedd46cb6613bef (diff)
downloadrcc-project-09e8ef3e0dae29be7aa21a6e22a118a3f545945f.tar.gz
rcc-project-09e8ef3e0dae29be7aa21a6e22a118a3f545945f.tar.xz
[Tooling] Complete the LLVM MIR command-line contract
Diffstat (limited to 'backend/lib')
-rw-r--r--backend/lib/CodeGen/CMakeLists.txt4
-rw-r--r--backend/lib/CodeGen/CommandFlags.cpp233
-rw-r--r--backend/lib/CodeGen/MachinePseudoExpansion.cpp2
-rw-r--r--backend/lib/CodeGen/MachineSSAExit.cpp4
-rw-r--r--backend/lib/CodeGen/PostRAScheduler.cpp2
-rw-r--r--backend/lib/CodeGen/PrologueEpilogueInsertion.cpp2
-rw-r--r--backend/lib/Tooling/BackendDriver.cpp44
-rw-r--r--backend/lib/Tooling/BackendInvocation.cpp74
-rw-r--r--backend/lib/Tooling/MIRFuzzer.cpp18
-rw-r--r--backend/lib/Tooling/PipelineDriver.cpp105
10 files changed, 359 insertions, 129 deletions
diff --git a/backend/lib/CodeGen/CMakeLists.txt b/backend/lib/CodeGen/CMakeLists.txt
index d25d164..e584131 100644
--- a/backend/lib/CodeGen/CMakeLists.txt
+++ b/backend/lib/CodeGen/CMakeLists.txt
@@ -3,6 +3,7 @@
set(
RCCBackendCodeGenSources
CompilationUnit.cpp
+ CommandFlags.cpp
GlobalObject.cpp
GenericOpcodes.cpp
GenericConstant.cpp
@@ -83,6 +84,7 @@ set(
set(
RCCBackendCodeGenHeaders
"${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/CompilationUnit.h"
+ "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/CommandFlags.h"
"${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/GlobalObject.h"
"${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/GenericOpcodes.h"
"${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/GenericConstant.h"
@@ -204,6 +206,7 @@ install(
rcc_backend_register_format_files(
"${CMAKE_CURRENT_SOURCE_DIR}/CompilationUnit.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/CommandFlags.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/GlobalObject.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/GenericOpcodes.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/GenericConstant.cpp"
@@ -285,6 +288,7 @@ rcc_backend_register_format_files(
)
rcc_backend_register_tidy_files(
"${CMAKE_CURRENT_SOURCE_DIR}/CompilationUnit.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/CommandFlags.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/GlobalObject.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/GenericOpcodes.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/GenericConstant.cpp"
diff --git a/backend/lib/CodeGen/CommandFlags.cpp b/backend/lib/CodeGen/CommandFlags.cpp
new file mode 100644
index 0000000..dfbaadc
--- /dev/null
+++ b/backend/lib/CodeGen/CommandFlags.cpp
@@ -0,0 +1,233 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "rcc/CodeGen/CommandFlags.h"
+
+#include <optional>
+#include <string>
+#include <utility>
+
+namespace rcc {
+namespace {
+
+[[nodiscard]] Expected<std::optional<std::string_view>>
+takeValue(std::span<const std::string_view> Arguments, std::size_t &Index, std::string_view Name) {
+ const std::string_view Argument = Arguments[Index];
+ const std::string LongName =
+ Name.starts_with('-') && !Name.starts_with("--") ? "-" + std::string(Name) : std::string();
+ if (Argument == Name || (!LongName.empty() && Argument == LongName)) {
+ if (Index + 1U >= Arguments.size()) {
+ return makeUnexpected(ErrorCode::InvalidInvocation,
+ "missing value for command-line option '" + std::string(Name) + "'");
+ }
+ return Arguments[++Index];
+ }
+ std::string Prefix(Name);
+ Prefix.push_back('=');
+ if (Argument.starts_with(Prefix))
+ return Argument.substr(Prefix.size());
+ if (!LongName.empty()) {
+ std::string LongPrefix(LongName);
+ LongPrefix.push_back('=');
+ if (Argument.starts_with(LongPrefix))
+ return Argument.substr(LongPrefix.size());
+ }
+ return std::nullopt;
+}
+
+template <typename ValueT, typename ParserT>
+[[nodiscard]] Expected<bool> parseValueFlag(std::span<const std::string_view> Arguments,
+ std::size_t &Index, std::string_view Name,
+ std::optional<ValueT> &Destination, ParserT Parser) {
+ auto Value = takeValue(Arguments, Index, Name);
+ if (!Value)
+ return std::unexpected(Value.error());
+ if (!*Value)
+ return false;
+ auto Parsed = Parser(**Value);
+ if (!Parsed)
+ return std::unexpected(Parsed.error());
+ Destination = std::move(*Parsed);
+ return true;
+}
+
+} // namespace
+
+std::string_view spelling(RelocationModel Value) noexcept {
+ switch (Value) {
+ case RelocationModel::Static:
+ return "static";
+ case RelocationModel::PositionIndependent:
+ return "pic";
+ }
+ return "invalid";
+}
+
+std::string_view spelling(CodeModel Value) noexcept {
+ switch (Value) {
+ case CodeModel::Small:
+ return "small";
+ case CodeModel::Medium:
+ return "medium";
+ case CodeModel::Large:
+ return "large";
+ }
+ return "invalid";
+}
+
+std::string_view spelling(OptimizationLevel Value) noexcept {
+ switch (Value) {
+ case OptimizationLevel::O0:
+ return "O0";
+ case OptimizationLevel::O1:
+ return "O1";
+ case OptimizationLevel::O2:
+ return "O2";
+ case OptimizationLevel::O3:
+ return "O3";
+ case OptimizationLevel::Os:
+ return "Os";
+ case OptimizationLevel::Oz:
+ return "Oz";
+ }
+ return "invalid";
+}
+
+std::string_view spelling(CodeGenFileType Value) noexcept {
+ switch (Value) {
+ case CodeGenFileType::AssemblyFile:
+ return "asm";
+ case CodeGenFileType::ObjectFile:
+ return "obj";
+ case CodeGenFileType::Null:
+ return "null";
+ }
+ return "invalid";
+}
+
+Expected<RelocationModel> parseRelocationModel(std::string_view Value) {
+ if (Value == "static")
+ return RelocationModel::Static;
+ if (Value == "pic")
+ return RelocationModel::PositionIndependent;
+ return makeUnexpected(ErrorCode::InvalidInvocation, "unknown relocation model");
+}
+
+Expected<CodeModel> parseCodeModel(std::string_view Value) {
+ if (Value == "small")
+ return CodeModel::Small;
+ if (Value == "medium")
+ return CodeModel::Medium;
+ if (Value == "large")
+ return CodeModel::Large;
+ return makeUnexpected(ErrorCode::InvalidInvocation, "unknown code model");
+}
+
+Expected<OptimizationLevel> parseOptimizationLevel(std::string_view Value) {
+ if (Value == "O0")
+ return OptimizationLevel::O0;
+ if (Value == "O1")
+ return OptimizationLevel::O1;
+ if (Value == "O2")
+ return OptimizationLevel::O2;
+ if (Value == "O3")
+ return OptimizationLevel::O3;
+ if (Value == "Os")
+ return OptimizationLevel::Os;
+ if (Value == "Oz")
+ return OptimizationLevel::Oz;
+ return makeUnexpected(ErrorCode::InvalidInvocation, "unknown optimization level");
+}
+
+Expected<CodeGenFileType> parseCodeGenFileType(std::string_view Value) {
+ if (Value == "asm")
+ return CodeGenFileType::AssemblyFile;
+ if (Value == "obj")
+ return CodeGenFileType::ObjectFile;
+ if (Value == "null")
+ return CodeGenFileType::Null;
+ return makeUnexpected(ErrorCode::InvalidInvocation, "unknown file type");
+}
+
+Expected<bool> parseCodeGenCommandFlag(std::span<const std::string_view> Arguments,
+ std::size_t &Index, CodeGenCommandFlags &Flags) {
+ const std::string_view Argument = Arguments[Index];
+ const std::string_view Optimization = Argument.starts_with("--") ? Argument.substr(2)
+ : Argument.starts_with('-') ? Argument.substr(1)
+ : std::string_view{};
+ if ((Argument.starts_with('-') && !Argument.starts_with("---")) &&
+ (Optimization == "O0" || Optimization == "O1" || Optimization == "O2" ||
+ Optimization == "O3" || Optimization == "Os" || Optimization == "Oz")) {
+ auto Level = parseOptimizationLevel(Optimization);
+ if (!Level)
+ return std::unexpected(Level.error());
+ Flags.Optimization = *Level;
+ return true;
+ }
+
+ const auto ParseString = [&](std::string_view Name,
+ std::optional<std::string> &Destination) -> Expected<bool> {
+ auto Value = takeValue(Arguments, Index, Name);
+ if (!Value)
+ return std::unexpected(Value.error());
+ if (!*Value)
+ return false;
+ Destination = std::string(**Value);
+ return true;
+ };
+ for (const auto &[Name, Destination] :
+ {std::pair{"-mtriple", &Flags.Triple}, std::pair{"-mcpu", &Flags.CPU},
+ std::pair{"-mtune", &Flags.TuneCPU}, std::pair{"-target-abi", &Flags.ABI}}) {
+ auto Parsed = ParseString(Name, *Destination);
+ if (!Parsed)
+ return std::unexpected(Parsed.error());
+ if (*Parsed)
+ return true;
+ }
+
+ auto Features = takeValue(Arguments, Index, "-mattr");
+ if (!Features)
+ return std::unexpected(Features.error());
+ if (*Features) {
+ std::size_t Begin = 0;
+ while (Begin <= (**Features).size()) {
+ const std::size_t Comma = (**Features).find(',', Begin);
+ const std::size_t End = Comma == std::string_view::npos ? (**Features).size() : Comma;
+ if (End == Begin) {
+ return makeUnexpected(ErrorCode::InvalidInvocation,
+ "target feature list contains an empty element");
+ }
+ Flags.FeatureOverrides.emplace_back((**Features).substr(Begin, End - Begin));
+ if (Comma == std::string_view::npos)
+ break;
+ Begin = Comma + 1U;
+ }
+ return true;
+ }
+
+ auto Relocation =
+ parseValueFlag(Arguments, Index, "-relocation-model", Flags.Relocation, parseRelocationModel);
+ if (!Relocation)
+ return std::unexpected(Relocation.error());
+ if (*Relocation)
+ return true;
+ auto Code = parseValueFlag(Arguments, Index, "-code-model", Flags.Code, parseCodeModel);
+ if (!Code)
+ return std::unexpected(Code.error());
+ if (*Code)
+ return true;
+
+ auto FileType = takeValue(Arguments, Index, "-filetype");
+ if (!FileType)
+ return std::unexpected(FileType.error());
+ if (*FileType) {
+ auto Parsed = parseCodeGenFileType(**FileType);
+ if (!Parsed)
+ return std::unexpected(Parsed.error());
+ Flags.FileType = *Parsed;
+ Flags.FileTypeWasSpecified = true;
+ return true;
+ }
+ return false;
+}
+
+} // namespace rcc
diff --git a/backend/lib/CodeGen/MachinePseudoExpansion.cpp b/backend/lib/CodeGen/MachinePseudoExpansion.cpp
index 924c515..022c63e 100644
--- a/backend/lib/CodeGen/MachinePseudoExpansion.cpp
+++ b/backend/lib/CodeGen/MachinePseudoExpansion.cpp
@@ -154,7 +154,7 @@ using BlockSizeSnapshot = std::vector<std::pair<MachineBlockStableID, std::uint6
}();
static const MachinePassContract PostRA = [] {
MachinePassContract Value{
- .Name = "expand-post-ra-pseudos",
+ .Name = "postrapseudos",
.Requires = {MachineFunctionProperty::Selected, MachineFunctionProperty::NoPHIs,
MachineFunctionProperty::NoVRegs, MachineFunctionProperty::TracksLiveness,
MachineFunctionProperty::FrameFinalized,
diff --git a/backend/lib/CodeGen/MachineSSAExit.cpp b/backend/lib/CodeGen/MachineSSAExit.cpp
index f2b0102..76963ea 100644
--- a/backend/lib/CodeGen/MachineSSAExit.cpp
+++ b/backend/lib/CodeGen/MachineSSAExit.cpp
@@ -776,7 +776,7 @@ placePhiCopiesOnEdge(MachineFunction &Function, const TargetDescription &Target,
const MachinePassContract &PHIEliminationPass::contract() const noexcept {
static const MachinePassContract Contract{
- .Name = "PHIElimination",
+ .Name = "phi-node-elimination",
.Requires = {MachineFunctionProperty::IsSSA, MachineFunctionProperty::Selected,
MachineFunctionProperty::RegBankSelected},
.Sets = {MachineFunctionProperty::NoPHIs},
@@ -912,7 +912,7 @@ Expected<MachinePassResult> PHIEliminationPass::runImplementation(MachineFunctio
const MachinePassContract &TwoAddressRewritePass::contract() const noexcept {
static const MachinePassContract Contract{
- .Name = "TwoAddressRewrite",
+ .Name = "twoaddressinstruction",
.Requires = {MachineFunctionProperty::Selected, MachineFunctionProperty::NoPHIs},
.Sets = {MachineFunctionProperty::TiedOpsRewritten},
.Clears = {MachineFunctionProperty::IsSSA},
diff --git a/backend/lib/CodeGen/PostRAScheduler.cpp b/backend/lib/CodeGen/PostRAScheduler.cpp
index 6a91239..38fd582 100644
--- a/backend/lib/CodeGen/PostRAScheduler.cpp
+++ b/backend/lib/CodeGen/PostRAScheduler.cpp
@@ -31,7 +31,7 @@ namespace {
[[nodiscard]] const MachinePassContract &postRASchedulerContract() {
static const MachinePassContract Contract = [] {
MachinePassContract Value{
- .Name = "post-ra-machine-scheduler",
+ .Name = "postmisched",
.Requires = {MachineFunctionProperty::Selected, MachineFunctionProperty::NoPHIs,
MachineFunctionProperty::TiedOpsRewritten, MachineFunctionProperty::NoVRegs,
MachineFunctionProperty::TracksLiveness},
diff --git a/backend/lib/CodeGen/PrologueEpilogueInsertion.cpp b/backend/lib/CodeGen/PrologueEpilogueInsertion.cpp
index acbcbcf..36315f7 100644
--- a/backend/lib/CodeGen/PrologueEpilogueInsertion.cpp
+++ b/backend/lib/CodeGen/PrologueEpilogueInsertion.cpp
@@ -31,7 +31,7 @@ namespace {
const MachinePassContract &PrologueEpilogueInsertionPass::contract() const noexcept {
static const MachinePassContract Contract = [] {
MachinePassContract Value{
- .Name = "PrologueEpilogueInsertion",
+ .Name = "prologepilog",
.Requires = {MachineFunctionProperty::Selected, MachineFunctionProperty::NoPHIs,
MachineFunctionProperty::TiedOpsRewritten, MachineFunctionProperty::NoVRegs},
.Sets = {MachineFunctionProperty::FrameFinalized, MachineFunctionProperty::NoFrameIndices},
diff --git a/backend/lib/Tooling/BackendDriver.cpp b/backend/lib/Tooling/BackendDriver.cpp
index fff58c5..c726fd3 100644
--- a/backend/lib/Tooling/BackendDriver.cpp
+++ b/backend/lib/Tooling/BackendDriver.cpp
@@ -164,30 +164,28 @@ std::string_view spelling(BackendOutputKind Output) noexcept {
return "invalid";
}
-Expected<BackendOutputKind> parseBackendOutputKind(std::string_view Value) {
- if (Value == "prelegalizer-mir")
- return BackendOutputKind::PreLegalizerMIR;
- if (Value == "legalized-mir")
- return BackendOutputKind::LegalizedMIR;
- if (Value == "regbank-mir")
- return BackendOutputKind::RegisterBankMIR;
- if (Value == "selected-mir")
- return BackendOutputKind::SelectedMIR;
- if (Value == "post-ssa-mir")
- return BackendOutputKind::PostSSAMIR;
- if (Value == "post-ra-mir")
- return BackendOutputKind::PostRegisterAllocationMIR;
- if (Value == "final-mir")
- return BackendOutputKind::FinalMIR;
- if (Value == "assembly" || Value == "asm")
- return BackendOutputKind::Assembly;
- if (Value == "object" || Value == "obj")
- return BackendOutputKind::RelocatableObject;
- return makeUnexpected(ErrorCode::InvalidInvocation, "unknown backend output kind");
-}
-
ArtifactKind manifestArtifactKind(BackendOutputKind Output) noexcept {
- return static_cast<ArtifactKind>(static_cast<std::uint8_t>(Output));
+ switch (Output) {
+ case BackendOutputKind::PreLegalizerMIR:
+ return ArtifactKind::PreLegalizerMIR;
+ case BackendOutputKind::LegalizedMIR:
+ return ArtifactKind::LegalizedMIR;
+ case BackendOutputKind::RegisterBankMIR:
+ return ArtifactKind::RegisterBankMIR;
+ case BackendOutputKind::SelectedMIR:
+ return ArtifactKind::SelectedMIR;
+ case BackendOutputKind::PostSSAMIR:
+ return ArtifactKind::PostSSAMIR;
+ case BackendOutputKind::PostRegisterAllocationMIR:
+ return ArtifactKind::PostRegisterAllocationMIR;
+ case BackendOutputKind::FinalMIR:
+ return ArtifactKind::FinalMIR;
+ case BackendOutputKind::Assembly:
+ return ArtifactKind::Assembly;
+ case BackendOutputKind::RelocatableObject:
+ return ArtifactKind::RelocatableObject;
+ }
+ return ArtifactKind::FinalMIR;
}
Expected<ArtifactKind> cacheArtifactKind(BackendOutputKind Output) {
diff --git a/backend/lib/Tooling/BackendInvocation.cpp b/backend/lib/Tooling/BackendInvocation.cpp
index 677cd2d..232d34f 100644
--- a/backend/lib/Tooling/BackendInvocation.cpp
+++ b/backend/lib/Tooling/BackendInvocation.cpp
@@ -265,46 +265,6 @@ void appendMIRTextInfo(std::string &Output, const TargetMIRTextInfo &MIRText) {
} // namespace
-std::string_view spelling(RelocationModel Value) noexcept {
- switch (Value) {
- case RelocationModel::Static:
- return "static";
- case RelocationModel::PositionIndependent:
- return "pic";
- }
- return "invalid";
-}
-
-std::string_view spelling(CodeModel Value) noexcept {
- switch (Value) {
- case CodeModel::Small:
- return "small";
- case CodeModel::Medium:
- return "medium";
- case CodeModel::Large:
- return "large";
- }
- return "invalid";
-}
-
-std::string_view spelling(OptimizationLevel Value) noexcept {
- switch (Value) {
- case OptimizationLevel::O0:
- return "O0";
- case OptimizationLevel::O1:
- return "O1";
- case OptimizationLevel::O2:
- return "O2";
- case OptimizationLevel::O3:
- return "O3";
- case OptimizationLevel::Os:
- return "Os";
- case OptimizationLevel::Oz:
- return "Oz";
- }
- return "invalid";
-}
-
std::string_view spelling(LayoutOwnership Value) noexcept {
switch (Value) {
case LayoutOwnership::NativeObject:
@@ -317,40 +277,6 @@ std::string_view spelling(LayoutOwnership Value) noexcept {
return "invalid";
}
-Expected<RelocationModel> parseRelocationModel(std::string_view Value) {
- if (Value == "static")
- return RelocationModel::Static;
- if (Value == "pic")
- return RelocationModel::PositionIndependent;
- return makeUnexpected(ErrorCode::ManifestError, "unknown relocation model");
-}
-
-Expected<CodeModel> parseCodeModel(std::string_view Value) {
- if (Value == "small")
- return CodeModel::Small;
- if (Value == "medium")
- return CodeModel::Medium;
- if (Value == "large")
- return CodeModel::Large;
- return makeUnexpected(ErrorCode::ManifestError, "unknown code model");
-}
-
-Expected<OptimizationLevel> parseOptimizationLevel(std::string_view Value) {
- if (Value == "O0")
- return OptimizationLevel::O0;
- if (Value == "O1")
- return OptimizationLevel::O1;
- if (Value == "O2")
- return OptimizationLevel::O2;
- if (Value == "O3")
- return OptimizationLevel::O3;
- if (Value == "Os")
- return OptimizationLevel::Os;
- if (Value == "Oz")
- return OptimizationLevel::Oz;
- return makeUnexpected(ErrorCode::ManifestError, "unknown optimization level");
-}
-
Expected<LayoutOwnership> parseLayoutOwnership(std::string_view Value) {
if (Value == "native-object")
return LayoutOwnership::NativeObject;
diff --git a/backend/lib/Tooling/MIRFuzzer.cpp b/backend/lib/Tooling/MIRFuzzer.cpp
index ed5b2fa..653b9fd 100644
--- a/backend/lib/Tooling/MIRFuzzer.cpp
+++ b/backend/lib/Tooling/MIRFuzzer.cpp
@@ -225,7 +225,23 @@ private:
}
[[nodiscard]] BackendOutputKind outputForStage(BackendStage Stage) noexcept {
- return static_cast<BackendOutputKind>(static_cast<std::uint8_t>(Stage));
+ switch (Stage) {
+ case BackendStage::PreLegalizerMIR:
+ return BackendOutputKind::PreLegalizerMIR;
+ case BackendStage::LegalizedMIR:
+ return BackendOutputKind::LegalizedMIR;
+ case BackendStage::RegisterBankMIR:
+ return BackendOutputKind::RegisterBankMIR;
+ case BackendStage::SelectedMIR:
+ return BackendOutputKind::SelectedMIR;
+ case BackendStage::PostSSAMIR:
+ return BackendOutputKind::PostSSAMIR;
+ case BackendStage::PostRegisterAllocationMIR:
+ return BackendOutputKind::PostRegisterAllocationMIR;
+ case BackendStage::FinalMIR:
+ return BackendOutputKind::FinalMIR;
+ }
+ return BackendOutputKind::FinalMIR;
}
[[nodiscard]] Expected<bool> propertyCase(std::string_view Canonical, DeterministicRandom &Random,
diff --git a/backend/lib/Tooling/PipelineDriver.cpp b/backend/lib/Tooling/PipelineDriver.cpp
index 620367f..3d137d8 100644
--- a/backend/lib/Tooling/PipelineDriver.cpp
+++ b/backend/lib/Tooling/PipelineDriver.cpp
@@ -11,6 +11,7 @@
#include <algorithm>
#include <atomic>
+#include <charconv>
#include <cstddef>
#include <cstdint>
#include <memory>
@@ -38,19 +39,27 @@ struct FunctionRunResult final {
}
[[nodiscard]] bool selectorMatches(std::string_view Selector, std::string_view Pass,
- std::uint32_t Occurrence) {
- if (Selector == "all" || Selector == Pass)
+ std::uint32_t Occurrence, bool MatchAllOccurrences) {
+ if (Selector == "all")
return true;
- std::string Qualified(Pass);
- Qualified.push_back('#');
- Qualified.append(std::to_string(Occurrence));
- return Selector == Qualified;
+ const std::size_t Comma = Selector.find(',');
+ const std::string_view Name =
+ Comma == std::string_view::npos ? Selector : Selector.substr(0, Comma);
+ if (Name != Pass)
+ return false;
+ if (Comma == std::string_view::npos)
+ return MatchAllOccurrences || Occurrence == 1U;
+ std::uint32_t Instance = 0;
+ const std::string_view Text = Selector.substr(Comma + 1U);
+ const auto Parsed = std::from_chars(Text.data(), Text.data() + Text.size(), Instance);
+ return Parsed.ec == std::errc{} && Parsed.ptr == Text.data() + Text.size() &&
+ Occurrence == Instance + 1U;
}
[[nodiscard]] bool anySelectorMatches(std::span<const std::string> Selectors, std::string_view Pass,
std::uint32_t Occurrence) {
return std::ranges::any_of(Selectors, [Pass, Occurrence](const std::string &Selector) {
- return selectorMatches(Selector, Pass, Occurrence);
+ return selectorMatches(Selector, Pass, Occurrence, true);
});
}
@@ -61,7 +70,7 @@ struct FunctionRunResult final {
return BackendStage::RegisterBankMIR;
if (Pass == "instruction-select")
return BackendStage::SelectedMIR;
- if (Pass == "TwoAddressRewrite")
+ if (Pass == "twoaddressinstruction")
return BackendStage::PostSSAMIR;
if (Pass == "PBQPAllocator")
return BackendStage::PostRegisterAllocationMIR;
@@ -84,8 +93,6 @@ createPasses(const MachineFunction &Function, const BackendInvocation &Invocatio
"the default pipeline marker cannot be mixed with named passes");
}
for (const std::string &Name : Invocation.PassPipeline) {
- if (Name == "none")
- continue;
auto Pass = riscv::createCanonicalPipelinePass(Name, Features);
if (!Pass)
return std::unexpected(Pass.error());
@@ -97,47 +104,67 @@ createPasses(const MachineFunction &Function, const BackendInvocation &Invocatio
return std::move(Pipeline).takePasses();
}
-[[nodiscard]] Expected<void> validateSelector(std::string_view Selector) {
- if (Selector == "all")
+[[nodiscard]] Expected<void> validateSelector(std::string_view Selector, bool AllowAll) {
+ if (Selector == "all" && AllowAll)
return {};
- const std::size_t Hash = Selector.rfind('#');
+ const std::size_t Comma = Selector.find(',');
const std::string_view Name =
- Hash == std::string_view::npos ? Selector : Selector.substr(0, Hash);
+ Comma == std::string_view::npos ? Selector : Selector.substr(0, Comma);
if (Name.empty())
return makeUnexpected(ErrorCode::InvalidInvocation, "empty pipeline pass selector");
auto Pass = riscv::createCanonicalPipelinePass(Name);
if (!Pass)
return std::unexpected(Pass.error());
- if (Hash != std::string_view::npos) {
- const std::string_view Occurrence = Selector.substr(Hash + 1U);
- if (Occurrence.empty() || Occurrence == "0" ||
- !std::ranges::all_of(Occurrence,
- [](char Character) { return Character >= '0' && Character <= '9'; })) {
+ if (Comma != std::string_view::npos) {
+ const std::string_view InstanceText = Selector.substr(Comma + 1U);
+ std::uint32_t Instance = 0;
+ const auto Parsed =
+ std::from_chars(InstanceText.data(), InstanceText.data() + InstanceText.size(), Instance);
+ if (InstanceText.empty() || Parsed.ec != std::errc{} ||
+ Parsed.ptr != InstanceText.data() + InstanceText.size()) {
return makeUnexpected(ErrorCode::InvalidInvocation,
- "pipeline pass occurrence must be a positive integer");
+ "pipeline pass instance must be a nonnegative integer");
}
}
return {};
}
[[nodiscard]] Expected<void> validateControls(const PipelineControls &Controls) {
+ if (Controls.StartBefore && Controls.StartAfter) {
+ return makeUnexpected(ErrorCode::InvalidInvocation,
+ "-start-before and -start-after cannot be combined");
+ }
+ if (Controls.StopBefore && Controls.StopAfter) {
+ return makeUnexpected(ErrorCode::InvalidInvocation,
+ "-stop-before and -stop-after cannot be combined");
+ }
+ if (Controls.StartBefore) {
+ auto Result = validateSelector(*Controls.StartBefore, false);
+ if (!Result)
+ return Result;
+ }
+ if (Controls.StartAfter) {
+ auto Result = validateSelector(*Controls.StartAfter, false);
+ if (!Result)
+ return Result;
+ }
if (Controls.StopBefore) {
- auto Result = validateSelector(*Controls.StopBefore);
+ auto Result = validateSelector(*Controls.StopBefore, false);
if (!Result)
return Result;
}
if (Controls.StopAfter) {
- auto Result = validateSelector(*Controls.StopAfter);
+ auto Result = validateSelector(*Controls.StopAfter, false);
if (!Result)
return Result;
}
for (const std::string &Selector : Controls.PrintBefore) {
- auto Result = validateSelector(Selector);
+ auto Result = validateSelector(Selector, true);
if (!Result)
return Result;
}
for (const std::string &Selector : Controls.PrintAfter) {
- auto Result = validateSelector(Selector);
+ auto Result = validateSelector(Selector, true);
if (!Result)
return Result;
}
@@ -198,13 +225,26 @@ runFunction(MachineFunction &Function, mir::MIRDocument &Document, const TargetD
}
std::unordered_map<std::string, std::uint32_t> Occurrences;
+ bool Started = !Controls.StartBefore && !Controls.StartAfter;
+ bool StopMatched = !Controls.StopBefore && !Controls.StopAfter;
for (std::unique_ptr<MachineFunctionPass> &Pass : *Passes) {
if (Pass == nullptr)
return makeUnexpected(ErrorCode::InternalError, "pipeline contains a null pass");
const std::string Name = Pass->contract().Name;
const std::uint32_t Occurrence = ++Occurrences[Name];
- if (Controls.StopBefore && selectorMatches(*Controls.StopBefore, Name, Occurrence))
+ if (!Started) {
+ if (Controls.StartBefore && selectorMatches(*Controls.StartBefore, Name, Occurrence, false)) {
+ Started = true;
+ } else {
+ if (Controls.StartAfter && selectorMatches(*Controls.StartAfter, Name, Occurrence, false))
+ Started = true;
+ continue;
+ }
+ }
+ if (Controls.StopBefore && selectorMatches(*Controls.StopBefore, Name, Occurrence, false)) {
+ StopMatched = true;
break;
+ }
if (AllowSnapshots && anySelectorMatches(Controls.PrintBefore, Name, Occurrence)) {
auto Captured =
captureSnapshot(Result, Document, Target, Invocation, SnapshotPhase::BeforePass,
@@ -241,12 +281,25 @@ runFunction(MachineFunction &Function, mir::MIRDocument &Document, const TargetD
if (!Captured)
return std::unexpected(Captured.error());
}
- if (Controls.StopAfter && selectorMatches(*Controls.StopAfter, Name, Occurrence))
+ if (Controls.StopAfter && selectorMatches(*Controls.StopAfter, Name, Occurrence, false)) {
+ StopMatched = true;
break;
+ }
if (Controls.StopAtStage && stageOrdinal(CurrentStage) >= stageOrdinal(*Controls.StopAtStage)) {
break;
}
}
+ if (!Started) {
+ const std::string &Selector =
+ Controls.StartBefore ? *Controls.StartBefore : *Controls.StartAfter;
+ return makeUnexpected(ErrorCode::InvalidInvocation,
+ "pipeline start selector did not match: '" + Selector + "'");
+ }
+ if (!StopMatched) {
+ const std::string &Selector = Controls.StopBefore ? *Controls.StopBefore : *Controls.StopAfter;
+ return makeUnexpected(ErrorCode::InvalidInvocation,
+ "pipeline stop selector did not match: '" + Selector + "'");
+ }
return Result;
}