aboutsummaryrefslogtreecommitdiff
path: root/backend/lib/CodeGen/MIRParser/MIRParser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'backend/lib/CodeGen/MIRParser/MIRParser.cpp')
-rw-r--r--backend/lib/CodeGen/MIRParser/MIRParser.cpp334
1 files changed, 334 insertions, 0 deletions
diff --git a/backend/lib/CodeGen/MIRParser/MIRParser.cpp b/backend/lib/CodeGen/MIRParser/MIRParser.cpp
new file mode 100644
index 0000000..e22f1bc
--- /dev/null
+++ b/backend/lib/CodeGen/MIRParser/MIRParser.cpp
@@ -0,0 +1,334 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "rcc/CodeGen/MIRParser/MIRParser.h"
+
+#include "rcc/CodeGen/MIRYamlMapping.h"
+#include "rcc/CodeGen/MachineBasicBlock.h"
+#include "rcc/CodeGen/MachineConstantPool.h"
+#include "rcc/CodeGen/MachineFrameInfo.h"
+#include "rcc/CodeGen/MachineFunction.h"
+#include "rcc/CodeGen/MachineFunctionAttributes.h"
+#include "rcc/CodeGen/MachineFunctionProperties.h"
+#include "rcc/CodeGen/MachineFunctionRecords.h"
+#include "rcc/CodeGen/MachineInstr.h"
+#include "rcc/CodeGen/MachineJumpTableInfo.h"
+#include "rcc/CodeGen/MachineMetadata.h"
+#include "rcc/CodeGen/MachineRegisterInfo.h"
+#include "rcc/CodeGen/MachineVerifier.h"
+#include "rcc/CodeGen/TargetDescription.h"
+#include "rcc/CodeGen/TargetMIRTextInfo.h"
+#include "rcc/CodeGen/TargetRegisterInfo.h"
+#include "rcc/MIR/LLVMConstantSyntax.h"
+
+#include <algorithm>
+#include <array>
+#include <cstddef>
+#include <cstdint>
+#include <initializer_list>
+#include <limits>
+#include <optional>
+#include <ranges>
+#include <span>
+#include <string>
+#include <string_view>
+#include <unordered_map>
+#include <unordered_set>
+#include <utility>
+#include <vector>
+
+namespace rcc::mir {
+
+bool MIRDocumentParser::fail(const TextRange &Range, std::string Message, std::string Category) {
+ if (Result.Diagnostics.empty()) {
+ Result.Diagnostics.push_back(TextDiagnostic{
+ .Severity = DiagnosticSeverity::Error,
+ .Category = std::move(Category),
+ .Message = std::move(Message),
+ .SourceName = std::string(Result.Source.name()),
+ .Range = Range,
+ });
+ }
+ return true;
+}
+
+bool MIRDocumentParser::importError(const TextRange &Range, const Error &Failure,
+ std::string Category) {
+ return fail(Range, std::string(Failure.message()), std::move(Category));
+}
+
+const yaml::Node *MIRDocumentParser::mappingValue(const yaml::Node &Mapping,
+ std::string_view Key) const noexcept {
+ const auto Value = Mapping.mappingValue(Key);
+ return Value ? node(*Value) : nullptr;
+}
+
+std::string_view MIRDocumentParser::effectiveDataLayout() const noexcept {
+ if (!Options.DataLayout.empty())
+ return Options.DataLayout;
+ if (ActiveDocument != nullptr) {
+ if (const EmbeddedIRReferenceIndex *Embedded = ActiveDocument->embeddedIR()) {
+ if (const auto DataLayout = Embedded->dataLayout())
+ return *DataLayout;
+ }
+ }
+ return {};
+}
+
+LLVMConstantSyntaxOptions MIRDocumentParser::pointerLayoutOptions() const noexcept {
+ return {
+ .DataLayout = effectiveDataLayout(),
+ .NamedTypes = {},
+ };
+}
+
+bool MIRDocumentParser::requireKind(const yaml::Node &Node, yaml::NodeKind Kind,
+ std::string_view Description) {
+ if (Node.kind() == Kind)
+ return false;
+ return fail(Node.range(), std::string(Description) + " has the wrong YAML node kind");
+}
+
+bool MIRDocumentParser::validateKeys(const yaml::Node &Mapping,
+ std::span<const std::string_view> Keys,
+ std::string_view Description) {
+ for (const yaml::MappingEntry &Entry : Mapping.mapping()) {
+ if (std::ranges::find(Keys, Entry.Key) == Keys.end()) {
+ return fail(Entry.KeyRange,
+ "unknown " + std::string(Description) + " key '" + Entry.Key + "'");
+ }
+ }
+ return false;
+}
+
+bool MIRDocumentParser::scalar(const yaml::Node &Node, std::string_view &Value,
+ std::string_view Description) {
+ if (requireKind(Node, yaml::NodeKind::Scalar, Description))
+ return true;
+ Value = Node.scalar();
+ return false;
+}
+
+MIRDocumentParseResult MIRDocumentParser::run() {
+ yaml::ParseResult YAMLResult = yaml::parse(std::string(Result.Source.name()),
+ std::string(Result.Source.text()), Options.YAML);
+ if (!YAMLResult.succeeded()) {
+ const auto Diagnostics = YAMLResult.diagnostics();
+ Result.Diagnostics.assign(Diagnostics.begin(), Diagnostics.end());
+ return std::move(Result);
+ }
+ Stream = YAMLResult.stream();
+ if (Stream == nullptr || Stream->documents().empty()) {
+ static_cast<void>(fail(Result.Source.range(0, 0), "MIR stream contains no YAML documents"));
+ return std::move(Result);
+ }
+
+ MIRDocument Staging;
+ ActiveDocument = &Staging;
+ const auto IsEmptyDocument = [this](std::size_t Index) {
+ const yaml::Node *Root = node(Stream->documents()[Index].Root);
+ return Root != nullptr && Root->kind() == yaml::NodeKind::Null &&
+ Root->range().Begin.Offset == Root->range().End.Offset;
+ };
+ std::size_t FirstContent = 0;
+ while (FirstContent < Stream->documents().size() && IsEmptyDocument(FirstContent))
+ ++FirstContent;
+ if (FirstContent == Stream->documents().size()) {
+ static_cast<void>(fail(Stream->documents().front().Range,
+ "MIR stream must contain at least one machine function document"));
+ return std::move(Result);
+ }
+
+ std::size_t FirstFunction = FirstContent;
+ const yaml::Node *FirstRoot = node(Stream->documents()[FirstContent].Root);
+ if (FirstRoot != nullptr && FirstRoot->kind() == yaml::NodeKind::Scalar) {
+ EmbeddedIRIndexResult Embedded = indexEmbeddedIR(
+ std::string(Result.Source.name()) + ":embedded-ir", std::string(FirstRoot->scalar()));
+ if (!Embedded.succeeded()) {
+ const auto Diagnostics = Embedded.diagnostics();
+ if (Diagnostics.empty()) {
+ static_cast<void>(
+ fail(FirstRoot->range(), "embedded LLVM IR indexing failed", "mir-embedded-ir"));
+ } else {
+ const TextDiagnostic &Diagnostic = Diagnostics.front();
+ const TextRange Range = scalarDiagnosticRange(*FirstRoot, Diagnostic.Range);
+ static_cast<void>(fail(Range, Diagnostic.Message, Diagnostic.Category));
+ }
+ return std::move(Result);
+ }
+ Staging.EmbeddedIR = std::move(Embedded).takeIndex();
+ FirstFunction = FirstContent + 1;
+ }
+
+ if (!effectiveDataLayout().empty()) {
+ auto PointerSize = llvmPointerSizeInBits(0, pointerLayoutOptions());
+ if (!PointerSize) {
+ static_cast<void>(importError(Stream->documents()[FirstContent].Range, PointerSize.error(),
+ "mir-data-layout"));
+ return std::move(Result);
+ }
+ }
+
+ bool SawFunction = false;
+ for (std::size_t Index = FirstFunction; Index < Stream->documents().size(); ++Index) {
+ if (IsEmptyDocument(Index))
+ continue;
+ const yaml::Node *Root = node(Stream->documents()[Index].Root);
+ if (Root == nullptr || requireKind(*Root, yaml::NodeKind::Mapping, "machine function") ||
+ parseFunction(*Root, Staging)) {
+ return std::move(Result);
+ }
+ SawFunction = true;
+ }
+ if (!SawFunction) {
+ static_cast<void>(fail(Stream->documents()[FirstContent].Range,
+ "MIR stream must contain at least one machine function document"));
+ return std::move(Result);
+ }
+
+ auto UnitResult = Staging.Unit.verify();
+ if (!UnitResult) {
+ static_cast<void>(
+ fail(Stream->documents().back().Range, "parsed compilation unit failed verification: " +
+ std::string(UnitResult.error().message())));
+ return std::move(Result);
+ }
+ ActiveDocument = nullptr;
+ Result.Document = std::move(Staging);
+ return std::move(Result);
+}
+
+bool MIRDocumentParser::parseFunction(const yaml::Node &Root, MIRDocument &Document) {
+ constexpr std::array Keys{
+ std::string_view{"name"},
+ std::string_view{"alignment"},
+ std::string_view{"exposesReturnsTwice"},
+ std::string_view{"legalized"},
+ std::string_view{"regBankSelected"},
+ std::string_view{"selected"},
+ std::string_view{"failedISel"},
+ std::string_view{"tracksRegLiveness"},
+ std::string_view{"hasWinCFI"},
+ std::string_view{"noPhis"},
+ std::string_view{"isSSA"},
+ std::string_view{"noVRegs"},
+ std::string_view{"hasFakeUses"},
+ std::string_view{"callsEHReturn"},
+ std::string_view{"callsUnwindInit"},
+ std::string_view{"hasEHContTarget"},
+ std::string_view{"hasEHScopes"},
+ std::string_view{"hasEHFunclets"},
+ std::string_view{"isOutlined"},
+ std::string_view{"debugInstrRef"},
+ std::string_view{"failsVerification"},
+ std::string_view{"tracksDebugUserValues"},
+ std::string_view{"registers"},
+ std::string_view{"liveins"},
+ std::string_view{"calleeSavedRegisters"},
+ std::string_view{"frameInfo"},
+ std::string_view{"fixedStack"},
+ std::string_view{"stack"},
+ std::string_view{"entry_values"},
+ std::string_view{"callSites"},
+ std::string_view{"debugValueSubstitutions"},
+ std::string_view{"constants"},
+ std::string_view{"machineFunctionInfo"},
+ std::string_view{"jumpTable"},
+ std::string_view{"machineMetadataNodes"},
+ std::string_view{"calledGlobals"},
+ std::string_view{"prefetch-targets"},
+ std::string_view{"body"},
+ };
+ const yaml::Node *NameNode = mappingValue(Root, "name");
+ if (NameNode == nullptr)
+ return fail(Root.range(), "missing required key 'name'");
+ if (validateKeys(Root, Keys, "machine function"))
+ return true;
+ std::string_view Name;
+ if (scalar(*NameNode, Name, "machine function name"))
+ return true;
+
+ const EmbeddedIRFunction *IRFunction = nullptr;
+ if (Document.EmbeddedIR) {
+ if (Name.empty()) {
+ std::size_t AnonymousOrdinal = 0;
+ for (std::size_t Index = 0; Index < Document.Unit.functionCount(); ++Index) {
+ const MachineFunction *Existing = Document.Unit.functionAt(Index);
+ if (Existing != nullptr && Existing->name().empty())
+ ++AnonymousOrdinal;
+ }
+ IRFunction = Document.EmbeddedIR->unnamedFunction(AnonymousOrdinal);
+ } else {
+ IRFunction = Document.EmbeddedIR->function(Name);
+ }
+ if (IRFunction == nullptr)
+ return fail(NameNode->range(),
+ "function '" + std::string(Name) + "' isn't defined in the provided LLVM IR");
+ }
+
+ if (!Name.empty() && Document.Unit.function(Name) != nullptr)
+ return fail(NameNode->range(), "redefinition of machine function '" + std::string(Name) + "'");
+
+ auto Created = Document.Unit.createMachineFunction(std::string(Name));
+ if (!Created)
+ return importError(NameNode->range(), Created.error());
+ MachineFunction &Function = **Created;
+ ActiveFunction = &Function;
+
+ MIRFunctionDocumentInfo DocumentInfo{
+ .Function = Function.stableID(),
+ .NoPHIs = std::nullopt,
+ .IsSSA = std::nullopt,
+ .NoVRegs = std::nullopt,
+ .HasFakeUses = std::nullopt,
+ };
+ MIParserContext Context;
+ if (IRFunction != nullptr) {
+ Context.HasIRBlock = [IRFunction](std::string_view BlockName) {
+ return IRFunction->block(BlockName) != nullptr;
+ };
+ }
+
+ std::vector<PendingJumpTable> PendingJumpTables;
+ if (parseFunctionScalars(Root, Function, DocumentInfo) ||
+ parseMetadata(Root, Function, Context) || parseRegisters(Root, Function, Context) ||
+ parseStackObjects(Root, Function, Context) || parseConstants(Root, Function, Context) ||
+ reserveJumpTables(Root, Function, Context, PendingJumpTables) ||
+ parseBody(Root, Function, Context) || defineJumpTables(Function, PendingJumpTables) ||
+ parseFrameState(Root, Function, Context) ||
+ recomputeProperties(Root, Function, DocumentInfo) || parseTargetState(Root, Function) ||
+ parseFunctionRecords(Root, Function) || validateDocumentReferences(Function)) {
+ return true;
+ }
+
+ if (Options.VerifyMachineFunctions) {
+ auto Verified = MachineVerifier::verify(Function, Target);
+ if (!Verified)
+ return importError(Root.range(), Verified.error(), "mir-verifier");
+ }
+ Document.FunctionInfo.push_back(DocumentInfo);
+ ActiveFunction = nullptr;
+ return false;
+}
+
+} // namespace rcc::mir
+
+namespace rcc::mir {
+
+MIRDocument::MIRDocument() = default;
+MIRDocument::MIRDocument(CompilationUnit Unit) : Unit(std::move(Unit)) {}
+MIRDocument::MIRDocument(MIRDocument &&) noexcept = default;
+MIRDocument &MIRDocument::operator=(MIRDocument &&) noexcept = default;
+MIRDocument::~MIRDocument() = default;
+
+MIRDocumentParseResult::MIRDocumentParseResult(MIRDocumentParseResult &&) noexcept = default;
+MIRDocumentParseResult &
+MIRDocumentParseResult::operator=(MIRDocumentParseResult &&) noexcept = default;
+MIRDocumentParseResult::~MIRDocumentParseResult() = default;
+
+MIRDocumentParseResult parseMIRDocument(const TargetDescription &Target, std::string SourceName,
+ std::string SourceText, MIRDocumentParserOptions Options) {
+ return MIRDocumentParser(Target, std::move(SourceName), std::move(SourceText), std::move(Options))
+ .run();
+}
+
+} // namespace rcc::mir