aboutsummaryrefslogtreecommitdiff
path: root/backend
diff options
context:
space:
mode:
authorRichard-Qin-X <richard.qin.001@gmail.com>2026-08-13 06:19:30 +0800
committerRichard-Qin-X <richard.qin.001@gmail.com>2026-08-13 06:19:30 +0800
commitd62f022c92fcc3ebe2269273628943a0089683fd (patch)
tree92d13e4888b095cca71a0ac8b9b9eb99cc845596 /backend
parent236082a0fe4dcd0362c5c74923a93e3db6a01291 (diff)
downloadrcc-project-d62f022c92fcc3ebe2269273628943a0089683fd.tar.gz
rcc-project-d62f022c92fcc3ebe2269273628943a0089683fd.tar.xz
[CodeGen] Align MIR parser and printer ownership
Diffstat (limited to 'backend')
-rw-r--r--backend/include/rcc/CodeGen/MIRParser/MIParser.h (renamed from backend/include/rcc/MIR/MIParser.h)6
-rw-r--r--backend/include/rcc/CodeGen/MIRParser/MIRParser.h (renamed from backend/include/rcc/MIR/MIRDocument.h)19
-rw-r--r--backend/include/rcc/CodeGen/MIRPrinter.h (renamed from backend/include/rcc/MIR/MIPrinter.h)18
-rw-r--r--backend/include/rcc/CodeGen/MIRYamlMapping.h (renamed from backend/lib/MIR/MIRDocumentParser.h)22
-rw-r--r--backend/include/rcc/MIR/MIDebugSyntax.h26
-rw-r--r--backend/include/rcc/Tooling/ArtifactStore.h2
-rw-r--r--backend/include/rcc/Tooling/PipelineDriver.h2
-rw-r--r--backend/lib/CodeGen/MIRParser/MILexer.cpp (renamed from backend/lib/MIR/MILexer.cpp)2
-rw-r--r--backend/lib/CodeGen/MIRParser/MILexer.h (renamed from backend/include/rcc/MIR/MILexer.h)6
-rw-r--r--backend/lib/CodeGen/MIRParser/MIParser.cpp (renamed from backend/lib/MIR/MIParser.cpp)6
-rw-r--r--backend/lib/CodeGen/MIRParser/MIRParser.cpp (renamed from backend/lib/MIR/MIRDocumentParser.cpp)110
-rw-r--r--backend/lib/CodeGen/MIRPrinter.cpp (renamed from backend/lib/MIR/MIPrinter.cpp)1477
-rw-r--r--backend/lib/CodeGen/MIRYamlMapping.cpp2926
-rw-r--r--backend/lib/MIR/CMakeLists.txt71
-rw-r--r--backend/lib/MIR/MIDebugSyntax.cpp264
-rw-r--r--backend/lib/MIR/MIRDataParser.cpp287
-rw-r--r--backend/lib/MIR/MIRDocument.cpp29
-rw-r--r--backend/lib/MIR/MIRDocumentPrinter.cpp1499
-rw-r--r--backend/lib/MIR/MIRDocumentReferenceValidation.cpp426
-rw-r--r--backend/lib/MIR/MIRDocumentReferenceValidation.h25
-rw-r--r--backend/lib/MIR/MIRFrameParser.cpp467
-rw-r--r--backend/lib/MIR/MIRFunctionParser.cpp552
-rw-r--r--backend/lib/MIR/MIRFunctionRecordsParser.cpp341
-rw-r--r--backend/lib/MIR/MIRMetadataParser.cpp333
-rw-r--r--backend/lib/MIR/MIRMetadataTextParser.cpp259
-rw-r--r--backend/lib/MIR/MIRMetadataTextParser.h74
-rw-r--r--backend/lib/MIR/MIRParserUtilities.h85
-rw-r--r--backend/lib/Tooling/ArtifactStore.cpp2
-rw-r--r--backend/lib/Tooling/BackendDriver.cpp2
-rw-r--r--backend/lib/Tooling/MIRFuzzer.cpp2
-rw-r--r--backend/lib/Tooling/PipelineDriver.cpp2
-rw-r--r--backend/unittests/MIR/CMakeLists.txt4
-rw-r--r--backend/unittests/MIR/MILexerTest.cpp2
-rw-r--r--backend/unittests/MIR/MIParserTest.cpp4
-rw-r--r--backend/unittests/MIR/MIRDocumentTest.cpp4
-rw-r--r--backend/unittests/Tooling/PipelineDriverTest.cpp2
36 files changed, 4505 insertions, 4853 deletions
diff --git a/backend/include/rcc/MIR/MIParser.h b/backend/include/rcc/CodeGen/MIRParser/MIParser.h
index b660f47..0b888e5 100644
--- a/backend/include/rcc/MIR/MIParser.h
+++ b/backend/include/rcc/CodeGen/MIRParser/MIParser.h
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-3.0-or-later
-#ifndef RCC_MIR_MIPARSER_H
-#define RCC_MIR_MIPARSER_H
+#ifndef RCC_CODEGEN_MIRPARSER_MIPARSER_H
+#define RCC_CODEGEN_MIRPARSER_MIPARSER_H
#include "rcc/CodeGen/MachineIRTypes.h"
#include "rcc/CodeGen/MachineMetadata.h"
@@ -104,4 +104,4 @@ private:
} // namespace mir
} // namespace rcc
-#endif // RCC_MIR_MIPARSER_H
+#endif // RCC_CODEGEN_MIRPARSER_MIPARSER_H
diff --git a/backend/include/rcc/MIR/MIRDocument.h b/backend/include/rcc/CodeGen/MIRParser/MIRParser.h
index 6041657..798a747 100644
--- a/backend/include/rcc/MIR/MIRDocument.h
+++ b/backend/include/rcc/CodeGen/MIRParser/MIRParser.h
@@ -1,13 +1,12 @@
// SPDX-License-Identifier: GPL-3.0-or-later
-#ifndef RCC_MIR_MIRDOCUMENT_H
-#define RCC_MIR_MIRDOCUMENT_H
+#ifndef RCC_CODEGEN_MIRPARSER_MIRPARSER_H
+#define RCC_CODEGEN_MIRPARSER_MIRPARSER_H
#include "rcc/CodeGen/CompilationUnit.h"
+#include "rcc/CodeGen/MIRParser/MIParser.h"
#include "rcc/CodeGen/StableID.h"
#include "rcc/MIR/EmbeddedIR.h"
-#include "rcc/MIR/MIParser.h"
-#include "rcc/MIR/MIPrinter.h"
#include "rcc/Support/TextDiagnostic.h"
#include "rcc/Support/YAML.h"
@@ -125,17 +124,7 @@ private:
std::string SourceText,
MIRDocumentParserOptions Options = {});
-struct MIRDocumentPrinterOptions final {
- MIPrintMode Mode = MIPrintMode::Complete;
- bool EmitEmbeddedIR = true;
- std::string DataLayout;
-};
-
-[[nodiscard]] Expected<std::string> printMIRDocument(const MIRDocument &Document,
- const TargetDescription &Target,
- MIRDocumentPrinterOptions Options = {});
-
} // namespace mir
} // namespace rcc
-#endif // RCC_MIR_MIRDOCUMENT_H
+#endif // RCC_CODEGEN_MIRPARSER_MIRPARSER_H
diff --git a/backend/include/rcc/MIR/MIPrinter.h b/backend/include/rcc/CodeGen/MIRPrinter.h
index 663859e..bb87509 100644
--- a/backend/include/rcc/MIR/MIPrinter.h
+++ b/backend/include/rcc/CodeGen/MIRPrinter.h
@@ -1,9 +1,9 @@
// SPDX-License-Identifier: GPL-3.0-or-later
-#ifndef RCC_MIR_MIPRINTER_H
-#define RCC_MIR_MIPRINTER_H
+#ifndef RCC_CODEGEN_MIRPRINTER_H
+#define RCC_CODEGEN_MIRPRINTER_H
-#include "rcc/MIR/MIParser.h"
+#include "rcc/CodeGen/MIRParser/MIRParser.h"
#include "rcc/Support/Error.h"
#include <string>
@@ -29,7 +29,17 @@ struct MIPrinterOptions final {
const MIParserContext &Context = {},
MIPrinterOptions Options = {});
+struct MIRDocumentPrinterOptions final {
+ MIPrintMode Mode = MIPrintMode::Complete;
+ bool EmitEmbeddedIR = true;
+ std::string DataLayout;
+};
+
+[[nodiscard]] Expected<std::string> printMIRDocument(const MIRDocument &Document,
+ const TargetDescription &Target,
+ MIRDocumentPrinterOptions Options = {});
+
} // namespace mir
} // namespace rcc
-#endif // RCC_MIR_MIPRINTER_H
+#endif // RCC_CODEGEN_MIRPRINTER_H
diff --git a/backend/lib/MIR/MIRDocumentParser.h b/backend/include/rcc/CodeGen/MIRYamlMapping.h
index 4d1d1ea..82880ca 100644
--- a/backend/lib/MIR/MIRDocumentParser.h
+++ b/backend/include/rcc/CodeGen/MIRYamlMapping.h
@@ -1,18 +1,19 @@
// SPDX-License-Identifier: GPL-3.0-or-later
-// NOLINTNEXTLINE(llvm-header-guard)
-#ifndef RCC_LIB_MIR_MIRDOCUMENTPARSER_H
-#define RCC_LIB_MIR_MIRDOCUMENTPARSER_H
+#ifndef RCC_CODEGEN_MIRYAMLMAPPING_H
+#define RCC_CODEGEN_MIRYAMLMAPPING_H
+#include "rcc/CodeGen/MIRParser/MIRParser.h"
#include "rcc/CodeGen/MachineDebugInfo.h"
#include "rcc/CodeGen/MachineFrameInfo.h"
#include "rcc/CodeGen/MachineJumpTableInfo.h"
#include "rcc/CodeGen/MachineMetadata.h"
#include "rcc/CodeGen/TargetMIRTextInfo.h"
#include "rcc/MIR/LLVMConstantSyntax.h"
-#include "rcc/MIR/MIRDocument.h"
+#include "rcc/Support/Error.h"
#include <cstdint>
+#include <optional>
#include <span>
#include <string>
#include <string_view>
@@ -126,7 +127,18 @@ private:
MachineFunction *ActiveFunction = nullptr;
};
+[[nodiscard]] Expected<void> verifyMIRDocumentReferences(const MIRDocument &Document,
+ const MachineFunction &Function,
+ bool UseEmbeddedIR);
+
+[[nodiscard]] std::optional<std::uint64_t>
+parseDIExpressionOpcodeName(std::string_view Name) noexcept;
+[[nodiscard]] std::optional<std::uint64_t>
+parseDIExpressionAttributeEncodingName(std::string_view Name) noexcept;
+[[nodiscard]] std::string invalidDIExpressionElementMessage(std::string_view Name);
+[[nodiscard]] std::string formatDIExpression(std::span<const std::uint64_t> Elements);
+
} // namespace mir
} // namespace rcc
-#endif // RCC_LIB_MIR_MIRDOCUMENTPARSER_H
+#endif // RCC_CODEGEN_MIRYAMLMAPPING_H
diff --git a/backend/include/rcc/MIR/MIDebugSyntax.h b/backend/include/rcc/MIR/MIDebugSyntax.h
deleted file mode 100644
index 4952128..0000000
--- a/backend/include/rcc/MIR/MIDebugSyntax.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#ifndef RCC_MIR_MIDEBUGSYNTAX_H
-#define RCC_MIR_MIDEBUGSYNTAX_H
-
-#include <cstdint>
-#include <optional>
-#include <span>
-#include <string>
-#include <string_view>
-
-namespace rcc::mir {
-
-[[nodiscard]] std::optional<std::uint64_t>
-parseDIExpressionOpcodeName(std::string_view Name) noexcept;
-
-[[nodiscard]] std::optional<std::uint64_t>
-parseDIExpressionAttributeEncodingName(std::string_view Name) noexcept;
-
-[[nodiscard]] std::string invalidDIExpressionElementMessage(std::string_view Name);
-
-[[nodiscard]] std::string formatDIExpression(std::span<const std::uint64_t> Elements);
-
-} // namespace rcc::mir
-
-#endif // RCC_MIR_MIDEBUGSYNTAX_H
diff --git a/backend/include/rcc/Tooling/ArtifactStore.h b/backend/include/rcc/Tooling/ArtifactStore.h
index a30f572..f11cd6c 100644
--- a/backend/include/rcc/Tooling/ArtifactStore.h
+++ b/backend/include/rcc/Tooling/ArtifactStore.h
@@ -3,7 +3,7 @@
#ifndef RCC_TOOLING_ARTIFACTSTORE_H
#define RCC_TOOLING_ARTIFACTSTORE_H
-#include "rcc/MIR/MIRDocument.h"
+#include "rcc/CodeGen/MIRParser/MIRParser.h"
#include "rcc/Support/Error.h"
#include "rcc/Tooling/BackendManifest.h"
diff --git a/backend/include/rcc/Tooling/PipelineDriver.h b/backend/include/rcc/Tooling/PipelineDriver.h
index 849b2f7..5fa4fba 100644
--- a/backend/include/rcc/Tooling/PipelineDriver.h
+++ b/backend/include/rcc/Tooling/PipelineDriver.h
@@ -3,8 +3,8 @@
#ifndef RCC_TOOLING_PIPELINEDRIVER_H
#define RCC_TOOLING_PIPELINEDRIVER_H
+#include "rcc/CodeGen/MIRParser/MIRParser.h"
#include "rcc/CodeGen/MachinePassManager.h"
-#include "rcc/MIR/MIRDocument.h"
#include "rcc/Tooling/BackendInvocation.h"
#include <cstdint>
diff --git a/backend/lib/MIR/MILexer.cpp b/backend/lib/CodeGen/MIRParser/MILexer.cpp
index f568cef..830c548 100644
--- a/backend/lib/MIR/MILexer.cpp
+++ b/backend/lib/CodeGen/MIRParser/MILexer.cpp
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-or-later
-#include "rcc/MIR/MILexer.h"
+#include "MILexer.h"
#include <algorithm>
#include <array>
diff --git a/backend/include/rcc/MIR/MILexer.h b/backend/lib/CodeGen/MIRParser/MILexer.h
index f54f255..e3d50e7 100644
--- a/backend/include/rcc/MIR/MILexer.h
+++ b/backend/lib/CodeGen/MIRParser/MILexer.h
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-3.0-or-later
-#ifndef RCC_MIR_MILEXER_H
-#define RCC_MIR_MILEXER_H
+#ifndef RCC_LIB_CODEGEN_MIRPARSER_MILEXER_H
+#define RCC_LIB_CODEGEN_MIRPARSER_MILEXER_H
#include "rcc/Support/TextDiagnostic.h"
@@ -178,4 +178,4 @@ private:
} // namespace rcc::mir
-#endif // RCC_MIR_MILEXER_H
+#endif // RCC_LIB_CODEGEN_MIRPARSER_MILEXER_H
diff --git a/backend/lib/MIR/MIParser.cpp b/backend/lib/CodeGen/MIRParser/MIParser.cpp
index 3748187..e38b9f1 100644
--- a/backend/lib/MIR/MIParser.cpp
+++ b/backend/lib/CodeGen/MIRParser/MIParser.cpp
@@ -1,10 +1,12 @@
// SPDX-License-Identifier: GPL-3.0-or-later
-#include "rcc/MIR/MIParser.h"
+#include "rcc/CodeGen/MIRParser/MIParser.h"
+#include "MILexer.h"
#include "rcc/CodeGen/GenericOpcodes.h"
#include "rcc/CodeGen/InlineAsm.h"
#include "rcc/CodeGen/InstructionDescriptor.h"
+#include "rcc/CodeGen/MIRYamlMapping.h"
#include "rcc/CodeGen/MachineBasicBlock.h"
#include "rcc/CodeGen/MachineCFGUpdater.h"
#include "rcc/CodeGen/MachineFunction.h"
@@ -16,8 +18,6 @@
#include "rcc/CodeGen/TargetMIRTextInfo.h"
#include "rcc/CodeGen/TargetRegisterInfo.h"
#include "rcc/MIR/LLVMConstantSyntax.h"
-#include "rcc/MIR/MIDebugSyntax.h"
-#include "rcc/MIR/MILexer.h"
#include <algorithm>
#include <array>
diff --git a/backend/lib/MIR/MIRDocumentParser.cpp b/backend/lib/CodeGen/MIRParser/MIRParser.cpp
index 08627d5..e22f1bc 100644
--- a/backend/lib/MIR/MIRDocumentParser.cpp
+++ b/backend/lib/CodeGen/MIRParser/MIRParser.cpp
@@ -1,10 +1,8 @@
// SPDX-License-Identifier: GPL-3.0-or-later
-#include "MIRDocumentParser.h"
+#include "rcc/CodeGen/MIRParser/MIRParser.h"
-#include "MIRDocumentReferenceValidation.h"
-#include "MIRMetadataTextParser.h"
-#include "MIRParserUtilities.h"
+#include "rcc/CodeGen/MIRYamlMapping.h"
#include "rcc/CodeGen/MachineBasicBlock.h"
#include "rcc/CodeGen/MachineConstantPool.h"
#include "rcc/CodeGen/MachineFrameInfo.h"
@@ -40,13 +38,6 @@
namespace rcc::mir {
-using parser_detail::parseBool;
-using parser_detail::parsePrefixedID;
-using parser_detail::parseSigned;
-using parser_detail::parseUnsigned;
-using parser_detail::parseUnsigned32;
-using parser_detail::trim;
-
bool MIRDocumentParser::fail(const TextRange &Range, std::string Message, std::string Category) {
if (Result.Diagnostics.empty()) {
Result.Diagnostics.push_back(TextDiagnostic{
@@ -117,82 +108,6 @@ bool MIRDocumentParser::scalar(const yaml::Node &Node, std::string_view &Value,
return false;
}
-bool MIRDocumentParser::boolean(const yaml::Node &Node, bool &Value) {
- std::string_view Text;
- if (scalar(Node, Text, "boolean") || !parseBool(Text, Value))
- return fail(Node.range(), "expected 'true' or 'false'");
- return false;
-}
-
-bool MIRDocumentParser::unsignedValue(const yaml::Node &Node, std::uint64_t &Value,
- std::string_view Description) {
- std::string_view Text;
- if (scalar(Node, Text, Description) || !parseUnsigned(Text, Value))
- return fail(Node.range(), "expected " + std::string(Description));
- return false;
-}
-
-bool MIRDocumentParser::signedValue(const yaml::Node &Node, std::int64_t &Value,
- std::string_view Description) {
- std::string_view Text;
- if (scalar(Node, Text, Description) || !parseSigned(Text, Value))
- return fail(Node.range(), "expected " + std::string(Description));
- return false;
-}
-
-bool MIRDocumentParser::alignment(const yaml::Node &Node, Alignment &Value, bool AllowZero) {
- std::uint64_t Bytes = 0;
- if (unsignedValue(Node, Bytes, "an unsigned alignment"))
- return true;
- if (Bytes == 0 && AllowZero) {
- Value = Alignment{};
- return false;
- }
- auto Parsed = Alignment::create(Bytes);
- if (!Parsed)
- return importError(Node.range(), Parsed.error());
- Value = *Parsed;
- return false;
-}
-
-bool MIRDocumentParser::physicalRegister(std::string_view Text, const TextRange &Range,
- PhysicalRegister &Reg) {
- Text = trim(Text);
- if (!Text.starts_with('$') || Text.size() == 1)
- return fail(Range, "physical register must use the '$name' MIR spelling");
- const PhysicalRegisterDesc *Description = Target.registers().physicalRegister(Text.substr(1));
- if (Description == nullptr)
- return fail(Range, "unknown physical register '" + std::string(Text) + "'");
- Reg = Description->Reg;
- return false;
-}
-
-bool MIRDocumentParser::physicalRegister(const yaml::Node &Node, PhysicalRegister &Reg) {
- std::string_view Text;
- return scalar(Node, Text, "physical register") || physicalRegister(Text, Node.range(), Reg);
-}
-
-bool MIRDocumentParser::virtualRegister(std::string_view Text, const TextRange &Range,
- MachineFunction &Function, MIParserContext &Context,
- Register &Reg) {
- const auto ID = parsePrefixedID(Text, "%");
- if (!ID)
- return fail(Range, "virtual register must use the '%N' MIR spelling");
- auto Found =
- std::ranges::find_if(Context.VirtualRegisters,
- [ID](const MIVirtualRegisterSlot &Slot) { return Slot.TextID == *ID; });
- if (Found == Context.VirtualRegisters.end()) {
- auto Created = Function.registerInfo().createVirtualRegister();
- if (!Created)
- return importError(Range, Created.error());
- Context.VirtualRegisters.push_back({*ID, *Created});
- Reg = *Created;
- return false;
- }
- Reg = Found->Reg;
- return false;
-}
-
MIRDocumentParseResult MIRDocumentParser::run() {
yaml::ParseResult YAMLResult = yaml::parse(std::string(Result.Source.name()),
std::string(Result.Source.text()), Options.YAML);
@@ -396,3 +311,24 @@ bool MIRDocumentParser::parseFunction(const yaml::Node &Root, MIRDocument &Docum
}
} // 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
diff --git a/backend/lib/MIR/MIPrinter.cpp b/backend/lib/CodeGen/MIRPrinter.cpp
index 0265564..d510c83 100644
--- a/backend/lib/MIR/MIPrinter.cpp
+++ b/backend/lib/CodeGen/MIRPrinter.cpp
@@ -1,14 +1,23 @@
// SPDX-License-Identifier: GPL-3.0-or-later
-#include "rcc/MIR/MIPrinter.h"
+#include "rcc/CodeGen/MIRPrinter.h"
#include "rcc/CodeGen/InlineAsm.h"
#include "rcc/CodeGen/InstructionDescriptor.h"
+#include "rcc/CodeGen/MIRYamlMapping.h"
#include "rcc/CodeGen/MachineBasicBlock.h"
#include "rcc/CodeGen/MachineCFIInfo.h"
+#include "rcc/CodeGen/MachineCompilationStatus.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/MachineMemOperand.h"
+#include "rcc/CodeGen/MachineMetadata.h"
#include "rcc/CodeGen/MachineOperand.h"
#include "rcc/CodeGen/MachineRegisterInfo.h"
#include "rcc/CodeGen/MachineVerifier.h"
@@ -16,11 +25,11 @@
#include "rcc/CodeGen/TargetMIRTextInfo.h"
#include "rcc/CodeGen/TargetRegisterInfo.h"
#include "rcc/MIR/LLVMConstantSyntax.h"
-#include "rcc/MIR/MIDebugSyntax.h"
#include <algorithm>
#include <array>
#include <bit>
+#include <charconv>
#include <cstddef>
#include <cstdint>
#include <limits>
@@ -30,12 +39,1476 @@
#include <string>
#include <string_view>
#include <unordered_map>
+#include <unordered_set>
#include <utility>
+#include <variant>
#include <vector>
namespace rcc::mir {
namespace {
+enum class YAMLQuoting : std::uint8_t {
+ None,
+ Single,
+ Double,
+};
+
+[[nodiscard]] bool isASCIIAlphanumeric(unsigned char Character) noexcept {
+ return (Character >= '0' && Character <= '9') || (Character >= 'A' && Character <= 'Z') ||
+ (Character >= 'a' && Character <= 'z');
+}
+
+[[nodiscard]] bool isYAMLNumeric(std::string_view Value) noexcept {
+ if (Value.empty() || Value == "+" || Value == "-")
+ return false;
+ if (Value == ".nan" || Value == ".NaN" || Value == ".NAN")
+ return true;
+
+ std::string_view Tail = Value;
+ if (Tail.front() == '+' || Tail.front() == '-')
+ Tail.remove_prefix(1);
+ if (Tail == ".inf" || Tail == ".Inf" || Tail == ".INF")
+ return true;
+ if (Value.starts_with("0o")) {
+ return Value.size() > 2U &&
+ Value.substr(2).find_first_not_of("01234567") == std::string_view::npos;
+ }
+ if (Value.starts_with("0x")) {
+ return Value.size() > 2U &&
+ Value.substr(2).find_first_not_of("0123456789abcdefABCDEF") == std::string_view::npos;
+ }
+
+ if (Tail.empty() || Tail.front() == 'e' || Tail.front() == 'E')
+ return false;
+ std::size_t Position = 0;
+ if (Tail.front() == '.') {
+ ++Position;
+ if (Position == Tail.size() || Tail[Position] < '0' || Tail[Position] > '9')
+ return false;
+ while (Position < Tail.size() && Tail[Position] >= '0' && Tail[Position] <= '9')
+ ++Position;
+ } else {
+ while (Position < Tail.size() && Tail[Position] >= '0' && Tail[Position] <= '9')
+ ++Position;
+ if (Position == 0)
+ return false;
+ }
+
+ if (Position < Tail.size() && Tail[Position] == '.') {
+ ++Position;
+ while (Position < Tail.size() && Tail[Position] >= '0' && Tail[Position] <= '9')
+ ++Position;
+ }
+ if (Position == Tail.size())
+ return true;
+ if (Tail[Position] != 'e' && Tail[Position] != 'E')
+ return false;
+ ++Position;
+ if (Position < Tail.size() && (Tail[Position] == '+' || Tail[Position] == '-'))
+ ++Position;
+ const std::size_t ExponentBegin = Position;
+ while (Position < Tail.size() && Tail[Position] >= '0' && Tail[Position] <= '9')
+ ++Position;
+ return Position == Tail.size() && Position != ExponentBegin;
+}
+
+[[nodiscard]] YAMLQuoting yamlQuoting(std::string_view Value) noexcept {
+ if (Value.empty())
+ return YAMLQuoting::Single;
+
+ YAMLQuoting Result = YAMLQuoting::None;
+ const auto IsSpace = [](unsigned char Character) {
+ return Character == ' ' || Character == '\t' || Character == '\r' || Character == '\n' ||
+ Character == '\f' || Character == '\v';
+ };
+ if (IsSpace(static_cast<unsigned char>(Value.front())) ||
+ IsSpace(static_cast<unsigned char>(Value.back())) || Value == "null" || Value == "Null" ||
+ Value == "NULL" || Value == "~" || Value == "true" || Value == "True" || Value == "TRUE" ||
+ Value == "false" || Value == "False" || Value == "FALSE" || isYAMLNumeric(Value)) {
+ Result = YAMLQuoting::Single;
+ }
+
+ switch (Value.front()) {
+ case '-':
+ case '?':
+ case ':':
+ case '\\':
+ case ',':
+ case '[':
+ case ']':
+ case '{':
+ case '}':
+ case '#':
+ case '&':
+ case '*':
+ case '!':
+ case '|':
+ case '>':
+ case '\'':
+ case '"':
+ case '%':
+ case '@':
+ case '`':
+ Result = YAMLQuoting::Single;
+ break;
+ default:
+ break;
+ }
+
+ for (const char RawCharacter : Value) {
+ const auto Character = static_cast<unsigned char>(RawCharacter);
+ if (isASCIIAlphanumeric(Character))
+ continue;
+ switch (Character) {
+ case '_':
+ case '-':
+ case '^':
+ case '.':
+ case ',':
+ case ' ':
+ case '\t':
+ continue;
+ case '\n':
+ case '\r':
+ case 0x7f:
+ return YAMLQuoting::Double;
+ default:
+ if (Character <= 0x1fU || (Character & 0x80U) != 0)
+ return YAMLQuoting::Double;
+ Result = YAMLQuoting::Single;
+ break;
+ }
+ }
+ return Result;
+}
+
+[[nodiscard]] std::string yamlScalar(std::string_view Value) {
+ const YAMLQuoting Quoting = yamlQuoting(Value);
+ if (Quoting == YAMLQuoting::None)
+ return std::string(Value);
+
+ std::string Result;
+ Result.reserve(Value.size() + 2U);
+ const char Quote = Quoting == YAMLQuoting::Single ? '\'' : '"';
+ Result.push_back(Quote);
+ for (const char Character : Value) {
+ if (Quoting == YAMLQuoting::Single) {
+ Result.push_back(Character);
+ if (Character == '\'')
+ Result.push_back('\'');
+ continue;
+ }
+ switch (Character) {
+ case '\\':
+ Result.append("\\\\");
+ break;
+ case '"':
+ Result.append("\\\"");
+ break;
+ case '\0':
+ Result.append("\\0");
+ break;
+ case '\a':
+ Result.append("\\a");
+ break;
+ case '\b':
+ Result.append("\\b");
+ break;
+ case '\t':
+ Result.append("\\t");
+ break;
+ case '\n':
+ Result.append("\\n");
+ break;
+ case '\v':
+ Result.append("\\v");
+ break;
+ case '\f':
+ Result.append("\\f");
+ break;
+ case '\r':
+ Result.append("\\r");
+ break;
+ case '\x1b':
+ Result.append("\\e");
+ break;
+ default: {
+ const auto Byte = static_cast<unsigned char>(Character);
+ if (Byte < 0x20U || Byte == 0x7fU) {
+ static constexpr std::string_view Hex = "0123456789ABCDEF";
+ Result.append("\\x");
+ Result.push_back(Hex[Byte >> 4U]);
+ Result.push_back(Hex[Byte & 0xfU]);
+ } else {
+ Result.push_back(Character);
+ }
+ break;
+ }
+ }
+ }
+ Result.push_back(Quote);
+ return Result;
+}
+
+void appendBlockScalar(std::string &Output, std::string_view Text, std::size_t Indent) {
+ std::size_t Position = 0;
+ do {
+ Output.append(Indent, ' ');
+ const std::size_t End = Text.find('\n', Position);
+ if (End == std::string_view::npos) {
+ Output.append(Text.substr(Position));
+ Output.push_back('\n');
+ return;
+ }
+ Output.append(Text.substr(Position, End - Position));
+ Output.push_back('\n');
+ Position = End + 1U;
+ } while (Position < Text.size());
+}
+
+[[nodiscard]] std::optional<std::uint32_t> parseDecimal32(std::string_view Text) noexcept {
+ std::uint32_t Value = 0;
+ if (Text.empty())
+ return std::nullopt;
+ const auto [End, Error] = std::from_chars(Text.data(), Text.data() + Text.size(), Value, 10);
+ if (Error != std::errc{} || End != Text.data() + Text.size())
+ return std::nullopt;
+ return Value;
+}
+
+[[nodiscard]] bool isDefaultFrameState(const MachineFrameState &State) {
+ return State == MachineFrameState{};
+}
+
+class MIRDocumentPrinter final {
+public:
+ MIRDocumentPrinter(const MIRDocument &Document, const TargetDescription &Target,
+ MIRDocumentPrinterOptions Options)
+ : Document(Document), Target(Target), Options(std::move(Options)) {}
+
+ [[nodiscard]] Expected<std::string> run();
+
+private:
+ struct InstructionLocation final {
+ std::uint32_t Block = 0;
+ std::uint32_t Offset = 0;
+ };
+
+ [[nodiscard]] Expected<void> printFunction(const MachineFunction &Function,
+ const MIRFunctionDocumentInfo *DocumentInfo);
+ [[nodiscard]] Expected<MIParserContext> createBodyContext(const MachineFunction &Function) const;
+ [[nodiscard]] Expected<void> printRegisters(const MachineFunction &Function,
+ const MIParserContext &Context);
+ [[nodiscard]] Expected<void> printFrameInfo(const MachineFunction &Function,
+ const MIParserContext &Context);
+ [[nodiscard]] Expected<void> printStackObjects(const MachineFunction &Function,
+ const MIParserContext &Context, bool Fixed);
+ [[nodiscard]] Expected<void> printConstants(const MachineFunction &Function,
+ const MIParserContext &Context);
+ [[nodiscard]] Expected<void> printJumpTable(const MachineFunction &Function,
+ const MIParserContext &Context);
+ [[nodiscard]] Expected<void> printMetadata(const MachineFunction &Function,
+ const MIParserContext &Context);
+ [[nodiscard]] Expected<void> printTargetState(const MachineFunction &Function);
+ [[nodiscard]] Expected<void> printFunctionRecords(const MachineFunction &Function,
+ const MIParserContext &Context);
+ [[nodiscard]] Expected<void> verifyOmittedEmbeddedIRLayout(const MachineFunction &Function) const;
+
+ [[nodiscard]] Expected<std::string> physicalRegister(PhysicalRegister Register) const;
+ [[nodiscard]] Expected<std::string> frameReference(FrameIndex Index,
+ const MIParserContext &Context) const;
+ [[nodiscard]] Expected<std::string> metadataReference(MachineMetadataReference Reference,
+ const MIParserContext &Context) const;
+ [[nodiscard]] Expected<std::string> metadataValue(const MachineMetadataValue &Value,
+ const MIParserContext &Context) const;
+ [[nodiscard]] Expected<std::string> debugLocation(const MachineDebugLocation &Location,
+ const MIParserContext &Context) const;
+ [[nodiscard]] Expected<std::string> diLocation(const MachineDILocation &Location,
+ std::size_t FrameIndex,
+ const MIParserContext &Context) const;
+ [[nodiscard]] Expected<InstructionLocation> instructionLocation(const MachineFunction &Function,
+ MachineInstrStableID ID) const;
+ [[nodiscard]] Expected<std::string> blockReference(const MachineFunction &Function,
+ MachineBlockStableID ID) const;
+ [[nodiscard]] Expected<std::string> targetNode(const MIRTargetNode &Node) const;
+ [[nodiscard]] const MIRFunctionDocumentInfo *
+ documentInfo(const MachineFunction &Function) const noexcept;
+
+ void printBoolean(std::string_view Key, bool Value, bool Always = false);
+ void printUnsigned(std::string_view Key, std::uint64_t Value, bool Always = false);
+ void printSigned(std::string_view Key, std::int64_t Value, bool Always = false);
+
+ const MIRDocument &Document;
+ const TargetDescription &Target;
+ MIRDocumentPrinterOptions Options;
+ std::string Output;
+};
+
+void MIRDocumentPrinter::printBoolean(std::string_view Key, bool Value, bool Always) {
+ if (!Always && Options.Mode == MIPrintMode::Readable && !Value)
+ return;
+ Output.append(Key);
+ Output.append(Value ? ": true\n" : ": false\n");
+}
+
+void MIRDocumentPrinter::printUnsigned(std::string_view Key, std::uint64_t Value, bool Always) {
+ if (!Always && Options.Mode == MIPrintMode::Readable && Value == 0)
+ return;
+ Output.append(Key);
+ Output.append(": ");
+ Output.append(std::to_string(Value));
+ Output.push_back('\n');
+}
+
+void MIRDocumentPrinter::printSigned(std::string_view Key, std::int64_t Value, bool Always) {
+ if (!Always && Options.Mode == MIPrintMode::Readable && Value == 0)
+ return;
+ Output.append(Key);
+ Output.append(": ");
+ Output.append(std::to_string(Value));
+ Output.push_back('\n');
+}
+
+Expected<std::string> MIRDocumentPrinter::physicalRegister(PhysicalRegister Register) const {
+ const auto Name = Target.registers().physicalRegisterMIRName(Register);
+ if (!Name) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "physical register has no canonical MIR name");
+ }
+ return "$" + std::string(*Name);
+}
+
+const MIRFunctionDocumentInfo *
+MIRDocumentPrinter::documentInfo(const MachineFunction &Function) const noexcept {
+ const auto Infos = Document.functionDocumentInfo();
+ const auto Found = std::ranges::find_if(Infos, [&Function](const MIRFunctionDocumentInfo &Info) {
+ return Info.Function == Function.stableID();
+ });
+ return Found == Infos.end() ? nullptr : &*Found;
+}
+
+Expected<void>
+MIRDocumentPrinter::verifyOmittedEmbeddedIRLayout(const MachineFunction &Function) const {
+ if (Options.EmitEmbeddedIR)
+ return {};
+
+ const EmbeddedIRReferenceIndex *Embedded = Document.embeddedIR();
+ if (Embedded == nullptr)
+ return {};
+
+ bool HasLayoutDependentContent = false;
+ for (const VirtualRegisterInfo &RegisterInfo : Function.registerInfo().virtualRegisters()) {
+ if (!RegisterInfo.IsTombstone && RegisterInfo.Type &&
+ RegisterInfo.Type->isPointerOrPointerVector()) {
+ HasLayoutDependentContent = true;
+ break;
+ }
+ }
+ if (!HasLayoutDependentContent) {
+ for (const MachineConstantPoolEntry &Entry : Function.constantPool().entries()) {
+ const MachineConstantPoolEntryDesc *Description = Entry.description();
+ if (Description != nullptr &&
+ std::holds_alternative<GenericConstantData>(Description->Value)) {
+ HasLayoutDependentContent = true;
+ break;
+ }
+ }
+ }
+ if (!HasLayoutDependentContent)
+ return {};
+
+ if (Options.DataLayout.empty()) {
+ return makeUnexpected(
+ ErrorCode::InvalidArgument,
+ "omitting embedded LLVM IR with layout-dependent MIR requires an explicit printer "
+ "DataLayout");
+ }
+
+ const LLVMConstantSyntaxOptions LayoutOptions{
+ .DataLayout = Options.DataLayout,
+ .NamedTypes = {},
+ };
+ for (const VirtualRegisterInfo &RegisterInfo : Function.registerInfo().virtualRegisters()) {
+ if (RegisterInfo.IsTombstone || !RegisterInfo.Type ||
+ !RegisterInfo.Type->isPointerOrPointerVector()) {
+ continue;
+ }
+ const auto AddressSpace = RegisterInfo.Type->addressSpace();
+ if (!AddressSpace) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "pointer virtual register has no address space");
+ }
+ auto PointerSize = llvmPointerSizeInBits(*AddressSpace, LayoutOptions);
+ if (!PointerSize) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "printer DataLayout cannot determine pointer width: " +
+ std::string(PointerSize.error().message()));
+ }
+ if (*PointerSize != RegisterInfo.Type->scalarBitWidth()) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "printer DataLayout does not match pointer virtual-register width");
+ }
+ }
+
+ for (const MachineConstantPoolEntry &Entry : Function.constantPool().entries()) {
+ const MachineConstantPoolEntryDesc *Description = Entry.description();
+ if (Description == nullptr)
+ continue;
+ const auto *Generic = std::get_if<GenericConstantData>(&Description->Value);
+ if (Generic == nullptr)
+ continue;
+ auto StorageSize = llvmConstantStorageSize(Generic->Type, LayoutOptions);
+ if (!StorageSize) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "printer DataLayout cannot lay out generic constant: " +
+ std::string(StorageSize.error().message()));
+ }
+ if (*StorageSize != Description->Size) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "printer DataLayout does not match generic constant storage size");
+ }
+ }
+ return {};
+}
+
+Expected<MIParserContext>
+MIRDocumentPrinter::createBodyContext(const MachineFunction &Function) const {
+ MIParserContext Context;
+ Context.SyncScopes.assign(Document.syncScopes().begin(), Document.syncScopes().end());
+ const auto VirtualRegisters = Function.registerInfo().virtualRegisters();
+ for (std::size_t Index = 0; Index < VirtualRegisters.size(); ++Index) {
+ if (VirtualRegisters[Index].IsTombstone)
+ continue;
+ auto Reg = Register::makeVirtual(Index);
+ if (!Reg)
+ return std::unexpected(Reg.error());
+ if (Index > std::numeric_limits<std::uint32_t>::max()) {
+ return makeUnexpected(ErrorCode::Unsupported,
+ "MIR cannot represent more than 2^32 virtual registers");
+ }
+ Context.VirtualRegisters.push_back({.TextID = static_cast<std::uint32_t>(Index), .Reg = *Reg});
+ }
+
+ std::uint32_t StackID = 0;
+ std::uint32_t FixedID = 0;
+ for (const StackObject &Object : Function.frameInfo().objects()) {
+ const StackObjectDesc *Description = Object.description();
+ if (Description == nullptr)
+ continue;
+ MIFrameObjectSlot Slot{
+ .TextID = Object.isFixed() ? FixedID++ : StackID++,
+ .Index = Object.index(),
+ .Name = Description->AllocaName,
+ };
+ (Object.isFixed() ? Context.FixedStackObjects : Context.StackObjects)
+ .push_back(std::move(Slot));
+ }
+
+ std::uint32_t ConstantID = 0;
+ for (const MachineConstantPoolEntry &Entry : Function.constantPool().entries()) {
+ if (Entry.description() != nullptr)
+ Context.ConstantPoolItems.push_back({.TextID = ConstantID++, .Index = Entry.index()});
+ }
+ std::uint32_t JumpTableID = 0;
+ for (const MachineJumpTableEntry &Entry : Function.jumpTableInfo().tables()) {
+ if (Entry.description() != nullptr)
+ Context.JumpTables.push_back({.TextID = JumpTableID++, .Index = Entry.index()});
+ }
+
+ std::unordered_set<std::uint32_t> EmbeddedMetadataSlots;
+ if (const EmbeddedIRReferenceIndex *Embedded = Document.embeddedIR()) {
+ for (const EmbeddedIRMetadataDefinition &Definition : Embedded->metadataDefinitions()) {
+ const auto Slot = parseDecimal32(Definition.Name);
+ if (Slot) {
+ if (!EmbeddedMetadataSlots.insert(*Slot).second) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "embedded LLVM IR metadata slots are not unique");
+ }
+ Context.Metadata.push_back(
+ {.TextID = *Slot, .Reference = MachineMetadataReference::embeddedIR(*Slot)});
+ }
+ }
+ }
+
+ std::unordered_set<std::uint32_t> UsedMetadataSlots = EmbeddedMetadataSlots;
+ for (const MachineMetadataNode &Node : Function.metadata().nodes()) {
+ if (!EmbeddedMetadataSlots.contains(Node.Slot))
+ UsedMetadataSlots.insert(Node.Slot);
+ }
+ std::uint64_t NextMetadataSlot = 0;
+ for (const MachineMetadataNode &Node : Function.metadata().nodes()) {
+ std::uint32_t TextID = Node.Slot;
+ if (EmbeddedMetadataSlots.contains(TextID)) {
+ while (NextMetadataSlot <= std::numeric_limits<std::uint32_t>::max() &&
+ UsedMetadataSlots.contains(static_cast<std::uint32_t>(NextMetadataSlot))) {
+ ++NextMetadataSlot;
+ }
+ if (NextMetadataSlot > std::numeric_limits<std::uint32_t>::max()) {
+ return makeUnexpected(ErrorCode::Unsupported, "MIR metadata text slot space is exhausted");
+ }
+ TextID = static_cast<std::uint32_t>(NextMetadataSlot);
+ UsedMetadataSlots.insert(TextID);
+ ++NextMetadataSlot;
+ }
+ Context.Metadata.push_back(
+ {.TextID = TextID, .Reference = MachineMetadataReference::machineFunction(Node.Slot)});
+ }
+ return Context;
+}
+
+Expected<std::string> MIRDocumentPrinter::frameReference(FrameIndex Index,
+ const MIParserContext &Context) const {
+ const auto Stack = std::ranges::find_if(
+ Context.StackObjects, [Index](const MIFrameObjectSlot &Slot) { return Slot.Index == Index; });
+ if (Stack != Context.StackObjects.end())
+ return "%stack." + std::to_string(Stack->TextID);
+ const auto Fixed =
+ std::ranges::find_if(Context.FixedStackObjects,
+ [Index](const MIFrameObjectSlot &Slot) { return Slot.Index == Index; });
+ if (Fixed != Context.FixedStackObjects.end())
+ return "%fixed-stack." + std::to_string(Fixed->TextID);
+ return makeUnexpected(ErrorCode::VerificationError,
+ "frame state references an unknown stack object");
+}
+
+Expected<std::string> MIRDocumentPrinter::metadataReference(MachineMetadataReference Reference,
+ const MIParserContext &Context) const {
+ if (!Reference.isValid())
+ return makeUnexpected(ErrorCode::VerificationError, "invalid metadata reference");
+ const auto Slot =
+ std::ranges::find_if(Context.Metadata, [Reference](const MIMetadataSlot &Candidate) {
+ return Candidate.Reference == Reference;
+ });
+ if (Slot == Context.Metadata.end()) {
+ return makeUnexpected(ErrorCode::VerificationError, "metadata reference has no MIR text slot");
+ }
+ return "!" + std::to_string(Slot->TextID);
+}
+
+Expected<std::string> MIRDocumentPrinter::metadataValue(const MachineMetadataValue &Value,
+ const MIParserContext &Context) const {
+ if (const MachineMetadataReference *Reference = Value.reference())
+ return metadataReference(*Reference, Context);
+ const MachineDIExpression *Expression = Value.expression();
+ if (Expression == nullptr)
+ return makeUnexpected(ErrorCode::VerificationError, "metadata value is not printable");
+ return formatDIExpression(Expression->elements());
+}
+
+Expected<std::string> MIRDocumentPrinter::diLocation(const MachineDILocation &Location,
+ std::size_t FrameIndex,
+ const MIParserContext &Context) const {
+ if (FrameIndex >= Location.inlineChain().size()) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "inline DILocation has an invalid frame chain");
+ }
+ const MachineDILocationFrame &Frame = Location.inlineChain()[FrameIndex];
+ auto Scope = metadataReference(Frame.Scope, Context);
+ if (!Scope)
+ return std::unexpected(Scope.error());
+ std::string Text = "!DILocation(line: " + std::to_string(Frame.Line);
+ if (Options.Mode == MIPrintMode::Complete || Frame.Column != 0)
+ Text.append(", column: " + std::to_string(Frame.Column));
+ Text.append(", scope: " + *Scope);
+ if (FrameIndex + 1U < Location.inlineChain().size()) {
+ auto Nested = diLocation(Location, FrameIndex + 1U, Context);
+ if (!Nested)
+ return std::unexpected(Nested.error());
+ Text.append(", inlinedAt: " + *Nested);
+ } else if (Location.terminalInlinedAt()) {
+ auto Terminal = metadataReference(*Location.terminalInlinedAt(), Context);
+ if (!Terminal)
+ return std::unexpected(Terminal.error());
+ Text.append(", inlinedAt: " + *Terminal);
+ }
+ if (Options.Mode == MIPrintMode::Complete || Frame.IsImplicitCode)
+ Text.append(Frame.IsImplicitCode ? ", isImplicitCode: true" : ", isImplicitCode: false");
+ if (Options.Mode == MIPrintMode::Complete || Frame.AtomGroup != 0)
+ Text.append(", atomGroup: " + std::to_string(Frame.AtomGroup));
+ if (Options.Mode == MIPrintMode::Complete || Frame.AtomRank != 0)
+ Text.append(", atomRank: " + std::to_string(Frame.AtomRank));
+ Text.push_back(')');
+ return Text;
+}
+
+Expected<std::string> MIRDocumentPrinter::debugLocation(const MachineDebugLocation &Location,
+ const MIParserContext &Context) const {
+ if (const MachineMetadataReference *Reference = Location.reference())
+ return metadataReference(*Reference, Context);
+ if (const MachineDILocation *Inline = Location.inlineLocation())
+ return diLocation(*Inline, 0, Context);
+ return makeUnexpected(ErrorCode::VerificationError, "debug location is not printable");
+}
+
+Expected<MIRDocumentPrinter::InstructionLocation>
+MIRDocumentPrinter::instructionLocation(const MachineFunction &Function,
+ MachineInstrStableID ID) const {
+ std::uint64_t BlockNumber = 0;
+ for (const MachineBasicBlock *Block = Function.firstBlock(); Block != nullptr;
+ Block = Block->next(), ++BlockNumber) {
+ if (BlockNumber > std::numeric_limits<std::uint32_t>::max())
+ break;
+ std::uint64_t Offset = 0;
+ for (const MachineInstr *Instruction = Block->firstInstruction(); Instruction != nullptr;
+ Instruction = Instruction->next(), ++Offset) {
+ if (Instruction->stableID() != ID)
+ continue;
+ if (Offset > std::numeric_limits<std::uint32_t>::max()) {
+ return makeUnexpected(ErrorCode::Unsupported, "MIR instruction offset exceeds 32 bits");
+ }
+ return InstructionLocation{static_cast<std::uint32_t>(BlockNumber),
+ static_cast<std::uint32_t>(Offset)};
+ }
+ }
+ return makeUnexpected(ErrorCode::VerificationError,
+ "function record references an unlinked instruction");
+}
+
+Expected<std::string> MIRDocumentPrinter::blockReference(const MachineFunction &Function,
+ MachineBlockStableID ID) const {
+ std::uint64_t Number = 0;
+ for (const MachineBasicBlock *Block = Function.firstBlock(); Block != nullptr;
+ Block = Block->next(), ++Number) {
+ if (Block->stableID() == ID) {
+ if (Number > std::numeric_limits<std::uint32_t>::max())
+ break;
+ return "%bb." + std::to_string(Number);
+ }
+ }
+ return makeUnexpected(ErrorCode::VerificationError,
+ "frame record references an unlinked basic block");
+}
+
+Expected<std::string> MIRDocumentPrinter::targetNode(const MIRTargetNode &Node) const {
+ switch (Node.Kind) {
+ case MIRTargetNodeKind::Null:
+ return std::string("null");
+ case MIRTargetNodeKind::Scalar:
+ return yamlScalar(Node.Scalar);
+ case MIRTargetNodeKind::Sequence: {
+ std::string Text = "[ ";
+ for (std::size_t Index = 0; Index < Node.Sequence.size(); ++Index) {
+ if (Index != 0)
+ Text.append(", ");
+ auto Element = targetNode(Node.Sequence[Index]);
+ if (!Element)
+ return std::unexpected(Element.error());
+ Text.append(*Element);
+ }
+ Text.append(" ]");
+ return Text;
+ }
+ case MIRTargetNodeKind::Mapping: {
+ std::string Text = "{ ";
+ for (std::size_t Index = 0; Index < Node.Mapping.size(); ++Index) {
+ if (Index != 0)
+ Text.append(", ");
+ Text.append(yamlScalar(Node.Mapping[Index].Key));
+ Text.append(": ");
+ auto Value = targetNode(Node.Mapping[Index].Value);
+ if (!Value)
+ return std::unexpected(Value.error());
+ Text.append(*Value);
+ }
+ Text.append(" }");
+ return Text;
+ }
+ case MIRTargetNodeKind::Count:
+ break;
+ }
+ return makeUnexpected(ErrorCode::VerificationError, "target MIR node has an invalid kind");
+}
+
+Expected<void> MIRDocumentPrinter::printRegisters(const MachineFunction &Function,
+ const MIParserContext &Context) {
+ if (Context.VirtualRegisters.empty()) {
+ if (Options.Mode == MIPrintMode::Complete)
+ Output.append("registers: []\n");
+ } else {
+ Output.append("registers:\n");
+ for (const MIVirtualRegisterSlot &Slot : Context.VirtualRegisters) {
+ auto InfoResult = Function.registerInfo().virtualRegisterInfo(Slot.Reg);
+ if (!InfoResult)
+ return std::unexpected(InfoResult.error());
+ const VirtualRegisterInfo &Info = InfoResult->get();
+ std::string Class = "_";
+ if (Info.RegClass) {
+ const auto Name = Target.registers().registerClassMIRName(*Info.RegClass);
+ if (!Name)
+ return makeUnexpected(ErrorCode::VerificationError,
+ "virtual register class has no canonical MIR name");
+ Class = *Name;
+ } else if (Info.Bank) {
+ const auto Name = Target.registers().registerBankMIRName(*Info.Bank);
+ if (!Name)
+ return makeUnexpected(ErrorCode::VerificationError,
+ "virtual register bank has no canonical MIR name");
+ Class = *Name;
+ }
+ if (Info.AllocationHints.size() > 1) {
+ return makeUnexpected(
+ ErrorCode::Unsupported,
+ "fixed LLVM MIR can serialize only one simple preferred-register hint");
+ }
+
+ Output.append(" - { id: ");
+ Output.append(std::to_string(Slot.TextID));
+ Output.append(", class: ");
+ Output.append(yamlScalar(Class));
+ if (!Info.AllocationHints.empty()) {
+ auto Preferred = physicalRegister(Info.AllocationHints.front());
+ if (!Preferred)
+ return std::unexpected(Preferred.error());
+ Output.append(", preferred-register: ");
+ Output.append(yamlScalar(*Preferred));
+ }
+ if (Info.TargetFlags != 0) {
+ auto Names = Target.mirText().virtualRegisterFlagNames(Info.TargetFlags);
+ if (!Names)
+ return std::unexpected(Names.error());
+ Output.append(", flags: [ ");
+ for (std::size_t Index = 0; Index < Names->size(); ++Index) {
+ if (Index != 0)
+ Output.append(", ");
+ Output.append(yamlScalar((*Names)[Index]));
+ }
+ Output.append(" ]");
+ }
+ Output.append(" }\n");
+ }
+ }
+
+ if (Function.registerInfo().liveIns().empty()) {
+ if (Options.Mode == MIPrintMode::Complete)
+ Output.append("liveins: []\n");
+ } else {
+ Output.append("liveins:\n");
+ for (const PhysicalRegisterLiveIn &LiveIn : Function.registerInfo().liveIns()) {
+ auto Name = physicalRegister(LiveIn.PhysReg);
+ if (!Name)
+ return std::unexpected(Name.error());
+ Output.append(" - { reg: ");
+ Output.append(yamlScalar(*Name));
+ if (LiveIn.InitialVirtualRegister) {
+ const auto Slot = std::ranges::find_if(
+ Context.VirtualRegisters, [&LiveIn](const MIVirtualRegisterSlot &Candidate) {
+ return Candidate.Reg == *LiveIn.InitialVirtualRegister;
+ });
+ if (Slot == Context.VirtualRegisters.end()) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "live-in references an unknown virtual register");
+ }
+ Output.append(", virtual-reg: ");
+ Output.append(yamlScalar("%" + std::to_string(Slot->TextID)));
+ }
+ Output.append(" }\n");
+ }
+ }
+
+ if (Function.records().hasCalleeSavedRegisters()) {
+ Output.append("calleeSavedRegisters: [ ");
+ const auto Registers = Function.records().calleeSavedRegisters();
+ for (std::size_t Index = 0; Index < Registers.size(); ++Index) {
+ if (Index != 0)
+ Output.append(", ");
+ auto Name = physicalRegister(Registers[Index]);
+ if (!Name)
+ return std::unexpected(Name.error());
+ Output.append(yamlScalar(*Name));
+ }
+ Output.append(" ]\n");
+ }
+ return {};
+}
+
+Expected<void> MIRDocumentPrinter::printFrameInfo(const MachineFunction &Function,
+ const MIParserContext &Context) {
+ const MachineFrameState &State = Function.frameInfo().state();
+ if (Options.Mode == MIPrintMode::Readable && isDefaultFrameState(State))
+ return {};
+ if (State.MaxCallFrameSize &&
+ *State.MaxCallFrameSize > std::numeric_limits<std::uint32_t>::max()) {
+ return makeUnexpected(ErrorCode::Unsupported,
+ "fixed LLVM MIR maxCallFrameSize is limited to 32 bits");
+ }
+ Output.append("frameInfo:\n");
+ const auto Bool = [this](std::string_view Key, bool Value) {
+ if (Options.Mode == MIPrintMode::Readable && !Value)
+ return;
+ Output.append(" ");
+ Output.append(Key);
+ Output.append(Value ? ": true\n" : ": false\n");
+ };
+ const auto Unsigned = [this](std::string_view Key, std::uint64_t Value) {
+ if (Options.Mode == MIPrintMode::Readable && Value == 0)
+ return;
+ Output.append(" ");
+ Output.append(Key);
+ Output.append(": ");
+ Output.append(std::to_string(Value));
+ Output.push_back('\n');
+ };
+
+ Bool("isFrameAddressTaken", State.IsFrameAddressTaken);
+ Bool("isReturnAddressTaken", State.IsReturnAddressTaken);
+ Bool("hasStackMap", State.HasStackMap);
+ Bool("hasPatchPoint", State.HasPatchPoint);
+ Unsigned("stackSize", State.StackSize);
+ if (Options.Mode == MIPrintMode::Complete || State.OffsetAdjustment != 0) {
+ Output.append(" offsetAdjustment: ");
+ Output.append(std::to_string(State.OffsetAdjustment));
+ Output.push_back('\n');
+ }
+ Unsigned("maxAlignment", State.MaxAlignment ? State.MaxAlignment->bytes() : 0);
+ Bool("adjustsStack", State.AdjustsStack);
+ Bool("hasCalls", State.HasCalls);
+ if (Options.Mode == MIPrintMode::Complete ||
+ State.FramePointerPolicy != MachineFramePointerPolicy::None) {
+ Output.append(" framePointerPolicy: ");
+ Output.append(getMachineFramePointerPolicyName(State.FramePointerPolicy));
+ Output.push_back('\n');
+ }
+ if (Options.Mode == MIPrintMode::Complete || State.StackProtector) {
+ std::string Reference;
+ if (State.StackProtector) {
+ auto PrintedReference = frameReference(*State.StackProtector, Context);
+ if (!PrintedReference)
+ return std::unexpected(PrintedReference.error());
+ Reference = std::move(*PrintedReference);
+ }
+ Output.append(" stackProtector: ");
+ Output.append(yamlScalar(Reference));
+ Output.push_back('\n');
+ }
+ if (Options.Mode == MIPrintMode::Complete || State.FunctionContext) {
+ std::string Reference;
+ if (State.FunctionContext) {
+ auto PrintedReference = frameReference(*State.FunctionContext, Context);
+ if (!PrintedReference)
+ return std::unexpected(PrintedReference.error());
+ Reference = std::move(*PrintedReference);
+ }
+ Output.append(" functionContext: ");
+ Output.append(yamlScalar(Reference));
+ Output.push_back('\n');
+ }
+ if (Options.Mode == MIPrintMode::Complete || State.MaxCallFrameSize) {
+ Unsigned("maxCallFrameSize",
+ State.MaxCallFrameSize.value_or(std::numeric_limits<std::uint32_t>::max()));
+ }
+ Unsigned("cvBytesOfCalleeSavedRegisters", State.CVBytesOfCalleeSavedRegisters);
+ Bool("hasOpaqueSPAdjustment", State.HasOpaqueSPAdjustment);
+ Bool("hasVAStart", State.HasVAStart);
+ Bool("hasMustTailInVarArgFunc", State.HasMustTailInVarArgFunc);
+ Bool("hasTailCall", State.HasTailCall);
+ Bool("isCalleeSavedInfoValid", State.IsCalleeSavedInfoValid);
+ Unsigned("localFrameSize", State.LocalFrameSize);
+
+ const auto PrintPoints =
+ [this, &Function](std::string_view Key,
+ std::span<const MachineFrameSaveRestorePoint> Points) -> Expected<void> {
+ if (Points.empty())
+ return {};
+ Output.append(" ");
+ Output.append(Key);
+ Output.append(":\n");
+ for (const MachineFrameSaveRestorePoint &Point : Points) {
+ auto Block = blockReference(Function, Point.Block);
+ if (!Block)
+ return std::unexpected(Block.error());
+ Output.append(" - { point: ");
+ Output.append(yamlScalar(*Block));
+ if (!Point.Registers.empty()) {
+ Output.append(", registers: [ ");
+ for (std::size_t Index = 0; Index < Point.Registers.size(); ++Index) {
+ if (Index != 0)
+ Output.append(", ");
+ auto Name = physicalRegister(Point.Registers[Index]);
+ if (!Name)
+ return std::unexpected(Name.error());
+ Output.append(yamlScalar(*Name));
+ }
+ Output.append(" ]");
+ }
+ Output.append(" }\n");
+ }
+ return {};
+ };
+ if (auto Printed = PrintPoints("savePoint", State.SavePoints); !Printed)
+ return Printed;
+ return PrintPoints("restorePoint", State.RestorePoints);
+}
+
+Expected<void> MIRDocumentPrinter::printStackObjects(const MachineFunction &Function,
+ const MIParserContext &Context, bool Fixed) {
+ const auto &Slots = Fixed ? Context.FixedStackObjects : Context.StackObjects;
+ if (Slots.empty()) {
+ if (Options.Mode == MIPrintMode::Complete)
+ Output.append(Fixed ? "fixedStack: []\n" : "stack: []\n");
+ return {};
+ }
+ Output.append(Fixed ? "fixedStack:\n" : "stack:\n");
+ for (const MIFrameObjectSlot &Slot : Slots) {
+ const StackObject *Object = Function.frameInfo().object(Slot.Index);
+ const StackObjectDesc *Description = Object == nullptr ? nullptr : Object->description();
+ if (Description == nullptr)
+ return makeUnexpected(ErrorCode::VerificationError, "MIR stack slot is a tombstone");
+ if (Description->IsDead || Description->AddressTaken || !Description->Lifetime.empty() ||
+ Description->TargetFlags != 0) {
+ return makeUnexpected(ErrorCode::Unsupported,
+ "stack object contains state not represented by fixed LLVM MIR");
+ }
+
+ std::string_view Type;
+ switch (Description->Kind) {
+ case StackObjectKind::Default:
+ case StackObjectKind::FixedABI:
+ Type = "default";
+ break;
+ case StackObjectKind::FixedSpillSlot:
+ case StackObjectKind::SpillSlot:
+ Type = "spill-slot";
+ break;
+ case StackObjectKind::VariableSized:
+ Type = "variable-sized";
+ break;
+ case StackObjectKind::DynamicAlloca:
+ case StackObjectKind::CalleeSaved:
+ case StackObjectKind::EmergencySpill:
+ case StackObjectKind::Target:
+ case StackObjectKind::Count:
+ return makeUnexpected(ErrorCode::Unsupported,
+ "stack object kind has no fixed LLVM MIR representation");
+ }
+
+ Output.append(" - { id: ");
+ Output.append(std::to_string(Slot.TextID));
+ if (!Fixed && Description->AllocaName) {
+ Output.append(", name: ");
+ Output.append(yamlScalar(*Description->AllocaName));
+ }
+ if (Options.Mode == MIPrintMode::Complete || Type != "default") {
+ Output.append(", type: ");
+ Output.append(Type);
+ }
+ if (Options.Mode == MIPrintMode::Complete || Description->Offset.value_or(0) != 0) {
+ Output.append(", offset: ");
+ Output.append(std::to_string(Description->Offset.value_or(0)));
+ }
+ if (Description->Kind != StackObjectKind::VariableSized) {
+ Output.append(", size: ");
+ Output.append(std::to_string(Description->Size.value_or(0)));
+ }
+ if (Options.Mode == MIPrintMode::Complete || Description->Align.bytes() != 1) {
+ Output.append(", alignment: ");
+ Output.append(std::to_string(Description->Align.bytes()));
+ }
+ if (Options.Mode == MIPrintMode::Complete || Description->Stack != StackID::defaultStack()) {
+ Output.append(", stack-id: ");
+ Output.append(getStackIDName(Description->Stack));
+ }
+ if (Fixed && Description->Kind != StackObjectKind::FixedSpillSlot) {
+ if (Options.Mode == MIPrintMode::Complete || Description->IsImmutable)
+ Output.append(Description->IsImmutable ? ", isImmutable: true" : ", isImmutable: false");
+ if (Options.Mode == MIPrintMode::Complete || Description->IsAliased)
+ Output.append(Description->IsAliased ? ", isAliased: true" : ", isAliased: false");
+ } else if (!Fixed && (Description->IsImmutable || Description->IsAliased)) {
+ return makeUnexpected(ErrorCode::Unsupported,
+ "ordinary stack object immutable/aliased state is not serialized");
+ }
+ if (Description->CalleeSavedRegister) {
+ auto Name = physicalRegister(*Description->CalleeSavedRegister);
+ if (!Name)
+ return std::unexpected(Name.error());
+ Output.append(", callee-saved-register: ");
+ Output.append(yamlScalar(*Name));
+ }
+ if (Options.Mode == MIPrintMode::Complete || !Description->CalleeSavedRestored) {
+ Output.append(Description->CalleeSavedRestored ? ", callee-saved-restored: true"
+ : ", callee-saved-restored: false");
+ }
+ if (!Fixed && Description->LocalOffset) {
+ Output.append(", local-offset: ");
+ Output.append(std::to_string(*Description->LocalOffset));
+ }
+ if (Description->DebugInfo) {
+ const MachineVariableDebugInfo &Debug = *Description->DebugInfo;
+ if (Debug.Variable) {
+ auto Value = metadataReference(*Debug.Variable, Context);
+ if (!Value)
+ return std::unexpected(Value.error());
+ Output.append(", debug-info-variable: ");
+ Output.append(yamlScalar(*Value));
+ }
+ if (Debug.Expression) {
+ auto Value = metadataValue(*Debug.Expression, Context);
+ if (!Value)
+ return std::unexpected(Value.error());
+ Output.append(", debug-info-expression: ");
+ Output.append(yamlScalar(*Value));
+ }
+ if (Debug.Location) {
+ auto Value = debugLocation(*Debug.Location, Context);
+ if (!Value)
+ return std::unexpected(Value.error());
+ Output.append(", debug-info-location: ");
+ Output.append(yamlScalar(*Value));
+ }
+ }
+ Output.append(" }\n");
+ }
+ return {};
+}
+
+Expected<void> MIRDocumentPrinter::printConstants(const MachineFunction &Function,
+ const MIParserContext &Context) {
+ if (Context.ConstantPoolItems.empty()) {
+ if (Options.Mode == MIPrintMode::Complete)
+ Output.append("constants: []\n");
+ return {};
+ }
+ Output.append("constants:\n");
+ for (const MIConstantPoolSlot &Slot : Context.ConstantPoolItems) {
+ const MachineConstantPoolEntry *Entry = Function.constantPool().entry(Slot.Index);
+ const MachineConstantPoolEntryDesc *Description =
+ Entry == nullptr ? nullptr : Entry->description();
+ if (Description == nullptr)
+ return makeUnexpected(ErrorCode::VerificationError, "constant-pool slot is a tombstone");
+ if (Description->Merge != ConstantPoolMergeKind::Never ||
+ Description->Relocation != ConstantPoolRelocationMode::None ||
+ Description->Placement != ConstantPoolPlacement{}) {
+ return makeUnexpected(ErrorCode::Unsupported,
+ "constant-pool placement state is not represented by fixed LLVM MIR");
+ }
+
+ bool IsTargetSpecific = false;
+ Expected<std::string> Value =
+ makeUnexpected(ErrorCode::Unsupported, "constant-pool kind is not MIR serializable");
+ if (const auto *Generic = std::get_if<GenericConstantData>(&Description->Value)) {
+ Value = printLLVMConstant(*Generic);
+ } else if (const auto *TargetConstantValue = std::get_if<TargetConstant>(&Description->Value)) {
+ IsTargetSpecific = true;
+ Value = Target.mirText().printTargetConstant(*TargetConstantValue);
+ }
+ if (!Value)
+ return std::unexpected(Value.error());
+
+ Output.append(" - id: ");
+ Output.append(std::to_string(Slot.TextID));
+ Output.append("\n value: ");
+ Output.append(yamlScalar(*Value));
+ Output.append("\n alignment: ");
+ Output.append(std::to_string(Description->Align.bytes()));
+ Output.push_back('\n');
+ if (IsTargetSpecific)
+ Output.append(" isTargetSpecific: true\n");
+ }
+ return {};
+}
+
+Expected<void> MIRDocumentPrinter::printJumpTable(const MachineFunction &Function,
+ const MIParserContext &Context) {
+ if (Context.JumpTables.empty())
+ return {};
+ const MachineJumpTableEntry *FirstEntry =
+ Function.jumpTableInfo().table(Context.JumpTables.front().Index);
+ const MachineJumpTableDesc *First = FirstEntry == nullptr ? nullptr : FirstEntry->description();
+ if (First == nullptr)
+ return makeUnexpected(ErrorCode::VerificationError, "jump-table slot is not defined");
+
+ std::string_view Kind;
+ std::uint8_t ExpectedSize = 8;
+ switch (First->Encoding) {
+ case JumpTableEntryEncoding::BlockAddress:
+ Kind = "block-address";
+ break;
+ case JumpTableEntryEncoding::GPRelative64BlockAddress:
+ Kind = "gp-rel64-block-address";
+ break;
+ case JumpTableEntryEncoding::GPRelative32BlockAddress:
+ Kind = "gp-rel32-block-address";
+ ExpectedSize = 4;
+ break;
+ case JumpTableEntryEncoding::LabelDifference32:
+ Kind = "label-difference32";
+ ExpectedSize = 4;
+ break;
+ case JumpTableEntryEncoding::LabelDifference64:
+ Kind = "label-difference64";
+ break;
+ case JumpTableEntryEncoding::Inline:
+ Kind = "inline";
+ ExpectedSize = 4;
+ break;
+ case JumpTableEntryEncoding::Custom32:
+ Kind = "custom32";
+ ExpectedSize = 4;
+ break;
+ case JumpTableEntryEncoding::Target:
+ case JumpTableEntryEncoding::Count:
+ return makeUnexpected(ErrorCode::Unsupported,
+ "target jump-table encoding has no fixed LLVM MIR spelling");
+ }
+
+ Output.append("jumpTable:\n kind: ");
+ Output.append(Kind);
+ Output.append("\n entries:\n");
+ for (const MIJumpTableSlot &Slot : Context.JumpTables) {
+ const MachineJumpTableEntry *Entry = Function.jumpTableInfo().table(Slot.Index);
+ const MachineJumpTableDesc *Description = Entry == nullptr ? nullptr : Entry->description();
+ if (Description == nullptr)
+ return makeUnexpected(ErrorCode::VerificationError, "jump-table slot is not defined");
+ if (Description->Encoding != First->Encoding || Description->EntrySize != ExpectedSize ||
+ Description->Align.bytes() != ExpectedSize ||
+ Description->Placement != JumpTablePlacement{} || Description->TargetEncoding != 0) {
+ return makeUnexpected(ErrorCode::Unsupported,
+ "jump-table entries cannot share one fixed LLVM MIR kind");
+ }
+ Output.append(" - { id: ");
+ Output.append(std::to_string(Slot.TextID));
+ Output.append(", blocks: [ ");
+ for (std::size_t Index = 0; Index < Description->Targets.size(); ++Index) {
+ if (Index != 0)
+ Output.append(", ");
+ if (Description->Targets[Index] == nullptr)
+ return makeUnexpected(ErrorCode::VerificationError,
+ "jump-table entry contains a null block");
+ auto Block = blockReference(Function, Description->Targets[Index]->stableID());
+ if (!Block)
+ return std::unexpected(Block.error());
+ Output.append(yamlScalar(*Block));
+ }
+ Output.append(" ] }\n");
+ }
+ return {};
+}
+
+Expected<void> MIRDocumentPrinter::printMetadata(const MachineFunction &Function,
+ const MIParserContext &Context) {
+ if (Function.metadata().nodes().empty())
+ return {};
+ Output.append("machineMetadataNodes:\n");
+ for (const MachineMetadataNode &Node : Function.metadata().nodes()) {
+ auto NodeReference =
+ metadataReference(MachineMetadataReference::machineFunction(Node.Slot), Context);
+ if (!NodeReference)
+ return std::unexpected(NodeReference.error());
+ std::string Text = *NodeReference + " = ";
+ if (Node.IsDistinct)
+ Text.append("distinct ");
+ Text.append("!{");
+ for (std::size_t Index = 0; Index < Node.Elements.size(); ++Index) {
+ if (Index != 0)
+ Text.append(", ");
+ const MachineMetadataElement &Element = Node.Elements[Index];
+ if (Element.isNull()) {
+ Text.append("null");
+ } else if (const MachineMetadataReference *Reference = Element.reference()) {
+ auto Printed = metadataReference(*Reference, Context);
+ if (!Printed)
+ return std::unexpected(Printed.error());
+ Text.append(*Printed);
+ } else if (const std::string *String = Element.string()) {
+ Text.append("!\"");
+ for (const char Character : *String) {
+ if (Character == '\\' || Character == '"')
+ Text.push_back('\\');
+ Text.push_back(Character);
+ }
+ Text.push_back('"');
+ } else {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "machine metadata tuple contains an invalid element");
+ }
+ }
+ Text.push_back('}');
+ Output.append(" - ");
+ Output.append(yamlScalar(Text));
+ Output.push_back('\n');
+ }
+ return {};
+}
+
+Expected<void> MIRDocumentPrinter::printTargetState(const MachineFunction &Function) {
+ if (!Function.targetState().hasValue())
+ return {};
+ auto Node = Target.mirText().printFunctionState(Function.targetState());
+ if (!Node)
+ return std::unexpected(Node.error());
+ auto Text = targetNode(*Node);
+ if (!Text)
+ return std::unexpected(Text.error());
+ Output.append("machineFunctionInfo: ");
+ Output.append(*Text);
+ Output.push_back('\n');
+ return {};
+}
+
+Expected<void> MIRDocumentPrinter::printFunctionRecords(const MachineFunction &Function,
+ const MIParserContext &Context) {
+ const MachineFunctionRecords &Records = Function.records();
+ if (!Records.entryValues().empty()) {
+ Output.append("entry_values:\n");
+ for (const MachineEntryValueInfo &Entry : Records.entryValues()) {
+ auto RegisterName = physicalRegister(Entry.Reg);
+ if (!RegisterName)
+ return std::unexpected(RegisterName.error());
+ if (!Entry.DebugInfo.Variable || !Entry.DebugInfo.Expression || !Entry.DebugInfo.Location) {
+ return makeUnexpected(ErrorCode::Unsupported,
+ "MIR entry value requires complete debug metadata");
+ }
+ auto Variable = metadataReference(*Entry.DebugInfo.Variable, Context);
+ auto Expression = metadataValue(*Entry.DebugInfo.Expression, Context);
+ auto Location = debugLocation(*Entry.DebugInfo.Location, Context);
+ if (!Variable)
+ return std::unexpected(Variable.error());
+ if (!Expression)
+ return std::unexpected(Expression.error());
+ if (!Location)
+ return std::unexpected(Location.error());
+ Output.append(" - { entry-value-register: ");
+ Output.append(yamlScalar(*RegisterName));
+ Output.append(", debug-info-variable: ");
+ Output.append(yamlScalar(*Variable));
+ Output.append(", debug-info-expression: ");
+ Output.append(yamlScalar(*Expression));
+ Output.append(", debug-info-location: ");
+ Output.append(yamlScalar(*Location));
+ Output.append(" }\n");
+ }
+ } else if (Options.Mode == MIPrintMode::Complete) {
+ Output.append("entry_values: []\n");
+ }
+
+ if (!Records.callSites().empty()) {
+ Output.append("callSites:\n");
+ for (const MachineCallSiteInfo &CallSite : Records.callSites()) {
+ auto Location = instructionLocation(Function, CallSite.CallInstruction);
+ if (!Location)
+ return std::unexpected(Location.error());
+ Output.append(" - { bb: ");
+ Output.append(std::to_string(Location->Block));
+ Output.append(", offset: ");
+ Output.append(std::to_string(Location->Offset));
+ if (!CallSite.ForwardedArguments.empty()) {
+ Output.append(", fwdArgRegs: [ ");
+ for (std::size_t Index = 0; Index < CallSite.ForwardedArguments.size(); ++Index) {
+ if (Index != 0)
+ Output.append(", ");
+ const MachineCallSiteArgument &Argument = CallSite.ForwardedArguments[Index];
+ auto RegisterName = physicalRegister(Argument.Reg);
+ if (!RegisterName)
+ return std::unexpected(RegisterName.error());
+ Output.append("{ arg: ");
+ Output.append(std::to_string(Argument.ArgumentNumber));
+ Output.append(", reg: ");
+ Output.append(yamlScalar(*RegisterName));
+ Output.append(" }");
+ }
+ Output.append(" ]");
+ }
+ if (!CallSite.CalleeTypeIDs.empty()) {
+ Output.append(", calleeTypeIds: [ ");
+ for (std::size_t Index = 0; Index < CallSite.CalleeTypeIDs.size(); ++Index) {
+ if (Index != 0)
+ Output.append(", ");
+ Output.append(std::to_string(CallSite.CalleeTypeIDs[Index]));
+ }
+ Output.append(" ]");
+ }
+ Output.append(" }\n");
+ }
+ } else if (Options.Mode == MIPrintMode::Complete) {
+ Output.append("callSites: []\n");
+ }
+
+ if (!Records.debugValueSubstitutions().empty()) {
+ Output.append("debugValueSubstitutions:\n");
+ for (const MachineDebugValueSubstitution &Substitution : Records.debugValueSubstitutions()) {
+ Output.append(" - { srcinst: ");
+ Output.append(std::to_string(Substitution.Source.InstructionNumber));
+ Output.append(", srcop: ");
+ Output.append(std::to_string(Substitution.Source.OperandNumber));
+ Output.append(", dstinst: ");
+ Output.append(std::to_string(Substitution.Destination.InstructionNumber));
+ Output.append(", dstop: ");
+ Output.append(std::to_string(Substitution.Destination.OperandNumber));
+ Output.append(", subreg: ");
+ Output.append(std::to_string(Substitution.Subregister.value()));
+ Output.append(" }\n");
+ }
+ } else if (Options.Mode == MIPrintMode::Complete) {
+ Output.append("debugValueSubstitutions: []\n");
+ }
+
+ if (!Records.calledGlobals().empty()) {
+ Output.append("calledGlobals:\n");
+ for (const MachineCalledGlobal &Called : Records.calledGlobals()) {
+ auto Location = instructionLocation(Function, Called.CallInstruction);
+ if (!Location)
+ return std::unexpected(Location.error());
+ Output.append(" - { bb: ");
+ Output.append(std::to_string(Location->Block));
+ Output.append(", offset: ");
+ Output.append(std::to_string(Location->Offset));
+ Output.append(", callee: ");
+ Output.append(yamlScalar(Called.Callee));
+ Output.append(", flags: ");
+ Output.append(std::to_string(Called.Flags));
+ Output.append(" }\n");
+ }
+ }
+
+ if (!Records.prefetchTargets().empty()) {
+ Output.append("prefetch-targets: [ ");
+ const auto Targets = Records.prefetchTargets();
+ for (std::size_t Index = 0; Index < Targets.size(); ++Index) {
+ if (Index != 0)
+ Output.append(", ");
+ const MachinePrefetchTarget &TargetValue = Targets[Index];
+ std::string Text = "bb_id " + std::to_string(TargetValue.Block.BaseID) + ", " +
+ std::to_string(TargetValue.Block.CloneID) + ", " +
+ std::to_string(TargetValue.CallsiteIndex);
+ Output.append(yamlScalar(Text));
+ }
+ Output.append(" ]\n");
+ }
+ return {};
+}
+
+Expected<void> MIRDocumentPrinter::printFunction(const MachineFunction &Function,
+ const MIRFunctionDocumentInfo *) {
+ if (Function.compilationStatus().state() != MachineCompilationState::Success) {
+ return makeUnexpected(ErrorCode::Unsupported,
+ "fixed LLVM MIR cannot serialize this compilation failure state");
+ }
+ auto Context = createBodyContext(Function);
+ if (!Context)
+ return std::unexpected(Context.error());
+
+ Output.append("---\nname: ");
+ Output.append(yamlScalar(Function.name()));
+ Output.push_back('\n');
+ const auto Alignment = Function.attributes().alignment();
+ if (Options.Mode == MIPrintMode::Complete || Alignment) {
+ Output.append("alignment: ");
+ Output.append(std::to_string(Alignment ? Alignment->bytes() : 1U));
+ Output.push_back('\n');
+ }
+
+ const MachineFunctionProperties &Properties = Function.properties();
+ const MachineFunctionAttributes &Attributes = Function.attributes();
+ printBoolean("exposesReturnsTwice",
+ Attributes.has(MachineFunctionAttribute::ExposesReturnsTwice));
+ printBoolean("legalized", Properties.has(MachineFunctionProperty::Legalized));
+ printBoolean("regBankSelected", Properties.has(MachineFunctionProperty::RegBankSelected));
+ printBoolean("selected", Properties.has(MachineFunctionProperty::Selected));
+ printBoolean("failedISel", Properties.has(MachineFunctionProperty::FailedISel));
+ printBoolean("tracksRegLiveness", Properties.has(MachineFunctionProperty::TracksLiveness));
+ printBoolean("hasWinCFI", Attributes.has(MachineFunctionAttribute::HasWinCFI));
+ printBoolean("noPhis", Properties.has(MachineFunctionProperty::NoPHIs), true);
+ printBoolean("isSSA", Properties.has(MachineFunctionProperty::IsSSA), true);
+ printBoolean("noVRegs", Properties.has(MachineFunctionProperty::NoVRegs), true);
+ printBoolean("hasFakeUses", Attributes.has(MachineFunctionAttribute::HasFakeUses), true);
+ printBoolean("callsEHReturn", Attributes.has(MachineFunctionAttribute::CallsEHReturn));
+ printBoolean("callsUnwindInit", Attributes.has(MachineFunctionAttribute::CallsUnwindInit));
+ printBoolean("hasEHContTarget", Attributes.has(MachineFunctionAttribute::HasEHContTarget));
+ printBoolean("hasEHScopes", Attributes.has(MachineFunctionAttribute::HasEHScopes));
+ printBoolean("hasEHFunclets", Attributes.has(MachineFunctionAttribute::HasEHFunclets));
+ printBoolean("isOutlined", Attributes.has(MachineFunctionAttribute::IsOutlined));
+ printBoolean("debugInstrRef", Attributes.has(MachineFunctionAttribute::DebugInstrRef));
+ printBoolean("failsVerification", Properties.has(MachineFunctionProperty::FailsVerification));
+ printBoolean("tracksDebugUserValues",
+ Attributes.has(MachineFunctionAttribute::TracksDebugUserValues));
+
+ if (auto Printed = printRegisters(Function, *Context); !Printed)
+ return Printed;
+ if (auto Printed = printFrameInfo(Function, *Context); !Printed)
+ return Printed;
+ if (auto Printed = printStackObjects(Function, *Context, true); !Printed)
+ return Printed;
+ if (auto Printed = printStackObjects(Function, *Context, false); !Printed)
+ return Printed;
+ if (auto Printed = printFunctionRecords(Function, *Context); !Printed)
+ return Printed;
+ if (auto Printed = printConstants(Function, *Context); !Printed)
+ return Printed;
+ if (auto Printed = printTargetState(Function); !Printed)
+ return Printed;
+ if (auto Printed = printJumpTable(Function, *Context); !Printed)
+ return Printed;
+ if (auto Printed = printMetadata(Function, *Context); !Printed)
+ return Printed;
+
+ auto Body =
+ printMachineFunctionBody(Function, Target, *Context, MIPrinterOptions{.Mode = Options.Mode});
+ if (!Body)
+ return std::unexpected(Body.error());
+ Output.append("body: |\n");
+ appendBlockScalar(Output, *Body, 2);
+ Output.append("...\n");
+ return {};
+}
+
+Expected<std::string> MIRDocumentPrinter::run() {
+ auto UnitVerification = Document.unit().verify();
+ if (!UnitVerification)
+ return std::unexpected(UnitVerification.error());
+ if (Document.unit().functionCount() == 0) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "MIR document must contain at least one machine function");
+ }
+
+ for (const MIRFunctionDocumentInfo &Info : Document.functionDocumentInfo()) {
+ if (Document.unit().function(Info.Function) == nullptr) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "MIR document info references an unknown machine function");
+ }
+ }
+
+ for (std::size_t Index = 0; Index < Document.unit().functionCount(); ++Index) {
+ const MachineFunction *Function = Document.unit().functionAt(Index);
+ if (Function == nullptr) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "compilation unit contains a null machine function");
+ }
+ auto ReferenceVerification =
+ verifyMIRDocumentReferences(Document, *Function, Options.EmitEmbeddedIR);
+ if (!ReferenceVerification)
+ return std::unexpected(ReferenceVerification.error());
+ auto LayoutVerification = verifyOmittedEmbeddedIRLayout(*Function);
+ if (!LayoutVerification)
+ return std::unexpected(LayoutVerification.error());
+ }
+
+ if (Options.EmitEmbeddedIR && Document.embeddedIR() != nullptr) {
+ Output.append("--- |\n");
+ appendBlockScalar(Output, Document.embeddedIR()->source().text(), 2);
+ Output.append("...\n");
+ }
+ for (std::size_t Index = 0; Index < Document.unit().functionCount(); ++Index) {
+ const MachineFunction *Function = Document.unit().functionAt(Index);
+ if (Function == nullptr)
+ return makeUnexpected(ErrorCode::VerificationError,
+ "compilation unit contains a null machine function");
+ if (auto Printed = printFunction(*Function, documentInfo(*Function)); !Printed)
+ return std::unexpected(Printed.error());
+ }
+ return std::move(Output);
+}
+
+} // namespace
+
+Expected<std::string> printMIRDocument(const MIRDocument &Document, const TargetDescription &Target,
+ MIRDocumentPrinterOptions Options) {
+ return MIRDocumentPrinter(Document, Target, std::move(Options)).run();
+}
+
+} // namespace rcc::mir
+
+namespace rcc::mir {
+namespace {
+
[[nodiscard]] bool isBareNameCharacter(char Character) noexcept {
return (Character >= 'a' && Character <= 'z') || (Character >= 'A' && Character <= 'Z') ||
(Character >= '0' && Character <= '9') || Character == '_' || Character == '-' ||
diff --git a/backend/lib/CodeGen/MIRYamlMapping.cpp b/backend/lib/CodeGen/MIRYamlMapping.cpp
new file mode 100644
index 0000000..9ca8cad
--- /dev/null
+++ b/backend/lib/CodeGen/MIRYamlMapping.cpp
@@ -0,0 +1,2926 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "rcc/CodeGen/MIRYamlMapping.h"
+
+#include "MIRParser/MILexer.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 <charconv>
+#include <cstddef>
+#include <cstdint>
+#include <functional>
+#include <initializer_list>
+#include <limits>
+#include <optional>
+#include <ranges>
+#include <span>
+#include <string>
+#include <string_view>
+#include <system_error>
+#include <unordered_map>
+#include <unordered_set>
+#include <utility>
+#include <variant>
+#include <vector>
+
+namespace rcc::mir {
+
+namespace parser_detail {
+
+[[nodiscard]] inline std::string_view trim(std::string_view Text) noexcept {
+ while (!Text.empty() && (Text.front() == ' ' || Text.front() == '\t' || Text.front() == '\r' ||
+ Text.front() == '\n')) {
+ Text.remove_prefix(1);
+ }
+ while (!Text.empty() && (Text.back() == ' ' || Text.back() == '\t' || Text.back() == '\r' ||
+ Text.back() == '\n')) {
+ Text.remove_suffix(1);
+ }
+ return Text;
+}
+
+[[nodiscard]] inline bool parseUnsigned(std::string_view Text, std::uint64_t &Value) noexcept {
+ Text = trim(Text);
+ int Base = 10;
+ if (Text.starts_with("0x") || Text.starts_with("0X")) {
+ Text.remove_prefix(2);
+ Base = 16;
+ }
+ if (Text.empty())
+ return false;
+ const auto [End, Error] = std::from_chars(Text.data(), Text.data() + Text.size(), Value, Base);
+ return Error == std::errc{} && End == Text.data() + Text.size();
+}
+
+[[nodiscard]] inline bool parseSigned(std::string_view Text, std::int64_t &Value) noexcept {
+ Text = trim(Text);
+ if (Text.empty())
+ return false;
+ const auto [End, Error] = std::from_chars(Text.data(), Text.data() + Text.size(), Value, 10);
+ return Error == std::errc{} && End == Text.data() + Text.size();
+}
+
+[[nodiscard]] inline bool parseUnsigned32(std::string_view Text, std::uint32_t &Value) noexcept {
+ std::uint64_t Wide = 0;
+ if (!parseUnsigned(Text, Wide) || Wide > std::numeric_limits<std::uint32_t>::max())
+ return false;
+ Value = static_cast<std::uint32_t>(Wide);
+ return true;
+}
+
+[[nodiscard]] inline bool parseBool(std::string_view Text, bool &Value) noexcept {
+ if (Text == "true") {
+ Value = true;
+ return true;
+ }
+ if (Text == "false") {
+ Value = false;
+ return true;
+ }
+ return false;
+}
+
+[[nodiscard]] inline std::optional<std::uint32_t>
+parsePrefixedID(std::string_view Text, std::string_view Prefix) noexcept {
+ Text = trim(Text);
+ if (!Text.starts_with(Prefix))
+ return std::nullopt;
+ Text.remove_prefix(Prefix.size());
+ const std::size_t Suffix = Text.find_first_of(". \t");
+ if (Suffix != std::string_view::npos)
+ Text = Text.substr(0, Suffix);
+ std::uint32_t ID = 0;
+ if (!parseUnsigned32(Text, ID))
+ return std::nullopt;
+ return ID;
+}
+
+} // namespace parser_detail
+
+class MetadataTextParser final {
+public:
+ using Resolver = std::function<Expected<MachineMetadataReference>(std::uint32_t Slot)>;
+
+ MetadataTextParser(std::string Text, Resolver Resolve)
+ : Lexer("MIR metadata scalar", std::move(Text)), Resolve(std::move(Resolve)) {
+ do {
+ Tokens.push_back(Lexer.next());
+ } while (!Tokens.back().isErrorOrEnd());
+ }
+
+ [[nodiscard]] Expected<MachineMetadataReference> parseReferenceOnly();
+ [[nodiscard]] Expected<MachineMetadataValue> parseValue();
+ [[nodiscard]] Expected<MachineDebugLocation> parseLocation();
+
+ [[nodiscard]] const TextRange &diagnosticRange() const noexcept {
+ return token().range();
+ }
+
+private:
+ [[nodiscard]] const MIToken &token() const noexcept {
+ return Tokens[Position];
+ }
+ [[nodiscard]] bool at(MITokenKind Kind) const noexcept {
+ return token().is(Kind);
+ }
+ void advance() noexcept {
+ if (Position + 1U < Tokens.size())
+ ++Position;
+ }
+ [[nodiscard]] bool consume(MITokenKind Kind) noexcept {
+ if (!at(Kind))
+ return false;
+ advance();
+ return true;
+ }
+ [[nodiscard]] Expected<void> expect(MITokenKind Kind, std::string_view Description);
+ [[nodiscard]] Expected<void> finish();
+ [[nodiscard]] Expected<MachineMetadataReference> parseReference();
+ [[nodiscard]] Expected<MachineDIExpression> parseExpression();
+ [[nodiscard]] Expected<void> parseDILocation(std::vector<MachineDILocationFrame> &Frames,
+ std::optional<MachineMetadataReference> &Terminal);
+
+ MILexer Lexer;
+ Resolver Resolve;
+ std::vector<MIToken> Tokens;
+ std::size_t Position = 0;
+};
+
+using parser_detail::parseBool;
+using parser_detail::parsePrefixedID;
+using parser_detail::parseSigned;
+using parser_detail::parseUnsigned;
+using parser_detail::parseUnsigned32;
+using parser_detail::trim;
+
+bool MIRDocumentParser::boolean(const yaml::Node &Node, bool &Value) {
+ std::string_view Text;
+ if (scalar(Node, Text, "boolean") || !parseBool(Text, Value))
+ return fail(Node.range(), "expected 'true' or 'false'");
+ return false;
+}
+
+bool MIRDocumentParser::unsignedValue(const yaml::Node &Node, std::uint64_t &Value,
+ std::string_view Description) {
+ std::string_view Text;
+ if (scalar(Node, Text, Description) || !parseUnsigned(Text, Value))
+ return fail(Node.range(), "expected " + std::string(Description));
+ return false;
+}
+
+bool MIRDocumentParser::signedValue(const yaml::Node &Node, std::int64_t &Value,
+ std::string_view Description) {
+ std::string_view Text;
+ if (scalar(Node, Text, Description) || !parseSigned(Text, Value))
+ return fail(Node.range(), "expected " + std::string(Description));
+ return false;
+}
+
+bool MIRDocumentParser::alignment(const yaml::Node &Node, Alignment &Value, bool AllowZero) {
+ std::uint64_t Bytes = 0;
+ if (unsignedValue(Node, Bytes, "an unsigned alignment"))
+ return true;
+ if (Bytes == 0 && AllowZero) {
+ Value = Alignment{};
+ return false;
+ }
+ auto Parsed = Alignment::create(Bytes);
+ if (!Parsed)
+ return importError(Node.range(), Parsed.error());
+ Value = *Parsed;
+ return false;
+}
+
+bool MIRDocumentParser::physicalRegister(std::string_view Text, const TextRange &Range,
+ PhysicalRegister &Reg) {
+ Text = trim(Text);
+ if (!Text.starts_with('$') || Text.size() == 1)
+ return fail(Range, "physical register must use the '$name' MIR spelling");
+ const PhysicalRegisterDesc *Description = Target.registers().physicalRegister(Text.substr(1));
+ if (Description == nullptr)
+ return fail(Range, "unknown physical register '" + std::string(Text) + "'");
+ Reg = Description->Reg;
+ return false;
+}
+
+bool MIRDocumentParser::physicalRegister(const yaml::Node &Node, PhysicalRegister &Reg) {
+ std::string_view Text;
+ return scalar(Node, Text, "physical register") || physicalRegister(Text, Node.range(), Reg);
+}
+
+bool MIRDocumentParser::virtualRegister(std::string_view Text, const TextRange &Range,
+ MachineFunction &Function, MIParserContext &Context,
+ Register &Reg) {
+ const auto ID = parsePrefixedID(Text, "%");
+ if (!ID)
+ return fail(Range, "virtual register must use the '%N' MIR spelling");
+ auto Found =
+ std::ranges::find_if(Context.VirtualRegisters,
+ [ID](const MIVirtualRegisterSlot &Slot) { return Slot.TextID == *ID; });
+ if (Found == Context.VirtualRegisters.end()) {
+ auto Created = Function.registerInfo().createVirtualRegister();
+ if (!Created)
+ return importError(Range, Created.error());
+ Context.VirtualRegisters.push_back({*ID, *Created});
+ Reg = *Created;
+ return false;
+ }
+ Reg = Found->Reg;
+ return false;
+}
+
+bool MIRDocumentParser::parseConstants(const yaml::Node &Root, MachineFunction &Function,
+ MIParserContext &Context) {
+ const yaml::Node *Constants = mappingValue(Root, "constants");
+ if (Constants == nullptr)
+ return false;
+ if (requireKind(*Constants, yaml::NodeKind::Sequence, "constant pool"))
+ return true;
+ constexpr std::array Keys{
+ std::string_view{"id"},
+ std::string_view{"value"},
+ std::string_view{"alignment"},
+ std::string_view{"isTargetSpecific"},
+ };
+ std::vector<LLVMNamedTypeDefinition> NamedTypes;
+ const std::string_view DataLayout = effectiveDataLayout();
+ if (ActiveDocument->EmbeddedIR) {
+ const auto Definitions = ActiveDocument->EmbeddedIR->typeDefinitions();
+ NamedTypes.reserve(Definitions.size());
+ for (const EmbeddedIRTypeDefinition &Definition : Definitions) {
+ NamedTypes.push_back({.Name = Definition.Name,
+ .Definition = Definition.Definition,
+ .NumericName = Definition.NumericName});
+ }
+ }
+ std::unordered_set<std::uint32_t> IDs;
+ for (const yaml::NodeID ConstantID : Constants->sequence()) {
+ const yaml::Node *Constant = node(ConstantID);
+ if (Constant == nullptr ||
+ requireKind(*Constant, yaml::NodeKind::Mapping, "constant pool entry") ||
+ validateKeys(*Constant, Keys, "constant pool entry")) {
+ return true;
+ }
+ const yaml::Node *IDNode = mappingValue(*Constant, "id");
+ const yaml::Node *ValueNode = mappingValue(*Constant, "value");
+ if (IDNode == nullptr || ValueNode == nullptr)
+ return fail(Constant->range(), "constant pool entry requires 'id' and 'value'");
+ std::uint64_t WideID = 0;
+ if (unsignedValue(*IDNode, WideID, "a 32-bit constant pool ID") ||
+ WideID > std::numeric_limits<std::uint32_t>::max()) {
+ return true;
+ }
+ const auto TextID = static_cast<std::uint32_t>(WideID);
+ if (!IDs.insert(TextID).second)
+ return fail(IDNode->range(), "duplicate constant pool ID");
+
+ bool IsTargetSpecific = false;
+ if (const yaml::Node *TargetNode = mappingValue(*Constant, "isTargetSpecific")) {
+ if (boolean(*TargetNode, IsTargetSpecific))
+ return true;
+ }
+ if (IsTargetSpecific) {
+ std::string_view ValueText;
+ if (scalar(*ValueNode, ValueText, "target constant"))
+ return true;
+ auto TargetConstantValue = Target.mirText().parseTargetConstant(ValueText);
+ if (!TargetConstantValue)
+ return importError(ValueNode->range(), TargetConstantValue.error(), "mir-target-constant");
+ auto Emission = TargetConstantValue->emit();
+ if (!Emission)
+ return importError(ValueNode->range(), Emission.error(), "mir-target-constant");
+ auto RequiredAlignment = TargetConstantValue->requiredAlignment();
+ if (!RequiredAlignment)
+ return importError(ValueNode->range(), RequiredAlignment.error(), "mir-target-constant");
+
+ MachineConstantPoolEntryDesc Description;
+ Description.Size = Emission->Bytes.size();
+ Description.Align = *RequiredAlignment;
+ Description.Value = std::move(*TargetConstantValue);
+ if (const yaml::Node *AlignmentNode = mappingValue(*Constant, "alignment")) {
+ if (alignment(*AlignmentNode, Description.Align, true))
+ return true;
+ }
+ auto Added = Function.constantPool().addEntry(std::move(Description));
+ if (!Added)
+ return importError(Constant->range(), Added.error());
+ Context.ConstantPoolItems.push_back({.TextID = TextID, .Index = *Added});
+ continue;
+ }
+
+ std::string_view ValueText;
+ if (scalar(*ValueNode, ValueText, "LLVM constant"))
+ return true;
+ const LLVMConstantSyntaxOptions ConstantOptions{
+ .DataLayout = DataLayout,
+ .NamedTypes = NamedTypes,
+ };
+ auto Data = parseLLVMConstant(trim(ValueText), ConstantOptions);
+ if (!Data)
+ return importError(ValueNode->range(), Data.error(), "mir-llvm-constant");
+ auto Size = llvmConstantStorageSize(Data->Type, ConstantOptions);
+ if (!Size)
+ return importError(ValueNode->range(), Size.error(), "mir-llvm-constant");
+
+ MachineConstantPoolEntryDesc Description;
+ Description.Size = *Size;
+ Description.Value = std::move(*Data);
+ if (const yaml::Node *AlignmentNode = mappingValue(*Constant, "alignment")) {
+ if (alignment(*AlignmentNode, Description.Align, true))
+ return true;
+ } else {
+ auto Preferred = llvmConstantPreferredAlignment(
+ std::get<GenericConstantData>(Description.Value).Type, ConstantOptions);
+ if (!Preferred)
+ return importError(ValueNode->range(), Preferred.error(), "mir-llvm-constant");
+ Description.Align = *Preferred;
+ }
+ auto Added = Function.constantPool().addEntry(std::move(Description));
+ if (!Added)
+ return importError(Constant->range(), Added.error());
+ Context.ConstantPoolItems.push_back({.TextID = TextID, .Index = *Added});
+ }
+ return false;
+}
+
+bool MIRDocumentParser::reserveJumpTables(const yaml::Node &Root, MachineFunction &Function,
+ MIParserContext &Context,
+ std::vector<PendingJumpTable> &Pending) {
+ const yaml::Node *JumpTable = mappingValue(Root, "jumpTable");
+ if (JumpTable == nullptr)
+ return false;
+ if (requireKind(*JumpTable, yaml::NodeKind::Mapping, "jump table"))
+ return true;
+ constexpr std::array JumpTableKeys{
+ std::string_view{"kind"},
+ std::string_view{"entries"},
+ };
+ if (validateKeys(*JumpTable, JumpTableKeys, "jump table"))
+ return true;
+ const yaml::Node *KindNode = mappingValue(*JumpTable, "kind");
+ if (KindNode == nullptr)
+ return fail(JumpTable->range(), "jump table requires 'kind'");
+ std::string_view KindName;
+ if (scalar(*KindNode, KindName, "jump table kind"))
+ return true;
+
+ JumpTableEntryEncoding Encoding;
+ std::uint8_t EntrySize = 8;
+ if (KindName == "block-address")
+ Encoding = JumpTableEntryEncoding::BlockAddress;
+ else if (KindName == "gp-rel64-block-address")
+ Encoding = JumpTableEntryEncoding::GPRelative64BlockAddress;
+ else if (KindName == "gp-rel32-block-address") {
+ Encoding = JumpTableEntryEncoding::GPRelative32BlockAddress;
+ EntrySize = 4;
+ } else if (KindName == "label-difference32") {
+ Encoding = JumpTableEntryEncoding::LabelDifference32;
+ EntrySize = 4;
+ } else if (KindName == "label-difference64")
+ Encoding = JumpTableEntryEncoding::LabelDifference64;
+ else if (KindName == "inline") {
+ Encoding = JumpTableEntryEncoding::Inline;
+ EntrySize = 4;
+ } else if (KindName == "custom32") {
+ Encoding = JumpTableEntryEncoding::Custom32;
+ EntrySize = 4;
+ } else {
+ return fail(KindNode->range(), "unknown enumerated scalar");
+ }
+
+ const yaml::Node *Entries = mappingValue(*JumpTable, "entries");
+ if (Entries == nullptr)
+ return false;
+ if (requireKind(*Entries, yaml::NodeKind::Sequence, "jump table entries"))
+ return true;
+ constexpr std::array EntryKeys{
+ std::string_view{"id"},
+ std::string_view{"blocks"},
+ };
+ std::unordered_set<std::uint32_t> IDs;
+ for (const yaml::NodeID EntryID : Entries->sequence()) {
+ const yaml::Node *Entry = node(EntryID);
+ if (Entry == nullptr || requireKind(*Entry, yaml::NodeKind::Mapping, "jump table entry") ||
+ validateKeys(*Entry, EntryKeys, "jump table entry")) {
+ return true;
+ }
+ const yaml::Node *IDNode = mappingValue(*Entry, "id");
+ if (IDNode == nullptr)
+ return fail(Entry->range(), "jump table entry requires 'id'");
+ std::uint64_t WideID = 0;
+ if (unsignedValue(*IDNode, WideID, "a 32-bit jump table ID") ||
+ WideID > std::numeric_limits<std::uint32_t>::max()) {
+ return true;
+ }
+ const auto TextID = static_cast<std::uint32_t>(WideID);
+ if (!IDs.insert(TextID).second)
+ return fail(IDNode->range(), "duplicate jump table ID");
+ auto Reserved = Function.jumpTableInfo().reserveTable();
+ if (!Reserved)
+ return importError(Entry->range(), Reserved.error());
+ PendingJumpTable NewPending{
+ .Index = *Reserved,
+ .TextID = TextID,
+ .Encoding = Encoding,
+ .EntrySize = EntrySize,
+ .Blocks = {},
+ };
+ if (const yaml::Node *Blocks = mappingValue(*Entry, "blocks")) {
+ if (requireKind(*Blocks, yaml::NodeKind::Sequence, "jump table blocks"))
+ return true;
+ for (const yaml::NodeID BlockID : Blocks->sequence()) {
+ const yaml::Node *BlockNode = node(BlockID);
+ std::string_view Text;
+ if (BlockNode == nullptr || scalar(*BlockNode, Text, "basic block reference"))
+ return true;
+ NewPending.Blocks.emplace_back(Text);
+ }
+ }
+ if (NewPending.Blocks.empty())
+ return fail(Entry->range(), "jump table entry must contain at least one block");
+ Context.JumpTables.push_back({.TextID = TextID, .Index = *Reserved});
+ Pending.push_back(std::move(NewPending));
+ }
+ return false;
+}
+
+bool MIRDocumentParser::defineJumpTables(MachineFunction &Function,
+ std::span<const PendingJumpTable> Pending) {
+ for (const PendingJumpTable &Table : Pending) {
+ MachineJumpTableDesc Description;
+ Description.Encoding = Table.Encoding;
+ Description.EntrySize = Table.EntrySize;
+ Description.Align = *Alignment::create(Table.EntrySize);
+ for (const std::string &Text : Table.Blocks) {
+ MachineBasicBlock *Block = nullptr;
+ if (parseBlockReference(Text, Result.Source.range(0, 0), Function, Block))
+ return true;
+ if (Block == nullptr)
+ return fail(Result.Source.range(0, 0), "reference to undefined machine basic block");
+ Description.Targets.push_back(Block);
+ }
+ auto DefineResult =
+ Function.jumpTableInfo().defineReservedTable(Table.Index, std::move(Description));
+ if (!DefineResult)
+ return importError(Result.Source.range(0, 0), DefineResult.error());
+ }
+ return false;
+}
+
+} // namespace rcc::mir
+
+namespace rcc::mir {
+namespace {
+
+[[nodiscard]] bool parseIndex(std::string_view Text, std::uint64_t &Value) noexcept {
+ if (Text.empty())
+ return false;
+ const auto [End, Error] = std::from_chars(Text.data(), Text.data() + Text.size(), Value, 10);
+ return Error == std::errc{} && End == Text.data() + Text.size();
+}
+
+[[nodiscard]] const auto &namedOpcodes() {
+ static constexpr auto Opcodes = std::to_array<std::pair<std::string_view, std::uint64_t>>({
+ {"DW_OP_addr", 0x03},
+ {"DW_OP_deref", 0x06},
+ {"DW_OP_const1u", 0x08},
+ {"DW_OP_const1s", 0x09},
+ {"DW_OP_const2u", 0x0A},
+ {"DW_OP_const2s", 0x0B},
+ {"DW_OP_const4u", 0x0C},
+ {"DW_OP_const4s", 0x0D},
+ {"DW_OP_const8u", 0x0E},
+ {"DW_OP_const8s", 0x0F},
+ {"DW_OP_constu", 0x10},
+ {"DW_OP_consts", 0x11},
+ {"DW_OP_dup", 0x12},
+ {"DW_OP_drop", 0x13},
+ {"DW_OP_over", 0x14},
+ {"DW_OP_pick", 0x15},
+ {"DW_OP_swap", 0x16},
+ {"DW_OP_rot", 0x17},
+ {"DW_OP_xderef", 0x18},
+ {"DW_OP_abs", 0x19},
+ {"DW_OP_and", 0x1A},
+ {"DW_OP_div", 0x1B},
+ {"DW_OP_minus", 0x1C},
+ {"DW_OP_mod", 0x1D},
+ {"DW_OP_mul", 0x1E},
+ {"DW_OP_neg", 0x1F},
+ {"DW_OP_not", 0x20},
+ {"DW_OP_or", 0x21},
+ {"DW_OP_plus", 0x22},
+ {"DW_OP_plus_uconst", 0x23},
+ {"DW_OP_shl", 0x24},
+ {"DW_OP_shr", 0x25},
+ {"DW_OP_shra", 0x26},
+ {"DW_OP_xor", 0x27},
+ {"DW_OP_bra", 0x28},
+ {"DW_OP_eq", 0x29},
+ {"DW_OP_ge", 0x2A},
+ {"DW_OP_gt", 0x2B},
+ {"DW_OP_le", 0x2C},
+ {"DW_OP_lt", 0x2D},
+ {"DW_OP_ne", 0x2E},
+ {"DW_OP_skip", 0x2F},
+ {"DW_OP_regx", 0x90},
+ {"DW_OP_fbreg", 0x91},
+ {"DW_OP_bregx", 0x92},
+ {"DW_OP_piece", 0x93},
+ {"DW_OP_deref_size", 0x94},
+ {"DW_OP_xderef_size", 0x95},
+ {"DW_OP_nop", 0x96},
+ {"DW_OP_push_object_address", 0x97},
+ {"DW_OP_call2", 0x98},
+ {"DW_OP_call4", 0x99},
+ {"DW_OP_call_ref", 0x9A},
+ {"DW_OP_form_tls_address", 0x9B},
+ {"DW_OP_call_frame_cfa", 0x9C},
+ {"DW_OP_bit_piece", 0x9D},
+ {"DW_OP_implicit_value", 0x9E},
+ {"DW_OP_stack_value", 0x9F},
+ {"DW_OP_implicit_pointer", 0xA0},
+ {"DW_OP_addrx", 0xA1},
+ {"DW_OP_constx", 0xA2},
+ {"DW_OP_entry_value", 0xA3},
+ {"DW_OP_const_type", 0xA4},
+ {"DW_OP_regval_type", 0xA5},
+ {"DW_OP_deref_type", 0xA6},
+ {"DW_OP_xderef_type", 0xA7},
+ {"DW_OP_convert", 0xA8},
+ {"DW_OP_reinterpret", 0xA9},
+ {"DW_OP_LLVM_fragment", 0x1000},
+ {"DW_OP_LLVM_convert", 0x1001},
+ {"DW_OP_LLVM_tag_offset", 0x1002},
+ {"DW_OP_LLVM_entry_value", 0x1003},
+ {"DW_OP_LLVM_implicit_pointer", 0x1004},
+ {"DW_OP_LLVM_arg", 0x1005},
+ {"DW_OP_LLVM_extract_bits_sext", 0x1006},
+ {"DW_OP_LLVM_extract_bits_zext", 0x1007},
+ });
+ return Opcodes;
+}
+
+[[nodiscard]] const auto &attributeEncodings() {
+ static constexpr auto Encodings = std::to_array<std::pair<std::string_view, std::uint64_t>>({
+ {"DW_ATE_address", 0x01},
+ {"DW_ATE_boolean", 0x02},
+ {"DW_ATE_complex_float", 0x03},
+ {"DW_ATE_float", 0x04},
+ {"DW_ATE_signed", 0x05},
+ {"DW_ATE_signed_char", 0x06},
+ {"DW_ATE_unsigned", 0x07},
+ {"DW_ATE_unsigned_char", 0x08},
+ {"DW_ATE_imaginary_float", 0x09},
+ {"DW_ATE_packed_decimal", 0x0A},
+ {"DW_ATE_numeric_string", 0x0B},
+ {"DW_ATE_edited", 0x0C},
+ {"DW_ATE_signed_fixed", 0x0D},
+ {"DW_ATE_unsigned_fixed", 0x0E},
+ {"DW_ATE_decimal_float", 0x0F},
+ {"DW_ATE_UTF", 0x10},
+ {"DW_ATE_UCS", 0x11},
+ {"DW_ATE_ASCII", 0x12},
+ });
+ return Encodings;
+}
+
+[[nodiscard]] std::optional<std::string> opcodeName(std::uint64_t Opcode) {
+ if (Opcode >= 0x30U && Opcode <= 0x4FU)
+ return "DW_OP_lit" + std::to_string(Opcode - 0x30U);
+ if (Opcode >= 0x50U && Opcode <= 0x6FU)
+ return "DW_OP_reg" + std::to_string(Opcode - 0x50U);
+ if (Opcode >= 0x70U && Opcode <= 0x8FU)
+ return "DW_OP_breg" + std::to_string(Opcode - 0x70U);
+ const auto &Opcodes = namedOpcodes();
+ const auto Found =
+ std::ranges::find_if(Opcodes, [Opcode](const auto &Entry) { return Entry.second == Opcode; });
+ if (Found == Opcodes.end())
+ return std::nullopt;
+ return std::string(Found->first);
+}
+
+[[nodiscard]] std::size_t opcodeSize(std::uint64_t Opcode) noexcept {
+ if (Opcode >= 0x70U && Opcode <= 0x8FU)
+ return 2;
+ switch (Opcode) {
+ case 0x92:
+ case 0x1000:
+ case 0x1001:
+ case 0x1006:
+ case 0x1007:
+ return 3;
+ case 0x10:
+ case 0x11:
+ case 0x23:
+ case 0x90:
+ case 0x94:
+ case 0x1002:
+ case 0x1003:
+ case 0x1005:
+ return 2;
+ default:
+ return 1;
+ }
+}
+
+[[nodiscard]] std::optional<std::string_view> attributeName(std::uint64_t Encoding) noexcept {
+ const auto &Encodings = attributeEncodings();
+ const auto Found = std::ranges::find_if(
+ Encodings, [Encoding](const auto &Entry) { return Entry.second == Encoding; });
+ if (Found == Encodings.end())
+ return std::nullopt;
+ return Found->first;
+}
+
+} // namespace
+
+std::optional<std::uint64_t> parseDIExpressionOpcodeName(std::string_view Name) noexcept {
+ const auto NumberedOpcode = [Name](std::string_view Prefix,
+ std::uint64_t Base) -> std::optional<std::uint64_t> {
+ if (!Name.starts_with(Prefix))
+ return std::nullopt;
+ std::uint64_t Index = 0;
+ if (!parseIndex(Name.substr(Prefix.size()), Index) || Index > 31)
+ return std::nullopt;
+ return Base + Index;
+ };
+ if (auto Opcode = NumberedOpcode("DW_OP_lit", 0x30U))
+ return Opcode;
+ if (auto Opcode = NumberedOpcode("DW_OP_reg", 0x50U))
+ return Opcode;
+ if (auto Opcode = NumberedOpcode("DW_OP_breg", 0x70U))
+ return Opcode;
+
+ const auto &NamedOpcodes = namedOpcodes();
+ const auto *const Found =
+ std::ranges::find_if(NamedOpcodes, [Name](const auto &Entry) { return Entry.first == Name; });
+ if (Found == NamedOpcodes.end())
+ return std::nullopt;
+ return Found->second;
+}
+
+std::optional<std::uint64_t>
+parseDIExpressionAttributeEncodingName(std::string_view Name) noexcept {
+ const auto &Encodings = attributeEncodings();
+ const auto Found =
+ std::ranges::find_if(Encodings, [Name](const auto &Entry) { return Entry.first == Name; });
+ if (Found == Encodings.end())
+ return std::nullopt;
+ return Found->second;
+}
+
+std::string invalidDIExpressionElementMessage(std::string_view Name) {
+ if (Name.starts_with("DW_OP_"))
+ return "invalid DWARF op '" + std::string(Name) + "'";
+ if (Name.starts_with("DW_ATE_"))
+ return "invalid DWARF attribute encoding '" + std::string(Name) + "'";
+ return "expected unsigned integer";
+}
+
+std::string formatDIExpression(std::span<const std::uint64_t> Elements) {
+ std::string Result = "!DIExpression(";
+ std::size_t Position = 0;
+ bool First = true;
+ while (Position < Elements.size()) {
+ const std::uint64_t Opcode = Elements[Position];
+ const auto Name = opcodeName(Opcode);
+ const std::size_t Size = opcodeSize(Opcode);
+ if (!Name || Position + Size > Elements.size()) {
+ Result = "!DIExpression(";
+ for (std::size_t Index = 0; Index < Elements.size(); ++Index) {
+ if (Index != 0)
+ Result.append(", ");
+ Result.append(std::to_string(Elements[Index]));
+ }
+ Result.push_back(')');
+ return Result;
+ }
+
+ if (!First)
+ Result.append(", ");
+ First = false;
+ Result.append(*Name);
+ for (std::size_t Argument = 1; Argument < Size; ++Argument) {
+ Result.append(", ");
+ if (Opcode == 0x1001U && Argument == 2U) {
+ if (const auto Encoding = attributeName(Elements[Position + Argument])) {
+ Result.append(*Encoding);
+ continue;
+ }
+ }
+ Result.append(std::to_string(Elements[Position + Argument]));
+ }
+ Position += Size;
+ }
+ Result.push_back(')');
+ return Result;
+}
+
+} // namespace rcc::mir
+
+namespace rcc::mir {
+
+using parser_detail::parseSigned;
+using parser_detail::parseUnsigned;
+using parser_detail::parseUnsigned32;
+
+Expected<void> MetadataTextParser::expect(MITokenKind Kind, std::string_view Description) {
+ if (consume(Kind))
+ return {};
+ return makeUnexpected(ErrorCode::ParseError,
+ "expected " + std::string(Description) + " in MIR metadata scalar");
+}
+
+Expected<void> MetadataTextParser::finish() {
+ while (consume(MITokenKind::Newline)) {
+ }
+ if (Lexer.failed()) {
+ if (!Lexer.diagnostics().empty()) {
+ return makeUnexpected(ErrorCode::ParseError,
+ std::string(Lexer.diagnostics().front().Message));
+ }
+ return makeUnexpected(ErrorCode::ParseError, "invalid MIR metadata scalar");
+ }
+ if (!at(MITokenKind::EndOfFile))
+ return makeUnexpected(ErrorCode::ParseError, "unexpected trailing MIR metadata tokens");
+ return {};
+}
+
+Expected<MachineMetadataReference> MetadataTextParser::parseReference() {
+ if (auto Exclaim = expect(MITokenKind::Exclaim, "'!' before a metadata slot"); !Exclaim)
+ return std::unexpected(Exclaim.error());
+ if (!at(MITokenKind::IntegerLiteral))
+ return makeUnexpected(ErrorCode::ParseError, "expected a 32-bit metadata slot");
+ std::uint32_t Slot = 0;
+ if (!parseUnsigned32(token().integerValue(), Slot))
+ return makeUnexpected(ErrorCode::ParseError, "metadata slot is outside the 32-bit range");
+ advance();
+ return Resolve(Slot);
+}
+
+Expected<MachineMetadataReference> MetadataTextParser::parseReferenceOnly() {
+ auto Reference = parseReference();
+ if (!Reference)
+ return std::unexpected(Reference.error());
+ if (auto Finished = finish(); !Finished)
+ return std::unexpected(Finished.error());
+ return Reference;
+}
+
+Expected<MachineDIExpression> MetadataTextParser::parseExpression() {
+ if (!at(MITokenKind::MetadataKeyword) || !token().isMetadataKeyword("!DIExpression")) {
+ return makeUnexpected(ErrorCode::ParseError, "expected an inline !DIExpression");
+ }
+ advance();
+ if (auto Left = expect(MITokenKind::LeftParen, "'(' after !DIExpression"); !Left)
+ return std::unexpected(Left.error());
+ std::vector<std::uint64_t> Elements;
+ while (!at(MITokenKind::RightParen)) {
+ if (at(MITokenKind::Identifier)) {
+ const auto Opcode = parseDIExpressionOpcodeName(token().stringValue());
+ const auto Encoding = parseDIExpressionAttributeEncodingName(token().stringValue());
+ if (!Opcode && !Encoding)
+ return makeUnexpected(ErrorCode::ParseError,
+ invalidDIExpressionElementMessage(token().stringValue()));
+ Elements.push_back(Opcode ? *Opcode : *Encoding);
+ advance();
+ } else if (at(MITokenKind::IntegerLiteral)) {
+ std::int64_t Signed = 0;
+ std::uint64_t Unsigned = 0;
+ if (parseSigned(token().integerValue(), Signed))
+ Elements.push_back(static_cast<std::uint64_t>(Signed));
+ else if (parseUnsigned(token().integerValue(), Unsigned))
+ Elements.push_back(Unsigned);
+ else
+ return makeUnexpected(ErrorCode::ParseError, "DIExpression integer exceeds 64 bits");
+ advance();
+ } else {
+ return makeUnexpected(ErrorCode::ParseError, "expected a DIExpression opcode or integer");
+ }
+ if (!consume(MITokenKind::Comma))
+ break;
+ }
+ if (auto Right = expect(MITokenKind::RightParen, "')' after !DIExpression"); !Right)
+ return std::unexpected(Right.error());
+ return MachineDIExpression(std::move(Elements));
+}
+
+Expected<MachineMetadataValue> MetadataTextParser::parseValue() {
+ MachineMetadataValue Value;
+ if (at(MITokenKind::Exclaim)) {
+ auto Reference = parseReference();
+ if (!Reference)
+ return std::unexpected(Reference.error());
+ auto Created = MachineMetadataValue::reference(*Reference);
+ if (!Created)
+ return std::unexpected(Created.error());
+ Value = std::move(*Created);
+ } else {
+ auto Expression = parseExpression();
+ if (!Expression)
+ return std::unexpected(Expression.error());
+ Value = MachineMetadataValue::expression(std::move(*Expression));
+ }
+ if (auto Finished = finish(); !Finished)
+ return std::unexpected(Finished.error());
+ return Value;
+}
+
+Expected<void>
+MetadataTextParser::parseDILocation(std::vector<MachineDILocationFrame> &Frames,
+ std::optional<MachineMetadataReference> &Terminal) {
+ if (!at(MITokenKind::MetadataKeyword) || !token().isMetadataKeyword("!DILocation")) {
+ return makeUnexpected(ErrorCode::ParseError, "expected an inline !DILocation");
+ }
+ advance();
+ if (auto Left = expect(MITokenKind::LeftParen, "'(' after !DILocation"); !Left)
+ return Left;
+
+ MachineDILocationFrame Frame;
+ bool HasLine = false;
+ bool HasScope = false;
+ std::vector<MachineDILocationFrame> NestedFrames;
+ std::optional<MachineMetadataReference> NestedTerminal;
+ std::unordered_set<std::string> Seen;
+ while (!at(MITokenKind::RightParen)) {
+ if (!at(MITokenKind::Identifier))
+ return makeUnexpected(ErrorCode::ParseError, "expected a DILocation field name");
+ const std::string Field(token().stringValue());
+ if (!Seen.insert(Field).second) {
+ return makeUnexpected(ErrorCode::ParseError, "duplicate DILocation field '" + Field + "'");
+ }
+ advance();
+ if (auto Colon = expect(MITokenKind::Colon, "':' after a DILocation field"); !Colon)
+ return Colon;
+
+ if (Field == "line") {
+ std::uint32_t Value = 0;
+ if (!at(MITokenKind::IntegerLiteral) || !parseUnsigned32(token().integerValue(), Value)) {
+ return makeUnexpected(ErrorCode::ParseError,
+ "DILocation line must be an unsigned 32-bit integer");
+ }
+ Frame.Line = Value;
+ HasLine = true;
+ advance();
+ } else if (Field == "column") {
+ std::uint64_t Value = 0;
+ if (!at(MITokenKind::IntegerLiteral) || !parseUnsigned(token().integerValue(), Value) ||
+ Value > std::numeric_limits<std::uint16_t>::max()) {
+ return makeUnexpected(ErrorCode::ParseError,
+ "DILocation column must be an unsigned 16-bit integer");
+ }
+ Frame.Column = static_cast<std::uint16_t>(Value);
+ advance();
+ } else if (Field == "scope") {
+ auto Scope = parseReference();
+ if (!Scope)
+ return std::unexpected(Scope.error());
+ if (Scope->scope() != MachineMetadataScope::EmbeddedIR) {
+ return makeUnexpected(ErrorCode::ParseError,
+ "DILocation scope must reference embedded LLVM IR metadata");
+ }
+ Frame.Scope = *Scope;
+ HasScope = true;
+ } else if (Field == "inlinedAt") {
+ if (at(MITokenKind::MetadataKeyword) && token().isMetadataKeyword("!DILocation")) {
+ if (auto Nested = parseDILocation(NestedFrames, NestedTerminal); !Nested)
+ return Nested;
+ } else {
+ auto Reference = parseReference();
+ if (!Reference)
+ return std::unexpected(Reference.error());
+ if (Reference->scope() != MachineMetadataScope::EmbeddedIR) {
+ return makeUnexpected(ErrorCode::ParseError,
+ "DILocation inlinedAt must reference embedded LLVM IR metadata");
+ }
+ NestedTerminal = *Reference;
+ }
+ } else if (Field == "isImplicitCode") {
+ if (!at(MITokenKind::Identifier) ||
+ (token().stringValue() != "true" && token().stringValue() != "false")) {
+ return makeUnexpected(ErrorCode::ParseError,
+ "DILocation isImplicitCode must be true or false");
+ }
+ Frame.IsImplicitCode = token().stringValue() == "true";
+ advance();
+ } else if (Field == "atomGroup") {
+ std::uint64_t Value = 0;
+ if (!at(MITokenKind::IntegerLiteral) || !parseUnsigned(token().integerValue(), Value) ||
+ Value > MachineDILocation::MaxAtomGroup) {
+ return makeUnexpected(ErrorCode::ParseError, "DILocation atomGroup exceeds 61 bits");
+ }
+ Frame.AtomGroup = Value;
+ advance();
+ } else if (Field == "atomRank") {
+ std::uint64_t Value = 0;
+ if (!at(MITokenKind::IntegerLiteral) || !parseUnsigned(token().integerValue(), Value) ||
+ Value > 7) {
+ return makeUnexpected(ErrorCode::ParseError, "DILocation atomRank exceeds three bits");
+ }
+ Frame.AtomRank = static_cast<std::uint8_t>(Value);
+ advance();
+ } else {
+ return makeUnexpected(ErrorCode::ParseError, "unknown DILocation field '" + Field + "'");
+ }
+ if (!consume(MITokenKind::Comma))
+ break;
+ }
+ if (auto Right = expect(MITokenKind::RightParen, "')' after !DILocation"); !Right)
+ return Right;
+ if (!HasLine || !HasScope)
+ return makeUnexpected(ErrorCode::ParseError, "DILocation requires line and scope fields");
+ Frames.push_back(Frame);
+ Frames.insert(Frames.end(), NestedFrames.begin(), NestedFrames.end());
+ Terminal = NestedTerminal;
+ return {};
+}
+
+Expected<MachineDebugLocation> MetadataTextParser::parseLocation() {
+ MachineDebugLocation Location;
+ if (at(MITokenKind::Exclaim)) {
+ auto Reference = parseReference();
+ if (!Reference)
+ return std::unexpected(Reference.error());
+ auto Created = MachineDebugLocation::reference(*Reference);
+ if (!Created)
+ return std::unexpected(Created.error());
+ Location = std::move(*Created);
+ } else {
+ std::vector<MachineDILocationFrame> Frames;
+ std::optional<MachineMetadataReference> Terminal;
+ if (auto Parsed = parseDILocation(Frames, Terminal); !Parsed)
+ return std::unexpected(Parsed.error());
+ auto Created = MachineDILocation::create(std::move(Frames), Terminal);
+ if (!Created)
+ return std::unexpected(Created.error());
+ Location = MachineDebugLocation::inlineLocation(std::move(*Created));
+ }
+ if (auto Finished = finish(); !Finished)
+ return std::unexpected(Finished.error());
+ return Location;
+}
+
+} // namespace rcc::mir
+
+namespace rcc::mir {
+
+Expected<void> verifyMIRDocumentReferences(const MIRDocument &Document,
+ const MachineFunction &Function, bool UseEmbeddedIR) {
+ const EmbeddedIRReferenceIndex *Embedded = UseEmbeddedIR ? Document.embeddedIR() : nullptr;
+ const EmbeddedIRFunction *IRFunction = nullptr;
+ if (Embedded != nullptr) {
+ if (Function.name().empty()) {
+ std::size_t AnonymousOrdinal = 0;
+ bool FoundFunction = false;
+ for (std::size_t Index = 0; Index < Document.unit().functionCount(); ++Index) {
+ const MachineFunction *Candidate = Document.unit().functionAt(Index);
+ if (Candidate == &Function) {
+ FoundFunction = true;
+ break;
+ }
+ if (Candidate != nullptr && Candidate->name().empty())
+ ++AnonymousOrdinal;
+ }
+ if (FoundFunction)
+ IRFunction = Embedded->unnamedFunction(AnonymousOrdinal);
+ } else {
+ IRFunction = Embedded->function(Function.name());
+ }
+ if (IRFunction == nullptr) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "machine function has no matching embedded LLVM IR function");
+ }
+ }
+
+ const auto ValidateGlobal = [Embedded](std::string_view Name,
+ std::string_view Description) -> Expected<void> {
+ if (Embedded == nullptr) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ std::string(Description) +
+ " requires embedded LLVM IR to resolve global '" +
+ std::string(Name) + "'");
+ }
+ if (Embedded->hasGlobalValue(Name))
+ return {};
+ return makeUnexpected(ErrorCode::VerificationError,
+ std::string(Description) +
+ " references an undefined embedded LLVM IR global '" +
+ std::string(Name) + "'");
+ };
+ const auto ValidateBlockAddress = [Embedded](std::string_view FunctionName,
+ std::string_view BlockName,
+ std::string_view Description) -> Expected<void> {
+ if (Embedded == nullptr) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ std::string(Description) +
+ " requires embedded LLVM IR to resolve a block address");
+ }
+ const EmbeddedIRFunction *AddressFunction = Embedded->function(FunctionName);
+ if (AddressFunction == nullptr) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ std::string(Description) +
+ " references an undefined embedded LLVM IR function '" +
+ std::string(FunctionName) + "'");
+ }
+ if (AddressFunction->block(BlockName) == nullptr) {
+ return makeUnexpected(
+ ErrorCode::VerificationError,
+ std::string(Description) + " references an undefined embedded LLVM IR block '" +
+ std::string(BlockName) + "' in function '" + std::string(FunctionName) + "'");
+ }
+ return {};
+ };
+ const auto ValidateIRValue = [IRFunction](std::string_view Name,
+ std::string_view Description) -> Expected<void> {
+ if (IRFunction == nullptr) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ std::string(Description) +
+ " requires embedded LLVM IR to resolve IR value '%" +
+ std::string(Name) + "'");
+ }
+ if (IRFunction->value(Name) != nullptr)
+ return {};
+ return makeUnexpected(ErrorCode::VerificationError,
+ std::string(Description) +
+ " references an undefined embedded LLVM IR value '%" +
+ std::string(Name) + "'");
+ };
+ const auto ValidateMetadata = [Embedded](MachineMetadataReference Reference,
+ std::initializer_list<std::string_view> ExpectedTypes,
+ std::string_view Description) -> Expected<void> {
+ if (!Reference.isValid()) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ std::string(Description) + " has an invalid metadata reference");
+ }
+ if (Reference.scope() != MachineMetadataScope::EmbeddedIR)
+ return {};
+ if (Embedded == nullptr) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ std::string(Description) + " requires embedded LLVM IR metadata !" +
+ std::to_string(Reference.slot()));
+ }
+ const EmbeddedIRMetadataDefinition *Definition =
+ Embedded->metadata(std::to_string(Reference.slot()));
+ if (Definition == nullptr) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ std::string(Description) +
+ " references undefined embedded LLVM IR metadata !" +
+ std::to_string(Reference.slot()));
+ }
+ if (ExpectedTypes.size() == 0 ||
+ std::ranges::find(ExpectedTypes, Definition->DynamicType) != ExpectedTypes.end()) {
+ return {};
+ }
+ std::string Expected;
+ for (const std::string_view Type : ExpectedTypes) {
+ if (!Expected.empty())
+ Expected.append(" or ");
+ Expected.append(Type);
+ }
+ return makeUnexpected(ErrorCode::VerificationError,
+ std::string(Description) + " requires " + Expected + " metadata, but !" +
+ std::to_string(Reference.slot()) + " is " +
+ (Definition->DynamicType.empty()
+ ? std::string("an unclassified metadata node")
+ : Definition->DynamicType));
+ };
+ const auto ValidateMetadataValue =
+ [&ValidateMetadata](const MachineMetadataValue &Value,
+ std::string_view Description) -> Expected<void> {
+ const MachineMetadataReference *Reference = Value.reference();
+ return Reference == nullptr ? Expected<void>{} : ValidateMetadata(*Reference, {}, Description);
+ };
+ const auto ValidateLocation =
+ [&ValidateMetadata](const MachineDebugLocation &Location,
+ std::string_view Description) -> Expected<void> {
+ if (const MachineMetadataReference *Reference = Location.reference())
+ return ValidateMetadata(*Reference, {"DILocation"}, Description);
+ const MachineDILocation *Inline = Location.inlineLocation();
+ if (Inline == nullptr)
+ return {};
+ for (const MachineDILocationFrame &Frame : Inline->inlineChain()) {
+ auto Verification =
+ ValidateMetadata(Frame.Scope, {"DISubprogram", "DILexicalBlock", "DILexicalBlockFile"},
+ "inline DILocation scope");
+ if (!Verification)
+ return Verification;
+ }
+ if (Inline->terminalInlinedAt()) {
+ return ValidateMetadata(*Inline->terminalInlinedAt(), {"DILocation"},
+ "inline DILocation inlinedAt");
+ }
+ return {};
+ };
+ const auto ValidateVariableDebugInfo =
+ [&ValidateMetadata, &ValidateMetadataValue,
+ &ValidateLocation](const MachineVariableDebugInfo &DebugInfo) -> Expected<void> {
+ if (DebugInfo.Variable) {
+ auto Verification =
+ ValidateMetadata(*DebugInfo.Variable, {"DILocalVariable"}, "debug variable");
+ if (!Verification)
+ return Verification;
+ }
+ if (DebugInfo.Expression) {
+ if (const MachineMetadataReference *Reference = DebugInfo.Expression->reference();
+ Reference != nullptr) {
+ auto Verification = ValidateMetadata(*Reference, {"DIExpression"}, "debug expression");
+ if (!Verification)
+ return Verification;
+ }
+ auto Verification = ValidateMetadataValue(*DebugInfo.Expression, "debug expression");
+ if (!Verification)
+ return Verification;
+ }
+ if (DebugInfo.Location)
+ return ValidateLocation(*DebugInfo.Location, "debug location");
+ return {};
+ };
+ const auto ValidateGenericType = [Embedded](const auto &Self,
+ const GenericConstantType &Type) -> Expected<void> {
+ if (Type.Kind == GenericConstantTypeKind::Named) {
+ if (Embedded == nullptr) {
+ return makeUnexpected(
+ ErrorCode::VerificationError,
+ "generic constant type requires embedded LLVM IR to resolve named type '%" + Type.Name +
+ "'");
+ }
+ if (Embedded->typeDefinition(Type.Name) == nullptr) {
+ return makeUnexpected(
+ ErrorCode::VerificationError,
+ "generic constant type references an undefined embedded LLVM IR named type '%" +
+ Type.Name + "'");
+ }
+ }
+ for (const GenericConstantType &Element : Type.Elements) {
+ auto Verification = Self(Self, Element);
+ if (!Verification)
+ return Verification;
+ }
+ return {};
+ };
+ const auto ValidateGenericConstant =
+ [&ValidateBlockAddress, &ValidateGenericType,
+ &ValidateGlobal](const auto &Self, const GenericConstantData &Constant) -> Expected<void> {
+ auto TypeVerification = ValidateGenericType(ValidateGenericType, Constant.Type);
+ if (!TypeVerification)
+ return TypeVerification;
+ if (Constant.SourceElementType) {
+ TypeVerification = ValidateGenericType(ValidateGenericType, *Constant.SourceElementType);
+ if (!TypeVerification)
+ return TypeVerification;
+ }
+ switch (Constant.Kind) {
+ case GenericConstantKind::GlobalAddress:
+ case GenericConstantKind::DSOLocalEquivalent:
+ case GenericConstantKind::NoCFI:
+ if (auto Verification = ValidateGlobal(Constant.Symbol, "generic constant"); !Verification)
+ return Verification;
+ break;
+ case GenericConstantKind::BlockAddress:
+ if (auto Verification = ValidateBlockAddress(Constant.Symbol, Constant.Block,
+ "generic constant blockaddress");
+ !Verification) {
+ return Verification;
+ }
+ break;
+ default:
+ break;
+ }
+ for (const GenericConstantData &Element : Constant.Elements) {
+ auto Verification = Self(Self, Element);
+ if (!Verification)
+ return Verification;
+ }
+ return {};
+ };
+
+ for (const MachineBasicBlock *Block = Function.firstBlock(); Block != nullptr;
+ Block = Block->next()) {
+ if (const auto Name = Block->attributes().irBlockName(); Name) {
+ if (IRFunction == nullptr) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "machine basic block requires embedded LLVM IR to resolve block '" +
+ std::string(*Name) + "'");
+ }
+ if (IRFunction->block(*Name) == nullptr) {
+ return makeUnexpected(
+ ErrorCode::VerificationError,
+ "machine basic block references an undefined embedded LLVM IR block '" +
+ std::string(*Name) + "'");
+ }
+ }
+ }
+ for (const StackObject &Object : Function.frameInfo().objects()) {
+ const StackObjectDesc *Description = Object.description();
+ if (Description == nullptr || !Description->AllocaName)
+ continue;
+ if (IRFunction == nullptr) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "stack object requires embedded LLVM IR to resolve alloca '" +
+ *Description->AllocaName + "'");
+ }
+ if (IRFunction->alloca(*Description->AllocaName) == nullptr) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "stack object references an undefined embedded LLVM IR alloca '" +
+ *Description->AllocaName + "'");
+ }
+ }
+
+ for (const MachineConstantPoolEntry &Entry : Function.constantPool().entries()) {
+ const MachineConstantPoolEntryDesc *Description = Entry.description();
+ if (Description == nullptr)
+ continue;
+ if (const auto *Generic = std::get_if<GenericConstantData>(&Description->Value)) {
+ auto Verification = ValidateGenericConstant(ValidateGenericConstant, *Generic);
+ if (!Verification)
+ return Verification;
+ } else if (const auto *Address = std::get_if<BlockAddressConstant>(&Description->Value)) {
+ auto Verification = ValidateBlockAddress(Address->FunctionName, Address->BlockName,
+ "constant-pool block address");
+ if (!Verification)
+ return Verification;
+ }
+ }
+
+ for (const MachineMetadataNode &Node : Function.metadata().nodes()) {
+ for (const MachineMetadataElement &Element : Node.Elements) {
+ const MachineMetadataReference *Reference = Element.reference();
+ if (Reference == nullptr)
+ continue;
+ auto Verification = ValidateMetadata(*Reference, {}, "machine metadata node");
+ if (!Verification)
+ return Verification;
+ }
+ }
+
+ for (const StackObject &Object : Function.frameInfo().objects()) {
+ const StackObjectDesc *Description = Object.description();
+ if (Description == nullptr || !Description->DebugInfo)
+ continue;
+ auto Verification = ValidateVariableDebugInfo(*Description->DebugInfo);
+ if (!Verification)
+ return Verification;
+ }
+ for (const MachineEntryValueInfo &Entry : Function.records().entryValues()) {
+ auto Verification = ValidateVariableDebugInfo(Entry.DebugInfo);
+ if (!Verification)
+ return Verification;
+ }
+ for (const MachineCalledGlobal &Called : Function.records().calledGlobals()) {
+ auto Verification = ValidateGlobal(Called.Callee, "called-global record");
+ if (!Verification)
+ return Verification;
+ }
+
+ for (const MachineBasicBlock *Block = Function.firstBlock(); Block != nullptr;
+ Block = Block->next()) {
+ for (const MachineInstr *Instruction = Block->firstInstruction(); Instruction != nullptr;
+ Instruction = Instruction->next()) {
+ if (Instruction->debugLocation()) {
+ auto Verification =
+ ValidateLocation(*Instruction->debugLocation(), "instruction debug location");
+ if (!Verification)
+ return Verification;
+ }
+ const std::array InstructionMetadata{
+ Instruction->heapAllocMarker(),
+ Instruction->pcSections(),
+ Instruction->mmra(),
+ };
+ for (const auto &Reference : InstructionMetadata) {
+ if (!Reference)
+ continue;
+ auto Verification = ValidateMetadata(*Reference, {}, "instruction metadata attachment");
+ if (!Verification)
+ return Verification;
+ }
+
+ for (const MachineOperand &Operand : Instruction->operands()) {
+ if (const auto *Global = Operand.getIf<GlobalSymbolOperand>(); Global != nullptr) {
+ auto Verification = ValidateGlobal(Global->Name, "global-symbol operand");
+ if (!Verification)
+ return Verification;
+ }
+ if (const auto *Address = Operand.getIf<BlockAddressOperand>(); Address != nullptr) {
+ auto Verification = ValidateBlockAddress(Address->FunctionName, Address->BlockName,
+ "block-address operand");
+ if (!Verification)
+ return Verification;
+ }
+ if (const auto *Metadata = Operand.getIf<MetadataOperand>(); Metadata != nullptr) {
+ auto Verification = ValidateMetadataValue(Metadata->Value, "metadata operand");
+ if (!Verification)
+ return Verification;
+ }
+ }
+
+ for (const MachineMemOperand *Memory : Instruction->memOperands()) {
+ const MachineMemOperandDesc &Description = Memory->description();
+ const std::optional<std::string_view> BaseName = Description.Base.symbolName();
+ switch (Description.Base.kind()) {
+ case MemoryBase::Kind::GlobalSymbol:
+ case MemoryBase::Kind::CallEntryGlobal:
+ if (BaseName) {
+ auto Verification = ValidateGlobal(*BaseName, "machine memory base");
+ if (!Verification)
+ return Verification;
+ }
+ break;
+ case MemoryBase::Kind::IRValue:
+ if (BaseName) {
+ auto Verification = ValidateIRValue(*BaseName, "machine memory base");
+ if (!Verification)
+ return Verification;
+ }
+ break;
+ default:
+ break;
+ }
+ const std::array MetadataReferences{
+ Description.TBAA, Description.AliasScope,
+ Description.NoAlias, Description.NoAliasAddressSpace,
+ Description.RangeMetadata,
+ };
+ for (const auto &Reference : MetadataReferences) {
+ if (!Reference)
+ continue;
+ auto Verification = ValidateMetadata(*Reference, {}, "machine memory operand metadata");
+ if (!Verification)
+ return Verification;
+ }
+ if (Description.SyncScope.value() == 0)
+ continue;
+ const auto Scope =
+ std::ranges::find_if(Document.syncScopes(), [&Description](const auto &Slot) {
+ return Slot.ID == Description.SyncScope;
+ });
+ if (Scope == Document.syncScopes().end()) {
+ return makeUnexpected(
+ ErrorCode::VerificationError,
+ "machine memory operand synchronization scope has no document name");
+ }
+ }
+ }
+ }
+ return {};
+}
+
+} // namespace rcc::mir
+
+namespace rcc::mir {
+
+using parser_detail::parseBool;
+using parser_detail::parsePrefixedID;
+using parser_detail::parseSigned;
+using parser_detail::parseUnsigned;
+using parser_detail::parseUnsigned32;
+using parser_detail::trim;
+
+bool MIRDocumentParser::parseStackObjects(const yaml::Node &Root, MachineFunction &Function,
+ MIParserContext &Context) {
+ auto ParseList = [this, &Function, &Context](const yaml::Node &List, bool Fixed) -> bool {
+ if (requireKind(List, yaml::NodeKind::Sequence,
+ Fixed ? "fixed stack objects" : "stack objects")) {
+ return true;
+ }
+ constexpr std::array FixedKeys{
+ std::string_view{"id"},
+ std::string_view{"type"},
+ std::string_view{"offset"},
+ std::string_view{"size"},
+ std::string_view{"alignment"},
+ std::string_view{"stack-id"},
+ std::string_view{"isImmutable"},
+ std::string_view{"isAliased"},
+ std::string_view{"callee-saved-register"},
+ std::string_view{"callee-saved-restored"},
+ std::string_view{"debug-info-variable"},
+ std::string_view{"debug-info-expression"},
+ std::string_view{"debug-info-location"},
+ };
+ constexpr std::array StackKeys{
+ std::string_view{"id"},
+ std::string_view{"name"},
+ std::string_view{"type"},
+ std::string_view{"offset"},
+ std::string_view{"size"},
+ std::string_view{"alignment"},
+ std::string_view{"stack-id"},
+ std::string_view{"callee-saved-register"},
+ std::string_view{"callee-saved-restored"},
+ std::string_view{"local-offset"},
+ std::string_view{"debug-info-variable"},
+ std::string_view{"debug-info-expression"},
+ std::string_view{"debug-info-location"},
+ };
+ std::unordered_set<std::uint32_t> IDs;
+ for (const yaml::NodeID ElementID : List.sequence()) {
+ const yaml::Node *Element = node(ElementID);
+ if (Element == nullptr || requireKind(*Element, yaml::NodeKind::Mapping, "stack object") ||
+ (Fixed ? validateKeys(*Element, FixedKeys, "fixed stack object")
+ : validateKeys(*Element, StackKeys, "stack object"))) {
+ return true;
+ }
+ const yaml::Node *IDNode = mappingValue(*Element, "id");
+ if (IDNode == nullptr)
+ return fail(Element->range(), "stack object requires 'id'");
+ std::uint64_t WideID = 0;
+ if (unsignedValue(*IDNode, WideID, "a 32-bit stack object ID") ||
+ WideID > std::numeric_limits<std::uint32_t>::max()) {
+ return true;
+ }
+ const auto TextID = static_cast<std::uint32_t>(WideID);
+ if (!IDs.insert(TextID).second)
+ return fail(IDNode->range(), "duplicate stack object ID");
+
+ StackObjectDesc Description;
+ Description.Kind = Fixed ? StackObjectKind::FixedABI : StackObjectKind::Default;
+ Description.Offset = 0;
+ if (const yaml::Node *NameNode = mappingValue(*Element, "name")) {
+ std::string_view Name;
+ if (scalar(*NameNode, Name, "stack object name"))
+ return true;
+ if (!Name.empty())
+ Description.AllocaName = std::string(Name);
+ }
+ if (const yaml::Node *TypeNode = mappingValue(*Element, "type")) {
+ std::string_view Type;
+ if (scalar(*TypeNode, Type, "stack object type"))
+ return true;
+ if (Type == "default")
+ Description.Kind = Fixed ? StackObjectKind::FixedABI : StackObjectKind::Default;
+ else if (Type == "spill-slot")
+ Description.Kind = Fixed ? StackObjectKind::FixedSpillSlot : StackObjectKind::SpillSlot;
+ else if (!Fixed && Type == "variable-sized")
+ Description.Kind = StackObjectKind::VariableSized;
+ else
+ return fail(TypeNode->range(), "unknown stack object type '" + std::string(Type) + "'");
+ }
+ if (const yaml::Node *OffsetNode = mappingValue(*Element, "offset")) {
+ std::int64_t Offset = 0;
+ if (signedValue(*OffsetNode, Offset, "a signed stack object offset"))
+ return true;
+ Description.Offset = Offset;
+ } else if (Fixed) {
+ Description.Offset = 0;
+ }
+ if (Description.Kind != StackObjectKind::VariableSized) {
+ const yaml::Node *SizeNode = mappingValue(*Element, "size");
+ if (SizeNode == nullptr) {
+ if (!Fixed)
+ return fail(Element->range(), "fixed-size stack object requires 'size'");
+ Description.Size = 0;
+ } else {
+ std::uint64_t Size = 0;
+ if (unsignedValue(*SizeNode, Size, "an unsigned stack object size"))
+ return true;
+ Description.Size = Size;
+ }
+ }
+ if (const yaml::Node *AlignmentNode = mappingValue(*Element, "alignment")) {
+ if (alignment(*AlignmentNode, Description.Align, true))
+ return true;
+ }
+ if (const yaml::Node *StackIDNode = mappingValue(*Element, "stack-id")) {
+ std::string_view Name;
+ if (scalar(*StackIDNode, Name, "stack ID"))
+ return true;
+ const auto Stack = parseStackIDName(Name);
+ if (!Stack)
+ return fail(StackIDNode->range(), "unknown stack ID '" + std::string(Name) + "'");
+ Description.Stack = *Stack;
+ }
+ if (const yaml::Node *ImmutableNode = mappingValue(*Element, "isImmutable")) {
+ if (boolean(*ImmutableNode, Description.IsImmutable))
+ return true;
+ }
+ if (const yaml::Node *AliasedNode = mappingValue(*Element, "isAliased")) {
+ if (boolean(*AliasedNode, Description.IsAliased))
+ return true;
+ }
+ if (const yaml::Node *SavedNode = mappingValue(*Element, "callee-saved-register")) {
+ std::string_view SavedName;
+ if (scalar(*SavedNode, SavedName, "physical register"))
+ return true;
+ if (!trim(SavedName).empty()) {
+ PhysicalRegister Reg;
+ if (physicalRegister(SavedName, SavedNode->range(), Reg))
+ return true;
+ Description.CalleeSavedRegister = Reg;
+ }
+ }
+ if (const yaml::Node *RestoredNode = mappingValue(*Element, "callee-saved-restored")) {
+ if (boolean(*RestoredNode, Description.CalleeSavedRestored))
+ return true;
+ }
+ if (const yaml::Node *LocalNode = mappingValue(*Element, "local-offset")) {
+ std::int64_t Offset = 0;
+ if (signedValue(*LocalNode, Offset, "a signed local frame offset"))
+ return true;
+ Description.LocalOffset = Offset;
+ }
+ MachineVariableDebugInfo DebugInfo;
+ if (parseVariableDebugInfo(*Element, Function, DebugInfo))
+ return true;
+ if (DebugInfo.Variable || DebugInfo.Expression || DebugInfo.Location)
+ Description.DebugInfo = std::move(DebugInfo);
+
+ auto Created = Function.frameInfo().createObject(std::move(Description));
+ if (!Created)
+ return importError(Element->range(), Created.error());
+ MIFrameObjectSlot Slot{
+ .TextID = TextID,
+ .Index = *Created,
+ .Name = std::nullopt,
+ };
+ if (const StackObjectDesc *CreatedDescription =
+ Function.frameInfo().object(*Created)->description();
+ CreatedDescription != nullptr && CreatedDescription->AllocaName) {
+ Slot.Name = *CreatedDescription->AllocaName;
+ }
+ (Fixed ? Context.FixedStackObjects : Context.StackObjects).push_back(std::move(Slot));
+ }
+ return false;
+ };
+
+ if (const yaml::Node *Fixed = mappingValue(Root, "fixedStack")) {
+ if (ParseList(*Fixed, true))
+ return true;
+ }
+ if (const yaml::Node *Stack = mappingValue(Root, "stack")) {
+ if (ParseList(*Stack, false))
+ return true;
+ }
+ return false;
+}
+
+bool MIRDocumentParser::parseFrameReference(const yaml::Node &Node, const MIParserContext &Context,
+ FrameIndex &Index) {
+ std::string_view Text;
+ if (scalar(Node, Text, "frame index"))
+ return true;
+ bool Fixed = false;
+ std::optional<std::uint32_t> ID = parsePrefixedID(Text, "%stack.");
+ if (!ID) {
+ ID = parsePrefixedID(Text, "%fixed-stack.");
+ Fixed = true;
+ }
+ if (!ID)
+ return fail(Node.range(), "frame index must use '%stack.N' or '%fixed-stack.N'");
+ const auto &Slots = Fixed ? Context.FixedStackObjects : Context.StackObjects;
+ const auto Found = std::ranges::find_if(
+ Slots, [ID](const MIFrameObjectSlot &Slot) { return Slot.TextID == *ID; });
+ if (Found == Slots.end())
+ return fail(Node.range(), "reference to undefined frame object");
+ Index = Found->Index;
+ return false;
+}
+
+MachineBasicBlock *MIRDocumentParser::blockAt(MachineFunction &Function,
+ std::uint32_t Number) const noexcept {
+ for (MachineBasicBlock *Block = Function.firstBlock(); Block != nullptr; Block = Block->next()) {
+ if (Block->denseNumber() == Number)
+ return Block;
+ }
+ return nullptr;
+}
+
+MachineInstr *MIRDocumentParser::instructionAt(MachineFunction &Function, std::uint32_t BlockNumber,
+ std::uint32_t Offset) const noexcept {
+ MachineBasicBlock *Block = blockAt(Function, BlockNumber);
+ if (Block == nullptr)
+ return nullptr;
+ MachineInstr *Instruction = Block->firstInstruction();
+ for (std::uint32_t Index = 0; Instruction != nullptr && Index < Offset; ++Index)
+ Instruction = Instruction->next();
+ return Instruction;
+}
+
+bool MIRDocumentParser::parseBlockReference(std::string_view Text, const TextRange &Range,
+ MachineFunction &Function, MachineBasicBlock *&Block) {
+ const auto Number = parsePrefixedID(Text, "%bb.");
+ if (!Number)
+ return fail(Range, "basic block reference must use the '%bb.N' MIR spelling");
+ Block = blockAt(Function, *Number);
+ if (Block == nullptr)
+ return fail(Range, "reference to undefined machine basic block");
+ return false;
+}
+
+bool MIRDocumentParser::parseSaveRestorePoints(const yaml::Node &Node, MachineFunction &Function,
+ std::vector<MachineFrameSaveRestorePoint> &Points) {
+ if (requireKind(Node, yaml::NodeKind::Sequence, "frame save/restore points"))
+ return true;
+ constexpr std::array Keys{
+ std::string_view{"point"},
+ std::string_view{"registers"},
+ };
+ for (const yaml::NodeID PointID : Node.sequence()) {
+ const yaml::Node *PointNode = node(PointID);
+ if (PointNode == nullptr ||
+ requireKind(*PointNode, yaml::NodeKind::Mapping, "frame save/restore point") ||
+ validateKeys(*PointNode, Keys, "frame save/restore point")) {
+ return true;
+ }
+ const yaml::Node *BlockNode = mappingValue(*PointNode, "point");
+ if (BlockNode == nullptr)
+ return fail(PointNode->range(), "frame save/restore point requires 'point'");
+ std::string_view BlockText;
+ MachineBasicBlock *Block = nullptr;
+ if (scalar(*BlockNode, BlockText, "basic block reference") ||
+ parseBlockReference(BlockText, BlockNode->range(), Function, Block)) {
+ return true;
+ }
+ if (Block == nullptr)
+ return fail(BlockNode->range(), "reference to undefined machine basic block");
+ MachineFrameSaveRestorePoint Point{.Block = Block->stableID(), .Registers = {}};
+ if (const yaml::Node *RegistersNode = mappingValue(*PointNode, "registers")) {
+ if (requireKind(*RegistersNode, yaml::NodeKind::Sequence, "save/restore point registers")) {
+ return true;
+ }
+ for (const yaml::NodeID RegID : RegistersNode->sequence()) {
+ const yaml::Node *RegNode = node(RegID);
+ PhysicalRegister Reg;
+ if (RegNode == nullptr || physicalRegister(*RegNode, Reg))
+ return true;
+ Point.Registers.push_back(Reg);
+ }
+ }
+ Points.push_back(std::move(Point));
+ }
+ return false;
+}
+
+bool MIRDocumentParser::parseFrameState(const yaml::Node &Root, MachineFunction &Function,
+ const MIParserContext &Context) {
+ const yaml::Node *Frame = mappingValue(Root, "frameInfo");
+ if (Frame == nullptr)
+ return false;
+ if (requireKind(*Frame, yaml::NodeKind::Mapping, "frameInfo"))
+ return true;
+ constexpr std::array Keys{
+ std::string_view{"isFrameAddressTaken"},
+ std::string_view{"isReturnAddressTaken"},
+ std::string_view{"hasStackMap"},
+ std::string_view{"hasPatchPoint"},
+ std::string_view{"stackSize"},
+ std::string_view{"offsetAdjustment"},
+ std::string_view{"maxAlignment"},
+ std::string_view{"adjustsStack"},
+ std::string_view{"hasCalls"},
+ std::string_view{"framePointerPolicy"},
+ std::string_view{"stackProtector"},
+ std::string_view{"functionContext"},
+ std::string_view{"maxCallFrameSize"},
+ std::string_view{"cvBytesOfCalleeSavedRegisters"},
+ std::string_view{"hasOpaqueSPAdjustment"},
+ std::string_view{"hasVAStart"},
+ std::string_view{"hasMustTailInVarArgFunc"},
+ std::string_view{"hasTailCall"},
+ std::string_view{"isCalleeSavedInfoValid"},
+ std::string_view{"localFrameSize"},
+ std::string_view{"savePoint"},
+ std::string_view{"restorePoint"},
+ };
+ if (validateKeys(*Frame, Keys, "frameInfo"))
+ return true;
+
+ MachineFrameState State = Function.frameInfo().state();
+ const std::array BoolBindings{
+ std::pair{"isFrameAddressTaken", &State.IsFrameAddressTaken},
+ std::pair{"isReturnAddressTaken", &State.IsReturnAddressTaken},
+ std::pair{"hasStackMap", &State.HasStackMap},
+ std::pair{"hasPatchPoint", &State.HasPatchPoint},
+ std::pair{"adjustsStack", &State.AdjustsStack},
+ std::pair{"hasCalls", &State.HasCalls},
+ std::pair{"hasOpaqueSPAdjustment", &State.HasOpaqueSPAdjustment},
+ std::pair{"hasVAStart", &State.HasVAStart},
+ std::pair{"hasMustTailInVarArgFunc", &State.HasMustTailInVarArgFunc},
+ std::pair{"hasTailCall", &State.HasTailCall},
+ std::pair{"isCalleeSavedInfoValid", &State.IsCalleeSavedInfoValid},
+ };
+ for (const auto &[Key, Destination] : BoolBindings) {
+ if (const yaml::Node *Value = mappingValue(*Frame, Key)) {
+ if (boolean(*Value, *Destination))
+ return true;
+ }
+ }
+
+ if (const yaml::Node *Value = mappingValue(*Frame, "stackSize")) {
+ if (unsignedValue(*Value, State.StackSize, "an unsigned stack size"))
+ return true;
+ }
+ if (const yaml::Node *Value = mappingValue(*Frame, "offsetAdjustment")) {
+ std::int64_t Wide = 0;
+ if (signedValue(*Value, Wide, "a 32-bit signed offset adjustment") ||
+ Wide < std::numeric_limits<std::int32_t>::min() ||
+ Wide > std::numeric_limits<std::int32_t>::max()) {
+ return fail(Value->range(), "offset adjustment is outside the 32-bit signed range");
+ }
+ State.OffsetAdjustment = static_cast<std::int32_t>(Wide);
+ }
+ if (const yaml::Node *Value = mappingValue(*Frame, "maxAlignment")) {
+ std::uint64_t Bytes = 0;
+ if (unsignedValue(*Value, Bytes, "an unsigned maximum alignment"))
+ return true;
+ if (Bytes == 0) {
+ State.MaxAlignment.reset();
+ } else {
+ auto Parsed = Alignment::create(Bytes);
+ if (!Parsed)
+ return importError(Value->range(), Parsed.error());
+ State.MaxAlignment = *Parsed;
+ }
+ }
+ if (const yaml::Node *Value = mappingValue(*Frame, "framePointerPolicy")) {
+ std::string_view Name;
+ if (scalar(*Value, Name, "frame-pointer policy"))
+ return true;
+ const auto Policy = parseMachineFramePointerPolicy(Name);
+ if (!Policy)
+ return fail(Value->range(), "unknown frame-pointer policy '" + std::string(Name) + "'");
+ State.FramePointerPolicy = *Policy;
+ }
+ for (const auto &[Key, Destination] : {std::pair{"stackProtector", &State.StackProtector},
+ std::pair{"functionContext", &State.FunctionContext}}) {
+ if (const yaml::Node *Value = mappingValue(*Frame, Key)) {
+ std::string_view Reference;
+ if (scalar(*Value, Reference, "frame index"))
+ return true;
+ if (Reference.empty()) {
+ Destination->reset();
+ continue;
+ }
+ FrameIndex Index;
+ if (parseFrameReference(*Value, Context, Index))
+ return true;
+ *Destination = Index;
+ }
+ }
+ if (const yaml::Node *Value = mappingValue(*Frame, "maxCallFrameSize")) {
+ std::uint64_t Size = 0;
+ if (unsignedValue(*Value, Size, "a 32-bit unsigned maximum call-frame size") ||
+ Size > std::numeric_limits<std::uint32_t>::max()) {
+ return fail(Value->range(), "maximum call-frame size is outside the 32-bit unsigned range");
+ }
+ if (Size == std::numeric_limits<std::uint32_t>::max())
+ State.MaxCallFrameSize.reset();
+ else
+ State.MaxCallFrameSize = Size;
+ }
+ for (const auto &[Key, Destination] :
+ {std::pair{"cvBytesOfCalleeSavedRegisters", &State.CVBytesOfCalleeSavedRegisters},
+ std::pair{"localFrameSize", &State.LocalFrameSize}}) {
+ if (const yaml::Node *Value = mappingValue(*Frame, Key)) {
+ std::uint64_t Wide = 0;
+ if (unsignedValue(*Value, Wide, "a 32-bit unsigned frame value") ||
+ Wide > std::numeric_limits<std::uint32_t>::max()) {
+ return fail(Value->range(), "frame value is outside the 32-bit unsigned range");
+ }
+ *Destination = static_cast<std::uint32_t>(Wide);
+ }
+ }
+ if (const yaml::Node *Value = mappingValue(*Frame, "savePoint")) {
+ if (parseSaveRestorePoints(*Value, Function, State.SavePoints))
+ return true;
+ }
+ if (const yaml::Node *Value = mappingValue(*Frame, "restorePoint")) {
+ if (parseSaveRestorePoints(*Value, Function, State.RestorePoints))
+ return true;
+ }
+ auto StateResult = Function.frameInfo().setState(std::move(State));
+ if (!StateResult)
+ return importError(Frame->range(), StateResult.error());
+ return false;
+}
+
+} // namespace rcc::mir
+
+namespace rcc::mir {
+
+using parser_detail::parseBool;
+using parser_detail::parsePrefixedID;
+using parser_detail::parseSigned;
+using parser_detail::parseUnsigned;
+using parser_detail::parseUnsigned32;
+using parser_detail::trim;
+
+bool MIRDocumentParser::parseFunctionScalars(const yaml::Node &Root, MachineFunction &Function,
+ MIRFunctionDocumentInfo &DocumentInfo) {
+ if (const yaml::Node *AlignNode = mappingValue(Root, "alignment")) {
+ Alignment Align;
+ if (alignment(*AlignNode, Align, true))
+ return true;
+ if (Align.bytes() != 1 || AlignNode->scalar() != "0")
+ Function.attributes().setAlignment(Align);
+ }
+
+ constexpr std::array PropertyBindings{
+ std::pair{"legalized", MachineFunctionProperty::Legalized},
+ std::pair{"regBankSelected", MachineFunctionProperty::RegBankSelected},
+ std::pair{"selected", MachineFunctionProperty::Selected},
+ std::pair{"failedISel", MachineFunctionProperty::FailedISel},
+ std::pair{"tracksRegLiveness", MachineFunctionProperty::TracksLiveness},
+ std::pair{"noPhis", MachineFunctionProperty::NoPHIs},
+ std::pair{"isSSA", MachineFunctionProperty::IsSSA},
+ std::pair{"noVRegs", MachineFunctionProperty::NoVRegs},
+ std::pair{"failsVerification", MachineFunctionProperty::FailsVerification},
+ };
+ for (const auto &[Key, Property] : PropertyBindings) {
+ const yaml::Node *ValueNode = mappingValue(Root, Key);
+ if (ValueNode == nullptr)
+ continue;
+ bool Value = false;
+ if (boolean(*ValueNode, Value))
+ return true;
+ if (Value)
+ Function.properties().set(Property);
+ else
+ Function.properties().clear(Property);
+ if (Key == std::string_view("noPhis"))
+ DocumentInfo.NoPHIs = Value;
+ else if (Key == std::string_view("isSSA"))
+ DocumentInfo.IsSSA = Value;
+ else if (Key == std::string_view("noVRegs"))
+ DocumentInfo.NoVRegs = Value;
+ }
+
+ constexpr std::array AttributeBindings{
+ std::pair{"exposesReturnsTwice", MachineFunctionAttribute::ExposesReturnsTwice},
+ std::pair{"hasWinCFI", MachineFunctionAttribute::HasWinCFI},
+ std::pair{"hasFakeUses", MachineFunctionAttribute::HasFakeUses},
+ std::pair{"callsEHReturn", MachineFunctionAttribute::CallsEHReturn},
+ std::pair{"callsUnwindInit", MachineFunctionAttribute::CallsUnwindInit},
+ std::pair{"hasEHContTarget", MachineFunctionAttribute::HasEHContTarget},
+ std::pair{"hasEHScopes", MachineFunctionAttribute::HasEHScopes},
+ std::pair{"hasEHFunclets", MachineFunctionAttribute::HasEHFunclets},
+ std::pair{"isOutlined", MachineFunctionAttribute::IsOutlined},
+ std::pair{"debugInstrRef", MachineFunctionAttribute::DebugInstrRef},
+ std::pair{"tracksDebugUserValues", MachineFunctionAttribute::TracksDebugUserValues},
+ };
+ for (const auto &[Key, Attribute] : AttributeBindings) {
+ const yaml::Node *ValueNode = mappingValue(Root, Key);
+ if (ValueNode == nullptr)
+ continue;
+ bool Value = false;
+ if (boolean(*ValueNode, Value))
+ return true;
+ if (Value)
+ Function.attributes().set(Attribute);
+ else
+ Function.attributes().clear(Attribute);
+ if (Key == std::string_view("hasFakeUses"))
+ DocumentInfo.HasFakeUses = Value;
+ }
+
+ return false;
+}
+
+bool MIRDocumentParser::recomputeProperties(const yaml::Node &Root, MachineFunction &Function,
+ const MIRFunctionDocumentInfo &DocumentInfo) {
+ bool HasPHI = false;
+ bool HasFakeUse = false;
+ bool IsSSA = true;
+ bool HasTiedOperands = false;
+ bool AllTiedOperandsRewritten = true;
+ bool HasFrameIndex = false;
+ bool HasExecutablePseudo = false;
+ std::unordered_set<Register> Definitions;
+ for (const MachineBasicBlock *Block = Function.firstBlock(); Block != nullptr;
+ Block = Block->next()) {
+ for (const MachineInstr *Instruction = Block->firstInstruction(); Instruction != nullptr;
+ Instruction = Instruction->next()) {
+ const InstructionDescriptor *Descriptor = Target.instructionDescriptor(Instruction->opcode());
+ if (Descriptor == nullptr)
+ return fail(Root.range(), "instruction has no target descriptor");
+ HasPHI |= Descriptor->Flags.has(InstructionFlag::Phi);
+ HasFakeUse |= Descriptor->Flags.has(InstructionFlag::FakeUse);
+ HasExecutablePseudo |= Descriptor->Flags.has(InstructionFlag::Pseudo) &&
+ !Descriptor->Flags.has(InstructionFlag::Meta) &&
+ !Descriptor->Flags.has(InstructionFlag::TerminalEmission);
+ const auto Operands = Instruction->operands();
+ for (std::size_t Index = 0; Index < Operands.size(); ++Index) {
+ const MachineOperand &Operand = Operands[Index];
+ HasFrameIndex |= Operand.getIf<FrameIndexOperand>() != nullptr;
+ const auto Reg = Operand.registerValue();
+ if (Reg && Reg->isVirtual() && Operand.isDef() && !Definitions.insert(*Reg).second) {
+ IsSSA = false;
+ }
+ if (!Operand.tiedOperandIndex() || !Operand.isUse())
+ continue;
+ HasTiedOperands = true;
+ const MachineOperand *Definition = Instruction->operandAt(*Operand.tiedOperandIndex());
+ if (Definition == nullptr || Definition->registerValue() != Reg)
+ AllTiedOperandsRewritten = false;
+ }
+ for (const MachineMemOperand *Memory : Instruction->memOperands())
+ HasFrameIndex |= Memory->description().Base.frameIndex().has_value();
+ }
+ }
+ const bool NoPHIs = !HasPHI;
+ const bool NoVRegs =
+ std::ranges::none_of(Function.registerInfo().virtualRegisters(),
+ [](const VirtualRegisterInfo &Info) { return !Info.IsTombstone; });
+
+ const auto ApplyComputed = [this, &Root, &Function](std::optional<bool> Explicit, bool Computed,
+ MachineFunctionProperty Property,
+ std::string_view Conflict) -> bool {
+ if (Explicit && *Explicit && !Computed) {
+ return fail(Root.range(), std::string(Function.name()) + std::string(Conflict));
+ }
+ if (Explicit.value_or(Computed))
+ Function.properties().set(Property);
+ else
+ Function.properties().clear(Property);
+ return false;
+ };
+ if (ApplyComputed(DocumentInfo.NoPHIs, NoPHIs, MachineFunctionProperty::NoPHIs,
+ " has explicit property NoPhi, but contains at least one PHI") ||
+ ApplyComputed(DocumentInfo.IsSSA, IsSSA, MachineFunctionProperty::IsSSA,
+ " has explicit property IsSSA, but is not valid SSA") ||
+ ApplyComputed(DocumentInfo.NoVRegs, NoVRegs, MachineFunctionProperty::NoVRegs,
+ " has explicit property NoVRegs, but contains virtual registers")) {
+ return true;
+ }
+
+ if (DocumentInfo.HasFakeUses && !*DocumentInfo.HasFakeUses && HasFakeUse) {
+ return fail(Root.range(),
+ std::string(Function.name()) +
+ " has explicit property hasFakeUses=false, but contains fake uses");
+ }
+ if (DocumentInfo.HasFakeUses.value_or(HasFakeUse))
+ Function.attributes().set(MachineFunctionAttribute::HasFakeUses);
+ else
+ Function.attributes().clear(MachineFunctionAttribute::HasFakeUses);
+
+ const bool Selected = Function.properties().has(MachineFunctionProperty::Selected);
+ const bool StructurallyPostSSA =
+ Selected && NoPHIs && !Function.properties().has(MachineFunctionProperty::IsSSA);
+ if (AllTiedOperandsRewritten && (HasTiedOperands || StructurallyPostSSA))
+ Function.properties().set(MachineFunctionProperty::TiedOpsRewritten);
+ else
+ Function.properties().clear(MachineFunctionProperty::TiedOpsRewritten);
+
+ if (HasFrameIndex)
+ Function.properties().clear(MachineFunctionProperty::NoFrameIndices);
+ else
+ Function.properties().set(MachineFunctionProperty::NoFrameIndices);
+
+ const MachineFrameState &FrameState = Function.frameInfo().state();
+ const bool StructurallyFinalizedFrame = Selected && NoVRegs && !HasFrameIndex &&
+ FrameState.IsCalleeSavedInfoValid &&
+ FrameState.MaxAlignment.has_value();
+ if (StructurallyFinalizedFrame) {
+ if (!Function.frameInfo().isFrozen()) {
+ auto Frozen = Function.frameInfo().freeze(FrameState.StackSize, *FrameState.MaxAlignment);
+ if (!Frozen)
+ return importError(Root.range(), Frozen.error(), "mir-frame-finalization");
+ }
+ Function.properties().set(MachineFunctionProperty::FrameFinalized);
+ } else {
+ Function.properties().clear(MachineFunctionProperty::FrameFinalized);
+ }
+
+ if (StructurallyFinalizedFrame && !HasExecutablePseudo)
+ Function.properties().set(MachineFunctionProperty::NoExecutablePseudos);
+ else
+ Function.properties().clear(MachineFunctionProperty::NoExecutablePseudos);
+ return false;
+}
+
+bool MIRDocumentParser::parseRegisters(const yaml::Node &Root, MachineFunction &Function,
+ MIParserContext &Context) {
+ const yaml::Node *Registers = mappingValue(Root, "registers");
+ if (Registers != nullptr) {
+ if (requireKind(*Registers, yaml::NodeKind::Sequence, "registers"))
+ return true;
+ constexpr std::array RegisterKeys{
+ std::string_view{"id"},
+ std::string_view{"class"},
+ std::string_view{"preferred-register"},
+ std::string_view{"flags"},
+ };
+ std::unordered_set<std::uint32_t> IDs;
+ for (const yaml::NodeID ElementID : Registers->sequence()) {
+ const yaml::Node *Element = node(ElementID);
+ if (Element == nullptr || requireKind(*Element, yaml::NodeKind::Mapping, "register entry") ||
+ validateKeys(*Element, RegisterKeys, "register")) {
+ return true;
+ }
+ const yaml::Node *IDNode = mappingValue(*Element, "id");
+ const yaml::Node *ClassNode = mappingValue(*Element, "class");
+ if (IDNode == nullptr || ClassNode == nullptr)
+ return fail(Element->range(), "register entry requires 'id' and 'class'");
+ std::uint64_t WideID = 0;
+ std::string_view ClassName;
+ if (unsignedValue(*IDNode, WideID, "a 32-bit virtual-register ID") ||
+ WideID > std::numeric_limits<std::uint32_t>::max() ||
+ scalar(*ClassNode, ClassName, "register class or bank")) {
+ return true;
+ }
+ const auto TextID = static_cast<std::uint32_t>(WideID);
+ if (!IDs.insert(TextID).second)
+ return fail(IDNode->range(), "duplicate virtual register ID");
+ auto Created = Function.registerInfo().createVirtualRegister();
+ if (!Created)
+ return importError(IDNode->range(), Created.error());
+ Context.VirtualRegisters.push_back({TextID, *Created});
+
+ if (ClassName != "_") {
+ if (const RegisterClassDesc *Class = Target.registers().registerClass(ClassName)) {
+ auto SetResult = Function.registerInfo().setRegisterClass(*Created, Class->ID);
+ if (!SetResult)
+ return importError(ClassNode->range(), SetResult.error());
+ SetResult = Function.registerInfo().setRegisterBank(*Created, Class->DefaultBank);
+ if (!SetResult)
+ return importError(ClassNode->range(), SetResult.error());
+ } else if (const RegisterBankDesc *Bank = Target.registers().registerBank(ClassName)) {
+ auto SetResult = Function.registerInfo().setRegisterBank(*Created, Bank->ID);
+ if (!SetResult)
+ return importError(ClassNode->range(), SetResult.error());
+ } else {
+ return fail(ClassNode->range(),
+ "unknown register class or bank '" + std::string(ClassName) + "'");
+ }
+ }
+
+ if (const yaml::Node *Preferred = mappingValue(*Element, "preferred-register")) {
+ std::string_view PreferredName;
+ PhysicalRegister Reg;
+ if (scalar(*Preferred, PreferredName, "physical register"))
+ return true;
+ if (!trim(PreferredName).empty()) {
+ if (physicalRegister(PreferredName, Preferred->range(), Reg))
+ return true;
+ auto AddResult = Function.registerInfo().addAllocationHint(*Created, Reg);
+ if (!AddResult)
+ return importError(Preferred->range(), AddResult.error());
+ }
+ }
+ if (const yaml::Node *Flags = mappingValue(*Element, "flags")) {
+ if (requireKind(*Flags, yaml::NodeKind::Sequence, "virtual-register flags"))
+ return true;
+ std::uint8_t Mask = 0;
+ for (const yaml::NodeID FlagID : Flags->sequence()) {
+ const yaml::Node *FlagNode = node(FlagID);
+ std::string_view Name;
+ if (FlagNode == nullptr || scalar(*FlagNode, Name, "virtual-register flag"))
+ return true;
+ const auto Flag = Target.mirText().virtualRegisterFlag(Name);
+ if (!Flag)
+ return fail(FlagNode->range(),
+ "use of undefined register flag '" + std::string(Name) + "'");
+ if ((Mask & *Flag) != 0)
+ return fail(FlagNode->range(), "duplicate virtual-register flag");
+ Mask = static_cast<std::uint8_t>(Mask | *Flag);
+ }
+ auto SetResult = Function.registerInfo().setTargetRegisterFlags(*Created, Mask);
+ if (!SetResult)
+ return importError(Flags->range(), SetResult.error());
+ }
+ }
+ }
+
+ const yaml::Node *LiveIns = mappingValue(Root, "liveins");
+ if (LiveIns != nullptr) {
+ if (requireKind(*LiveIns, yaml::NodeKind::Sequence, "liveins"))
+ return true;
+ constexpr std::array LiveInKeys{
+ std::string_view{"reg"},
+ std::string_view{"virtual-reg"},
+ };
+ for (const yaml::NodeID ElementID : LiveIns->sequence()) {
+ const yaml::Node *Element = node(ElementID);
+ if (Element == nullptr || requireKind(*Element, yaml::NodeKind::Mapping, "live-in entry") ||
+ validateKeys(*Element, LiveInKeys, "live-in")) {
+ return true;
+ }
+ const yaml::Node *RegNode = mappingValue(*Element, "reg");
+ if (RegNode == nullptr)
+ return fail(Element->range(), "live-in entry requires 'reg'");
+ PhysicalRegister Reg;
+ if (physicalRegister(*RegNode, Reg))
+ return true;
+ std::optional<Register> Initial;
+ if (const yaml::Node *VirtualNode = mappingValue(*Element, "virtual-reg")) {
+ std::string_view Text;
+ if (scalar(*VirtualNode, Text, "virtual register"))
+ return true;
+ if (!trim(Text).empty()) {
+ Register Virtual;
+ if (virtualRegister(Text, VirtualNode->range(), Function, Context, Virtual))
+ return true;
+ Initial = Virtual;
+ }
+ }
+ auto AddResult = Function.registerInfo().addLiveIn(Reg, Initial);
+ if (!AddResult)
+ return importError(Element->range(), AddResult.error());
+ }
+ }
+
+ if (const yaml::Node *CalleeSaved = mappingValue(Root, "calleeSavedRegisters")) {
+ if (requireKind(*CalleeSaved, yaml::NodeKind::Sequence, "callee-saved registers"))
+ return true;
+ std::vector<PhysicalRegister> RegistersValue;
+ for (const yaml::NodeID RegID : CalleeSaved->sequence()) {
+ const yaml::Node *RegNode = node(RegID);
+ PhysicalRegister Reg;
+ if (RegNode == nullptr || physicalRegister(*RegNode, Reg))
+ return true;
+ RegistersValue.push_back(Reg);
+ }
+ auto SetResult = Function.records().setCalleeSavedRegisters(std::move(RegistersValue));
+ if (!SetResult)
+ return importError(CalleeSaved->range(), SetResult.error());
+ }
+ return false;
+}
+
+TextRange MIRDocumentParser::scalarDiagnosticRange(const yaml::Node &Scalar,
+ const TextRange &ScalarRange) const noexcept {
+ if (Scalar.scalarStyle() != yaml::ScalarStyle::Literal &&
+ Scalar.scalarStyle() != yaml::ScalarStyle::Folded) {
+ std::size_t ContentOffset = Scalar.range().Begin.Offset;
+ if (Scalar.scalarStyle() == yaml::ScalarStyle::SingleQuoted ||
+ Scalar.scalarStyle() == yaml::ScalarStyle::DoubleQuoted) {
+ ++ContentOffset;
+ }
+ return Result.Source.range(ContentOffset + ScalarRange.Begin.Offset,
+ ContentOffset + ScalarRange.End.Offset);
+ }
+
+ const std::size_t FirstBodyLine = Scalar.range().Begin.Line + 1U;
+ const std::size_t LastBodyLine = Scalar.range().End.Line;
+ if (FirstBodyLine > LastBodyLine || FirstBodyLine == 0)
+ return Scalar.range();
+
+ std::size_t Indent = 0;
+ bool FoundContent = false;
+ for (std::size_t Line = FirstBodyLine; Line <= LastBodyLine; ++Line) {
+ const std::string_view Text = Result.Source.line(Line);
+ std::size_t CurrentIndent = 0;
+ while (CurrentIndent < Text.size() && Text[CurrentIndent] == ' ')
+ ++CurrentIndent;
+ if (CurrentIndent == Text.size())
+ continue;
+ Indent = CurrentIndent;
+ FoundContent = true;
+ break;
+ }
+ if (!FoundContent)
+ return Scalar.range();
+
+ const TextPosition DefaultPosition = Scalar.range().Begin;
+ const auto MapPosition = [this, FirstBodyLine, LastBodyLine, Indent,
+ DefaultPosition](TextPosition Position) {
+ const std::size_t SourceLine = FirstBodyLine + Position.Line - 1U;
+ if (Position.Line == 0 || SourceLine > LastBodyLine || SourceLine > Result.Source.lineCount()) {
+ return DefaultPosition;
+ }
+ const std::string_view SourceLineText = Result.Source.line(SourceLine);
+ const std::size_t BodyColumn = Position.Column == 0 ? 0 : Position.Column - 1U;
+ const std::size_t SourceColumn = std::min(Indent + BodyColumn, SourceLineText.size());
+ const std::size_t Offset = Result.Source.lineOffsets()[SourceLine - 1U] + SourceColumn;
+ return Result.Source.positionAt(Offset);
+ };
+ return {.Begin = MapPosition(ScalarRange.Begin), .End = MapPosition(ScalarRange.End)};
+}
+
+bool MIRDocumentParser::parseBody(const yaml::Node &Root, MachineFunction &Function,
+ MIParserContext &Context) {
+ const yaml::Node *Body = mappingValue(Root, "body");
+ std::string BodyText;
+ TextRange BodyRange = Root.range();
+ if (Body != nullptr) {
+ std::string_view Text;
+ if (scalar(*Body, Text, "machine function body"))
+ return true;
+ BodyText = Text;
+ BodyRange = Body->range();
+ }
+
+ if (!effectiveDataLayout().empty()) {
+ const LLVMConstantSyntaxOptions PointerOptions = pointerLayoutOptions();
+ Context.PointerSizeInBits = [PointerOptions](std::uint32_t AddressSpace) {
+ return llvmPointerSizeInBits(AddressSpace, PointerOptions);
+ };
+ }
+ Context.SyncScopes = ActiveDocument->SyncScopes;
+ Context.InternSyncScope = [this](std::string_view Name) -> Expected<SyncScopeID> {
+ if (Name.empty())
+ return makeUnexpected(ErrorCode::ParseError, "synchronization scope name cannot be empty");
+ const auto Existing =
+ std::ranges::find_if(ActiveDocument->SyncScopes,
+ [Name](const MISyncScopeSlot &Slot) { return Slot.Name == Name; });
+ if (Existing != ActiveDocument->SyncScopes.end())
+ return Existing->ID;
+ if (ActiveDocument->SyncScopes.size() >=
+ static_cast<std::size_t>(std::numeric_limits<std::uint32_t>::max())) {
+ return makeUnexpected(ErrorCode::Unsupported,
+ "MIR document contains too many synchronization scopes");
+ }
+ const auto ID =
+ SyncScopeID::fromValue(static_cast<std::uint32_t>(ActiveDocument->SyncScopes.size() + 1U));
+ ActiveDocument->SyncScopes.push_back({.Name = std::string(Name), .ID = ID});
+ return ID;
+ };
+ MIParserOptions BodyOptions = Options.MachineInstructions;
+ BodyOptions.VerifyAfterParse = false;
+ MIParser Parser(Function, Target,
+ std::string(Result.Source.name()) + ":" + std::string(Function.name()),
+ std::move(BodyText), Context, BodyOptions);
+ auto Parsed = Parser.parse();
+ if (!Parsed) {
+ if (!Parser.diagnostics().empty()) {
+ const TextRange Range =
+ Body == nullptr ? BodyRange
+ : scalarDiagnosticRange(*Body, Parser.diagnostics().front().Range);
+ return fail(Range, Parser.diagnostics().front().Message, "mir-body");
+ }
+ return importError(BodyRange, Parsed.error(), "mir-body");
+ }
+ return false;
+}
+
+bool MIRDocumentParser::convertTargetNode(const yaml::Node &Source, MIRTargetNode &Destination) {
+ switch (Source.kind()) {
+ case yaml::NodeKind::Null:
+ Destination = MIRTargetNode::null();
+ return false;
+ case yaml::NodeKind::Scalar:
+ Destination = MIRTargetNode::scalar(std::string(Source.scalar()));
+ return false;
+ case yaml::NodeKind::Sequence: {
+ std::vector<MIRTargetNode> Values;
+ Values.reserve(Source.sequence().size());
+ for (const yaml::NodeID ElementID : Source.sequence()) {
+ const yaml::Node *Element = node(ElementID);
+ if (Element == nullptr)
+ return fail(Source.range(), "target MIR sequence contains an invalid node");
+ MIRTargetNode Value;
+ if (convertTargetNode(*Element, Value))
+ return true;
+ Values.push_back(std::move(Value));
+ }
+ Destination = MIRTargetNode::sequence(std::move(Values));
+ return false;
+ }
+ case yaml::NodeKind::Mapping: {
+ std::vector<MIRTargetMappingEntry> Entries;
+ Entries.reserve(Source.mapping().size());
+ for (const yaml::MappingEntry &SourceEntry : Source.mapping()) {
+ const yaml::Node *ValueNode = node(SourceEntry.Value);
+ if (ValueNode == nullptr)
+ return fail(SourceEntry.KeyRange, "target MIR mapping contains an invalid node");
+ MIRTargetNode Value;
+ if (convertTargetNode(*ValueNode, Value))
+ return true;
+ Entries.push_back({SourceEntry.Key, std::move(Value)});
+ }
+ Destination = MIRTargetNode::mapping(std::move(Entries));
+ return false;
+ }
+ }
+ return fail(Source.range(), "target MIR node has an unknown kind");
+}
+
+bool MIRDocumentParser::parseTargetState(const yaml::Node &Root, MachineFunction &Function) {
+ const yaml::Node *StateNode = mappingValue(Root, "machineFunctionInfo");
+ if (StateNode == nullptr)
+ return false;
+ MIRTargetNode TargetNode;
+ if (convertTargetNode(*StateNode, TargetNode))
+ return true;
+ auto State = Target.mirText().parseFunctionState(TargetNode);
+ if (!State)
+ return importError(StateNode->range(), State.error(), "mir-target-state");
+ auto SetResult = Function.setTargetState(std::move(*State));
+ if (!SetResult)
+ return importError(StateNode->range(), SetResult.error(), "mir-target-state");
+ return false;
+}
+
+bool MIRDocumentParser::validateDocumentReferences(const MachineFunction &Function) {
+ auto Verification = verifyMIRDocumentReferences(*ActiveDocument, Function, true);
+ if (Verification)
+ return false;
+ return importError(Result.Source.range(0, 0), Verification.error());
+}
+
+} // namespace rcc::mir
+
+namespace rcc::mir {
+
+using parser_detail::parseBool;
+using parser_detail::parsePrefixedID;
+using parser_detail::parseSigned;
+using parser_detail::parseUnsigned;
+using parser_detail::parseUnsigned32;
+using parser_detail::trim;
+
+bool MIRDocumentParser::parseFunctionRecords(const yaml::Node &Root, MachineFunction &Function) {
+ auto ParseLocation = [this, &Function](const yaml::Node &Entry,
+ MachineInstr *&Instruction) -> bool {
+ const yaml::Node *BlockNode = mappingValue(Entry, "bb");
+ const yaml::Node *OffsetNode = mappingValue(Entry, "offset");
+ if (BlockNode == nullptr || OffsetNode == nullptr)
+ return fail(Entry.range(), "instruction location requires 'bb' and 'offset'");
+ std::uint64_t Block = 0;
+ std::uint64_t Offset = 0;
+ if (unsignedValue(*BlockNode, Block, "a 32-bit basic block number") ||
+ unsignedValue(*OffsetNode, Offset, "a 32-bit instruction offset") ||
+ Block > std::numeric_limits<std::uint32_t>::max() ||
+ Offset > std::numeric_limits<std::uint32_t>::max()) {
+ return true;
+ }
+ Instruction = instructionAt(Function, static_cast<std::uint32_t>(Block),
+ static_cast<std::uint32_t>(Offset));
+ if (Instruction == nullptr)
+ return fail(Entry.range(), "instruction location is outside the machine function body");
+ return false;
+ };
+
+ if (const yaml::Node *CallSites = mappingValue(Root, "callSites")) {
+ if (requireKind(*CallSites, yaml::NodeKind::Sequence, "call-site records"))
+ return true;
+ constexpr std::array CallSiteKeys{
+ std::string_view{"bb"},
+ std::string_view{"offset"},
+ std::string_view{"fwdArgRegs"},
+ std::string_view{"calleeTypeIds"},
+ };
+ constexpr std::array ArgumentKeys{
+ std::string_view{"arg"},
+ std::string_view{"reg"},
+ };
+ for (const yaml::NodeID CallSiteID : CallSites->sequence()) {
+ const yaml::Node *CallSiteNode = node(CallSiteID);
+ if (CallSiteNode == nullptr ||
+ requireKind(*CallSiteNode, yaml::NodeKind::Mapping, "call-site record") ||
+ validateKeys(*CallSiteNode, CallSiteKeys, "call-site record")) {
+ return true;
+ }
+ MachineInstr *Call = nullptr;
+ if (ParseLocation(*CallSiteNode, Call))
+ return true;
+ if (Call == nullptr)
+ return fail(CallSiteNode->range(),
+ "instruction location is outside the machine function body");
+ MachineCallSiteInfo Info{
+ .CallInstruction = Call->stableID(),
+ .ForwardedArguments = {},
+ .CalleeTypeIDs = {},
+ };
+ if (const yaml::Node *Arguments = mappingValue(*CallSiteNode, "fwdArgRegs")) {
+ if (requireKind(*Arguments, yaml::NodeKind::Sequence, "forwarded call arguments")) {
+ return true;
+ }
+ for (const yaml::NodeID ArgumentID : Arguments->sequence()) {
+ const yaml::Node *Argument = node(ArgumentID);
+ if (Argument == nullptr ||
+ requireKind(*Argument, yaml::NodeKind::Mapping, "forwarded call argument") ||
+ validateKeys(*Argument, ArgumentKeys, "forwarded call argument")) {
+ return true;
+ }
+ const yaml::Node *NumberNode = mappingValue(*Argument, "arg");
+ const yaml::Node *RegNode = mappingValue(*Argument, "reg");
+ if (NumberNode == nullptr || RegNode == nullptr)
+ return fail(Argument->range(), "forwarded call argument requires 'arg' and 'reg'");
+ std::uint16_t Number = 0;
+ std::uint64_t WideNumber = 0;
+ PhysicalRegister Reg;
+ if (unsignedValue(*NumberNode, WideNumber, "a 16-bit argument number") ||
+ WideNumber > std::numeric_limits<std::uint16_t>::max() ||
+ physicalRegister(*RegNode, Reg)) {
+ return true;
+ }
+ Number = static_cast<std::uint16_t>(WideNumber);
+ Info.ForwardedArguments.push_back({Reg, Number});
+ }
+ }
+ if (const yaml::Node *TypeIDs = mappingValue(*CallSiteNode, "calleeTypeIds")) {
+ if (requireKind(*TypeIDs, yaml::NodeKind::Sequence, "callee type IDs"))
+ return true;
+ for (const yaml::NodeID TypeID : TypeIDs->sequence()) {
+ const yaml::Node *TypeNode = node(TypeID);
+ std::uint64_t Value = 0;
+ if (TypeNode == nullptr ||
+ unsignedValue(*TypeNode, Value, "an unsigned callee type ID")) {
+ return true;
+ }
+ Info.CalleeTypeIDs.push_back(Value);
+ }
+ }
+ auto AddResult = Function.records().addCallSite(std::move(Info));
+ if (!AddResult)
+ return importError(CallSiteNode->range(), AddResult.error());
+ }
+ }
+
+ if (const yaml::Node *CalledGlobals = mappingValue(Root, "calledGlobals")) {
+ if (requireKind(*CalledGlobals, yaml::NodeKind::Sequence, "called-global records"))
+ return true;
+ constexpr std::array Keys{
+ std::string_view{"bb"},
+ std::string_view{"offset"},
+ std::string_view{"callee"},
+ std::string_view{"flags"},
+ };
+ for (const yaml::NodeID CalledID : CalledGlobals->sequence()) {
+ const yaml::Node *CalledNode = node(CalledID);
+ if (CalledNode == nullptr ||
+ requireKind(*CalledNode, yaml::NodeKind::Mapping, "called-global record") ||
+ validateKeys(*CalledNode, Keys, "called-global record")) {
+ return true;
+ }
+ MachineInstr *Call = nullptr;
+ if (ParseLocation(*CalledNode, Call))
+ return true;
+ if (Call == nullptr)
+ return fail(CalledNode->range(),
+ "instruction location is outside the machine function body");
+ const yaml::Node *CalleeNode = mappingValue(*CalledNode, "callee");
+ const yaml::Node *FlagsNode = mappingValue(*CalledNode, "flags");
+ if (CalleeNode == nullptr || FlagsNode == nullptr)
+ return fail(CalledNode->range(), "called-global record requires 'callee' and 'flags'");
+ std::string_view Callee;
+ std::uint64_t WideFlags = 0;
+ if (scalar(*CalleeNode, Callee, "callee name") ||
+ unsignedValue(*FlagsNode, WideFlags, "32-bit called-global flags") ||
+ WideFlags > std::numeric_limits<std::uint32_t>::max()) {
+ return true;
+ }
+ if (ActiveDocument->EmbeddedIR && !ActiveDocument->EmbeddedIR->hasGlobalValue(Callee)) {
+ return fail(CalleeNode->range(), "called global references an undefined embedded global");
+ }
+ auto AddResult =
+ Function.records().addCalledGlobal({.CallInstruction = Call->stableID(),
+ .Callee = std::string(Callee),
+ .Flags = static_cast<std::uint32_t>(WideFlags)});
+ if (!AddResult)
+ return importError(CalledNode->range(), AddResult.error());
+ }
+ }
+
+ if (const yaml::Node *Substitutions = mappingValue(Root, "debugValueSubstitutions")) {
+ if (requireKind(*Substitutions, yaml::NodeKind::Sequence, "debug value substitutions")) {
+ return true;
+ }
+ constexpr std::array Keys{
+ std::string_view{"srcinst"}, std::string_view{"srcop"}, std::string_view{"dstinst"},
+ std::string_view{"dstop"}, std::string_view{"subreg"},
+ };
+ for (const yaml::NodeID SubstitutionID : Substitutions->sequence()) {
+ const yaml::Node *Substitution = node(SubstitutionID);
+ if (Substitution == nullptr ||
+ requireKind(*Substitution, yaml::NodeKind::Mapping, "debug value substitution") ||
+ validateKeys(*Substitution, Keys, "debug value substitution")) {
+ return true;
+ }
+ std::array<std::uint32_t, 5> Values{};
+ for (std::size_t Index = 0; Index < Keys.size(); ++Index) {
+ const yaml::Node *ValueNode = mappingValue(*Substitution, Keys[Index]);
+ if (ValueNode == nullptr)
+ return fail(Substitution->range(), "debug value substitution requires every fixed field");
+ std::uint64_t Wide = 0;
+ if (unsignedValue(*ValueNode, Wide, "a 32-bit debug substitution value") ||
+ Wide > std::numeric_limits<std::uint32_t>::max()) {
+ return true;
+ }
+ Values[Index] = static_cast<std::uint32_t>(Wide);
+ }
+ auto AddResult = Function.records().addDebugValueSubstitution(
+ {.Source = {Values[0], Values[1]},
+ .Destination = {Values[2], Values[3]},
+ .Subregister = SubRegIndex::fromValue(Values[4])});
+ if (!AddResult)
+ return importError(Substitution->range(), AddResult.error());
+ }
+ }
+
+ if (const yaml::Node *PrefetchTargets = mappingValue(Root, "prefetch-targets")) {
+ if (requireKind(*PrefetchTargets, yaml::NodeKind::Sequence, "prefetch targets"))
+ return true;
+ for (const yaml::NodeID TargetID : PrefetchTargets->sequence()) {
+ const yaml::Node *TargetNode = node(TargetID);
+ std::string_view Text;
+ if (TargetNode == nullptr || scalar(*TargetNode, Text, "prefetch target"))
+ return true;
+
+ std::size_t ContentOffset = TargetNode->range().Begin.Offset;
+ if (TargetNode->scalarStyle() == yaml::ScalarStyle::SingleQuoted ||
+ TargetNode->scalarStyle() == yaml::ScalarStyle::DoubleQuoted) {
+ ++ContentOffset;
+ }
+ const auto ErrorRange = [this, ContentOffset](std::size_t Begin, std::size_t End = 1U) {
+ return Result.Source.range(ContentOffset + Begin, ContentOffset + Begin + End);
+ };
+ const auto SkipSpace = [&Text](std::size_t &Cursor) {
+ while (Cursor < Text.size() && (Text[Cursor] == ' ' || Text[Cursor] == '\t'))
+ ++Cursor;
+ };
+ const auto ParseNumber = [&Text, &SkipSpace](std::size_t &Cursor, std::uint32_t &Value) {
+ SkipSpace(Cursor);
+ const std::size_t Begin = Cursor;
+ while (Cursor < Text.size() && Text[Cursor] >= '0' && Text[Cursor] <= '9')
+ ++Cursor;
+ return Begin != Cursor && parseUnsigned32(Text.substr(Begin, Cursor - Begin), Value);
+ };
+
+ std::size_t Cursor = 0;
+ SkipSpace(Cursor);
+ if (!Text.substr(Cursor).starts_with("bb_id") ||
+ (Cursor + std::string_view("bb_id").size() < Text.size() &&
+ Text[Cursor + std::string_view("bb_id").size()] != ' ' &&
+ Text[Cursor + std::string_view("bb_id").size()] != '\t')) {
+ return fail(ErrorRange(Cursor), "expected 'bb_id'");
+ }
+ Cursor += std::string_view("bb_id").size();
+
+ std::array<std::uint32_t, 3> Values{};
+ if (!ParseNumber(Cursor, Values[0]))
+ return fail(ErrorRange(0), "expected unsigned integer");
+
+ SkipSpace(Cursor);
+ if (Cursor < Text.size() && (Text[Cursor] == ',' || Text[Cursor] == '.')) {
+ ++Cursor;
+ if (!ParseNumber(Cursor, Values[1]))
+ return fail(ErrorRange(0), "expected unsigned integer");
+ } else if (Cursor < Text.size() && Text[Cursor] >= '0' && Text[Cursor] <= '9') {
+ if (!ParseNumber(Cursor, Values[1]))
+ return fail(ErrorRange(0), "expected unsigned integer");
+ }
+
+ SkipSpace(Cursor);
+ if (Cursor >= Text.size() || Text[Cursor] != ',')
+ return fail(ErrorRange(Cursor), "expected ','");
+ ++Cursor;
+ if (!ParseNumber(Cursor, Values[2]))
+ return fail(ErrorRange(0), "expected unsigned integer");
+ SkipSpace(Cursor);
+ if (Cursor != Text.size())
+ return fail(ErrorRange(0), "unexpected content after prefetch target");
+
+ auto AddResult = Function.records().addPrefetchTarget(
+ {.Block = {Values[0], Values[1]}, .CallsiteIndex = Values[2]});
+ if (!AddResult)
+ return importError(TargetNode->range(), AddResult.error());
+ }
+ }
+
+ if (const yaml::Node *EntryValues = mappingValue(Root, "entry_values")) {
+ if (requireKind(*EntryValues, yaml::NodeKind::Sequence, "entry values"))
+ return true;
+ constexpr std::array Keys{
+ std::string_view{"entry-value-register"},
+ std::string_view{"debug-info-variable"},
+ std::string_view{"debug-info-expression"},
+ std::string_view{"debug-info-location"},
+ };
+ for (const yaml::NodeID EntryID : EntryValues->sequence()) {
+ const yaml::Node *Entry = node(EntryID);
+ if (Entry == nullptr || requireKind(*Entry, yaml::NodeKind::Mapping, "entry value") ||
+ validateKeys(*Entry, Keys, "entry value")) {
+ return true;
+ }
+ const yaml::Node *RegNode = mappingValue(*Entry, "entry-value-register");
+ const yaml::Node *VariableNode = mappingValue(*Entry, "debug-info-variable");
+ const yaml::Node *ExpressionNode = mappingValue(*Entry, "debug-info-expression");
+ const yaml::Node *LocationNode = mappingValue(*Entry, "debug-info-location");
+ if (RegNode == nullptr || VariableNode == nullptr || ExpressionNode == nullptr ||
+ LocationNode == nullptr) {
+ return fail(Entry->range(), "entry value requires all four fixed fields");
+ }
+ PhysicalRegister Reg;
+ if (physicalRegister(*RegNode, Reg))
+ return true;
+ MachineVariableDebugInfo DebugInfo;
+ if (parseVariableDebugInfo(*Entry, Function, DebugInfo))
+ return true;
+ if (!DebugInfo.Variable || !DebugInfo.Expression || !DebugInfo.Location) {
+ return fail(Entry->range(), "entry value debug metadata cannot be empty",
+ "mir-debug-metadata");
+ }
+ auto AddResult =
+ Function.records().addEntryValue({.Reg = Reg, .DebugInfo = std::move(DebugInfo)});
+ if (!AddResult)
+ return importError(Entry->range(), AddResult.error());
+ }
+ }
+ return false;
+}
+
+} // namespace rcc::mir
+
+namespace rcc::mir {
+
+using parser_detail::parseBool;
+using parser_detail::parsePrefixedID;
+using parser_detail::parseSigned;
+using parser_detail::parseUnsigned;
+using parser_detail::parseUnsigned32;
+using parser_detail::trim;
+
+Expected<MachineMetadataReference>
+MIRDocumentParser::resolveMetadataReference(const MachineFunction &Function,
+ std::uint32_t Slot) const {
+ if (ActiveDocument != nullptr && ActiveDocument->EmbeddedIR &&
+ ActiveDocument->EmbeddedIR->metadata(std::to_string(Slot)) != nullptr) {
+ return MachineMetadataReference::embeddedIR(Slot);
+ }
+ if (Function.metadata().node(Slot) != nullptr)
+ return MachineMetadataReference::machineFunction(Slot);
+ return makeUnexpected(ErrorCode::ParseError,
+ "metadata reference uses undefined slot '!" + std::to_string(Slot) + "'");
+}
+
+bool MIRDocumentParser::parseVariableDebugInfo(const yaml::Node &Entry, MachineFunction &Function,
+ MachineVariableDebugInfo &DebugInfo) {
+ const auto Resolve = [this, &Function](std::uint32_t Slot) {
+ return resolveMetadataReference(Function, Slot);
+ };
+ if (const yaml::Node *VariableNode = mappingValue(Entry, "debug-info-variable")) {
+ std::string_view Text;
+ if (scalar(*VariableNode, Text, "debug variable"))
+ return true;
+ if (!Text.empty()) {
+ MetadataTextParser Parser(std::string(Text), Resolve);
+ auto Reference = Parser.parseReferenceOnly();
+ if (!Reference)
+ return importError(VariableNode->range(), Reference.error(), "mir-debug-metadata");
+ if (Reference->scope() != MachineMetadataScope::EmbeddedIR) {
+ return fail(VariableNode->range(),
+ "debug variable must reference embedded LLVM IR metadata",
+ "mir-debug-metadata");
+ }
+ DebugInfo.Variable = *Reference;
+ }
+ }
+ if (const yaml::Node *ExpressionNode = mappingValue(Entry, "debug-info-expression")) {
+ std::string_view Text;
+ if (scalar(*ExpressionNode, Text, "debug expression"))
+ return true;
+ if (!Text.empty()) {
+ MetadataTextParser Parser(std::string(Text), Resolve);
+ auto Value = Parser.parseValue();
+ if (!Value)
+ return importError(scalarDiagnosticRange(*ExpressionNode, Parser.diagnosticRange()),
+ Value.error(), "mir-debug-metadata");
+ if (Value->reference() != nullptr &&
+ Value->reference()->scope() != MachineMetadataScope::EmbeddedIR) {
+ return fail(ExpressionNode->range(),
+ "debug expression reference must use embedded LLVM IR metadata",
+ "mir-debug-metadata");
+ }
+ DebugInfo.Expression = std::move(*Value);
+ }
+ }
+ if (const yaml::Node *LocationNode = mappingValue(Entry, "debug-info-location")) {
+ std::string_view Text;
+ if (scalar(*LocationNode, Text, "debug location"))
+ return true;
+ if (!Text.empty()) {
+ MetadataTextParser Parser(std::string(Text), Resolve);
+ auto Location = Parser.parseLocation();
+ if (!Location)
+ return importError(LocationNode->range(), Location.error(), "mir-debug-metadata");
+ if (Location->reference() != nullptr &&
+ Location->reference()->scope() != MachineMetadataScope::EmbeddedIR) {
+ return fail(LocationNode->range(),
+ "variable debug location must reference embedded LLVM IR metadata",
+ "mir-debug-metadata");
+ }
+ DebugInfo.Location = std::move(*Location);
+ }
+ }
+ if (!DebugInfo.Variable && !DebugInfo.Expression && !DebugInfo.Location)
+ return false;
+ auto Verification = DebugInfo.verifyStructure();
+ if (!Verification)
+ return importError(Entry.range(), Verification.error(), "mir-debug-metadata");
+ return false;
+}
+
+bool MIRDocumentParser::parseMetadata(const yaml::Node &Root, MachineFunction &Function,
+ MIParserContext &Context) {
+ if (ActiveDocument->EmbeddedIR) {
+ for (const EmbeddedIRMetadataDefinition &Reference :
+ ActiveDocument->EmbeddedIR->metadataDefinitions()) {
+ std::uint32_t ID = 0;
+ if (!parseUnsigned32(Reference.Name, ID))
+ continue;
+ if (std::ranges::any_of(Context.Metadata,
+ [ID](const MIMetadataSlot &Slot) { return Slot.TextID == ID; })) {
+ return fail(Root.range(), "embedded LLVM IR metadata slots are not unique");
+ }
+ Context.Metadata.push_back(
+ {.TextID = ID, .Reference = MachineMetadataReference::embeddedIR(ID)});
+ }
+ }
+
+ const yaml::Node *Metadata = mappingValue(Root, "machineMetadataNodes");
+ if (Metadata == nullptr)
+ return false;
+ if (requireKind(*Metadata, yaml::NodeKind::Sequence, "machine metadata nodes"))
+ return true;
+
+ enum class ParsedElementKind : std::uint8_t {
+ Null,
+ Reference,
+ String,
+ };
+ struct ParsedElement final {
+ ParsedElementKind Kind = ParsedElementKind::Reference;
+ std::uint32_t Reference = 0;
+ std::string String;
+ TextRange Range;
+ };
+ struct Definition final {
+ const yaml::Node *Node = nullptr;
+ std::uint32_t Slot = 0;
+ bool IsDistinct = false;
+ std::vector<ParsedElement> Elements;
+ };
+ std::vector<Definition> Definitions;
+ std::unordered_set<std::uint32_t> Slots;
+
+ const auto TranslateRange = [this](const yaml::Node &Node, const TextRange &Range) {
+ std::size_t ContentBegin = Node.range().Begin.Offset;
+ if (Node.scalarStyle() == yaml::ScalarStyle::SingleQuoted ||
+ Node.scalarStyle() == yaml::ScalarStyle::DoubleQuoted) {
+ ++ContentBegin;
+ }
+ return Result.Source.range(ContentBegin + Range.Begin.Offset, ContentBegin + Range.End.Offset);
+ };
+
+ for (const yaml::NodeID DefinitionID : Metadata->sequence()) {
+ const yaml::Node *DefinitionNode = node(DefinitionID);
+ std::string_view Text;
+ if (DefinitionNode == nullptr || scalar(*DefinitionNode, Text, "machine metadata definition"))
+ return true;
+
+ MILexer Lexer("machine-metadata", std::string(Text));
+ MIToken Token = Lexer.next();
+ const auto LexerFailed = [this, &Lexer, DefinitionNode, &TranslateRange, &Token]() -> bool {
+ if (!Token.is(MITokenKind::Error))
+ return false;
+ const auto Diagnostics = Lexer.diagnostics();
+ const TextRange Range = Diagnostics.empty()
+ ? TranslateRange(*DefinitionNode, Token.range())
+ : TranslateRange(*DefinitionNode, Diagnostics.front().Range);
+ return fail(Range, Diagnostics.empty() ? "invalid machine metadata node"
+ : Diagnostics.front().Message);
+ };
+ const auto Advance = [&Lexer, &Token, &LexerFailed]() -> bool {
+ Token = Lexer.next();
+ return LexerFailed();
+ };
+ const auto FailAtToken = [this, DefinitionNode, &TranslateRange](const MIToken &At,
+ std::string Message) {
+ return fail(TranslateRange(*DefinitionNode, At.range()), std::move(Message));
+ };
+
+ if (LexerFailed())
+ return true;
+ if (!Token.is(MITokenKind::Exclaim))
+ return FailAtToken(Token, "expected a metadata node");
+ if (Advance())
+ return true;
+ std::uint32_t Slot = 0;
+ if (!Token.is(MITokenKind::IntegerLiteral) || !parseUnsigned32(Token.integerValue(), Slot)) {
+ return FailAtToken(Token, "expected metadata id after '!'");
+ }
+ if (Advance())
+ return true;
+ if (!Token.is(MITokenKind::Equal))
+ return FailAtToken(Token, "expected '='");
+ if (Advance())
+ return true;
+
+ const bool IsDistinct = Token.isKeyword("distinct");
+ if (IsDistinct && Advance())
+ return true;
+ if (!Token.is(MITokenKind::Exclaim))
+ return FailAtToken(Token, "expected a metadata node");
+ if (Advance())
+ return true;
+ if (!Token.is(MITokenKind::LeftBrace))
+ return FailAtToken(Token, "expected '{' here");
+ if (Advance())
+ return true;
+
+ std::vector<ParsedElement> Elements;
+ if (!Token.is(MITokenKind::RightBrace)) {
+ while (true) {
+ if (Token.hasStringValue() && Token.stringValue() == "null") {
+ Elements.push_back({.Kind = ParsedElementKind::Null,
+ .Reference = 0,
+ .String = {},
+ .Range = TranslateRange(*DefinitionNode, Token.range())});
+ } else {
+ if (!Token.is(MITokenKind::Exclaim))
+ return FailAtToken(Token, "expected '!' here");
+ const TextRange ReferenceRange = TranslateRange(*DefinitionNode, Token.range());
+ if (Advance())
+ return true;
+
+ if (Token.is(MITokenKind::StringConstant)) {
+ Elements.push_back({.Kind = ParsedElementKind::String,
+ .Reference = 0,
+ .String = std::string(Token.stringValue()),
+ .Range = TranslateRange(*DefinitionNode, Token.range())});
+ } else {
+ std::uint32_t Reference = 0;
+ if (!Token.is(MITokenKind::IntegerLiteral) ||
+ !parseUnsigned32(Token.integerValue(), Reference)) {
+ return FailAtToken(Token, "expected metadata id after '!'");
+ }
+ Elements.push_back({.Kind = ParsedElementKind::Reference,
+ .Reference = Reference,
+ .String = {},
+ .Range = ReferenceRange});
+ }
+ }
+ if (Advance())
+ return true;
+ if (!Token.is(MITokenKind::Comma))
+ break;
+ if (Advance())
+ return true;
+ }
+ }
+ if (!Token.is(MITokenKind::RightBrace))
+ return FailAtToken(Token, "expected end of metadata node");
+ if (Advance())
+ return true;
+ if (!Token.is(MITokenKind::EndOfFile))
+ return FailAtToken(Token, "expected end of metadata node");
+ if (!Slots.insert(Slot).second)
+ return FailAtToken(Token, "Metadata id is already used");
+
+ Definitions.push_back({.Node = DefinitionNode,
+ .Slot = Slot,
+ .IsDistinct = IsDistinct,
+ .Elements = std::move(Elements)});
+ Context.Metadata.push_back(
+ {.TextID = Slot, .Reference = MachineMetadataReference::machineFunction(Slot)});
+ }
+
+ for (const Definition &Definition : Definitions) {
+ MachineMetadataNode NewNode{
+ .Slot = Definition.Slot,
+ .IsDistinct = Definition.IsDistinct,
+ .Elements = {},
+ };
+ for (const ParsedElement &Element : Definition.Elements) {
+ if (Element.Kind == ParsedElementKind::Null) {
+ NewNode.Elements.push_back(MachineMetadataElement::null());
+ continue;
+ }
+ if (Element.Kind == ParsedElementKind::String) {
+ NewNode.Elements.push_back(MachineMetadataElement::string(Element.String));
+ continue;
+ }
+
+ MachineMetadataReference Reference;
+ if (ActiveDocument->EmbeddedIR != std::nullopt &&
+ ActiveDocument->EmbeddedIR->metadata(std::to_string(Element.Reference)) != nullptr) {
+ Reference = MachineMetadataReference::embeddedIR(Element.Reference);
+ } else if (Slots.contains(Element.Reference)) {
+ Reference = MachineMetadataReference::machineFunction(Element.Reference);
+ } else {
+ return fail(Element.Range,
+ "use of undefined metadata '!" + std::to_string(Element.Reference) + "'");
+ }
+ auto Resolved = MachineMetadataElement::reference(Reference);
+ if (!Resolved)
+ return importError(Element.Range, Resolved.error());
+ NewNode.Elements.push_back(std::move(*Resolved));
+ }
+ auto AddResult = Function.metadata().addNode(std::move(NewNode));
+ if (!AddResult)
+ return importError(Definition.Node->range(), AddResult.error());
+ }
+ return false;
+}
+
+} // namespace rcc::mir
diff --git a/backend/lib/MIR/CMakeLists.txt b/backend/lib/MIR/CMakeLists.txt
index f227390..cf5dbcd 100644
--- a/backend/lib/MIR/CMakeLists.txt
+++ b/backend/lib/MIR/CMakeLists.txt
@@ -4,30 +4,20 @@ set(
RCCBackendMIRSources
EmbeddedIR.cpp
LLVMConstantSyntax.cpp
- MIDebugSyntax.cpp
- MILexer.cpp
- MIRDataParser.cpp
- MIRDocument.cpp
- MIRDocumentParser.cpp
- MIRDocumentReferenceValidation.cpp
- MIRDocumentPrinter.cpp
- MIRFrameParser.cpp
- MIRFunctionParser.cpp
- MIRFunctionRecordsParser.cpp
- MIRMetadataParser.cpp
- MIRMetadataTextParser.cpp
- MIParser.cpp
- MIPrinter.cpp
+ "${RCC_BACKEND_SOURCE_DIR}/lib/CodeGen/MIRParser/MILexer.cpp"
+ "${RCC_BACKEND_SOURCE_DIR}/lib/CodeGen/MIRParser/MIParser.cpp"
+ "${RCC_BACKEND_SOURCE_DIR}/lib/CodeGen/MIRParser/MIRParser.cpp"
+ "${RCC_BACKEND_SOURCE_DIR}/lib/CodeGen/MIRPrinter.cpp"
+ "${RCC_BACKEND_SOURCE_DIR}/lib/CodeGen/MIRYamlMapping.cpp"
)
set(
RCCBackendMIRHeaders
+ "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/MIRParser/MIParser.h"
+ "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/MIRParser/MIRParser.h"
+ "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/MIRPrinter.h"
+ "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/MIRYamlMapping.h"
"${RCC_BACKEND_SOURCE_DIR}/include/rcc/MIR/EmbeddedIR.h"
"${RCC_BACKEND_SOURCE_DIR}/include/rcc/MIR/LLVMConstantSyntax.h"
- "${RCC_BACKEND_SOURCE_DIR}/include/rcc/MIR/MIDebugSyntax.h"
- "${RCC_BACKEND_SOURCE_DIR}/include/rcc/MIR/MILexer.h"
- "${RCC_BACKEND_SOURCE_DIR}/include/rcc/MIR/MIRDocument.h"
- "${RCC_BACKEND_SOURCE_DIR}/include/rcc/MIR/MIParser.h"
- "${RCC_BACKEND_SOURCE_DIR}/include/rcc/MIR/MIPrinter.h"
)
add_library(RCCBackendMIR ${RCCBackendMIRSources})
@@ -61,41 +51,20 @@ install(
rcc_backend_register_format_files(
"${CMAKE_CURRENT_SOURCE_DIR}/EmbeddedIR.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/LLVMConstantSyntax.cpp"
- "${CMAKE_CURRENT_SOURCE_DIR}/MIDebugSyntax.cpp"
- "${CMAKE_CURRENT_SOURCE_DIR}/MILexer.cpp"
- "${CMAKE_CURRENT_SOURCE_DIR}/MIRDataParser.cpp"
- "${CMAKE_CURRENT_SOURCE_DIR}/MIRDocument.cpp"
- "${CMAKE_CURRENT_SOURCE_DIR}/MIRDocumentParser.cpp"
- "${CMAKE_CURRENT_SOURCE_DIR}/MIRDocumentParser.h"
- "${CMAKE_CURRENT_SOURCE_DIR}/MIRDocumentReferenceValidation.h"
- "${CMAKE_CURRENT_SOURCE_DIR}/MIRDocumentReferenceValidation.cpp"
- "${CMAKE_CURRENT_SOURCE_DIR}/MIRDocumentPrinter.cpp"
- "${CMAKE_CURRENT_SOURCE_DIR}/MIRFrameParser.cpp"
- "${CMAKE_CURRENT_SOURCE_DIR}/MIRFunctionParser.cpp"
- "${CMAKE_CURRENT_SOURCE_DIR}/MIRFunctionRecordsParser.cpp"
- "${CMAKE_CURRENT_SOURCE_DIR}/MIRMetadataParser.cpp"
- "${CMAKE_CURRENT_SOURCE_DIR}/MIRMetadataTextParser.h"
- "${CMAKE_CURRENT_SOURCE_DIR}/MIRMetadataTextParser.cpp"
- "${CMAKE_CURRENT_SOURCE_DIR}/MIRParserUtilities.h"
- "${CMAKE_CURRENT_SOURCE_DIR}/MIParser.cpp"
- "${CMAKE_CURRENT_SOURCE_DIR}/MIPrinter.cpp"
+ "${RCC_BACKEND_SOURCE_DIR}/lib/CodeGen/MIRParser/MILexer.cpp"
+ "${RCC_BACKEND_SOURCE_DIR}/lib/CodeGen/MIRParser/MILexer.h"
+ "${RCC_BACKEND_SOURCE_DIR}/lib/CodeGen/MIRParser/MIParser.cpp"
+ "${RCC_BACKEND_SOURCE_DIR}/lib/CodeGen/MIRParser/MIRParser.cpp"
+ "${RCC_BACKEND_SOURCE_DIR}/lib/CodeGen/MIRPrinter.cpp"
+ "${RCC_BACKEND_SOURCE_DIR}/lib/CodeGen/MIRYamlMapping.cpp"
${RCCBackendMIRHeaders}
)
rcc_backend_register_tidy_files(
"${CMAKE_CURRENT_SOURCE_DIR}/EmbeddedIR.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/LLVMConstantSyntax.cpp"
- "${CMAKE_CURRENT_SOURCE_DIR}/MIDebugSyntax.cpp"
- "${CMAKE_CURRENT_SOURCE_DIR}/MILexer.cpp"
- "${CMAKE_CURRENT_SOURCE_DIR}/MIRDataParser.cpp"
- "${CMAKE_CURRENT_SOURCE_DIR}/MIRDocument.cpp"
- "${CMAKE_CURRENT_SOURCE_DIR}/MIRDocumentParser.cpp"
- "${CMAKE_CURRENT_SOURCE_DIR}/MIRDocumentReferenceValidation.cpp"
- "${CMAKE_CURRENT_SOURCE_DIR}/MIRDocumentPrinter.cpp"
- "${CMAKE_CURRENT_SOURCE_DIR}/MIRFrameParser.cpp"
- "${CMAKE_CURRENT_SOURCE_DIR}/MIRFunctionParser.cpp"
- "${CMAKE_CURRENT_SOURCE_DIR}/MIRFunctionRecordsParser.cpp"
- "${CMAKE_CURRENT_SOURCE_DIR}/MIRMetadataParser.cpp"
- "${CMAKE_CURRENT_SOURCE_DIR}/MIRMetadataTextParser.cpp"
- "${CMAKE_CURRENT_SOURCE_DIR}/MIParser.cpp"
- "${CMAKE_CURRENT_SOURCE_DIR}/MIPrinter.cpp"
+ "${RCC_BACKEND_SOURCE_DIR}/lib/CodeGen/MIRParser/MILexer.cpp"
+ "${RCC_BACKEND_SOURCE_DIR}/lib/CodeGen/MIRParser/MIParser.cpp"
+ "${RCC_BACKEND_SOURCE_DIR}/lib/CodeGen/MIRParser/MIRParser.cpp"
+ "${RCC_BACKEND_SOURCE_DIR}/lib/CodeGen/MIRPrinter.cpp"
+ "${RCC_BACKEND_SOURCE_DIR}/lib/CodeGen/MIRYamlMapping.cpp"
)
diff --git a/backend/lib/MIR/MIDebugSyntax.cpp b/backend/lib/MIR/MIDebugSyntax.cpp
deleted file mode 100644
index e614a21..0000000
--- a/backend/lib/MIR/MIDebugSyntax.cpp
+++ /dev/null
@@ -1,264 +0,0 @@
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#include "rcc/MIR/MIDebugSyntax.h"
-
-#include <array>
-#include <charconv>
-#include <cstdint>
-#include <optional>
-#include <ranges>
-#include <span>
-#include <string>
-#include <string_view>
-#include <system_error>
-#include <utility>
-
-namespace rcc::mir {
-namespace {
-
-[[nodiscard]] bool parseIndex(std::string_view Text, std::uint64_t &Value) noexcept {
- if (Text.empty())
- return false;
- const auto [End, Error] = std::from_chars(Text.data(), Text.data() + Text.size(), Value, 10);
- return Error == std::errc{} && End == Text.data() + Text.size();
-}
-
-[[nodiscard]] const auto &namedOpcodes() {
- static constexpr auto Opcodes = std::to_array<std::pair<std::string_view, std::uint64_t>>({
- {"DW_OP_addr", 0x03},
- {"DW_OP_deref", 0x06},
- {"DW_OP_const1u", 0x08},
- {"DW_OP_const1s", 0x09},
- {"DW_OP_const2u", 0x0A},
- {"DW_OP_const2s", 0x0B},
- {"DW_OP_const4u", 0x0C},
- {"DW_OP_const4s", 0x0D},
- {"DW_OP_const8u", 0x0E},
- {"DW_OP_const8s", 0x0F},
- {"DW_OP_constu", 0x10},
- {"DW_OP_consts", 0x11},
- {"DW_OP_dup", 0x12},
- {"DW_OP_drop", 0x13},
- {"DW_OP_over", 0x14},
- {"DW_OP_pick", 0x15},
- {"DW_OP_swap", 0x16},
- {"DW_OP_rot", 0x17},
- {"DW_OP_xderef", 0x18},
- {"DW_OP_abs", 0x19},
- {"DW_OP_and", 0x1A},
- {"DW_OP_div", 0x1B},
- {"DW_OP_minus", 0x1C},
- {"DW_OP_mod", 0x1D},
- {"DW_OP_mul", 0x1E},
- {"DW_OP_neg", 0x1F},
- {"DW_OP_not", 0x20},
- {"DW_OP_or", 0x21},
- {"DW_OP_plus", 0x22},
- {"DW_OP_plus_uconst", 0x23},
- {"DW_OP_shl", 0x24},
- {"DW_OP_shr", 0x25},
- {"DW_OP_shra", 0x26},
- {"DW_OP_xor", 0x27},
- {"DW_OP_bra", 0x28},
- {"DW_OP_eq", 0x29},
- {"DW_OP_ge", 0x2A},
- {"DW_OP_gt", 0x2B},
- {"DW_OP_le", 0x2C},
- {"DW_OP_lt", 0x2D},
- {"DW_OP_ne", 0x2E},
- {"DW_OP_skip", 0x2F},
- {"DW_OP_regx", 0x90},
- {"DW_OP_fbreg", 0x91},
- {"DW_OP_bregx", 0x92},
- {"DW_OP_piece", 0x93},
- {"DW_OP_deref_size", 0x94},
- {"DW_OP_xderef_size", 0x95},
- {"DW_OP_nop", 0x96},
- {"DW_OP_push_object_address", 0x97},
- {"DW_OP_call2", 0x98},
- {"DW_OP_call4", 0x99},
- {"DW_OP_call_ref", 0x9A},
- {"DW_OP_form_tls_address", 0x9B},
- {"DW_OP_call_frame_cfa", 0x9C},
- {"DW_OP_bit_piece", 0x9D},
- {"DW_OP_implicit_value", 0x9E},
- {"DW_OP_stack_value", 0x9F},
- {"DW_OP_implicit_pointer", 0xA0},
- {"DW_OP_addrx", 0xA1},
- {"DW_OP_constx", 0xA2},
- {"DW_OP_entry_value", 0xA3},
- {"DW_OP_const_type", 0xA4},
- {"DW_OP_regval_type", 0xA5},
- {"DW_OP_deref_type", 0xA6},
- {"DW_OP_xderef_type", 0xA7},
- {"DW_OP_convert", 0xA8},
- {"DW_OP_reinterpret", 0xA9},
- {"DW_OP_LLVM_fragment", 0x1000},
- {"DW_OP_LLVM_convert", 0x1001},
- {"DW_OP_LLVM_tag_offset", 0x1002},
- {"DW_OP_LLVM_entry_value", 0x1003},
- {"DW_OP_LLVM_implicit_pointer", 0x1004},
- {"DW_OP_LLVM_arg", 0x1005},
- {"DW_OP_LLVM_extract_bits_sext", 0x1006},
- {"DW_OP_LLVM_extract_bits_zext", 0x1007},
- });
- return Opcodes;
-}
-
-[[nodiscard]] const auto &attributeEncodings() {
- static constexpr auto Encodings = std::to_array<std::pair<std::string_view, std::uint64_t>>({
- {"DW_ATE_address", 0x01},
- {"DW_ATE_boolean", 0x02},
- {"DW_ATE_complex_float", 0x03},
- {"DW_ATE_float", 0x04},
- {"DW_ATE_signed", 0x05},
- {"DW_ATE_signed_char", 0x06},
- {"DW_ATE_unsigned", 0x07},
- {"DW_ATE_unsigned_char", 0x08},
- {"DW_ATE_imaginary_float", 0x09},
- {"DW_ATE_packed_decimal", 0x0A},
- {"DW_ATE_numeric_string", 0x0B},
- {"DW_ATE_edited", 0x0C},
- {"DW_ATE_signed_fixed", 0x0D},
- {"DW_ATE_unsigned_fixed", 0x0E},
- {"DW_ATE_decimal_float", 0x0F},
- {"DW_ATE_UTF", 0x10},
- {"DW_ATE_UCS", 0x11},
- {"DW_ATE_ASCII", 0x12},
- });
- return Encodings;
-}
-
-[[nodiscard]] std::optional<std::string> opcodeName(std::uint64_t Opcode) {
- if (Opcode >= 0x30U && Opcode <= 0x4FU)
- return "DW_OP_lit" + std::to_string(Opcode - 0x30U);
- if (Opcode >= 0x50U && Opcode <= 0x6FU)
- return "DW_OP_reg" + std::to_string(Opcode - 0x50U);
- if (Opcode >= 0x70U && Opcode <= 0x8FU)
- return "DW_OP_breg" + std::to_string(Opcode - 0x70U);
- const auto &Opcodes = namedOpcodes();
- const auto Found =
- std::ranges::find_if(Opcodes, [Opcode](const auto &Entry) { return Entry.second == Opcode; });
- if (Found == Opcodes.end())
- return std::nullopt;
- return std::string(Found->first);
-}
-
-[[nodiscard]] std::size_t opcodeSize(std::uint64_t Opcode) noexcept {
- if (Opcode >= 0x70U && Opcode <= 0x8FU)
- return 2;
- switch (Opcode) {
- case 0x92:
- case 0x1000:
- case 0x1001:
- case 0x1006:
- case 0x1007:
- return 3;
- case 0x10:
- case 0x11:
- case 0x23:
- case 0x90:
- case 0x94:
- case 0x1002:
- case 0x1003:
- case 0x1005:
- return 2;
- default:
- return 1;
- }
-}
-
-[[nodiscard]] std::optional<std::string_view> attributeName(std::uint64_t Encoding) noexcept {
- const auto &Encodings = attributeEncodings();
- const auto Found = std::ranges::find_if(
- Encodings, [Encoding](const auto &Entry) { return Entry.second == Encoding; });
- if (Found == Encodings.end())
- return std::nullopt;
- return Found->first;
-}
-
-} // namespace
-
-std::optional<std::uint64_t> parseDIExpressionOpcodeName(std::string_view Name) noexcept {
- const auto NumberedOpcode = [Name](std::string_view Prefix,
- std::uint64_t Base) -> std::optional<std::uint64_t> {
- if (!Name.starts_with(Prefix))
- return std::nullopt;
- std::uint64_t Index = 0;
- if (!parseIndex(Name.substr(Prefix.size()), Index) || Index > 31)
- return std::nullopt;
- return Base + Index;
- };
- if (auto Opcode = NumberedOpcode("DW_OP_lit", 0x30U))
- return Opcode;
- if (auto Opcode = NumberedOpcode("DW_OP_reg", 0x50U))
- return Opcode;
- if (auto Opcode = NumberedOpcode("DW_OP_breg", 0x70U))
- return Opcode;
-
- const auto &NamedOpcodes = namedOpcodes();
- const auto *const Found =
- std::ranges::find_if(NamedOpcodes, [Name](const auto &Entry) { return Entry.first == Name; });
- if (Found == NamedOpcodes.end())
- return std::nullopt;
- return Found->second;
-}
-
-std::optional<std::uint64_t>
-parseDIExpressionAttributeEncodingName(std::string_view Name) noexcept {
- const auto &Encodings = attributeEncodings();
- const auto Found =
- std::ranges::find_if(Encodings, [Name](const auto &Entry) { return Entry.first == Name; });
- if (Found == Encodings.end())
- return std::nullopt;
- return Found->second;
-}
-
-std::string invalidDIExpressionElementMessage(std::string_view Name) {
- if (Name.starts_with("DW_OP_"))
- return "invalid DWARF op '" + std::string(Name) + "'";
- if (Name.starts_with("DW_ATE_"))
- return "invalid DWARF attribute encoding '" + std::string(Name) + "'";
- return "expected unsigned integer";
-}
-
-std::string formatDIExpression(std::span<const std::uint64_t> Elements) {
- std::string Result = "!DIExpression(";
- std::size_t Position = 0;
- bool First = true;
- while (Position < Elements.size()) {
- const std::uint64_t Opcode = Elements[Position];
- const auto Name = opcodeName(Opcode);
- const std::size_t Size = opcodeSize(Opcode);
- if (!Name || Position + Size > Elements.size()) {
- Result = "!DIExpression(";
- for (std::size_t Index = 0; Index < Elements.size(); ++Index) {
- if (Index != 0)
- Result.append(", ");
- Result.append(std::to_string(Elements[Index]));
- }
- Result.push_back(')');
- return Result;
- }
-
- if (!First)
- Result.append(", ");
- First = false;
- Result.append(*Name);
- for (std::size_t Argument = 1; Argument < Size; ++Argument) {
- Result.append(", ");
- if (Opcode == 0x1001U && Argument == 2U) {
- if (const auto Encoding = attributeName(Elements[Position + Argument])) {
- Result.append(*Encoding);
- continue;
- }
- }
- Result.append(std::to_string(Elements[Position + Argument]));
- }
- Position += Size;
- }
- Result.push_back(')');
- return Result;
-}
-
-} // namespace rcc::mir
diff --git a/backend/lib/MIR/MIRDataParser.cpp b/backend/lib/MIR/MIRDataParser.cpp
deleted file mode 100644
index 2ac1f2d..0000000
--- a/backend/lib/MIR/MIRDataParser.cpp
+++ /dev/null
@@ -1,287 +0,0 @@
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#include "MIRDocumentParser.h"
-#include "MIRDocumentReferenceValidation.h"
-#include "MIRMetadataTextParser.h"
-#include "MIRParserUtilities.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 {
-
-using parser_detail::parseBool;
-using parser_detail::parsePrefixedID;
-using parser_detail::parseSigned;
-using parser_detail::parseUnsigned;
-using parser_detail::parseUnsigned32;
-using parser_detail::trim;
-
-bool MIRDocumentParser::parseConstants(const yaml::Node &Root, MachineFunction &Function,
- MIParserContext &Context) {
- const yaml::Node *Constants = mappingValue(Root, "constants");
- if (Constants == nullptr)
- return false;
- if (requireKind(*Constants, yaml::NodeKind::Sequence, "constant pool"))
- return true;
- constexpr std::array Keys{
- std::string_view{"id"},
- std::string_view{"value"},
- std::string_view{"alignment"},
- std::string_view{"isTargetSpecific"},
- };
- std::vector<LLVMNamedTypeDefinition> NamedTypes;
- const std::string_view DataLayout = effectiveDataLayout();
- if (ActiveDocument->EmbeddedIR) {
- const auto Definitions = ActiveDocument->EmbeddedIR->typeDefinitions();
- NamedTypes.reserve(Definitions.size());
- for (const EmbeddedIRTypeDefinition &Definition : Definitions) {
- NamedTypes.push_back({.Name = Definition.Name,
- .Definition = Definition.Definition,
- .NumericName = Definition.NumericName});
- }
- }
- std::unordered_set<std::uint32_t> IDs;
- for (const yaml::NodeID ConstantID : Constants->sequence()) {
- const yaml::Node *Constant = node(ConstantID);
- if (Constant == nullptr ||
- requireKind(*Constant, yaml::NodeKind::Mapping, "constant pool entry") ||
- validateKeys(*Constant, Keys, "constant pool entry")) {
- return true;
- }
- const yaml::Node *IDNode = mappingValue(*Constant, "id");
- const yaml::Node *ValueNode = mappingValue(*Constant, "value");
- if (IDNode == nullptr || ValueNode == nullptr)
- return fail(Constant->range(), "constant pool entry requires 'id' and 'value'");
- std::uint64_t WideID = 0;
- if (unsignedValue(*IDNode, WideID, "a 32-bit constant pool ID") ||
- WideID > std::numeric_limits<std::uint32_t>::max()) {
- return true;
- }
- const auto TextID = static_cast<std::uint32_t>(WideID);
- if (!IDs.insert(TextID).second)
- return fail(IDNode->range(), "duplicate constant pool ID");
-
- bool IsTargetSpecific = false;
- if (const yaml::Node *TargetNode = mappingValue(*Constant, "isTargetSpecific")) {
- if (boolean(*TargetNode, IsTargetSpecific))
- return true;
- }
- if (IsTargetSpecific) {
- std::string_view ValueText;
- if (scalar(*ValueNode, ValueText, "target constant"))
- return true;
- auto TargetConstantValue = Target.mirText().parseTargetConstant(ValueText);
- if (!TargetConstantValue)
- return importError(ValueNode->range(), TargetConstantValue.error(), "mir-target-constant");
- auto Emission = TargetConstantValue->emit();
- if (!Emission)
- return importError(ValueNode->range(), Emission.error(), "mir-target-constant");
- auto RequiredAlignment = TargetConstantValue->requiredAlignment();
- if (!RequiredAlignment)
- return importError(ValueNode->range(), RequiredAlignment.error(), "mir-target-constant");
-
- MachineConstantPoolEntryDesc Description;
- Description.Size = Emission->Bytes.size();
- Description.Align = *RequiredAlignment;
- Description.Value = std::move(*TargetConstantValue);
- if (const yaml::Node *AlignmentNode = mappingValue(*Constant, "alignment")) {
- if (alignment(*AlignmentNode, Description.Align, true))
- return true;
- }
- auto Added = Function.constantPool().addEntry(std::move(Description));
- if (!Added)
- return importError(Constant->range(), Added.error());
- Context.ConstantPoolItems.push_back({.TextID = TextID, .Index = *Added});
- continue;
- }
-
- std::string_view ValueText;
- if (scalar(*ValueNode, ValueText, "LLVM constant"))
- return true;
- const LLVMConstantSyntaxOptions ConstantOptions{
- .DataLayout = DataLayout,
- .NamedTypes = NamedTypes,
- };
- auto Data = parseLLVMConstant(trim(ValueText), ConstantOptions);
- if (!Data)
- return importError(ValueNode->range(), Data.error(), "mir-llvm-constant");
- auto Size = llvmConstantStorageSize(Data->Type, ConstantOptions);
- if (!Size)
- return importError(ValueNode->range(), Size.error(), "mir-llvm-constant");
-
- MachineConstantPoolEntryDesc Description;
- Description.Size = *Size;
- Description.Value = std::move(*Data);
- if (const yaml::Node *AlignmentNode = mappingValue(*Constant, "alignment")) {
- if (alignment(*AlignmentNode, Description.Align, true))
- return true;
- } else {
- auto Preferred = llvmConstantPreferredAlignment(
- std::get<GenericConstantData>(Description.Value).Type, ConstantOptions);
- if (!Preferred)
- return importError(ValueNode->range(), Preferred.error(), "mir-llvm-constant");
- Description.Align = *Preferred;
- }
- auto Added = Function.constantPool().addEntry(std::move(Description));
- if (!Added)
- return importError(Constant->range(), Added.error());
- Context.ConstantPoolItems.push_back({.TextID = TextID, .Index = *Added});
- }
- return false;
-}
-
-bool MIRDocumentParser::reserveJumpTables(const yaml::Node &Root, MachineFunction &Function,
- MIParserContext &Context,
- std::vector<PendingJumpTable> &Pending) {
- const yaml::Node *JumpTable = mappingValue(Root, "jumpTable");
- if (JumpTable == nullptr)
- return false;
- if (requireKind(*JumpTable, yaml::NodeKind::Mapping, "jump table"))
- return true;
- constexpr std::array JumpTableKeys{
- std::string_view{"kind"},
- std::string_view{"entries"},
- };
- if (validateKeys(*JumpTable, JumpTableKeys, "jump table"))
- return true;
- const yaml::Node *KindNode = mappingValue(*JumpTable, "kind");
- if (KindNode == nullptr)
- return fail(JumpTable->range(), "jump table requires 'kind'");
- std::string_view KindName;
- if (scalar(*KindNode, KindName, "jump table kind"))
- return true;
-
- JumpTableEntryEncoding Encoding;
- std::uint8_t EntrySize = 8;
- if (KindName == "block-address")
- Encoding = JumpTableEntryEncoding::BlockAddress;
- else if (KindName == "gp-rel64-block-address")
- Encoding = JumpTableEntryEncoding::GPRelative64BlockAddress;
- else if (KindName == "gp-rel32-block-address") {
- Encoding = JumpTableEntryEncoding::GPRelative32BlockAddress;
- EntrySize = 4;
- } else if (KindName == "label-difference32") {
- Encoding = JumpTableEntryEncoding::LabelDifference32;
- EntrySize = 4;
- } else if (KindName == "label-difference64")
- Encoding = JumpTableEntryEncoding::LabelDifference64;
- else if (KindName == "inline") {
- Encoding = JumpTableEntryEncoding::Inline;
- EntrySize = 4;
- } else if (KindName == "custom32") {
- Encoding = JumpTableEntryEncoding::Custom32;
- EntrySize = 4;
- } else {
- return fail(KindNode->range(), "unknown enumerated scalar");
- }
-
- const yaml::Node *Entries = mappingValue(*JumpTable, "entries");
- if (Entries == nullptr)
- return false;
- if (requireKind(*Entries, yaml::NodeKind::Sequence, "jump table entries"))
- return true;
- constexpr std::array EntryKeys{
- std::string_view{"id"},
- std::string_view{"blocks"},
- };
- std::unordered_set<std::uint32_t> IDs;
- for (const yaml::NodeID EntryID : Entries->sequence()) {
- const yaml::Node *Entry = node(EntryID);
- if (Entry == nullptr || requireKind(*Entry, yaml::NodeKind::Mapping, "jump table entry") ||
- validateKeys(*Entry, EntryKeys, "jump table entry")) {
- return true;
- }
- const yaml::Node *IDNode = mappingValue(*Entry, "id");
- if (IDNode == nullptr)
- return fail(Entry->range(), "jump table entry requires 'id'");
- std::uint64_t WideID = 0;
- if (unsignedValue(*IDNode, WideID, "a 32-bit jump table ID") ||
- WideID > std::numeric_limits<std::uint32_t>::max()) {
- return true;
- }
- const auto TextID = static_cast<std::uint32_t>(WideID);
- if (!IDs.insert(TextID).second)
- return fail(IDNode->range(), "duplicate jump table ID");
- auto Reserved = Function.jumpTableInfo().reserveTable();
- if (!Reserved)
- return importError(Entry->range(), Reserved.error());
- PendingJumpTable NewPending{
- .Index = *Reserved,
- .TextID = TextID,
- .Encoding = Encoding,
- .EntrySize = EntrySize,
- .Blocks = {},
- };
- if (const yaml::Node *Blocks = mappingValue(*Entry, "blocks")) {
- if (requireKind(*Blocks, yaml::NodeKind::Sequence, "jump table blocks"))
- return true;
- for (const yaml::NodeID BlockID : Blocks->sequence()) {
- const yaml::Node *BlockNode = node(BlockID);
- std::string_view Text;
- if (BlockNode == nullptr || scalar(*BlockNode, Text, "basic block reference"))
- return true;
- NewPending.Blocks.emplace_back(Text);
- }
- }
- if (NewPending.Blocks.empty())
- return fail(Entry->range(), "jump table entry must contain at least one block");
- Context.JumpTables.push_back({.TextID = TextID, .Index = *Reserved});
- Pending.push_back(std::move(NewPending));
- }
- return false;
-}
-
-bool MIRDocumentParser::defineJumpTables(MachineFunction &Function,
- std::span<const PendingJumpTable> Pending) {
- for (const PendingJumpTable &Table : Pending) {
- MachineJumpTableDesc Description;
- Description.Encoding = Table.Encoding;
- Description.EntrySize = Table.EntrySize;
- Description.Align = *Alignment::create(Table.EntrySize);
- for (const std::string &Text : Table.Blocks) {
- MachineBasicBlock *Block = nullptr;
- if (parseBlockReference(Text, Result.Source.range(0, 0), Function, Block))
- return true;
- if (Block == nullptr)
- return fail(Result.Source.range(0, 0), "reference to undefined machine basic block");
- Description.Targets.push_back(Block);
- }
- auto DefineResult =
- Function.jumpTableInfo().defineReservedTable(Table.Index, std::move(Description));
- if (!DefineResult)
- return importError(Result.Source.range(0, 0), DefineResult.error());
- }
- return false;
-}
-
-} // namespace rcc::mir
diff --git a/backend/lib/MIR/MIRDocument.cpp b/backend/lib/MIR/MIRDocument.cpp
deleted file mode 100644
index ad9ec40..0000000
--- a/backend/lib/MIR/MIRDocument.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#include "rcc/MIR/MIRDocument.h"
-
-#include "MIRDocumentParser.h"
-
-#include <string>
-#include <utility>
-
-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
diff --git a/backend/lib/MIR/MIRDocumentPrinter.cpp b/backend/lib/MIR/MIRDocumentPrinter.cpp
deleted file mode 100644
index 06998bb..0000000
--- a/backend/lib/MIR/MIRDocumentPrinter.cpp
+++ /dev/null
@@ -1,1499 +0,0 @@
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#include "MIRDocumentReferenceValidation.h"
-#include "rcc/CodeGen/MachineBasicBlock.h"
-#include "rcc/CodeGen/MachineCompilationStatus.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/TargetDescription.h"
-#include "rcc/CodeGen/TargetMIRTextInfo.h"
-#include "rcc/CodeGen/TargetRegisterInfo.h"
-#include "rcc/MIR/LLVMConstantSyntax.h"
-#include "rcc/MIR/MIDebugSyntax.h"
-#include "rcc/MIR/MIRDocument.h"
-
-#include <algorithm>
-#include <array>
-#include <charconv>
-#include <cstddef>
-#include <cstdint>
-#include <limits>
-#include <optional>
-#include <ranges>
-#include <span>
-#include <string>
-#include <string_view>
-#include <unordered_set>
-#include <utility>
-#include <variant>
-#include <vector>
-
-namespace rcc::mir {
-namespace {
-
-enum class YAMLQuoting : std::uint8_t {
- None,
- Single,
- Double,
-};
-
-[[nodiscard]] bool isASCIIAlphanumeric(unsigned char Character) noexcept {
- return (Character >= '0' && Character <= '9') || (Character >= 'A' && Character <= 'Z') ||
- (Character >= 'a' && Character <= 'z');
-}
-
-[[nodiscard]] bool isYAMLNumeric(std::string_view Value) noexcept {
- if (Value.empty() || Value == "+" || Value == "-")
- return false;
- if (Value == ".nan" || Value == ".NaN" || Value == ".NAN")
- return true;
-
- std::string_view Tail = Value;
- if (Tail.front() == '+' || Tail.front() == '-')
- Tail.remove_prefix(1);
- if (Tail == ".inf" || Tail == ".Inf" || Tail == ".INF")
- return true;
- if (Value.starts_with("0o")) {
- return Value.size() > 2U &&
- Value.substr(2).find_first_not_of("01234567") == std::string_view::npos;
- }
- if (Value.starts_with("0x")) {
- return Value.size() > 2U &&
- Value.substr(2).find_first_not_of("0123456789abcdefABCDEF") == std::string_view::npos;
- }
-
- if (Tail.empty() || Tail.front() == 'e' || Tail.front() == 'E')
- return false;
- std::size_t Position = 0;
- if (Tail.front() == '.') {
- ++Position;
- if (Position == Tail.size() || Tail[Position] < '0' || Tail[Position] > '9')
- return false;
- while (Position < Tail.size() && Tail[Position] >= '0' && Tail[Position] <= '9')
- ++Position;
- } else {
- while (Position < Tail.size() && Tail[Position] >= '0' && Tail[Position] <= '9')
- ++Position;
- if (Position == 0)
- return false;
- }
-
- if (Position < Tail.size() && Tail[Position] == '.') {
- ++Position;
- while (Position < Tail.size() && Tail[Position] >= '0' && Tail[Position] <= '9')
- ++Position;
- }
- if (Position == Tail.size())
- return true;
- if (Tail[Position] != 'e' && Tail[Position] != 'E')
- return false;
- ++Position;
- if (Position < Tail.size() && (Tail[Position] == '+' || Tail[Position] == '-'))
- ++Position;
- const std::size_t ExponentBegin = Position;
- while (Position < Tail.size() && Tail[Position] >= '0' && Tail[Position] <= '9')
- ++Position;
- return Position == Tail.size() && Position != ExponentBegin;
-}
-
-[[nodiscard]] YAMLQuoting yamlQuoting(std::string_view Value) noexcept {
- if (Value.empty())
- return YAMLQuoting::Single;
-
- YAMLQuoting Result = YAMLQuoting::None;
- const auto IsSpace = [](unsigned char Character) {
- return Character == ' ' || Character == '\t' || Character == '\r' || Character == '\n' ||
- Character == '\f' || Character == '\v';
- };
- if (IsSpace(static_cast<unsigned char>(Value.front())) ||
- IsSpace(static_cast<unsigned char>(Value.back())) || Value == "null" || Value == "Null" ||
- Value == "NULL" || Value == "~" || Value == "true" || Value == "True" || Value == "TRUE" ||
- Value == "false" || Value == "False" || Value == "FALSE" || isYAMLNumeric(Value)) {
- Result = YAMLQuoting::Single;
- }
-
- switch (Value.front()) {
- case '-':
- case '?':
- case ':':
- case '\\':
- case ',':
- case '[':
- case ']':
- case '{':
- case '}':
- case '#':
- case '&':
- case '*':
- case '!':
- case '|':
- case '>':
- case '\'':
- case '"':
- case '%':
- case '@':
- case '`':
- Result = YAMLQuoting::Single;
- break;
- default:
- break;
- }
-
- for (const char RawCharacter : Value) {
- const auto Character = static_cast<unsigned char>(RawCharacter);
- if (isASCIIAlphanumeric(Character))
- continue;
- switch (Character) {
- case '_':
- case '-':
- case '^':
- case '.':
- case ',':
- case ' ':
- case '\t':
- continue;
- case '\n':
- case '\r':
- case 0x7f:
- return YAMLQuoting::Double;
- default:
- if (Character <= 0x1fU || (Character & 0x80U) != 0)
- return YAMLQuoting::Double;
- Result = YAMLQuoting::Single;
- break;
- }
- }
- return Result;
-}
-
-[[nodiscard]] std::string yamlScalar(std::string_view Value) {
- const YAMLQuoting Quoting = yamlQuoting(Value);
- if (Quoting == YAMLQuoting::None)
- return std::string(Value);
-
- std::string Result;
- Result.reserve(Value.size() + 2U);
- const char Quote = Quoting == YAMLQuoting::Single ? '\'' : '"';
- Result.push_back(Quote);
- for (const char Character : Value) {
- if (Quoting == YAMLQuoting::Single) {
- Result.push_back(Character);
- if (Character == '\'')
- Result.push_back('\'');
- continue;
- }
- switch (Character) {
- case '\\':
- Result.append("\\\\");
- break;
- case '"':
- Result.append("\\\"");
- break;
- case '\0':
- Result.append("\\0");
- break;
- case '\a':
- Result.append("\\a");
- break;
- case '\b':
- Result.append("\\b");
- break;
- case '\t':
- Result.append("\\t");
- break;
- case '\n':
- Result.append("\\n");
- break;
- case '\v':
- Result.append("\\v");
- break;
- case '\f':
- Result.append("\\f");
- break;
- case '\r':
- Result.append("\\r");
- break;
- case '\x1b':
- Result.append("\\e");
- break;
- default: {
- const auto Byte = static_cast<unsigned char>(Character);
- if (Byte < 0x20U || Byte == 0x7fU) {
- static constexpr std::string_view Hex = "0123456789ABCDEF";
- Result.append("\\x");
- Result.push_back(Hex[Byte >> 4U]);
- Result.push_back(Hex[Byte & 0xfU]);
- } else {
- Result.push_back(Character);
- }
- break;
- }
- }
- }
- Result.push_back(Quote);
- return Result;
-}
-
-void appendBlockScalar(std::string &Output, std::string_view Text, std::size_t Indent) {
- std::size_t Position = 0;
- do {
- Output.append(Indent, ' ');
- const std::size_t End = Text.find('\n', Position);
- if (End == std::string_view::npos) {
- Output.append(Text.substr(Position));
- Output.push_back('\n');
- return;
- }
- Output.append(Text.substr(Position, End - Position));
- Output.push_back('\n');
- Position = End + 1U;
- } while (Position < Text.size());
-}
-
-[[nodiscard]] std::optional<std::uint32_t> parseDecimal32(std::string_view Text) noexcept {
- std::uint32_t Value = 0;
- if (Text.empty())
- return std::nullopt;
- const auto [End, Error] = std::from_chars(Text.data(), Text.data() + Text.size(), Value, 10);
- if (Error != std::errc{} || End != Text.data() + Text.size())
- return std::nullopt;
- return Value;
-}
-
-[[nodiscard]] bool isDefaultFrameState(const MachineFrameState &State) {
- return State == MachineFrameState{};
-}
-
-class MIRDocumentPrinter final {
-public:
- MIRDocumentPrinter(const MIRDocument &Document, const TargetDescription &Target,
- MIRDocumentPrinterOptions Options)
- : Document(Document), Target(Target), Options(std::move(Options)) {}
-
- [[nodiscard]] Expected<std::string> run();
-
-private:
- struct InstructionLocation final {
- std::uint32_t Block = 0;
- std::uint32_t Offset = 0;
- };
-
- [[nodiscard]] Expected<void> printFunction(const MachineFunction &Function,
- const MIRFunctionDocumentInfo *DocumentInfo);
- [[nodiscard]] Expected<MIParserContext> createBodyContext(const MachineFunction &Function) const;
- [[nodiscard]] Expected<void> printRegisters(const MachineFunction &Function,
- const MIParserContext &Context);
- [[nodiscard]] Expected<void> printFrameInfo(const MachineFunction &Function,
- const MIParserContext &Context);
- [[nodiscard]] Expected<void> printStackObjects(const MachineFunction &Function,
- const MIParserContext &Context, bool Fixed);
- [[nodiscard]] Expected<void> printConstants(const MachineFunction &Function,
- const MIParserContext &Context);
- [[nodiscard]] Expected<void> printJumpTable(const MachineFunction &Function,
- const MIParserContext &Context);
- [[nodiscard]] Expected<void> printMetadata(const MachineFunction &Function,
- const MIParserContext &Context);
- [[nodiscard]] Expected<void> printTargetState(const MachineFunction &Function);
- [[nodiscard]] Expected<void> printFunctionRecords(const MachineFunction &Function,
- const MIParserContext &Context);
- [[nodiscard]] Expected<void> verifyOmittedEmbeddedIRLayout(const MachineFunction &Function) const;
-
- [[nodiscard]] Expected<std::string> physicalRegister(PhysicalRegister Register) const;
- [[nodiscard]] Expected<std::string> frameReference(FrameIndex Index,
- const MIParserContext &Context) const;
- [[nodiscard]] Expected<std::string> metadataReference(MachineMetadataReference Reference,
- const MIParserContext &Context) const;
- [[nodiscard]] Expected<std::string> metadataValue(const MachineMetadataValue &Value,
- const MIParserContext &Context) const;
- [[nodiscard]] Expected<std::string> debugLocation(const MachineDebugLocation &Location,
- const MIParserContext &Context) const;
- [[nodiscard]] Expected<std::string> diLocation(const MachineDILocation &Location,
- std::size_t FrameIndex,
- const MIParserContext &Context) const;
- [[nodiscard]] Expected<InstructionLocation> instructionLocation(const MachineFunction &Function,
- MachineInstrStableID ID) const;
- [[nodiscard]] Expected<std::string> blockReference(const MachineFunction &Function,
- MachineBlockStableID ID) const;
- [[nodiscard]] Expected<std::string> targetNode(const MIRTargetNode &Node) const;
- [[nodiscard]] const MIRFunctionDocumentInfo *
- documentInfo(const MachineFunction &Function) const noexcept;
-
- void printBoolean(std::string_view Key, bool Value, bool Always = false);
- void printUnsigned(std::string_view Key, std::uint64_t Value, bool Always = false);
- void printSigned(std::string_view Key, std::int64_t Value, bool Always = false);
-
- const MIRDocument &Document;
- const TargetDescription &Target;
- MIRDocumentPrinterOptions Options;
- std::string Output;
-};
-
-void MIRDocumentPrinter::printBoolean(std::string_view Key, bool Value, bool Always) {
- if (!Always && Options.Mode == MIPrintMode::Readable && !Value)
- return;
- Output.append(Key);
- Output.append(Value ? ": true\n" : ": false\n");
-}
-
-void MIRDocumentPrinter::printUnsigned(std::string_view Key, std::uint64_t Value, bool Always) {
- if (!Always && Options.Mode == MIPrintMode::Readable && Value == 0)
- return;
- Output.append(Key);
- Output.append(": ");
- Output.append(std::to_string(Value));
- Output.push_back('\n');
-}
-
-void MIRDocumentPrinter::printSigned(std::string_view Key, std::int64_t Value, bool Always) {
- if (!Always && Options.Mode == MIPrintMode::Readable && Value == 0)
- return;
- Output.append(Key);
- Output.append(": ");
- Output.append(std::to_string(Value));
- Output.push_back('\n');
-}
-
-Expected<std::string> MIRDocumentPrinter::physicalRegister(PhysicalRegister Register) const {
- const auto Name = Target.registers().physicalRegisterMIRName(Register);
- if (!Name) {
- return makeUnexpected(ErrorCode::VerificationError,
- "physical register has no canonical MIR name");
- }
- return "$" + std::string(*Name);
-}
-
-const MIRFunctionDocumentInfo *
-MIRDocumentPrinter::documentInfo(const MachineFunction &Function) const noexcept {
- const auto Infos = Document.functionDocumentInfo();
- const auto Found = std::ranges::find_if(Infos, [&Function](const MIRFunctionDocumentInfo &Info) {
- return Info.Function == Function.stableID();
- });
- return Found == Infos.end() ? nullptr : &*Found;
-}
-
-Expected<void>
-MIRDocumentPrinter::verifyOmittedEmbeddedIRLayout(const MachineFunction &Function) const {
- if (Options.EmitEmbeddedIR)
- return {};
-
- const EmbeddedIRReferenceIndex *Embedded = Document.embeddedIR();
- if (Embedded == nullptr)
- return {};
-
- bool HasLayoutDependentContent = false;
- for (const VirtualRegisterInfo &RegisterInfo : Function.registerInfo().virtualRegisters()) {
- if (!RegisterInfo.IsTombstone && RegisterInfo.Type &&
- RegisterInfo.Type->isPointerOrPointerVector()) {
- HasLayoutDependentContent = true;
- break;
- }
- }
- if (!HasLayoutDependentContent) {
- for (const MachineConstantPoolEntry &Entry : Function.constantPool().entries()) {
- const MachineConstantPoolEntryDesc *Description = Entry.description();
- if (Description != nullptr &&
- std::holds_alternative<GenericConstantData>(Description->Value)) {
- HasLayoutDependentContent = true;
- break;
- }
- }
- }
- if (!HasLayoutDependentContent)
- return {};
-
- if (Options.DataLayout.empty()) {
- return makeUnexpected(
- ErrorCode::InvalidArgument,
- "omitting embedded LLVM IR with layout-dependent MIR requires an explicit printer "
- "DataLayout");
- }
-
- const LLVMConstantSyntaxOptions LayoutOptions{
- .DataLayout = Options.DataLayout,
- .NamedTypes = {},
- };
- for (const VirtualRegisterInfo &RegisterInfo : Function.registerInfo().virtualRegisters()) {
- if (RegisterInfo.IsTombstone || !RegisterInfo.Type ||
- !RegisterInfo.Type->isPointerOrPointerVector()) {
- continue;
- }
- const auto AddressSpace = RegisterInfo.Type->addressSpace();
- if (!AddressSpace) {
- return makeUnexpected(ErrorCode::VerificationError,
- "pointer virtual register has no address space");
- }
- auto PointerSize = llvmPointerSizeInBits(*AddressSpace, LayoutOptions);
- if (!PointerSize) {
- return makeUnexpected(ErrorCode::InvalidArgument,
- "printer DataLayout cannot determine pointer width: " +
- std::string(PointerSize.error().message()));
- }
- if (*PointerSize != RegisterInfo.Type->scalarBitWidth()) {
- return makeUnexpected(ErrorCode::VerificationError,
- "printer DataLayout does not match pointer virtual-register width");
- }
- }
-
- for (const MachineConstantPoolEntry &Entry : Function.constantPool().entries()) {
- const MachineConstantPoolEntryDesc *Description = Entry.description();
- if (Description == nullptr)
- continue;
- const auto *Generic = std::get_if<GenericConstantData>(&Description->Value);
- if (Generic == nullptr)
- continue;
- auto StorageSize = llvmConstantStorageSize(Generic->Type, LayoutOptions);
- if (!StorageSize) {
- return makeUnexpected(ErrorCode::InvalidArgument,
- "printer DataLayout cannot lay out generic constant: " +
- std::string(StorageSize.error().message()));
- }
- if (*StorageSize != Description->Size) {
- return makeUnexpected(ErrorCode::VerificationError,
- "printer DataLayout does not match generic constant storage size");
- }
- }
- return {};
-}
-
-Expected<MIParserContext>
-MIRDocumentPrinter::createBodyContext(const MachineFunction &Function) const {
- MIParserContext Context;
- Context.SyncScopes.assign(Document.syncScopes().begin(), Document.syncScopes().end());
- const auto VirtualRegisters = Function.registerInfo().virtualRegisters();
- for (std::size_t Index = 0; Index < VirtualRegisters.size(); ++Index) {
- if (VirtualRegisters[Index].IsTombstone)
- continue;
- auto Reg = Register::makeVirtual(Index);
- if (!Reg)
- return std::unexpected(Reg.error());
- if (Index > std::numeric_limits<std::uint32_t>::max()) {
- return makeUnexpected(ErrorCode::Unsupported,
- "MIR cannot represent more than 2^32 virtual registers");
- }
- Context.VirtualRegisters.push_back({.TextID = static_cast<std::uint32_t>(Index), .Reg = *Reg});
- }
-
- std::uint32_t StackID = 0;
- std::uint32_t FixedID = 0;
- for (const StackObject &Object : Function.frameInfo().objects()) {
- const StackObjectDesc *Description = Object.description();
- if (Description == nullptr)
- continue;
- MIFrameObjectSlot Slot{
- .TextID = Object.isFixed() ? FixedID++ : StackID++,
- .Index = Object.index(),
- .Name = Description->AllocaName,
- };
- (Object.isFixed() ? Context.FixedStackObjects : Context.StackObjects)
- .push_back(std::move(Slot));
- }
-
- std::uint32_t ConstantID = 0;
- for (const MachineConstantPoolEntry &Entry : Function.constantPool().entries()) {
- if (Entry.description() != nullptr)
- Context.ConstantPoolItems.push_back({.TextID = ConstantID++, .Index = Entry.index()});
- }
- std::uint32_t JumpTableID = 0;
- for (const MachineJumpTableEntry &Entry : Function.jumpTableInfo().tables()) {
- if (Entry.description() != nullptr)
- Context.JumpTables.push_back({.TextID = JumpTableID++, .Index = Entry.index()});
- }
-
- std::unordered_set<std::uint32_t> EmbeddedMetadataSlots;
- if (const EmbeddedIRReferenceIndex *Embedded = Document.embeddedIR()) {
- for (const EmbeddedIRMetadataDefinition &Definition : Embedded->metadataDefinitions()) {
- const auto Slot = parseDecimal32(Definition.Name);
- if (Slot) {
- if (!EmbeddedMetadataSlots.insert(*Slot).second) {
- return makeUnexpected(ErrorCode::VerificationError,
- "embedded LLVM IR metadata slots are not unique");
- }
- Context.Metadata.push_back(
- {.TextID = *Slot, .Reference = MachineMetadataReference::embeddedIR(*Slot)});
- }
- }
- }
-
- std::unordered_set<std::uint32_t> UsedMetadataSlots = EmbeddedMetadataSlots;
- for (const MachineMetadataNode &Node : Function.metadata().nodes()) {
- if (!EmbeddedMetadataSlots.contains(Node.Slot))
- UsedMetadataSlots.insert(Node.Slot);
- }
- std::uint64_t NextMetadataSlot = 0;
- for (const MachineMetadataNode &Node : Function.metadata().nodes()) {
- std::uint32_t TextID = Node.Slot;
- if (EmbeddedMetadataSlots.contains(TextID)) {
- while (NextMetadataSlot <= std::numeric_limits<std::uint32_t>::max() &&
- UsedMetadataSlots.contains(static_cast<std::uint32_t>(NextMetadataSlot))) {
- ++NextMetadataSlot;
- }
- if (NextMetadataSlot > std::numeric_limits<std::uint32_t>::max()) {
- return makeUnexpected(ErrorCode::Unsupported, "MIR metadata text slot space is exhausted");
- }
- TextID = static_cast<std::uint32_t>(NextMetadataSlot);
- UsedMetadataSlots.insert(TextID);
- ++NextMetadataSlot;
- }
- Context.Metadata.push_back(
- {.TextID = TextID, .Reference = MachineMetadataReference::machineFunction(Node.Slot)});
- }
- return Context;
-}
-
-Expected<std::string> MIRDocumentPrinter::frameReference(FrameIndex Index,
- const MIParserContext &Context) const {
- const auto Stack = std::ranges::find_if(
- Context.StackObjects, [Index](const MIFrameObjectSlot &Slot) { return Slot.Index == Index; });
- if (Stack != Context.StackObjects.end())
- return "%stack." + std::to_string(Stack->TextID);
- const auto Fixed =
- std::ranges::find_if(Context.FixedStackObjects,
- [Index](const MIFrameObjectSlot &Slot) { return Slot.Index == Index; });
- if (Fixed != Context.FixedStackObjects.end())
- return "%fixed-stack." + std::to_string(Fixed->TextID);
- return makeUnexpected(ErrorCode::VerificationError,
- "frame state references an unknown stack object");
-}
-
-Expected<std::string> MIRDocumentPrinter::metadataReference(MachineMetadataReference Reference,
- const MIParserContext &Context) const {
- if (!Reference.isValid())
- return makeUnexpected(ErrorCode::VerificationError, "invalid metadata reference");
- const auto Slot =
- std::ranges::find_if(Context.Metadata, [Reference](const MIMetadataSlot &Candidate) {
- return Candidate.Reference == Reference;
- });
- if (Slot == Context.Metadata.end()) {
- return makeUnexpected(ErrorCode::VerificationError, "metadata reference has no MIR text slot");
- }
- return "!" + std::to_string(Slot->TextID);
-}
-
-Expected<std::string> MIRDocumentPrinter::metadataValue(const MachineMetadataValue &Value,
- const MIParserContext &Context) const {
- if (const MachineMetadataReference *Reference = Value.reference())
- return metadataReference(*Reference, Context);
- const MachineDIExpression *Expression = Value.expression();
- if (Expression == nullptr)
- return makeUnexpected(ErrorCode::VerificationError, "metadata value is not printable");
- return formatDIExpression(Expression->elements());
-}
-
-Expected<std::string> MIRDocumentPrinter::diLocation(const MachineDILocation &Location,
- std::size_t FrameIndex,
- const MIParserContext &Context) const {
- if (FrameIndex >= Location.inlineChain().size()) {
- return makeUnexpected(ErrorCode::VerificationError,
- "inline DILocation has an invalid frame chain");
- }
- const MachineDILocationFrame &Frame = Location.inlineChain()[FrameIndex];
- auto Scope = metadataReference(Frame.Scope, Context);
- if (!Scope)
- return std::unexpected(Scope.error());
- std::string Text = "!DILocation(line: " + std::to_string(Frame.Line);
- if (Options.Mode == MIPrintMode::Complete || Frame.Column != 0)
- Text.append(", column: " + std::to_string(Frame.Column));
- Text.append(", scope: " + *Scope);
- if (FrameIndex + 1U < Location.inlineChain().size()) {
- auto Nested = diLocation(Location, FrameIndex + 1U, Context);
- if (!Nested)
- return std::unexpected(Nested.error());
- Text.append(", inlinedAt: " + *Nested);
- } else if (Location.terminalInlinedAt()) {
- auto Terminal = metadataReference(*Location.terminalInlinedAt(), Context);
- if (!Terminal)
- return std::unexpected(Terminal.error());
- Text.append(", inlinedAt: " + *Terminal);
- }
- if (Options.Mode == MIPrintMode::Complete || Frame.IsImplicitCode)
- Text.append(Frame.IsImplicitCode ? ", isImplicitCode: true" : ", isImplicitCode: false");
- if (Options.Mode == MIPrintMode::Complete || Frame.AtomGroup != 0)
- Text.append(", atomGroup: " + std::to_string(Frame.AtomGroup));
- if (Options.Mode == MIPrintMode::Complete || Frame.AtomRank != 0)
- Text.append(", atomRank: " + std::to_string(Frame.AtomRank));
- Text.push_back(')');
- return Text;
-}
-
-Expected<std::string> MIRDocumentPrinter::debugLocation(const MachineDebugLocation &Location,
- const MIParserContext &Context) const {
- if (const MachineMetadataReference *Reference = Location.reference())
- return metadataReference(*Reference, Context);
- if (const MachineDILocation *Inline = Location.inlineLocation())
- return diLocation(*Inline, 0, Context);
- return makeUnexpected(ErrorCode::VerificationError, "debug location is not printable");
-}
-
-Expected<MIRDocumentPrinter::InstructionLocation>
-MIRDocumentPrinter::instructionLocation(const MachineFunction &Function,
- MachineInstrStableID ID) const {
- std::uint64_t BlockNumber = 0;
- for (const MachineBasicBlock *Block = Function.firstBlock(); Block != nullptr;
- Block = Block->next(), ++BlockNumber) {
- if (BlockNumber > std::numeric_limits<std::uint32_t>::max())
- break;
- std::uint64_t Offset = 0;
- for (const MachineInstr *Instruction = Block->firstInstruction(); Instruction != nullptr;
- Instruction = Instruction->next(), ++Offset) {
- if (Instruction->stableID() != ID)
- continue;
- if (Offset > std::numeric_limits<std::uint32_t>::max()) {
- return makeUnexpected(ErrorCode::Unsupported, "MIR instruction offset exceeds 32 bits");
- }
- return InstructionLocation{static_cast<std::uint32_t>(BlockNumber),
- static_cast<std::uint32_t>(Offset)};
- }
- }
- return makeUnexpected(ErrorCode::VerificationError,
- "function record references an unlinked instruction");
-}
-
-Expected<std::string> MIRDocumentPrinter::blockReference(const MachineFunction &Function,
- MachineBlockStableID ID) const {
- std::uint64_t Number = 0;
- for (const MachineBasicBlock *Block = Function.firstBlock(); Block != nullptr;
- Block = Block->next(), ++Number) {
- if (Block->stableID() == ID) {
- if (Number > std::numeric_limits<std::uint32_t>::max())
- break;
- return "%bb." + std::to_string(Number);
- }
- }
- return makeUnexpected(ErrorCode::VerificationError,
- "frame record references an unlinked basic block");
-}
-
-Expected<std::string> MIRDocumentPrinter::targetNode(const MIRTargetNode &Node) const {
- switch (Node.Kind) {
- case MIRTargetNodeKind::Null:
- return std::string("null");
- case MIRTargetNodeKind::Scalar:
- return yamlScalar(Node.Scalar);
- case MIRTargetNodeKind::Sequence: {
- std::string Text = "[ ";
- for (std::size_t Index = 0; Index < Node.Sequence.size(); ++Index) {
- if (Index != 0)
- Text.append(", ");
- auto Element = targetNode(Node.Sequence[Index]);
- if (!Element)
- return std::unexpected(Element.error());
- Text.append(*Element);
- }
- Text.append(" ]");
- return Text;
- }
- case MIRTargetNodeKind::Mapping: {
- std::string Text = "{ ";
- for (std::size_t Index = 0; Index < Node.Mapping.size(); ++Index) {
- if (Index != 0)
- Text.append(", ");
- Text.append(yamlScalar(Node.Mapping[Index].Key));
- Text.append(": ");
- auto Value = targetNode(Node.Mapping[Index].Value);
- if (!Value)
- return std::unexpected(Value.error());
- Text.append(*Value);
- }
- Text.append(" }");
- return Text;
- }
- case MIRTargetNodeKind::Count:
- break;
- }
- return makeUnexpected(ErrorCode::VerificationError, "target MIR node has an invalid kind");
-}
-
-Expected<void> MIRDocumentPrinter::printRegisters(const MachineFunction &Function,
- const MIParserContext &Context) {
- if (Context.VirtualRegisters.empty()) {
- if (Options.Mode == MIPrintMode::Complete)
- Output.append("registers: []\n");
- } else {
- Output.append("registers:\n");
- for (const MIVirtualRegisterSlot &Slot : Context.VirtualRegisters) {
- auto InfoResult = Function.registerInfo().virtualRegisterInfo(Slot.Reg);
- if (!InfoResult)
- return std::unexpected(InfoResult.error());
- const VirtualRegisterInfo &Info = InfoResult->get();
- std::string Class = "_";
- if (Info.RegClass) {
- const auto Name = Target.registers().registerClassMIRName(*Info.RegClass);
- if (!Name)
- return makeUnexpected(ErrorCode::VerificationError,
- "virtual register class has no canonical MIR name");
- Class = *Name;
- } else if (Info.Bank) {
- const auto Name = Target.registers().registerBankMIRName(*Info.Bank);
- if (!Name)
- return makeUnexpected(ErrorCode::VerificationError,
- "virtual register bank has no canonical MIR name");
- Class = *Name;
- }
- if (Info.AllocationHints.size() > 1) {
- return makeUnexpected(
- ErrorCode::Unsupported,
- "fixed LLVM MIR can serialize only one simple preferred-register hint");
- }
-
- Output.append(" - { id: ");
- Output.append(std::to_string(Slot.TextID));
- Output.append(", class: ");
- Output.append(yamlScalar(Class));
- if (!Info.AllocationHints.empty()) {
- auto Preferred = physicalRegister(Info.AllocationHints.front());
- if (!Preferred)
- return std::unexpected(Preferred.error());
- Output.append(", preferred-register: ");
- Output.append(yamlScalar(*Preferred));
- }
- if (Info.TargetFlags != 0) {
- auto Names = Target.mirText().virtualRegisterFlagNames(Info.TargetFlags);
- if (!Names)
- return std::unexpected(Names.error());
- Output.append(", flags: [ ");
- for (std::size_t Index = 0; Index < Names->size(); ++Index) {
- if (Index != 0)
- Output.append(", ");
- Output.append(yamlScalar((*Names)[Index]));
- }
- Output.append(" ]");
- }
- Output.append(" }\n");
- }
- }
-
- if (Function.registerInfo().liveIns().empty()) {
- if (Options.Mode == MIPrintMode::Complete)
- Output.append("liveins: []\n");
- } else {
- Output.append("liveins:\n");
- for (const PhysicalRegisterLiveIn &LiveIn : Function.registerInfo().liveIns()) {
- auto Name = physicalRegister(LiveIn.PhysReg);
- if (!Name)
- return std::unexpected(Name.error());
- Output.append(" - { reg: ");
- Output.append(yamlScalar(*Name));
- if (LiveIn.InitialVirtualRegister) {
- const auto Slot = std::ranges::find_if(
- Context.VirtualRegisters, [&LiveIn](const MIVirtualRegisterSlot &Candidate) {
- return Candidate.Reg == *LiveIn.InitialVirtualRegister;
- });
- if (Slot == Context.VirtualRegisters.end()) {
- return makeUnexpected(ErrorCode::VerificationError,
- "live-in references an unknown virtual register");
- }
- Output.append(", virtual-reg: ");
- Output.append(yamlScalar("%" + std::to_string(Slot->TextID)));
- }
- Output.append(" }\n");
- }
- }
-
- if (Function.records().hasCalleeSavedRegisters()) {
- Output.append("calleeSavedRegisters: [ ");
- const auto Registers = Function.records().calleeSavedRegisters();
- for (std::size_t Index = 0; Index < Registers.size(); ++Index) {
- if (Index != 0)
- Output.append(", ");
- auto Name = physicalRegister(Registers[Index]);
- if (!Name)
- return std::unexpected(Name.error());
- Output.append(yamlScalar(*Name));
- }
- Output.append(" ]\n");
- }
- return {};
-}
-
-Expected<void> MIRDocumentPrinter::printFrameInfo(const MachineFunction &Function,
- const MIParserContext &Context) {
- const MachineFrameState &State = Function.frameInfo().state();
- if (Options.Mode == MIPrintMode::Readable && isDefaultFrameState(State))
- return {};
- if (State.MaxCallFrameSize &&
- *State.MaxCallFrameSize > std::numeric_limits<std::uint32_t>::max()) {
- return makeUnexpected(ErrorCode::Unsupported,
- "fixed LLVM MIR maxCallFrameSize is limited to 32 bits");
- }
- Output.append("frameInfo:\n");
- const auto Bool = [this](std::string_view Key, bool Value) {
- if (Options.Mode == MIPrintMode::Readable && !Value)
- return;
- Output.append(" ");
- Output.append(Key);
- Output.append(Value ? ": true\n" : ": false\n");
- };
- const auto Unsigned = [this](std::string_view Key, std::uint64_t Value) {
- if (Options.Mode == MIPrintMode::Readable && Value == 0)
- return;
- Output.append(" ");
- Output.append(Key);
- Output.append(": ");
- Output.append(std::to_string(Value));
- Output.push_back('\n');
- };
-
- Bool("isFrameAddressTaken", State.IsFrameAddressTaken);
- Bool("isReturnAddressTaken", State.IsReturnAddressTaken);
- Bool("hasStackMap", State.HasStackMap);
- Bool("hasPatchPoint", State.HasPatchPoint);
- Unsigned("stackSize", State.StackSize);
- if (Options.Mode == MIPrintMode::Complete || State.OffsetAdjustment != 0) {
- Output.append(" offsetAdjustment: ");
- Output.append(std::to_string(State.OffsetAdjustment));
- Output.push_back('\n');
- }
- Unsigned("maxAlignment", State.MaxAlignment ? State.MaxAlignment->bytes() : 0);
- Bool("adjustsStack", State.AdjustsStack);
- Bool("hasCalls", State.HasCalls);
- if (Options.Mode == MIPrintMode::Complete ||
- State.FramePointerPolicy != MachineFramePointerPolicy::None) {
- Output.append(" framePointerPolicy: ");
- Output.append(getMachineFramePointerPolicyName(State.FramePointerPolicy));
- Output.push_back('\n');
- }
- if (Options.Mode == MIPrintMode::Complete || State.StackProtector) {
- std::string Reference;
- if (State.StackProtector) {
- auto PrintedReference = frameReference(*State.StackProtector, Context);
- if (!PrintedReference)
- return std::unexpected(PrintedReference.error());
- Reference = std::move(*PrintedReference);
- }
- Output.append(" stackProtector: ");
- Output.append(yamlScalar(Reference));
- Output.push_back('\n');
- }
- if (Options.Mode == MIPrintMode::Complete || State.FunctionContext) {
- std::string Reference;
- if (State.FunctionContext) {
- auto PrintedReference = frameReference(*State.FunctionContext, Context);
- if (!PrintedReference)
- return std::unexpected(PrintedReference.error());
- Reference = std::move(*PrintedReference);
- }
- Output.append(" functionContext: ");
- Output.append(yamlScalar(Reference));
- Output.push_back('\n');
- }
- if (Options.Mode == MIPrintMode::Complete || State.MaxCallFrameSize) {
- Unsigned("maxCallFrameSize",
- State.MaxCallFrameSize.value_or(std::numeric_limits<std::uint32_t>::max()));
- }
- Unsigned("cvBytesOfCalleeSavedRegisters", State.CVBytesOfCalleeSavedRegisters);
- Bool("hasOpaqueSPAdjustment", State.HasOpaqueSPAdjustment);
- Bool("hasVAStart", State.HasVAStart);
- Bool("hasMustTailInVarArgFunc", State.HasMustTailInVarArgFunc);
- Bool("hasTailCall", State.HasTailCall);
- Bool("isCalleeSavedInfoValid", State.IsCalleeSavedInfoValid);
- Unsigned("localFrameSize", State.LocalFrameSize);
-
- const auto PrintPoints =
- [this, &Function](std::string_view Key,
- std::span<const MachineFrameSaveRestorePoint> Points) -> Expected<void> {
- if (Points.empty())
- return {};
- Output.append(" ");
- Output.append(Key);
- Output.append(":\n");
- for (const MachineFrameSaveRestorePoint &Point : Points) {
- auto Block = blockReference(Function, Point.Block);
- if (!Block)
- return std::unexpected(Block.error());
- Output.append(" - { point: ");
- Output.append(yamlScalar(*Block));
- if (!Point.Registers.empty()) {
- Output.append(", registers: [ ");
- for (std::size_t Index = 0; Index < Point.Registers.size(); ++Index) {
- if (Index != 0)
- Output.append(", ");
- auto Name = physicalRegister(Point.Registers[Index]);
- if (!Name)
- return std::unexpected(Name.error());
- Output.append(yamlScalar(*Name));
- }
- Output.append(" ]");
- }
- Output.append(" }\n");
- }
- return {};
- };
- if (auto Printed = PrintPoints("savePoint", State.SavePoints); !Printed)
- return Printed;
- return PrintPoints("restorePoint", State.RestorePoints);
-}
-
-Expected<void> MIRDocumentPrinter::printStackObjects(const MachineFunction &Function,
- const MIParserContext &Context, bool Fixed) {
- const auto &Slots = Fixed ? Context.FixedStackObjects : Context.StackObjects;
- if (Slots.empty()) {
- if (Options.Mode == MIPrintMode::Complete)
- Output.append(Fixed ? "fixedStack: []\n" : "stack: []\n");
- return {};
- }
- Output.append(Fixed ? "fixedStack:\n" : "stack:\n");
- for (const MIFrameObjectSlot &Slot : Slots) {
- const StackObject *Object = Function.frameInfo().object(Slot.Index);
- const StackObjectDesc *Description = Object == nullptr ? nullptr : Object->description();
- if (Description == nullptr)
- return makeUnexpected(ErrorCode::VerificationError, "MIR stack slot is a tombstone");
- if (Description->IsDead || Description->AddressTaken || !Description->Lifetime.empty() ||
- Description->TargetFlags != 0) {
- return makeUnexpected(ErrorCode::Unsupported,
- "stack object contains state not represented by fixed LLVM MIR");
- }
-
- std::string_view Type;
- switch (Description->Kind) {
- case StackObjectKind::Default:
- case StackObjectKind::FixedABI:
- Type = "default";
- break;
- case StackObjectKind::FixedSpillSlot:
- case StackObjectKind::SpillSlot:
- Type = "spill-slot";
- break;
- case StackObjectKind::VariableSized:
- Type = "variable-sized";
- break;
- case StackObjectKind::DynamicAlloca:
- case StackObjectKind::CalleeSaved:
- case StackObjectKind::EmergencySpill:
- case StackObjectKind::Target:
- case StackObjectKind::Count:
- return makeUnexpected(ErrorCode::Unsupported,
- "stack object kind has no fixed LLVM MIR representation");
- }
-
- Output.append(" - { id: ");
- Output.append(std::to_string(Slot.TextID));
- if (!Fixed && Description->AllocaName) {
- Output.append(", name: ");
- Output.append(yamlScalar(*Description->AllocaName));
- }
- if (Options.Mode == MIPrintMode::Complete || Type != "default") {
- Output.append(", type: ");
- Output.append(Type);
- }
- if (Options.Mode == MIPrintMode::Complete || Description->Offset.value_or(0) != 0) {
- Output.append(", offset: ");
- Output.append(std::to_string(Description->Offset.value_or(0)));
- }
- if (Description->Kind != StackObjectKind::VariableSized) {
- Output.append(", size: ");
- Output.append(std::to_string(Description->Size.value_or(0)));
- }
- if (Options.Mode == MIPrintMode::Complete || Description->Align.bytes() != 1) {
- Output.append(", alignment: ");
- Output.append(std::to_string(Description->Align.bytes()));
- }
- if (Options.Mode == MIPrintMode::Complete || Description->Stack != StackID::defaultStack()) {
- Output.append(", stack-id: ");
- Output.append(getStackIDName(Description->Stack));
- }
- if (Fixed && Description->Kind != StackObjectKind::FixedSpillSlot) {
- if (Options.Mode == MIPrintMode::Complete || Description->IsImmutable)
- Output.append(Description->IsImmutable ? ", isImmutable: true" : ", isImmutable: false");
- if (Options.Mode == MIPrintMode::Complete || Description->IsAliased)
- Output.append(Description->IsAliased ? ", isAliased: true" : ", isAliased: false");
- } else if (!Fixed && (Description->IsImmutable || Description->IsAliased)) {
- return makeUnexpected(ErrorCode::Unsupported,
- "ordinary stack object immutable/aliased state is not serialized");
- }
- if (Description->CalleeSavedRegister) {
- auto Name = physicalRegister(*Description->CalleeSavedRegister);
- if (!Name)
- return std::unexpected(Name.error());
- Output.append(", callee-saved-register: ");
- Output.append(yamlScalar(*Name));
- }
- if (Options.Mode == MIPrintMode::Complete || !Description->CalleeSavedRestored) {
- Output.append(Description->CalleeSavedRestored ? ", callee-saved-restored: true"
- : ", callee-saved-restored: false");
- }
- if (!Fixed && Description->LocalOffset) {
- Output.append(", local-offset: ");
- Output.append(std::to_string(*Description->LocalOffset));
- }
- if (Description->DebugInfo) {
- const MachineVariableDebugInfo &Debug = *Description->DebugInfo;
- if (Debug.Variable) {
- auto Value = metadataReference(*Debug.Variable, Context);
- if (!Value)
- return std::unexpected(Value.error());
- Output.append(", debug-info-variable: ");
- Output.append(yamlScalar(*Value));
- }
- if (Debug.Expression) {
- auto Value = metadataValue(*Debug.Expression, Context);
- if (!Value)
- return std::unexpected(Value.error());
- Output.append(", debug-info-expression: ");
- Output.append(yamlScalar(*Value));
- }
- if (Debug.Location) {
- auto Value = debugLocation(*Debug.Location, Context);
- if (!Value)
- return std::unexpected(Value.error());
- Output.append(", debug-info-location: ");
- Output.append(yamlScalar(*Value));
- }
- }
- Output.append(" }\n");
- }
- return {};
-}
-
-Expected<void> MIRDocumentPrinter::printConstants(const MachineFunction &Function,
- const MIParserContext &Context) {
- if (Context.ConstantPoolItems.empty()) {
- if (Options.Mode == MIPrintMode::Complete)
- Output.append("constants: []\n");
- return {};
- }
- Output.append("constants:\n");
- for (const MIConstantPoolSlot &Slot : Context.ConstantPoolItems) {
- const MachineConstantPoolEntry *Entry = Function.constantPool().entry(Slot.Index);
- const MachineConstantPoolEntryDesc *Description =
- Entry == nullptr ? nullptr : Entry->description();
- if (Description == nullptr)
- return makeUnexpected(ErrorCode::VerificationError, "constant-pool slot is a tombstone");
- if (Description->Merge != ConstantPoolMergeKind::Never ||
- Description->Relocation != ConstantPoolRelocationMode::None ||
- Description->Placement != ConstantPoolPlacement{}) {
- return makeUnexpected(ErrorCode::Unsupported,
- "constant-pool placement state is not represented by fixed LLVM MIR");
- }
-
- bool IsTargetSpecific = false;
- Expected<std::string> Value =
- makeUnexpected(ErrorCode::Unsupported, "constant-pool kind is not MIR serializable");
- if (const auto *Generic = std::get_if<GenericConstantData>(&Description->Value)) {
- Value = printLLVMConstant(*Generic);
- } else if (const auto *TargetConstantValue = std::get_if<TargetConstant>(&Description->Value)) {
- IsTargetSpecific = true;
- Value = Target.mirText().printTargetConstant(*TargetConstantValue);
- }
- if (!Value)
- return std::unexpected(Value.error());
-
- Output.append(" - id: ");
- Output.append(std::to_string(Slot.TextID));
- Output.append("\n value: ");
- Output.append(yamlScalar(*Value));
- Output.append("\n alignment: ");
- Output.append(std::to_string(Description->Align.bytes()));
- Output.push_back('\n');
- if (IsTargetSpecific)
- Output.append(" isTargetSpecific: true\n");
- }
- return {};
-}
-
-Expected<void> MIRDocumentPrinter::printJumpTable(const MachineFunction &Function,
- const MIParserContext &Context) {
- if (Context.JumpTables.empty())
- return {};
- const MachineJumpTableEntry *FirstEntry =
- Function.jumpTableInfo().table(Context.JumpTables.front().Index);
- const MachineJumpTableDesc *First = FirstEntry == nullptr ? nullptr : FirstEntry->description();
- if (First == nullptr)
- return makeUnexpected(ErrorCode::VerificationError, "jump-table slot is not defined");
-
- std::string_view Kind;
- std::uint8_t ExpectedSize = 8;
- switch (First->Encoding) {
- case JumpTableEntryEncoding::BlockAddress:
- Kind = "block-address";
- break;
- case JumpTableEntryEncoding::GPRelative64BlockAddress:
- Kind = "gp-rel64-block-address";
- break;
- case JumpTableEntryEncoding::GPRelative32BlockAddress:
- Kind = "gp-rel32-block-address";
- ExpectedSize = 4;
- break;
- case JumpTableEntryEncoding::LabelDifference32:
- Kind = "label-difference32";
- ExpectedSize = 4;
- break;
- case JumpTableEntryEncoding::LabelDifference64:
- Kind = "label-difference64";
- break;
- case JumpTableEntryEncoding::Inline:
- Kind = "inline";
- ExpectedSize = 4;
- break;
- case JumpTableEntryEncoding::Custom32:
- Kind = "custom32";
- ExpectedSize = 4;
- break;
- case JumpTableEntryEncoding::Target:
- case JumpTableEntryEncoding::Count:
- return makeUnexpected(ErrorCode::Unsupported,
- "target jump-table encoding has no fixed LLVM MIR spelling");
- }
-
- Output.append("jumpTable:\n kind: ");
- Output.append(Kind);
- Output.append("\n entries:\n");
- for (const MIJumpTableSlot &Slot : Context.JumpTables) {
- const MachineJumpTableEntry *Entry = Function.jumpTableInfo().table(Slot.Index);
- const MachineJumpTableDesc *Description = Entry == nullptr ? nullptr : Entry->description();
- if (Description == nullptr)
- return makeUnexpected(ErrorCode::VerificationError, "jump-table slot is not defined");
- if (Description->Encoding != First->Encoding || Description->EntrySize != ExpectedSize ||
- Description->Align.bytes() != ExpectedSize ||
- Description->Placement != JumpTablePlacement{} || Description->TargetEncoding != 0) {
- return makeUnexpected(ErrorCode::Unsupported,
- "jump-table entries cannot share one fixed LLVM MIR kind");
- }
- Output.append(" - { id: ");
- Output.append(std::to_string(Slot.TextID));
- Output.append(", blocks: [ ");
- for (std::size_t Index = 0; Index < Description->Targets.size(); ++Index) {
- if (Index != 0)
- Output.append(", ");
- if (Description->Targets[Index] == nullptr)
- return makeUnexpected(ErrorCode::VerificationError,
- "jump-table entry contains a null block");
- auto Block = blockReference(Function, Description->Targets[Index]->stableID());
- if (!Block)
- return std::unexpected(Block.error());
- Output.append(yamlScalar(*Block));
- }
- Output.append(" ] }\n");
- }
- return {};
-}
-
-Expected<void> MIRDocumentPrinter::printMetadata(const MachineFunction &Function,
- const MIParserContext &Context) {
- if (Function.metadata().nodes().empty())
- return {};
- Output.append("machineMetadataNodes:\n");
- for (const MachineMetadataNode &Node : Function.metadata().nodes()) {
- auto NodeReference =
- metadataReference(MachineMetadataReference::machineFunction(Node.Slot), Context);
- if (!NodeReference)
- return std::unexpected(NodeReference.error());
- std::string Text = *NodeReference + " = ";
- if (Node.IsDistinct)
- Text.append("distinct ");
- Text.append("!{");
- for (std::size_t Index = 0; Index < Node.Elements.size(); ++Index) {
- if (Index != 0)
- Text.append(", ");
- const MachineMetadataElement &Element = Node.Elements[Index];
- if (Element.isNull()) {
- Text.append("null");
- } else if (const MachineMetadataReference *Reference = Element.reference()) {
- auto Printed = metadataReference(*Reference, Context);
- if (!Printed)
- return std::unexpected(Printed.error());
- Text.append(*Printed);
- } else if (const std::string *String = Element.string()) {
- Text.append("!\"");
- for (const char Character : *String) {
- if (Character == '\\' || Character == '"')
- Text.push_back('\\');
- Text.push_back(Character);
- }
- Text.push_back('"');
- } else {
- return makeUnexpected(ErrorCode::VerificationError,
- "machine metadata tuple contains an invalid element");
- }
- }
- Text.push_back('}');
- Output.append(" - ");
- Output.append(yamlScalar(Text));
- Output.push_back('\n');
- }
- return {};
-}
-
-Expected<void> MIRDocumentPrinter::printTargetState(const MachineFunction &Function) {
- if (!Function.targetState().hasValue())
- return {};
- auto Node = Target.mirText().printFunctionState(Function.targetState());
- if (!Node)
- return std::unexpected(Node.error());
- auto Text = targetNode(*Node);
- if (!Text)
- return std::unexpected(Text.error());
- Output.append("machineFunctionInfo: ");
- Output.append(*Text);
- Output.push_back('\n');
- return {};
-}
-
-Expected<void> MIRDocumentPrinter::printFunctionRecords(const MachineFunction &Function,
- const MIParserContext &Context) {
- const MachineFunctionRecords &Records = Function.records();
- if (!Records.entryValues().empty()) {
- Output.append("entry_values:\n");
- for (const MachineEntryValueInfo &Entry : Records.entryValues()) {
- auto RegisterName = physicalRegister(Entry.Reg);
- if (!RegisterName)
- return std::unexpected(RegisterName.error());
- if (!Entry.DebugInfo.Variable || !Entry.DebugInfo.Expression || !Entry.DebugInfo.Location) {
- return makeUnexpected(ErrorCode::Unsupported,
- "MIR entry value requires complete debug metadata");
- }
- auto Variable = metadataReference(*Entry.DebugInfo.Variable, Context);
- auto Expression = metadataValue(*Entry.DebugInfo.Expression, Context);
- auto Location = debugLocation(*Entry.DebugInfo.Location, Context);
- if (!Variable)
- return std::unexpected(Variable.error());
- if (!Expression)
- return std::unexpected(Expression.error());
- if (!Location)
- return std::unexpected(Location.error());
- Output.append(" - { entry-value-register: ");
- Output.append(yamlScalar(*RegisterName));
- Output.append(", debug-info-variable: ");
- Output.append(yamlScalar(*Variable));
- Output.append(", debug-info-expression: ");
- Output.append(yamlScalar(*Expression));
- Output.append(", debug-info-location: ");
- Output.append(yamlScalar(*Location));
- Output.append(" }\n");
- }
- } else if (Options.Mode == MIPrintMode::Complete) {
- Output.append("entry_values: []\n");
- }
-
- if (!Records.callSites().empty()) {
- Output.append("callSites:\n");
- for (const MachineCallSiteInfo &CallSite : Records.callSites()) {
- auto Location = instructionLocation(Function, CallSite.CallInstruction);
- if (!Location)
- return std::unexpected(Location.error());
- Output.append(" - { bb: ");
- Output.append(std::to_string(Location->Block));
- Output.append(", offset: ");
- Output.append(std::to_string(Location->Offset));
- if (!CallSite.ForwardedArguments.empty()) {
- Output.append(", fwdArgRegs: [ ");
- for (std::size_t Index = 0; Index < CallSite.ForwardedArguments.size(); ++Index) {
- if (Index != 0)
- Output.append(", ");
- const MachineCallSiteArgument &Argument = CallSite.ForwardedArguments[Index];
- auto RegisterName = physicalRegister(Argument.Reg);
- if (!RegisterName)
- return std::unexpected(RegisterName.error());
- Output.append("{ arg: ");
- Output.append(std::to_string(Argument.ArgumentNumber));
- Output.append(", reg: ");
- Output.append(yamlScalar(*RegisterName));
- Output.append(" }");
- }
- Output.append(" ]");
- }
- if (!CallSite.CalleeTypeIDs.empty()) {
- Output.append(", calleeTypeIds: [ ");
- for (std::size_t Index = 0; Index < CallSite.CalleeTypeIDs.size(); ++Index) {
- if (Index != 0)
- Output.append(", ");
- Output.append(std::to_string(CallSite.CalleeTypeIDs[Index]));
- }
- Output.append(" ]");
- }
- Output.append(" }\n");
- }
- } else if (Options.Mode == MIPrintMode::Complete) {
- Output.append("callSites: []\n");
- }
-
- if (!Records.debugValueSubstitutions().empty()) {
- Output.append("debugValueSubstitutions:\n");
- for (const MachineDebugValueSubstitution &Substitution : Records.debugValueSubstitutions()) {
- Output.append(" - { srcinst: ");
- Output.append(std::to_string(Substitution.Source.InstructionNumber));
- Output.append(", srcop: ");
- Output.append(std::to_string(Substitution.Source.OperandNumber));
- Output.append(", dstinst: ");
- Output.append(std::to_string(Substitution.Destination.InstructionNumber));
- Output.append(", dstop: ");
- Output.append(std::to_string(Substitution.Destination.OperandNumber));
- Output.append(", subreg: ");
- Output.append(std::to_string(Substitution.Subregister.value()));
- Output.append(" }\n");
- }
- } else if (Options.Mode == MIPrintMode::Complete) {
- Output.append("debugValueSubstitutions: []\n");
- }
-
- if (!Records.calledGlobals().empty()) {
- Output.append("calledGlobals:\n");
- for (const MachineCalledGlobal &Called : Records.calledGlobals()) {
- auto Location = instructionLocation(Function, Called.CallInstruction);
- if (!Location)
- return std::unexpected(Location.error());
- Output.append(" - { bb: ");
- Output.append(std::to_string(Location->Block));
- Output.append(", offset: ");
- Output.append(std::to_string(Location->Offset));
- Output.append(", callee: ");
- Output.append(yamlScalar(Called.Callee));
- Output.append(", flags: ");
- Output.append(std::to_string(Called.Flags));
- Output.append(" }\n");
- }
- }
-
- if (!Records.prefetchTargets().empty()) {
- Output.append("prefetch-targets: [ ");
- const auto Targets = Records.prefetchTargets();
- for (std::size_t Index = 0; Index < Targets.size(); ++Index) {
- if (Index != 0)
- Output.append(", ");
- const MachinePrefetchTarget &TargetValue = Targets[Index];
- std::string Text = "bb_id " + std::to_string(TargetValue.Block.BaseID) + ", " +
- std::to_string(TargetValue.Block.CloneID) + ", " +
- std::to_string(TargetValue.CallsiteIndex);
- Output.append(yamlScalar(Text));
- }
- Output.append(" ]\n");
- }
- return {};
-}
-
-Expected<void> MIRDocumentPrinter::printFunction(const MachineFunction &Function,
- const MIRFunctionDocumentInfo *) {
- if (Function.compilationStatus().state() != MachineCompilationState::Success) {
- return makeUnexpected(ErrorCode::Unsupported,
- "fixed LLVM MIR cannot serialize this compilation failure state");
- }
- auto Context = createBodyContext(Function);
- if (!Context)
- return std::unexpected(Context.error());
-
- Output.append("---\nname: ");
- Output.append(yamlScalar(Function.name()));
- Output.push_back('\n');
- const auto Alignment = Function.attributes().alignment();
- if (Options.Mode == MIPrintMode::Complete || Alignment) {
- Output.append("alignment: ");
- Output.append(std::to_string(Alignment ? Alignment->bytes() : 1U));
- Output.push_back('\n');
- }
-
- const MachineFunctionProperties &Properties = Function.properties();
- const MachineFunctionAttributes &Attributes = Function.attributes();
- printBoolean("exposesReturnsTwice",
- Attributes.has(MachineFunctionAttribute::ExposesReturnsTwice));
- printBoolean("legalized", Properties.has(MachineFunctionProperty::Legalized));
- printBoolean("regBankSelected", Properties.has(MachineFunctionProperty::RegBankSelected));
- printBoolean("selected", Properties.has(MachineFunctionProperty::Selected));
- printBoolean("failedISel", Properties.has(MachineFunctionProperty::FailedISel));
- printBoolean("tracksRegLiveness", Properties.has(MachineFunctionProperty::TracksLiveness));
- printBoolean("hasWinCFI", Attributes.has(MachineFunctionAttribute::HasWinCFI));
- printBoolean("noPhis", Properties.has(MachineFunctionProperty::NoPHIs), true);
- printBoolean("isSSA", Properties.has(MachineFunctionProperty::IsSSA), true);
- printBoolean("noVRegs", Properties.has(MachineFunctionProperty::NoVRegs), true);
- printBoolean("hasFakeUses", Attributes.has(MachineFunctionAttribute::HasFakeUses), true);
- printBoolean("callsEHReturn", Attributes.has(MachineFunctionAttribute::CallsEHReturn));
- printBoolean("callsUnwindInit", Attributes.has(MachineFunctionAttribute::CallsUnwindInit));
- printBoolean("hasEHContTarget", Attributes.has(MachineFunctionAttribute::HasEHContTarget));
- printBoolean("hasEHScopes", Attributes.has(MachineFunctionAttribute::HasEHScopes));
- printBoolean("hasEHFunclets", Attributes.has(MachineFunctionAttribute::HasEHFunclets));
- printBoolean("isOutlined", Attributes.has(MachineFunctionAttribute::IsOutlined));
- printBoolean("debugInstrRef", Attributes.has(MachineFunctionAttribute::DebugInstrRef));
- printBoolean("failsVerification", Properties.has(MachineFunctionProperty::FailsVerification));
- printBoolean("tracksDebugUserValues",
- Attributes.has(MachineFunctionAttribute::TracksDebugUserValues));
-
- if (auto Printed = printRegisters(Function, *Context); !Printed)
- return Printed;
- if (auto Printed = printFrameInfo(Function, *Context); !Printed)
- return Printed;
- if (auto Printed = printStackObjects(Function, *Context, true); !Printed)
- return Printed;
- if (auto Printed = printStackObjects(Function, *Context, false); !Printed)
- return Printed;
- if (auto Printed = printFunctionRecords(Function, *Context); !Printed)
- return Printed;
- if (auto Printed = printConstants(Function, *Context); !Printed)
- return Printed;
- if (auto Printed = printTargetState(Function); !Printed)
- return Printed;
- if (auto Printed = printJumpTable(Function, *Context); !Printed)
- return Printed;
- if (auto Printed = printMetadata(Function, *Context); !Printed)
- return Printed;
-
- auto Body =
- printMachineFunctionBody(Function, Target, *Context, MIPrinterOptions{.Mode = Options.Mode});
- if (!Body)
- return std::unexpected(Body.error());
- Output.append("body: |\n");
- appendBlockScalar(Output, *Body, 2);
- Output.append("...\n");
- return {};
-}
-
-Expected<std::string> MIRDocumentPrinter::run() {
- auto UnitVerification = Document.unit().verify();
- if (!UnitVerification)
- return std::unexpected(UnitVerification.error());
- if (Document.unit().functionCount() == 0) {
- return makeUnexpected(ErrorCode::InvalidArgument,
- "MIR document must contain at least one machine function");
- }
-
- for (const MIRFunctionDocumentInfo &Info : Document.functionDocumentInfo()) {
- if (Document.unit().function(Info.Function) == nullptr) {
- return makeUnexpected(ErrorCode::VerificationError,
- "MIR document info references an unknown machine function");
- }
- }
-
- for (std::size_t Index = 0; Index < Document.unit().functionCount(); ++Index) {
- const MachineFunction *Function = Document.unit().functionAt(Index);
- if (Function == nullptr) {
- return makeUnexpected(ErrorCode::VerificationError,
- "compilation unit contains a null machine function");
- }
- auto ReferenceVerification =
- verifyMIRDocumentReferences(Document, *Function, Options.EmitEmbeddedIR);
- if (!ReferenceVerification)
- return std::unexpected(ReferenceVerification.error());
- auto LayoutVerification = verifyOmittedEmbeddedIRLayout(*Function);
- if (!LayoutVerification)
- return std::unexpected(LayoutVerification.error());
- }
-
- if (Options.EmitEmbeddedIR && Document.embeddedIR() != nullptr) {
- Output.append("--- |\n");
- appendBlockScalar(Output, Document.embeddedIR()->source().text(), 2);
- Output.append("...\n");
- }
- for (std::size_t Index = 0; Index < Document.unit().functionCount(); ++Index) {
- const MachineFunction *Function = Document.unit().functionAt(Index);
- if (Function == nullptr)
- return makeUnexpected(ErrorCode::VerificationError,
- "compilation unit contains a null machine function");
- if (auto Printed = printFunction(*Function, documentInfo(*Function)); !Printed)
- return std::unexpected(Printed.error());
- }
- return std::move(Output);
-}
-
-} // namespace
-
-Expected<std::string> printMIRDocument(const MIRDocument &Document, const TargetDescription &Target,
- MIRDocumentPrinterOptions Options) {
- return MIRDocumentPrinter(Document, Target, std::move(Options)).run();
-}
-
-} // namespace rcc::mir
diff --git a/backend/lib/MIR/MIRDocumentReferenceValidation.cpp b/backend/lib/MIR/MIRDocumentReferenceValidation.cpp
deleted file mode 100644
index 69b0dd1..0000000
--- a/backend/lib/MIR/MIRDocumentReferenceValidation.cpp
+++ /dev/null
@@ -1,426 +0,0 @@
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#include "MIRDocumentReferenceValidation.h"
-
-#include "rcc/CodeGen/MachineBasicBlock.h"
-#include "rcc/CodeGen/MachineConstantPool.h"
-#include "rcc/CodeGen/MachineFrameInfo.h"
-#include "rcc/CodeGen/MachineFunction.h"
-#include "rcc/CodeGen/MachineFunctionRecords.h"
-#include "rcc/CodeGen/MachineInstr.h"
-#include "rcc/CodeGen/MachineMetadata.h"
-#include "rcc/MIR/MIRDocument.h"
-
-#include <algorithm>
-#include <array>
-#include <initializer_list>
-#include <optional>
-#include <ranges>
-#include <string>
-#include <string_view>
-#include <variant>
-
-namespace rcc::mir {
-
-Expected<void> verifyMIRDocumentReferences(const MIRDocument &Document,
- const MachineFunction &Function, bool UseEmbeddedIR) {
- const EmbeddedIRReferenceIndex *Embedded = UseEmbeddedIR ? Document.embeddedIR() : nullptr;
- const EmbeddedIRFunction *IRFunction = nullptr;
- if (Embedded != nullptr) {
- if (Function.name().empty()) {
- std::size_t AnonymousOrdinal = 0;
- bool FoundFunction = false;
- for (std::size_t Index = 0; Index < Document.unit().functionCount(); ++Index) {
- const MachineFunction *Candidate = Document.unit().functionAt(Index);
- if (Candidate == &Function) {
- FoundFunction = true;
- break;
- }
- if (Candidate != nullptr && Candidate->name().empty())
- ++AnonymousOrdinal;
- }
- if (FoundFunction)
- IRFunction = Embedded->unnamedFunction(AnonymousOrdinal);
- } else {
- IRFunction = Embedded->function(Function.name());
- }
- if (IRFunction == nullptr) {
- return makeUnexpected(ErrorCode::VerificationError,
- "machine function has no matching embedded LLVM IR function");
- }
- }
-
- const auto ValidateGlobal = [Embedded](std::string_view Name,
- std::string_view Description) -> Expected<void> {
- if (Embedded == nullptr) {
- return makeUnexpected(ErrorCode::VerificationError,
- std::string(Description) +
- " requires embedded LLVM IR to resolve global '" +
- std::string(Name) + "'");
- }
- if (Embedded->hasGlobalValue(Name))
- return {};
- return makeUnexpected(ErrorCode::VerificationError,
- std::string(Description) +
- " references an undefined embedded LLVM IR global '" +
- std::string(Name) + "'");
- };
- const auto ValidateBlockAddress = [Embedded](std::string_view FunctionName,
- std::string_view BlockName,
- std::string_view Description) -> Expected<void> {
- if (Embedded == nullptr) {
- return makeUnexpected(ErrorCode::VerificationError,
- std::string(Description) +
- " requires embedded LLVM IR to resolve a block address");
- }
- const EmbeddedIRFunction *AddressFunction = Embedded->function(FunctionName);
- if (AddressFunction == nullptr) {
- return makeUnexpected(ErrorCode::VerificationError,
- std::string(Description) +
- " references an undefined embedded LLVM IR function '" +
- std::string(FunctionName) + "'");
- }
- if (AddressFunction->block(BlockName) == nullptr) {
- return makeUnexpected(
- ErrorCode::VerificationError,
- std::string(Description) + " references an undefined embedded LLVM IR block '" +
- std::string(BlockName) + "' in function '" + std::string(FunctionName) + "'");
- }
- return {};
- };
- const auto ValidateIRValue = [IRFunction](std::string_view Name,
- std::string_view Description) -> Expected<void> {
- if (IRFunction == nullptr) {
- return makeUnexpected(ErrorCode::VerificationError,
- std::string(Description) +
- " requires embedded LLVM IR to resolve IR value '%" +
- std::string(Name) + "'");
- }
- if (IRFunction->value(Name) != nullptr)
- return {};
- return makeUnexpected(ErrorCode::VerificationError,
- std::string(Description) +
- " references an undefined embedded LLVM IR value '%" +
- std::string(Name) + "'");
- };
- const auto ValidateMetadata = [Embedded](MachineMetadataReference Reference,
- std::initializer_list<std::string_view> ExpectedTypes,
- std::string_view Description) -> Expected<void> {
- if (!Reference.isValid()) {
- return makeUnexpected(ErrorCode::VerificationError,
- std::string(Description) + " has an invalid metadata reference");
- }
- if (Reference.scope() != MachineMetadataScope::EmbeddedIR)
- return {};
- if (Embedded == nullptr) {
- return makeUnexpected(ErrorCode::VerificationError,
- std::string(Description) + " requires embedded LLVM IR metadata !" +
- std::to_string(Reference.slot()));
- }
- const EmbeddedIRMetadataDefinition *Definition =
- Embedded->metadata(std::to_string(Reference.slot()));
- if (Definition == nullptr) {
- return makeUnexpected(ErrorCode::VerificationError,
- std::string(Description) +
- " references undefined embedded LLVM IR metadata !" +
- std::to_string(Reference.slot()));
- }
- if (ExpectedTypes.size() == 0 ||
- std::ranges::find(ExpectedTypes, Definition->DynamicType) != ExpectedTypes.end()) {
- return {};
- }
- std::string Expected;
- for (const std::string_view Type : ExpectedTypes) {
- if (!Expected.empty())
- Expected.append(" or ");
- Expected.append(Type);
- }
- return makeUnexpected(ErrorCode::VerificationError,
- std::string(Description) + " requires " + Expected + " metadata, but !" +
- std::to_string(Reference.slot()) + " is " +
- (Definition->DynamicType.empty()
- ? std::string("an unclassified metadata node")
- : Definition->DynamicType));
- };
- const auto ValidateMetadataValue =
- [&ValidateMetadata](const MachineMetadataValue &Value,
- std::string_view Description) -> Expected<void> {
- const MachineMetadataReference *Reference = Value.reference();
- return Reference == nullptr ? Expected<void>{} : ValidateMetadata(*Reference, {}, Description);
- };
- const auto ValidateLocation =
- [&ValidateMetadata](const MachineDebugLocation &Location,
- std::string_view Description) -> Expected<void> {
- if (const MachineMetadataReference *Reference = Location.reference())
- return ValidateMetadata(*Reference, {"DILocation"}, Description);
- const MachineDILocation *Inline = Location.inlineLocation();
- if (Inline == nullptr)
- return {};
- for (const MachineDILocationFrame &Frame : Inline->inlineChain()) {
- auto Verification =
- ValidateMetadata(Frame.Scope, {"DISubprogram", "DILexicalBlock", "DILexicalBlockFile"},
- "inline DILocation scope");
- if (!Verification)
- return Verification;
- }
- if (Inline->terminalInlinedAt()) {
- return ValidateMetadata(*Inline->terminalInlinedAt(), {"DILocation"},
- "inline DILocation inlinedAt");
- }
- return {};
- };
- const auto ValidateVariableDebugInfo =
- [&ValidateMetadata, &ValidateMetadataValue,
- &ValidateLocation](const MachineVariableDebugInfo &DebugInfo) -> Expected<void> {
- if (DebugInfo.Variable) {
- auto Verification =
- ValidateMetadata(*DebugInfo.Variable, {"DILocalVariable"}, "debug variable");
- if (!Verification)
- return Verification;
- }
- if (DebugInfo.Expression) {
- if (const MachineMetadataReference *Reference = DebugInfo.Expression->reference();
- Reference != nullptr) {
- auto Verification = ValidateMetadata(*Reference, {"DIExpression"}, "debug expression");
- if (!Verification)
- return Verification;
- }
- auto Verification = ValidateMetadataValue(*DebugInfo.Expression, "debug expression");
- if (!Verification)
- return Verification;
- }
- if (DebugInfo.Location)
- return ValidateLocation(*DebugInfo.Location, "debug location");
- return {};
- };
- const auto ValidateGenericType = [Embedded](const auto &Self,
- const GenericConstantType &Type) -> Expected<void> {
- if (Type.Kind == GenericConstantTypeKind::Named) {
- if (Embedded == nullptr) {
- return makeUnexpected(
- ErrorCode::VerificationError,
- "generic constant type requires embedded LLVM IR to resolve named type '%" + Type.Name +
- "'");
- }
- if (Embedded->typeDefinition(Type.Name) == nullptr) {
- return makeUnexpected(
- ErrorCode::VerificationError,
- "generic constant type references an undefined embedded LLVM IR named type '%" +
- Type.Name + "'");
- }
- }
- for (const GenericConstantType &Element : Type.Elements) {
- auto Verification = Self(Self, Element);
- if (!Verification)
- return Verification;
- }
- return {};
- };
- const auto ValidateGenericConstant =
- [&ValidateBlockAddress, &ValidateGenericType,
- &ValidateGlobal](const auto &Self, const GenericConstantData &Constant) -> Expected<void> {
- auto TypeVerification = ValidateGenericType(ValidateGenericType, Constant.Type);
- if (!TypeVerification)
- return TypeVerification;
- if (Constant.SourceElementType) {
- TypeVerification = ValidateGenericType(ValidateGenericType, *Constant.SourceElementType);
- if (!TypeVerification)
- return TypeVerification;
- }
- switch (Constant.Kind) {
- case GenericConstantKind::GlobalAddress:
- case GenericConstantKind::DSOLocalEquivalent:
- case GenericConstantKind::NoCFI:
- if (auto Verification = ValidateGlobal(Constant.Symbol, "generic constant"); !Verification)
- return Verification;
- break;
- case GenericConstantKind::BlockAddress:
- if (auto Verification = ValidateBlockAddress(Constant.Symbol, Constant.Block,
- "generic constant blockaddress");
- !Verification) {
- return Verification;
- }
- break;
- default:
- break;
- }
- for (const GenericConstantData &Element : Constant.Elements) {
- auto Verification = Self(Self, Element);
- if (!Verification)
- return Verification;
- }
- return {};
- };
-
- for (const MachineBasicBlock *Block = Function.firstBlock(); Block != nullptr;
- Block = Block->next()) {
- if (const auto Name = Block->attributes().irBlockName(); Name) {
- if (IRFunction == nullptr) {
- return makeUnexpected(ErrorCode::VerificationError,
- "machine basic block requires embedded LLVM IR to resolve block '" +
- std::string(*Name) + "'");
- }
- if (IRFunction->block(*Name) == nullptr) {
- return makeUnexpected(
- ErrorCode::VerificationError,
- "machine basic block references an undefined embedded LLVM IR block '" +
- std::string(*Name) + "'");
- }
- }
- }
- for (const StackObject &Object : Function.frameInfo().objects()) {
- const StackObjectDesc *Description = Object.description();
- if (Description == nullptr || !Description->AllocaName)
- continue;
- if (IRFunction == nullptr) {
- return makeUnexpected(ErrorCode::VerificationError,
- "stack object requires embedded LLVM IR to resolve alloca '" +
- *Description->AllocaName + "'");
- }
- if (IRFunction->alloca(*Description->AllocaName) == nullptr) {
- return makeUnexpected(ErrorCode::VerificationError,
- "stack object references an undefined embedded LLVM IR alloca '" +
- *Description->AllocaName + "'");
- }
- }
-
- for (const MachineConstantPoolEntry &Entry : Function.constantPool().entries()) {
- const MachineConstantPoolEntryDesc *Description = Entry.description();
- if (Description == nullptr)
- continue;
- if (const auto *Generic = std::get_if<GenericConstantData>(&Description->Value)) {
- auto Verification = ValidateGenericConstant(ValidateGenericConstant, *Generic);
- if (!Verification)
- return Verification;
- } else if (const auto *Address = std::get_if<BlockAddressConstant>(&Description->Value)) {
- auto Verification = ValidateBlockAddress(Address->FunctionName, Address->BlockName,
- "constant-pool block address");
- if (!Verification)
- return Verification;
- }
- }
-
- for (const MachineMetadataNode &Node : Function.metadata().nodes()) {
- for (const MachineMetadataElement &Element : Node.Elements) {
- const MachineMetadataReference *Reference = Element.reference();
- if (Reference == nullptr)
- continue;
- auto Verification = ValidateMetadata(*Reference, {}, "machine metadata node");
- if (!Verification)
- return Verification;
- }
- }
-
- for (const StackObject &Object : Function.frameInfo().objects()) {
- const StackObjectDesc *Description = Object.description();
- if (Description == nullptr || !Description->DebugInfo)
- continue;
- auto Verification = ValidateVariableDebugInfo(*Description->DebugInfo);
- if (!Verification)
- return Verification;
- }
- for (const MachineEntryValueInfo &Entry : Function.records().entryValues()) {
- auto Verification = ValidateVariableDebugInfo(Entry.DebugInfo);
- if (!Verification)
- return Verification;
- }
- for (const MachineCalledGlobal &Called : Function.records().calledGlobals()) {
- auto Verification = ValidateGlobal(Called.Callee, "called-global record");
- if (!Verification)
- return Verification;
- }
-
- for (const MachineBasicBlock *Block = Function.firstBlock(); Block != nullptr;
- Block = Block->next()) {
- for (const MachineInstr *Instruction = Block->firstInstruction(); Instruction != nullptr;
- Instruction = Instruction->next()) {
- if (Instruction->debugLocation()) {
- auto Verification =
- ValidateLocation(*Instruction->debugLocation(), "instruction debug location");
- if (!Verification)
- return Verification;
- }
- const std::array InstructionMetadata{
- Instruction->heapAllocMarker(),
- Instruction->pcSections(),
- Instruction->mmra(),
- };
- for (const auto &Reference : InstructionMetadata) {
- if (!Reference)
- continue;
- auto Verification = ValidateMetadata(*Reference, {}, "instruction metadata attachment");
- if (!Verification)
- return Verification;
- }
-
- for (const MachineOperand &Operand : Instruction->operands()) {
- if (const auto *Global = Operand.getIf<GlobalSymbolOperand>(); Global != nullptr) {
- auto Verification = ValidateGlobal(Global->Name, "global-symbol operand");
- if (!Verification)
- return Verification;
- }
- if (const auto *Address = Operand.getIf<BlockAddressOperand>(); Address != nullptr) {
- auto Verification = ValidateBlockAddress(Address->FunctionName, Address->BlockName,
- "block-address operand");
- if (!Verification)
- return Verification;
- }
- if (const auto *Metadata = Operand.getIf<MetadataOperand>(); Metadata != nullptr) {
- auto Verification = ValidateMetadataValue(Metadata->Value, "metadata operand");
- if (!Verification)
- return Verification;
- }
- }
-
- for (const MachineMemOperand *Memory : Instruction->memOperands()) {
- const MachineMemOperandDesc &Description = Memory->description();
- const std::optional<std::string_view> BaseName = Description.Base.symbolName();
- switch (Description.Base.kind()) {
- case MemoryBase::Kind::GlobalSymbol:
- case MemoryBase::Kind::CallEntryGlobal:
- if (BaseName) {
- auto Verification = ValidateGlobal(*BaseName, "machine memory base");
- if (!Verification)
- return Verification;
- }
- break;
- case MemoryBase::Kind::IRValue:
- if (BaseName) {
- auto Verification = ValidateIRValue(*BaseName, "machine memory base");
- if (!Verification)
- return Verification;
- }
- break;
- default:
- break;
- }
- const std::array MetadataReferences{
- Description.TBAA, Description.AliasScope,
- Description.NoAlias, Description.NoAliasAddressSpace,
- Description.RangeMetadata,
- };
- for (const auto &Reference : MetadataReferences) {
- if (!Reference)
- continue;
- auto Verification = ValidateMetadata(*Reference, {}, "machine memory operand metadata");
- if (!Verification)
- return Verification;
- }
- if (Description.SyncScope.value() == 0)
- continue;
- const auto Scope =
- std::ranges::find_if(Document.syncScopes(), [&Description](const auto &Slot) {
- return Slot.ID == Description.SyncScope;
- });
- if (Scope == Document.syncScopes().end()) {
- return makeUnexpected(
- ErrorCode::VerificationError,
- "machine memory operand synchronization scope has no document name");
- }
- }
- }
- }
- return {};
-}
-
-} // namespace rcc::mir
diff --git a/backend/lib/MIR/MIRDocumentReferenceValidation.h b/backend/lib/MIR/MIRDocumentReferenceValidation.h
deleted file mode 100644
index 2ab6291..0000000
--- a/backend/lib/MIR/MIRDocumentReferenceValidation.h
+++ /dev/null
@@ -1,25 +0,0 @@
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-// The private header has no stable include-root-relative guard name.
-// NOLINTNEXTLINE(llvm-header-guard)
-#ifndef RCC_LIB_MIR_MIRDOCUMENTREFERENCEVALIDATION_H
-#define RCC_LIB_MIR_MIRDOCUMENTREFERENCEVALIDATION_H
-
-#include "rcc/Support/Error.h"
-
-namespace rcc {
-
-class MachineFunction;
-
-namespace mir {
-
-class MIRDocument;
-
-[[nodiscard]] Expected<void> verifyMIRDocumentReferences(const MIRDocument &Document,
- const MachineFunction &Function,
- bool UseEmbeddedIR);
-
-} // namespace mir
-} // namespace rcc
-
-#endif // RCC_LIB_MIR_MIRDOCUMENTREFERENCEVALIDATION_H
diff --git a/backend/lib/MIR/MIRFrameParser.cpp b/backend/lib/MIR/MIRFrameParser.cpp
deleted file mode 100644
index 4367fbd..0000000
--- a/backend/lib/MIR/MIRFrameParser.cpp
+++ /dev/null
@@ -1,467 +0,0 @@
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#include "MIRDocumentParser.h"
-#include "MIRDocumentReferenceValidation.h"
-#include "MIRMetadataTextParser.h"
-#include "MIRParserUtilities.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 {
-
-using parser_detail::parseBool;
-using parser_detail::parsePrefixedID;
-using parser_detail::parseSigned;
-using parser_detail::parseUnsigned;
-using parser_detail::parseUnsigned32;
-using parser_detail::trim;
-
-bool MIRDocumentParser::parseStackObjects(const yaml::Node &Root, MachineFunction &Function,
- MIParserContext &Context) {
- auto ParseList = [this, &Function, &Context](const yaml::Node &List, bool Fixed) -> bool {
- if (requireKind(List, yaml::NodeKind::Sequence,
- Fixed ? "fixed stack objects" : "stack objects")) {
- return true;
- }
- constexpr std::array FixedKeys{
- std::string_view{"id"},
- std::string_view{"type"},
- std::string_view{"offset"},
- std::string_view{"size"},
- std::string_view{"alignment"},
- std::string_view{"stack-id"},
- std::string_view{"isImmutable"},
- std::string_view{"isAliased"},
- std::string_view{"callee-saved-register"},
- std::string_view{"callee-saved-restored"},
- std::string_view{"debug-info-variable"},
- std::string_view{"debug-info-expression"},
- std::string_view{"debug-info-location"},
- };
- constexpr std::array StackKeys{
- std::string_view{"id"},
- std::string_view{"name"},
- std::string_view{"type"},
- std::string_view{"offset"},
- std::string_view{"size"},
- std::string_view{"alignment"},
- std::string_view{"stack-id"},
- std::string_view{"callee-saved-register"},
- std::string_view{"callee-saved-restored"},
- std::string_view{"local-offset"},
- std::string_view{"debug-info-variable"},
- std::string_view{"debug-info-expression"},
- std::string_view{"debug-info-location"},
- };
- std::unordered_set<std::uint32_t> IDs;
- for (const yaml::NodeID ElementID : List.sequence()) {
- const yaml::Node *Element = node(ElementID);
- if (Element == nullptr || requireKind(*Element, yaml::NodeKind::Mapping, "stack object") ||
- (Fixed ? validateKeys(*Element, FixedKeys, "fixed stack object")
- : validateKeys(*Element, StackKeys, "stack object"))) {
- return true;
- }
- const yaml::Node *IDNode = mappingValue(*Element, "id");
- if (IDNode == nullptr)
- return fail(Element->range(), "stack object requires 'id'");
- std::uint64_t WideID = 0;
- if (unsignedValue(*IDNode, WideID, "a 32-bit stack object ID") ||
- WideID > std::numeric_limits<std::uint32_t>::max()) {
- return true;
- }
- const auto TextID = static_cast<std::uint32_t>(WideID);
- if (!IDs.insert(TextID).second)
- return fail(IDNode->range(), "duplicate stack object ID");
-
- StackObjectDesc Description;
- Description.Kind = Fixed ? StackObjectKind::FixedABI : StackObjectKind::Default;
- Description.Offset = 0;
- if (const yaml::Node *NameNode = mappingValue(*Element, "name")) {
- std::string_view Name;
- if (scalar(*NameNode, Name, "stack object name"))
- return true;
- if (!Name.empty())
- Description.AllocaName = std::string(Name);
- }
- if (const yaml::Node *TypeNode = mappingValue(*Element, "type")) {
- std::string_view Type;
- if (scalar(*TypeNode, Type, "stack object type"))
- return true;
- if (Type == "default")
- Description.Kind = Fixed ? StackObjectKind::FixedABI : StackObjectKind::Default;
- else if (Type == "spill-slot")
- Description.Kind = Fixed ? StackObjectKind::FixedSpillSlot : StackObjectKind::SpillSlot;
- else if (!Fixed && Type == "variable-sized")
- Description.Kind = StackObjectKind::VariableSized;
- else
- return fail(TypeNode->range(), "unknown stack object type '" + std::string(Type) + "'");
- }
- if (const yaml::Node *OffsetNode = mappingValue(*Element, "offset")) {
- std::int64_t Offset = 0;
- if (signedValue(*OffsetNode, Offset, "a signed stack object offset"))
- return true;
- Description.Offset = Offset;
- } else if (Fixed) {
- Description.Offset = 0;
- }
- if (Description.Kind != StackObjectKind::VariableSized) {
- const yaml::Node *SizeNode = mappingValue(*Element, "size");
- if (SizeNode == nullptr) {
- if (!Fixed)
- return fail(Element->range(), "fixed-size stack object requires 'size'");
- Description.Size = 0;
- } else {
- std::uint64_t Size = 0;
- if (unsignedValue(*SizeNode, Size, "an unsigned stack object size"))
- return true;
- Description.Size = Size;
- }
- }
- if (const yaml::Node *AlignmentNode = mappingValue(*Element, "alignment")) {
- if (alignment(*AlignmentNode, Description.Align, true))
- return true;
- }
- if (const yaml::Node *StackIDNode = mappingValue(*Element, "stack-id")) {
- std::string_view Name;
- if (scalar(*StackIDNode, Name, "stack ID"))
- return true;
- const auto Stack = parseStackIDName(Name);
- if (!Stack)
- return fail(StackIDNode->range(), "unknown stack ID '" + std::string(Name) + "'");
- Description.Stack = *Stack;
- }
- if (const yaml::Node *ImmutableNode = mappingValue(*Element, "isImmutable")) {
- if (boolean(*ImmutableNode, Description.IsImmutable))
- return true;
- }
- if (const yaml::Node *AliasedNode = mappingValue(*Element, "isAliased")) {
- if (boolean(*AliasedNode, Description.IsAliased))
- return true;
- }
- if (const yaml::Node *SavedNode = mappingValue(*Element, "callee-saved-register")) {
- std::string_view SavedName;
- if (scalar(*SavedNode, SavedName, "physical register"))
- return true;
- if (!trim(SavedName).empty()) {
- PhysicalRegister Reg;
- if (physicalRegister(SavedName, SavedNode->range(), Reg))
- return true;
- Description.CalleeSavedRegister = Reg;
- }
- }
- if (const yaml::Node *RestoredNode = mappingValue(*Element, "callee-saved-restored")) {
- if (boolean(*RestoredNode, Description.CalleeSavedRestored))
- return true;
- }
- if (const yaml::Node *LocalNode = mappingValue(*Element, "local-offset")) {
- std::int64_t Offset = 0;
- if (signedValue(*LocalNode, Offset, "a signed local frame offset"))
- return true;
- Description.LocalOffset = Offset;
- }
- MachineVariableDebugInfo DebugInfo;
- if (parseVariableDebugInfo(*Element, Function, DebugInfo))
- return true;
- if (DebugInfo.Variable || DebugInfo.Expression || DebugInfo.Location)
- Description.DebugInfo = std::move(DebugInfo);
-
- auto Created = Function.frameInfo().createObject(std::move(Description));
- if (!Created)
- return importError(Element->range(), Created.error());
- MIFrameObjectSlot Slot{
- .TextID = TextID,
- .Index = *Created,
- .Name = std::nullopt,
- };
- if (const StackObjectDesc *CreatedDescription =
- Function.frameInfo().object(*Created)->description();
- CreatedDescription != nullptr && CreatedDescription->AllocaName) {
- Slot.Name = *CreatedDescription->AllocaName;
- }
- (Fixed ? Context.FixedStackObjects : Context.StackObjects).push_back(std::move(Slot));
- }
- return false;
- };
-
- if (const yaml::Node *Fixed = mappingValue(Root, "fixedStack")) {
- if (ParseList(*Fixed, true))
- return true;
- }
- if (const yaml::Node *Stack = mappingValue(Root, "stack")) {
- if (ParseList(*Stack, false))
- return true;
- }
- return false;
-}
-
-bool MIRDocumentParser::parseFrameReference(const yaml::Node &Node, const MIParserContext &Context,
- FrameIndex &Index) {
- std::string_view Text;
- if (scalar(Node, Text, "frame index"))
- return true;
- bool Fixed = false;
- std::optional<std::uint32_t> ID = parsePrefixedID(Text, "%stack.");
- if (!ID) {
- ID = parsePrefixedID(Text, "%fixed-stack.");
- Fixed = true;
- }
- if (!ID)
- return fail(Node.range(), "frame index must use '%stack.N' or '%fixed-stack.N'");
- const auto &Slots = Fixed ? Context.FixedStackObjects : Context.StackObjects;
- const auto Found = std::ranges::find_if(
- Slots, [ID](const MIFrameObjectSlot &Slot) { return Slot.TextID == *ID; });
- if (Found == Slots.end())
- return fail(Node.range(), "reference to undefined frame object");
- Index = Found->Index;
- return false;
-}
-
-MachineBasicBlock *MIRDocumentParser::blockAt(MachineFunction &Function,
- std::uint32_t Number) const noexcept {
- for (MachineBasicBlock *Block = Function.firstBlock(); Block != nullptr; Block = Block->next()) {
- if (Block->denseNumber() == Number)
- return Block;
- }
- return nullptr;
-}
-
-MachineInstr *MIRDocumentParser::instructionAt(MachineFunction &Function, std::uint32_t BlockNumber,
- std::uint32_t Offset) const noexcept {
- MachineBasicBlock *Block = blockAt(Function, BlockNumber);
- if (Block == nullptr)
- return nullptr;
- MachineInstr *Instruction = Block->firstInstruction();
- for (std::uint32_t Index = 0; Instruction != nullptr && Index < Offset; ++Index)
- Instruction = Instruction->next();
- return Instruction;
-}
-
-bool MIRDocumentParser::parseBlockReference(std::string_view Text, const TextRange &Range,
- MachineFunction &Function, MachineBasicBlock *&Block) {
- const auto Number = parsePrefixedID(Text, "%bb.");
- if (!Number)
- return fail(Range, "basic block reference must use the '%bb.N' MIR spelling");
- Block = blockAt(Function, *Number);
- if (Block == nullptr)
- return fail(Range, "reference to undefined machine basic block");
- return false;
-}
-
-bool MIRDocumentParser::parseSaveRestorePoints(const yaml::Node &Node, MachineFunction &Function,
- std::vector<MachineFrameSaveRestorePoint> &Points) {
- if (requireKind(Node, yaml::NodeKind::Sequence, "frame save/restore points"))
- return true;
- constexpr std::array Keys{
- std::string_view{"point"},
- std::string_view{"registers"},
- };
- for (const yaml::NodeID PointID : Node.sequence()) {
- const yaml::Node *PointNode = node(PointID);
- if (PointNode == nullptr ||
- requireKind(*PointNode, yaml::NodeKind::Mapping, "frame save/restore point") ||
- validateKeys(*PointNode, Keys, "frame save/restore point")) {
- return true;
- }
- const yaml::Node *BlockNode = mappingValue(*PointNode, "point");
- if (BlockNode == nullptr)
- return fail(PointNode->range(), "frame save/restore point requires 'point'");
- std::string_view BlockText;
- MachineBasicBlock *Block = nullptr;
- if (scalar(*BlockNode, BlockText, "basic block reference") ||
- parseBlockReference(BlockText, BlockNode->range(), Function, Block)) {
- return true;
- }
- if (Block == nullptr)
- return fail(BlockNode->range(), "reference to undefined machine basic block");
- MachineFrameSaveRestorePoint Point{.Block = Block->stableID(), .Registers = {}};
- if (const yaml::Node *RegistersNode = mappingValue(*PointNode, "registers")) {
- if (requireKind(*RegistersNode, yaml::NodeKind::Sequence, "save/restore point registers")) {
- return true;
- }
- for (const yaml::NodeID RegID : RegistersNode->sequence()) {
- const yaml::Node *RegNode = node(RegID);
- PhysicalRegister Reg;
- if (RegNode == nullptr || physicalRegister(*RegNode, Reg))
- return true;
- Point.Registers.push_back(Reg);
- }
- }
- Points.push_back(std::move(Point));
- }
- return false;
-}
-
-bool MIRDocumentParser::parseFrameState(const yaml::Node &Root, MachineFunction &Function,
- const MIParserContext &Context) {
- const yaml::Node *Frame = mappingValue(Root, "frameInfo");
- if (Frame == nullptr)
- return false;
- if (requireKind(*Frame, yaml::NodeKind::Mapping, "frameInfo"))
- return true;
- constexpr std::array Keys{
- std::string_view{"isFrameAddressTaken"},
- std::string_view{"isReturnAddressTaken"},
- std::string_view{"hasStackMap"},
- std::string_view{"hasPatchPoint"},
- std::string_view{"stackSize"},
- std::string_view{"offsetAdjustment"},
- std::string_view{"maxAlignment"},
- std::string_view{"adjustsStack"},
- std::string_view{"hasCalls"},
- std::string_view{"framePointerPolicy"},
- std::string_view{"stackProtector"},
- std::string_view{"functionContext"},
- std::string_view{"maxCallFrameSize"},
- std::string_view{"cvBytesOfCalleeSavedRegisters"},
- std::string_view{"hasOpaqueSPAdjustment"},
- std::string_view{"hasVAStart"},
- std::string_view{"hasMustTailInVarArgFunc"},
- std::string_view{"hasTailCall"},
- std::string_view{"isCalleeSavedInfoValid"},
- std::string_view{"localFrameSize"},
- std::string_view{"savePoint"},
- std::string_view{"restorePoint"},
- };
- if (validateKeys(*Frame, Keys, "frameInfo"))
- return true;
-
- MachineFrameState State = Function.frameInfo().state();
- const std::array BoolBindings{
- std::pair{"isFrameAddressTaken", &State.IsFrameAddressTaken},
- std::pair{"isReturnAddressTaken", &State.IsReturnAddressTaken},
- std::pair{"hasStackMap", &State.HasStackMap},
- std::pair{"hasPatchPoint", &State.HasPatchPoint},
- std::pair{"adjustsStack", &State.AdjustsStack},
- std::pair{"hasCalls", &State.HasCalls},
- std::pair{"hasOpaqueSPAdjustment", &State.HasOpaqueSPAdjustment},
- std::pair{"hasVAStart", &State.HasVAStart},
- std::pair{"hasMustTailInVarArgFunc", &State.HasMustTailInVarArgFunc},
- std::pair{"hasTailCall", &State.HasTailCall},
- std::pair{"isCalleeSavedInfoValid", &State.IsCalleeSavedInfoValid},
- };
- for (const auto &[Key, Destination] : BoolBindings) {
- if (const yaml::Node *Value = mappingValue(*Frame, Key)) {
- if (boolean(*Value, *Destination))
- return true;
- }
- }
-
- if (const yaml::Node *Value = mappingValue(*Frame, "stackSize")) {
- if (unsignedValue(*Value, State.StackSize, "an unsigned stack size"))
- return true;
- }
- if (const yaml::Node *Value = mappingValue(*Frame, "offsetAdjustment")) {
- std::int64_t Wide = 0;
- if (signedValue(*Value, Wide, "a 32-bit signed offset adjustment") ||
- Wide < std::numeric_limits<std::int32_t>::min() ||
- Wide > std::numeric_limits<std::int32_t>::max()) {
- return fail(Value->range(), "offset adjustment is outside the 32-bit signed range");
- }
- State.OffsetAdjustment = static_cast<std::int32_t>(Wide);
- }
- if (const yaml::Node *Value = mappingValue(*Frame, "maxAlignment")) {
- std::uint64_t Bytes = 0;
- if (unsignedValue(*Value, Bytes, "an unsigned maximum alignment"))
- return true;
- if (Bytes == 0) {
- State.MaxAlignment.reset();
- } else {
- auto Parsed = Alignment::create(Bytes);
- if (!Parsed)
- return importError(Value->range(), Parsed.error());
- State.MaxAlignment = *Parsed;
- }
- }
- if (const yaml::Node *Value = mappingValue(*Frame, "framePointerPolicy")) {
- std::string_view Name;
- if (scalar(*Value, Name, "frame-pointer policy"))
- return true;
- const auto Policy = parseMachineFramePointerPolicy(Name);
- if (!Policy)
- return fail(Value->range(), "unknown frame-pointer policy '" + std::string(Name) + "'");
- State.FramePointerPolicy = *Policy;
- }
- for (const auto &[Key, Destination] : {std::pair{"stackProtector", &State.StackProtector},
- std::pair{"functionContext", &State.FunctionContext}}) {
- if (const yaml::Node *Value = mappingValue(*Frame, Key)) {
- std::string_view Reference;
- if (scalar(*Value, Reference, "frame index"))
- return true;
- if (Reference.empty()) {
- Destination->reset();
- continue;
- }
- FrameIndex Index;
- if (parseFrameReference(*Value, Context, Index))
- return true;
- *Destination = Index;
- }
- }
- if (const yaml::Node *Value = mappingValue(*Frame, "maxCallFrameSize")) {
- std::uint64_t Size = 0;
- if (unsignedValue(*Value, Size, "a 32-bit unsigned maximum call-frame size") ||
- Size > std::numeric_limits<std::uint32_t>::max()) {
- return fail(Value->range(), "maximum call-frame size is outside the 32-bit unsigned range");
- }
- if (Size == std::numeric_limits<std::uint32_t>::max())
- State.MaxCallFrameSize.reset();
- else
- State.MaxCallFrameSize = Size;
- }
- for (const auto &[Key, Destination] :
- {std::pair{"cvBytesOfCalleeSavedRegisters", &State.CVBytesOfCalleeSavedRegisters},
- std::pair{"localFrameSize", &State.LocalFrameSize}}) {
- if (const yaml::Node *Value = mappingValue(*Frame, Key)) {
- std::uint64_t Wide = 0;
- if (unsignedValue(*Value, Wide, "a 32-bit unsigned frame value") ||
- Wide > std::numeric_limits<std::uint32_t>::max()) {
- return fail(Value->range(), "frame value is outside the 32-bit unsigned range");
- }
- *Destination = static_cast<std::uint32_t>(Wide);
- }
- }
- if (const yaml::Node *Value = mappingValue(*Frame, "savePoint")) {
- if (parseSaveRestorePoints(*Value, Function, State.SavePoints))
- return true;
- }
- if (const yaml::Node *Value = mappingValue(*Frame, "restorePoint")) {
- if (parseSaveRestorePoints(*Value, Function, State.RestorePoints))
- return true;
- }
- auto StateResult = Function.frameInfo().setState(std::move(State));
- if (!StateResult)
- return importError(Frame->range(), StateResult.error());
- return false;
-}
-
-} // namespace rcc::mir
diff --git a/backend/lib/MIR/MIRFunctionParser.cpp b/backend/lib/MIR/MIRFunctionParser.cpp
deleted file mode 100644
index 6acf774..0000000
--- a/backend/lib/MIR/MIRFunctionParser.cpp
+++ /dev/null
@@ -1,552 +0,0 @@
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#include "MIRDocumentParser.h"
-#include "MIRDocumentReferenceValidation.h"
-#include "MIRMetadataTextParser.h"
-#include "MIRParserUtilities.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 {
-
-using parser_detail::parseBool;
-using parser_detail::parsePrefixedID;
-using parser_detail::parseSigned;
-using parser_detail::parseUnsigned;
-using parser_detail::parseUnsigned32;
-using parser_detail::trim;
-
-bool MIRDocumentParser::parseFunctionScalars(const yaml::Node &Root, MachineFunction &Function,
- MIRFunctionDocumentInfo &DocumentInfo) {
- if (const yaml::Node *AlignNode = mappingValue(Root, "alignment")) {
- Alignment Align;
- if (alignment(*AlignNode, Align, true))
- return true;
- if (Align.bytes() != 1 || AlignNode->scalar() != "0")
- Function.attributes().setAlignment(Align);
- }
-
- constexpr std::array PropertyBindings{
- std::pair{"legalized", MachineFunctionProperty::Legalized},
- std::pair{"regBankSelected", MachineFunctionProperty::RegBankSelected},
- std::pair{"selected", MachineFunctionProperty::Selected},
- std::pair{"failedISel", MachineFunctionProperty::FailedISel},
- std::pair{"tracksRegLiveness", MachineFunctionProperty::TracksLiveness},
- std::pair{"noPhis", MachineFunctionProperty::NoPHIs},
- std::pair{"isSSA", MachineFunctionProperty::IsSSA},
- std::pair{"noVRegs", MachineFunctionProperty::NoVRegs},
- std::pair{"failsVerification", MachineFunctionProperty::FailsVerification},
- };
- for (const auto &[Key, Property] : PropertyBindings) {
- const yaml::Node *ValueNode = mappingValue(Root, Key);
- if (ValueNode == nullptr)
- continue;
- bool Value = false;
- if (boolean(*ValueNode, Value))
- return true;
- if (Value)
- Function.properties().set(Property);
- else
- Function.properties().clear(Property);
- if (Key == std::string_view("noPhis"))
- DocumentInfo.NoPHIs = Value;
- else if (Key == std::string_view("isSSA"))
- DocumentInfo.IsSSA = Value;
- else if (Key == std::string_view("noVRegs"))
- DocumentInfo.NoVRegs = Value;
- }
-
- constexpr std::array AttributeBindings{
- std::pair{"exposesReturnsTwice", MachineFunctionAttribute::ExposesReturnsTwice},
- std::pair{"hasWinCFI", MachineFunctionAttribute::HasWinCFI},
- std::pair{"hasFakeUses", MachineFunctionAttribute::HasFakeUses},
- std::pair{"callsEHReturn", MachineFunctionAttribute::CallsEHReturn},
- std::pair{"callsUnwindInit", MachineFunctionAttribute::CallsUnwindInit},
- std::pair{"hasEHContTarget", MachineFunctionAttribute::HasEHContTarget},
- std::pair{"hasEHScopes", MachineFunctionAttribute::HasEHScopes},
- std::pair{"hasEHFunclets", MachineFunctionAttribute::HasEHFunclets},
- std::pair{"isOutlined", MachineFunctionAttribute::IsOutlined},
- std::pair{"debugInstrRef", MachineFunctionAttribute::DebugInstrRef},
- std::pair{"tracksDebugUserValues", MachineFunctionAttribute::TracksDebugUserValues},
- };
- for (const auto &[Key, Attribute] : AttributeBindings) {
- const yaml::Node *ValueNode = mappingValue(Root, Key);
- if (ValueNode == nullptr)
- continue;
- bool Value = false;
- if (boolean(*ValueNode, Value))
- return true;
- if (Value)
- Function.attributes().set(Attribute);
- else
- Function.attributes().clear(Attribute);
- if (Key == std::string_view("hasFakeUses"))
- DocumentInfo.HasFakeUses = Value;
- }
-
- return false;
-}
-
-bool MIRDocumentParser::recomputeProperties(const yaml::Node &Root, MachineFunction &Function,
- const MIRFunctionDocumentInfo &DocumentInfo) {
- bool HasPHI = false;
- bool HasFakeUse = false;
- bool IsSSA = true;
- bool HasTiedOperands = false;
- bool AllTiedOperandsRewritten = true;
- bool HasFrameIndex = false;
- bool HasExecutablePseudo = false;
- std::unordered_set<Register> Definitions;
- for (const MachineBasicBlock *Block = Function.firstBlock(); Block != nullptr;
- Block = Block->next()) {
- for (const MachineInstr *Instruction = Block->firstInstruction(); Instruction != nullptr;
- Instruction = Instruction->next()) {
- const InstructionDescriptor *Descriptor = Target.instructionDescriptor(Instruction->opcode());
- if (Descriptor == nullptr)
- return fail(Root.range(), "instruction has no target descriptor");
- HasPHI |= Descriptor->Flags.has(InstructionFlag::Phi);
- HasFakeUse |= Descriptor->Flags.has(InstructionFlag::FakeUse);
- HasExecutablePseudo |= Descriptor->Flags.has(InstructionFlag::Pseudo) &&
- !Descriptor->Flags.has(InstructionFlag::Meta) &&
- !Descriptor->Flags.has(InstructionFlag::TerminalEmission);
- const auto Operands = Instruction->operands();
- for (std::size_t Index = 0; Index < Operands.size(); ++Index) {
- const MachineOperand &Operand = Operands[Index];
- HasFrameIndex |= Operand.getIf<FrameIndexOperand>() != nullptr;
- const auto Reg = Operand.registerValue();
- if (Reg && Reg->isVirtual() && Operand.isDef() && !Definitions.insert(*Reg).second) {
- IsSSA = false;
- }
- if (!Operand.tiedOperandIndex() || !Operand.isUse())
- continue;
- HasTiedOperands = true;
- const MachineOperand *Definition = Instruction->operandAt(*Operand.tiedOperandIndex());
- if (Definition == nullptr || Definition->registerValue() != Reg)
- AllTiedOperandsRewritten = false;
- }
- for (const MachineMemOperand *Memory : Instruction->memOperands())
- HasFrameIndex |= Memory->description().Base.frameIndex().has_value();
- }
- }
- const bool NoPHIs = !HasPHI;
- const bool NoVRegs =
- std::ranges::none_of(Function.registerInfo().virtualRegisters(),
- [](const VirtualRegisterInfo &Info) { return !Info.IsTombstone; });
-
- const auto ApplyComputed = [this, &Root, &Function](std::optional<bool> Explicit, bool Computed,
- MachineFunctionProperty Property,
- std::string_view Conflict) -> bool {
- if (Explicit && *Explicit && !Computed) {
- return fail(Root.range(), std::string(Function.name()) + std::string(Conflict));
- }
- if (Explicit.value_or(Computed))
- Function.properties().set(Property);
- else
- Function.properties().clear(Property);
- return false;
- };
- if (ApplyComputed(DocumentInfo.NoPHIs, NoPHIs, MachineFunctionProperty::NoPHIs,
- " has explicit property NoPhi, but contains at least one PHI") ||
- ApplyComputed(DocumentInfo.IsSSA, IsSSA, MachineFunctionProperty::IsSSA,
- " has explicit property IsSSA, but is not valid SSA") ||
- ApplyComputed(DocumentInfo.NoVRegs, NoVRegs, MachineFunctionProperty::NoVRegs,
- " has explicit property NoVRegs, but contains virtual registers")) {
- return true;
- }
-
- if (DocumentInfo.HasFakeUses && !*DocumentInfo.HasFakeUses && HasFakeUse) {
- return fail(Root.range(),
- std::string(Function.name()) +
- " has explicit property hasFakeUses=false, but contains fake uses");
- }
- if (DocumentInfo.HasFakeUses.value_or(HasFakeUse))
- Function.attributes().set(MachineFunctionAttribute::HasFakeUses);
- else
- Function.attributes().clear(MachineFunctionAttribute::HasFakeUses);
-
- const bool Selected = Function.properties().has(MachineFunctionProperty::Selected);
- const bool StructurallyPostSSA =
- Selected && NoPHIs && !Function.properties().has(MachineFunctionProperty::IsSSA);
- if (AllTiedOperandsRewritten && (HasTiedOperands || StructurallyPostSSA))
- Function.properties().set(MachineFunctionProperty::TiedOpsRewritten);
- else
- Function.properties().clear(MachineFunctionProperty::TiedOpsRewritten);
-
- if (HasFrameIndex)
- Function.properties().clear(MachineFunctionProperty::NoFrameIndices);
- else
- Function.properties().set(MachineFunctionProperty::NoFrameIndices);
-
- const MachineFrameState &FrameState = Function.frameInfo().state();
- const bool StructurallyFinalizedFrame = Selected && NoVRegs && !HasFrameIndex &&
- FrameState.IsCalleeSavedInfoValid &&
- FrameState.MaxAlignment.has_value();
- if (StructurallyFinalizedFrame) {
- if (!Function.frameInfo().isFrozen()) {
- auto Frozen = Function.frameInfo().freeze(FrameState.StackSize, *FrameState.MaxAlignment);
- if (!Frozen)
- return importError(Root.range(), Frozen.error(), "mir-frame-finalization");
- }
- Function.properties().set(MachineFunctionProperty::FrameFinalized);
- } else {
- Function.properties().clear(MachineFunctionProperty::FrameFinalized);
- }
-
- if (StructurallyFinalizedFrame && !HasExecutablePseudo)
- Function.properties().set(MachineFunctionProperty::NoExecutablePseudos);
- else
- Function.properties().clear(MachineFunctionProperty::NoExecutablePseudos);
- return false;
-}
-
-bool MIRDocumentParser::parseRegisters(const yaml::Node &Root, MachineFunction &Function,
- MIParserContext &Context) {
- const yaml::Node *Registers = mappingValue(Root, "registers");
- if (Registers != nullptr) {
- if (requireKind(*Registers, yaml::NodeKind::Sequence, "registers"))
- return true;
- constexpr std::array RegisterKeys{
- std::string_view{"id"},
- std::string_view{"class"},
- std::string_view{"preferred-register"},
- std::string_view{"flags"},
- };
- std::unordered_set<std::uint32_t> IDs;
- for (const yaml::NodeID ElementID : Registers->sequence()) {
- const yaml::Node *Element = node(ElementID);
- if (Element == nullptr || requireKind(*Element, yaml::NodeKind::Mapping, "register entry") ||
- validateKeys(*Element, RegisterKeys, "register")) {
- return true;
- }
- const yaml::Node *IDNode = mappingValue(*Element, "id");
- const yaml::Node *ClassNode = mappingValue(*Element, "class");
- if (IDNode == nullptr || ClassNode == nullptr)
- return fail(Element->range(), "register entry requires 'id' and 'class'");
- std::uint64_t WideID = 0;
- std::string_view ClassName;
- if (unsignedValue(*IDNode, WideID, "a 32-bit virtual-register ID") ||
- WideID > std::numeric_limits<std::uint32_t>::max() ||
- scalar(*ClassNode, ClassName, "register class or bank")) {
- return true;
- }
- const auto TextID = static_cast<std::uint32_t>(WideID);
- if (!IDs.insert(TextID).second)
- return fail(IDNode->range(), "duplicate virtual register ID");
- auto Created = Function.registerInfo().createVirtualRegister();
- if (!Created)
- return importError(IDNode->range(), Created.error());
- Context.VirtualRegisters.push_back({TextID, *Created});
-
- if (ClassName != "_") {
- if (const RegisterClassDesc *Class = Target.registers().registerClass(ClassName)) {
- auto SetResult = Function.registerInfo().setRegisterClass(*Created, Class->ID);
- if (!SetResult)
- return importError(ClassNode->range(), SetResult.error());
- SetResult = Function.registerInfo().setRegisterBank(*Created, Class->DefaultBank);
- if (!SetResult)
- return importError(ClassNode->range(), SetResult.error());
- } else if (const RegisterBankDesc *Bank = Target.registers().registerBank(ClassName)) {
- auto SetResult = Function.registerInfo().setRegisterBank(*Created, Bank->ID);
- if (!SetResult)
- return importError(ClassNode->range(), SetResult.error());
- } else {
- return fail(ClassNode->range(),
- "unknown register class or bank '" + std::string(ClassName) + "'");
- }
- }
-
- if (const yaml::Node *Preferred = mappingValue(*Element, "preferred-register")) {
- std::string_view PreferredName;
- PhysicalRegister Reg;
- if (scalar(*Preferred, PreferredName, "physical register"))
- return true;
- if (!trim(PreferredName).empty()) {
- if (physicalRegister(PreferredName, Preferred->range(), Reg))
- return true;
- auto AddResult = Function.registerInfo().addAllocationHint(*Created, Reg);
- if (!AddResult)
- return importError(Preferred->range(), AddResult.error());
- }
- }
- if (const yaml::Node *Flags = mappingValue(*Element, "flags")) {
- if (requireKind(*Flags, yaml::NodeKind::Sequence, "virtual-register flags"))
- return true;
- std::uint8_t Mask = 0;
- for (const yaml::NodeID FlagID : Flags->sequence()) {
- const yaml::Node *FlagNode = node(FlagID);
- std::string_view Name;
- if (FlagNode == nullptr || scalar(*FlagNode, Name, "virtual-register flag"))
- return true;
- const auto Flag = Target.mirText().virtualRegisterFlag(Name);
- if (!Flag)
- return fail(FlagNode->range(),
- "use of undefined register flag '" + std::string(Name) + "'");
- if ((Mask & *Flag) != 0)
- return fail(FlagNode->range(), "duplicate virtual-register flag");
- Mask = static_cast<std::uint8_t>(Mask | *Flag);
- }
- auto SetResult = Function.registerInfo().setTargetRegisterFlags(*Created, Mask);
- if (!SetResult)
- return importError(Flags->range(), SetResult.error());
- }
- }
- }
-
- const yaml::Node *LiveIns = mappingValue(Root, "liveins");
- if (LiveIns != nullptr) {
- if (requireKind(*LiveIns, yaml::NodeKind::Sequence, "liveins"))
- return true;
- constexpr std::array LiveInKeys{
- std::string_view{"reg"},
- std::string_view{"virtual-reg"},
- };
- for (const yaml::NodeID ElementID : LiveIns->sequence()) {
- const yaml::Node *Element = node(ElementID);
- if (Element == nullptr || requireKind(*Element, yaml::NodeKind::Mapping, "live-in entry") ||
- validateKeys(*Element, LiveInKeys, "live-in")) {
- return true;
- }
- const yaml::Node *RegNode = mappingValue(*Element, "reg");
- if (RegNode == nullptr)
- return fail(Element->range(), "live-in entry requires 'reg'");
- PhysicalRegister Reg;
- if (physicalRegister(*RegNode, Reg))
- return true;
- std::optional<Register> Initial;
- if (const yaml::Node *VirtualNode = mappingValue(*Element, "virtual-reg")) {
- std::string_view Text;
- if (scalar(*VirtualNode, Text, "virtual register"))
- return true;
- if (!trim(Text).empty()) {
- Register Virtual;
- if (virtualRegister(Text, VirtualNode->range(), Function, Context, Virtual))
- return true;
- Initial = Virtual;
- }
- }
- auto AddResult = Function.registerInfo().addLiveIn(Reg, Initial);
- if (!AddResult)
- return importError(Element->range(), AddResult.error());
- }
- }
-
- if (const yaml::Node *CalleeSaved = mappingValue(Root, "calleeSavedRegisters")) {
- if (requireKind(*CalleeSaved, yaml::NodeKind::Sequence, "callee-saved registers"))
- return true;
- std::vector<PhysicalRegister> RegistersValue;
- for (const yaml::NodeID RegID : CalleeSaved->sequence()) {
- const yaml::Node *RegNode = node(RegID);
- PhysicalRegister Reg;
- if (RegNode == nullptr || physicalRegister(*RegNode, Reg))
- return true;
- RegistersValue.push_back(Reg);
- }
- auto SetResult = Function.records().setCalleeSavedRegisters(std::move(RegistersValue));
- if (!SetResult)
- return importError(CalleeSaved->range(), SetResult.error());
- }
- return false;
-}
-
-TextRange MIRDocumentParser::scalarDiagnosticRange(const yaml::Node &Scalar,
- const TextRange &ScalarRange) const noexcept {
- if (Scalar.scalarStyle() != yaml::ScalarStyle::Literal &&
- Scalar.scalarStyle() != yaml::ScalarStyle::Folded) {
- std::size_t ContentOffset = Scalar.range().Begin.Offset;
- if (Scalar.scalarStyle() == yaml::ScalarStyle::SingleQuoted ||
- Scalar.scalarStyle() == yaml::ScalarStyle::DoubleQuoted) {
- ++ContentOffset;
- }
- return Result.Source.range(ContentOffset + ScalarRange.Begin.Offset,
- ContentOffset + ScalarRange.End.Offset);
- }
-
- const std::size_t FirstBodyLine = Scalar.range().Begin.Line + 1U;
- const std::size_t LastBodyLine = Scalar.range().End.Line;
- if (FirstBodyLine > LastBodyLine || FirstBodyLine == 0)
- return Scalar.range();
-
- std::size_t Indent = 0;
- bool FoundContent = false;
- for (std::size_t Line = FirstBodyLine; Line <= LastBodyLine; ++Line) {
- const std::string_view Text = Result.Source.line(Line);
- std::size_t CurrentIndent = 0;
- while (CurrentIndent < Text.size() && Text[CurrentIndent] == ' ')
- ++CurrentIndent;
- if (CurrentIndent == Text.size())
- continue;
- Indent = CurrentIndent;
- FoundContent = true;
- break;
- }
- if (!FoundContent)
- return Scalar.range();
-
- const TextPosition DefaultPosition = Scalar.range().Begin;
- const auto MapPosition = [this, FirstBodyLine, LastBodyLine, Indent,
- DefaultPosition](TextPosition Position) {
- const std::size_t SourceLine = FirstBodyLine + Position.Line - 1U;
- if (Position.Line == 0 || SourceLine > LastBodyLine || SourceLine > Result.Source.lineCount()) {
- return DefaultPosition;
- }
- const std::string_view SourceLineText = Result.Source.line(SourceLine);
- const std::size_t BodyColumn = Position.Column == 0 ? 0 : Position.Column - 1U;
- const std::size_t SourceColumn = std::min(Indent + BodyColumn, SourceLineText.size());
- const std::size_t Offset = Result.Source.lineOffsets()[SourceLine - 1U] + SourceColumn;
- return Result.Source.positionAt(Offset);
- };
- return {.Begin = MapPosition(ScalarRange.Begin), .End = MapPosition(ScalarRange.End)};
-}
-
-bool MIRDocumentParser::parseBody(const yaml::Node &Root, MachineFunction &Function,
- MIParserContext &Context) {
- const yaml::Node *Body = mappingValue(Root, "body");
- std::string BodyText;
- TextRange BodyRange = Root.range();
- if (Body != nullptr) {
- std::string_view Text;
- if (scalar(*Body, Text, "machine function body"))
- return true;
- BodyText = Text;
- BodyRange = Body->range();
- }
-
- if (!effectiveDataLayout().empty()) {
- const LLVMConstantSyntaxOptions PointerOptions = pointerLayoutOptions();
- Context.PointerSizeInBits = [PointerOptions](std::uint32_t AddressSpace) {
- return llvmPointerSizeInBits(AddressSpace, PointerOptions);
- };
- }
- Context.SyncScopes = ActiveDocument->SyncScopes;
- Context.InternSyncScope = [this](std::string_view Name) -> Expected<SyncScopeID> {
- if (Name.empty())
- return makeUnexpected(ErrorCode::ParseError, "synchronization scope name cannot be empty");
- const auto Existing =
- std::ranges::find_if(ActiveDocument->SyncScopes,
- [Name](const MISyncScopeSlot &Slot) { return Slot.Name == Name; });
- if (Existing != ActiveDocument->SyncScopes.end())
- return Existing->ID;
- if (ActiveDocument->SyncScopes.size() >=
- static_cast<std::size_t>(std::numeric_limits<std::uint32_t>::max())) {
- return makeUnexpected(ErrorCode::Unsupported,
- "MIR document contains too many synchronization scopes");
- }
- const auto ID =
- SyncScopeID::fromValue(static_cast<std::uint32_t>(ActiveDocument->SyncScopes.size() + 1U));
- ActiveDocument->SyncScopes.push_back({.Name = std::string(Name), .ID = ID});
- return ID;
- };
- MIParserOptions BodyOptions = Options.MachineInstructions;
- BodyOptions.VerifyAfterParse = false;
- MIParser Parser(Function, Target,
- std::string(Result.Source.name()) + ":" + std::string(Function.name()),
- std::move(BodyText), Context, BodyOptions);
- auto Parsed = Parser.parse();
- if (!Parsed) {
- if (!Parser.diagnostics().empty()) {
- const TextRange Range =
- Body == nullptr ? BodyRange
- : scalarDiagnosticRange(*Body, Parser.diagnostics().front().Range);
- return fail(Range, Parser.diagnostics().front().Message, "mir-body");
- }
- return importError(BodyRange, Parsed.error(), "mir-body");
- }
- return false;
-}
-
-bool MIRDocumentParser::convertTargetNode(const yaml::Node &Source, MIRTargetNode &Destination) {
- switch (Source.kind()) {
- case yaml::NodeKind::Null:
- Destination = MIRTargetNode::null();
- return false;
- case yaml::NodeKind::Scalar:
- Destination = MIRTargetNode::scalar(std::string(Source.scalar()));
- return false;
- case yaml::NodeKind::Sequence: {
- std::vector<MIRTargetNode> Values;
- Values.reserve(Source.sequence().size());
- for (const yaml::NodeID ElementID : Source.sequence()) {
- const yaml::Node *Element = node(ElementID);
- if (Element == nullptr)
- return fail(Source.range(), "target MIR sequence contains an invalid node");
- MIRTargetNode Value;
- if (convertTargetNode(*Element, Value))
- return true;
- Values.push_back(std::move(Value));
- }
- Destination = MIRTargetNode::sequence(std::move(Values));
- return false;
- }
- case yaml::NodeKind::Mapping: {
- std::vector<MIRTargetMappingEntry> Entries;
- Entries.reserve(Source.mapping().size());
- for (const yaml::MappingEntry &SourceEntry : Source.mapping()) {
- const yaml::Node *ValueNode = node(SourceEntry.Value);
- if (ValueNode == nullptr)
- return fail(SourceEntry.KeyRange, "target MIR mapping contains an invalid node");
- MIRTargetNode Value;
- if (convertTargetNode(*ValueNode, Value))
- return true;
- Entries.push_back({SourceEntry.Key, std::move(Value)});
- }
- Destination = MIRTargetNode::mapping(std::move(Entries));
- return false;
- }
- }
- return fail(Source.range(), "target MIR node has an unknown kind");
-}
-
-bool MIRDocumentParser::parseTargetState(const yaml::Node &Root, MachineFunction &Function) {
- const yaml::Node *StateNode = mappingValue(Root, "machineFunctionInfo");
- if (StateNode == nullptr)
- return false;
- MIRTargetNode TargetNode;
- if (convertTargetNode(*StateNode, TargetNode))
- return true;
- auto State = Target.mirText().parseFunctionState(TargetNode);
- if (!State)
- return importError(StateNode->range(), State.error(), "mir-target-state");
- auto SetResult = Function.setTargetState(std::move(*State));
- if (!SetResult)
- return importError(StateNode->range(), SetResult.error(), "mir-target-state");
- return false;
-}
-
-bool MIRDocumentParser::validateDocumentReferences(const MachineFunction &Function) {
- auto Verification = verifyMIRDocumentReferences(*ActiveDocument, Function, true);
- if (Verification)
- return false;
- return importError(Result.Source.range(0, 0), Verification.error());
-}
-
-} // namespace rcc::mir
diff --git a/backend/lib/MIR/MIRFunctionRecordsParser.cpp b/backend/lib/MIR/MIRFunctionRecordsParser.cpp
deleted file mode 100644
index 04857bb..0000000
--- a/backend/lib/MIR/MIRFunctionRecordsParser.cpp
+++ /dev/null
@@ -1,341 +0,0 @@
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#include "MIRDocumentParser.h"
-#include "MIRDocumentReferenceValidation.h"
-#include "MIRMetadataTextParser.h"
-#include "MIRParserUtilities.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 {
-
-using parser_detail::parseBool;
-using parser_detail::parsePrefixedID;
-using parser_detail::parseSigned;
-using parser_detail::parseUnsigned;
-using parser_detail::parseUnsigned32;
-using parser_detail::trim;
-
-bool MIRDocumentParser::parseFunctionRecords(const yaml::Node &Root, MachineFunction &Function) {
- auto ParseLocation = [this, &Function](const yaml::Node &Entry,
- MachineInstr *&Instruction) -> bool {
- const yaml::Node *BlockNode = mappingValue(Entry, "bb");
- const yaml::Node *OffsetNode = mappingValue(Entry, "offset");
- if (BlockNode == nullptr || OffsetNode == nullptr)
- return fail(Entry.range(), "instruction location requires 'bb' and 'offset'");
- std::uint64_t Block = 0;
- std::uint64_t Offset = 0;
- if (unsignedValue(*BlockNode, Block, "a 32-bit basic block number") ||
- unsignedValue(*OffsetNode, Offset, "a 32-bit instruction offset") ||
- Block > std::numeric_limits<std::uint32_t>::max() ||
- Offset > std::numeric_limits<std::uint32_t>::max()) {
- return true;
- }
- Instruction = instructionAt(Function, static_cast<std::uint32_t>(Block),
- static_cast<std::uint32_t>(Offset));
- if (Instruction == nullptr)
- return fail(Entry.range(), "instruction location is outside the machine function body");
- return false;
- };
-
- if (const yaml::Node *CallSites = mappingValue(Root, "callSites")) {
- if (requireKind(*CallSites, yaml::NodeKind::Sequence, "call-site records"))
- return true;
- constexpr std::array CallSiteKeys{
- std::string_view{"bb"},
- std::string_view{"offset"},
- std::string_view{"fwdArgRegs"},
- std::string_view{"calleeTypeIds"},
- };
- constexpr std::array ArgumentKeys{
- std::string_view{"arg"},
- std::string_view{"reg"},
- };
- for (const yaml::NodeID CallSiteID : CallSites->sequence()) {
- const yaml::Node *CallSiteNode = node(CallSiteID);
- if (CallSiteNode == nullptr ||
- requireKind(*CallSiteNode, yaml::NodeKind::Mapping, "call-site record") ||
- validateKeys(*CallSiteNode, CallSiteKeys, "call-site record")) {
- return true;
- }
- MachineInstr *Call = nullptr;
- if (ParseLocation(*CallSiteNode, Call))
- return true;
- if (Call == nullptr)
- return fail(CallSiteNode->range(),
- "instruction location is outside the machine function body");
- MachineCallSiteInfo Info{
- .CallInstruction = Call->stableID(),
- .ForwardedArguments = {},
- .CalleeTypeIDs = {},
- };
- if (const yaml::Node *Arguments = mappingValue(*CallSiteNode, "fwdArgRegs")) {
- if (requireKind(*Arguments, yaml::NodeKind::Sequence, "forwarded call arguments")) {
- return true;
- }
- for (const yaml::NodeID ArgumentID : Arguments->sequence()) {
- const yaml::Node *Argument = node(ArgumentID);
- if (Argument == nullptr ||
- requireKind(*Argument, yaml::NodeKind::Mapping, "forwarded call argument") ||
- validateKeys(*Argument, ArgumentKeys, "forwarded call argument")) {
- return true;
- }
- const yaml::Node *NumberNode = mappingValue(*Argument, "arg");
- const yaml::Node *RegNode = mappingValue(*Argument, "reg");
- if (NumberNode == nullptr || RegNode == nullptr)
- return fail(Argument->range(), "forwarded call argument requires 'arg' and 'reg'");
- std::uint16_t Number = 0;
- std::uint64_t WideNumber = 0;
- PhysicalRegister Reg;
- if (unsignedValue(*NumberNode, WideNumber, "a 16-bit argument number") ||
- WideNumber > std::numeric_limits<std::uint16_t>::max() ||
- physicalRegister(*RegNode, Reg)) {
- return true;
- }
- Number = static_cast<std::uint16_t>(WideNumber);
- Info.ForwardedArguments.push_back({Reg, Number});
- }
- }
- if (const yaml::Node *TypeIDs = mappingValue(*CallSiteNode, "calleeTypeIds")) {
- if (requireKind(*TypeIDs, yaml::NodeKind::Sequence, "callee type IDs"))
- return true;
- for (const yaml::NodeID TypeID : TypeIDs->sequence()) {
- const yaml::Node *TypeNode = node(TypeID);
- std::uint64_t Value = 0;
- if (TypeNode == nullptr ||
- unsignedValue(*TypeNode, Value, "an unsigned callee type ID")) {
- return true;
- }
- Info.CalleeTypeIDs.push_back(Value);
- }
- }
- auto AddResult = Function.records().addCallSite(std::move(Info));
- if (!AddResult)
- return importError(CallSiteNode->range(), AddResult.error());
- }
- }
-
- if (const yaml::Node *CalledGlobals = mappingValue(Root, "calledGlobals")) {
- if (requireKind(*CalledGlobals, yaml::NodeKind::Sequence, "called-global records"))
- return true;
- constexpr std::array Keys{
- std::string_view{"bb"},
- std::string_view{"offset"},
- std::string_view{"callee"},
- std::string_view{"flags"},
- };
- for (const yaml::NodeID CalledID : CalledGlobals->sequence()) {
- const yaml::Node *CalledNode = node(CalledID);
- if (CalledNode == nullptr ||
- requireKind(*CalledNode, yaml::NodeKind::Mapping, "called-global record") ||
- validateKeys(*CalledNode, Keys, "called-global record")) {
- return true;
- }
- MachineInstr *Call = nullptr;
- if (ParseLocation(*CalledNode, Call))
- return true;
- if (Call == nullptr)
- return fail(CalledNode->range(),
- "instruction location is outside the machine function body");
- const yaml::Node *CalleeNode = mappingValue(*CalledNode, "callee");
- const yaml::Node *FlagsNode = mappingValue(*CalledNode, "flags");
- if (CalleeNode == nullptr || FlagsNode == nullptr)
- return fail(CalledNode->range(), "called-global record requires 'callee' and 'flags'");
- std::string_view Callee;
- std::uint64_t WideFlags = 0;
- if (scalar(*CalleeNode, Callee, "callee name") ||
- unsignedValue(*FlagsNode, WideFlags, "32-bit called-global flags") ||
- WideFlags > std::numeric_limits<std::uint32_t>::max()) {
- return true;
- }
- if (ActiveDocument->EmbeddedIR && !ActiveDocument->EmbeddedIR->hasGlobalValue(Callee)) {
- return fail(CalleeNode->range(), "called global references an undefined embedded global");
- }
- auto AddResult =
- Function.records().addCalledGlobal({.CallInstruction = Call->stableID(),
- .Callee = std::string(Callee),
- .Flags = static_cast<std::uint32_t>(WideFlags)});
- if (!AddResult)
- return importError(CalledNode->range(), AddResult.error());
- }
- }
-
- if (const yaml::Node *Substitutions = mappingValue(Root, "debugValueSubstitutions")) {
- if (requireKind(*Substitutions, yaml::NodeKind::Sequence, "debug value substitutions")) {
- return true;
- }
- constexpr std::array Keys{
- std::string_view{"srcinst"}, std::string_view{"srcop"}, std::string_view{"dstinst"},
- std::string_view{"dstop"}, std::string_view{"subreg"},
- };
- for (const yaml::NodeID SubstitutionID : Substitutions->sequence()) {
- const yaml::Node *Substitution = node(SubstitutionID);
- if (Substitution == nullptr ||
- requireKind(*Substitution, yaml::NodeKind::Mapping, "debug value substitution") ||
- validateKeys(*Substitution, Keys, "debug value substitution")) {
- return true;
- }
- std::array<std::uint32_t, 5> Values{};
- for (std::size_t Index = 0; Index < Keys.size(); ++Index) {
- const yaml::Node *ValueNode = mappingValue(*Substitution, Keys[Index]);
- if (ValueNode == nullptr)
- return fail(Substitution->range(), "debug value substitution requires every fixed field");
- std::uint64_t Wide = 0;
- if (unsignedValue(*ValueNode, Wide, "a 32-bit debug substitution value") ||
- Wide > std::numeric_limits<std::uint32_t>::max()) {
- return true;
- }
- Values[Index] = static_cast<std::uint32_t>(Wide);
- }
- auto AddResult = Function.records().addDebugValueSubstitution(
- {.Source = {Values[0], Values[1]},
- .Destination = {Values[2], Values[3]},
- .Subregister = SubRegIndex::fromValue(Values[4])});
- if (!AddResult)
- return importError(Substitution->range(), AddResult.error());
- }
- }
-
- if (const yaml::Node *PrefetchTargets = mappingValue(Root, "prefetch-targets")) {
- if (requireKind(*PrefetchTargets, yaml::NodeKind::Sequence, "prefetch targets"))
- return true;
- for (const yaml::NodeID TargetID : PrefetchTargets->sequence()) {
- const yaml::Node *TargetNode = node(TargetID);
- std::string_view Text;
- if (TargetNode == nullptr || scalar(*TargetNode, Text, "prefetch target"))
- return true;
-
- std::size_t ContentOffset = TargetNode->range().Begin.Offset;
- if (TargetNode->scalarStyle() == yaml::ScalarStyle::SingleQuoted ||
- TargetNode->scalarStyle() == yaml::ScalarStyle::DoubleQuoted) {
- ++ContentOffset;
- }
- const auto ErrorRange = [this, ContentOffset](std::size_t Begin, std::size_t End = 1U) {
- return Result.Source.range(ContentOffset + Begin, ContentOffset + Begin + End);
- };
- const auto SkipSpace = [&Text](std::size_t &Cursor) {
- while (Cursor < Text.size() && (Text[Cursor] == ' ' || Text[Cursor] == '\t'))
- ++Cursor;
- };
- const auto ParseNumber = [&Text, &SkipSpace](std::size_t &Cursor, std::uint32_t &Value) {
- SkipSpace(Cursor);
- const std::size_t Begin = Cursor;
- while (Cursor < Text.size() && Text[Cursor] >= '0' && Text[Cursor] <= '9')
- ++Cursor;
- return Begin != Cursor && parseUnsigned32(Text.substr(Begin, Cursor - Begin), Value);
- };
-
- std::size_t Cursor = 0;
- SkipSpace(Cursor);
- if (!Text.substr(Cursor).starts_with("bb_id") ||
- (Cursor + std::string_view("bb_id").size() < Text.size() &&
- Text[Cursor + std::string_view("bb_id").size()] != ' ' &&
- Text[Cursor + std::string_view("bb_id").size()] != '\t')) {
- return fail(ErrorRange(Cursor), "expected 'bb_id'");
- }
- Cursor += std::string_view("bb_id").size();
-
- std::array<std::uint32_t, 3> Values{};
- if (!ParseNumber(Cursor, Values[0]))
- return fail(ErrorRange(0), "expected unsigned integer");
-
- SkipSpace(Cursor);
- if (Cursor < Text.size() && (Text[Cursor] == ',' || Text[Cursor] == '.')) {
- ++Cursor;
- if (!ParseNumber(Cursor, Values[1]))
- return fail(ErrorRange(0), "expected unsigned integer");
- } else if (Cursor < Text.size() && Text[Cursor] >= '0' && Text[Cursor] <= '9') {
- if (!ParseNumber(Cursor, Values[1]))
- return fail(ErrorRange(0), "expected unsigned integer");
- }
-
- SkipSpace(Cursor);
- if (Cursor >= Text.size() || Text[Cursor] != ',')
- return fail(ErrorRange(Cursor), "expected ','");
- ++Cursor;
- if (!ParseNumber(Cursor, Values[2]))
- return fail(ErrorRange(0), "expected unsigned integer");
- SkipSpace(Cursor);
- if (Cursor != Text.size())
- return fail(ErrorRange(0), "unexpected content after prefetch target");
-
- auto AddResult = Function.records().addPrefetchTarget(
- {.Block = {Values[0], Values[1]}, .CallsiteIndex = Values[2]});
- if (!AddResult)
- return importError(TargetNode->range(), AddResult.error());
- }
- }
-
- if (const yaml::Node *EntryValues = mappingValue(Root, "entry_values")) {
- if (requireKind(*EntryValues, yaml::NodeKind::Sequence, "entry values"))
- return true;
- constexpr std::array Keys{
- std::string_view{"entry-value-register"},
- std::string_view{"debug-info-variable"},
- std::string_view{"debug-info-expression"},
- std::string_view{"debug-info-location"},
- };
- for (const yaml::NodeID EntryID : EntryValues->sequence()) {
- const yaml::Node *Entry = node(EntryID);
- if (Entry == nullptr || requireKind(*Entry, yaml::NodeKind::Mapping, "entry value") ||
- validateKeys(*Entry, Keys, "entry value")) {
- return true;
- }
- const yaml::Node *RegNode = mappingValue(*Entry, "entry-value-register");
- const yaml::Node *VariableNode = mappingValue(*Entry, "debug-info-variable");
- const yaml::Node *ExpressionNode = mappingValue(*Entry, "debug-info-expression");
- const yaml::Node *LocationNode = mappingValue(*Entry, "debug-info-location");
- if (RegNode == nullptr || VariableNode == nullptr || ExpressionNode == nullptr ||
- LocationNode == nullptr) {
- return fail(Entry->range(), "entry value requires all four fixed fields");
- }
- PhysicalRegister Reg;
- if (physicalRegister(*RegNode, Reg))
- return true;
- MachineVariableDebugInfo DebugInfo;
- if (parseVariableDebugInfo(*Entry, Function, DebugInfo))
- return true;
- if (!DebugInfo.Variable || !DebugInfo.Expression || !DebugInfo.Location) {
- return fail(Entry->range(), "entry value debug metadata cannot be empty",
- "mir-debug-metadata");
- }
- auto AddResult =
- Function.records().addEntryValue({.Reg = Reg, .DebugInfo = std::move(DebugInfo)});
- if (!AddResult)
- return importError(Entry->range(), AddResult.error());
- }
- }
- return false;
-}
-
-} // namespace rcc::mir
diff --git a/backend/lib/MIR/MIRMetadataParser.cpp b/backend/lib/MIR/MIRMetadataParser.cpp
deleted file mode 100644
index 63727ee..0000000
--- a/backend/lib/MIR/MIRMetadataParser.cpp
+++ /dev/null
@@ -1,333 +0,0 @@
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#include "MIRDocumentParser.h"
-#include "MIRDocumentReferenceValidation.h"
-#include "MIRMetadataTextParser.h"
-#include "MIRParserUtilities.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 "rcc/MIR/MILexer.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 {
-
-using parser_detail::parseBool;
-using parser_detail::parsePrefixedID;
-using parser_detail::parseSigned;
-using parser_detail::parseUnsigned;
-using parser_detail::parseUnsigned32;
-using parser_detail::trim;
-
-Expected<MachineMetadataReference>
-MIRDocumentParser::resolveMetadataReference(const MachineFunction &Function,
- std::uint32_t Slot) const {
- if (ActiveDocument != nullptr && ActiveDocument->EmbeddedIR &&
- ActiveDocument->EmbeddedIR->metadata(std::to_string(Slot)) != nullptr) {
- return MachineMetadataReference::embeddedIR(Slot);
- }
- if (Function.metadata().node(Slot) != nullptr)
- return MachineMetadataReference::machineFunction(Slot);
- return makeUnexpected(ErrorCode::ParseError,
- "metadata reference uses undefined slot '!" + std::to_string(Slot) + "'");
-}
-
-bool MIRDocumentParser::parseVariableDebugInfo(const yaml::Node &Entry, MachineFunction &Function,
- MachineVariableDebugInfo &DebugInfo) {
- const auto Resolve = [this, &Function](std::uint32_t Slot) {
- return resolveMetadataReference(Function, Slot);
- };
- if (const yaml::Node *VariableNode = mappingValue(Entry, "debug-info-variable")) {
- std::string_view Text;
- if (scalar(*VariableNode, Text, "debug variable"))
- return true;
- if (!Text.empty()) {
- MetadataTextParser Parser(std::string(Text), Resolve);
- auto Reference = Parser.parseReferenceOnly();
- if (!Reference)
- return importError(VariableNode->range(), Reference.error(), "mir-debug-metadata");
- if (Reference->scope() != MachineMetadataScope::EmbeddedIR) {
- return fail(VariableNode->range(),
- "debug variable must reference embedded LLVM IR metadata",
- "mir-debug-metadata");
- }
- DebugInfo.Variable = *Reference;
- }
- }
- if (const yaml::Node *ExpressionNode = mappingValue(Entry, "debug-info-expression")) {
- std::string_view Text;
- if (scalar(*ExpressionNode, Text, "debug expression"))
- return true;
- if (!Text.empty()) {
- MetadataTextParser Parser(std::string(Text), Resolve);
- auto Value = Parser.parseValue();
- if (!Value)
- return importError(scalarDiagnosticRange(*ExpressionNode, Parser.diagnosticRange()),
- Value.error(), "mir-debug-metadata");
- if (Value->reference() != nullptr &&
- Value->reference()->scope() != MachineMetadataScope::EmbeddedIR) {
- return fail(ExpressionNode->range(),
- "debug expression reference must use embedded LLVM IR metadata",
- "mir-debug-metadata");
- }
- DebugInfo.Expression = std::move(*Value);
- }
- }
- if (const yaml::Node *LocationNode = mappingValue(Entry, "debug-info-location")) {
- std::string_view Text;
- if (scalar(*LocationNode, Text, "debug location"))
- return true;
- if (!Text.empty()) {
- MetadataTextParser Parser(std::string(Text), Resolve);
- auto Location = Parser.parseLocation();
- if (!Location)
- return importError(LocationNode->range(), Location.error(), "mir-debug-metadata");
- if (Location->reference() != nullptr &&
- Location->reference()->scope() != MachineMetadataScope::EmbeddedIR) {
- return fail(LocationNode->range(),
- "variable debug location must reference embedded LLVM IR metadata",
- "mir-debug-metadata");
- }
- DebugInfo.Location = std::move(*Location);
- }
- }
- if (!DebugInfo.Variable && !DebugInfo.Expression && !DebugInfo.Location)
- return false;
- auto Verification = DebugInfo.verifyStructure();
- if (!Verification)
- return importError(Entry.range(), Verification.error(), "mir-debug-metadata");
- return false;
-}
-
-bool MIRDocumentParser::parseMetadata(const yaml::Node &Root, MachineFunction &Function,
- MIParserContext &Context) {
- if (ActiveDocument->EmbeddedIR) {
- for (const EmbeddedIRMetadataDefinition &Reference :
- ActiveDocument->EmbeddedIR->metadataDefinitions()) {
- std::uint32_t ID = 0;
- if (!parseUnsigned32(Reference.Name, ID))
- continue;
- if (std::ranges::any_of(Context.Metadata,
- [ID](const MIMetadataSlot &Slot) { return Slot.TextID == ID; })) {
- return fail(Root.range(), "embedded LLVM IR metadata slots are not unique");
- }
- Context.Metadata.push_back(
- {.TextID = ID, .Reference = MachineMetadataReference::embeddedIR(ID)});
- }
- }
-
- const yaml::Node *Metadata = mappingValue(Root, "machineMetadataNodes");
- if (Metadata == nullptr)
- return false;
- if (requireKind(*Metadata, yaml::NodeKind::Sequence, "machine metadata nodes"))
- return true;
-
- enum class ParsedElementKind : std::uint8_t {
- Null,
- Reference,
- String,
- };
- struct ParsedElement final {
- ParsedElementKind Kind = ParsedElementKind::Reference;
- std::uint32_t Reference = 0;
- std::string String;
- TextRange Range;
- };
- struct Definition final {
- const yaml::Node *Node = nullptr;
- std::uint32_t Slot = 0;
- bool IsDistinct = false;
- std::vector<ParsedElement> Elements;
- };
- std::vector<Definition> Definitions;
- std::unordered_set<std::uint32_t> Slots;
-
- const auto TranslateRange = [this](const yaml::Node &Node, const TextRange &Range) {
- std::size_t ContentBegin = Node.range().Begin.Offset;
- if (Node.scalarStyle() == yaml::ScalarStyle::SingleQuoted ||
- Node.scalarStyle() == yaml::ScalarStyle::DoubleQuoted) {
- ++ContentBegin;
- }
- return Result.Source.range(ContentBegin + Range.Begin.Offset, ContentBegin + Range.End.Offset);
- };
-
- for (const yaml::NodeID DefinitionID : Metadata->sequence()) {
- const yaml::Node *DefinitionNode = node(DefinitionID);
- std::string_view Text;
- if (DefinitionNode == nullptr || scalar(*DefinitionNode, Text, "machine metadata definition"))
- return true;
-
- MILexer Lexer("machine-metadata", std::string(Text));
- MIToken Token = Lexer.next();
- const auto LexerFailed = [this, &Lexer, DefinitionNode, &TranslateRange, &Token]() -> bool {
- if (!Token.is(MITokenKind::Error))
- return false;
- const auto Diagnostics = Lexer.diagnostics();
- const TextRange Range = Diagnostics.empty()
- ? TranslateRange(*DefinitionNode, Token.range())
- : TranslateRange(*DefinitionNode, Diagnostics.front().Range);
- return fail(Range, Diagnostics.empty() ? "invalid machine metadata node"
- : Diagnostics.front().Message);
- };
- const auto Advance = [&Lexer, &Token, &LexerFailed]() -> bool {
- Token = Lexer.next();
- return LexerFailed();
- };
- const auto FailAtToken = [this, DefinitionNode, &TranslateRange](const MIToken &At,
- std::string Message) {
- return fail(TranslateRange(*DefinitionNode, At.range()), std::move(Message));
- };
-
- if (LexerFailed())
- return true;
- if (!Token.is(MITokenKind::Exclaim))
- return FailAtToken(Token, "expected a metadata node");
- if (Advance())
- return true;
- std::uint32_t Slot = 0;
- if (!Token.is(MITokenKind::IntegerLiteral) || !parseUnsigned32(Token.integerValue(), Slot)) {
- return FailAtToken(Token, "expected metadata id after '!'");
- }
- if (Advance())
- return true;
- if (!Token.is(MITokenKind::Equal))
- return FailAtToken(Token, "expected '='");
- if (Advance())
- return true;
-
- const bool IsDistinct = Token.isKeyword("distinct");
- if (IsDistinct && Advance())
- return true;
- if (!Token.is(MITokenKind::Exclaim))
- return FailAtToken(Token, "expected a metadata node");
- if (Advance())
- return true;
- if (!Token.is(MITokenKind::LeftBrace))
- return FailAtToken(Token, "expected '{' here");
- if (Advance())
- return true;
-
- std::vector<ParsedElement> Elements;
- if (!Token.is(MITokenKind::RightBrace)) {
- while (true) {
- if (Token.hasStringValue() && Token.stringValue() == "null") {
- Elements.push_back({.Kind = ParsedElementKind::Null,
- .Reference = 0,
- .String = {},
- .Range = TranslateRange(*DefinitionNode, Token.range())});
- } else {
- if (!Token.is(MITokenKind::Exclaim))
- return FailAtToken(Token, "expected '!' here");
- const TextRange ReferenceRange = TranslateRange(*DefinitionNode, Token.range());
- if (Advance())
- return true;
-
- if (Token.is(MITokenKind::StringConstant)) {
- Elements.push_back({.Kind = ParsedElementKind::String,
- .Reference = 0,
- .String = std::string(Token.stringValue()),
- .Range = TranslateRange(*DefinitionNode, Token.range())});
- } else {
- std::uint32_t Reference = 0;
- if (!Token.is(MITokenKind::IntegerLiteral) ||
- !parseUnsigned32(Token.integerValue(), Reference)) {
- return FailAtToken(Token, "expected metadata id after '!'");
- }
- Elements.push_back({.Kind = ParsedElementKind::Reference,
- .Reference = Reference,
- .String = {},
- .Range = ReferenceRange});
- }
- }
- if (Advance())
- return true;
- if (!Token.is(MITokenKind::Comma))
- break;
- if (Advance())
- return true;
- }
- }
- if (!Token.is(MITokenKind::RightBrace))
- return FailAtToken(Token, "expected end of metadata node");
- if (Advance())
- return true;
- if (!Token.is(MITokenKind::EndOfFile))
- return FailAtToken(Token, "expected end of metadata node");
- if (!Slots.insert(Slot).second)
- return FailAtToken(Token, "Metadata id is already used");
-
- Definitions.push_back({.Node = DefinitionNode,
- .Slot = Slot,
- .IsDistinct = IsDistinct,
- .Elements = std::move(Elements)});
- Context.Metadata.push_back(
- {.TextID = Slot, .Reference = MachineMetadataReference::machineFunction(Slot)});
- }
-
- for (const Definition &Definition : Definitions) {
- MachineMetadataNode NewNode{
- .Slot = Definition.Slot,
- .IsDistinct = Definition.IsDistinct,
- .Elements = {},
- };
- for (const ParsedElement &Element : Definition.Elements) {
- if (Element.Kind == ParsedElementKind::Null) {
- NewNode.Elements.push_back(MachineMetadataElement::null());
- continue;
- }
- if (Element.Kind == ParsedElementKind::String) {
- NewNode.Elements.push_back(MachineMetadataElement::string(Element.String));
- continue;
- }
-
- MachineMetadataReference Reference;
- if (ActiveDocument->EmbeddedIR != std::nullopt &&
- ActiveDocument->EmbeddedIR->metadata(std::to_string(Element.Reference)) != nullptr) {
- Reference = MachineMetadataReference::embeddedIR(Element.Reference);
- } else if (Slots.contains(Element.Reference)) {
- Reference = MachineMetadataReference::machineFunction(Element.Reference);
- } else {
- return fail(Element.Range,
- "use of undefined metadata '!" + std::to_string(Element.Reference) + "'");
- }
- auto Resolved = MachineMetadataElement::reference(Reference);
- if (!Resolved)
- return importError(Element.Range, Resolved.error());
- NewNode.Elements.push_back(std::move(*Resolved));
- }
- auto AddResult = Function.metadata().addNode(std::move(NewNode));
- if (!AddResult)
- return importError(Definition.Node->range(), AddResult.error());
- }
- return false;
-}
-
-} // namespace rcc::mir
diff --git a/backend/lib/MIR/MIRMetadataTextParser.cpp b/backend/lib/MIR/MIRMetadataTextParser.cpp
deleted file mode 100644
index 7875b06..0000000
--- a/backend/lib/MIR/MIRMetadataTextParser.cpp
+++ /dev/null
@@ -1,259 +0,0 @@
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#include "MIRMetadataTextParser.h"
-
-#include "MIRParserUtilities.h"
-#include "rcc/MIR/MIDebugSyntax.h"
-
-#include <cstdint>
-#include <limits>
-#include <optional>
-#include <string>
-#include <string_view>
-#include <unordered_set>
-#include <utility>
-#include <vector>
-
-namespace rcc::mir {
-
-using parser_detail::parseSigned;
-using parser_detail::parseUnsigned;
-using parser_detail::parseUnsigned32;
-
-Expected<void> MetadataTextParser::expect(MITokenKind Kind, std::string_view Description) {
- if (consume(Kind))
- return {};
- return makeUnexpected(ErrorCode::ParseError,
- "expected " + std::string(Description) + " in MIR metadata scalar");
-}
-
-Expected<void> MetadataTextParser::finish() {
- while (consume(MITokenKind::Newline)) {
- }
- if (Lexer.failed()) {
- if (!Lexer.diagnostics().empty()) {
- return makeUnexpected(ErrorCode::ParseError,
- std::string(Lexer.diagnostics().front().Message));
- }
- return makeUnexpected(ErrorCode::ParseError, "invalid MIR metadata scalar");
- }
- if (!at(MITokenKind::EndOfFile))
- return makeUnexpected(ErrorCode::ParseError, "unexpected trailing MIR metadata tokens");
- return {};
-}
-
-Expected<MachineMetadataReference> MetadataTextParser::parseReference() {
- if (auto Exclaim = expect(MITokenKind::Exclaim, "'!' before a metadata slot"); !Exclaim)
- return std::unexpected(Exclaim.error());
- if (!at(MITokenKind::IntegerLiteral))
- return makeUnexpected(ErrorCode::ParseError, "expected a 32-bit metadata slot");
- std::uint32_t Slot = 0;
- if (!parseUnsigned32(token().integerValue(), Slot))
- return makeUnexpected(ErrorCode::ParseError, "metadata slot is outside the 32-bit range");
- advance();
- return Resolve(Slot);
-}
-
-Expected<MachineMetadataReference> MetadataTextParser::parseReferenceOnly() {
- auto Reference = parseReference();
- if (!Reference)
- return std::unexpected(Reference.error());
- if (auto Finished = finish(); !Finished)
- return std::unexpected(Finished.error());
- return Reference;
-}
-
-Expected<MachineDIExpression> MetadataTextParser::parseExpression() {
- if (!at(MITokenKind::MetadataKeyword) || !token().isMetadataKeyword("!DIExpression")) {
- return makeUnexpected(ErrorCode::ParseError, "expected an inline !DIExpression");
- }
- advance();
- if (auto Left = expect(MITokenKind::LeftParen, "'(' after !DIExpression"); !Left)
- return std::unexpected(Left.error());
- std::vector<std::uint64_t> Elements;
- while (!at(MITokenKind::RightParen)) {
- if (at(MITokenKind::Identifier)) {
- const auto Opcode = parseDIExpressionOpcodeName(token().stringValue());
- const auto Encoding = parseDIExpressionAttributeEncodingName(token().stringValue());
- if (!Opcode && !Encoding)
- return makeUnexpected(ErrorCode::ParseError,
- invalidDIExpressionElementMessage(token().stringValue()));
- Elements.push_back(Opcode ? *Opcode : *Encoding);
- advance();
- } else if (at(MITokenKind::IntegerLiteral)) {
- std::int64_t Signed = 0;
- std::uint64_t Unsigned = 0;
- if (parseSigned(token().integerValue(), Signed))
- Elements.push_back(static_cast<std::uint64_t>(Signed));
- else if (parseUnsigned(token().integerValue(), Unsigned))
- Elements.push_back(Unsigned);
- else
- return makeUnexpected(ErrorCode::ParseError, "DIExpression integer exceeds 64 bits");
- advance();
- } else {
- return makeUnexpected(ErrorCode::ParseError, "expected a DIExpression opcode or integer");
- }
- if (!consume(MITokenKind::Comma))
- break;
- }
- if (auto Right = expect(MITokenKind::RightParen, "')' after !DIExpression"); !Right)
- return std::unexpected(Right.error());
- return MachineDIExpression(std::move(Elements));
-}
-
-Expected<MachineMetadataValue> MetadataTextParser::parseValue() {
- MachineMetadataValue Value;
- if (at(MITokenKind::Exclaim)) {
- auto Reference = parseReference();
- if (!Reference)
- return std::unexpected(Reference.error());
- auto Created = MachineMetadataValue::reference(*Reference);
- if (!Created)
- return std::unexpected(Created.error());
- Value = std::move(*Created);
- } else {
- auto Expression = parseExpression();
- if (!Expression)
- return std::unexpected(Expression.error());
- Value = MachineMetadataValue::expression(std::move(*Expression));
- }
- if (auto Finished = finish(); !Finished)
- return std::unexpected(Finished.error());
- return Value;
-}
-
-Expected<void>
-MetadataTextParser::parseDILocation(std::vector<MachineDILocationFrame> &Frames,
- std::optional<MachineMetadataReference> &Terminal) {
- if (!at(MITokenKind::MetadataKeyword) || !token().isMetadataKeyword("!DILocation")) {
- return makeUnexpected(ErrorCode::ParseError, "expected an inline !DILocation");
- }
- advance();
- if (auto Left = expect(MITokenKind::LeftParen, "'(' after !DILocation"); !Left)
- return Left;
-
- MachineDILocationFrame Frame;
- bool HasLine = false;
- bool HasScope = false;
- std::vector<MachineDILocationFrame> NestedFrames;
- std::optional<MachineMetadataReference> NestedTerminal;
- std::unordered_set<std::string> Seen;
- while (!at(MITokenKind::RightParen)) {
- if (!at(MITokenKind::Identifier))
- return makeUnexpected(ErrorCode::ParseError, "expected a DILocation field name");
- const std::string Field(token().stringValue());
- if (!Seen.insert(Field).second) {
- return makeUnexpected(ErrorCode::ParseError, "duplicate DILocation field '" + Field + "'");
- }
- advance();
- if (auto Colon = expect(MITokenKind::Colon, "':' after a DILocation field"); !Colon)
- return Colon;
-
- if (Field == "line") {
- std::uint32_t Value = 0;
- if (!at(MITokenKind::IntegerLiteral) || !parseUnsigned32(token().integerValue(), Value)) {
- return makeUnexpected(ErrorCode::ParseError,
- "DILocation line must be an unsigned 32-bit integer");
- }
- Frame.Line = Value;
- HasLine = true;
- advance();
- } else if (Field == "column") {
- std::uint64_t Value = 0;
- if (!at(MITokenKind::IntegerLiteral) || !parseUnsigned(token().integerValue(), Value) ||
- Value > std::numeric_limits<std::uint16_t>::max()) {
- return makeUnexpected(ErrorCode::ParseError,
- "DILocation column must be an unsigned 16-bit integer");
- }
- Frame.Column = static_cast<std::uint16_t>(Value);
- advance();
- } else if (Field == "scope") {
- auto Scope = parseReference();
- if (!Scope)
- return std::unexpected(Scope.error());
- if (Scope->scope() != MachineMetadataScope::EmbeddedIR) {
- return makeUnexpected(ErrorCode::ParseError,
- "DILocation scope must reference embedded LLVM IR metadata");
- }
- Frame.Scope = *Scope;
- HasScope = true;
- } else if (Field == "inlinedAt") {
- if (at(MITokenKind::MetadataKeyword) && token().isMetadataKeyword("!DILocation")) {
- if (auto Nested = parseDILocation(NestedFrames, NestedTerminal); !Nested)
- return Nested;
- } else {
- auto Reference = parseReference();
- if (!Reference)
- return std::unexpected(Reference.error());
- if (Reference->scope() != MachineMetadataScope::EmbeddedIR) {
- return makeUnexpected(ErrorCode::ParseError,
- "DILocation inlinedAt must reference embedded LLVM IR metadata");
- }
- NestedTerminal = *Reference;
- }
- } else if (Field == "isImplicitCode") {
- if (!at(MITokenKind::Identifier) ||
- (token().stringValue() != "true" && token().stringValue() != "false")) {
- return makeUnexpected(ErrorCode::ParseError,
- "DILocation isImplicitCode must be true or false");
- }
- Frame.IsImplicitCode = token().stringValue() == "true";
- advance();
- } else if (Field == "atomGroup") {
- std::uint64_t Value = 0;
- if (!at(MITokenKind::IntegerLiteral) || !parseUnsigned(token().integerValue(), Value) ||
- Value > MachineDILocation::MaxAtomGroup) {
- return makeUnexpected(ErrorCode::ParseError, "DILocation atomGroup exceeds 61 bits");
- }
- Frame.AtomGroup = Value;
- advance();
- } else if (Field == "atomRank") {
- std::uint64_t Value = 0;
- if (!at(MITokenKind::IntegerLiteral) || !parseUnsigned(token().integerValue(), Value) ||
- Value > 7) {
- return makeUnexpected(ErrorCode::ParseError, "DILocation atomRank exceeds three bits");
- }
- Frame.AtomRank = static_cast<std::uint8_t>(Value);
- advance();
- } else {
- return makeUnexpected(ErrorCode::ParseError, "unknown DILocation field '" + Field + "'");
- }
- if (!consume(MITokenKind::Comma))
- break;
- }
- if (auto Right = expect(MITokenKind::RightParen, "')' after !DILocation"); !Right)
- return Right;
- if (!HasLine || !HasScope)
- return makeUnexpected(ErrorCode::ParseError, "DILocation requires line and scope fields");
- Frames.push_back(Frame);
- Frames.insert(Frames.end(), NestedFrames.begin(), NestedFrames.end());
- Terminal = NestedTerminal;
- return {};
-}
-
-Expected<MachineDebugLocation> MetadataTextParser::parseLocation() {
- MachineDebugLocation Location;
- if (at(MITokenKind::Exclaim)) {
- auto Reference = parseReference();
- if (!Reference)
- return std::unexpected(Reference.error());
- auto Created = MachineDebugLocation::reference(*Reference);
- if (!Created)
- return std::unexpected(Created.error());
- Location = std::move(*Created);
- } else {
- std::vector<MachineDILocationFrame> Frames;
- std::optional<MachineMetadataReference> Terminal;
- if (auto Parsed = parseDILocation(Frames, Terminal); !Parsed)
- return std::unexpected(Parsed.error());
- auto Created = MachineDILocation::create(std::move(Frames), Terminal);
- if (!Created)
- return std::unexpected(Created.error());
- Location = MachineDebugLocation::inlineLocation(std::move(*Created));
- }
- if (auto Finished = finish(); !Finished)
- return std::unexpected(Finished.error());
- return Location;
-}
-
-} // namespace rcc::mir
diff --git a/backend/lib/MIR/MIRMetadataTextParser.h b/backend/lib/MIR/MIRMetadataTextParser.h
deleted file mode 100644
index 032523d..0000000
--- a/backend/lib/MIR/MIRMetadataTextParser.h
+++ /dev/null
@@ -1,74 +0,0 @@
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-// NOLINTNEXTLINE(llvm-header-guard)
-#ifndef RCC_LIB_MIR_MIRMETADATATEXTPARSER_H
-#define RCC_LIB_MIR_MIRMETADATATEXTPARSER_H
-
-#include "rcc/CodeGen/MachineDebugInfo.h"
-#include "rcc/CodeGen/MachineMetadata.h"
-#include "rcc/MIR/MILexer.h"
-#include "rcc/Support/Error.h"
-
-#include <cstddef>
-#include <cstdint>
-#include <functional>
-#include <optional>
-#include <string>
-#include <string_view>
-#include <utility>
-#include <vector>
-
-namespace rcc::mir {
-
-class MetadataTextParser final {
-public:
- using Resolver = std::function<Expected<MachineMetadataReference>(std::uint32_t Slot)>;
-
- MetadataTextParser(std::string Text, Resolver Resolve)
- : Lexer("MIR metadata scalar", std::move(Text)), Resolve(std::move(Resolve)) {
- do {
- Tokens.push_back(Lexer.next());
- } while (!Tokens.back().isErrorOrEnd());
- }
-
- [[nodiscard]] Expected<MachineMetadataReference> parseReferenceOnly();
- [[nodiscard]] Expected<MachineMetadataValue> parseValue();
- [[nodiscard]] Expected<MachineDebugLocation> parseLocation();
-
- [[nodiscard]] const TextRange &diagnosticRange() const noexcept {
- return token().range();
- }
-
-private:
- [[nodiscard]] const MIToken &token() const noexcept {
- return Tokens[Position];
- }
- [[nodiscard]] bool at(MITokenKind Kind) const noexcept {
- return token().is(Kind);
- }
- void advance() noexcept {
- if (Position + 1U < Tokens.size())
- ++Position;
- }
- [[nodiscard]] bool consume(MITokenKind Kind) noexcept {
- if (!at(Kind))
- return false;
- advance();
- return true;
- }
- [[nodiscard]] Expected<void> expect(MITokenKind Kind, std::string_view Description);
- [[nodiscard]] Expected<void> finish();
- [[nodiscard]] Expected<MachineMetadataReference> parseReference();
- [[nodiscard]] Expected<MachineDIExpression> parseExpression();
- [[nodiscard]] Expected<void> parseDILocation(std::vector<MachineDILocationFrame> &Frames,
- std::optional<MachineMetadataReference> &Terminal);
-
- MILexer Lexer;
- Resolver Resolve;
- std::vector<MIToken> Tokens;
- std::size_t Position = 0;
-};
-
-} // namespace rcc::mir
-
-#endif // RCC_LIB_MIR_MIRMETADATATEXTPARSER_H
diff --git a/backend/lib/MIR/MIRParserUtilities.h b/backend/lib/MIR/MIRParserUtilities.h
deleted file mode 100644
index 2f69548..0000000
--- a/backend/lib/MIR/MIRParserUtilities.h
+++ /dev/null
@@ -1,85 +0,0 @@
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-// NOLINTNEXTLINE(llvm-header-guard)
-#ifndef RCC_LIB_MIR_MIRPARSERUTILITIES_H
-#define RCC_LIB_MIR_MIRPARSERUTILITIES_H
-
-#include <charconv>
-#include <cstdint>
-#include <limits>
-#include <optional>
-#include <string_view>
-
-namespace rcc::mir::parser_detail {
-
-[[nodiscard]] inline std::string_view trim(std::string_view Text) noexcept {
- while (!Text.empty() && (Text.front() == ' ' || Text.front() == '\t' || Text.front() == '\r' ||
- Text.front() == '\n')) {
- Text.remove_prefix(1);
- }
- while (!Text.empty() && (Text.back() == ' ' || Text.back() == '\t' || Text.back() == '\r' ||
- Text.back() == '\n')) {
- Text.remove_suffix(1);
- }
- return Text;
-}
-
-[[nodiscard]] inline bool parseUnsigned(std::string_view Text, std::uint64_t &Value) noexcept {
- Text = trim(Text);
- int Base = 10;
- if (Text.starts_with("0x") || Text.starts_with("0X")) {
- Text.remove_prefix(2);
- Base = 16;
- }
- if (Text.empty())
- return false;
- const auto [End, Error] = std::from_chars(Text.data(), Text.data() + Text.size(), Value, Base);
- return Error == std::errc{} && End == Text.data() + Text.size();
-}
-
-[[nodiscard]] inline bool parseSigned(std::string_view Text, std::int64_t &Value) noexcept {
- Text = trim(Text);
- if (Text.empty())
- return false;
- const auto [End, Error] = std::from_chars(Text.data(), Text.data() + Text.size(), Value, 10);
- return Error == std::errc{} && End == Text.data() + Text.size();
-}
-
-[[nodiscard]] inline bool parseUnsigned32(std::string_view Text, std::uint32_t &Value) noexcept {
- std::uint64_t Wide = 0;
- if (!parseUnsigned(Text, Wide) || Wide > std::numeric_limits<std::uint32_t>::max())
- return false;
- Value = static_cast<std::uint32_t>(Wide);
- return true;
-}
-
-[[nodiscard]] inline bool parseBool(std::string_view Text, bool &Value) noexcept {
- if (Text == "true") {
- Value = true;
- return true;
- }
- if (Text == "false") {
- Value = false;
- return true;
- }
- return false;
-}
-
-[[nodiscard]] inline std::optional<std::uint32_t>
-parsePrefixedID(std::string_view Text, std::string_view Prefix) noexcept {
- Text = trim(Text);
- if (!Text.starts_with(Prefix))
- return std::nullopt;
- Text.remove_prefix(Prefix.size());
- const std::size_t Suffix = Text.find_first_of(". \t");
- if (Suffix != std::string_view::npos)
- Text = Text.substr(0, Suffix);
- std::uint32_t ID = 0;
- if (!parseUnsigned32(Text, ID))
- return std::nullopt;
- return ID;
-}
-
-} // namespace rcc::mir::parser_detail
-
-#endif // RCC_LIB_MIR_MIRPARSERUTILITIES_H
diff --git a/backend/lib/Tooling/ArtifactStore.cpp b/backend/lib/Tooling/ArtifactStore.cpp
index db330b1..115f58f 100644
--- a/backend/lib/Tooling/ArtifactStore.cpp
+++ b/backend/lib/Tooling/ArtifactStore.cpp
@@ -3,10 +3,10 @@
#include "rcc/Tooling/ArtifactStore.h"
#include "rcc/CodeGen/CompilationUnit.h"
+#include "rcc/CodeGen/MIRPrinter.h"
#include "rcc/CodeGen/MachineFunction.h"
#include "rcc/CodeGen/MachineVerifier.h"
#include "rcc/CodeGen/TargetDescription.h"
-#include "rcc/MIR/MIRDocument.h"
#include "rcc/Support/SHA256.h"
#include "rcc/Support/TextDiagnostic.h"
#include "rcc/Tooling/BackendInvocation.h"
diff --git a/backend/lib/Tooling/BackendDriver.cpp b/backend/lib/Tooling/BackendDriver.cpp
index c726fd3..0402f5a 100644
--- a/backend/lib/Tooling/BackendDriver.cpp
+++ b/backend/lib/Tooling/BackendDriver.cpp
@@ -2,8 +2,8 @@
#include "rcc/Tooling/BackendDriver.h"
+#include "rcc/CodeGen/MIRPrinter.h"
#include "rcc/CodeGen/MachineFunction.h"
-#include "rcc/MIR/MIRDocument.h"
#include "rcc/Target/RISCV/RISCVAssemblyPrinter.h"
#include "rcc/Target/RISCV/RISCVObjectFile.h"
diff --git a/backend/lib/Tooling/MIRFuzzer.cpp b/backend/lib/Tooling/MIRFuzzer.cpp
index e75e9f9..83762c4 100644
--- a/backend/lib/Tooling/MIRFuzzer.cpp
+++ b/backend/lib/Tooling/MIRFuzzer.cpp
@@ -5,6 +5,7 @@
#include "rcc/CodeGen/GenericOpcodes.h"
#include "rcc/CodeGen/InstructionDescriptor.h"
#include "rcc/CodeGen/LowLevelType.h"
+#include "rcc/CodeGen/MIRPrinter.h"
#include "rcc/CodeGen/MachineBasicBlock.h"
#include "rcc/CodeGen/MachineFunction.h"
#include "rcc/CodeGen/MachineIRBuilder.h"
@@ -13,7 +14,6 @@
#include "rcc/CodeGen/MachineRegisterInfo.h"
#include "rcc/CodeGen/MachineVerifier.h"
#include "rcc/CodeGen/TargetDescription.h"
-#include "rcc/MIR/MIRDocument.h"
#include "rcc/Target/RISCV/RISCVPipeline.h"
#include "rcc/Tooling/ArtifactStore.h"
#include "rcc/Tooling/BackendDriver.h"
diff --git a/backend/lib/Tooling/PipelineDriver.cpp b/backend/lib/Tooling/PipelineDriver.cpp
index d7a7d16..68cbb57 100644
--- a/backend/lib/Tooling/PipelineDriver.cpp
+++ b/backend/lib/Tooling/PipelineDriver.cpp
@@ -3,10 +3,10 @@
#include "rcc/Tooling/PipelineDriver.h"
#include "rcc/CodeGen/CompilationUnit.h"
+#include "rcc/CodeGen/MIRPrinter.h"
#include "rcc/CodeGen/MachineFunction.h"
#include "rcc/CodeGen/MachineOptimizationPipeline.h"
#include "rcc/CodeGen/MachineVerifier.h"
-#include "rcc/MIR/MIRDocument.h"
#include "rcc/Target/RISCV/RISCVPipeline.h"
#include <algorithm>
diff --git a/backend/unittests/MIR/CMakeLists.txt b/backend/unittests/MIR/CMakeLists.txt
index 6b41474..ae72452 100644
--- a/backend/unittests/MIR/CMakeLists.txt
+++ b/backend/unittests/MIR/CMakeLists.txt
@@ -17,6 +17,10 @@ add_test(
add_executable(RCCBackendMILexerTests MILexerTest.cpp)
rcc_backend_configure_target(RCCBackendMILexerTests)
+target_include_directories(
+ RCCBackendMILexerTests
+ PRIVATE "${RCC_BACKEND_SOURCE_DIR}/lib/CodeGen/MIRParser"
+)
target_link_libraries(RCCBackendMILexerTests PRIVATE RCCBackend::MIR)
add_test(NAME RCCBackend.MIR.MILexer COMMAND RCCBackendMILexerTests)
diff --git a/backend/unittests/MIR/MILexerTest.cpp b/backend/unittests/MIR/MILexerTest.cpp
index 5ebd2ca..46b9cae 100644
--- a/backend/unittests/MIR/MILexerTest.cpp
+++ b/backend/unittests/MIR/MILexerTest.cpp
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-or-later
-#include "rcc/MIR/MILexer.h"
+#include "MILexer.h"
#include "rcc/Support/TextDiagnostic.h"
diff --git a/backend/unittests/MIR/MIParserTest.cpp b/backend/unittests/MIR/MIParserTest.cpp
index 815a18e..60987f4 100644
--- a/backend/unittests/MIR/MIParserTest.cpp
+++ b/backend/unittests/MIR/MIParserTest.cpp
@@ -1,10 +1,11 @@
// SPDX-License-Identifier: GPL-3.0-or-later
-#include "rcc/MIR/MIParser.h"
+#include "rcc/CodeGen/MIRParser/MIParser.h"
#include "Target/TestTarget.h"
#include "rcc/CodeGen/GenericOpcodes.h"
#include "rcc/CodeGen/InstructionDescriptor.h"
+#include "rcc/CodeGen/MIRPrinter.h"
#include "rcc/CodeGen/MachineBasicBlock.h"
#include "rcc/CodeGen/MachineFunction.h"
#include "rcc/CodeGen/MachineInstr.h"
@@ -12,7 +13,6 @@
#include "rcc/CodeGen/MachineOperand.h"
#include "rcc/CodeGen/MachineRegisterInfo.h"
#include "rcc/CodeGen/TargetDescription.h"
-#include "rcc/MIR/MIPrinter.h"
#include <cstdint>
#include <iostream>
diff --git a/backend/unittests/MIR/MIRDocumentTest.cpp b/backend/unittests/MIR/MIRDocumentTest.cpp
index 52e5d65..7f1dfc3 100644
--- a/backend/unittests/MIR/MIRDocumentTest.cpp
+++ b/backend/unittests/MIR/MIRDocumentTest.cpp
@@ -1,10 +1,10 @@
// SPDX-License-Identifier: GPL-3.0-or-later
-#include "rcc/MIR/MIRDocument.h"
-
#include "Target/TestTarget.h"
#include "rcc/CodeGen/CompilationUnit.h"
#include "rcc/CodeGen/InstructionDescriptor.h"
+#include "rcc/CodeGen/MIRParser/MIRParser.h"
+#include "rcc/CodeGen/MIRPrinter.h"
#include "rcc/CodeGen/MachineBasicBlock.h"
#include "rcc/CodeGen/MachineCompilationStatus.h"
#include "rcc/CodeGen/MachineConstantPool.h"
diff --git a/backend/unittests/Tooling/PipelineDriverTest.cpp b/backend/unittests/Tooling/PipelineDriverTest.cpp
index 473b444..bed09be 100644
--- a/backend/unittests/Tooling/PipelineDriverTest.cpp
+++ b/backend/unittests/Tooling/PipelineDriverTest.cpp
@@ -3,8 +3,8 @@
#include "rcc/Tooling/PipelineDriver.h"
#include "ToolingTestSupport.h"
+#include "rcc/CodeGen/MIRPrinter.h"
#include "rcc/CodeGen/MachineFunction.h"
-#include "rcc/MIR/MIRDocument.h"
#include "rcc/Target/RISCV/RISCVTarget.h"
#include "rcc/Tooling/BackendDriver.h"