aboutsummaryrefslogtreecommitdiff
path: root/backend/lib/MIR/MIRDocumentPrinter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'backend/lib/MIR/MIRDocumentPrinter.cpp')
-rw-r--r--backend/lib/MIR/MIRDocumentPrinter.cpp1302
1 files changed, 1302 insertions, 0 deletions
diff --git a/backend/lib/MIR/MIRDocumentPrinter.cpp b/backend/lib/MIR/MIRDocumentPrinter.cpp
new file mode 100644
index 0000000..e9f9e1c
--- /dev/null
+++ b/backend/lib/MIR/MIRDocumentPrinter.cpp
@@ -0,0 +1,1302 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "MIRDocumentReferenceValidation.h"
+#include "rcc/CodeGen/MachineBasicBlock.h"
+#include "rcc/CodeGen/MachineCompilationStatus.h"
+#include "rcc/CodeGen/MachineConstantPool.h"
+#include "rcc/CodeGen/MachineFrameInfo.h"
+#include "rcc/CodeGen/MachineFunction.h"
+#include "rcc/CodeGen/MachineFunctionAttributes.h"
+#include "rcc/CodeGen/MachineFunctionProperties.h"
+#include "rcc/CodeGen/MachineFunctionRecords.h"
+#include "rcc/CodeGen/MachineInstr.h"
+#include "rcc/CodeGen/MachineJumpTableInfo.h"
+#include "rcc/CodeGen/MachineMetadata.h"
+#include "rcc/CodeGen/MachineRegisterInfo.h"
+#include "rcc/CodeGen/TargetDescription.h"
+#include "rcc/CodeGen/TargetMIRTextInfo.h"
+#include "rcc/CodeGen/TargetRegisterInfo.h"
+#include "rcc/MIR/LLVMConstantSyntax.h"
+#include "rcc/MIR/MIRDocument.h"
+
+#include <algorithm>
+#include <array>
+#include <charconv>
+#include <cstddef>
+#include <cstdint>
+#include <limits>
+#include <optional>
+#include <ranges>
+#include <span>
+#include <string>
+#include <string_view>
+#include <unordered_set>
+#include <utility>
+#include <variant>
+#include <vector>
+
+namespace rcc::mir {
+namespace {
+
+[[nodiscard]] std::string yamlScalar(std::string_view Value) {
+ std::string Result;
+ Result.reserve(Value.size() + 2U);
+ Result.push_back('\'');
+ for (const char Character : Value) {
+ Result.push_back(Character);
+ if (Character == '\'')
+ Result.push_back('\'');
+ }
+ Result.push_back('\'');
+ return Result;
+}
+
+void appendBlockScalar(std::string &Output, std::string_view Text, std::size_t Indent) {
+ std::size_t Position = 0;
+ do {
+ Output.append(Indent, ' ');
+ const std::size_t End = Text.find('\n', Position);
+ if (End == std::string_view::npos) {
+ Output.append(Text.substr(Position));
+ Output.push_back('\n');
+ return;
+ }
+ Output.append(Text.substr(Position, End - Position));
+ Output.push_back('\n');
+ Position = End + 1U;
+ } while (Position < Text.size());
+}
+
+[[nodiscard]] std::optional<std::uint32_t> parseDecimal32(std::string_view Text) noexcept {
+ std::uint32_t Value = 0;
+ if (Text.empty())
+ return std::nullopt;
+ const auto [End, Error] = std::from_chars(Text.data(), Text.data() + Text.size(), Value, 10);
+ if (Error != std::errc{} || End != Text.data() + Text.size())
+ return std::nullopt;
+ return Value;
+}
+
+[[nodiscard]] bool isDefaultFrameState(const MachineFrameState &State) {
+ return State == MachineFrameState{};
+}
+
+class MIRDocumentPrinter final {
+public:
+ MIRDocumentPrinter(const MIRDocument &Document, const TargetDescription &Target,
+ MIRDocumentPrinterOptions Options)
+ : Document(Document), Target(Target), Options(Options) {}
+
+ [[nodiscard]] Expected<std::string> run();
+
+private:
+ struct InstructionLocation final {
+ std::uint32_t Block = 0;
+ std::uint32_t Offset = 0;
+ };
+
+ [[nodiscard]] Expected<void> printFunction(const MachineFunction &Function,
+ const MIRFunctionDocumentInfo *DocumentInfo);
+ [[nodiscard]] Expected<MIParserContext> createBodyContext(const MachineFunction &Function) const;
+ [[nodiscard]] Expected<void> printRegisters(const MachineFunction &Function,
+ const MIParserContext &Context);
+ [[nodiscard]] Expected<void> printFrameInfo(const MachineFunction &Function,
+ const MIParserContext &Context);
+ [[nodiscard]] Expected<void> printStackObjects(const MachineFunction &Function,
+ const MIParserContext &Context, bool Fixed);
+ [[nodiscard]] Expected<void> printConstants(const MachineFunction &Function,
+ const MIParserContext &Context);
+ [[nodiscard]] Expected<void> printJumpTable(const MachineFunction &Function,
+ const MIParserContext &Context);
+ [[nodiscard]] Expected<void> printMetadata(const MachineFunction &Function,
+ const MIParserContext &Context);
+ [[nodiscard]] Expected<void> printTargetState(const MachineFunction &Function);
+ [[nodiscard]] Expected<void> printFunctionRecords(const MachineFunction &Function,
+ const MIParserContext &Context);
+ [[nodiscard]] Expected<void> verifyOmittedEmbeddedIRLayout(const MachineFunction &Function) const;
+
+ [[nodiscard]] Expected<std::string> physicalRegister(PhysicalRegister Register) const;
+ [[nodiscard]] Expected<std::string> frameReference(FrameIndex Index,
+ const MIParserContext &Context) const;
+ [[nodiscard]] Expected<std::string> metadataReference(MachineMetadataReference Reference,
+ const MIParserContext &Context) const;
+ [[nodiscard]] Expected<std::string> metadataValue(const MachineMetadataValue &Value,
+ const MIParserContext &Context) const;
+ [[nodiscard]] Expected<std::string> debugLocation(const MachineDebugLocation &Location,
+ const MIParserContext &Context) const;
+ [[nodiscard]] Expected<std::string> diLocation(const MachineDILocation &Location,
+ std::size_t FrameIndex,
+ const MIParserContext &Context) const;
+ [[nodiscard]] Expected<InstructionLocation> instructionLocation(const MachineFunction &Function,
+ MachineInstrStableID ID) const;
+ [[nodiscard]] Expected<std::string> blockReference(const MachineFunction &Function,
+ MachineBlockStableID ID) const;
+ [[nodiscard]] Expected<std::string> targetNode(const MIRTargetNode &Node) const;
+ [[nodiscard]] const MIRFunctionDocumentInfo *
+ documentInfo(const MachineFunction &Function) const noexcept;
+
+ void printBoolean(std::string_view Key, bool Value, bool Always = false);
+ void printUnsigned(std::string_view Key, std::uint64_t Value, bool Always = false);
+ void printSigned(std::string_view Key, std::int64_t Value, bool Always = false);
+
+ const MIRDocument &Document;
+ const TargetDescription &Target;
+ MIRDocumentPrinterOptions Options;
+ std::string Output;
+};
+
+void MIRDocumentPrinter::printBoolean(std::string_view Key, bool Value, bool Always) {
+ if (!Always && Options.Mode == MIPrintMode::Readable && !Value)
+ return;
+ Output.append(Key);
+ Output.append(Value ? ": true\n" : ": false\n");
+}
+
+void MIRDocumentPrinter::printUnsigned(std::string_view Key, std::uint64_t Value, bool Always) {
+ if (!Always && Options.Mode == MIPrintMode::Readable && Value == 0)
+ return;
+ Output.append(Key);
+ Output.append(": ");
+ Output.append(std::to_string(Value));
+ Output.push_back('\n');
+}
+
+void MIRDocumentPrinter::printSigned(std::string_view Key, std::int64_t Value, bool Always) {
+ if (!Always && Options.Mode == MIPrintMode::Readable && Value == 0)
+ return;
+ Output.append(Key);
+ Output.append(": ");
+ Output.append(std::to_string(Value));
+ Output.push_back('\n');
+}
+
+Expected<std::string> MIRDocumentPrinter::physicalRegister(PhysicalRegister Register) const {
+ const auto Name = Target.registers().physicalRegisterMIRName(Register);
+ if (!Name) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "physical register has no canonical MIR name");
+ }
+ return "$" + std::string(*Name);
+}
+
+const MIRFunctionDocumentInfo *
+MIRDocumentPrinter::documentInfo(const MachineFunction &Function) const noexcept {
+ const auto Infos = Document.functionDocumentInfo();
+ const auto Found = std::ranges::find_if(Infos, [&Function](const MIRFunctionDocumentInfo &Info) {
+ return Info.Function == Function.stableID();
+ });
+ return Found == Infos.end() ? nullptr : &*Found;
+}
+
+Expected<void>
+MIRDocumentPrinter::verifyOmittedEmbeddedIRLayout(const MachineFunction &Function) const {
+ if (Options.EmitEmbeddedIR)
+ return {};
+
+ const EmbeddedIRReferenceIndex *Embedded = Document.embeddedIR();
+ if (Embedded == nullptr)
+ return {};
+ const auto EmbeddedDataLayout = Embedded->dataLayout();
+ if (!EmbeddedDataLayout)
+ return {};
+
+ bool HasLayoutDependentContent = false;
+ for (const VirtualRegisterInfo &RegisterInfo : Function.registerInfo().virtualRegisters()) {
+ if (!RegisterInfo.IsTombstone && RegisterInfo.Type &&
+ RegisterInfo.Type->isPointerOrPointerVector()) {
+ HasLayoutDependentContent = true;
+ break;
+ }
+ }
+ if (!HasLayoutDependentContent) {
+ for (const MachineConstantPoolEntry &Entry : Function.constantPool().entries()) {
+ const MachineConstantPoolEntryDesc *Description = Entry.description();
+ if (Description != nullptr &&
+ std::holds_alternative<GenericConstantData>(Description->Value)) {
+ HasLayoutDependentContent = true;
+ break;
+ }
+ }
+ }
+ if (!HasLayoutDependentContent)
+ return {};
+
+ if (Options.DataLayout != *EmbeddedDataLayout) {
+ return makeUnexpected(
+ ErrorCode::InvalidArgument,
+ "omitting embedded LLVM IR requires printer DataLayout to exactly match its data layout");
+ }
+
+ const LLVMConstantSyntaxOptions LayoutOptions{
+ .DefaultPointerSizeInBits = Options.DefaultPointerSizeInBits,
+ .DataLayout = Options.DataLayout,
+ .NamedTypes = {},
+ };
+ for (const VirtualRegisterInfo &RegisterInfo : Function.registerInfo().virtualRegisters()) {
+ if (RegisterInfo.IsTombstone || !RegisterInfo.Type ||
+ !RegisterInfo.Type->isPointerOrPointerVector()) {
+ continue;
+ }
+ const auto AddressSpace = RegisterInfo.Type->addressSpace();
+ if (!AddressSpace) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "pointer virtual register has no address space");
+ }
+ auto PointerSize = llvmPointerSizeInBits(*AddressSpace, LayoutOptions);
+ if (!PointerSize) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "printer DataLayout cannot determine pointer width: " +
+ std::string(PointerSize.error().message()));
+ }
+ if (*PointerSize != RegisterInfo.Type->scalarBitWidth()) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "printer DataLayout does not match pointer virtual-register width");
+ }
+ }
+
+ for (const MachineConstantPoolEntry &Entry : Function.constantPool().entries()) {
+ const MachineConstantPoolEntryDesc *Description = Entry.description();
+ if (Description == nullptr)
+ continue;
+ const auto *Generic = std::get_if<GenericConstantData>(&Description->Value);
+ if (Generic == nullptr)
+ continue;
+ auto StorageSize = llvmConstantStorageSize(Generic->Type, LayoutOptions);
+ if (!StorageSize) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "printer DataLayout cannot lay out generic constant: " +
+ std::string(StorageSize.error().message()));
+ }
+ if (*StorageSize != Description->Size) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "printer DataLayout does not match generic constant storage size");
+ }
+ }
+ return {};
+}
+
+Expected<MIParserContext>
+MIRDocumentPrinter::createBodyContext(const MachineFunction &Function) const {
+ MIParserContext Context;
+ Context.SyncScopes.assign(Document.syncScopes().begin(), Document.syncScopes().end());
+ const auto VirtualRegisters = Function.registerInfo().virtualRegisters();
+ for (std::size_t Index = 0; Index < VirtualRegisters.size(); ++Index) {
+ if (VirtualRegisters[Index].IsTombstone)
+ continue;
+ auto Reg = Register::makeVirtual(Index);
+ if (!Reg)
+ return std::unexpected(Reg.error());
+ if (Index > std::numeric_limits<std::uint32_t>::max()) {
+ return makeUnexpected(ErrorCode::Unsupported,
+ "MIR cannot represent more than 2^32 virtual registers");
+ }
+ Context.VirtualRegisters.push_back({.TextID = static_cast<std::uint32_t>(Index), .Reg = *Reg});
+ }
+
+ std::uint32_t StackID = 0;
+ std::uint32_t FixedID = 0;
+ for (const StackObject &Object : Function.frameInfo().objects()) {
+ const StackObjectDesc *Description = Object.description();
+ if (Description == nullptr)
+ continue;
+ MIFrameObjectSlot Slot{
+ .TextID = Object.isFixed() ? FixedID++ : StackID++,
+ .Index = Object.index(),
+ .Name = Description->AllocaName,
+ };
+ (Object.isFixed() ? Context.FixedStackObjects : Context.StackObjects)
+ .push_back(std::move(Slot));
+ }
+
+ std::uint32_t ConstantID = 0;
+ for (const MachineConstantPoolEntry &Entry : Function.constantPool().entries()) {
+ if (Entry.description() != nullptr)
+ Context.ConstantPoolItems.push_back({.TextID = ConstantID++, .Index = Entry.index()});
+ }
+ std::uint32_t JumpTableID = 0;
+ for (const MachineJumpTableEntry &Entry : Function.jumpTableInfo().tables()) {
+ if (Entry.description() != nullptr)
+ Context.JumpTables.push_back({.TextID = JumpTableID++, .Index = Entry.index()});
+ }
+
+ std::unordered_set<std::uint32_t> EmbeddedMetadataSlots;
+ if (const EmbeddedIRReferenceIndex *Embedded = Document.embeddedIR()) {
+ for (const EmbeddedIRMetadataDefinition &Definition : Embedded->metadataDefinitions()) {
+ const auto Slot = parseDecimal32(Definition.Name);
+ if (Slot) {
+ if (!EmbeddedMetadataSlots.insert(*Slot).second) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "embedded LLVM IR metadata slots are not unique");
+ }
+ Context.Metadata.push_back(
+ {.TextID = *Slot, .Reference = MachineMetadataReference::embeddedIR(*Slot)});
+ }
+ }
+ }
+
+ std::unordered_set<std::uint32_t> UsedMetadataSlots = EmbeddedMetadataSlots;
+ for (const MachineMetadataNode &Node : Function.metadata().nodes()) {
+ if (!EmbeddedMetadataSlots.contains(Node.Slot))
+ UsedMetadataSlots.insert(Node.Slot);
+ }
+ std::uint64_t NextMetadataSlot = 0;
+ for (const MachineMetadataNode &Node : Function.metadata().nodes()) {
+ std::uint32_t TextID = Node.Slot;
+ if (EmbeddedMetadataSlots.contains(TextID)) {
+ while (NextMetadataSlot <= std::numeric_limits<std::uint32_t>::max() &&
+ UsedMetadataSlots.contains(static_cast<std::uint32_t>(NextMetadataSlot))) {
+ ++NextMetadataSlot;
+ }
+ if (NextMetadataSlot > std::numeric_limits<std::uint32_t>::max()) {
+ return makeUnexpected(ErrorCode::Unsupported, "MIR metadata text slot space is exhausted");
+ }
+ TextID = static_cast<std::uint32_t>(NextMetadataSlot);
+ UsedMetadataSlots.insert(TextID);
+ ++NextMetadataSlot;
+ }
+ Context.Metadata.push_back(
+ {.TextID = TextID, .Reference = MachineMetadataReference::machineFunction(Node.Slot)});
+ }
+ return Context;
+}
+
+Expected<std::string> MIRDocumentPrinter::frameReference(FrameIndex Index,
+ const MIParserContext &Context) const {
+ const auto Stack = std::ranges::find_if(
+ Context.StackObjects, [Index](const MIFrameObjectSlot &Slot) { return Slot.Index == Index; });
+ if (Stack != Context.StackObjects.end())
+ return "%stack." + std::to_string(Stack->TextID);
+ const auto Fixed =
+ std::ranges::find_if(Context.FixedStackObjects,
+ [Index](const MIFrameObjectSlot &Slot) { return Slot.Index == Index; });
+ if (Fixed != Context.FixedStackObjects.end())
+ return "%fixed-stack." + std::to_string(Fixed->TextID);
+ return makeUnexpected(ErrorCode::VerificationError,
+ "frame state references an unknown stack object");
+}
+
+Expected<std::string> MIRDocumentPrinter::metadataReference(MachineMetadataReference Reference,
+ const MIParserContext &Context) const {
+ if (!Reference.isValid())
+ return makeUnexpected(ErrorCode::VerificationError, "invalid metadata reference");
+ const auto Slot =
+ std::ranges::find_if(Context.Metadata, [Reference](const MIMetadataSlot &Candidate) {
+ return Candidate.Reference == Reference;
+ });
+ if (Slot == Context.Metadata.end()) {
+ return makeUnexpected(ErrorCode::VerificationError, "metadata reference has no MIR text slot");
+ }
+ return "!" + std::to_string(Slot->TextID);
+}
+
+Expected<std::string> MIRDocumentPrinter::metadataValue(const MachineMetadataValue &Value,
+ const MIParserContext &Context) const {
+ if (const MachineMetadataReference *Reference = Value.reference())
+ return metadataReference(*Reference, Context);
+ const MachineDIExpression *Expression = Value.expression();
+ if (Expression == nullptr)
+ return makeUnexpected(ErrorCode::VerificationError, "metadata value is not printable");
+ std::string Text = "!DIExpression(";
+ for (std::size_t Index = 0; Index < Expression->elements().size(); ++Index) {
+ if (Index != 0)
+ Text.append(", ");
+ Text.append(std::to_string(Expression->elements()[Index]));
+ }
+ Text.push_back(')');
+ return Text;
+}
+
+Expected<std::string> MIRDocumentPrinter::diLocation(const MachineDILocation &Location,
+ std::size_t FrameIndex,
+ const MIParserContext &Context) const {
+ if (FrameIndex >= Location.inlineChain().size()) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "inline DILocation has an invalid frame chain");
+ }
+ const MachineDILocationFrame &Frame = Location.inlineChain()[FrameIndex];
+ auto Scope = metadataReference(Frame.Scope, Context);
+ if (!Scope)
+ return std::unexpected(Scope.error());
+ std::string Text = "!DILocation(line: " + std::to_string(Frame.Line);
+ if (Options.Mode == MIPrintMode::Complete || Frame.Column != 0)
+ Text.append(", column: " + std::to_string(Frame.Column));
+ Text.append(", scope: " + *Scope);
+ if (FrameIndex + 1U < Location.inlineChain().size()) {
+ auto Nested = diLocation(Location, FrameIndex + 1U, Context);
+ if (!Nested)
+ return std::unexpected(Nested.error());
+ Text.append(", inlinedAt: " + *Nested);
+ } else if (Location.terminalInlinedAt()) {
+ auto Terminal = metadataReference(*Location.terminalInlinedAt(), Context);
+ if (!Terminal)
+ return std::unexpected(Terminal.error());
+ Text.append(", inlinedAt: " + *Terminal);
+ }
+ if (Options.Mode == MIPrintMode::Complete || Frame.IsImplicitCode)
+ Text.append(Frame.IsImplicitCode ? ", isImplicitCode: true" : ", isImplicitCode: false");
+ if (Options.Mode == MIPrintMode::Complete || Frame.AtomGroup != 0)
+ Text.append(", atomGroup: " + std::to_string(Frame.AtomGroup));
+ if (Options.Mode == MIPrintMode::Complete || Frame.AtomRank != 0)
+ Text.append(", atomRank: " + std::to_string(Frame.AtomRank));
+ Text.push_back(')');
+ return Text;
+}
+
+Expected<std::string> MIRDocumentPrinter::debugLocation(const MachineDebugLocation &Location,
+ const MIParserContext &Context) const {
+ if (const MachineMetadataReference *Reference = Location.reference())
+ return metadataReference(*Reference, Context);
+ if (const MachineDILocation *Inline = Location.inlineLocation())
+ return diLocation(*Inline, 0, Context);
+ return makeUnexpected(ErrorCode::VerificationError, "debug location is not printable");
+}
+
+Expected<MIRDocumentPrinter::InstructionLocation>
+MIRDocumentPrinter::instructionLocation(const MachineFunction &Function,
+ MachineInstrStableID ID) const {
+ std::uint64_t BlockNumber = 0;
+ for (const MachineBasicBlock *Block = Function.firstBlock(); Block != nullptr;
+ Block = Block->next(), ++BlockNumber) {
+ if (BlockNumber > std::numeric_limits<std::uint32_t>::max())
+ break;
+ std::uint64_t Offset = 0;
+ for (const MachineInstr *Instruction = Block->firstInstruction(); Instruction != nullptr;
+ Instruction = Instruction->next(), ++Offset) {
+ if (Instruction->stableID() != ID)
+ continue;
+ if (Offset > std::numeric_limits<std::uint32_t>::max()) {
+ return makeUnexpected(ErrorCode::Unsupported, "MIR instruction offset exceeds 32 bits");
+ }
+ return InstructionLocation{static_cast<std::uint32_t>(BlockNumber),
+ static_cast<std::uint32_t>(Offset)};
+ }
+ }
+ return makeUnexpected(ErrorCode::VerificationError,
+ "function record references an unlinked instruction");
+}
+
+Expected<std::string> MIRDocumentPrinter::blockReference(const MachineFunction &Function,
+ MachineBlockStableID ID) const {
+ std::uint64_t Number = 0;
+ for (const MachineBasicBlock *Block = Function.firstBlock(); Block != nullptr;
+ Block = Block->next(), ++Number) {
+ if (Block->stableID() == ID) {
+ if (Number > std::numeric_limits<std::uint32_t>::max())
+ break;
+ return "%bb." + std::to_string(Number);
+ }
+ }
+ return makeUnexpected(ErrorCode::VerificationError,
+ "frame record references an unlinked basic block");
+}
+
+Expected<std::string> MIRDocumentPrinter::targetNode(const MIRTargetNode &Node) const {
+ switch (Node.Kind) {
+ case MIRTargetNodeKind::Null:
+ return std::string("null");
+ case MIRTargetNodeKind::Scalar:
+ return yamlScalar(Node.Scalar);
+ case MIRTargetNodeKind::Sequence: {
+ std::string Text = "[ ";
+ for (std::size_t Index = 0; Index < Node.Sequence.size(); ++Index) {
+ if (Index != 0)
+ Text.append(", ");
+ auto Element = targetNode(Node.Sequence[Index]);
+ if (!Element)
+ return std::unexpected(Element.error());
+ Text.append(*Element);
+ }
+ Text.append(" ]");
+ return Text;
+ }
+ case MIRTargetNodeKind::Mapping: {
+ std::string Text = "{ ";
+ for (std::size_t Index = 0; Index < Node.Mapping.size(); ++Index) {
+ if (Index != 0)
+ Text.append(", ");
+ Text.append(yamlScalar(Node.Mapping[Index].Key));
+ Text.append(": ");
+ auto Value = targetNode(Node.Mapping[Index].Value);
+ if (!Value)
+ return std::unexpected(Value.error());
+ Text.append(*Value);
+ }
+ Text.append(" }");
+ return Text;
+ }
+ case MIRTargetNodeKind::Count:
+ break;
+ }
+ return makeUnexpected(ErrorCode::VerificationError, "target MIR node has an invalid kind");
+}
+
+Expected<void> MIRDocumentPrinter::printRegisters(const MachineFunction &Function,
+ const MIParserContext &Context) {
+ if (Context.VirtualRegisters.empty()) {
+ if (Options.Mode == MIPrintMode::Complete)
+ Output.append("registers: []\n");
+ } else {
+ Output.append("registers:\n");
+ for (const MIVirtualRegisterSlot &Slot : Context.VirtualRegisters) {
+ auto InfoResult = Function.registerInfo().virtualRegisterInfo(Slot.Reg);
+ if (!InfoResult)
+ return std::unexpected(InfoResult.error());
+ const VirtualRegisterInfo &Info = InfoResult->get();
+ std::string Class = "_";
+ if (Info.RegClass) {
+ const auto Name = Target.registers().registerClassMIRName(*Info.RegClass);
+ if (!Name)
+ return makeUnexpected(ErrorCode::VerificationError,
+ "virtual register class has no canonical MIR name");
+ Class = *Name;
+ } else if (Info.Bank) {
+ const auto Name = Target.registers().registerBankMIRName(*Info.Bank);
+ if (!Name)
+ return makeUnexpected(ErrorCode::VerificationError,
+ "virtual register bank has no canonical MIR name");
+ Class = *Name;
+ }
+ if (Info.AllocationHints.size() > 1) {
+ return makeUnexpected(
+ ErrorCode::Unsupported,
+ "fixed LLVM MIR can serialize only one simple preferred-register hint");
+ }
+
+ Output.append(" - { id: ");
+ Output.append(std::to_string(Slot.TextID));
+ Output.append(", class: ");
+ Output.append(yamlScalar(Class));
+ if (!Info.AllocationHints.empty()) {
+ auto Preferred = physicalRegister(Info.AllocationHints.front());
+ if (!Preferred)
+ return std::unexpected(Preferred.error());
+ Output.append(", preferred-register: ");
+ Output.append(yamlScalar(*Preferred));
+ }
+ if (Info.TargetFlags != 0) {
+ auto Names = Target.mirText().virtualRegisterFlagNames(Info.TargetFlags);
+ if (!Names)
+ return std::unexpected(Names.error());
+ Output.append(", flags: [ ");
+ for (std::size_t Index = 0; Index < Names->size(); ++Index) {
+ if (Index != 0)
+ Output.append(", ");
+ Output.append(yamlScalar((*Names)[Index]));
+ }
+ Output.append(" ]");
+ }
+ Output.append(" }\n");
+ }
+ }
+
+ if (Function.registerInfo().liveIns().empty()) {
+ if (Options.Mode == MIPrintMode::Complete)
+ Output.append("liveins: []\n");
+ } else {
+ Output.append("liveins:\n");
+ for (const PhysicalRegisterLiveIn &LiveIn : Function.registerInfo().liveIns()) {
+ auto Name = physicalRegister(LiveIn.PhysReg);
+ if (!Name)
+ return std::unexpected(Name.error());
+ Output.append(" - { reg: ");
+ Output.append(yamlScalar(*Name));
+ if (LiveIn.InitialVirtualRegister) {
+ const auto Slot = std::ranges::find_if(
+ Context.VirtualRegisters, [&LiveIn](const MIVirtualRegisterSlot &Candidate) {
+ return Candidate.Reg == *LiveIn.InitialVirtualRegister;
+ });
+ if (Slot == Context.VirtualRegisters.end()) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "live-in references an unknown virtual register");
+ }
+ Output.append(", virtual-reg: ");
+ Output.append(yamlScalar("%" + std::to_string(Slot->TextID)));
+ }
+ Output.append(" }\n");
+ }
+ }
+
+ if (Function.records().hasCalleeSavedRegisters()) {
+ Output.append("calleeSavedRegisters: [ ");
+ const auto Registers = Function.records().calleeSavedRegisters();
+ for (std::size_t Index = 0; Index < Registers.size(); ++Index) {
+ if (Index != 0)
+ Output.append(", ");
+ auto Name = physicalRegister(Registers[Index]);
+ if (!Name)
+ return std::unexpected(Name.error());
+ Output.append(yamlScalar(*Name));
+ }
+ Output.append(" ]\n");
+ }
+ return {};
+}
+
+Expected<void> MIRDocumentPrinter::printFrameInfo(const MachineFunction &Function,
+ const MIParserContext &Context) {
+ const MachineFrameState &State = Function.frameInfo().state();
+ if (Options.Mode == MIPrintMode::Readable && isDefaultFrameState(State))
+ return {};
+ if (State.MaxCallFrameSize &&
+ *State.MaxCallFrameSize > std::numeric_limits<std::uint32_t>::max()) {
+ return makeUnexpected(ErrorCode::Unsupported,
+ "fixed LLVM MIR maxCallFrameSize is limited to 32 bits");
+ }
+ Output.append("frameInfo:\n");
+ const auto Bool = [this](std::string_view Key, bool Value) {
+ if (Options.Mode == MIPrintMode::Readable && !Value)
+ return;
+ Output.append(" ");
+ Output.append(Key);
+ Output.append(Value ? ": true\n" : ": false\n");
+ };
+ const auto Unsigned = [this](std::string_view Key, std::uint64_t Value) {
+ if (Options.Mode == MIPrintMode::Readable && Value == 0)
+ return;
+ Output.append(" ");
+ Output.append(Key);
+ Output.append(": ");
+ Output.append(std::to_string(Value));
+ Output.push_back('\n');
+ };
+
+ Bool("isFrameAddressTaken", State.IsFrameAddressTaken);
+ Bool("isReturnAddressTaken", State.IsReturnAddressTaken);
+ Bool("hasStackMap", State.HasStackMap);
+ Bool("hasPatchPoint", State.HasPatchPoint);
+ Unsigned("stackSize", State.StackSize);
+ if (Options.Mode == MIPrintMode::Complete || State.OffsetAdjustment != 0) {
+ Output.append(" offsetAdjustment: ");
+ Output.append(std::to_string(State.OffsetAdjustment));
+ Output.push_back('\n');
+ }
+ Unsigned("maxAlignment", State.MaxAlignment ? State.MaxAlignment->bytes() : 0);
+ Bool("adjustsStack", State.AdjustsStack);
+ Bool("hasCalls", State.HasCalls);
+ if (Options.Mode == MIPrintMode::Complete ||
+ State.FramePointerPolicy != MachineFramePointerPolicy::None) {
+ Output.append(" framePointerPolicy: ");
+ Output.append(getMachineFramePointerPolicyName(State.FramePointerPolicy));
+ Output.push_back('\n');
+ }
+ if (State.StackProtector) {
+ auto Reference = frameReference(*State.StackProtector, Context);
+ if (!Reference)
+ return std::unexpected(Reference.error());
+ Output.append(" stackProtector: ");
+ Output.append(yamlScalar(*Reference));
+ Output.push_back('\n');
+ }
+ if (State.FunctionContext) {
+ auto Reference = frameReference(*State.FunctionContext, Context);
+ if (!Reference)
+ return std::unexpected(Reference.error());
+ Output.append(" functionContext: ");
+ Output.append(yamlScalar(*Reference));
+ Output.push_back('\n');
+ }
+ if (State.MaxCallFrameSize)
+ Unsigned("maxCallFrameSize", *State.MaxCallFrameSize);
+ Unsigned("cvBytesOfCalleeSavedRegisters", State.CVBytesOfCalleeSavedRegisters);
+ Bool("hasOpaqueSPAdjustment", State.HasOpaqueSPAdjustment);
+ Bool("hasVAStart", State.HasVAStart);
+ Bool("hasMustTailInVarArgFunc", State.HasMustTailInVarArgFunc);
+ Bool("hasTailCall", State.HasTailCall);
+ Bool("isCalleeSavedInfoValid", State.IsCalleeSavedInfoValid);
+ Unsigned("localFrameSize", State.LocalFrameSize);
+
+ const auto PrintPoints =
+ [this, &Function](std::string_view Key,
+ std::span<const MachineFrameSaveRestorePoint> Points) -> Expected<void> {
+ if (Points.empty())
+ return {};
+ Output.append(" ");
+ Output.append(Key);
+ Output.append(":\n");
+ for (const MachineFrameSaveRestorePoint &Point : Points) {
+ auto Block = blockReference(Function, Point.Block);
+ if (!Block)
+ return std::unexpected(Block.error());
+ Output.append(" - { point: ");
+ Output.append(yamlScalar(*Block));
+ if (!Point.Registers.empty()) {
+ Output.append(", registers: [ ");
+ for (std::size_t Index = 0; Index < Point.Registers.size(); ++Index) {
+ if (Index != 0)
+ Output.append(", ");
+ auto Name = physicalRegister(Point.Registers[Index]);
+ if (!Name)
+ return std::unexpected(Name.error());
+ Output.append(yamlScalar(*Name));
+ }
+ Output.append(" ]");
+ }
+ Output.append(" }\n");
+ }
+ return {};
+ };
+ if (auto Printed = PrintPoints("savePoint", State.SavePoints); !Printed)
+ return Printed;
+ return PrintPoints("restorePoint", State.RestorePoints);
+}
+
+Expected<void> MIRDocumentPrinter::printStackObjects(const MachineFunction &Function,
+ const MIParserContext &Context, bool Fixed) {
+ const auto &Slots = Fixed ? Context.FixedStackObjects : Context.StackObjects;
+ if (Slots.empty()) {
+ if (Options.Mode == MIPrintMode::Complete)
+ Output.append(Fixed ? "fixedStack: []\n" : "stack: []\n");
+ return {};
+ }
+ Output.append(Fixed ? "fixedStack:\n" : "stack:\n");
+ for (const MIFrameObjectSlot &Slot : Slots) {
+ const StackObject *Object = Function.frameInfo().object(Slot.Index);
+ const StackObjectDesc *Description = Object == nullptr ? nullptr : Object->description();
+ if (Description == nullptr)
+ return makeUnexpected(ErrorCode::VerificationError, "MIR stack slot is a tombstone");
+ if (Description->IsDead || Description->AddressTaken || !Description->Lifetime.empty() ||
+ Description->TargetFlags != 0) {
+ return makeUnexpected(ErrorCode::Unsupported,
+ "stack object contains state not represented by fixed LLVM MIR");
+ }
+
+ std::string_view Type;
+ switch (Description->Kind) {
+ case StackObjectKind::Default:
+ case StackObjectKind::FixedABI:
+ Type = "default";
+ break;
+ case StackObjectKind::FixedSpillSlot:
+ case StackObjectKind::SpillSlot:
+ Type = "spill-slot";
+ break;
+ case StackObjectKind::VariableSized:
+ Type = "variable-sized";
+ break;
+ case StackObjectKind::DynamicAlloca:
+ case StackObjectKind::CalleeSaved:
+ case StackObjectKind::EmergencySpill:
+ case StackObjectKind::Target:
+ case StackObjectKind::Count:
+ return makeUnexpected(ErrorCode::Unsupported,
+ "stack object kind has no fixed LLVM MIR representation");
+ }
+
+ Output.append(" - { id: ");
+ Output.append(std::to_string(Slot.TextID));
+ if (!Fixed && Description->AllocaName) {
+ Output.append(", name: ");
+ Output.append(yamlScalar(*Description->AllocaName));
+ }
+ if (Options.Mode == MIPrintMode::Complete || Type != "default") {
+ Output.append(", type: ");
+ Output.append(Type);
+ }
+ if (Options.Mode == MIPrintMode::Complete || Description->Offset.value_or(0) != 0) {
+ Output.append(", offset: ");
+ Output.append(std::to_string(Description->Offset.value_or(0)));
+ }
+ if (Description->Kind != StackObjectKind::VariableSized) {
+ Output.append(", size: ");
+ Output.append(std::to_string(Description->Size.value_or(0)));
+ }
+ if (Options.Mode == MIPrintMode::Complete || Description->Align.bytes() != 1) {
+ Output.append(", alignment: ");
+ Output.append(std::to_string(Description->Align.bytes()));
+ }
+ if (Options.Mode == MIPrintMode::Complete || Description->Stack != StackID::defaultStack()) {
+ Output.append(", stack-id: ");
+ Output.append(getStackIDName(Description->Stack));
+ }
+ if (Fixed && Description->Kind != StackObjectKind::FixedSpillSlot) {
+ if (Options.Mode == MIPrintMode::Complete || Description->IsImmutable)
+ Output.append(Description->IsImmutable ? ", isImmutable: true" : ", isImmutable: false");
+ if (Options.Mode == MIPrintMode::Complete || Description->IsAliased)
+ Output.append(Description->IsAliased ? ", isAliased: true" : ", isAliased: false");
+ } else if (!Fixed && (Description->IsImmutable || Description->IsAliased)) {
+ return makeUnexpected(ErrorCode::Unsupported,
+ "ordinary stack object immutable/aliased state is not serialized");
+ }
+ if (Description->CalleeSavedRegister) {
+ auto Name = physicalRegister(*Description->CalleeSavedRegister);
+ if (!Name)
+ return std::unexpected(Name.error());
+ Output.append(", callee-saved-register: ");
+ Output.append(yamlScalar(*Name));
+ }
+ if (Options.Mode == MIPrintMode::Complete || !Description->CalleeSavedRestored) {
+ Output.append(Description->CalleeSavedRestored ? ", callee-saved-restored: true"
+ : ", callee-saved-restored: false");
+ }
+ if (!Fixed && Description->LocalOffset) {
+ Output.append(", local-offset: ");
+ Output.append(std::to_string(*Description->LocalOffset));
+ }
+ if (Description->DebugInfo) {
+ const MachineVariableDebugInfo &Debug = *Description->DebugInfo;
+ if (Debug.Variable) {
+ auto Value = metadataReference(*Debug.Variable, Context);
+ if (!Value)
+ return std::unexpected(Value.error());
+ Output.append(", debug-info-variable: ");
+ Output.append(yamlScalar(*Value));
+ }
+ if (Debug.Expression) {
+ auto Value = metadataValue(*Debug.Expression, Context);
+ if (!Value)
+ return std::unexpected(Value.error());
+ Output.append(", debug-info-expression: ");
+ Output.append(yamlScalar(*Value));
+ }
+ if (Debug.Location) {
+ auto Value = debugLocation(*Debug.Location, Context);
+ if (!Value)
+ return std::unexpected(Value.error());
+ Output.append(", debug-info-location: ");
+ Output.append(yamlScalar(*Value));
+ }
+ }
+ Output.append(" }\n");
+ }
+ return {};
+}
+
+Expected<void> MIRDocumentPrinter::printConstants(const MachineFunction &Function,
+ const MIParserContext &Context) {
+ if (Context.ConstantPoolItems.empty()) {
+ if (Options.Mode == MIPrintMode::Complete)
+ Output.append("constants: []\n");
+ return {};
+ }
+ Output.append("constants:\n");
+ for (const MIConstantPoolSlot &Slot : Context.ConstantPoolItems) {
+ const MachineConstantPoolEntry *Entry = Function.constantPool().entry(Slot.Index);
+ const MachineConstantPoolEntryDesc *Description =
+ Entry == nullptr ? nullptr : Entry->description();
+ if (Description == nullptr)
+ return makeUnexpected(ErrorCode::VerificationError, "constant-pool slot is a tombstone");
+ if (Description->Merge != ConstantPoolMergeKind::Never ||
+ Description->Relocation != ConstantPoolRelocationMode::None ||
+ Description->Placement != ConstantPoolPlacement{}) {
+ return makeUnexpected(ErrorCode::Unsupported,
+ "constant-pool placement state is not represented by fixed LLVM MIR");
+ }
+
+ bool IsTargetSpecific = false;
+ Expected<std::string> Value =
+ makeUnexpected(ErrorCode::Unsupported, "constant-pool kind is not MIR serializable");
+ if (const auto *Generic = std::get_if<GenericConstantData>(&Description->Value)) {
+ Value = printLLVMConstant(*Generic);
+ } else if (const auto *TargetConstantValue = std::get_if<TargetConstant>(&Description->Value)) {
+ IsTargetSpecific = true;
+ Value = Target.mirText().printTargetConstant(*TargetConstantValue);
+ }
+ if (!Value)
+ return std::unexpected(Value.error());
+
+ Output.append(" - id: ");
+ Output.append(std::to_string(Slot.TextID));
+ Output.append("\n value: ");
+ Output.append(yamlScalar(*Value));
+ Output.append("\n alignment: ");
+ Output.append(std::to_string(Description->Align.bytes()));
+ Output.push_back('\n');
+ if (IsTargetSpecific)
+ Output.append(" isTargetSpecific: true\n");
+ }
+ return {};
+}
+
+Expected<void> MIRDocumentPrinter::printJumpTable(const MachineFunction &Function,
+ const MIParserContext &Context) {
+ if (Context.JumpTables.empty())
+ return {};
+ const MachineJumpTableEntry *FirstEntry =
+ Function.jumpTableInfo().table(Context.JumpTables.front().Index);
+ const MachineJumpTableDesc *First = FirstEntry == nullptr ? nullptr : FirstEntry->description();
+ if (First == nullptr)
+ return makeUnexpected(ErrorCode::VerificationError, "jump-table slot is not defined");
+
+ std::string_view Kind;
+ std::uint8_t ExpectedSize = 8;
+ switch (First->Encoding) {
+ case JumpTableEntryEncoding::BlockAddress:
+ Kind = "block-address";
+ break;
+ case JumpTableEntryEncoding::GPRelative64BlockAddress:
+ Kind = "gp-rel64-block-address";
+ break;
+ case JumpTableEntryEncoding::GPRelative32BlockAddress:
+ Kind = "gp-rel32-block-address";
+ ExpectedSize = 4;
+ break;
+ case JumpTableEntryEncoding::LabelDifference32:
+ Kind = "label-difference32";
+ ExpectedSize = 4;
+ break;
+ case JumpTableEntryEncoding::LabelDifference64:
+ Kind = "label-difference64";
+ break;
+ case JumpTableEntryEncoding::Inline:
+ Kind = "inline";
+ ExpectedSize = 4;
+ break;
+ case JumpTableEntryEncoding::Custom32:
+ Kind = "custom32";
+ ExpectedSize = 4;
+ break;
+ case JumpTableEntryEncoding::Target:
+ case JumpTableEntryEncoding::Count:
+ return makeUnexpected(ErrorCode::Unsupported,
+ "target jump-table encoding has no fixed LLVM MIR spelling");
+ }
+
+ Output.append("jumpTable:\n kind: ");
+ Output.append(Kind);
+ Output.append("\n entries:\n");
+ for (const MIJumpTableSlot &Slot : Context.JumpTables) {
+ const MachineJumpTableEntry *Entry = Function.jumpTableInfo().table(Slot.Index);
+ const MachineJumpTableDesc *Description = Entry == nullptr ? nullptr : Entry->description();
+ if (Description == nullptr)
+ return makeUnexpected(ErrorCode::VerificationError, "jump-table slot is not defined");
+ if (Description->Encoding != First->Encoding || Description->EntrySize != ExpectedSize ||
+ Description->Align.bytes() != ExpectedSize ||
+ Description->Placement != JumpTablePlacement{} || Description->TargetEncoding != 0) {
+ return makeUnexpected(ErrorCode::Unsupported,
+ "jump-table entries cannot share one fixed LLVM MIR kind");
+ }
+ Output.append(" - { id: ");
+ Output.append(std::to_string(Slot.TextID));
+ Output.append(", blocks: [ ");
+ for (std::size_t Index = 0; Index < Description->Targets.size(); ++Index) {
+ if (Index != 0)
+ Output.append(", ");
+ if (Description->Targets[Index] == nullptr)
+ return makeUnexpected(ErrorCode::VerificationError,
+ "jump-table entry contains a null block");
+ auto Block = blockReference(Function, Description->Targets[Index]->stableID());
+ if (!Block)
+ return std::unexpected(Block.error());
+ Output.append(yamlScalar(*Block));
+ }
+ Output.append(" ] }\n");
+ }
+ return {};
+}
+
+Expected<void> MIRDocumentPrinter::printMetadata(const MachineFunction &Function,
+ const MIParserContext &Context) {
+ if (Function.metadata().nodes().empty())
+ return {};
+ Output.append("machineMetadataNodes:\n");
+ for (const MachineMetadataNode &Node : Function.metadata().nodes()) {
+ auto NodeReference =
+ metadataReference(MachineMetadataReference::machineFunction(Node.Slot), Context);
+ if (!NodeReference)
+ return std::unexpected(NodeReference.error());
+ std::string Text = *NodeReference + " = ";
+ if (Node.IsDistinct)
+ Text.append("distinct ");
+ Text.append("!{");
+ for (std::size_t Index = 0; Index < Node.Elements.size(); ++Index) {
+ if (Index != 0)
+ Text.append(", ");
+ const MachineMetadataElement &Element = Node.Elements[Index];
+ if (Element.isNull()) {
+ Text.append("null");
+ } else if (const MachineMetadataReference *Reference = Element.reference()) {
+ auto Printed = metadataReference(*Reference, Context);
+ if (!Printed)
+ return std::unexpected(Printed.error());
+ Text.append(*Printed);
+ } else if (const std::string *String = Element.string()) {
+ Text.append("!\"");
+ for (const char Character : *String) {
+ if (Character == '\\' || Character == '"')
+ Text.push_back('\\');
+ Text.push_back(Character);
+ }
+ Text.push_back('"');
+ } else {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "machine metadata tuple contains an invalid element");
+ }
+ }
+ Text.push_back('}');
+ Output.append(" - ");
+ Output.append(yamlScalar(Text));
+ Output.push_back('\n');
+ }
+ return {};
+}
+
+Expected<void> MIRDocumentPrinter::printTargetState(const MachineFunction &Function) {
+ if (!Function.targetState().hasValue())
+ return {};
+ auto Node = Target.mirText().printFunctionState(Function.targetState());
+ if (!Node)
+ return std::unexpected(Node.error());
+ auto Text = targetNode(*Node);
+ if (!Text)
+ return std::unexpected(Text.error());
+ Output.append("machineFunctionInfo: ");
+ Output.append(*Text);
+ Output.push_back('\n');
+ return {};
+}
+
+Expected<void> MIRDocumentPrinter::printFunctionRecords(const MachineFunction &Function,
+ const MIParserContext &Context) {
+ const MachineFunctionRecords &Records = Function.records();
+ if (!Records.entryValues().empty()) {
+ Output.append("entry_values:\n");
+ for (const MachineEntryValueInfo &Entry : Records.entryValues()) {
+ auto RegisterName = physicalRegister(Entry.Reg);
+ if (!RegisterName)
+ return std::unexpected(RegisterName.error());
+ if (!Entry.DebugInfo.Variable || !Entry.DebugInfo.Expression || !Entry.DebugInfo.Location) {
+ return makeUnexpected(ErrorCode::Unsupported,
+ "MIR entry value requires complete debug metadata");
+ }
+ auto Variable = metadataReference(*Entry.DebugInfo.Variable, Context);
+ auto Expression = metadataValue(*Entry.DebugInfo.Expression, Context);
+ auto Location = debugLocation(*Entry.DebugInfo.Location, Context);
+ if (!Variable)
+ return std::unexpected(Variable.error());
+ if (!Expression)
+ return std::unexpected(Expression.error());
+ if (!Location)
+ return std::unexpected(Location.error());
+ Output.append(" - { entry-value-register: ");
+ Output.append(yamlScalar(*RegisterName));
+ Output.append(", debug-info-variable: ");
+ Output.append(yamlScalar(*Variable));
+ Output.append(", debug-info-expression: ");
+ Output.append(yamlScalar(*Expression));
+ Output.append(", debug-info-location: ");
+ Output.append(yamlScalar(*Location));
+ Output.append(" }\n");
+ }
+ } else if (Options.Mode == MIPrintMode::Complete) {
+ Output.append("entry_values: []\n");
+ }
+
+ if (!Records.callSites().empty()) {
+ Output.append("callSites:\n");
+ for (const MachineCallSiteInfo &CallSite : Records.callSites()) {
+ auto Location = instructionLocation(Function, CallSite.CallInstruction);
+ if (!Location)
+ return std::unexpected(Location.error());
+ Output.append(" - { bb: ");
+ Output.append(std::to_string(Location->Block));
+ Output.append(", offset: ");
+ Output.append(std::to_string(Location->Offset));
+ if (!CallSite.ForwardedArguments.empty()) {
+ Output.append(", fwdArgRegs: [ ");
+ for (std::size_t Index = 0; Index < CallSite.ForwardedArguments.size(); ++Index) {
+ if (Index != 0)
+ Output.append(", ");
+ const MachineCallSiteArgument &Argument = CallSite.ForwardedArguments[Index];
+ auto RegisterName = physicalRegister(Argument.Reg);
+ if (!RegisterName)
+ return std::unexpected(RegisterName.error());
+ Output.append("{ arg: ");
+ Output.append(std::to_string(Argument.ArgumentNumber));
+ Output.append(", reg: ");
+ Output.append(yamlScalar(*RegisterName));
+ Output.append(" }");
+ }
+ Output.append(" ]");
+ }
+ if (!CallSite.CalleeTypeIDs.empty()) {
+ Output.append(", calleeTypeIds: [ ");
+ for (std::size_t Index = 0; Index < CallSite.CalleeTypeIDs.size(); ++Index) {
+ if (Index != 0)
+ Output.append(", ");
+ Output.append(std::to_string(CallSite.CalleeTypeIDs[Index]));
+ }
+ Output.append(" ]");
+ }
+ Output.append(" }\n");
+ }
+ } else if (Options.Mode == MIPrintMode::Complete) {
+ Output.append("callSites: []\n");
+ }
+
+ if (!Records.debugValueSubstitutions().empty()) {
+ Output.append("debugValueSubstitutions:\n");
+ for (const MachineDebugValueSubstitution &Substitution : Records.debugValueSubstitutions()) {
+ Output.append(" - { srcinst: ");
+ Output.append(std::to_string(Substitution.Source.InstructionNumber));
+ Output.append(", srcop: ");
+ Output.append(std::to_string(Substitution.Source.OperandNumber));
+ Output.append(", dstinst: ");
+ Output.append(std::to_string(Substitution.Destination.InstructionNumber));
+ Output.append(", dstop: ");
+ Output.append(std::to_string(Substitution.Destination.OperandNumber));
+ Output.append(", subreg: ");
+ Output.append(std::to_string(Substitution.Subregister.value()));
+ Output.append(" }\n");
+ }
+ } else if (Options.Mode == MIPrintMode::Complete) {
+ Output.append("debugValueSubstitutions: []\n");
+ }
+
+ if (!Records.calledGlobals().empty()) {
+ Output.append("calledGlobals:\n");
+ for (const MachineCalledGlobal &Called : Records.calledGlobals()) {
+ auto Location = instructionLocation(Function, Called.CallInstruction);
+ if (!Location)
+ return std::unexpected(Location.error());
+ Output.append(" - { bb: ");
+ Output.append(std::to_string(Location->Block));
+ Output.append(", offset: ");
+ Output.append(std::to_string(Location->Offset));
+ Output.append(", callee: ");
+ Output.append(yamlScalar(Called.Callee));
+ Output.append(", flags: ");
+ Output.append(std::to_string(Called.Flags));
+ Output.append(" }\n");
+ }
+ }
+
+ if (!Records.prefetchTargets().empty()) {
+ Output.append("prefetch-targets:\n");
+ for (const MachinePrefetchTarget &TargetValue : Records.prefetchTargets()) {
+ std::string Text = "bb_id " + std::to_string(TargetValue.Block.BaseID) + ", " +
+ std::to_string(TargetValue.Block.CloneID) + ", " +
+ std::to_string(TargetValue.CallsiteIndex);
+ Output.append(" - ");
+ Output.append(yamlScalar(Text));
+ Output.push_back('\n');
+ }
+ }
+ return {};
+}
+
+Expected<void> MIRDocumentPrinter::printFunction(const MachineFunction &Function,
+ const MIRFunctionDocumentInfo *) {
+ if (Function.compilationStatus().state() != MachineCompilationState::Success) {
+ return makeUnexpected(ErrorCode::Unsupported,
+ "fixed LLVM MIR cannot serialize this compilation failure state");
+ }
+ auto Context = createBodyContext(Function);
+ if (!Context)
+ return std::unexpected(Context.error());
+
+ Output.append("---\nname: ");
+ Output.append(yamlScalar(Function.name()));
+ Output.push_back('\n');
+ if (Function.attributes().alignment()) {
+ Output.append("alignment: ");
+ Output.append(std::to_string(Function.attributes().alignment()->bytes()));
+ Output.push_back('\n');
+ }
+
+ const MachineFunctionProperties &Properties = Function.properties();
+ const MachineFunctionAttributes &Attributes = Function.attributes();
+ printBoolean("exposesReturnsTwice",
+ Attributes.has(MachineFunctionAttribute::ExposesReturnsTwice));
+ printBoolean("legalized", Properties.has(MachineFunctionProperty::Legalized));
+ printBoolean("regBankSelected", Properties.has(MachineFunctionProperty::RegBankSelected));
+ printBoolean("selected", Properties.has(MachineFunctionProperty::Selected));
+ printBoolean("failedISel", Properties.has(MachineFunctionProperty::FailedISel));
+ printBoolean("tracksRegLiveness", Properties.has(MachineFunctionProperty::TracksLiveness));
+ printBoolean("hasWinCFI", Attributes.has(MachineFunctionAttribute::HasWinCFI));
+ printBoolean("noPhis", Properties.has(MachineFunctionProperty::NoPHIs), true);
+ printBoolean("isSSA", Properties.has(MachineFunctionProperty::IsSSA), true);
+ printBoolean("noVRegs", Properties.has(MachineFunctionProperty::NoVRegs), true);
+ printBoolean("hasFakeUses", Attributes.has(MachineFunctionAttribute::HasFakeUses), true);
+ printBoolean("callsEHReturn", Attributes.has(MachineFunctionAttribute::CallsEHReturn));
+ printBoolean("callsUnwindInit", Attributes.has(MachineFunctionAttribute::CallsUnwindInit));
+ printBoolean("hasEHContTarget", Attributes.has(MachineFunctionAttribute::HasEHContTarget));
+ printBoolean("hasEHScopes", Attributes.has(MachineFunctionAttribute::HasEHScopes));
+ printBoolean("hasEHFunclets", Attributes.has(MachineFunctionAttribute::HasEHFunclets));
+ printBoolean("isOutlined", Attributes.has(MachineFunctionAttribute::IsOutlined));
+ printBoolean("debugInstrRef", Attributes.has(MachineFunctionAttribute::DebugInstrRef));
+ printBoolean("failsVerification", Properties.has(MachineFunctionProperty::FailsVerification));
+ printBoolean("tracksDebugUserValues",
+ Attributes.has(MachineFunctionAttribute::TracksDebugUserValues));
+
+ if (auto Printed = printRegisters(Function, *Context); !Printed)
+ return Printed;
+ if (auto Printed = printFrameInfo(Function, *Context); !Printed)
+ return Printed;
+ if (auto Printed = printStackObjects(Function, *Context, true); !Printed)
+ return Printed;
+ if (auto Printed = printStackObjects(Function, *Context, false); !Printed)
+ return Printed;
+ if (auto Printed = printFunctionRecords(Function, *Context); !Printed)
+ return Printed;
+ if (auto Printed = printConstants(Function, *Context); !Printed)
+ return Printed;
+ if (auto Printed = printTargetState(Function); !Printed)
+ return Printed;
+ if (auto Printed = printJumpTable(Function, *Context); !Printed)
+ return Printed;
+ if (auto Printed = printMetadata(Function, *Context); !Printed)
+ return Printed;
+
+ auto Body =
+ printMachineFunctionBody(Function, Target, *Context, MIPrinterOptions{.Mode = Options.Mode});
+ if (!Body)
+ return std::unexpected(Body.error());
+ Output.append("body: |\n");
+ appendBlockScalar(Output, *Body, 2);
+ return {};
+}
+
+Expected<std::string> MIRDocumentPrinter::run() {
+ auto UnitVerification = Document.unit().verify();
+ if (!UnitVerification)
+ return std::unexpected(UnitVerification.error());
+ if (Document.unit().functionCount() == 0) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "MIR document must contain at least one machine function");
+ }
+
+ for (const MIRFunctionDocumentInfo &Info : Document.functionDocumentInfo()) {
+ if (Document.unit().function(Info.Function) == nullptr) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "MIR document info references an unknown machine function");
+ }
+ }
+
+ for (std::size_t Index = 0; Index < Document.unit().functionCount(); ++Index) {
+ const MachineFunction *Function = Document.unit().functionAt(Index);
+ if (Function == nullptr) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "compilation unit contains a null machine function");
+ }
+ auto ReferenceVerification =
+ verifyMIRDocumentReferences(Document, *Function, Options.EmitEmbeddedIR);
+ if (!ReferenceVerification)
+ return std::unexpected(ReferenceVerification.error());
+ auto LayoutVerification = verifyOmittedEmbeddedIRLayout(*Function);
+ if (!LayoutVerification)
+ return std::unexpected(LayoutVerification.error());
+ }
+
+ if (Options.EmitEmbeddedIR && Document.embeddedIR() != nullptr) {
+ Output.append("--- |\n");
+ appendBlockScalar(Output, Document.embeddedIR()->source().text(), 2);
+ }
+ for (std::size_t Index = 0; Index < Document.unit().functionCount(); ++Index) {
+ const MachineFunction *Function = Document.unit().functionAt(Index);
+ if (Function == nullptr)
+ return makeUnexpected(ErrorCode::VerificationError,
+ "compilation unit contains a null machine function");
+ if (auto Printed = printFunction(*Function, documentInfo(*Function)); !Printed)
+ return std::unexpected(Printed.error());
+ }
+ return std::move(Output);
+}
+
+} // namespace
+
+Expected<std::string> printMIRDocument(const MIRDocument &Document, const TargetDescription &Target,
+ MIRDocumentPrinterOptions Options) {
+ return MIRDocumentPrinter(Document, Target, Options).run();
+}
+
+} // namespace rcc::mir