diff options
| author | Richard-Qin-X <richard.qin.001@gmail.com> | 2026-08-16 15:39:45 +0800 |
|---|---|---|
| committer | Richard-Qin-X <richard.qin.001@gmail.com> | 2026-08-16 15:39:45 +0800 |
| commit | 4f7b41c3c711d6c6fa4e422f3eec8961c91eea7a (patch) | |
| tree | a825927a4a3ef91ec6e3489aa1f96e63788ff476 /backend/utils/TableGen | |
| parent | a39d8f6f4ed2ad0f8435b73bd6b2a35769c6fead (diff) | |
| download | rcc-project-4f7b41c3c711d6c6fa4e422f3eec8961c91eea7a.tar.gz rcc-project-4f7b41c3c711d6c6fa4e422f3eec8961c91eea7a.tar.xz | |
tablegen: Generate GlobalISel match tables
Diffstat (limited to 'backend/utils/TableGen')
| -rw-r--r-- | backend/utils/TableGen/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | backend/utils/TableGen/Common/CodeGenSelection.cpp | 183 | ||||
| -rw-r--r-- | backend/utils/TableGen/Common/CodeGenTarget.cpp | 4 | ||||
| -rw-r--r-- | backend/utils/TableGen/Common/CodeGenTarget.h | 41 | ||||
| -rw-r--r-- | backend/utils/TableGen/GlobalISelEmitter.cpp | 96 | ||||
| -rw-r--r-- | backend/utils/TableGen/TableGenBackends.h | 3 | ||||
| -rw-r--r-- | backend/utils/TableGen/rcc-tblgen.cpp | 160 |
7 files changed, 469 insertions, 20 deletions
diff --git a/backend/utils/TableGen/CMakeLists.txt b/backend/utils/TableGen/CMakeLists.txt index 45b4bff..60fc769 100644 --- a/backend/utils/TableGen/CMakeLists.txt +++ b/backend/utils/TableGen/CMakeLists.txt @@ -5,9 +5,11 @@ set( AsmWriterEmitter.cpp CallingConvEmitter.cpp CodeEmitterGen.cpp + GlobalISelEmitter.cpp Common/CodeGenInstruction.cpp Common/CodeGenRegisters.cpp Common/CodeGenSchedule.cpp + Common/CodeGenSelection.cpp Common/CodeGenTarget.cpp InstrInfoEmitter.cpp RegisterBankEmitter.cpp diff --git a/backend/utils/TableGen/Common/CodeGenSelection.cpp b/backend/utils/TableGen/Common/CodeGenSelection.cpp new file mode 100644 index 0000000..0216136 --- /dev/null +++ b/backend/utils/TableGen/Common/CodeGenSelection.cpp @@ -0,0 +1,183 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "CodeGenTarget.h" +#include "rcc/CodeGen/GenericOpcodes.h" +#include "rcc/CodeGen/LowLevelType.h" + +#include <algorithm> +#include <cstdint> +#include <optional> +#include <ranges> +#include <string_view> +#include <unordered_set> +#include <utility> + +namespace rcc::targetgen { +namespace { + +[[nodiscard]] std::optional<std::uint32_t> genericOpcodeValue(std::string_view Symbol) { +#define RCC_CORE_OPCODE(Name) \ + if (Symbol == #Name) \ + return static_cast<std::uint32_t>(GenericOpcode::Name); +#define RCC_GENERIC_OPCODE(Name) RCC_CORE_OPCODE(Name) +#include "rcc/CodeGen/GenericOpcode.def" +#undef RCC_GENERIC_OPCODE +#undef RCC_CORE_OPCODE + return std::nullopt; +} + +[[nodiscard]] const NormalizedInstruction * +findInstructionBySymbol(const NormalizedTargetModel &Model, std::string_view Symbol) { + const auto Position = + std::ranges::find(Model.Instructions, Symbol, &NormalizedInstruction::Symbol); + return Position == Model.Instructions.end() ? nullptr : &*Position; +} + +[[nodiscard]] std::optional<std::uint32_t> resolveOpcode(const NormalizedTargetModel &Model, + std::string_view Symbol) { + if (const NormalizedInstruction *Instruction = findInstructionBySymbol(Model, Symbol)) + return Instruction->ID; + return genericOpcodeValue(Symbol); +} + +[[nodiscard]] bool isRegisterOperand(const NormalizedInstruction &Instruction, + std::uint32_t Index) { + return Index < Instruction.Operands.size() && + std::ranges::contains(Instruction.Operands[Index].Kinds, "Register"); +} + +} // namespace + +bool CodeGenTarget::normalizeSelectionRules(NormalizedTargetModel &Result) { + std::unordered_set<std::string> Symbols; + for (const SourceSelectionRule &Rule : Input.SelectionRules) { + const auto MatchOpcode = resolveOpcode(Result, Rule.MatchOpcodeSymbol); + const auto ResultOpcode = resolveOpcode(Result, Rule.ResultOpcodeSymbol); + const NormalizedInstruction *MatchInstruction = + findInstructionBySymbol(Result, Rule.MatchOpcodeSymbol); + const NormalizedInstruction *ResultInstruction = + findInstructionBySymbol(Result, Rule.ResultOpcodeSymbol); + if (!isIdentifier(Rule.Symbol) || !Symbols.insert(Rule.Symbol).second || !MatchOpcode || + !ResultOpcode) { + fail(Rule.Range, + "selection rules require a unique identifier and declared match/result opcodes"); + return false; + } + if (MatchInstruction != nullptr && !std::ranges::contains(MatchInstruction->Flags, "Generic")) { + fail(Rule.Range, "selection rule match opcode must be generic"); + return false; + } + if (isGenericOpcode(Opcode::fromValue(*ResultOpcode)) || + (ResultInstruction != nullptr && + std::ranges::contains(ResultInstruction->Flags, "Generic"))) { + fail(Rule.Range, "selection rule result opcode must not be generic"); + return false; + } + if (MatchInstruction != nullptr && ResultInstruction != nullptr && + ResultInstruction->Operands.size() != + MatchInstruction->Operands.size() + Rule.AppendedImmediates.size()) { + fail(Rule.Range, "selection rule result opcode has an incompatible operand count"); + return false; + } + + const auto NormalizeType = [this](const SourceSelectionTypePredicate &Source) + -> std::optional<NormalizedSelectionTypePredicate> { + NormalizedSelectionTypeKind Kind = NormalizedSelectionTypeKind::Any; + if (Source.Kind == "scalar") + Kind = NormalizedSelectionTypeKind::Scalar; + else if (Source.Kind == "pointer") + Kind = NormalizedSelectionTypeKind::Pointer; + else if (Source.Kind != "any") { + fail(Source.Range, "selection type kind must be 'any', 'scalar', or 'pointer'"); + return std::nullopt; + } + if (Source.Bits > LowLevelType::MaxBitWidth || + (Kind != NormalizedSelectionTypeKind::Any && Source.Bits == 0)) { + fail(Source.Range, "selection type bit width is invalid for its kind"); + return std::nullopt; + } + return NormalizedSelectionTypePredicate{.Kind = Kind, .Bits = Source.Bits}; + }; + + NormalizedSelectionRule Normalized; + Normalized.Symbol = Rule.Symbol; + Normalized.Priority = Rule.Priority; + Normalized.Specificity = 1; + Normalized.MatchOpcode = *MatchOpcode; + Normalized.ResultOpcode = *ResultOpcode; + Normalized.AppendedImmediates = Rule.AppendedImmediates; + if (Rule.ResultType) { + auto Type = NormalizeType(*Rule.ResultType); + if (!Type) + return false; + if (MatchInstruction != nullptr && !isRegisterOperand(*MatchInstruction, 0)) { + fail(Rule.ResultType->Range, + "selection result type predicate requires register operand zero"); + return false; + } + Normalized.ResultType = *Type; + ++Normalized.Specificity; + } + + std::unordered_set<std::uint32_t> TypeOperands; + for (const SourceSelectionOperandTypePredicate &Predicate : Rule.OperandTypes) { + auto Type = NormalizeType(Predicate.Type); + if (!Type) + return false; + if (!TypeOperands.insert(Predicate.OperandIndex).second || + (MatchInstruction != nullptr && + !isRegisterOperand(*MatchInstruction, Predicate.OperandIndex))) { + fail(Predicate.Range, + "selection operand type predicates require distinct register operands"); + return false; + } + Normalized.OperandTypes.push_back({.OperandIndex = Predicate.OperandIndex, .Type = *Type}); + ++Normalized.Specificity; + } + + std::unordered_set<std::uint32_t> BankOperands; + for (const SourceSelectionOperandBankPredicate &Predicate : Rule.OperandBanks) { + const auto Bank = BankBySymbol.find(Predicate.BankSymbol); + if (!BankOperands.insert(Predicate.OperandIndex).second || Bank == BankBySymbol.end() || + (MatchInstruction != nullptr && + !isRegisterOperand(*MatchInstruction, Predicate.OperandIndex))) { + fail(Predicate.Range, + "selection operand bank predicates require distinct register operands and declared " + "banks"); + return false; + } + Normalized.OperandBanks.push_back( + {.OperandIndex = Predicate.OperandIndex, .Bank = Bank->second}); + ++Normalized.Specificity; + } + + std::unordered_set<std::uint32_t> ClassOperands; + for (const SourceSelectionOperandClassPredicate &Predicate : Rule.OperandClasses) { + const auto RegClass = ClassBySymbol.find(Predicate.ClassSymbol); + if (!ClassOperands.insert(Predicate.OperandIndex).second || RegClass == ClassBySymbol.end() || + (MatchInstruction != nullptr && + !isRegisterOperand(*MatchInstruction, Predicate.OperandIndex))) { + fail(Predicate.Range, + "selection operand class predicates require distinct register operands and declared " + "classes"); + return false; + } + Normalized.OperandClasses.push_back( + {.OperandIndex = Predicate.OperandIndex, .RegClass = RegClass->second}); + ++Normalized.Specificity; + } + Result.SelectionRules.push_back(std::move(Normalized)); + } + + std::ranges::sort(Result.SelectionRules, + [](const NormalizedSelectionRule &Left, const NormalizedSelectionRule &Right) { + if (Left.Priority != Right.Priority) + return Left.Priority > Right.Priority; + if (Left.Specificity != Right.Specificity) + return Left.Specificity > Right.Specificity; + return Left.Symbol < Right.Symbol; + }); + return true; +} + +} // namespace rcc::targetgen diff --git a/backend/utils/TableGen/Common/CodeGenTarget.cpp b/backend/utils/TableGen/Common/CodeGenTarget.cpp index 89fea08..6d2e47e 100644 --- a/backend/utils/TableGen/Common/CodeGenTarget.cpp +++ b/backend/utils/TableGen/Common/CodeGenTarget.cpp @@ -35,7 +35,8 @@ CodeGenTarget::CodeGenTarget(const DescriptionAST &Input, std::string SourceName !normalizeMIROperandFlags(Result) || !normalizeCallingConventions(Result) || !normalizeNamedOperands(Result) || !normalizeImmediatePredicates(Result) || !normalizeSchedulingClasses(Result) || !normalizeEncodingCategories(Result) || - !normalizeAssemblyFormats(Result) || !normalizeInstructions(Result)) { + !normalizeAssemblyFormats(Result) || !normalizeInstructions(Result) || + !normalizeSelectionRules(Result)) { return std::nullopt; } std::ranges::sort(Result.RegisterUnits, {}, &SourceRegisterUnit::ID); @@ -214,6 +215,7 @@ DescriptionGenerationResult generateDescription(const std::string &SourceName, emitSubtarget(*Model) + emitCodeEmitter(*Model) + emitAssemblyWriter(*Model) + emitSearchableTables(*Model) + emitCallingConventions(*Model), + .GlobalISel = emitGlobalISel(*Model, DisplayName), }, .Diagnostics = {}, }; diff --git a/backend/utils/TableGen/Common/CodeGenTarget.h b/backend/utils/TableGen/Common/CodeGenTarget.h index cbddd4a..929d3e7 100644 --- a/backend/utils/TableGen/Common/CodeGenTarget.h +++ b/backend/utils/TableGen/Common/CodeGenTarget.h @@ -163,6 +163,45 @@ struct NormalizedInstruction final { std::string Deadline; }; +enum class NormalizedSelectionTypeKind : std::uint8_t { + Any, + Scalar, + Pointer, +}; + +struct NormalizedSelectionTypePredicate final { + NormalizedSelectionTypeKind Kind = NormalizedSelectionTypeKind::Any; + std::uint32_t Bits = 0; +}; + +struct NormalizedSelectionOperandTypePredicate final { + std::uint32_t OperandIndex = 0; + NormalizedSelectionTypePredicate Type; +}; + +struct NormalizedSelectionOperandBankPredicate final { + std::uint32_t OperandIndex = 0; + std::uint32_t Bank = 0; +}; + +struct NormalizedSelectionOperandClassPredicate final { + std::uint32_t OperandIndex = 0; + std::uint32_t RegClass = 0; +}; + +struct NormalizedSelectionRule final { + std::string Symbol; + std::uint32_t Priority = 0; + std::uint32_t Specificity = 0; + std::uint32_t MatchOpcode = 0; + std::optional<NormalizedSelectionTypePredicate> ResultType; + std::vector<NormalizedSelectionOperandTypePredicate> OperandTypes; + std::vector<NormalizedSelectionOperandBankPredicate> OperandBanks; + std::vector<NormalizedSelectionOperandClassPredicate> OperandClasses; + std::uint32_t ResultOpcode = 0; + std::vector<std::int64_t> AppendedImmediates; +}; + struct NormalizedTargetModel final { SourceTarget Target; std::vector<SourceRegisterUnit> RegisterUnits; @@ -178,6 +217,7 @@ struct NormalizedTargetModel final { std::vector<NormalizedEncodingCategory> EncodingCategories; std::vector<NormalizedAssemblyFormat> AssemblyFormats; std::vector<NormalizedInstruction> Instructions; + std::vector<NormalizedSelectionRule> SelectionRules; }; [[nodiscard]] std::optional<std::string_view> @@ -219,6 +259,7 @@ private: normalizeImplicitRegisters(const std::vector<SourceImplicitRegister> &InputRegisters, bool IsDef, std::vector<NormalizedImplicitRegister> &Result); [[nodiscard]] bool normalizeInstructions(NormalizedTargetModel &Result); + [[nodiscard]] bool normalizeSelectionRules(NormalizedTargetModel &Result); const DescriptionAST &Input; std::string SourceName; diff --git a/backend/utils/TableGen/GlobalISelEmitter.cpp b/backend/utils/TableGen/GlobalISelEmitter.cpp new file mode 100644 index 0000000..1634eb3 --- /dev/null +++ b/backend/utils/TableGen/GlobalISelEmitter.cpp @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "TableGenBackends.h" + +#include <cstddef> +#include <cstdint> +#include <ostream> +#include <sstream> +#include <string> +#include <string_view> + +namespace rcc::targetgen { +namespace { + +[[nodiscard]] std::string_view typeKindName(NormalizedSelectionTypeKind Kind) noexcept { + switch (Kind) { + case NormalizedSelectionTypeKind::Any: + return "Any"; + case NormalizedSelectionTypeKind::Scalar: + return "Scalar"; + case NormalizedSelectionTypeKind::Pointer: + return "Pointer"; + } + return "Any"; +} + +void emitInstruction(std::ostream &Output, std::string_view Opcode, std::uint32_t OperandIndex, + std::uint32_t Value = 0, std::string_view Auxiliary = "0", + std::int64_t Immediate = 0) { + Output << " {GIMatchTableOpcode::" << Opcode << ", " << OperandIndex << ", " << Value << ", " + << Auxiliary << ", " << Immediate << "},\n"; +} + +void emitTypePredicate(std::ostream &Output, std::uint32_t OperandIndex, + const NormalizedSelectionTypePredicate &Predicate) { + emitInstruction(Output, "CheckOperandType", OperandIndex, Predicate.Bits, + "static_cast<std::uint32_t>(GIMatchTypeKind::" + + std::string(typeKindName(Predicate.Kind)) + ")"); +} + +[[nodiscard]] std::size_t instructionCount(const NormalizedSelectionRule &Rule) noexcept { + return 3U + (Rule.ResultType ? 1U : 0U) + Rule.OperandTypes.size() + Rule.OperandBanks.size() + + Rule.OperandClasses.size() + Rule.AppendedImmediates.size(); +} + +} // namespace + +std::string emitGlobalISel(const NormalizedTargetModel &Model, std::string_view SourceName) { + std::size_t InstructionCount = 0; + for (const NormalizedSelectionRule &Rule : Model.SelectionRules) + InstructionCount += instructionCount(Rule); + + std::ostringstream Output; + Output << "// SPDX-License-Identifier: GPL-3.0-or-later\n\n"; + Output << "// This file is generated by rcc-tblgen from "; + appendQuoted(Output, SourceName); + Output << ". Do not edit it manually.\n\n"; + Output << "#ifdef GET_GLOBALISEL_MATCH_TABLE\n\n"; + Output << "constexpr std::array<GIMatchTableInstruction, " << InstructionCount + << "> GeneratedGlobalISelInstructions = {{\n"; + for (const NormalizedSelectionRule &Rule : Model.SelectionRules) { + emitInstruction(Output, "CheckOpcode", 0, Rule.MatchOpcode); + if (Rule.ResultType) + emitTypePredicate(Output, 0, *Rule.ResultType); + for (const NormalizedSelectionOperandTypePredicate &Predicate : Rule.OperandTypes) + emitTypePredicate(Output, Predicate.OperandIndex, Predicate.Type); + for (const NormalizedSelectionOperandBankPredicate &Predicate : Rule.OperandBanks) + emitInstruction(Output, "CheckOperandBank", Predicate.OperandIndex, Predicate.Bank); + for (const NormalizedSelectionOperandClassPredicate &Predicate : Rule.OperandClasses) + emitInstruction(Output, "CheckOperandClass", Predicate.OperandIndex, Predicate.RegClass); + emitInstruction(Output, "MutateOpcode", 0, Rule.ResultOpcode); + for (const std::int64_t Immediate : Rule.AppendedImmediates) + emitInstruction(Output, "AppendImmediate", 0, 0, "0", Immediate); + emitInstruction(Output, "Complete", 0); + } + Output << "}};\n\n"; + + Output << "constexpr std::array<GIMatchTableRule, " << Model.SelectionRules.size() + << "> GeneratedGlobalISelRules = {{\n"; + std::size_t FirstInstruction = 0; + for (const NormalizedSelectionRule &Rule : Model.SelectionRules) { + const std::size_t Count = instructionCount(Rule); + Output << " {"; + appendQuoted(Output, Rule.Symbol); + Output << ", " << Rule.Priority << ", " << FirstInstruction << ", " << Count << "},\n"; + FirstInstruction += Count; + } + Output << "}};\n\n"; + Output << "[[nodiscard]] constexpr GIMatchTable generatedGlobalISelMatchTable() noexcept {\n" + " return {GeneratedGlobalISelInstructions, GeneratedGlobalISelRules};\n" + "}\n\n"; + Output << "#endif // GET_GLOBALISEL_MATCH_TABLE\n"; + return Output.str(); +} + +} // namespace rcc::targetgen diff --git a/backend/utils/TableGen/TableGenBackends.h b/backend/utils/TableGen/TableGenBackends.h index 7835efb..a5f283e 100644 --- a/backend/utils/TableGen/TableGenBackends.h +++ b/backend/utils/TableGen/TableGenBackends.h @@ -17,6 +17,7 @@ struct GeneratedDescription final { std::string RegisterHeader; std::string InstructionHeader; std::string TargetTables; + std::string GlobalISel; }; struct DescriptionGenerationResult final { @@ -41,6 +42,8 @@ struct DescriptionGenerationResult final { [[nodiscard]] std::string emitAssemblyWriter(const NormalizedTargetModel &Model); [[nodiscard]] std::string emitSubtarget(const NormalizedTargetModel &Model); [[nodiscard]] std::string emitSearchableTables(const NormalizedTargetModel &Model); +[[nodiscard]] std::string emitGlobalISel(const NormalizedTargetModel &Model, + std::string_view SourceName); [[nodiscard]] DescriptionGenerationResult generateDescription(const std::string &SourceName, std::string SourceText); diff --git a/backend/utils/TableGen/rcc-tblgen.cpp b/backend/utils/TableGen/rcc-tblgen.cpp index 3e4e093..45690ee 100644 --- a/backend/utils/TableGen/rcc-tblgen.cpp +++ b/backend/utils/TableGen/rcc-tblgen.cpp @@ -3,38 +3,51 @@ #include "TableGenBackends.h" #include "rcc/Support/TextDiagnostic.h" +#include <cctype> #include <filesystem> #include <fstream> #include <iostream> #include <iterator> +#include <optional> #include <string> #include <string_view> +#include <unordered_map> +#include <vector> namespace { struct CommandLine final { - std::string InputPath; + std::vector<std::string> InputPaths; std::string RegisterHeaderPath; std::string InstructionHeaderPath; std::string TablesPath; + std::string GlobalISelPath; }; [[nodiscard]] bool parseCommandLine(int ArgumentCount, char *Arguments[], CommandLine &Result) { for (int Index = 1; Index < ArgumentCount; ++Index) { const std::string_view Argument(Arguments[Index]); - if (Argument == "--input" || Argument == "--register-header" || - Argument == "--instruction-header" || Argument == "--tables") { + if (Argument == "--input") { + if (Index + 1 >= ArgumentCount) { + std::cerr << "rcc-tblgen: missing value for " << Argument << '\n'; + return false; + } + Result.InputPaths.emplace_back(Arguments[++Index]); + continue; + } + if (Argument == "--register-header" || Argument == "--instruction-header" || + Argument == "--tables" || Argument == "--global-isel") { if (Index + 1 >= ArgumentCount) { std::cerr << "rcc-tblgen: missing value for " << Argument << '\n'; return false; } std::string *Destination = nullptr; - if (Argument == "--input") - Destination = &Result.InputPath; - else if (Argument == "--register-header") + if (Argument == "--register-header") Destination = &Result.RegisterHeaderPath; else if (Argument == "--instruction-header") Destination = &Result.InstructionHeaderPath; + else if (Argument == "--global-isel") + Destination = &Result.GlobalISelPath; else Destination = &Result.TablesPath; *Destination = Arguments[++Index]; @@ -43,12 +56,13 @@ struct CommandLine final { std::cerr << "rcc-tblgen: unknown argument '" << Argument << "'\n"; return false; } - if (!Result.InputPath.empty() && !Result.RegisterHeaderPath.empty() && - !Result.InstructionHeaderPath.empty() && !Result.TablesPath.empty()) { + if (!Result.InputPaths.empty() && !Result.RegisterHeaderPath.empty() && + !Result.InstructionHeaderPath.empty() && !Result.TablesPath.empty() && + !Result.GlobalISelPath.empty()) { return true; } - std::cerr << "usage: rcc-tblgen --input <file> --register-header <file> " - "--instruction-header <file> --tables <file>\n"; + std::cerr << "usage: rcc-tblgen --input <file> [--input <fragment>]... --register-header <file> " + "--instruction-header <file> --tables <file> --global-isel <file>\n"; return false; } @@ -74,6 +88,107 @@ struct CommandLine final { return false; } +struct SourceSection final { + std::string Name; + std::string Contents; + std::size_t HeaderSize = 0; +}; + +[[nodiscard]] bool isIdentifierCharacter(char Character) noexcept { + return std::isalnum(static_cast<unsigned char>(Character)) != 0 || Character == '_'; +} + +[[nodiscard]] std::optional<std::string_view> topLevelKey(std::string_view Line) noexcept { + if (Line.empty() || Line.front() == '#' || + (!std::isalpha(static_cast<unsigned char>(Line.front())) && Line.front() != '_')) { + return std::nullopt; + } + std::size_t End = 1; + while (End < Line.size() && isIdentifierCharacter(Line[End])) + ++End; + if (End == Line.size() || Line[End] != ':') + return std::nullopt; + return Line.substr(0, End); +} + +[[nodiscard]] std::vector<SourceSection> splitSections(std::string_view SourceText) { + std::vector<SourceSection> Result; + std::size_t SectionStart = 0; + std::size_t HeaderEnd = 0; + std::optional<std::string> SectionName; + std::size_t LineStart = 0; + while (LineStart < SourceText.size()) { + const std::size_t LineEnd = SourceText.find('\n', LineStart); + const std::size_t ContentEnd = LineEnd == std::string_view::npos ? SourceText.size() : LineEnd; + const std::string_view Line = SourceText.substr(LineStart, ContentEnd - LineStart); + if (const auto Key = topLevelKey(Line)) { + if (SectionName) { + Result.push_back({ + .Name = std::move(*SectionName), + .Contents = std::string(SourceText.substr(SectionStart, LineStart - SectionStart)), + .HeaderSize = HeaderEnd - SectionStart, + }); + } + SectionStart = LineStart; + HeaderEnd = LineEnd == std::string_view::npos ? SourceText.size() : LineEnd + 1U; + SectionName = std::string(*Key); + } + if (LineEnd == std::string_view::npos) + break; + LineStart = LineEnd + 1U; + } + if (SectionName) { + Result.push_back({ + .Name = std::move(*SectionName), + .Contents = std::string(SourceText.substr(SectionStart)), + .HeaderSize = HeaderEnd - SectionStart, + }); + } + return Result; +} + +[[nodiscard]] std::optional<std::string> +combineDescriptionSources(const std::vector<std::string> &Paths, + const std::vector<std::string> &Sources) { + std::vector<SourceSection> Result; + std::unordered_map<std::string, std::size_t> SectionIndex; + for (std::size_t Index = 0; Index < Sources.size(); ++Index) { + const std::vector<SourceSection> Sections = splitSections(Sources[Index]); + if (Sections.empty()) { + std::cerr << "rcc-tblgen: input '" << Paths[Index] + << "' does not contain a top-level description section\n"; + return std::nullopt; + } + for (const SourceSection &Section : Sections) { + const auto Position = SectionIndex.find(Section.Name); + if (Position == SectionIndex.end()) { + SectionIndex.emplace(Section.Name, Result.size()); + Result.push_back(Section); + continue; + } + if (Section.Name != "instructions" && Section.Name != "selection_rules") { + std::cerr << "rcc-tblgen: duplicate description section '" << Section.Name << "' in '" + << Paths[Index] << "'\n"; + return std::nullopt; + } + SourceSection &Existing = Result[Position->second]; + const std::string_view Body(Section.Contents.data() + Section.HeaderSize, + Section.Contents.size() - Section.HeaderSize); + if (!Existing.Contents.empty() && Existing.Contents.back() != '\n') + Existing.Contents.push_back('\n'); + Existing.Contents.append(Body); + } + } + + std::string Combined; + for (const SourceSection &Section : Result) { + if (!Combined.empty() && Combined.back() != '\n') + Combined.push_back('\n'); + Combined.append(Section.Contents); + } + return Combined; +} + } // namespace int main(int ArgumentCount, char *Arguments[]) { @@ -81,17 +196,23 @@ int main(int ArgumentCount, char *Arguments[]) { if (!parseCommandLine(ArgumentCount, Arguments, Command)) return 1; - std::ifstream Input(Command.InputPath, std::ios::binary); - if (!Input) { - std::cerr << "rcc-tblgen: cannot open input '" << Command.InputPath << "'\n"; - return 1; + std::vector<std::string> Sources; + Sources.reserve(Command.InputPaths.size()); + for (const std::string &InputPath : Command.InputPaths) { + std::ifstream Input(InputPath, std::ios::binary); + if (!Input) { + std::cerr << "rcc-tblgen: cannot open input '" << InputPath << "'\n"; + return 1; + } + Sources.emplace_back(std::istreambuf_iterator<char>(Input), std::istreambuf_iterator<char>()); } - const std::string SourceText{std::istreambuf_iterator<char>(Input), - std::istreambuf_iterator<char>()}; + const auto SourceText = combineDescriptionSources(Command.InputPaths, Sources); + if (!SourceText) + return 1; const rcc::targetgen::DescriptionGenerationResult Result = - rcc::targetgen::generateDescription(Command.InputPath, SourceText); + rcc::targetgen::generateDescription(Command.InputPaths.front(), *SourceText); if (!Result.succeeded()) { - const rcc::TextSource Source(Command.InputPath, SourceText); + const rcc::TextSource Source(Command.InputPaths.front(), *SourceText); for (const rcc::TextDiagnostic &Diagnostic : Result.Diagnostics) std::cerr << rcc::formatTextDiagnostic(Diagnostic, Source) << '\n'; return 1; @@ -104,7 +225,8 @@ int main(int ArgumentCount, char *Arguments[]) { const rcc::targetgen::GeneratedDescription &Output = Result.Output.value(); return writeFile(Command.RegisterHeaderPath, Output.RegisterHeader) && writeFile(Command.InstructionHeaderPath, Output.InstructionHeader) && - writeFile(Command.TablesPath, Output.TargetTables) + writeFile(Command.TablesPath, Output.TargetTables) && + writeFile(Command.GlobalISelPath, Output.GlobalISel) ? 0 : 1; } |
