aboutsummaryrefslogtreecommitdiff
path: root/backend/utils/TableGen/rcc-tblgen.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'backend/utils/TableGen/rcc-tblgen.cpp')
-rw-r--r--backend/utils/TableGen/rcc-tblgen.cpp251
1 files changed, 251 insertions, 0 deletions
diff --git a/backend/utils/TableGen/rcc-tblgen.cpp b/backend/utils/TableGen/rcc-tblgen.cpp
new file mode 100644
index 0000000..cf10665
--- /dev/null
+++ b/backend/utils/TableGen/rcc-tblgen.cpp
@@ -0,0 +1,251 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#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::vector<std::string> InputPaths;
+ std::string RegisterHeaderPath;
+ std::string InstructionHeaderPath;
+ std::string TablesPath;
+ std::string GlobalISelPath;
+ std::string GlobalISelCombinerPath;
+ std::string MacroFusionPath;
+ std::string MCPseudoLoweringPath;
+};
+
+[[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") {
+ 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" ||
+ Argument == "--global-isel-combiner" || Argument == "--macro-fusion" ||
+ Argument == "--mc-pseudo-lowering") {
+ if (Index + 1 >= ArgumentCount) {
+ std::cerr << "rcc-tblgen: missing value for " << Argument << '\n';
+ return false;
+ }
+ std::string *Destination = nullptr;
+ if (Argument == "--register-header")
+ Destination = &Result.RegisterHeaderPath;
+ else if (Argument == "--instruction-header")
+ Destination = &Result.InstructionHeaderPath;
+ else if (Argument == "--global-isel")
+ Destination = &Result.GlobalISelPath;
+ else if (Argument == "--global-isel-combiner")
+ Destination = &Result.GlobalISelCombinerPath;
+ else if (Argument == "--macro-fusion")
+ Destination = &Result.MacroFusionPath;
+ else if (Argument == "--mc-pseudo-lowering")
+ Destination = &Result.MCPseudoLoweringPath;
+ else
+ Destination = &Result.TablesPath;
+ *Destination = Arguments[++Index];
+ continue;
+ }
+ std::cerr << "rcc-tblgen: unknown argument '" << Argument << "'\n";
+ return false;
+ }
+ if (!Result.InputPaths.empty() && !Result.RegisterHeaderPath.empty() &&
+ !Result.InstructionHeaderPath.empty() && !Result.TablesPath.empty() &&
+ !Result.GlobalISelPath.empty() && !Result.GlobalISelCombinerPath.empty() &&
+ !Result.MacroFusionPath.empty() && !Result.MCPseudoLoweringPath.empty()) {
+ return true;
+ }
+ std::cerr << "usage: rcc-tblgen --input <file> [--input <fragment>]... --register-header <file> "
+ "--instruction-header <file> --tables <file> --global-isel <file> "
+ "--global-isel-combiner <file> --macro-fusion <file> "
+ "--mc-pseudo-lowering <file>\n";
+ return false;
+}
+
+[[nodiscard]] bool writeFile(const std::filesystem::path &Path, std::string_view Contents) {
+ std::error_code Error;
+ std::filesystem::create_directories(Path.parent_path(), Error);
+ if (Error) {
+ std::cerr << "rcc-tblgen: cannot create output directory for '" << Path.string()
+ << "': " << Error.message() << '\n';
+ return false;
+ }
+ std::ofstream Output(Path, std::ios::binary | std::ios::trunc);
+ if (!Output) {
+ std::cerr << "rcc-tblgen: cannot open output '" << Path.string() << "'\n";
+ return false;
+ }
+ Output.write(Contents.data(), static_cast<std::streamsize>(Contents.size()));
+ if (Output) {
+ Output.close();
+ return true;
+ }
+ std::cerr << "rcc-tblgen: cannot write output '" << Path.string() << "'\n";
+ 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" &&
+ Section.Name != "combiner_rules" && Section.Name != "macro_fusion_rules" &&
+ Section.Name != "mc_pseudo_expansions") {
+ 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[]) {
+ CommandLine Command;
+ if (!parseCommandLine(ArgumentCount, Arguments, Command))
+ 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 auto SourceText = combineDescriptionSources(Command.InputPaths, Sources);
+ if (!SourceText)
+ return 1;
+ const rcc::targetgen::DescriptionGenerationResult Result =
+ rcc::targetgen::generateDescription(Command.InputPaths.front(), *SourceText);
+ if (!Result.succeeded()) {
+ const rcc::TextSource Source(Command.InputPaths.front(), *SourceText);
+ for (const rcc::TextDiagnostic &Diagnostic : Result.Diagnostics)
+ std::cerr << rcc::formatTextDiagnostic(Diagnostic, Source) << '\n';
+ return 1;
+ }
+
+ if (!Result.Output) {
+ std::cerr << "rcc-tblgen: successful generation produced no output\n";
+ return 1;
+ }
+ 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.GlobalISelPath, Output.GlobalISel) &&
+ writeFile(Command.GlobalISelCombinerPath, Output.GlobalISelCombiner) &&
+ writeFile(Command.MacroFusionPath, Output.MacroFusion) &&
+ writeFile(Command.MCPseudoLoweringPath, Output.MCPseudoLowering)
+ ? 0
+ : 1;
+}