From 4f7b41c3c711d6c6fa4e422f3eec8961c91eea7a Mon Sep 17 00:00:00 2001 From: Richard-Qin-X Date: Sun, 16 Aug 2026 15:39:45 +0800 Subject: tablegen: Generate GlobalISel match tables --- backend/utils/TableGen/rcc-tblgen.cpp | 160 ++++++++++++++++++++++++++++++---- 1 file changed, 141 insertions(+), 19 deletions(-) (limited to 'backend/utils/TableGen/rcc-tblgen.cpp') 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 #include #include #include #include +#include #include #include +#include +#include namespace { struct CommandLine final { - std::string InputPath; + std::vector 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 --register-header " - "--instruction-header --tables \n"; + std::cerr << "usage: rcc-tblgen --input [--input ]... --register-header " + "--instruction-header --tables --global-isel \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(Character)) != 0 || Character == '_'; +} + +[[nodiscard]] std::optional topLevelKey(std::string_view Line) noexcept { + if (Line.empty() || Line.front() == '#' || + (!std::isalpha(static_cast(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 splitSections(std::string_view SourceText) { + std::vector Result; + std::size_t SectionStart = 0; + std::size_t HeaderEnd = 0; + std::optional 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 +combineDescriptionSources(const std::vector &Paths, + const std::vector &Sources) { + std::vector Result; + std::unordered_map SectionIndex; + for (std::size_t Index = 0; Index < Sources.size(); ++Index) { + const std::vector 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 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(Input), std::istreambuf_iterator()); } - const std::string SourceText{std::istreambuf_iterator(Input), - std::istreambuf_iterator()}; + 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; } -- cgit v1.2.3