aboutsummaryrefslogtreecommitdiff
path: root/backend/lib/MIR
diff options
context:
space:
mode:
authorRichard-Qin-X <richard.qin.001@gmail.com>2026-08-10 20:36:50 +0800
committerRichard-Qin-X <richard.qin.001@gmail.com>2026-08-10 20:36:50 +0800
commitadcf8c9b1bd1b47e00bf859693112cea1f7e8c9f (patch)
tree59067aa71befab31678946dbf5e19f57131dd6f2 /backend/lib/MIR
parent3a57627e59ba18b6f9f9aae7bb60c390c0d3575a (diff)
downloadrcc-project-adcf8c9b1bd1b47e00bf859693112cea1f7e8c9f.tar.gz
rcc-project-adcf8c9b1bd1b47e00bf859693112cea1f7e8c9f.tar.xz
[Tooling] Complete backend tooling and validation
Diffstat (limited to 'backend/lib/MIR')
-rw-r--r--backend/lib/MIR/CMakeLists.txt27
-rw-r--r--backend/lib/MIR/MIParser.cpp11
-rw-r--r--backend/lib/MIR/MIRDataParser.cpp288
-rw-r--r--backend/lib/MIR/MIRDocument.cpp2745
-rw-r--r--backend/lib/MIR/MIRDocumentParser.cpp358
-rw-r--r--backend/lib/MIR/MIRDocumentParser.h131
-rw-r--r--backend/lib/MIR/MIRDocumentReferenceValidation.cpp410
-rw-r--r--backend/lib/MIR/MIRFrameParser.cpp451
-rw-r--r--backend/lib/MIR/MIRFunctionParser.cpp536
-rw-r--r--backend/lib/MIR/MIRFunctionRecordsParser.cpp301
-rw-r--r--backend/lib/MIR/MIRMetadataParser.cpp285
-rw-r--r--backend/lib/MIR/MIRMetadataTextParser.cpp259
-rw-r--r--backend/lib/MIR/MIRMetadataTextParser.h70
-rw-r--r--backend/lib/MIR/MIRParserUtilities.h85
14 files changed, 3211 insertions, 2746 deletions
diff --git a/backend/lib/MIR/CMakeLists.txt b/backend/lib/MIR/CMakeLists.txt
index 2ef63ed..f227390 100644
--- a/backend/lib/MIR/CMakeLists.txt
+++ b/backend/lib/MIR/CMakeLists.txt
@@ -6,8 +6,16 @@ set(
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
)
@@ -55,9 +63,20 @@ rcc_backend_register_format_files(
"${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"
${RCCBackendMIRHeaders}
@@ -67,8 +86,16 @@ rcc_backend_register_tidy_files(
"${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"
)
diff --git a/backend/lib/MIR/MIParser.cpp b/backend/lib/MIR/MIParser.cpp
index ecf4986..483c2f2 100644
--- a/backend/lib/MIR/MIParser.cpp
+++ b/backend/lib/MIR/MIParser.cpp
@@ -1098,14 +1098,21 @@ bool MIParser::Impl::parseRegisterOperand(bool IsDef, ParsedOperand &Result) {
} else if (at(MITokenKind::Identifier)) {
if (const RegisterClassDesc *Class =
Target.registers().registerClass(token().stringValue())) {
- if ((Info.RegClass && *Info.RegClass != Class->ID) || Info.Bank)
+ if ((Info.RegClass && *Info.RegClass != Class->ID) ||
+ (Info.Bank && *Info.Bank != Class->DefaultBank))
return fail("conflicting virtual register class");
auto SetClass = Function.registerInfo().setRegisterClass(Reg, Class->ID);
if (!SetClass)
return importError(token(), SetClass.error());
+ auto SetBank = Function.registerInfo().setRegisterBank(Reg, Class->DefaultBank);
+ if (!SetBank)
+ return importError(token(), SetBank.error());
} else if (const RegisterBankDesc *Bank =
Target.registers().registerBank(token().stringValue())) {
- if ((Info.Bank && *Info.Bank != Bank->ID) || Info.RegClass)
+ const RegisterClassDesc *ExistingClass =
+ Info.RegClass ? Target.registers().registerClass(*Info.RegClass) : nullptr;
+ if ((Info.Bank && *Info.Bank != Bank->ID) ||
+ (Info.RegClass && (ExistingClass == nullptr || ExistingClass->DefaultBank != Bank->ID)))
return fail("conflicting virtual register bank");
auto SetBank = Function.registerInfo().setRegisterBank(Reg, Bank->ID);
if (!SetBank)
diff --git a/backend/lib/MIR/MIRDataParser.cpp b/backend/lib/MIR/MIRDataParser.cpp
new file mode 100644
index 0000000..fc70f0f
--- /dev/null
+++ b/backend/lib/MIR/MIRDataParser.cpp
@@ -0,0 +1,288 @@
+// 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{
+ .DefaultPointerSizeInBits = Context.DefaultPointerSizeInBits,
+ .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 jump table kind '" + std::string(KindName) + "'");
+ }
+
+ 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
index 67e6ffb..ad9ec40 100644
--- a/backend/lib/MIR/MIRDocument.cpp
+++ b/backend/lib/MIR/MIRDocument.cpp
@@ -2,2755 +2,12 @@
#include "rcc/MIR/MIRDocument.h"
-#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/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/MIDebugSyntax.h"
-#include "rcc/MIR/MILexer.h"
+#include "MIRDocumentParser.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 <unordered_map>
-#include <unordered_set>
#include <utility>
-#include <vector>
namespace rcc::mir {
-namespace {
-
-[[nodiscard]] 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]] 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]] 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]] 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]] 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]] 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;
-}
-
-struct PendingJumpTable final {
- JumpTableIndex Index;
- std::uint32_t TextID = 0;
- JumpTableEntryEncoding Encoding = JumpTableEntryEncoding::BlockAddress;
- std::uint8_t EntrySize = 8;
- std::vector<std::string> Blocks;
-};
-
-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();
-
-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;
-};
-
-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());
- if (!Opcode) {
- return makeUnexpected(ErrorCode::ParseError, "unknown DIExpression opcode '" +
- std::string(token().stringValue()) + "'");
- }
- Elements.push_back(*Opcode);
- 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
-
-class MIRDocumentParser final {
-public:
- MIRDocumentParser(const TargetDescription &Target, std::string SourceName, std::string SourceText,
- MIRDocumentParserOptions Options)
- : Target(Target), Options(std::move(Options)),
- Result(std::move(SourceName), std::move(SourceText)) {}
-
- [[nodiscard]] MIRDocumentParseResult run();
-
-private:
- [[nodiscard]] bool fail(const TextRange &Range, std::string Message,
- std::string Category = "mir-yaml");
- [[nodiscard]] bool importError(const TextRange &Range, const Error &Failure,
- std::string Category = "mir-yaml");
- [[nodiscard]] const yaml::Node *node(yaml::NodeID ID) const noexcept {
- return Stream->node(ID);
- }
- [[nodiscard]] const yaml::Node *mappingValue(const yaml::Node &Mapping,
- std::string_view Key) const noexcept;
- [[nodiscard]] bool requireKind(const yaml::Node &Node, yaml::NodeKind Kind,
- std::string_view Description);
- [[nodiscard]] bool validateKeys(const yaml::Node &Mapping, std::span<const std::string_view> Keys,
- std::string_view Description);
- [[nodiscard]] bool scalar(const yaml::Node &Node, std::string_view &Value,
- std::string_view Description);
- [[nodiscard]] bool boolean(const yaml::Node &Node, bool &Value);
- [[nodiscard]] bool unsignedValue(const yaml::Node &Node, std::uint64_t &Value,
- std::string_view Description);
- [[nodiscard]] bool signedValue(const yaml::Node &Node, std::int64_t &Value,
- std::string_view Description);
- [[nodiscard]] bool alignment(const yaml::Node &Node, Alignment &Value, bool AllowZero);
- [[nodiscard]] bool physicalRegister(const yaml::Node &Node, PhysicalRegister &Reg);
- [[nodiscard]] bool physicalRegister(std::string_view Text, const TextRange &Range,
- PhysicalRegister &Reg);
- [[nodiscard]] bool virtualRegister(std::string_view Text, const TextRange &Range,
- const MIParserContext &Context, Register &Reg);
- [[nodiscard]] Expected<MachineMetadataReference>
- resolveMetadataReference(const MachineFunction &Function, std::uint32_t Slot) const;
- [[nodiscard]] bool parseVariableDebugInfo(const yaml::Node &Entry, MachineFunction &Function,
- MachineVariableDebugInfo &DebugInfo);
- [[nodiscard]] bool parseFunction(const yaml::Node &Root, MIRDocument &Document);
- [[nodiscard]] bool parseFunctionScalars(const yaml::Node &Root, MachineFunction &Function,
- MIRFunctionDocumentInfo &DocumentInfo);
- [[nodiscard]] bool recomputeProperties(const yaml::Node &Root, MachineFunction &Function,
- const MIRFunctionDocumentInfo &DocumentInfo);
- [[nodiscard]] bool parseRegisters(const yaml::Node &Root, MachineFunction &Function,
- MIParserContext &Context);
- [[nodiscard]] bool parseMetadata(const yaml::Node &Root, MachineFunction &Function,
- MIParserContext &Context);
- [[nodiscard]] bool parseStackObjects(const yaml::Node &Root, MachineFunction &Function,
- MIParserContext &Context);
- [[nodiscard]] bool parseConstants(const yaml::Node &Root, MachineFunction &Function,
- MIParserContext &Context);
- [[nodiscard]] bool parseFrameState(const yaml::Node &Root, MachineFunction &Function,
- const MIParserContext &Context);
- [[nodiscard]] bool reserveJumpTables(const yaml::Node &Root, MachineFunction &Function,
- MIParserContext &Context,
- std::vector<PendingJumpTable> &Pending);
- [[nodiscard]] bool defineJumpTables(MachineFunction &Function,
- std::span<const PendingJumpTable> Pending);
- [[nodiscard]] bool parseBody(const yaml::Node &Root, MachineFunction &Function,
- MIParserContext &Context);
- [[nodiscard]] TextRange bodyDiagnosticRange(const yaml::Node &Body,
- const TextRange &BodyRange) const noexcept;
- [[nodiscard]] bool parseFunctionRecords(const yaml::Node &Root, MachineFunction &Function);
- [[nodiscard]] bool parseTargetState(const yaml::Node &Root, MachineFunction &Function);
- [[nodiscard]] bool convertTargetNode(const yaml::Node &Source, MIRTargetNode &Destination);
- [[nodiscard]] bool validateDocumentReferences(const MachineFunction &Function);
- [[nodiscard]] std::string_view effectiveDataLayout() const noexcept;
- [[nodiscard]] LLVMConstantSyntaxOptions pointerLayoutOptions() const noexcept;
-
- [[nodiscard]] MachineBasicBlock *blockAt(MachineFunction &Function,
- std::uint32_t Number) const noexcept;
- [[nodiscard]] MachineInstr *instructionAt(MachineFunction &Function, std::uint32_t Block,
- std::uint32_t Offset) const noexcept;
- [[nodiscard]] bool parseBlockReference(std::string_view Text, const TextRange &Range,
- MachineFunction &Function, MachineBasicBlock *&Block);
- [[nodiscard]] bool parseFrameReference(const yaml::Node &Node, const MIParserContext &Context,
- FrameIndex &Index);
- [[nodiscard]] bool parseSaveRestorePoints(const yaml::Node &Node, MachineFunction &Function,
- std::vector<MachineFrameSaveRestorePoint> &Points);
-
- const TargetDescription &Target;
- MIRDocumentParserOptions Options;
- MIRDocumentParseResult Result;
- const yaml::Stream *Stream = nullptr;
- MIRDocument *ActiveDocument = nullptr;
- MachineFunction *ActiveFunction = nullptr;
-};
-
-bool MIRDocumentParser::fail(const TextRange &Range, std::string Message, std::string Category) {
- if (Result.Diagnostics.empty()) {
- if (ActiveFunction != nullptr)
- Message = "function '" + std::string(ActiveFunction->name()) + "': " + std::move(Message);
- Result.Diagnostics.push_back(TextDiagnostic{
- .Severity = DiagnosticSeverity::Error,
- .Category = std::move(Category),
- .Message = std::move(Message),
- .SourceName = std::string(Result.Source.name()),
- .Range = Range,
- });
- }
- return true;
-}
-
-bool MIRDocumentParser::importError(const TextRange &Range, const Error &Failure,
- std::string Category) {
- return fail(Range, std::string(Failure.message()), std::move(Category));
-}
-
-const yaml::Node *MIRDocumentParser::mappingValue(const yaml::Node &Mapping,
- std::string_view Key) const noexcept {
- const auto Value = Mapping.mappingValue(Key);
- return Value ? node(*Value) : nullptr;
-}
-
-std::string_view MIRDocumentParser::effectiveDataLayout() const noexcept {
- if (ActiveDocument != nullptr) {
- if (const EmbeddedIRReferenceIndex *Embedded = ActiveDocument->embeddedIR()) {
- if (const auto DataLayout = Embedded->dataLayout())
- return *DataLayout;
- }
- }
- return Options.DataLayout;
-}
-
-LLVMConstantSyntaxOptions MIRDocumentParser::pointerLayoutOptions() const noexcept {
- return {
- .DefaultPointerSizeInBits = Options.DefaultPointerSizeInBits,
- .DataLayout = effectiveDataLayout(),
- .NamedTypes = {},
- };
-}
-
-bool MIRDocumentParser::requireKind(const yaml::Node &Node, yaml::NodeKind Kind,
- std::string_view Description) {
- if (Node.kind() == Kind)
- return false;
- return fail(Node.range(), std::string(Description) + " has the wrong YAML node kind");
-}
-
-bool MIRDocumentParser::validateKeys(const yaml::Node &Mapping,
- std::span<const std::string_view> Keys,
- std::string_view Description) {
- for (const yaml::MappingEntry &Entry : Mapping.mapping()) {
- if (std::ranges::find(Keys, Entry.Key) == Keys.end()) {
- return fail(Entry.KeyRange,
- "unknown " + std::string(Description) + " key '" + Entry.Key + "'");
- }
- }
- return false;
-}
-
-bool MIRDocumentParser::scalar(const yaml::Node &Node, std::string_view &Value,
- std::string_view Description) {
- if (requireKind(Node, yaml::NodeKind::Scalar, Description))
- return true;
- Value = Node.scalar();
- return false;
-}
-
-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,
- const MIParserContext &Context, Register &Reg) {
- const auto ID = parsePrefixedID(Text, "%");
- if (!ID)
- return fail(Range, "virtual register must use the '%N' MIR spelling");
- const auto Found =
- std::ranges::find_if(Context.VirtualRegisters,
- [ID](const MIVirtualRegisterSlot &Slot) { return Slot.TextID == *ID; });
- if (Found == Context.VirtualRegisters.end())
- return fail(Range, "reference to undefined virtual register '%" + std::to_string(*ID) + "'");
- Reg = Found->Reg;
- return false;
-}
-
-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(ExpressionNode->range(), 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;
-}
-
-MIRDocumentParseResult MIRDocumentParser::run() {
- yaml::ParseResult YAMLResult = yaml::parse(std::string(Result.Source.name()),
- std::string(Result.Source.text()), Options.YAML);
- if (!YAMLResult.succeeded()) {
- const auto Diagnostics = YAMLResult.diagnostics();
- Result.Diagnostics.assign(Diagnostics.begin(), Diagnostics.end());
- return std::move(Result);
- }
- Stream = YAMLResult.stream();
- if (Stream == nullptr || Stream->documents().empty()) {
- static_cast<void>(fail(Result.Source.range(0, 0), "MIR stream contains no YAML documents"));
- return std::move(Result);
- }
-
- MIRDocument Staging;
- ActiveDocument = &Staging;
- std::size_t FirstFunction = 0;
- const yaml::Node *FirstRoot = node(Stream->documents().front().Root);
- if (FirstRoot != nullptr && FirstRoot->kind() == yaml::NodeKind::Scalar) {
- EmbeddedIRIndexResult Embedded = indexEmbeddedIR(
- std::string(Result.Source.name()) + ":embedded-ir", std::string(FirstRoot->scalar()));
- if (!Embedded.succeeded()) {
- const auto Diagnostics = Embedded.diagnostics();
- Result.Diagnostics.assign(Diagnostics.begin(), Diagnostics.end());
- return std::move(Result);
- }
- Staging.EmbeddedIR = std::move(Embedded).takeIndex();
- FirstFunction = 1;
- }
-
- if (const EmbeddedIRReferenceIndex *Embedded = Staging.embeddedIR()) {
- if (const auto DataLayout = Embedded->dataLayout();
- DataLayout && !Options.DataLayout.empty() && *DataLayout != Options.DataLayout) {
- static_cast<void>(fail(Stream->documents().front().Range,
- "explicit DataLayout does not match embedded LLVM IR data layout"));
- return std::move(Result);
- }
- }
- if (!effectiveDataLayout().empty()) {
- auto PointerSize = llvmPointerSizeInBits(0, pointerLayoutOptions());
- if (!PointerSize) {
- static_cast<void>(
- importError(Stream->documents().front().Range, PointerSize.error(), "mir-data-layout"));
- return std::move(Result);
- }
- }
-
- if (FirstFunction == Stream->documents().size()) {
- static_cast<void>(fail(Stream->documents().front().Range,
- "MIR stream must contain at least one machine function document"));
- return std::move(Result);
- }
-
- for (std::size_t Index = FirstFunction; Index < Stream->documents().size(); ++Index) {
- const yaml::Node *Root = node(Stream->documents()[Index].Root);
- if (Root == nullptr || requireKind(*Root, yaml::NodeKind::Mapping, "machine function") ||
- parseFunction(*Root, Staging)) {
- return std::move(Result);
- }
- }
-
- auto UnitResult = Staging.Unit.verify();
- if (!UnitResult) {
- static_cast<void>(
- fail(Stream->documents().back().Range, "parsed compilation unit failed verification: " +
- std::string(UnitResult.error().message())));
- return std::move(Result);
- }
- ActiveDocument = nullptr;
- Result.Document = std::move(Staging);
- return std::move(Result);
-}
-
-bool MIRDocumentParser::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;
- 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);
- const auto Operands = Instruction->operands();
- for (std::size_t Index = 0; Index < Operands.size(); ++Index) {
- const MachineOperand &Operand = Operands[Index];
- 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;
- }
- }
- }
- 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 Name) -> bool {
- if (Explicit && *Explicit && !Computed) {
- return fail(Root.range(), "explicit property '" + std::string(Name) +
- "' conflicts with the parsed machine function");
- }
- if (Explicit.value_or(Computed))
- Function.properties().set(Property);
- else
- Function.properties().clear(Property);
- return false;
- };
- if (ApplyComputed(DocumentInfo.NoPHIs, NoPHIs, MachineFunctionProperty::NoPHIs, "noPhis") ||
- ApplyComputed(DocumentInfo.IsSSA, IsSSA, MachineFunctionProperty::IsSSA, "isSSA") ||
- ApplyComputed(DocumentInfo.NoVRegs, NoVRegs, MachineFunctionProperty::NoVRegs, "noVRegs")) {
- return true;
- }
-
- if (DocumentInfo.HasFakeUses && !*DocumentInfo.HasFakeUses && HasFakeUse) {
- return fail(Root.range(),
- "explicit property 'hasFakeUses: false' conflicts with a fake-use instruction");
- }
- if (DocumentInfo.HasFakeUses.value_or(HasFakeUse))
- Function.attributes().set(MachineFunctionAttribute::HasFakeUses);
- else
- Function.attributes().clear(MachineFunctionAttribute::HasFakeUses);
-
- if (HasTiedOperands && AllTiedOperandsRewritten)
- Function.properties().set(MachineFunctionProperty::TiedOpsRewritten);
- else
- Function.properties().clear(MachineFunctionProperty::TiedOpsRewritten);
- 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());
- } 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")) {
- PhysicalRegister Reg;
- if (physicalRegister(*Preferred, 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(),
- "unknown virtual-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;
- Register Virtual;
- if (scalar(*VirtualNode, Text, "virtual register") ||
- virtualRegister(Text, VirtualNode->range(), 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;
-}
-
-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;
-
- struct Definition final {
- const yaml::Node *Node = nullptr;
- std::uint32_t Slot = 0;
- bool IsDistinct = false;
- std::string_view Elements;
- };
- std::vector<Definition> Definitions;
- std::unordered_set<std::uint32_t> Slots;
-
- 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;
- }
- Text = trim(Text);
- if (!Text.starts_with('!'))
- return fail(DefinitionNode->range(), "machine metadata definition must start with '!N'");
- const std::size_t Equals = Text.find('=');
- if (Equals == std::string_view::npos)
- return fail(DefinitionNode->range(), "machine metadata definition requires '='");
- std::uint32_t Slot = 0;
- if (!parseUnsigned32(trim(Text.substr(1, Equals - 1)), Slot))
- return fail(DefinitionNode->range(), "machine metadata definition has an invalid slot");
- if (!Slots.insert(Slot).second)
- return fail(DefinitionNode->range(), "duplicate machine metadata slot");
- std::string_view Right = trim(Text.substr(Equals + 1));
- bool IsDistinct = false;
- if (Right.starts_with("distinct")) {
- Right = trim(Right.substr(std::string_view("distinct").size()));
- IsDistinct = true;
- }
- if (!Right.starts_with("!{") || !Right.ends_with('}'))
- return fail(DefinitionNode->range(), "machine metadata node must be a metadata tuple");
- Definitions.push_back({.Node = DefinitionNode,
- .Slot = Slot,
- .IsDistinct = IsDistinct,
- .Elements = Right.substr(2, Right.size() - 3)});
- Context.Metadata.push_back(
- {.TextID = Slot, .Reference = MachineMetadataReference::machineFunction(Slot)});
- }
-
- auto ResolveReference = [this,
- &Slots](std::uint32_t Slot,
- const TextRange &Range) -> Expected<MachineMetadataElement> {
- MachineMetadataReference Reference;
- if (ActiveDocument->EmbeddedIR != std::nullopt &&
- ActiveDocument->EmbeddedIR->metadata(std::to_string(Slot)) != nullptr) {
- Reference = MachineMetadataReference::embeddedIR(Slot);
- } else if (Slots.contains(Slot)) {
- Reference = MachineMetadataReference::machineFunction(Slot);
- } else {
- return makeUnexpected(ErrorCode::ParseError, "metadata tuple references undefined slot '!" +
- std::to_string(Slot) + "' at line " +
- std::to_string(Range.Begin.Line));
- }
- return MachineMetadataElement::reference(Reference);
- };
-
- for (const Definition &Definition : Definitions) {
- MachineMetadataNode NewNode{
- .Slot = Definition.Slot,
- .IsDistinct = Definition.IsDistinct,
- .Elements = {},
- };
- std::string_view Remaining = trim(Definition.Elements);
- while (!Remaining.empty()) {
- std::size_t End = 0;
- bool InString = false;
- bool Escaped = false;
- for (; End < Remaining.size(); ++End) {
- const char Character = Remaining[End];
- if (InString && Escaped) {
- Escaped = false;
- continue;
- }
- if (InString && Character == '\\') {
- Escaped = true;
- continue;
- }
- if (Character == '"') {
- InString = !InString;
- continue;
- }
- if (!InString && Character == ',')
- break;
- }
- if (InString)
- return fail(Definition.Node->range(), "unterminated machine metadata string");
- std::string_view Element = trim(Remaining.substr(0, End));
- if (Element.empty())
- return fail(Definition.Node->range(), "empty machine metadata tuple element");
-
- if (Element == "null") {
- NewNode.Elements.push_back(MachineMetadataElement::null());
- } else if (Element.starts_with('!') && Element.size() > 1 && Element[1] >= '0' &&
- Element[1] <= '9') {
- std::uint32_t ReferenceSlot = 0;
- if (!parseUnsigned32(Element.substr(1), ReferenceSlot))
- return fail(Definition.Node->range(), "invalid metadata tuple reference");
- auto Reference = ResolveReference(ReferenceSlot, Definition.Node->range());
- if (!Reference)
- return importError(Definition.Node->range(), Reference.error());
- NewNode.Elements.push_back(std::move(*Reference));
- } else if (Element.starts_with("!\"") && Element.ends_with('"')) {
- std::string Value;
- std::string_view Encoded = Element.substr(2, Element.size() - 3);
- for (std::size_t Index = 0; Index < Encoded.size(); ++Index) {
- if (Encoded[Index] == '\\' && Index + 1 < Encoded.size()) {
- const char EscapedCharacter = Encoded[++Index];
- if (EscapedCharacter != '\\' && EscapedCharacter != '"')
- return fail(Definition.Node->range(), "unsupported machine metadata string escape");
- Value.push_back(EscapedCharacter);
- } else {
- Value.push_back(Encoded[Index]);
- }
- }
- NewNode.Elements.push_back(MachineMetadataElement::string(std::move(Value)));
- } else {
- return fail(Definition.Node->range(), "unsupported machine metadata tuple element");
- }
-
- if (End == Remaining.size())
- Remaining = {};
- else
- Remaining = trim(Remaining.substr(End + 1));
- }
- auto AddResult = Function.metadata().addNode(std::move(NewNode));
- if (!AddResult)
- return importError(Definition.Node->range(), AddResult.error());
- }
- return false;
-}
-
-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") || Name.empty())
- return fail(NameNode->range(), "stack object name cannot be 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")) {
- PhysicalRegister Reg;
- if (physicalRegister(*SavedNode, 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::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{
- .DefaultPointerSizeInBits = Context.DefaultPointerSizeInBits,
- .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::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;
- }
- 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)) {
- 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");
- }
- 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;
-}
-
-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 jump table kind '" + std::string(KindName) + "'");
- }
-
- 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;
- 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;
-}
-
-TextRange MIRDocumentParser::bodyDiagnosticRange(const yaml::Node &Body,
- const TextRange &BodyRange) const noexcept {
- if (Body.scalarStyle() != yaml::ScalarStyle::Literal)
- return Body.range();
-
- const std::size_t FirstBodyLine = Body.range().Begin.Line + 1U;
- const std::size_t LastBodyLine = Body.range().End.Line;
- if (FirstBodyLine > LastBodyLine || FirstBodyLine == 0)
- return Body.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 Body.range();
-
- const TextPosition Fallback = Body.range().Begin;
- const auto MapPosition = [this, FirstBodyLine, LastBodyLine, Indent,
- Fallback](TextPosition Position) {
- const std::size_t SourceLine = FirstBodyLine + Position.Line - 1U;
- if (Position.Line == 0 || SourceLine > LastBodyLine || SourceLine > Result.Source.lineCount()) {
- return Fallback;
- }
- 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(BodyRange.Begin), .End = MapPosition(BodyRange.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 (Options.DefaultPointerSizeInBits != 0)
- Context.DefaultPointerSizeInBits = Options.DefaultPointerSizeInBits;
- 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
- : bodyDiagnosticRange(*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::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;
- 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;
- 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;
- Text = trim(Text);
- if (!Text.starts_with("bb_id"))
- return fail(TargetNode->range(), "prefetch target must start with 'bb_id'");
- Text = trim(Text.substr(std::string_view("bb_id").size()));
- std::array<std::uint32_t, 3> Values{};
- for (std::size_t Index = 0; Index < Values.size(); ++Index) {
- const std::size_t Comma = Text.find(',');
- const std::string_view Part = Index + 1 == Values.size() ? Text : Text.substr(0, Comma);
- if ((Index + 1 != Values.size() && Comma == std::string_view::npos) ||
- !parseUnsigned32(trim(Part), Values[Index])) {
- return fail(TargetNode->range(), "prefetch target has invalid numeric fields");
- }
- if (Index + 1 != Values.size())
- Text = trim(Text.substr(Comma + 1));
- }
- 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;
-}
-
-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) {
- 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 {};
-}
-
-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());
-}
-
-bool MIRDocumentParser::parseFunction(const yaml::Node &Root, MIRDocument &Document) {
- constexpr std::array Keys{
- std::string_view{"name"},
- std::string_view{"alignment"},
- std::string_view{"exposesReturnsTwice"},
- std::string_view{"legalized"},
- std::string_view{"regBankSelected"},
- std::string_view{"selected"},
- std::string_view{"failedISel"},
- std::string_view{"tracksRegLiveness"},
- std::string_view{"hasWinCFI"},
- std::string_view{"noPhis"},
- std::string_view{"isSSA"},
- std::string_view{"noVRegs"},
- std::string_view{"hasFakeUses"},
- std::string_view{"callsEHReturn"},
- std::string_view{"callsUnwindInit"},
- std::string_view{"hasEHContTarget"},
- std::string_view{"hasEHScopes"},
- std::string_view{"hasEHFunclets"},
- std::string_view{"isOutlined"},
- std::string_view{"debugInstrRef"},
- std::string_view{"failsVerification"},
- std::string_view{"tracksDebugUserValues"},
- std::string_view{"registers"},
- std::string_view{"liveins"},
- std::string_view{"calleeSavedRegisters"},
- std::string_view{"frameInfo"},
- std::string_view{"fixedStack"},
- std::string_view{"stack"},
- std::string_view{"entry_values"},
- std::string_view{"callSites"},
- std::string_view{"debugValueSubstitutions"},
- std::string_view{"constants"},
- std::string_view{"machineFunctionInfo"},
- std::string_view{"jumpTable"},
- std::string_view{"machineMetadataNodes"},
- std::string_view{"calledGlobals"},
- std::string_view{"prefetch-targets"},
- std::string_view{"body"},
- };
- if (validateKeys(Root, Keys, "machine function"))
- return true;
- const yaml::Node *NameNode = mappingValue(Root, "name");
- if (NameNode == nullptr)
- return fail(Root.range(), "machine function requires 'name'");
- std::string_view Name;
- if (scalar(*NameNode, Name, "machine function name") || Name.empty())
- return fail(NameNode->range(), "machine function name cannot be empty");
-
- if (Document.EmbeddedIR) {
- const EmbeddedIRFunction *IRFunction = Document.EmbeddedIR->function(Name);
- if (IRFunction == nullptr)
- return fail(NameNode->range(),
- "machine function has no corresponding embedded LLVM IR function");
- }
-
- auto Created = Document.Unit.createMachineFunction(std::string(Name));
- if (!Created)
- return importError(NameNode->range(), Created.error());
- MachineFunction &Function = **Created;
- ActiveFunction = &Function;
-
- MIRFunctionDocumentInfo DocumentInfo{
- .Function = Function.stableID(),
- .NoPHIs = std::nullopt,
- .IsSSA = std::nullopt,
- .NoVRegs = std::nullopt,
- .HasFakeUses = std::nullopt,
- };
- MIParserContext Context;
- Context.DefaultPointerSizeInBits = Options.DefaultPointerSizeInBits;
-
- std::vector<PendingJumpTable> PendingJumpTables;
- if (parseFunctionScalars(Root, Function, DocumentInfo) ||
- parseMetadata(Root, Function, Context) || parseRegisters(Root, Function, Context) ||
- parseStackObjects(Root, Function, Context) || parseConstants(Root, Function, Context) ||
- reserveJumpTables(Root, Function, Context, PendingJumpTables) ||
- parseBody(Root, Function, Context) || defineJumpTables(Function, PendingJumpTables) ||
- recomputeProperties(Root, Function, DocumentInfo) ||
- parseFrameState(Root, Function, Context) || parseTargetState(Root, Function) ||
- parseFunctionRecords(Root, Function) || validateDocumentReferences(Function)) {
- return true;
- }
-
- if (Options.VerifyMachineFunctions) {
- auto Verified = MachineVerifier::verify(Function, Target);
- if (!Verified)
- return importError(Root.range(), Verified.error(), "mir-verifier");
- }
- Document.FunctionInfo.push_back(DocumentInfo);
- ActiveFunction = nullptr;
- return false;
-}
MIRDocument::MIRDocument() = default;
MIRDocument::MIRDocument(CompilationUnit Unit) : Unit(std::move(Unit)) {}
diff --git a/backend/lib/MIR/MIRDocumentParser.cpp b/backend/lib/MIR/MIRDocumentParser.cpp
new file mode 100644
index 0000000..defcce9
--- /dev/null
+++ b/backend/lib/MIR/MIRDocumentParser.cpp
@@ -0,0 +1,358 @@
+// 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::fail(const TextRange &Range, std::string Message, std::string Category) {
+ if (Result.Diagnostics.empty()) {
+ if (ActiveFunction != nullptr)
+ Message = "function '" + std::string(ActiveFunction->name()) + "': " + std::move(Message);
+ Result.Diagnostics.push_back(TextDiagnostic{
+ .Severity = DiagnosticSeverity::Error,
+ .Category = std::move(Category),
+ .Message = std::move(Message),
+ .SourceName = std::string(Result.Source.name()),
+ .Range = Range,
+ });
+ }
+ return true;
+}
+
+bool MIRDocumentParser::importError(const TextRange &Range, const Error &Failure,
+ std::string Category) {
+ return fail(Range, std::string(Failure.message()), std::move(Category));
+}
+
+const yaml::Node *MIRDocumentParser::mappingValue(const yaml::Node &Mapping,
+ std::string_view Key) const noexcept {
+ const auto Value = Mapping.mappingValue(Key);
+ return Value ? node(*Value) : nullptr;
+}
+
+std::string_view MIRDocumentParser::effectiveDataLayout() const noexcept {
+ if (ActiveDocument != nullptr) {
+ if (const EmbeddedIRReferenceIndex *Embedded = ActiveDocument->embeddedIR()) {
+ if (const auto DataLayout = Embedded->dataLayout())
+ return *DataLayout;
+ }
+ }
+ return Options.DataLayout;
+}
+
+LLVMConstantSyntaxOptions MIRDocumentParser::pointerLayoutOptions() const noexcept {
+ return {
+ .DefaultPointerSizeInBits = Options.DefaultPointerSizeInBits,
+ .DataLayout = effectiveDataLayout(),
+ .NamedTypes = {},
+ };
+}
+
+bool MIRDocumentParser::requireKind(const yaml::Node &Node, yaml::NodeKind Kind,
+ std::string_view Description) {
+ if (Node.kind() == Kind)
+ return false;
+ return fail(Node.range(), std::string(Description) + " has the wrong YAML node kind");
+}
+
+bool MIRDocumentParser::validateKeys(const yaml::Node &Mapping,
+ std::span<const std::string_view> Keys,
+ std::string_view Description) {
+ for (const yaml::MappingEntry &Entry : Mapping.mapping()) {
+ if (std::ranges::find(Keys, Entry.Key) == Keys.end()) {
+ return fail(Entry.KeyRange,
+ "unknown " + std::string(Description) + " key '" + Entry.Key + "'");
+ }
+ }
+ return false;
+}
+
+bool MIRDocumentParser::scalar(const yaml::Node &Node, std::string_view &Value,
+ std::string_view Description) {
+ if (requireKind(Node, yaml::NodeKind::Scalar, Description))
+ return true;
+ Value = Node.scalar();
+ return false;
+}
+
+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,
+ const MIParserContext &Context, Register &Reg) {
+ const auto ID = parsePrefixedID(Text, "%");
+ if (!ID)
+ return fail(Range, "virtual register must use the '%N' MIR spelling");
+ const auto Found =
+ std::ranges::find_if(Context.VirtualRegisters,
+ [ID](const MIVirtualRegisterSlot &Slot) { return Slot.TextID == *ID; });
+ if (Found == Context.VirtualRegisters.end())
+ return fail(Range, "reference to undefined virtual register '%" + std::to_string(*ID) + "'");
+ 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);
+ if (!YAMLResult.succeeded()) {
+ const auto Diagnostics = YAMLResult.diagnostics();
+ Result.Diagnostics.assign(Diagnostics.begin(), Diagnostics.end());
+ return std::move(Result);
+ }
+ Stream = YAMLResult.stream();
+ if (Stream == nullptr || Stream->documents().empty()) {
+ static_cast<void>(fail(Result.Source.range(0, 0), "MIR stream contains no YAML documents"));
+ return std::move(Result);
+ }
+
+ MIRDocument Staging;
+ ActiveDocument = &Staging;
+ std::size_t FirstFunction = 0;
+ const yaml::Node *FirstRoot = node(Stream->documents().front().Root);
+ if (FirstRoot != nullptr && FirstRoot->kind() == yaml::NodeKind::Scalar) {
+ EmbeddedIRIndexResult Embedded = indexEmbeddedIR(
+ std::string(Result.Source.name()) + ":embedded-ir", std::string(FirstRoot->scalar()));
+ if (!Embedded.succeeded()) {
+ const auto Diagnostics = Embedded.diagnostics();
+ Result.Diagnostics.assign(Diagnostics.begin(), Diagnostics.end());
+ return std::move(Result);
+ }
+ Staging.EmbeddedIR = std::move(Embedded).takeIndex();
+ FirstFunction = 1;
+ }
+
+ if (const EmbeddedIRReferenceIndex *Embedded = Staging.embeddedIR()) {
+ if (const auto DataLayout = Embedded->dataLayout();
+ DataLayout && !Options.DataLayout.empty() && *DataLayout != Options.DataLayout) {
+ static_cast<void>(fail(Stream->documents().front().Range,
+ "explicit DataLayout does not match embedded LLVM IR data layout"));
+ return std::move(Result);
+ }
+ }
+ if (!effectiveDataLayout().empty()) {
+ auto PointerSize = llvmPointerSizeInBits(0, pointerLayoutOptions());
+ if (!PointerSize) {
+ static_cast<void>(
+ importError(Stream->documents().front().Range, PointerSize.error(), "mir-data-layout"));
+ return std::move(Result);
+ }
+ }
+
+ if (FirstFunction == Stream->documents().size()) {
+ static_cast<void>(fail(Stream->documents().front().Range,
+ "MIR stream must contain at least one machine function document"));
+ return std::move(Result);
+ }
+
+ for (std::size_t Index = FirstFunction; Index < Stream->documents().size(); ++Index) {
+ const yaml::Node *Root = node(Stream->documents()[Index].Root);
+ if (Root == nullptr || requireKind(*Root, yaml::NodeKind::Mapping, "machine function") ||
+ parseFunction(*Root, Staging)) {
+ return std::move(Result);
+ }
+ }
+
+ auto UnitResult = Staging.Unit.verify();
+ if (!UnitResult) {
+ static_cast<void>(
+ fail(Stream->documents().back().Range, "parsed compilation unit failed verification: " +
+ std::string(UnitResult.error().message())));
+ return std::move(Result);
+ }
+ ActiveDocument = nullptr;
+ Result.Document = std::move(Staging);
+ return std::move(Result);
+}
+
+bool MIRDocumentParser::parseFunction(const yaml::Node &Root, MIRDocument &Document) {
+ constexpr std::array Keys{
+ std::string_view{"name"},
+ std::string_view{"alignment"},
+ std::string_view{"exposesReturnsTwice"},
+ std::string_view{"legalized"},
+ std::string_view{"regBankSelected"},
+ std::string_view{"selected"},
+ std::string_view{"failedISel"},
+ std::string_view{"tracksRegLiveness"},
+ std::string_view{"hasWinCFI"},
+ std::string_view{"noPhis"},
+ std::string_view{"isSSA"},
+ std::string_view{"noVRegs"},
+ std::string_view{"hasFakeUses"},
+ std::string_view{"callsEHReturn"},
+ std::string_view{"callsUnwindInit"},
+ std::string_view{"hasEHContTarget"},
+ std::string_view{"hasEHScopes"},
+ std::string_view{"hasEHFunclets"},
+ std::string_view{"isOutlined"},
+ std::string_view{"debugInstrRef"},
+ std::string_view{"failsVerification"},
+ std::string_view{"tracksDebugUserValues"},
+ std::string_view{"registers"},
+ std::string_view{"liveins"},
+ std::string_view{"calleeSavedRegisters"},
+ std::string_view{"frameInfo"},
+ std::string_view{"fixedStack"},
+ std::string_view{"stack"},
+ std::string_view{"entry_values"},
+ std::string_view{"callSites"},
+ std::string_view{"debugValueSubstitutions"},
+ std::string_view{"constants"},
+ std::string_view{"machineFunctionInfo"},
+ std::string_view{"jumpTable"},
+ std::string_view{"machineMetadataNodes"},
+ std::string_view{"calledGlobals"},
+ std::string_view{"prefetch-targets"},
+ std::string_view{"body"},
+ };
+ if (validateKeys(Root, Keys, "machine function"))
+ return true;
+ const yaml::Node *NameNode = mappingValue(Root, "name");
+ if (NameNode == nullptr)
+ return fail(Root.range(), "machine function requires 'name'");
+ std::string_view Name;
+ if (scalar(*NameNode, Name, "machine function name") || Name.empty())
+ return fail(NameNode->range(), "machine function name cannot be empty");
+
+ if (Document.EmbeddedIR) {
+ const EmbeddedIRFunction *IRFunction = Document.EmbeddedIR->function(Name);
+ if (IRFunction == nullptr)
+ return fail(NameNode->range(),
+ "machine function has no corresponding embedded LLVM IR function");
+ }
+
+ auto Created = Document.Unit.createMachineFunction(std::string(Name));
+ if (!Created)
+ return importError(NameNode->range(), Created.error());
+ MachineFunction &Function = **Created;
+ ActiveFunction = &Function;
+
+ MIRFunctionDocumentInfo DocumentInfo{
+ .Function = Function.stableID(),
+ .NoPHIs = std::nullopt,
+ .IsSSA = std::nullopt,
+ .NoVRegs = std::nullopt,
+ .HasFakeUses = std::nullopt,
+ };
+ MIParserContext Context;
+ Context.DefaultPointerSizeInBits = Options.DefaultPointerSizeInBits;
+
+ std::vector<PendingJumpTable> PendingJumpTables;
+ if (parseFunctionScalars(Root, Function, DocumentInfo) ||
+ parseMetadata(Root, Function, Context) || parseRegisters(Root, Function, Context) ||
+ parseStackObjects(Root, Function, Context) || parseConstants(Root, Function, Context) ||
+ reserveJumpTables(Root, Function, Context, PendingJumpTables) ||
+ parseBody(Root, Function, Context) || defineJumpTables(Function, PendingJumpTables) ||
+ parseFrameState(Root, Function, Context) ||
+ recomputeProperties(Root, Function, DocumentInfo) || parseTargetState(Root, Function) ||
+ parseFunctionRecords(Root, Function) || validateDocumentReferences(Function)) {
+ return true;
+ }
+
+ if (Options.VerifyMachineFunctions) {
+ auto Verified = MachineVerifier::verify(Function, Target);
+ if (!Verified)
+ return importError(Root.range(), Verified.error(), "mir-verifier");
+ }
+ Document.FunctionInfo.push_back(DocumentInfo);
+ ActiveFunction = nullptr;
+ return false;
+}
+
+} // namespace rcc::mir
diff --git a/backend/lib/MIR/MIRDocumentParser.h b/backend/lib/MIR/MIRDocumentParser.h
new file mode 100644
index 0000000..53a31b4
--- /dev/null
+++ b/backend/lib/MIR/MIRDocumentParser.h
@@ -0,0 +1,131 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+// NOLINTNEXTLINE(llvm-header-guard)
+#ifndef RCC_LIB_MIR_MIRDOCUMENTPARSER_H
+#define RCC_LIB_MIR_MIRDOCUMENTPARSER_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 <cstdint>
+#include <span>
+#include <string>
+#include <string_view>
+#include <utility>
+#include <vector>
+
+namespace rcc {
+
+class MachineBasicBlock;
+class MachineFunction;
+class MachineInstr;
+
+namespace mir {
+
+struct PendingJumpTable final {
+ JumpTableIndex Index;
+ std::uint32_t TextID = 0;
+ JumpTableEntryEncoding Encoding = JumpTableEntryEncoding::BlockAddress;
+ std::uint8_t EntrySize = 8;
+ std::vector<std::string> Blocks;
+};
+
+class MIRDocumentParser final {
+public:
+ MIRDocumentParser(const TargetDescription &Target, std::string SourceName, std::string SourceText,
+ MIRDocumentParserOptions Options)
+ : Target(Target), Options(std::move(Options)),
+ Result(std::move(SourceName), std::move(SourceText)) {}
+
+ [[nodiscard]] MIRDocumentParseResult run();
+
+private:
+ [[nodiscard]] bool fail(const TextRange &Range, std::string Message,
+ std::string Category = "mir-yaml");
+ [[nodiscard]] bool importError(const TextRange &Range, const Error &Failure,
+ std::string Category = "mir-yaml");
+ [[nodiscard]] const yaml::Node *node(yaml::NodeID ID) const noexcept {
+ return Stream->node(ID);
+ }
+ [[nodiscard]] const yaml::Node *mappingValue(const yaml::Node &Mapping,
+ std::string_view Key) const noexcept;
+ [[nodiscard]] bool requireKind(const yaml::Node &Node, yaml::NodeKind Kind,
+ std::string_view Description);
+ [[nodiscard]] bool validateKeys(const yaml::Node &Mapping, std::span<const std::string_view> Keys,
+ std::string_view Description);
+ [[nodiscard]] bool scalar(const yaml::Node &Node, std::string_view &Value,
+ std::string_view Description);
+ [[nodiscard]] bool boolean(const yaml::Node &Node, bool &Value);
+ [[nodiscard]] bool unsignedValue(const yaml::Node &Node, std::uint64_t &Value,
+ std::string_view Description);
+ [[nodiscard]] bool signedValue(const yaml::Node &Node, std::int64_t &Value,
+ std::string_view Description);
+ [[nodiscard]] bool alignment(const yaml::Node &Node, Alignment &Value, bool AllowZero);
+ [[nodiscard]] bool physicalRegister(const yaml::Node &Node, PhysicalRegister &Reg);
+ [[nodiscard]] bool physicalRegister(std::string_view Text, const TextRange &Range,
+ PhysicalRegister &Reg);
+ [[nodiscard]] bool virtualRegister(std::string_view Text, const TextRange &Range,
+ const MIParserContext &Context, Register &Reg);
+ [[nodiscard]] Expected<MachineMetadataReference>
+ resolveMetadataReference(const MachineFunction &Function, std::uint32_t Slot) const;
+ [[nodiscard]] bool parseVariableDebugInfo(const yaml::Node &Entry, MachineFunction &Function,
+ MachineVariableDebugInfo &DebugInfo);
+ [[nodiscard]] bool parseFunction(const yaml::Node &Root, MIRDocument &Document);
+ [[nodiscard]] bool parseFunctionScalars(const yaml::Node &Root, MachineFunction &Function,
+ MIRFunctionDocumentInfo &DocumentInfo);
+ [[nodiscard]] bool recomputeProperties(const yaml::Node &Root, MachineFunction &Function,
+ const MIRFunctionDocumentInfo &DocumentInfo);
+ [[nodiscard]] bool parseRegisters(const yaml::Node &Root, MachineFunction &Function,
+ MIParserContext &Context);
+ [[nodiscard]] bool parseMetadata(const yaml::Node &Root, MachineFunction &Function,
+ MIParserContext &Context);
+ [[nodiscard]] bool parseStackObjects(const yaml::Node &Root, MachineFunction &Function,
+ MIParserContext &Context);
+ [[nodiscard]] bool parseConstants(const yaml::Node &Root, MachineFunction &Function,
+ MIParserContext &Context);
+ [[nodiscard]] bool parseFrameState(const yaml::Node &Root, MachineFunction &Function,
+ const MIParserContext &Context);
+ [[nodiscard]] bool reserveJumpTables(const yaml::Node &Root, MachineFunction &Function,
+ MIParserContext &Context,
+ std::vector<PendingJumpTable> &Pending);
+ [[nodiscard]] bool defineJumpTables(MachineFunction &Function,
+ std::span<const PendingJumpTable> Pending);
+ [[nodiscard]] bool parseBody(const yaml::Node &Root, MachineFunction &Function,
+ MIParserContext &Context);
+ [[nodiscard]] TextRange bodyDiagnosticRange(const yaml::Node &Body,
+ const TextRange &BodyRange) const noexcept;
+ [[nodiscard]] bool parseFunctionRecords(const yaml::Node &Root, MachineFunction &Function);
+ [[nodiscard]] bool parseTargetState(const yaml::Node &Root, MachineFunction &Function);
+ [[nodiscard]] bool convertTargetNode(const yaml::Node &Source, MIRTargetNode &Destination);
+ [[nodiscard]] bool validateDocumentReferences(const MachineFunction &Function);
+ [[nodiscard]] std::string_view effectiveDataLayout() const noexcept;
+ [[nodiscard]] LLVMConstantSyntaxOptions pointerLayoutOptions() const noexcept;
+
+ [[nodiscard]] MachineBasicBlock *blockAt(MachineFunction &Function,
+ std::uint32_t Number) const noexcept;
+ [[nodiscard]] MachineInstr *instructionAt(MachineFunction &Function, std::uint32_t Block,
+ std::uint32_t Offset) const noexcept;
+ [[nodiscard]] bool parseBlockReference(std::string_view Text, const TextRange &Range,
+ MachineFunction &Function, MachineBasicBlock *&Block);
+ [[nodiscard]] bool parseFrameReference(const yaml::Node &Node, const MIParserContext &Context,
+ FrameIndex &Index);
+ [[nodiscard]] bool parseSaveRestorePoints(const yaml::Node &Node, MachineFunction &Function,
+ std::vector<MachineFrameSaveRestorePoint> &Points);
+
+ const TargetDescription &Target;
+ MIRDocumentParserOptions Options;
+ MIRDocumentParseResult Result;
+ const yaml::Stream *Stream = nullptr;
+ MIRDocument *ActiveDocument = nullptr;
+ MachineFunction *ActiveFunction = nullptr;
+};
+
+} // namespace mir
+} // namespace rcc
+
+#endif // RCC_LIB_MIR_MIRDOCUMENTPARSER_H
diff --git a/backend/lib/MIR/MIRDocumentReferenceValidation.cpp b/backend/lib/MIR/MIRDocumentReferenceValidation.cpp
new file mode 100644
index 0000000..a6dbb56
--- /dev/null
+++ b/backend/lib/MIR/MIRDocumentReferenceValidation.cpp
@@ -0,0 +1,410 @@
+// 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) {
+ 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/MIRFrameParser.cpp b/backend/lib/MIR/MIRFrameParser.cpp
new file mode 100644
index 0000000..76ace8f
--- /dev/null
+++ b/backend/lib/MIR/MIRFrameParser.cpp
@@ -0,0 +1,451 @@
+// 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") || Name.empty())
+ return fail(NameNode->range(), "stack object name cannot be 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")) {
+ PhysicalRegister Reg;
+ if (physicalRegister(*SavedNode, 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)) {
+ 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");
+ }
+ 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
new file mode 100644
index 0000000..07cb2b0
--- /dev/null
+++ b/backend/lib/MIR/MIRFunctionParser.cpp
@@ -0,0 +1,536 @@
+// 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 Name) -> bool {
+ if (Explicit && *Explicit && !Computed) {
+ return fail(Root.range(), "explicit property '" + std::string(Name) +
+ "' conflicts with the parsed machine function");
+ }
+ if (Explicit.value_or(Computed))
+ Function.properties().set(Property);
+ else
+ Function.properties().clear(Property);
+ return false;
+ };
+ if (ApplyComputed(DocumentInfo.NoPHIs, NoPHIs, MachineFunctionProperty::NoPHIs, "noPhis") ||
+ ApplyComputed(DocumentInfo.IsSSA, IsSSA, MachineFunctionProperty::IsSSA, "isSSA") ||
+ ApplyComputed(DocumentInfo.NoVRegs, NoVRegs, MachineFunctionProperty::NoVRegs, "noVRegs")) {
+ return true;
+ }
+
+ if (DocumentInfo.HasFakeUses && !*DocumentInfo.HasFakeUses && HasFakeUse) {
+ return fail(Root.range(),
+ "explicit property 'hasFakeUses: false' conflicts with a fake-use instruction");
+ }
+ 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")) {
+ PhysicalRegister Reg;
+ if (physicalRegister(*Preferred, 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(),
+ "unknown virtual-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;
+ Register Virtual;
+ if (scalar(*VirtualNode, Text, "virtual register") ||
+ virtualRegister(Text, VirtualNode->range(), 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::bodyDiagnosticRange(const yaml::Node &Body,
+ const TextRange &BodyRange) const noexcept {
+ if (Body.scalarStyle() != yaml::ScalarStyle::Literal)
+ return Body.range();
+
+ const std::size_t FirstBodyLine = Body.range().Begin.Line + 1U;
+ const std::size_t LastBodyLine = Body.range().End.Line;
+ if (FirstBodyLine > LastBodyLine || FirstBodyLine == 0)
+ return Body.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 Body.range();
+
+ const TextPosition Fallback = Body.range().Begin;
+ const auto MapPosition = [this, FirstBodyLine, LastBodyLine, Indent,
+ Fallback](TextPosition Position) {
+ const std::size_t SourceLine = FirstBodyLine + Position.Line - 1U;
+ if (Position.Line == 0 || SourceLine > LastBodyLine || SourceLine > Result.Source.lineCount()) {
+ return Fallback;
+ }
+ 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(BodyRange.Begin), .End = MapPosition(BodyRange.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 (Options.DefaultPointerSizeInBits != 0)
+ Context.DefaultPointerSizeInBits = Options.DefaultPointerSizeInBits;
+ 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
+ : bodyDiagnosticRange(*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
new file mode 100644
index 0000000..dcd486e
--- /dev/null
+++ b/backend/lib/MIR/MIRFunctionRecordsParser.cpp
@@ -0,0 +1,301 @@
+// 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;
+ Text = trim(Text);
+ if (!Text.starts_with("bb_id"))
+ return fail(TargetNode->range(), "prefetch target must start with 'bb_id'");
+ Text = trim(Text.substr(std::string_view("bb_id").size()));
+ std::array<std::uint32_t, 3> Values{};
+ for (std::size_t Index = 0; Index < Values.size(); ++Index) {
+ const std::size_t Comma = Text.find(',');
+ const std::string_view Part = Index + 1 == Values.size() ? Text : Text.substr(0, Comma);
+ if ((Index + 1 != Values.size() && Comma == std::string_view::npos) ||
+ !parseUnsigned32(trim(Part), Values[Index])) {
+ return fail(TargetNode->range(), "prefetch target has invalid numeric fields");
+ }
+ if (Index + 1 != Values.size())
+ Text = trim(Text.substr(Comma + 1));
+ }
+ 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
new file mode 100644
index 0000000..6ce717b
--- /dev/null
+++ b/backend/lib/MIR/MIRMetadataParser.cpp
@@ -0,0 +1,285 @@
+// 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;
+
+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(ExpressionNode->range(), 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;
+
+ struct Definition final {
+ const yaml::Node *Node = nullptr;
+ std::uint32_t Slot = 0;
+ bool IsDistinct = false;
+ std::string_view Elements;
+ };
+ std::vector<Definition> Definitions;
+ std::unordered_set<std::uint32_t> Slots;
+
+ 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;
+ }
+ Text = trim(Text);
+ if (!Text.starts_with('!'))
+ return fail(DefinitionNode->range(), "machine metadata definition must start with '!N'");
+ const std::size_t Equals = Text.find('=');
+ if (Equals == std::string_view::npos)
+ return fail(DefinitionNode->range(), "machine metadata definition requires '='");
+ std::uint32_t Slot = 0;
+ if (!parseUnsigned32(trim(Text.substr(1, Equals - 1)), Slot))
+ return fail(DefinitionNode->range(), "machine metadata definition has an invalid slot");
+ if (!Slots.insert(Slot).second)
+ return fail(DefinitionNode->range(), "duplicate machine metadata slot");
+ std::string_view Right = trim(Text.substr(Equals + 1));
+ bool IsDistinct = false;
+ if (Right.starts_with("distinct")) {
+ Right = trim(Right.substr(std::string_view("distinct").size()));
+ IsDistinct = true;
+ }
+ if (!Right.starts_with("!{") || !Right.ends_with('}'))
+ return fail(DefinitionNode->range(), "machine metadata node must be a metadata tuple");
+ Definitions.push_back({.Node = DefinitionNode,
+ .Slot = Slot,
+ .IsDistinct = IsDistinct,
+ .Elements = Right.substr(2, Right.size() - 3)});
+ Context.Metadata.push_back(
+ {.TextID = Slot, .Reference = MachineMetadataReference::machineFunction(Slot)});
+ }
+
+ auto ResolveReference = [this,
+ &Slots](std::uint32_t Slot,
+ const TextRange &Range) -> Expected<MachineMetadataElement> {
+ MachineMetadataReference Reference;
+ if (ActiveDocument->EmbeddedIR != std::nullopt &&
+ ActiveDocument->EmbeddedIR->metadata(std::to_string(Slot)) != nullptr) {
+ Reference = MachineMetadataReference::embeddedIR(Slot);
+ } else if (Slots.contains(Slot)) {
+ Reference = MachineMetadataReference::machineFunction(Slot);
+ } else {
+ return makeUnexpected(ErrorCode::ParseError, "metadata tuple references undefined slot '!" +
+ std::to_string(Slot) + "' at line " +
+ std::to_string(Range.Begin.Line));
+ }
+ return MachineMetadataElement::reference(Reference);
+ };
+
+ for (const Definition &Definition : Definitions) {
+ MachineMetadataNode NewNode{
+ .Slot = Definition.Slot,
+ .IsDistinct = Definition.IsDistinct,
+ .Elements = {},
+ };
+ std::string_view Remaining = trim(Definition.Elements);
+ while (!Remaining.empty()) {
+ std::size_t End = 0;
+ bool InString = false;
+ bool Escaped = false;
+ for (; End < Remaining.size(); ++End) {
+ const char Character = Remaining[End];
+ if (InString && Escaped) {
+ Escaped = false;
+ continue;
+ }
+ if (InString && Character == '\\') {
+ Escaped = true;
+ continue;
+ }
+ if (Character == '"') {
+ InString = !InString;
+ continue;
+ }
+ if (!InString && Character == ',')
+ break;
+ }
+ if (InString)
+ return fail(Definition.Node->range(), "unterminated machine metadata string");
+ std::string_view Element = trim(Remaining.substr(0, End));
+ if (Element.empty())
+ return fail(Definition.Node->range(), "empty machine metadata tuple element");
+
+ if (Element == "null") {
+ NewNode.Elements.push_back(MachineMetadataElement::null());
+ } else if (Element.starts_with('!') && Element.size() > 1 && Element[1] >= '0' &&
+ Element[1] <= '9') {
+ std::uint32_t ReferenceSlot = 0;
+ if (!parseUnsigned32(Element.substr(1), ReferenceSlot))
+ return fail(Definition.Node->range(), "invalid metadata tuple reference");
+ auto Reference = ResolveReference(ReferenceSlot, Definition.Node->range());
+ if (!Reference)
+ return importError(Definition.Node->range(), Reference.error());
+ NewNode.Elements.push_back(std::move(*Reference));
+ } else if (Element.starts_with("!\"") && Element.ends_with('"')) {
+ std::string Value;
+ std::string_view Encoded = Element.substr(2, Element.size() - 3);
+ for (std::size_t Index = 0; Index < Encoded.size(); ++Index) {
+ if (Encoded[Index] == '\\' && Index + 1 < Encoded.size()) {
+ const char EscapedCharacter = Encoded[++Index];
+ if (EscapedCharacter != '\\' && EscapedCharacter != '"')
+ return fail(Definition.Node->range(), "unsupported machine metadata string escape");
+ Value.push_back(EscapedCharacter);
+ } else {
+ Value.push_back(Encoded[Index]);
+ }
+ }
+ NewNode.Elements.push_back(MachineMetadataElement::string(std::move(Value)));
+ } else {
+ return fail(Definition.Node->range(), "unsupported machine metadata tuple element");
+ }
+
+ if (End == Remaining.size())
+ Remaining = {};
+ else
+ Remaining = trim(Remaining.substr(End + 1));
+ }
+ 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
new file mode 100644
index 0000000..51b039f
--- /dev/null
+++ b/backend/lib/MIR/MIRMetadataTextParser.cpp
@@ -0,0 +1,259 @@
+// 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());
+ if (!Opcode) {
+ return makeUnexpected(ErrorCode::ParseError, "unknown DIExpression opcode '" +
+ std::string(token().stringValue()) + "'");
+ }
+ Elements.push_back(*Opcode);
+ 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
new file mode 100644
index 0000000..b8ebb12
--- /dev/null
+++ b/backend/lib/MIR/MIRMetadataTextParser.h
@@ -0,0 +1,70 @@
+// 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();
+
+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
new file mode 100644
index 0000000..2f69548
--- /dev/null
+++ b/backend/lib/MIR/MIRParserUtilities.h
@@ -0,0 +1,85 @@
+// 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