diff options
| author | Richard-Qin-X <richard.qin.001@gmail.com> | 2026-08-12 19:45:09 +0800 |
|---|---|---|
| committer | Richard-Qin-X <richard.qin.001@gmail.com> | 2026-08-12 19:45:09 +0800 |
| commit | 09e8ef3e0dae29be7aa21a6e22a118a3f545945f (patch) | |
| tree | cfc5a7148aca9e768df16743c2c637e8c00a9d44 /backend/lib | |
| parent | 1775d50ba02dab033c639f5c8fedd46cb6613bef (diff) | |
| download | rcc-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.txt | 4 | ||||
| -rw-r--r-- | backend/lib/CodeGen/CommandFlags.cpp | 233 | ||||
| -rw-r--r-- | backend/lib/CodeGen/MachinePseudoExpansion.cpp | 2 | ||||
| -rw-r--r-- | backend/lib/CodeGen/MachineSSAExit.cpp | 4 | ||||
| -rw-r--r-- | backend/lib/CodeGen/PostRAScheduler.cpp | 2 | ||||
| -rw-r--r-- | backend/lib/CodeGen/PrologueEpilogueInsertion.cpp | 2 | ||||
| -rw-r--r-- | backend/lib/Tooling/BackendDriver.cpp | 44 | ||||
| -rw-r--r-- | backend/lib/Tooling/BackendInvocation.cpp | 74 | ||||
| -rw-r--r-- | backend/lib/Tooling/MIRFuzzer.cpp | 18 | ||||
| -rw-r--r-- | backend/lib/Tooling/PipelineDriver.cpp | 105 |
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; } |
