diff options
Diffstat (limited to 'backend/lib/TableGen')
| -rw-r--r-- | backend/lib/TableGen/CMakeLists.txt | 23 | ||||
| -rw-r--r-- | backend/lib/TableGen/Parser.cpp | 2333 | ||||
| -rw-r--r-- | backend/lib/TableGen/Record.cpp | 92 |
3 files changed, 2448 insertions, 0 deletions
diff --git a/backend/lib/TableGen/CMakeLists.txt b/backend/lib/TableGen/CMakeLists.txt new file mode 100644 index 0000000..4033e67 --- /dev/null +++ b/backend/lib/TableGen/CMakeLists.txt @@ -0,0 +1,23 @@ +# SPDX-License-Identifier: GPL-3.0-or-later + +set(RCCBackendTableGenSources Parser.cpp Record.cpp) +list(TRANSFORM RCCBackendTableGenSources PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/") +set( + RCCBackendTableGenHeaders + "${RCC_BACKEND_SOURCE_DIR}/include/rcc/TableGen/Parser.h" + "${RCC_BACKEND_SOURCE_DIR}/include/rcc/TableGen/Record.h" +) + +add_library(RCCBackendTableGen ${RCCBackendTableGenSources}) +add_library(RCCBackend::TableGen ALIAS RCCBackendTableGen) +rcc_backend_configure_target(RCCBackendTableGen) +target_include_directories( + RCCBackendTableGen + PUBLIC "$<BUILD_INTERFACE:${RCC_BACKEND_SOURCE_DIR}/include>" +) +target_link_libraries(RCCBackendTableGen PUBLIC RCCBackend::Support) + +rcc_backend_register_format_files( + ${RCCBackendTableGenSources} + ${RCCBackendTableGenHeaders} +) diff --git a/backend/lib/TableGen/Parser.cpp b/backend/lib/TableGen/Parser.cpp new file mode 100644 index 0000000..1044309 --- /dev/null +++ b/backend/lib/TableGen/Parser.cpp @@ -0,0 +1,2333 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "rcc/TableGen/Parser.h" + +#include "rcc/Support/YAML.h" + +#include <algorithm> +#include <array> +#include <charconv> +#include <cstdint> +#include <limits> +#include <optional> +#include <ranges> +#include <span> +#include <string> +#include <string_view> +#include <unordered_set> +#include <utility> +#include <vector> + +namespace rcc::targetgen { +namespace { + +class ASTParser final { +public: + explicit ASTParser(const yaml::Stream &Input) : Input(Input) {} + + [[nodiscard]] std::optional<DescriptionAST> parse() { + const std::span<const yaml::Document> Documents = Input.documents(); + if (Documents.size() != 1) { + fail(Input.source().range(0, Input.source().text().size()), + "target description must contain exactly one YAML document"); + return std::nullopt; + } + + const yaml::Node *Root = Input.node(Documents.front().Root); + if (!requireMapping(Root, "target description root")) + return std::nullopt; + if (!checkKeys(*Root, + {"schema", "target", "register_units", "registers", "register_banks", + "register_classes", "register_masks", "mir_operand_flags", "named_operands", + "immediate_predicates", "scheduling_classes", "encoding_categories", + "assembly_formats", "calling_conventions", "instructions", "selection_rules", + "combiner_rules", "macro_fusion_rules", "mc_pseudo_expansions"}, + "target description root")) { + return std::nullopt; + } + + const yaml::Node *SchemaNode = requiredNode(*Root, "schema", "target description root"); + if (SchemaNode == nullptr) + return std::nullopt; + const auto Schema = scalar(*SchemaNode, "target description schema"); + if (!Schema || *Schema != "rcc-target-description-v2") { + if (Schema) + fail(SchemaNode->range(), "unsupported target description schema"); + return std::nullopt; + } + + DescriptionAST Result; + const yaml::Node *TargetNode = requiredNode(*Root, "target", "target description root"); + if (TargetNode == nullptr || !parseTarget(*TargetNode, Result.Target)) + return std::nullopt; + const yaml::Node *UnitsNode = requiredNode(*Root, "register_units", "target description root"); + const yaml::Node *RegistersNode = requiredNode(*Root, "registers", "target description root"); + const yaml::Node *BanksNode = requiredNode(*Root, "register_banks", "target description root"); + const yaml::Node *ClassesNode = + requiredNode(*Root, "register_classes", "target description root"); + const yaml::Node *MasksNode = requiredNode(*Root, "register_masks", "target description root"); + const yaml::Node *MIROperandFlagsNode = optionalNode(*Root, "mir_operand_flags"); + const yaml::Node *NamedOperandsNode = + requiredNode(*Root, "named_operands", "target description root"); + const yaml::Node *ImmediatePredicatesNode = + requiredNode(*Root, "immediate_predicates", "target description root"); + const yaml::Node *SchedulingClassesNode = + requiredNode(*Root, "scheduling_classes", "target description root"); + const yaml::Node *EncodingCategoriesNode = + requiredNode(*Root, "encoding_categories", "target description root"); + const yaml::Node *AssemblyFormatsNode = + requiredNode(*Root, "assembly_formats", "target description root"); + const yaml::Node *CallingConventionsNode = + requiredNode(*Root, "calling_conventions", "target description root"); + const yaml::Node *InstructionsNode = + requiredNode(*Root, "instructions", "target description root"); + const yaml::Node *SelectionRulesNode = optionalNode(*Root, "selection_rules"); + const yaml::Node *CombinerRulesNode = optionalNode(*Root, "combiner_rules"); + const yaml::Node *MacroFusionRulesNode = optionalNode(*Root, "macro_fusion_rules"); + const yaml::Node *MCPseudoExpansionsNode = optionalNode(*Root, "mc_pseudo_expansions"); + if (UnitsNode == nullptr || RegistersNode == nullptr || BanksNode == nullptr || + ClassesNode == nullptr || MasksNode == nullptr || NamedOperandsNode == nullptr || + ImmediatePredicatesNode == nullptr || SchedulingClassesNode == nullptr || + EncodingCategoriesNode == nullptr || AssemblyFormatsNode == nullptr || + CallingConventionsNode == nullptr || InstructionsNode == nullptr || + !parseRegisterUnits(*UnitsNode, Result.RegisterUnits) || + !parseRegisters(*RegistersNode, Result.Registers) || + !parseRegisterBanks(*BanksNode, Result.RegisterBanks) || + !parseRegisterClasses(*ClassesNode, Result.RegisterClasses) || + !parseRegisterMasks(*MasksNode, Result.RegisterMasks) || + (MIROperandFlagsNode != nullptr && + !parseMIROperandFlags(*MIROperandFlagsNode, Result.MIROperandFlags)) || + !parseNamedOperands(*NamedOperandsNode, Result.NamedOperands) || + !parseImmediatePredicates(*ImmediatePredicatesNode, Result.ImmediatePredicates) || + !parseSchedulingClasses(*SchedulingClassesNode, Result.SchedulingClasses) || + !parseEncodingCategories(*EncodingCategoriesNode, Result.EncodingCategories) || + !parseAssemblyFormats(*AssemblyFormatsNode, Result.AssemblyFormats) || + !parseCallingConventions(*CallingConventionsNode, Result.CallingConventions) || + !parseInstructions(*InstructionsNode, Result.Instructions) || + (SelectionRulesNode != nullptr && + !parseSelectionRules(*SelectionRulesNode, Result.SelectionRules)) || + (CombinerRulesNode != nullptr && + !parseCombinerRules(*CombinerRulesNode, Result.CombinerRules)) || + (MacroFusionRulesNode != nullptr && + !parseMacroFusionRules(*MacroFusionRulesNode, Result.MacroFusionRules)) || + (MCPseudoExpansionsNode != nullptr && + !parseMCPseudoExpansions(*MCPseudoExpansionsNode, Result.MCPseudoExpansions))) { + return std::nullopt; + } + return Result; + } + + [[nodiscard]] std::vector<TextDiagnostic> takeDiagnostics() { + return std::move(Diagnostics); + } + +private: + void fail(TextRange Range, std::string Message) { + Diagnostics.push_back({ + .Severity = DiagnosticSeverity::Error, + .Category = "target-description", + .Message = std::move(Message), + .SourceName = std::string(Input.source().name()), + .Range = Range, + }); + } + + [[nodiscard]] bool requireMapping(const yaml::Node *Node, std::string_view Context) { + if (Node != nullptr && Node->kind() == yaml::NodeKind::Mapping) + return true; + fail(Node == nullptr ? Input.source().range(0, 0) : Node->range(), + std::string(Context) + " must be a mapping"); + return false; + } + + [[nodiscard]] bool requireSequence(const yaml::Node *Node, std::string_view Context) { + if (Node != nullptr && Node->kind() == yaml::NodeKind::Sequence) + return true; + fail(Node == nullptr ? Input.source().range(0, 0) : Node->range(), + std::string(Context) + " must be a sequence"); + return false; + } + + [[nodiscard]] bool checkKeys(const yaml::Node &Mapping, + std::initializer_list<std::string_view> Allowed, + std::string_view Context) { + for (const yaml::MappingEntry &Entry : Mapping.mapping()) { + const bool IsAllowed = + std::ranges::any_of(Allowed, [&Entry](std::string_view Key) { return Entry.Key == Key; }); + if (!IsAllowed) { + fail(Entry.KeyRange, std::string(Context) + " has unknown key '" + Entry.Key + "'"); + return false; + } + } + return true; + } + + [[nodiscard]] const yaml::Node *requiredNode(const yaml::Node &Mapping, std::string_view Key, + std::string_view Context) { + const std::optional<yaml::NodeID> Value = Mapping.mappingValue(Key); + if (Value) { + const yaml::Node *Node = Input.node(*Value); + if (Node != nullptr) + return Node; + } + fail(Mapping.range(), + std::string(Context) + " is missing required key '" + std::string(Key) + "'"); + return nullptr; + } + + [[nodiscard]] std::optional<std::string> + requiredScalar(const yaml::Node &Mapping, std::string_view Key, std::string_view Context) { + const yaml::Node *Node = requiredNode(Mapping, Key, Context); + if (Node == nullptr) + return std::nullopt; + return scalar(*Node, std::string(Context) + " key '" + std::string(Key) + "'"); + } + + [[nodiscard]] std::optional<std::string> scalar(const yaml::Node &Node, + std::string_view Context) { + if (Node.kind() != yaml::NodeKind::Scalar) { + fail(Node.range(), std::string(Context) + " must be a scalar"); + return std::nullopt; + } + return std::string(Node.scalar()); + } + + [[nodiscard]] std::optional<std::uint64_t> unsignedInteger(const yaml::Node &Node, + std::string_view Context) { + const auto Text = scalar(Node, Context); + if (!Text) + return std::nullopt; + std::uint64_t Result = 0; + const auto [End, Error] = + std::from_chars(Text->data(), Text->data() + Text->size(), Result, 10); + if (Error != std::errc{} || End != Text->data() + Text->size()) { + fail(Node.range(), std::string(Context) + " must be an unsigned decimal integer"); + return std::nullopt; + } + return Result; + } + + [[nodiscard]] std::optional<std::uint32_t> unsignedInteger32(const yaml::Node &Node, + std::string_view Context) { + const auto Value = unsignedInteger(Node, Context); + if (!Value) + return std::nullopt; + if (*Value > std::numeric_limits<std::uint32_t>::max()) { + fail(Node.range(), std::string(Context) + " is outside the uint32 range"); + return std::nullopt; + } + return static_cast<std::uint32_t>(*Value); + } + + [[nodiscard]] std::optional<std::int64_t> signedInteger(const yaml::Node &Node, + std::string_view Context) { + const auto Text = scalar(Node, Context); + if (!Text) + return std::nullopt; + std::int64_t Result = 0; + const auto [End, Error] = + std::from_chars(Text->data(), Text->data() + Text->size(), Result, 10); + if (Error != std::errc{} || End != Text->data() + Text->size()) { + fail(Node.range(), std::string(Context) + " must be a signed decimal integer"); + return std::nullopt; + } + return Result; + } + + [[nodiscard]] std::optional<bool> boolean(const yaml::Node &Node, std::string_view Context) { + const auto Text = scalar(Node, Context); + if (!Text) + return std::nullopt; + if (*Text == "true") + return true; + if (*Text == "false") + return false; + fail(Node.range(), std::string(Context) + " must be 'true' or 'false'"); + return std::nullopt; + } + + [[nodiscard]] std::optional<std::vector<std::string>> scalarSequence(const yaml::Node &Node, + std::string_view Context) { + if (!requireSequence(&Node, Context)) + return std::nullopt; + std::vector<std::string> Result; + for (const yaml::NodeID Value : Node.sequence()) { + const yaml::Node *Element = Input.node(Value); + if (Element == nullptr) + return std::nullopt; + const auto Text = scalar(*Element, Context); + if (!Text) + return std::nullopt; + Result.push_back(*Text); + } + return Result; + } + + [[nodiscard]] std::optional<std::vector<std::int64_t>> + signedIntegerSequence(const yaml::Node &Node, std::string_view Context) { + if (!requireSequence(&Node, Context)) + return std::nullopt; + std::vector<std::int64_t> Result; + for (const yaml::NodeID Value : Node.sequence()) { + const yaml::Node *Element = Input.node(Value); + if (Element == nullptr) + return std::nullopt; + const auto Parsed = signedInteger(*Element, Context); + if (!Parsed) + return std::nullopt; + Result.push_back(*Parsed); + } + return Result; + } + + [[nodiscard]] std::optional<std::vector<std::uint32_t>> + unsignedInteger32Sequence(const yaml::Node &Node, std::string_view Context) { + if (!requireSequence(&Node, Context)) + return std::nullopt; + std::vector<std::uint32_t> Result; + for (const yaml::NodeID Value : Node.sequence()) { + const yaml::Node *Element = Input.node(Value); + if (Element == nullptr) + return std::nullopt; + const auto Parsed = unsignedInteger32(*Element, Context); + if (!Parsed) + return std::nullopt; + Result.push_back(*Parsed); + } + return Result; + } + + [[nodiscard]] bool parseTarget(const yaml::Node &Node, SourceTarget &Result) { + if (!requireMapping(&Node, "target")) + return false; + if (!checkKeys(Node, {"namespace", "name", "triple", "cpu", "abi", "data_layout", "features"}, + "target")) { + return false; + } + const auto Namespace = requiredScalar(Node, "namespace", "target"); + const auto Name = requiredScalar(Node, "name", "target"); + const auto Triple = requiredScalar(Node, "triple", "target"); + const auto CPU = requiredScalar(Node, "cpu", "target"); + const auto ABI = requiredScalar(Node, "abi", "target"); + const auto DataLayout = requiredScalar(Node, "data_layout", "target"); + const yaml::Node *Features = requiredNode(Node, "features", "target"); + if (!Namespace || !Name || !Triple || !CPU || !ABI || !DataLayout || Features == nullptr) + return false; + const auto ParsedFeatures = scalarSequence(*Features, "target features"); + if (!ParsedFeatures) + return false; + Result = { + .Namespace = *Namespace, + .Name = *Name, + .Triple = *Triple, + .CPU = *CPU, + .ABI = *ABI, + .DataLayout = *DataLayout, + .Features = *ParsedFeatures, + .Range = Node.range(), + }; + return true; + } + + [[nodiscard]] bool parseRegisterUnits(const yaml::Node &Node, + std::vector<SourceRegisterUnit> &Result) { + if (!requireSequence(&Node, "register_units")) + return false; + for (const yaml::NodeID ElementID : Node.sequence()) { + const yaml::Node *Element = Input.node(ElementID); + if (!requireMapping(Element, "register unit")) + return false; + if (!checkKeys(*Element, {"id", "symbol", "name"}, "register unit")) + return false; + const yaml::Node *IDNode = requiredNode(*Element, "id", "register unit"); + const auto Symbol = requiredScalar(*Element, "symbol", "register unit"); + const auto Name = requiredScalar(*Element, "name", "register unit"); + if (IDNode == nullptr || !Symbol || !Name) + return false; + const auto ID = unsignedInteger32(*IDNode, "register unit id"); + if (!ID) + return false; + Result.push_back({.ID = *ID, .Symbol = *Symbol, .Name = *Name, .Range = Element->range()}); + } + return true; + } + + [[nodiscard]] std::optional<std::vector<SourceRegisterUnitLane>> + parseRegisterUnitLanes(const yaml::Node &Node) { + if (!requireSequence(&Node, "register units")) + return std::nullopt; + std::vector<SourceRegisterUnitLane> Result; + for (const yaml::NodeID ElementID : Node.sequence()) { + const yaml::Node *Element = Input.node(ElementID); + if (!requireMapping(Element, "register unit lane")) + return std::nullopt; + if (!checkKeys(*Element, {"unit", "lanes"}, "register unit lane")) + return std::nullopt; + const auto Unit = requiredScalar(*Element, "unit", "register unit lane"); + const yaml::Node *LanesNode = requiredNode(*Element, "lanes", "register unit lane"); + if (!Unit || LanesNode == nullptr) + return std::nullopt; + const auto Lanes = unsignedInteger(*LanesNode, "register unit lane lanes"); + if (!Lanes) + return std::nullopt; + Result.push_back({.UnitSymbol = *Unit, .Lanes = *Lanes, .Range = Element->range()}); + } + return Result; + } + + [[nodiscard]] bool parseRegisters(const yaml::Node &Node, std::vector<SourceRegister> &Result) { + if (!requireSequence(&Node, "registers")) + return false; + for (const yaml::NodeID ElementID : Node.sequence()) { + const yaml::Node *Element = Input.node(ElementID); + if (!requireMapping(Element, "register")) + return false; + if (!checkKeys(*Element, + {"id", "symbol", "name", "encoding", "dwarf_register", "units", "constant", + "artificial", "reserved", "aliases"}, + "register")) { + return false; + } + const yaml::Node *IDNode = requiredNode(*Element, "id", "register"); + const auto Symbol = requiredScalar(*Element, "symbol", "register"); + const auto Name = requiredScalar(*Element, "name", "register"); + const yaml::Node *EncodingNode = requiredNode(*Element, "encoding", "register"); + const yaml::Node *DwarfNode = requiredNode(*Element, "dwarf_register", "register"); + const yaml::Node *UnitsNode = requiredNode(*Element, "units", "register"); + if (IDNode == nullptr || !Symbol || !Name || EncodingNode == nullptr || + DwarfNode == nullptr || UnitsNode == nullptr) { + return false; + } + const auto ID = unsignedInteger32(*IDNode, "register id"); + const auto Encoding = unsignedInteger32(*EncodingNode, "register encoding"); + const auto DwarfRegister = unsignedInteger32(*DwarfNode, "register DWARF number"); + const auto Units = parseRegisterUnitLanes(*UnitsNode); + if (!ID || !Encoding || !DwarfRegister || !Units) + return false; + + SourceRegister Register{ + .ID = *ID, + .Symbol = *Symbol, + .Name = *Name, + .Encoding = *Encoding, + .DwarfRegister = *DwarfRegister, + .Units = *Units, + .IsConstant = false, + .IsArtificial = false, + .IsReserved = false, + .AlternativeNames = {}, + .Range = Element->range(), + }; + if (const yaml::Node *Constant = optionalNode(*Element, "constant")) { + const auto Value = boolean(*Constant, "register constant"); + if (!Value) + return false; + Register.IsConstant = *Value; + } + if (const yaml::Node *Artificial = optionalNode(*Element, "artificial")) { + const auto Value = boolean(*Artificial, "register artificial"); + if (!Value) + return false; + Register.IsArtificial = *Value; + } + if (const yaml::Node *Reserved = optionalNode(*Element, "reserved")) { + const auto Value = boolean(*Reserved, "register reserved"); + if (!Value) + return false; + Register.IsReserved = *Value; + } + if (const yaml::Node *Aliases = optionalNode(*Element, "aliases")) { + const auto Values = scalarSequence(*Aliases, "register aliases"); + if (!Values) + return false; + Register.AlternativeNames = *Values; + } + Result.push_back(std::move(Register)); + } + return true; + } + + [[nodiscard]] bool parseRegisterBanks(const yaml::Node &Node, + std::vector<SourceRegisterBank> &Result) { + if (!requireSequence(&Node, "register_banks")) + return false; + for (const yaml::NodeID ElementID : Node.sequence()) { + const yaml::Node *Element = Input.node(ElementID); + if (!requireMapping(Element, "register bank")) + return false; + if (!checkKeys(*Element, {"id", "symbol", "name", "maximum_size_bits"}, "register bank")) + return false; + const yaml::Node *IDNode = requiredNode(*Element, "id", "register bank"); + const auto Symbol = requiredScalar(*Element, "symbol", "register bank"); + const auto Name = requiredScalar(*Element, "name", "register bank"); + const yaml::Node *SizeNode = requiredNode(*Element, "maximum_size_bits", "register bank"); + if (IDNode == nullptr || !Symbol || !Name || SizeNode == nullptr) + return false; + const auto ID = unsignedInteger32(*IDNode, "register bank id"); + const auto Size = unsignedInteger32(*SizeNode, "register bank maximum_size_bits"); + if (!ID || !Size) + return false; + Result.push_back({.ID = *ID, + .Symbol = *Symbol, + .Name = *Name, + .MaximumSizeInBits = *Size, + .Range = Element->range()}); + } + return true; + } + + [[nodiscard]] bool parseRegisterClasses(const yaml::Node &Node, + std::vector<SourceRegisterClass> &Result) { + if (!requireSequence(&Node, "register_classes")) + return false; + for (const yaml::NodeID ElementID : Node.sequence()) { + const yaml::Node *Element = Input.node(ElementID); + if (!requireMapping(Element, "register class")) + return false; + if (!checkKeys(*Element, + {"id", "symbol", "name", "bank", "size_bits", "spill_size_bits", + "spill_alignment_bits", "lanes", "registers", "allocation_order", + "superclasses", "allocatable", "copy_cost"}, + "register class")) { + return false; + } + const yaml::Node *IDNode = requiredNode(*Element, "id", "register class"); + const auto Symbol = requiredScalar(*Element, "symbol", "register class"); + const auto Name = requiredScalar(*Element, "name", "register class"); + const auto Bank = requiredScalar(*Element, "bank", "register class"); + const yaml::Node *SizeNode = requiredNode(*Element, "size_bits", "register class"); + const yaml::Node *SpillSizeNode = requiredNode(*Element, "spill_size_bits", "register class"); + const yaml::Node *SpillAlignmentNode = + requiredNode(*Element, "spill_alignment_bits", "register class"); + const yaml::Node *LanesNode = requiredNode(*Element, "lanes", "register class"); + const yaml::Node *RegistersNode = requiredNode(*Element, "registers", "register class"); + const yaml::Node *AllocationNode = + requiredNode(*Element, "allocation_order", "register class"); + if (IDNode == nullptr || !Symbol || !Name || !Bank || SizeNode == nullptr || + SpillSizeNode == nullptr || SpillAlignmentNode == nullptr || LanesNode == nullptr || + RegistersNode == nullptr || AllocationNode == nullptr) { + return false; + } + const auto ID = unsignedInteger32(*IDNode, "register class id"); + const auto Size = unsignedInteger32(*SizeNode, "register class size_bits"); + const auto SpillSize = unsignedInteger32(*SpillSizeNode, "register class spill_size_bits"); + const auto SpillAlignment = + unsignedInteger32(*SpillAlignmentNode, "register class spill_alignment_bits"); + const auto Lanes = unsignedInteger(*LanesNode, "register class lanes"); + const auto Registers = scalarSequence(*RegistersNode, "register class registers"); + const auto AllocationOrder = + scalarSequence(*AllocationNode, "register class allocation_order"); + if (!ID || !Size || !SpillSize || !SpillAlignment || !Lanes || !Registers || + !AllocationOrder) { + return false; + } + SourceRegisterClass RegisterClass{ + .ID = *ID, + .Symbol = *Symbol, + .Name = *Name, + .BankSymbol = *Bank, + .SizeInBits = *Size, + .SpillSizeInBits = *SpillSize, + .SpillAlignmentInBits = *SpillAlignment, + .Lanes = *Lanes, + .RegisterSymbols = *Registers, + .AllocationOrderSymbols = *AllocationOrder, + .SuperClassSymbols = {}, + .Range = Element->range(), + }; + if (const yaml::Node *SuperClasses = optionalNode(*Element, "superclasses")) { + const auto Values = scalarSequence(*SuperClasses, "register class superclasses"); + if (!Values) + return false; + RegisterClass.SuperClassSymbols = *Values; + } + if (const yaml::Node *Allocatable = optionalNode(*Element, "allocatable")) { + const auto Value = boolean(*Allocatable, "register class allocatable"); + if (!Value) + return false; + RegisterClass.Allocatable = *Value; + } + if (const yaml::Node *CopyCost = optionalNode(*Element, "copy_cost")) { + const auto Value = unsignedInteger32(*CopyCost, "register class copy_cost"); + if (!Value) + return false; + RegisterClass.CopyCost = *Value; + } + Result.push_back(std::move(RegisterClass)); + } + return true; + } + + [[nodiscard]] bool parseRegisterMasks(const yaml::Node &Node, + std::vector<SourceRegisterMask> &Result) { + if (!requireSequence(&Node, "register_masks")) + return false; + for (const yaml::NodeID ElementID : Node.sequence()) { + const yaml::Node *Element = Input.node(ElementID); + if (!requireMapping(Element, "register mask")) + return false; + if (!checkKeys(*Element, {"name", "preserved"}, "register mask")) + return false; + const auto Name = requiredScalar(*Element, "name", "register mask"); + const yaml::Node *PreservedNode = requiredNode(*Element, "preserved", "register mask"); + if (!Name || PreservedNode == nullptr) + return false; + const auto Preserved = scalarSequence(*PreservedNode, "register mask preserved"); + if (!Preserved) + return false; + Result.push_back( + {.Name = *Name, .PreservedRegisterSymbols = *Preserved, .Range = Element->range()}); + } + return true; + } + + [[nodiscard]] bool parseMIROperandFlagSequence(const yaml::Node &Node, std::string_view Context, + std::vector<SourceMIROperandFlag> &Result) { + if (!requireSequence(&Node, Context)) + return false; + for (const yaml::NodeID ElementID : Node.sequence()) { + const yaml::Node *Element = Input.node(ElementID); + if (!requireMapping(Element, Context) || !checkKeys(*Element, {"name", "value"}, Context)) { + return false; + } + const auto Name = requiredScalar(*Element, "name", Context); + const yaml::Node *ValueNode = requiredNode(*Element, "value", Context); + if (!Name || ValueNode == nullptr) + return false; + const auto Value = unsignedInteger32(*ValueNode, std::string(Context) + " value"); + if (!Value) + return false; + Result.push_back({.Name = *Name, .Value = *Value, .Range = Element->range()}); + } + return true; + } + + [[nodiscard]] bool parseMIROperandFlags(const yaml::Node &Node, SourceMIROperandFlags &Result) { + if (!requireMapping(&Node, "MIR operand flags") || + !checkKeys(Node, {"direct_mask", "direct", "bitmask"}, "MIR operand flags")) { + return false; + } + const yaml::Node *DirectMaskNode = requiredNode(Node, "direct_mask", "MIR operand flags"); + const yaml::Node *DirectNode = requiredNode(Node, "direct", "MIR operand flags"); + const yaml::Node *BitmaskNode = requiredNode(Node, "bitmask", "MIR operand flags"); + if (DirectMaskNode == nullptr || DirectNode == nullptr || BitmaskNode == nullptr) + return false; + const auto DirectMask = unsignedInteger32(*DirectMaskNode, "MIR operand flag direct_mask"); + if (!DirectMask || + !parseMIROperandFlagSequence(*DirectNode, "direct MIR operand flag", Result.Direct) || + !parseMIROperandFlagSequence(*BitmaskNode, "bitmask MIR operand flag", Result.Bitmask)) { + return false; + } + Result.DirectMask = *DirectMask; + Result.Range = Node.range(); + return true; + } + + [[nodiscard]] bool parseNamedOperands(const yaml::Node &Node, + std::vector<SourceNamedOperand> &Result) { + if (!requireSequence(&Node, "named_operands")) + return false; + for (const yaml::NodeID ElementID : Node.sequence()) { + const yaml::Node *Element = Input.node(ElementID); + if (!requireMapping(Element, "named operand")) + return false; + if (!checkKeys(*Element, {"id", "symbol", "name"}, "named operand")) + return false; + const yaml::Node *IDNode = requiredNode(*Element, "id", "named operand"); + const auto Symbol = requiredScalar(*Element, "symbol", "named operand"); + const auto Name = requiredScalar(*Element, "name", "named operand"); + if (IDNode == nullptr || !Symbol || !Name) + return false; + const auto ID = unsignedInteger32(*IDNode, "named operand id"); + if (!ID) + return false; + Result.push_back({.ID = *ID, .Symbol = *Symbol, .Name = *Name, .Range = Element->range()}); + } + return true; + } + + [[nodiscard]] bool parseImmediatePredicates(const yaml::Node &Node, + std::vector<SourceImmediatePredicate> &Result) { + if (!requireSequence(&Node, "immediate_predicates")) + return false; + for (const yaml::NodeID ElementID : Node.sequence()) { + const yaml::Node *Element = Input.node(ElementID); + if (!requireMapping(Element, "immediate predicate")) + return false; + if (!checkKeys( + *Element, + {"id", "symbol", "name", "kind", "bits", "alignment", "nonzero", "allowed_values"}, + "immediate predicate")) { + return false; + } + const yaml::Node *IDNode = requiredNode(*Element, "id", "immediate predicate"); + const auto Symbol = requiredScalar(*Element, "symbol", "immediate predicate"); + const auto Name = requiredScalar(*Element, "name", "immediate predicate"); + const auto Kind = requiredScalar(*Element, "kind", "immediate predicate"); + const yaml::Node *BitsNode = requiredNode(*Element, "bits", "immediate predicate"); + if (IDNode == nullptr || !Symbol || !Name || !Kind || BitsNode == nullptr) + return false; + const auto ID = unsignedInteger32(*IDNode, "immediate predicate id"); + const auto Bits = unsignedInteger32(*BitsNode, "immediate predicate bits"); + if (!ID || !Bits) + return false; + SourceImmediatePredicate Predicate{ + .ID = *ID, + .Symbol = *Symbol, + .Name = *Name, + .Kind = *Kind, + .Bits = *Bits, + .Alignment = 1, + .NonZero = false, + .AllowedValues = {}, + .Range = Element->range(), + }; + if (const yaml::Node *Alignment = optionalNode(*Element, "alignment")) { + const auto Value = unsignedInteger(*Alignment, "immediate predicate alignment"); + if (!Value) + return false; + Predicate.Alignment = *Value; + } + if (const yaml::Node *NonZero = optionalNode(*Element, "nonzero")) { + const auto Value = boolean(*NonZero, "immediate predicate nonzero"); + if (!Value) + return false; + Predicate.NonZero = *Value; + } + if (const yaml::Node *AllowedValues = optionalNode(*Element, "allowed_values")) { + const auto Values = + signedIntegerSequence(*AllowedValues, "immediate predicate allowed_values"); + if (!Values || Values->empty()) { + if (Values) + fail(AllowedValues->range(), "immediate predicate allowed_values must be nonempty"); + return false; + } + Predicate.AllowedValues = *Values; + } + Result.push_back(std::move(Predicate)); + } + return true; + } + + [[nodiscard]] bool parseSchedulingClasses(const yaml::Node &Node, + std::vector<SourceSchedulingClass> &Result) { + if (!requireSequence(&Node, "scheduling_classes")) + return false; + for (const yaml::NodeID ElementID : Node.sequence()) { + const yaml::Node *Element = Input.node(ElementID); + if (!requireMapping(Element, "scheduling class")) + return false; + if (!checkKeys(*Element, {"id", "symbol", "name", "micro_ops", "latency"}, + "scheduling class")) { + return false; + } + const yaml::Node *IDNode = requiredNode(*Element, "id", "scheduling class"); + const auto Symbol = requiredScalar(*Element, "symbol", "scheduling class"); + const auto Name = requiredScalar(*Element, "name", "scheduling class"); + const yaml::Node *MicroOpsNode = requiredNode(*Element, "micro_ops", "scheduling class"); + const yaml::Node *LatencyNode = requiredNode(*Element, "latency", "scheduling class"); + if (IDNode == nullptr || !Symbol || !Name || MicroOpsNode == nullptr || + LatencyNode == nullptr) + return false; + const auto ID = unsignedInteger32(*IDNode, "scheduling class id"); + const auto MicroOps = unsignedInteger32(*MicroOpsNode, "scheduling class micro_ops"); + const auto Latency = unsignedInteger32(*LatencyNode, "scheduling class latency"); + if (!ID || !MicroOps || !Latency) + return false; + Result.push_back({ + .ID = *ID, + .Symbol = *Symbol, + .Name = *Name, + .MicroOps = *MicroOps, + .Latency = *Latency, + .Range = Element->range(), + }); + } + return true; + } + + [[nodiscard]] bool parseEncodingCategories(const yaml::Node &Node, + std::vector<SourceEncodingCategory> &Result) { + if (!requireSequence(&Node, "encoding_categories")) + return false; + for (const yaml::NodeID ElementID : Node.sequence()) { + const yaml::Node *Element = Input.node(ElementID); + if (!requireMapping(Element, "encoding category")) + return false; + if (!checkKeys(*Element, {"id", "symbol", "name", "size_bytes"}, "encoding category")) + return false; + const yaml::Node *IDNode = requiredNode(*Element, "id", "encoding category"); + const auto Symbol = requiredScalar(*Element, "symbol", "encoding category"); + const auto Name = requiredScalar(*Element, "name", "encoding category"); + const yaml::Node *SizeNode = requiredNode(*Element, "size_bytes", "encoding category"); + if (IDNode == nullptr || !Symbol || !Name || SizeNode == nullptr) + return false; + const auto ID = unsignedInteger32(*IDNode, "encoding category id"); + const auto Size = unsignedInteger32(*SizeNode, "encoding category size_bytes"); + if (!ID || !Size) + return false; + Result.push_back({ + .ID = *ID, + .Symbol = *Symbol, + .Name = *Name, + .SizeInBytes = *Size, + .Range = Element->range(), + }); + } + return true; + } + + [[nodiscard]] bool parseAssemblyFormats(const yaml::Node &Node, + std::vector<SourceAssemblyFormat> &Result) { + if (!requireSequence(&Node, "assembly_formats")) + return false; + for (const yaml::NodeID ElementID : Node.sequence()) { + const yaml::Node *Element = Input.node(ElementID); + if (!requireMapping(Element, "assembly format")) + return false; + if (!checkKeys(*Element, {"id", "symbol", "name"}, "assembly format")) + return false; + const yaml::Node *IDNode = requiredNode(*Element, "id", "assembly format"); + const auto Symbol = requiredScalar(*Element, "symbol", "assembly format"); + const auto Name = requiredScalar(*Element, "name", "assembly format"); + if (IDNode == nullptr || !Symbol || !Name) + return false; + const auto ID = unsignedInteger32(*IDNode, "assembly format id"); + if (!ID) + return false; + Result.push_back({.ID = *ID, .Symbol = *Symbol, .Name = *Name, .Range = Element->range()}); + } + return true; + } + + [[nodiscard]] bool parseCallingConventions(const yaml::Node &Node, + std::vector<SourceCallingConvention> &Result) { + if (!requireSequence(&Node, "calling_conventions")) + return false; + for (const yaml::NodeID ElementID : Node.sequence()) { + const yaml::Node *Element = Input.node(ElementID); + if (!requireMapping(Element, "calling convention")) + return false; + if (!checkKeys(*Element, + {"id", "symbol", "name", "integer_register_class", "floating_register_class", + "integer_argument_registers", "floating_argument_registers", + "integer_return_registers", "floating_return_registers", "preserved_mask", + "stack_alignment_bytes", "stack_argument_alignment_bytes", + "shadow_space_bytes", "supports_varargs", "supports_tail_calls"}, + "calling convention")) { + return false; + } + const yaml::Node *IDNode = requiredNode(*Element, "id", "calling convention"); + const auto Symbol = requiredScalar(*Element, "symbol", "calling convention"); + const auto Name = requiredScalar(*Element, "name", "calling convention"); + const auto IntegerClass = + requiredScalar(*Element, "integer_register_class", "calling convention"); + const auto FloatingClass = + requiredScalar(*Element, "floating_register_class", "calling convention"); + const yaml::Node *IntegerArguments = + requiredNode(*Element, "integer_argument_registers", "calling convention"); + const yaml::Node *FloatingArguments = + requiredNode(*Element, "floating_argument_registers", "calling convention"); + const yaml::Node *IntegerReturns = + requiredNode(*Element, "integer_return_registers", "calling convention"); + const yaml::Node *FloatingReturns = + requiredNode(*Element, "floating_return_registers", "calling convention"); + const auto PreservedMask = requiredScalar(*Element, "preserved_mask", "calling convention"); + const yaml::Node *StackAlignment = + requiredNode(*Element, "stack_alignment_bytes", "calling convention"); + const yaml::Node *StackArgumentAlignment = + requiredNode(*Element, "stack_argument_alignment_bytes", "calling convention"); + const yaml::Node *ShadowSpace = + requiredNode(*Element, "shadow_space_bytes", "calling convention"); + const yaml::Node *SupportsVarArgs = + requiredNode(*Element, "supports_varargs", "calling convention"); + const yaml::Node *SupportsTailCalls = + requiredNode(*Element, "supports_tail_calls", "calling convention"); + if (IDNode == nullptr || !Symbol || !Name || !IntegerClass || !FloatingClass || + IntegerArguments == nullptr || FloatingArguments == nullptr || + IntegerReturns == nullptr || FloatingReturns == nullptr || !PreservedMask || + StackAlignment == nullptr || StackArgumentAlignment == nullptr || + ShadowSpace == nullptr || SupportsVarArgs == nullptr || SupportsTailCalls == nullptr) { + return false; + } + const auto ID = unsignedInteger32(*IDNode, "calling convention id"); + const auto IntegerArgumentList = + scalarSequence(*IntegerArguments, "calling convention integer arguments"); + const auto FloatingArgumentList = + scalarSequence(*FloatingArguments, "calling convention floating arguments"); + const auto IntegerReturnList = + scalarSequence(*IntegerReturns, "calling convention integer returns"); + const auto FloatingReturnList = + scalarSequence(*FloatingReturns, "calling convention floating returns"); + const auto StackAlignmentValue = + unsignedInteger32(*StackAlignment, "calling convention stack alignment"); + const auto StackArgumentAlignmentValue = + unsignedInteger32(*StackArgumentAlignment, "calling convention stack argument alignment"); + const auto ShadowSpaceValue = + unsignedInteger32(*ShadowSpace, "calling convention shadow space"); + const auto SupportsVarArgsValue = boolean(*SupportsVarArgs, "calling convention varargs"); + const auto SupportsTailCallsValue = + boolean(*SupportsTailCalls, "calling convention tail calls"); + if (!ID || !IntegerArgumentList || !FloatingArgumentList || !IntegerReturnList || + !FloatingReturnList || !StackAlignmentValue || !StackArgumentAlignmentValue || + !ShadowSpaceValue || !SupportsVarArgsValue || !SupportsTailCallsValue) { + return false; + } + Result.push_back({ + .ID = *ID, + .Symbol = *Symbol, + .Name = *Name, + .IntegerRegisterClassSymbol = *IntegerClass, + .FloatingRegisterClassSymbol = *FloatingClass, + .IntegerArgumentRegisterSymbols = *IntegerArgumentList, + .FloatingArgumentRegisterSymbols = *FloatingArgumentList, + .IntegerReturnRegisterSymbols = *IntegerReturnList, + .FloatingReturnRegisterSymbols = *FloatingReturnList, + .PreservedMaskName = *PreservedMask, + .StackAlignmentBytes = *StackAlignmentValue, + .StackArgumentAlignmentBytes = *StackArgumentAlignmentValue, + .ShadowSpaceBytes = *ShadowSpaceValue, + .SupportsVarArgs = *SupportsVarArgsValue, + .SupportsTailCalls = *SupportsTailCallsValue, + .Range = Element->range(), + }); + } + return true; + } + + [[nodiscard]] std::optional<SourceOperand> parseOperand(const yaml::Node &Node) { + if (!requireMapping(&Node, "instruction operand")) + return std::nullopt; + if (!checkKeys(Node, + {"kinds", "role", "register_class", "register_bank", "immediate_predicate", + "memory_role", "default_tied_operand", "early_clobber", "optional", + "variadic_group", "allow_subreg", "name", "type_index", "allowed_target_flags"}, + "instruction operand")) { + return std::nullopt; + } + const yaml::Node *KindsNode = requiredNode(Node, "kinds", "instruction operand"); + if (KindsNode == nullptr) + return std::nullopt; + const auto Kinds = scalarSequence(*KindsNode, "instruction operand kinds"); + if (!Kinds) + return std::nullopt; + + SourceOperand Result; + Result.Kinds = *Kinds; + Result.Range = Node.range(); + if (const yaml::Node *Role = optionalNode(Node, "role")) { + const auto Value = scalar(*Role, "instruction operand role"); + if (!Value) + return std::nullopt; + Result.RegisterRole = *Value; + } + if (const yaml::Node *RegisterClass = optionalNode(Node, "register_class")) { + const auto Value = scalar(*RegisterClass, "instruction operand register_class"); + if (!Value) + return std::nullopt; + Result.RegisterClassSymbol = *Value; + } + if (const yaml::Node *RegisterBank = optionalNode(Node, "register_bank")) { + const auto Value = scalar(*RegisterBank, "instruction operand register_bank"); + if (!Value) + return std::nullopt; + Result.RegisterBankSymbol = *Value; + } + if (const yaml::Node *Predicate = optionalNode(Node, "immediate_predicate")) { + const auto Value = scalar(*Predicate, "instruction operand immediate_predicate"); + if (!Value) + return std::nullopt; + Result.ImmediatePredicateSymbol = *Value; + } + if (const yaml::Node *MemoryRole = optionalNode(Node, "memory_role")) { + const auto Value = scalar(*MemoryRole, "instruction operand memory_role"); + if (!Value) + return std::nullopt; + Result.MemoryRole = *Value; + } + if (const yaml::Node *Tie = optionalNode(Node, "default_tied_operand")) { + const auto Value = unsignedInteger32(*Tie, "instruction operand default_tied_operand"); + if (!Value) + return std::nullopt; + Result.DefaultTiedOperand = Value; + } + if (const yaml::Node *EarlyClobber = optionalNode(Node, "early_clobber")) { + const auto Value = boolean(*EarlyClobber, "instruction operand early_clobber"); + if (!Value) + return std::nullopt; + Result.EarlyClobber = *Value; + } + if (const yaml::Node *Optional = optionalNode(Node, "optional")) { + const auto Value = boolean(*Optional, "instruction operand optional"); + if (!Value) + return std::nullopt; + Result.Optional = *Value; + } + if (const yaml::Node *VariadicGroup = optionalNode(Node, "variadic_group")) { + const auto Value = boolean(*VariadicGroup, "instruction operand variadic_group"); + if (!Value) + return std::nullopt; + Result.VariadicGroup = *Value; + } + if (const yaml::Node *AllowSubReg = optionalNode(Node, "allow_subreg")) { + const auto Value = boolean(*AllowSubReg, "instruction operand allow_subreg"); + if (!Value) + return std::nullopt; + Result.AllowSubReg = *Value; + } + if (const yaml::Node *Name = optionalNode(Node, "name")) { + const auto Value = scalar(*Name, "instruction operand name"); + if (!Value) + return std::nullopt; + Result.NameSymbol = *Value; + } + if (const yaml::Node *TypeIndex = optionalNode(Node, "type_index")) { + const auto Value = unsignedInteger32(*TypeIndex, "instruction operand type_index"); + if (!Value) + return std::nullopt; + if (*Value > std::numeric_limits<std::uint16_t>::max()) { + fail(TypeIndex->range(), "instruction operand type_index is outside the uint16 range"); + return std::nullopt; + } + Result.TypeIndex = static_cast<std::uint16_t>(*Value); + } + if (const yaml::Node *TargetFlags = optionalNode(Node, "allowed_target_flags")) { + const auto Value = + unsignedInteger32(*TargetFlags, "instruction operand allowed_target_flags"); + if (!Value) + return std::nullopt; + Result.AllowedTargetFlags = *Value; + } + return Result; + } + + [[nodiscard]] std::optional<std::vector<SourceOperand>> parseOperands(const yaml::Node &Node) { + if (!requireSequence(&Node, "instruction operands")) + return std::nullopt; + std::vector<SourceOperand> Result; + for (const yaml::NodeID ElementID : Node.sequence()) { + const yaml::Node *Element = Input.node(ElementID); + if (Element == nullptr) + return std::nullopt; + const auto Operand = parseOperand(*Element); + if (!Operand) + return std::nullopt; + Result.push_back(*Operand); + } + return Result; + } + + [[nodiscard]] std::optional<SourceImplicitRegister> + parseImplicitRegister(const yaml::Node &Node, std::string_view Context) { + if (!requireMapping(&Node, Context)) + return std::nullopt; + if (!checkKeys(Node, {"register", "early_clobber", "renamable"}, Context)) + return std::nullopt; + const auto Register = requiredScalar(Node, "register", Context); + if (!Register) + return std::nullopt; + SourceImplicitRegister Result{ + .RegisterSymbol = *Register, + .Range = Node.range(), + }; + if (const yaml::Node *EarlyClobber = optionalNode(Node, "early_clobber")) { + const auto Value = boolean(*EarlyClobber, std::string(Context) + " early_clobber"); + if (!Value) + return std::nullopt; + Result.EarlyClobber = *Value; + } + if (const yaml::Node *Renamable = optionalNode(Node, "renamable")) { + const auto Value = boolean(*Renamable, std::string(Context) + " renamable"); + if (!Value) + return std::nullopt; + Result.Renamable = *Value; + } + return Result; + } + + [[nodiscard]] std::optional<std::vector<SourceImplicitRegister>> + parseImplicitRegisters(const yaml::Node &Node, std::string_view Context) { + if (!requireSequence(&Node, Context)) + return std::nullopt; + std::vector<SourceImplicitRegister> Result; + for (const yaml::NodeID ElementID : Node.sequence()) { + const yaml::Node *Element = Input.node(ElementID); + if (Element == nullptr) + return std::nullopt; + const auto Register = parseImplicitRegister(*Element, Context); + if (!Register) + return std::nullopt; + Result.push_back(*Register); + } + return Result; + } + + [[nodiscard]] std::optional<SourceSelectionTypePredicate> + parseSelectionTypePredicate(const yaml::Node &Node, std::string_view Context) { + if (!requireMapping(&Node, Context)) + return std::nullopt; + if (!checkKeys(Node, {"kind", "bits"}, Context)) + return std::nullopt; + const auto Kind = requiredScalar(Node, "kind", Context); + const yaml::Node *BitsNode = requiredNode(Node, "bits", Context); + if (!Kind || BitsNode == nullptr) + return std::nullopt; + const auto Bits = unsignedInteger32(*BitsNode, std::string(Context) + " bits"); + if (!Bits) + return std::nullopt; + return SourceSelectionTypePredicate{ + .Kind = *Kind, + .Bits = *Bits, + .Range = Node.range(), + }; + } + + [[nodiscard]] bool + parseSelectionOperandTypes(const yaml::Node &Node, + std::vector<SourceSelectionOperandTypePredicate> &Result) { + if (!requireSequence(&Node, "selection operand type predicates")) + return false; + for (const yaml::NodeID ElementID : Node.sequence()) { + const yaml::Node *Element = Input.node(ElementID); + if (!requireMapping(Element, "selection operand type predicate") || + !checkKeys(*Element, {"index", "kind", "bits"}, "selection operand type predicate")) { + return false; + } + const yaml::Node *IndexNode = + requiredNode(*Element, "index", "selection operand type predicate"); + const auto Kind = requiredScalar(*Element, "kind", "selection operand type predicate"); + const yaml::Node *BitsNode = + requiredNode(*Element, "bits", "selection operand type predicate"); + if (IndexNode == nullptr || !Kind || BitsNode == nullptr) + return false; + const auto Index = unsignedInteger32(*IndexNode, "selection operand type index"); + const auto Bits = unsignedInteger32(*BitsNode, "selection operand type bits"); + if (!Index || !Bits) + return false; + Result.push_back({ + .OperandIndex = *Index, + .Type = {.Kind = *Kind, .Bits = *Bits, .Range = Element->range()}, + .Range = Element->range(), + }); + } + return true; + } + + [[nodiscard]] bool + parseSelectionOperandBanks(const yaml::Node &Node, + std::vector<SourceSelectionOperandBankPredicate> &Result) { + if (!requireSequence(&Node, "selection operand bank predicates")) + return false; + for (const yaml::NodeID ElementID : Node.sequence()) { + const yaml::Node *Element = Input.node(ElementID); + if (!requireMapping(Element, "selection operand bank predicate") || + !checkKeys(*Element, {"index", "bank"}, "selection operand bank predicate")) { + return false; + } + const yaml::Node *IndexNode = + requiredNode(*Element, "index", "selection operand bank predicate"); + const auto Bank = requiredScalar(*Element, "bank", "selection operand bank predicate"); + if (IndexNode == nullptr || !Bank) + return false; + const auto Index = unsignedInteger32(*IndexNode, "selection operand bank index"); + if (!Index) + return false; + Result.push_back({ + .OperandIndex = *Index, + .BankSymbol = *Bank, + .Range = Element->range(), + }); + } + return true; + } + + [[nodiscard]] bool + parseSelectionOperandClasses(const yaml::Node &Node, + std::vector<SourceSelectionOperandClassPredicate> &Result) { + if (!requireSequence(&Node, "selection operand class predicates")) + return false; + for (const yaml::NodeID ElementID : Node.sequence()) { + const yaml::Node *Element = Input.node(ElementID); + if (!requireMapping(Element, "selection operand class predicate") || + !checkKeys(*Element, {"index", "class"}, "selection operand class predicate")) { + return false; + } + const yaml::Node *IndexNode = + requiredNode(*Element, "index", "selection operand class predicate"); + const auto Class = requiredScalar(*Element, "class", "selection operand class predicate"); + if (IndexNode == nullptr || !Class) + return false; + const auto Index = unsignedInteger32(*IndexNode, "selection operand class index"); + if (!Index) + return false; + Result.push_back({ + .OperandIndex = *Index, + .ClassSymbol = *Class, + .Range = Element->range(), + }); + } + return true; + } + + [[nodiscard]] bool + parseSelectionIntegerPredicate(const yaml::Node &Node, + std::optional<SourceSelectionIntegerPredicate> &Result) { + if (!requireMapping(&Node, "selection integer predicate") || + !checkKeys(Node, {"index", "value"}, "selection integer predicate")) { + return false; + } + const yaml::Node *IndexNode = requiredNode(Node, "index", "selection integer predicate"); + const auto Value = requiredScalar(Node, "value", "selection integer predicate"); + if (IndexNode == nullptr || !Value) + return false; + const auto Index = unsignedInteger32(*IndexNode, "selection integer predicate index"); + if (!Index) + return false; + Result = SourceSelectionIntegerPredicate{ + .OperandIndex = *Index, + .Predicate = *Value, + .Range = Node.range(), + }; + return true; + } + + [[nodiscard]] bool + parseSelectionFloatingPredicate(const yaml::Node &Node, + std::optional<SourceSelectionFloatingPredicate> &Result) { + if (!requireMapping(&Node, "selection floating predicate") || + !checkKeys(Node, {"index", "value"}, "selection floating predicate")) { + return false; + } + const yaml::Node *IndexNode = requiredNode(Node, "index", "selection floating predicate"); + const auto Value = requiredScalar(Node, "value", "selection floating predicate"); + if (IndexNode == nullptr || !Value) + return false; + const auto Index = unsignedInteger32(*IndexNode, "selection floating predicate index"); + if (!Index) + return false; + Result = SourceSelectionFloatingPredicate{ + .OperandIndex = *Index, + .Predicate = *Value, + .Range = Node.range(), + }; + return true; + } + + [[nodiscard]] bool + parseSelectionMemoryPredicate(const yaml::Node &Node, + std::optional<SourceSelectionMemoryPredicate> &Result) { + if (!requireMapping(&Node, "selection memory predicate") || + !checkKeys(Node, {"index", "bytes"}, "selection memory predicate")) { + return false; + } + const yaml::Node *IndexNode = requiredNode(Node, "index", "selection memory predicate"); + const yaml::Node *BytesNode = requiredNode(Node, "bytes", "selection memory predicate"); + if (IndexNode == nullptr || BytesNode == nullptr) + return false; + const auto Index = unsignedInteger32(*IndexNode, "selection memory predicate index"); + const auto Bytes = unsignedInteger32(*BytesNode, "selection memory predicate bytes"); + if (!Index || !Bytes) + return false; + Result = SourceSelectionMemoryPredicate{ + .MemoryOperandIndex = *Index, + .Bytes = *Bytes, + .Range = Node.range(), + }; + return true; + } + + [[nodiscard]] bool + parseSelectionTargetPredicates(const yaml::Node &Node, + std::vector<SourceSelectionTargetPredicate> &Result) { + if (!requireSequence(&Node, "selection target predicates")) + return false; + for (const yaml::NodeID ElementID : Node.sequence()) { + const yaml::Node *Element = Input.node(ElementID); + if (Element == nullptr) + return false; + const auto Symbol = scalar(*Element, "selection target predicate"); + if (!Symbol) + return false; + Result.push_back({ + .Symbol = *Symbol, + .Range = Element->range(), + }); + } + return true; + } + + [[nodiscard]] bool parseSelectionConstantRanges(const yaml::Node &Node, + std::vector<SourceSelectionConstantRange> &Result, + std::string_view Context) { + if (!requireSequence(&Node, Context) || Node.sequence().empty()) + return false; + for (const yaml::NodeID RangeID : Node.sequence()) { + const yaml::Node *Range = Input.node(RangeID); + const std::string RangeContext = std::string(Context) + " range"; + if (!requireMapping(Range, RangeContext) || + !checkKeys(*Range, {"min", "max"}, RangeContext)) { + return false; + } + const yaml::Node *MinimumNode = requiredNode(*Range, "min", RangeContext); + const yaml::Node *MaximumNode = requiredNode(*Range, "max", RangeContext); + if (MinimumNode == nullptr || MaximumNode == nullptr) + return false; + const auto Minimum = signedInteger(*MinimumNode, "selection constant range minimum"); + const auto Maximum = signedInteger(*MaximumNode, "selection constant range maximum"); + if (!Minimum || !Maximum || *Minimum > *Maximum) { + if (Minimum && Maximum) + fail(Range->range(), "selection constant range is invalid"); + return false; + } + Result.push_back({ + .Minimum = *Minimum, + .Maximum = *Maximum, + .SourceRange = Range->range(), + }); + } + return true; + } + + [[nodiscard]] bool + parseSelectionOperandImmediates(const yaml::Node &Node, + std::vector<SourceSelectionOperandImmediatePredicate> &Result) { + if (!requireSequence(&Node, "selection operand immediates")) + return false; + for (const yaml::NodeID ElementID : Node.sequence()) { + const yaml::Node *Element = Input.node(ElementID); + if (!requireMapping(Element, "selection operand immediate") || + !checkKeys(*Element, {"index", "ranges"}, "selection operand immediate")) { + return false; + } + const yaml::Node *IndexNode = requiredNode(*Element, "index", "selection operand immediate"); + const yaml::Node *RangesNode = + requiredNode(*Element, "ranges", "selection operand immediate"); + if (IndexNode == nullptr || RangesNode == nullptr) + return false; + const auto Index = unsignedInteger32(*IndexNode, "selection operand immediate index"); + if (!Index) + return false; + + SourceSelectionOperandImmediatePredicate Predicate{ + .OperandIndex = *Index, + .Ranges = {}, + .Range = Element->range(), + }; + if (!parseSelectionConstantRanges(*RangesNode, Predicate.Ranges, + "selection operand immediate ranges")) { + return false; + } + Result.push_back(std::move(Predicate)); + } + return true; + } + + [[nodiscard]] bool parseSelectionOperandDefConstants( + const yaml::Node &Node, std::vector<SourceSelectionOperandDefConstantPredicate> &Result) { + if (!requireSequence(&Node, "selection operand definition constants")) + return false; + for (const yaml::NodeID ElementID : Node.sequence()) { + const yaml::Node *Element = Input.node(ElementID); + if (!requireMapping(Element, "selection operand definition constant") || + !checkKeys(*Element, {"index", "ranges"}, "selection operand definition constant")) { + return false; + } + const yaml::Node *IndexNode = + requiredNode(*Element, "index", "selection operand definition constant"); + const yaml::Node *RangesNode = + requiredNode(*Element, "ranges", "selection operand definition constant"); + if (IndexNode == nullptr || RangesNode == nullptr) + return false; + const auto Index = + unsignedInteger32(*IndexNode, "selection operand definition constant index"); + if (!Index) { + return false; + } + + SourceSelectionOperandDefConstantPredicate Predicate{ + .OperandIndex = *Index, + .Ranges = {}, + .Range = Element->range(), + }; + if (!parseSelectionConstantRanges(*RangesNode, Predicate.Ranges, + "selection operand definition constant ranges")) { + return false; + } + Result.push_back(std::move(Predicate)); + } + return true; + } + + [[nodiscard]] bool + parseSelectionDefUsePredicates(const yaml::Node &Node, + std::vector<SourceSelectionDefUsePredicate> &Result) { + if (!requireSequence(&Node, "selection def-use predicates")) + return false; + for (const yaml::NodeID ElementID : Node.sequence()) { + const yaml::Node *Element = Input.node(ElementID); + if (!requireMapping(Element, "selection def-use predicate") || + !checkKeys( + *Element, + {"operand", "opcode", "integer_predicate", "captures", "operand_def_constants"}, + "selection def-use predicate")) { + return false; + } + const yaml::Node *OperandNode = + requiredNode(*Element, "operand", "selection def-use predicate"); + const auto Opcode = requiredScalar(*Element, "opcode", "selection def-use predicate"); + if (OperandNode == nullptr || !Opcode) + return false; + const auto Operand = unsignedInteger32(*OperandNode, "selection def-use operand"); + if (!Operand) + return false; + + SourceSelectionDefUsePredicate Predicate{ + .RootOperandIndex = *Operand, + .OpcodeSymbol = *Opcode, + .IntegerPredicate = std::nullopt, + .Captures = {}, + .OperandDefConstants = {}, + .Range = Element->range(), + }; + if (const yaml::Node *IntegerPredicate = optionalNode(*Element, "integer_predicate")) { + if (!parseSelectionIntegerPredicate(*IntegerPredicate, Predicate.IntegerPredicate)) + return false; + } + if (const yaml::Node *Captures = optionalNode(*Element, "captures")) { + if (!requireSequence(Captures, "selection def-use captures")) + return false; + for (const yaml::NodeID CaptureID : Captures->sequence()) { + const yaml::Node *Capture = Input.node(CaptureID); + if (!requireMapping(Capture, "selection def-use capture") || + !checkKeys(*Capture, {"id", "operand"}, "selection def-use capture")) { + return false; + } + const auto Symbol = requiredScalar(*Capture, "id", "selection def-use capture"); + const yaml::Node *CaptureOperand = + requiredNode(*Capture, "operand", "selection def-use capture"); + if (!Symbol || CaptureOperand == nullptr) + return false; + const auto Index = + unsignedInteger32(*CaptureOperand, "selection def-use capture operand"); + if (!Index) + return false; + Predicate.Captures.push_back({ + .Symbol = *Symbol, + .OperandIndex = *Index, + .Range = Capture->range(), + }); + } + } + if (const yaml::Node *Constants = optionalNode(*Element, "operand_def_constants")) { + if (!parseSelectionOperandDefConstants(*Constants, Predicate.OperandDefConstants)) + return false; + } + if (!Predicate.IntegerPredicate && Predicate.Captures.empty() && + Predicate.OperandDefConstants.empty()) { + fail(Element->range(), + "selection def-use predicate requires a capture or constant predicate"); + return false; + } + Result.push_back(std::move(Predicate)); + } + return true; + } + + [[nodiscard]] bool + parseSelectionOperandRenderers(const yaml::Node &Node, + std::vector<SourceSelectionOperandRenderer> &Result) { + if (!requireSequence(&Node, "selection operand renderers")) + return false; + for (const yaml::NodeID ElementID : Node.sequence()) { + const yaml::Node *Element = Input.node(ElementID); + if (!requireMapping(Element, "selection operand renderer") || + !checkKeys(*Element, + {"operand", "complex", "physical_register", "temporary", "captured_operand", + "captured_immediate", "immediate", "transform", "flags"}, + "selection operand renderer")) { + return false; + } + const yaml::Node *OperandNode = optionalNode(*Element, "operand"); + const yaml::Node *ComplexNode = optionalNode(*Element, "complex"); + const yaml::Node *PhysicalRegisterNode = optionalNode(*Element, "physical_register"); + const yaml::Node *TemporaryNode = optionalNode(*Element, "temporary"); + const yaml::Node *CapturedOperandNode = optionalNode(*Element, "captured_operand"); + const yaml::Node *CapturedImmediateNode = optionalNode(*Element, "captured_immediate"); + const yaml::Node *ImmediateNode = optionalNode(*Element, "immediate"); + const std::uint32_t SourceCount = + static_cast<std::uint32_t>(ComplexNode != nullptr) + + static_cast<std::uint32_t>(ComplexNode == nullptr && OperandNode != nullptr) + + static_cast<std::uint32_t>(PhysicalRegisterNode != nullptr) + + static_cast<std::uint32_t>(TemporaryNode != nullptr) + + static_cast<std::uint32_t>(CapturedOperandNode != nullptr) + + static_cast<std::uint32_t>(CapturedImmediateNode != nullptr) + + static_cast<std::uint32_t>(ImmediateNode != nullptr); + if (SourceCount != 1) { + fail(Element->range(), "selection operand renderer requires exactly one source"); + return false; + } + SourceSelectionOperandRenderer Renderer{ + .OperandIndex = std::nullopt, + .ComplexOperandIndex = std::nullopt, + .PhysicalRegisterSymbol = {}, + .ComplexRendererSymbol = {}, + .TemporarySymbol = {}, + .CapturedOperandSymbol = {}, + .CapturedImmediateOperand = std::nullopt, + .Immediate = std::nullopt, + .ImmediateTransform = "identity", + .Flags = {}, + .Range = Element->range(), + }; + if (ComplexNode != nullptr) { + if (OperandNode == nullptr) { + fail(Element->range(), "complex selection operand renderer requires an operand index"); + return false; + } + const auto Name = scalar(*ComplexNode, "selection operand renderer complex matcher"); + const auto Index = unsignedInteger32(*OperandNode, "selection operand renderer operand"); + if (!Name || !Index) + return false; + Renderer.ComplexRendererSymbol = *Name; + Renderer.ComplexOperandIndex = Index; + } else if (OperandNode != nullptr) { + const auto Index = unsignedInteger32(*OperandNode, "selection operand renderer operand"); + if (!Index) + return false; + Renderer.OperandIndex = Index; + } else if (PhysicalRegisterNode != nullptr) { + const auto Symbol = + scalar(*PhysicalRegisterNode, "selection operand renderer physical register"); + if (!Symbol) + return false; + Renderer.PhysicalRegisterSymbol = *Symbol; + } else if (TemporaryNode != nullptr) { + const auto Symbol = scalar(*TemporaryNode, "selection operand renderer temporary"); + if (!Symbol) + return false; + Renderer.TemporarySymbol = *Symbol; + } else if (CapturedOperandNode != nullptr) { + const auto Symbol = + scalar(*CapturedOperandNode, "selection operand renderer captured operand"); + if (!Symbol) + return false; + Renderer.CapturedOperandSymbol = *Symbol; + } else if (CapturedImmediateNode != nullptr) { + const auto Index = unsignedInteger32(*CapturedImmediateNode, + "selection operand renderer captured immediate"); + if (!Index) + return false; + Renderer.CapturedImmediateOperand = Index; + } else { + const auto Value = signedInteger(*ImmediateNode, "selection operand renderer immediate"); + if (!Value) + return false; + Renderer.Immediate = Value; + } + if (const yaml::Node *TransformNode = optionalNode(*Element, "transform")) { + const auto Transform = scalar(*TransformNode, "selection operand renderer transform"); + if (!Transform) + return false; + Renderer.ImmediateTransform = *Transform; + } + if (!Renderer.CapturedImmediateOperand && Renderer.ImmediateTransform != "identity") { + fail(Element->range(), + "selection operand renderer transform requires a captured immediate"); + return false; + } + if (const yaml::Node *FlagsNode = optionalNode(*Element, "flags")) { + const auto Flags = scalarSequence(*FlagsNode, "selection operand renderer flags"); + if (!Flags) + return false; + Renderer.Flags = *Flags; + } + Result.push_back(std::move(Renderer)); + } + return true; + } + + [[nodiscard]] bool parseSelectionTemporaries(const yaml::Node &Node, + std::vector<SourceSelectionTemporary> &Result) { + if (!requireSequence(&Node, "selection temporaries")) + return false; + for (const yaml::NodeID ElementID : Node.sequence()) { + const yaml::Node *Element = Input.node(ElementID); + if (!requireMapping(Element, "selection temporary") || + !checkKeys(*Element, {"id", "type_from_operand", "type"}, "selection temporary")) { + return false; + } + const auto Symbol = requiredScalar(*Element, "id", "selection temporary"); + const yaml::Node *TypeFromOperandNode = optionalNode(*Element, "type_from_operand"); + const yaml::Node *TypeNode = optionalNode(*Element, "type"); + if (!Symbol) + return false; + if ((TypeFromOperandNode == nullptr) == (TypeNode == nullptr)) { + fail(Element->range(), + "selection temporary requires exactly one of type_from_operand and type"); + return false; + } + std::optional<std::uint32_t> TypeFromOperand; + std::optional<SourceSelectionTypePredicate> Type; + if (TypeFromOperandNode != nullptr) { + TypeFromOperand = + unsignedInteger32(*TypeFromOperandNode, "selection temporary type operand"); + if (!TypeFromOperand) + return false; + } else { + Type = parseSelectionTypePredicate(*TypeNode, "selection temporary type"); + if (!Type) + return false; + } + Result.push_back({ + .Symbol = *Symbol, + .TypeFromOperand = TypeFromOperand, + .Type = std::move(Type), + .Range = Element->range(), + }); + } + return true; + } + + [[nodiscard]] bool parseSelectionActions(const yaml::Node &Node, + std::vector<SourceSelectionAction> &Result) { + if (!requireSequence(&Node, "selection instructions")) + return false; + for (const yaml::NodeID ElementID : Node.sequence()) { + const yaml::Node *Element = Input.node(ElementID); + if (!requireMapping(Element, "selection instruction") || + !checkKeys(*Element, {"opcode", "root", "operands"}, "selection instruction")) { + return false; + } + const auto Opcode = requiredScalar(*Element, "opcode", "selection instruction"); + const yaml::Node *RootNode = requiredNode(*Element, "root", "selection instruction"); + const yaml::Node *OperandsNode = requiredNode(*Element, "operands", "selection instruction"); + if (!Opcode || RootNode == nullptr || OperandsNode == nullptr) + return false; + const auto ReplacesRoot = boolean(*RootNode, "selection instruction root"); + if (!ReplacesRoot) + return false; + std::vector<SourceSelectionOperandRenderer> Renderers; + if (!parseSelectionOperandRenderers(*OperandsNode, Renderers)) + return false; + Result.push_back({ + .ResultOpcodeSymbol = *Opcode, + .ReplacesRoot = *ReplacesRoot, + .OperandRenderers = std::move(Renderers), + .Range = Element->range(), + }); + } + return true; + } + + [[nodiscard]] bool parseSelectionRuleMatch(const yaml::Node &Node, SourceSelectionRule &Result) { + if (!requireMapping(&Node, "selection rule match") || + !checkKeys(Node, + {"opcode", "result_type", "integer_predicate", "floating_predicate", "memory", + "target_predicates", "operand_types", "operand_banks", "operand_classes", + "operand_immediates", "operand_def_constants", "def_use"}, + "selection rule match")) { + return false; + } + const auto Opcode = requiredScalar(Node, "opcode", "selection rule match"); + if (!Opcode) + return false; + Result.MatchOpcodeSymbol = *Opcode; + if (const yaml::Node *ResultType = optionalNode(Node, "result_type")) { + auto Parsed = parseSelectionTypePredicate(*ResultType, "selection result type predicate"); + if (!Parsed) + return false; + Result.ResultType = std::move(*Parsed); + } + if (const yaml::Node *Predicate = optionalNode(Node, "integer_predicate")) { + if (!parseSelectionIntegerPredicate(*Predicate, Result.IntegerPredicate)) + return false; + } + if (const yaml::Node *Predicate = optionalNode(Node, "floating_predicate")) { + if (!parseSelectionFloatingPredicate(*Predicate, Result.FloatingPredicate)) + return false; + } + if (const yaml::Node *Predicate = optionalNode(Node, "memory")) { + if (!parseSelectionMemoryPredicate(*Predicate, Result.MemoryPredicate)) + return false; + } + if (const yaml::Node *TargetPredicates = optionalNode(Node, "target_predicates")) { + if (!parseSelectionTargetPredicates(*TargetPredicates, Result.TargetPredicates)) + return false; + } + if (const yaml::Node *OperandTypes = optionalNode(Node, "operand_types")) { + if (!parseSelectionOperandTypes(*OperandTypes, Result.OperandTypes)) + return false; + } + if (const yaml::Node *OperandBanks = optionalNode(Node, "operand_banks")) { + if (!parseSelectionOperandBanks(*OperandBanks, Result.OperandBanks)) + return false; + } + if (const yaml::Node *OperandClasses = optionalNode(Node, "operand_classes")) { + if (!parseSelectionOperandClasses(*OperandClasses, Result.OperandClasses)) + return false; + } + if (const yaml::Node *OperandImmediates = optionalNode(Node, "operand_immediates")) { + if (!parseSelectionOperandImmediates(*OperandImmediates, Result.OperandImmediates)) + return false; + } + if (const yaml::Node *OperandDefConstants = optionalNode(Node, "operand_def_constants")) { + if (!parseSelectionOperandDefConstants(*OperandDefConstants, Result.OperandDefConstants)) + return false; + } + if (const yaml::Node *DefUse = optionalNode(Node, "def_use")) { + if (!parseSelectionDefUsePredicates(*DefUse, Result.DefUsePredicates)) + return false; + } + return true; + } + + [[nodiscard]] bool parseSelectionRuleApply(const yaml::Node &Node, SourceSelectionRule &Result) { + if (!requireMapping(&Node, "selection rule apply") || + !checkKeys(Node, {"temporaries", "instructions", "erase_operand_definitions"}, + "selection rule apply")) { + return false; + } + const yaml::Node *Instructions = requiredNode(Node, "instructions", "selection rule apply"); + if (Instructions == nullptr) + return false; + if (const yaml::Node *Temporaries = optionalNode(Node, "temporaries")) { + if (!parseSelectionTemporaries(*Temporaries, Result.Temporaries)) + return false; + } + if (!parseSelectionActions(*Instructions, Result.Actions)) + return false; + if (const yaml::Node *Erase = optionalNode(Node, "erase_operand_definitions")) { + auto Indices = unsignedInteger32Sequence(*Erase, "selection operand definitions"); + if (!Indices) + return false; + Result.EraseOperandDefinitions = std::move(*Indices); + } + return true; + } + + [[nodiscard]] bool parseSelectionRules(const yaml::Node &Node, + std::vector<SourceSelectionRule> &Result) { + if (!requireSequence(&Node, "selection_rules")) + return false; + for (const yaml::NodeID ElementID : Node.sequence()) { + const yaml::Node *Element = Input.node(ElementID); + if (!requireMapping(Element, "selection rule") || + !checkKeys(*Element, {"id", "priority", "match", "apply"}, "selection rule")) { + return false; + } + const auto Symbol = requiredScalar(*Element, "id", "selection rule"); + const yaml::Node *PriorityNode = requiredNode(*Element, "priority", "selection rule"); + const yaml::Node *MatchNode = requiredNode(*Element, "match", "selection rule"); + const yaml::Node *ApplyNode = requiredNode(*Element, "apply", "selection rule"); + if (!Symbol || PriorityNode == nullptr || MatchNode == nullptr || ApplyNode == nullptr) + return false; + const auto Priority = unsignedInteger32(*PriorityNode, "selection rule priority"); + if (!Priority) + return false; + SourceSelectionRule Rule; + Rule.Symbol = *Symbol; + Rule.Priority = *Priority; + Rule.Range = Element->range(); + if (!parseSelectionRuleMatch(*MatchNode, Rule) || + !parseSelectionRuleApply(*ApplyNode, Rule)) { + return false; + } + Result.push_back(std::move(Rule)); + } + return true; + } + + [[nodiscard]] bool + parseCombinerOperandRenderers(const yaml::Node &Node, + std::vector<SourceCombinerOperandRenderer> &Result) { + if (!requireSequence(&Node, "combiner operand renderers")) + return false; + for (const yaml::NodeID ElementID : Node.sequence()) { + const yaml::Node *Element = Input.node(ElementID); + if (!requireMapping(Element, "combiner operand renderer") || + !checkKeys(*Element, {"operand", "captured_operand", "captured_type_bits", "immediate"}, + "combiner operand renderer")) { + return false; + } + const yaml::Node *Operand = optionalNode(*Element, "operand"); + const yaml::Node *CapturedOperand = optionalNode(*Element, "captured_operand"); + const yaml::Node *CapturedTypeBits = optionalNode(*Element, "captured_type_bits"); + const yaml::Node *Immediate = optionalNode(*Element, "immediate"); + const std::uint32_t SourceCount = static_cast<std::uint32_t>(Operand != nullptr) + + static_cast<std::uint32_t>(CapturedOperand != nullptr) + + static_cast<std::uint32_t>(CapturedTypeBits != nullptr) + + static_cast<std::uint32_t>(Immediate != nullptr); + if (SourceCount != 1) { + fail(Element->range(), "combiner operand renderer requires exactly one source"); + return false; + } + SourceCombinerOperandRenderer Renderer{.SourceKind = + SourceCombinerOperandRenderer::Kind::RootOperand, + .OperandIndex = 0, + .CaptureSymbol = {}, + .Immediate = 0, + .Range = Element->range()}; + if (Operand != nullptr) { + const auto Index = unsignedInteger32(*Operand, "combiner operand renderer operand"); + if (!Index) + return false; + Renderer.OperandIndex = *Index; + } else if (CapturedOperand != nullptr) { + const auto Symbol = scalar(*CapturedOperand, "combiner operand renderer captured operand"); + if (!Symbol) + return false; + Renderer.SourceKind = SourceCombinerOperandRenderer::Kind::CapturedOperand; + Renderer.CaptureSymbol = *Symbol; + } else if (CapturedTypeBits != nullptr) { + const auto Symbol = + scalar(*CapturedTypeBits, "combiner operand renderer captured type bits"); + if (!Symbol) + return false; + Renderer.SourceKind = SourceCombinerOperandRenderer::Kind::CapturedTypeBits; + Renderer.CaptureSymbol = *Symbol; + } else { + const auto Value = signedInteger(*Immediate, "combiner operand renderer immediate"); + if (!Value) + return false; + Renderer.SourceKind = SourceCombinerOperandRenderer::Kind::Immediate; + Renderer.Immediate = *Value; + } + Result.push_back(std::move(Renderer)); + } + return true; + } + + [[nodiscard]] bool parseCombinerRuleMatch(const yaml::Node &Node, SourceCombinerRule &Result) { + if (!requireMapping(&Node, "combiner rule match") || + !checkKeys( + Node, + {"opcode", "result_type", "operand_types", "target_predicates", "flags", "def_use"}, + "combiner rule match")) { + return false; + } + const auto Opcode = requiredScalar(Node, "opcode", "combiner rule match"); + if (!Opcode) + return false; + Result.MatchOpcodeSymbol = *Opcode; + if (const yaml::Node *Type = optionalNode(Node, "result_type")) { + auto Parsed = parseSelectionTypePredicate(*Type, "combiner result type predicate"); + if (!Parsed) + return false; + Result.ResultType = std::move(*Parsed); + } + if (const yaml::Node *Types = optionalNode(Node, "operand_types")) { + if (!parseSelectionOperandTypes(*Types, Result.OperandTypes)) + return false; + } + if (const yaml::Node *Predicates = optionalNode(Node, "target_predicates")) { + if (!parseSelectionTargetPredicates(*Predicates, Result.TargetPredicates)) + return false; + } + if (const yaml::Node *Flags = optionalNode(Node, "flags")) { + const auto Parsed = scalarSequence(*Flags, "combiner instruction flags"); + if (!Parsed) + return false; + Result.InstructionFlags = *Parsed; + } + if (const yaml::Node *DefUse = optionalNode(Node, "def_use")) { + if (!parseSelectionDefUsePredicates(*DefUse, Result.DefUsePredicates)) + return false; + } + return true; + } + + [[nodiscard]] bool parseCombinerRuleApply(const yaml::Node &Node, SourceCombinerRule &Result) { + if (!requireMapping(&Node, "combiner rule apply") || + !checkKeys( + Node, + {"action", "capture", "opcode", "operands", "clear_flags", "erase_operand_definitions"}, + "combiner rule apply")) { + return false; + } + const auto Action = requiredScalar(Node, "action", "combiner rule apply"); + if (!Action) + return false; + if (*Action == "replace_root_with_capture") { + const auto Capture = requiredScalar(Node, "capture", "combiner rule apply"); + if (!Capture) + return false; + Result.Action.Kind = SourceCombinerActionKind::ReplaceRootWithCapturedOperand; + Result.Action.CapturedOperandSymbol = *Capture; + } else if (*Action == "mutate_root") { + const auto Opcode = requiredScalar(Node, "opcode", "combiner rule apply"); + const yaml::Node *Operands = requiredNode(Node, "operands", "combiner rule apply"); + if (!Opcode || Operands == nullptr) + return false; + Result.Action.Kind = SourceCombinerActionKind::MutateRoot; + Result.Action.ResultOpcodeSymbol = *Opcode; + if (!parseCombinerOperandRenderers(*Operands, Result.Action.OperandRenderers)) + return false; + } else { + fail(Node.range(), + "combiner rule apply action must be replace_root_with_capture or mutate_root"); + return false; + } + if (const yaml::Node *ClearFlags = optionalNode(Node, "clear_flags")) { + const auto Parsed = scalarSequence(*ClearFlags, "combiner action clear flags"); + if (!Parsed) + return false; + Result.Action.ClearInstructionFlags = *Parsed; + } + if (const yaml::Node *Erase = optionalNode(Node, "erase_operand_definitions")) { + auto Indices = unsignedInteger32Sequence(*Erase, "combiner operand definitions"); + if (!Indices) + return false; + Result.EraseOperandDefinitions = std::move(*Indices); + } + return true; + } + + [[nodiscard]] bool parseCombinerRules(const yaml::Node &Node, + std::vector<SourceCombinerRule> &Result) { + if (!requireSequence(&Node, "combiner_rules")) + return false; + for (const yaml::NodeID ElementID : Node.sequence()) { + const yaml::Node *Element = Input.node(ElementID); + if (!requireMapping(Element, "combiner rule") || + !checkKeys(*Element, {"id", "priority", "match", "apply"}, "combiner rule")) { + return false; + } + const auto Symbol = requiredScalar(*Element, "id", "combiner rule"); + const yaml::Node *PriorityNode = requiredNode(*Element, "priority", "combiner rule"); + const yaml::Node *MatchNode = requiredNode(*Element, "match", "combiner rule"); + const yaml::Node *ApplyNode = requiredNode(*Element, "apply", "combiner rule"); + if (!Symbol || PriorityNode == nullptr || MatchNode == nullptr || ApplyNode == nullptr) + return false; + const auto Priority = unsignedInteger32(*PriorityNode, "combiner rule priority"); + if (!Priority) + return false; + SourceCombinerRule Rule; + Rule.Symbol = *Symbol; + Rule.Priority = *Priority; + Rule.Range = Element->range(); + if (!parseCombinerRuleMatch(*MatchNode, Rule) || !parseCombinerRuleApply(*ApplyNode, Rule)) + return false; + Result.push_back(std::move(Rule)); + } + return true; + } + + [[nodiscard]] bool parseMacroFusionOpcodePairs(const yaml::Node &Node, + std::vector<SourceMacroFusionOpcodePair> &Result) { + if (!requireSequence(&Node, "macro fusion opcode pairs")) + return false; + for (const yaml::NodeID ElementID : Node.sequence()) { + const yaml::Node *Element = Input.node(ElementID); + if (!requireMapping(Element, "macro fusion opcode pair") || + !checkKeys(*Element, {"first", "second"}, "macro fusion opcode pair")) { + return false; + } + const auto First = requiredScalar(*Element, "first", "macro fusion opcode pair"); + const auto Second = requiredScalar(*Element, "second", "macro fusion opcode pair"); + if (!First || !Second) + return false; + Result.push_back({*First, *Second, Element->range()}); + } + return true; + } + + [[nodiscard]] bool + parseMacroFusionImmediatePredicates(const yaml::Node &Node, + std::vector<SourceMacroFusionImmediatePredicate> &Result) { + if (!requireSequence(&Node, "macro fusion immediate predicates")) + return false; + for (const yaml::NodeID ElementID : Node.sequence()) { + const yaml::Node *Element = Input.node(ElementID); + if (!requireMapping(Element, "macro fusion immediate predicate") || + !checkKeys(*Element, {"instruction", "operand", "min", "max"}, + "macro fusion immediate predicate")) { + return false; + } + const auto Instruction = + requiredScalar(*Element, "instruction", "macro fusion immediate predicate"); + const yaml::Node *OperandNode = + requiredNode(*Element, "operand", "macro fusion immediate predicate"); + const yaml::Node *MinimumNode = + requiredNode(*Element, "min", "macro fusion immediate predicate"); + const yaml::Node *MaximumNode = + requiredNode(*Element, "max", "macro fusion immediate predicate"); + if (!Instruction || OperandNode == nullptr || MinimumNode == nullptr || + MaximumNode == nullptr) { + return false; + } + const auto Operand = unsignedInteger32(*OperandNode, "macro fusion immediate operand"); + const auto Minimum = signedInteger(*MinimumNode, "macro fusion immediate minimum"); + const auto Maximum = signedInteger(*MaximumNode, "macro fusion immediate maximum"); + if (!Operand || !Minimum || !Maximum) + return false; + Result.push_back({*Instruction, *Operand, *Minimum, *Maximum, Element->range()}); + } + return true; + } + + [[nodiscard]] bool + parseMacroFusionRegisterInequalities(const yaml::Node &Node, + std::vector<SourceMacroFusionRegisterInequality> &Result) { + if (!requireSequence(&Node, "macro fusion register inequalities")) + return false; + for (const yaml::NodeID ElementID : Node.sequence()) { + const yaml::Node *Element = Input.node(ElementID); + if (!requireMapping(Element, "macro fusion register inequality") || + !checkKeys(*Element, {"instruction", "left", "right"}, + "macro fusion register inequality")) { + return false; + } + const auto Instruction = + requiredScalar(*Element, "instruction", "macro fusion register inequality"); + const yaml::Node *LeftNode = + requiredNode(*Element, "left", "macro fusion register inequality"); + const yaml::Node *RightNode = + requiredNode(*Element, "right", "macro fusion register inequality"); + if (!Instruction || LeftNode == nullptr || RightNode == nullptr) + return false; + const auto Left = unsignedInteger32(*LeftNode, "macro fusion left register operand"); + const auto Right = unsignedInteger32(*RightNode, "macro fusion right register operand"); + if (!Left || !Right) + return false; + Result.push_back({*Instruction, *Left, *Right, Element->range()}); + } + return true; + } + + [[nodiscard]] bool + parseMacroFusionImmediateRelations(const yaml::Node &Node, + std::vector<SourceMacroFusionImmediateRelation> &Result) { + if (!requireSequence(&Node, "macro fusion immediate relations")) + return false; + for (const yaml::NodeID ElementID : Node.sequence()) { + const yaml::Node *Element = Input.node(ElementID); + if (!requireMapping(Element, "macro fusion immediate relation") || + !checkKeys(*Element, {"kind", "first_operand", "second_operand"}, + "macro fusion immediate relation")) { + return false; + } + const auto Kind = requiredScalar(*Element, "kind", "macro fusion immediate relation"); + const yaml::Node *FirstNode = + requiredNode(*Element, "first_operand", "macro fusion immediate relation"); + const yaml::Node *SecondNode = + requiredNode(*Element, "second_operand", "macro fusion immediate relation"); + if (!Kind || FirstNode == nullptr || SecondNode == nullptr) + return false; + const auto First = unsignedInteger32(*FirstNode, "macro fusion first immediate operand"); + const auto Second = unsignedInteger32(*SecondNode, "macro fusion second immediate operand"); + if (!First || !Second) + return false; + Result.push_back({*Kind, *First, *Second, Element->range()}); + } + return true; + } + + [[nodiscard]] bool parseMacroFusionLink(const yaml::Node &Node, SourceMacroFusionRule &Result) { + if (!requireMapping(&Node, "macro fusion link") || + !checkKeys(Node, + {"first_definition", "second_use", "second_definition", "commutable_second_use", + "one_non_debug_use", "second_destination_tied"}, + "macro fusion link")) { + return false; + } + const yaml::Node *FirstNode = requiredNode(Node, "first_definition", "macro fusion link"); + const yaml::Node *SecondUseNode = requiredNode(Node, "second_use", "macro fusion link"); + const yaml::Node *SecondDefinitionNode = + requiredNode(Node, "second_definition", "macro fusion link"); + const yaml::Node *SingleUseNode = requiredNode(Node, "one_non_debug_use", "macro fusion link"); + const yaml::Node *TiedNode = requiredNode(Node, "second_destination_tied", "macro fusion link"); + if (FirstNode == nullptr || SecondUseNode == nullptr || SecondDefinitionNode == nullptr || + SingleUseNode == nullptr || TiedNode == nullptr) { + return false; + } + const auto First = unsignedInteger32(*FirstNode, "macro fusion first definition"); + const auto SecondUse = unsignedInteger32(*SecondUseNode, "macro fusion second use"); + const auto SecondDefinition = + unsignedInteger32(*SecondDefinitionNode, "macro fusion second definition"); + const auto SingleUse = boolean(*SingleUseNode, "macro fusion one_non_debug_use"); + const auto Tied = boolean(*TiedNode, "macro fusion second_destination_tied"); + if (!First || !SecondUse || !SecondDefinition || !SingleUse || !Tied) + return false; + Result.FirstDefinitionOperand = *First; + Result.SecondUseOperand = *SecondUse; + Result.SecondDefinitionOperand = *SecondDefinition; + Result.RequireSingleNonDebugUse = *SingleUse; + Result.RequireSecondDestinationTie = *Tied; + if (const yaml::Node *Commutable = optionalNode(Node, "commutable_second_use")) { + const auto Value = unsignedInteger32(*Commutable, "macro fusion commutable second use"); + if (!Value) + return false; + Result.CommutableSecondUseOperand = Value; + } + return true; + } + + [[nodiscard]] bool parseMacroFusionRules(const yaml::Node &Node, + std::vector<SourceMacroFusionRule> &Result) { + if (!requireSequence(&Node, "macro_fusion_rules")) + return false; + for (const yaml::NodeID ElementID : Node.sequence()) { + const yaml::Node *Element = Input.node(ElementID); + if (!requireMapping(Element, "macro fusion rule") || + !checkKeys(*Element, + {"id", "feature", "first_opcodes", "second_opcodes", "opcode_pairs", + "immediates", "register_not_equal", "immediate_relations", "link"}, + "macro fusion rule")) { + return false; + } + const auto Symbol = requiredScalar(*Element, "id", "macro fusion rule"); + const auto Feature = requiredScalar(*Element, "feature", "macro fusion rule"); + const yaml::Node *FirstNode = requiredNode(*Element, "first_opcodes", "macro fusion rule"); + const yaml::Node *SecondNode = requiredNode(*Element, "second_opcodes", "macro fusion rule"); + const yaml::Node *LinkNode = requiredNode(*Element, "link", "macro fusion rule"); + if (!Symbol || !Feature || FirstNode == nullptr || SecondNode == nullptr || + LinkNode == nullptr) { + return false; + } + auto FirstOpcodes = scalarSequence(*FirstNode, "macro fusion first opcodes"); + auto SecondOpcodes = scalarSequence(*SecondNode, "macro fusion second opcodes"); + if (!FirstOpcodes || !SecondOpcodes) + return false; + SourceMacroFusionRule Rule; + Rule.Symbol = *Symbol; + Rule.Feature = *Feature; + Rule.FirstOpcodeSymbols = std::move(*FirstOpcodes); + Rule.SecondOpcodeSymbols = std::move(*SecondOpcodes); + Rule.Range = Element->range(); + if (!parseMacroFusionLink(*LinkNode, Rule)) + return false; + if (const yaml::Node *Pairs = optionalNode(*Element, "opcode_pairs")) { + if (!parseMacroFusionOpcodePairs(*Pairs, Rule.OpcodePairs)) + return false; + } + if (const yaml::Node *Immediates = optionalNode(*Element, "immediates")) { + if (!parseMacroFusionImmediatePredicates(*Immediates, Rule.ImmediatePredicates)) + return false; + } + if (const yaml::Node *Inequalities = optionalNode(*Element, "register_not_equal")) { + if (!parseMacroFusionRegisterInequalities(*Inequalities, Rule.RegisterInequalities)) + return false; + } + if (const yaml::Node *Relations = optionalNode(*Element, "immediate_relations")) { + if (!parseMacroFusionImmediateRelations(*Relations, Rule.ImmediateRelations)) + return false; + } + Result.push_back(std::move(Rule)); + } + return true; + } + + [[nodiscard]] bool parseMCPseudoOperands(const yaml::Node &Node, + std::vector<SourceMCPseudoOperand> &Result) { + if (!requireSequence(&Node, "MC pseudo expansion operands")) + return false; + for (const yaml::NodeID ElementID : Node.sequence()) { + const yaml::Node *Element = Input.node(ElementID); + if (!requireMapping(Element, "MC pseudo expansion operand") || + !checkKeys(*Element, {"operand", "register", "immediate"}, + "MC pseudo expansion operand")) { + return false; + } + SourceMCPseudoOperand Operand; + Operand.Range = Element->range(); + if (const yaml::Node *SourceNode = optionalNode(*Element, "operand")) { + const auto Source = unsignedInteger32(*SourceNode, "MC pseudo source operand"); + if (!Source) + return false; + Operand.SourceOperandIndex = Source; + } + if (const yaml::Node *RegisterNode = optionalNode(*Element, "register")) { + const auto Register = scalar(*RegisterNode, "MC pseudo fixed register"); + if (!Register) + return false; + Operand.RegisterSymbol = *Register; + } + if (const yaml::Node *ImmediateNode = optionalNode(*Element, "immediate")) { + const auto Immediate = signedInteger(*ImmediateNode, "MC pseudo fixed immediate"); + if (!Immediate) + return false; + Operand.Immediate = Immediate; + } + Result.push_back(std::move(Operand)); + } + return true; + } + + [[nodiscard]] bool parseMCPseudoExpansions(const yaml::Node &Node, + std::vector<SourceMCPseudoExpansion> &Result) { + if (!requireSequence(&Node, "mc_pseudo_expansions")) + return false; + for (const yaml::NodeID ElementID : Node.sequence()) { + const yaml::Node *Element = Input.node(ElementID); + if (!requireMapping(Element, "MC pseudo expansion") || + !checkKeys(*Element, {"pseudo", "opcode", "operands"}, "MC pseudo expansion")) { + return false; + } + const auto Pseudo = requiredScalar(*Element, "pseudo", "MC pseudo expansion"); + const auto Opcode = requiredScalar(*Element, "opcode", "MC pseudo expansion"); + const yaml::Node *OperandsNode = requiredNode(*Element, "operands", "MC pseudo expansion"); + if (!Pseudo || !Opcode || OperandsNode == nullptr) + return false; + SourceMCPseudoExpansion Expansion; + Expansion.PseudoOpcodeSymbol = *Pseudo; + Expansion.ResultOpcodeSymbol = *Opcode; + Expansion.Range = Element->range(); + if (!parseMCPseudoOperands(*OperandsNode, Expansion.Operands)) + return false; + Result.push_back(std::move(Expansion)); + } + return true; + } + + [[nodiscard]] bool parseInstructions(const yaml::Node &Node, + std::vector<SourceInstruction> &Result) { + if (!requireSequence(&Node, "instructions")) + return false; + for (const yaml::NodeID ElementID : Node.sequence()) { + const yaml::Node *Element = Input.node(ElementID); + if (!requireMapping(Element, "instruction")) + return false; + if (!checkKeys(*Element, + {"id", "symbol", "name", "assembly_mnemonic", "encoding", "operands", + "num_explicit_defs", "variadic", "implicit_defs", "implicit_uses", + "allow_extra_implicit_operands", "flags", "scheduling_class", + "encoding_category", "size", "assembly_format", "requires_memory_operands", + "owner", "deadline"}, + "instruction")) { + return false; + } + const yaml::Node *IDNode = requiredNode(*Element, "id", "instruction"); + const auto Symbol = requiredScalar(*Element, "symbol", "instruction"); + const auto Name = requiredScalar(*Element, "name", "instruction"); + const yaml::Node *OperandsNode = requiredNode(*Element, "operands", "instruction"); + const yaml::Node *DefsNode = requiredNode(*Element, "num_explicit_defs", "instruction"); + const yaml::Node *FlagsNode = requiredNode(*Element, "flags", "instruction"); + if (IDNode == nullptr || !Symbol || !Name || OperandsNode == nullptr || DefsNode == nullptr || + FlagsNode == nullptr) { + return false; + } + const auto ID = unsignedInteger32(*IDNode, "instruction id"); + const auto Defs = unsignedInteger32(*DefsNode, "instruction num_explicit_defs"); + const auto Operands = parseOperands(*OperandsNode); + const auto Flags = scalarSequence(*FlagsNode, "instruction flags"); + if (!ID || !Defs || !Operands || !Flags) + return false; + SourceInstruction Instruction; + Instruction.ID = *ID; + Instruction.Symbol = *Symbol; + Instruction.Name = *Name; + Instruction.Operands = *Operands; + Instruction.NumExplicitDefs = *Defs; + Instruction.Flags = *Flags; + Instruction.Range = Element->range(); + if (const yaml::Node *Mnemonic = optionalNode(*Element, "assembly_mnemonic")) { + const auto Value = scalar(*Mnemonic, "instruction assembly_mnemonic"); + if (!Value) + return false; + Instruction.AssemblyMnemonic = *Value; + } + if (const yaml::Node *Encoding = optionalNode(*Element, "encoding")) { + const auto Value = unsignedInteger32(*Encoding, "instruction encoding"); + if (!Value) + return false; + Instruction.BinaryEncoding = Value; + } + if (const yaml::Node *SchedulingClass = optionalNode(*Element, "scheduling_class")) { + const auto Value = scalar(*SchedulingClass, "instruction scheduling_class"); + if (!Value) + return false; + Instruction.SchedulingClassSymbol = *Value; + } + if (const yaml::Node *EncodingCategory = optionalNode(*Element, "encoding_category")) { + const auto Value = scalar(*EncodingCategory, "instruction encoding_category"); + if (!Value) + return false; + Instruction.EncodingCategorySymbol = *Value; + } + if (const yaml::Node *Size = optionalNode(*Element, "size")) { + const auto Value = unsignedInteger32(*Size, "instruction size"); + if (!Value) + return false; + Instruction.SizeInBytes = *Value; + } + if (const yaml::Node *AssemblyFormat = optionalNode(*Element, "assembly_format")) { + const auto Value = scalar(*AssemblyFormat, "instruction assembly_format"); + if (!Value) + return false; + Instruction.AssemblyFormatSymbol = *Value; + } + if (const yaml::Node *Variadic = optionalNode(*Element, "variadic")) { + const auto Value = boolean(*Variadic, "instruction variadic"); + if (!Value) + return false; + Instruction.Variadic = *Value; + } + if (const yaml::Node *ImplicitDefs = optionalNode(*Element, "implicit_defs")) { + const auto Values = parseImplicitRegisters(*ImplicitDefs, "instruction implicit_defs"); + if (!Values) + return false; + Instruction.ImplicitDefs = *Values; + } + if (const yaml::Node *ImplicitUses = optionalNode(*Element, "implicit_uses")) { + const auto Values = parseImplicitRegisters(*ImplicitUses, "instruction implicit_uses"); + if (!Values) + return false; + Instruction.ImplicitUses = *Values; + } + if (const yaml::Node *AllowExtra = optionalNode(*Element, "allow_extra_implicit_operands")) { + const auto Value = boolean(*AllowExtra, "instruction allow_extra_implicit_operands"); + if (!Value) + return false; + Instruction.AllowExtraImplicitOperands = *Value; + } + if (const yaml::Node *MemoryOperands = optionalNode(*Element, "requires_memory_operands")) { + const auto Value = boolean(*MemoryOperands, "instruction requires_memory_operands"); + if (!Value) + return false; + Instruction.RequiresMemoryOperands = *Value; + } + if (const yaml::Node *Owner = optionalNode(*Element, "owner")) { + const auto Value = scalar(*Owner, "instruction owner"); + if (!Value) + return false; + Instruction.Owner = *Value; + } + if (const yaml::Node *Deadline = optionalNode(*Element, "deadline")) { + const auto Value = scalar(*Deadline, "instruction deadline"); + if (!Value) + return false; + Instruction.Deadline = *Value; + } + Result.push_back(std::move(Instruction)); + } + return true; + } + + [[nodiscard]] const yaml::Node *optionalNode(const yaml::Node &Mapping, + std::string_view Key) const noexcept { + const std::optional<yaml::NodeID> Value = Mapping.mappingValue(Key); + return Value ? Input.node(*Value) : nullptr; + } + + const yaml::Stream &Input; + std::vector<TextDiagnostic> Diagnostics; +}; + +} // namespace + +DescriptionParseResult parseDescription(std::string SourceName, std::string SourceText) { + yaml::ParseResult YAMLResult = yaml::parse(std::move(SourceName), std::move(SourceText)); + if (!YAMLResult.succeeded()) { + DescriptionParseResult Result; + Result.Diagnostics.assign(YAMLResult.diagnostics().begin(), YAMLResult.diagnostics().end()); + return Result; + } + + ASTParser Parser(*YAMLResult.stream()); + auto Records = Parser.parse(); + if (!Records) + return {.Records = std::nullopt, .Diagnostics = Parser.takeDiagnostics()}; + return {.Records = std::move(Records), .Diagnostics = {}}; +} + +} // namespace rcc::targetgen diff --git a/backend/lib/TableGen/Record.cpp b/backend/lib/TableGen/Record.cpp new file mode 100644 index 0000000..c8981c6 --- /dev/null +++ b/backend/lib/TableGen/Record.cpp @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "rcc/TableGen/Record.h" + +#include <algorithm> +#include <cctype> +#include <ranges> + +namespace rcc::targetgen { + +[[nodiscard]] std::string lowerASCII(std::string_view Value) { + std::string Result(Value); + for (char &Character : Result) { + if (Character >= 'A' && Character <= 'Z') + Character = static_cast<char>(Character - 'A' + 'a'); + } + return Result; +} + +[[nodiscard]] bool isIdentifier(std::string_view Value) noexcept { + if (Value.empty()) + return false; + const auto IsHead = [](unsigned char Character) { + return std::isalpha(Character) != 0 || Character == '_'; + }; + const auto IsTail = [](unsigned char Character) { + return std::isalnum(Character) != 0 || Character == '_'; + }; + if (!IsHead(static_cast<unsigned char>(Value.front()))) + return false; + return std::ranges::all_of(Value.substr(1), [IsTail](char Character) { + return IsTail(static_cast<unsigned char>(Character)); + }); +} + +[[nodiscard]] bool isAssemblyMnemonic(std::string_view Value) noexcept { + if (Value.empty()) + return false; + if (std::islower(static_cast<unsigned char>(Value.front())) == 0) + return false; + if (std::islower(static_cast<unsigned char>(Value.back())) == 0 && + std::isdigit(static_cast<unsigned char>(Value.back())) == 0) + return false; + return std::ranges::all_of(Value, [](char Character) { + const unsigned char Code = static_cast<unsigned char>(Character); + return std::islower(Code) != 0 || std::isdigit(Code) != 0 || Character == '.' || + Character == '_'; + }); +} + +[[nodiscard]] std::string upperIdentifier(std::string_view Value) { + std::string Result; + Result.reserve(Value.size()); + for (const char Character : Value) { + if (Character >= 'a' && Character <= 'z') + Result.push_back(static_cast<char>(Character - 'a' + 'A')); + else if ((Character >= 'A' && Character <= 'Z') || (Character >= '0' && Character <= '9')) + Result.push_back(Character); + else + Result.push_back('_'); + } + return Result; +} + +void appendQuoted(std::ostream &Output, std::string_view Value) { + Output << '"'; + for (const char Character : Value) { + switch (Character) { + case '\\': + Output << "\\\\"; + break; + case '"': + Output << "\\\""; + break; + case '\n': + Output << "\\n"; + break; + case '\r': + Output << "\\r"; + break; + case '\t': + Output << "\\t"; + break; + default: + Output << Character; + break; + } + } + Output << '"'; +} + +} // namespace rcc::targetgen |
