aboutsummaryrefslogtreecommitdiff
path: root/backend/utils/TableGen
diff options
context:
space:
mode:
authorRichard-Qin-X <richard.qin.001@gmail.com>2026-08-16 15:39:45 +0800
committerRichard-Qin-X <richard.qin.001@gmail.com>2026-08-16 15:39:45 +0800
commit4f7b41c3c711d6c6fa4e422f3eec8961c91eea7a (patch)
treea825927a4a3ef91ec6e3489aa1f96e63788ff476 /backend/utils/TableGen
parenta39d8f6f4ed2ad0f8435b73bd6b2a35769c6fead (diff)
downloadrcc-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.txt2
-rw-r--r--backend/utils/TableGen/Common/CodeGenSelection.cpp183
-rw-r--r--backend/utils/TableGen/Common/CodeGenTarget.cpp4
-rw-r--r--backend/utils/TableGen/Common/CodeGenTarget.h41
-rw-r--r--backend/utils/TableGen/GlobalISelEmitter.cpp96
-rw-r--r--backend/utils/TableGen/TableGenBackends.h3
-rw-r--r--backend/utils/TableGen/rcc-tblgen.cpp160
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;
}