From d62f022c92fcc3ebe2269273628943a0089683fd Mon Sep 17 00:00:00 2001 From: Richard-Qin-X Date: Thu, 13 Aug 2026 06:19:30 +0800 Subject: [CodeGen] Align MIR parser and printer ownership --- backend/lib/CodeGen/MIRParser/MILexer.cpp | 964 ++++++ backend/lib/CodeGen/MIRParser/MILexer.h | 181 ++ backend/lib/CodeGen/MIRParser/MIParser.cpp | 2967 ++++++++++++++++++ backend/lib/CodeGen/MIRParser/MIRParser.cpp | 334 +++ backend/lib/CodeGen/MIRPrinter.cpp | 3167 ++++++++++++++++++++ backend/lib/CodeGen/MIRYamlMapping.cpp | 2926 ++++++++++++++++++ backend/lib/MIR/CMakeLists.txt | 71 +- backend/lib/MIR/MIDebugSyntax.cpp | 264 -- backend/lib/MIR/MILexer.cpp | 964 ------ backend/lib/MIR/MIParser.cpp | 2967 ------------------ backend/lib/MIR/MIPrinter.cpp | 1694 ----------- backend/lib/MIR/MIRDataParser.cpp | 287 -- backend/lib/MIR/MIRDocument.cpp | 29 - backend/lib/MIR/MIRDocumentParser.cpp | 398 --- backend/lib/MIR/MIRDocumentParser.h | 132 - backend/lib/MIR/MIRDocumentPrinter.cpp | 1499 --------- backend/lib/MIR/MIRDocumentReferenceValidation.cpp | 426 --- backend/lib/MIR/MIRDocumentReferenceValidation.h | 25 - backend/lib/MIR/MIRFrameParser.cpp | 467 --- backend/lib/MIR/MIRFunctionParser.cpp | 552 ---- backend/lib/MIR/MIRFunctionRecordsParser.cpp | 341 --- backend/lib/MIR/MIRMetadataParser.cpp | 333 -- backend/lib/MIR/MIRMetadataTextParser.cpp | 259 -- backend/lib/MIR/MIRMetadataTextParser.h | 74 - backend/lib/MIR/MIRParserUtilities.h | 85 - backend/lib/Tooling/ArtifactStore.cpp | 2 +- backend/lib/Tooling/BackendDriver.cpp | 2 +- backend/lib/Tooling/MIRFuzzer.cpp | 2 +- backend/lib/Tooling/PipelineDriver.cpp | 2 +- 29 files changed, 10563 insertions(+), 10851 deletions(-) create mode 100644 backend/lib/CodeGen/MIRParser/MILexer.cpp create mode 100644 backend/lib/CodeGen/MIRParser/MILexer.h create mode 100644 backend/lib/CodeGen/MIRParser/MIParser.cpp create mode 100644 backend/lib/CodeGen/MIRParser/MIRParser.cpp create mode 100644 backend/lib/CodeGen/MIRPrinter.cpp create mode 100644 backend/lib/CodeGen/MIRYamlMapping.cpp delete mode 100644 backend/lib/MIR/MIDebugSyntax.cpp delete mode 100644 backend/lib/MIR/MILexer.cpp delete mode 100644 backend/lib/MIR/MIParser.cpp delete mode 100644 backend/lib/MIR/MIPrinter.cpp delete mode 100644 backend/lib/MIR/MIRDataParser.cpp delete mode 100644 backend/lib/MIR/MIRDocument.cpp delete mode 100644 backend/lib/MIR/MIRDocumentParser.cpp delete mode 100644 backend/lib/MIR/MIRDocumentParser.h delete mode 100644 backend/lib/MIR/MIRDocumentPrinter.cpp delete mode 100644 backend/lib/MIR/MIRDocumentReferenceValidation.cpp delete mode 100644 backend/lib/MIR/MIRDocumentReferenceValidation.h delete mode 100644 backend/lib/MIR/MIRFrameParser.cpp delete mode 100644 backend/lib/MIR/MIRFunctionParser.cpp delete mode 100644 backend/lib/MIR/MIRFunctionRecordsParser.cpp delete mode 100644 backend/lib/MIR/MIRMetadataParser.cpp delete mode 100644 backend/lib/MIR/MIRMetadataTextParser.cpp delete mode 100644 backend/lib/MIR/MIRMetadataTextParser.h delete mode 100644 backend/lib/MIR/MIRParserUtilities.h (limited to 'backend/lib') diff --git a/backend/lib/CodeGen/MIRParser/MILexer.cpp b/backend/lib/CodeGen/MIRParser/MILexer.cpp new file mode 100644 index 0000000..830c548 --- /dev/null +++ b/backend/lib/CodeGen/MIRParser/MILexer.cpp @@ -0,0 +1,964 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "MILexer.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace rcc::mir { +namespace { + +template +[[nodiscard]] consteval std::array +sortSpellings(std::array Spellings) { + for (std::size_t Left = 0; Left < Spellings.size(); ++Left) { + for (std::size_t Right = Left + 1; Right < Spellings.size(); ++Right) { + if (Spellings[Right] < Spellings[Left]) + std::swap(Spellings[Left], Spellings[Right]); + } + } + return Spellings; +} + +template +[[nodiscard]] consteval bool +hasUniqueSpellings(const std::array &Spellings) { + for (std::size_t Index = 1; Index < Spellings.size(); ++Index) { + if (Spellings[Index - 1] == Spellings[Index]) + return false; + } + return true; +} + +constexpr auto Keywords = sortSpellings(std::array{ + std::string_view{"addrspace"}, + std::string_view{"adjust_cfa_offset"}, + std::string_view{"afn"}, + std::string_view{"align"}, + std::string_view{"arcp"}, + std::string_view{"basealign"}, + std::string_view{"bb_id"}, + std::string_view{"bbsections"}, + std::string_view{"bfloat"}, + std::string_view{"blockaddress"}, + std::string_view{"call-entry"}, + std::string_view{"call-frame-size"}, + std::string_view{"cfi-type"}, + std::string_view{"constant-pool"}, + std::string_view{"contract"}, + std::string_view{"custom"}, + std::string_view{"dead"}, + std::string_view{"deactivation-symbol"}, + std::string_view{"debug-instr-number"}, + std::string_view{"debug-location"}, + std::string_view{"debug-use"}, + std::string_view{"dbg-instr-ref"}, + std::string_view{"def"}, + std::string_view{"def_cfa"}, + std::string_view{"def_cfa_offset"}, + std::string_view{"def_cfa_register"}, + std::string_view{"dereferenceable"}, + std::string_view{"disjoint"}, + std::string_view{"distinct"}, + std::string_view{"double"}, + std::string_view{"early-clobber"}, + std::string_view{"ehfunclet-entry"}, + std::string_view{"ehscope-entry"}, + std::string_view{"escape"}, + std::string_view{"exact"}, + std::string_view{"float"}, + std::string_view{"floatpred"}, + std::string_view{"fp128"}, + std::string_view{"frame-destroy"}, + std::string_view{"frame-setup"}, + std::string_view{"got"}, + std::string_view{"half"}, + std::string_view{"heap-alloc-marker"}, + std::string_view{"implicit"}, + std::string_view{"implicit-def"}, + std::string_view{"inbounds"}, + std::string_view{"inlineasm-br-indirect-target"}, + std::string_view{"internal"}, + std::string_view{"intpred"}, + std::string_view{"intrinsic"}, + std::string_view{"invariant"}, + std::string_view{"ir-block-address-taken"}, + std::string_view{"jump-table"}, + std::string_view{"killed"}, + std::string_view{"landing-pad"}, + std::string_view{"lanemask"}, + std::string_view{"liveins"}, + std::string_view{"liveout"}, + std::string_view{"llvm_def_aspace_cfa"}, + std::string_view{"llvm_register_pair"}, + std::string_view{"llvm_vector_offset"}, + std::string_view{"llvm_vector_register_mask"}, + std::string_view{"llvm_vector_registers"}, + std::string_view{"machine-block-address-taken"}, + std::string_view{"mmra"}, + std::string_view{"negate_ra_sign_state"}, + std::string_view{"negate_ra_sign_state_with_pc"}, + std::string_view{"ninf"}, + std::string_view{"nnan"}, + std::string_view{"noconvergent"}, + std::string_view{"nofpexcept"}, + std::string_view{"non-temporal"}, + std::string_view{"nneg"}, + std::string_view{"nsw"}, + std::string_view{"nsz"}, + std::string_view{"nusw"}, + std::string_view{"nuw"}, + std::string_view{"offset"}, + std::string_view{"pcsections"}, + std::string_view{"post-instr-symbol"}, + std::string_view{"ppc_fp128"}, + std::string_view{"pre-instr-symbol"}, + std::string_view{"reassoc"}, + std::string_view{"rel_offset"}, + std::string_view{"remember_state"}, + std::string_view{"renamable"}, + std::string_view{"register"}, + std::string_view{"restore"}, + std::string_view{"restore_state"}, + std::string_view{"same_value"}, + std::string_view{"samesign"}, + std::string_view{"shufflemask"}, + std::string_view{"stack"}, + std::string_view{"successors"}, + std::string_view{"target-flags"}, + std::string_view{"target-index"}, + std::string_view{"tied-def"}, + std::string_view{"undef"}, + std::string_view{"undefined"}, + std::string_view{"unknown-address"}, + std::string_view{"unknown-size"}, + std::string_view{"unpredictable"}, + std::string_view{"volatile"}, + std::string_view{"window_save"}, + std::string_view{"x86_fp80"}, +}); + +constexpr auto MetadataKeywords = sortSpellings(std::array{ + std::string_view{"!DIExpression"}, + std::string_view{"!DILocation"}, + std::string_view{"!alias.scope"}, + std::string_view{"!noalias"}, + std::string_view{"!noalias.addrspace"}, + std::string_view{"!range"}, + std::string_view{"!tbaa"}, +}); + +static_assert(hasUniqueSpellings(Keywords)); +static_assert(hasUniqueSpellings(MetadataKeywords)); + +[[nodiscard]] constexpr bool isASCIIAlpha(char Character) noexcept { + return (Character >= 'a' && Character <= 'z') || (Character >= 'A' && Character <= 'Z'); +} + +[[nodiscard]] constexpr bool isASCIIDigit(char Character) noexcept { + return Character >= '0' && Character <= '9'; +} + +[[nodiscard]] constexpr bool isASCIIHexDigit(char Character) noexcept { + return isASCIIDigit(Character) || (Character >= 'a' && Character <= 'f') || + (Character >= 'A' && Character <= 'F'); +} + +[[nodiscard]] constexpr unsigned hexDigitValue(char Character) noexcept { + if (isASCIIDigit(Character)) + return static_cast(Character - '0'); + if (Character >= 'a' && Character <= 'f') + return static_cast(Character - 'a' + 10); + return static_cast(Character - 'A' + 10); +} + +[[nodiscard]] constexpr bool isIdentifierCharacter(char Character) noexcept { + return isASCIIAlpha(Character) || isASCIIDigit(Character) || Character == '_' || + Character == '-' || Character == '.' || Character == '$'; +} + +[[nodiscard]] constexpr bool isRegisterCharacter(char Character) noexcept { + return isIdentifierCharacter(Character) && Character != '.'; +} + +[[nodiscard]] constexpr bool isHorizontalWhitespace(char Character) noexcept { + return Character == ' ' || Character == '\t'; +} + +[[nodiscard]] constexpr bool isNewlineCharacter(char Character) noexcept { + return Character == '\n' || Character == '\r'; +} + +[[nodiscard]] constexpr bool isHexFloatingPointPrefix(char Character) noexcept { + return Character == 'H' || Character == 'K' || Character == 'L' || Character == 'M' || + Character == 'R'; +} + +[[nodiscard]] std::string unexpectedCharacterMessage(unsigned char Character) { + if (Character >= 0x20U && Character <= 0x7EU) { + return "unexpected character '" + std::string(1, static_cast(Character)) + "'"; + } + std::ostringstream Message; + Message << "unexpected byte 0x" << std::uppercase << std::hex << std::setw(2) << std::setfill('0') + << static_cast(Character); + return Message.str(); +} + +} // namespace + +std::string_view getMITokenKindName(MITokenKind Kind) noexcept { + switch (Kind) { + case MITokenKind::EndOfFile: + return "end-of-file"; + case MITokenKind::Error: + return "error"; + case MITokenKind::Newline: + return "newline"; + case MITokenKind::Comma: + return "comma"; + case MITokenKind::Equal: + return "equal"; + case MITokenKind::Underscore: + return "underscore"; + case MITokenKind::Colon: + return "colon"; + case MITokenKind::DoubleColon: + return "double-colon"; + case MITokenKind::Dot: + return "dot"; + case MITokenKind::Exclaim: + return "exclaim"; + case MITokenKind::LeftParen: + return "left-paren"; + case MITokenKind::RightParen: + return "right-paren"; + case MITokenKind::LeftBrace: + return "left-brace"; + case MITokenKind::RightBrace: + return "right-brace"; + case MITokenKind::Plus: + return "plus"; + case MITokenKind::Minus: + return "minus"; + case MITokenKind::Less: + return "less"; + case MITokenKind::Greater: + return "greater"; + case MITokenKind::Keyword: + return "keyword"; + case MITokenKind::MetadataKeyword: + return "metadata-keyword"; + case MITokenKind::Identifier: + return "identifier"; + case MITokenKind::NamedRegister: + return "named-register"; + case MITokenKind::NamedVirtualRegister: + return "named-virtual-register"; + case MITokenKind::MachineBasicBlockLabel: + return "machine-basic-block-label"; + case MITokenKind::MachineBasicBlock: + return "machine-basic-block"; + case MITokenKind::StackObject: + return "stack-object"; + case MITokenKind::FixedStackObject: + return "fixed-stack-object"; + case MITokenKind::NamedGlobalValue: + return "named-global-value"; + case MITokenKind::GlobalValue: + return "global-value"; + case MITokenKind::ExternalSymbol: + return "external-symbol"; + case MITokenKind::MCSymbol: + return "mc-symbol"; + case MITokenKind::IntegerLiteral: + return "integer-literal"; + case MITokenKind::FloatingPointLiteral: + return "floating-point-literal"; + case MITokenKind::HexLiteral: + return "hex-literal"; + case MITokenKind::VirtualRegister: + return "virtual-register"; + case MITokenKind::ConstantPoolItem: + return "constant-pool-item"; + case MITokenKind::JumpTableIndex: + return "jump-table-index"; + case MITokenKind::NamedIRBlock: + return "named-ir-block"; + case MITokenKind::IRBlock: + return "ir-block"; + case MITokenKind::NamedIRValue: + return "named-ir-value"; + case MITokenKind::IRValue: + return "ir-value"; + case MITokenKind::QuotedIRValue: + return "quoted-ir-value"; + case MITokenKind::SubRegisterIndex: + return "subregister-index"; + case MITokenKind::StringConstant: + return "string-constant"; + } + return "unknown"; +} + +bool isMIKeyword(std::string_view Spelling) noexcept { + return std::ranges::binary_search(Keywords, Spelling); +} + +bool isMIMetadataKeyword(std::string_view Spelling) noexcept { + return std::ranges::binary_search(MetadataKeywords, Spelling); +} + +std::string_view MIToken::stringValue() const noexcept { + if (const auto *View = std::get_if(&StringValue)) + return *View; + if (const auto *Owned = std::get_if(&StringValue)) + return *Owned; + return {}; +} + +bool MIToken::isRegister() const noexcept { + return Kind == MITokenKind::NamedRegister || Kind == MITokenKind::NamedVirtualRegister || + Kind == MITokenKind::VirtualRegister || Kind == MITokenKind::Underscore; +} + +bool MIToken::isRegisterFlag() const noexcept { + return isKeyword("implicit") || isKeyword("implicit-def") || isKeyword("def") || + isKeyword("dead") || isKeyword("killed") || isKeyword("undef") || isKeyword("internal") || + isKeyword("early-clobber") || isKeyword("debug-use") || isKeyword("renamable"); +} + +bool MIToken::isMemoryOperandFlag() const noexcept { + return isKeyword("volatile") || isKeyword("non-temporal") || isKeyword("dereferenceable") || + isKeyword("invariant") || Kind == MITokenKind::StringConstant; +} + +MIToken MILexer::makeToken(MITokenKind Kind, TextRange Range, std::string_view Spelling, + std::string_view IntegerValue) { + return MIToken(Kind, Range, Spelling, {}, IntegerValue); +} + +MIToken MILexer::makeStringToken(MITokenKind Kind, TextRange Range, std::string_view Spelling, + std::string_view StringValue, std::string_view IntegerValue) { + return MIToken(Kind, Range, Spelling, StringValue, IntegerValue); +} + +MIToken MILexer::makeOwnedStringToken(MITokenKind Kind, TextRange Range, std::string_view Spelling, + std::string StringValue, std::string_view IntegerValue) { + return MIToken(Kind, Range, Spelling, std::move(StringValue), IntegerValue); +} + +class MILexer::Impl final { +public: + Impl(std::string SourceName, std::string SourceText, MILexerOptions Options) + : Source(std::move(SourceName), std::move(SourceText)), Options(Options) {} + + [[nodiscard]] MIToken next() { + if (Failed) + return token(MITokenKind::EndOfFile, Offset, Offset); + + if (auto TriviaError = skipTrivia()) + return std::move(*TriviaError); + + if (atEnd()) + return token(MITokenKind::EndOfFile, Offset, Offset); + + const std::size_t Begin = Offset; + MIToken Result = lexToken(); + if (Result.kind() == MITokenKind::Error) + return Result; + + if (Options.MaximumTokenLength != 0 && + Result.range().End.Offset - Result.range().Begin.Offset > Options.MaximumTokenLength) { + return fail(Begin, Offset, "machine instruction token exceeds the configured length limit"); + } + if (Options.MaximumTokens != 0 && TokenCount >= Options.MaximumTokens) { + return fail(Begin, Offset, "machine instruction token count exceeds the configured limit"); + } + ++TokenCount; + return Result; + } + + [[nodiscard]] const TextSource &source() const noexcept { + return Source; + } + + [[nodiscard]] std::span diagnostics() const noexcept { + return Diagnostics; + } + + [[nodiscard]] bool failed() const noexcept { + return Failed; + } + + [[nodiscard]] std::size_t tokenCount() const noexcept { + return TokenCount; + } + +private: + struct QuotedValue final { + std::size_t End = 0; + std::string Value; + }; + + [[nodiscard]] std::string_view text() const noexcept { + return Source.text(); + } + + [[nodiscard]] bool atEnd(std::size_t Lookahead = 0) const noexcept { + return Offset >= text().size() || Lookahead >= text().size() - Offset; + } + + [[nodiscard]] char peek(std::size_t Lookahead = 0) const noexcept { + if (Offset >= text().size() || Lookahead >= text().size() - Offset) + return '\0'; + return text()[Offset + Lookahead]; + } + + [[nodiscard]] bool startsWith(std::string_view Prefix) const noexcept { + return text().substr(Offset).starts_with(Prefix); + } + + [[nodiscard]] std::string_view slice(std::size_t Begin, std::size_t End) const noexcept { + assert(Begin <= End && End <= text().size()); + return text().substr(Begin, End - Begin); + } + + [[nodiscard]] MIToken token(MITokenKind Kind, std::size_t Begin, std::size_t End, + std::string_view IntegerValue = {}) const { + return MILexer::makeToken(Kind, Source.range(Begin, End), slice(Begin, End), IntegerValue); + } + + [[nodiscard]] MIToken stringToken(MITokenKind Kind, std::size_t Begin, std::size_t End, + std::string_view Value, + std::string_view IntegerValue = {}) const { + return MILexer::makeStringToken(Kind, Source.range(Begin, End), slice(Begin, End), Value, + IntegerValue); + } + + [[nodiscard]] MIToken ownedStringToken(MITokenKind Kind, std::size_t Begin, std::size_t End, + std::string Value, + std::string_view IntegerValue = {}) const { + return MILexer::makeOwnedStringToken(Kind, Source.range(Begin, End), slice(Begin, End), + std::move(Value), IntegerValue); + } + + [[nodiscard]] MIToken fail(std::size_t Begin, std::size_t End, std::string Message) { + End = std::clamp(End, Begin, text().size()); + Diagnostics.push_back(TextDiagnostic{ + .Severity = DiagnosticSeverity::Error, + .Category = "mir-lexer", + .Message = std::move(Message), + .SourceName = std::string(Source.name()), + .Range = Source.range(Begin, End), + }); + Failed = true; + return token(MITokenKind::Error, Begin, End); + } + + [[nodiscard]] std::optional skipTrivia() { + while (true) { + while (!atEnd() && isHorizontalWhitespace(peek())) + ++Offset; + + if (peek() == ';') { + while (!atEnd() && !isNewlineCharacter(peek())) + ++Offset; + continue; + } + + if (!startsWith("/*")) + return std::nullopt; + + const std::size_t Begin = Offset; + Offset += 2; + while (!atEnd() && !startsWith("*/")) + ++Offset; + if (atEnd()) { + return fail(Begin, Offset, + "end of machine instruction input reached before the closing '*/'"); + } + Offset += 2; + } + } + + [[nodiscard]] std::optional scanQuotedString(std::size_t QuoteOffset) { + assert(QuoteOffset < text().size() && text()[QuoteOffset] == '"'); + std::size_t Cursor = QuoteOffset + 1; + while (Cursor < text().size() && text()[Cursor] != '"') { + if (isNewlineCharacter(text()[Cursor])) { + Offset = Cursor; + static_cast(fail(QuoteOffset, Cursor, + "end of machine instruction reached before the closing '\"'")); + return std::nullopt; + } + ++Cursor; + } + if (Cursor == text().size()) { + Offset = Cursor; + static_cast( + fail(QuoteOffset, Cursor, "end of machine instruction reached before the closing '\"'")); + return std::nullopt; + } + + std::string Value; + Value.reserve(Cursor - QuoteOffset - 1); + for (std::size_t Index = QuoteOffset + 1; Index < Cursor; ++Index) { + const char Character = text()[Index]; + if (Character == '\\' && Index + 1 < Cursor && text()[Index + 1] == '\\') { + Value.push_back('\\'); + ++Index; + continue; + } + if (Character == '\\' && Index + 2 < Cursor && isASCIIHexDigit(text()[Index + 1]) && + isASCIIHexDigit(text()[Index + 2])) { + const unsigned Decoded = + hexDigitValue(text()[Index + 1]) * 16U + hexDigitValue(text()[Index + 2]); + Value.push_back(static_cast(Decoded)); + Index += 2; + continue; + } + Value.push_back(Character); + } + return QuotedValue{.End = Cursor + 1, .Value = std::move(Value)}; + } + + [[nodiscard]] MIToken lexName(MITokenKind Kind, std::size_t PrefixLength) { + const std::size_t Begin = Offset; + Offset += PrefixLength; + if (peek() == '"') { + auto Quoted = scanQuotedString(Offset); + if (!Quoted) + return token(MITokenKind::Error, Begin, Offset); + Offset = Quoted->End; + return ownedStringToken(Kind, Begin, Offset, std::move(Quoted->Value)); + } + + const std::size_t ValueBegin = Offset; + while (!atEnd() && isIdentifierCharacter(peek())) + ++Offset; + return stringToken(Kind, Begin, Offset, slice(ValueBegin, Offset)); + } + + [[nodiscard]] std::optional lexMachineBasicBlock() { + const bool IsReference = startsWith("%bb."); + if (!IsReference && !startsWith("bb.")) + return std::nullopt; + + const std::size_t Begin = Offset; + const std::size_t PrefixLength = IsReference ? 4 : 3; + Offset += PrefixLength; + if (!isASCIIDigit(peek())) { + return fail(Offset, std::min(Offset + 1, text().size()), + "expected a decimal block number after '" + + std::string(IsReference ? "%bb.'" : "bb.'")); + } + + const std::size_t NumberBegin = Offset; + while (!atEnd() && isASCIIDigit(peek())) + ++Offset; + const std::string_view Number = slice(NumberBegin, Offset); + + if (peek() != '.') { + return stringToken(IsReference ? MITokenKind::MachineBasicBlock + : MITokenKind::MachineBasicBlockLabel, + Begin, Offset, {}, Number); + } + + ++Offset; + const std::size_t NameBegin = Offset; + if (peek() == '"') { + auto Quoted = scanQuotedString(Offset); + if (!Quoted) + return token(MITokenKind::Error, Begin, Offset); + Offset = Quoted->End; + return ownedStringToken(IsReference ? MITokenKind::MachineBasicBlock + : MITokenKind::MachineBasicBlockLabel, + Begin, Offset, std::move(Quoted->Value), Number); + } + while (!atEnd() && isIdentifierCharacter(peek())) + ++Offset; + return stringToken(IsReference ? MITokenKind::MachineBasicBlock + : MITokenKind::MachineBasicBlockLabel, + Begin, Offset, slice(NameBegin, Offset), Number); + } + + [[nodiscard]] std::optional lexIndex(std::string_view Prefix, MITokenKind Kind, + bool HasOptionalName) { + if (!startsWith(Prefix) || !isASCIIDigit(peek(Prefix.size()))) + return std::nullopt; + + const std::size_t Begin = Offset; + Offset += Prefix.size(); + const std::size_t NumberBegin = Offset; + while (!atEnd() && isASCIIDigit(peek())) + ++Offset; + const std::string_view Number = slice(NumberBegin, Offset); + + if (!HasOptionalName) + return token(Kind, Begin, Offset, Number); + + if (peek() != '.') + return stringToken(Kind, Begin, Offset, {}, Number); + + ++Offset; + const std::size_t NameBegin = Offset; + if (peek() == '"') { + auto Quoted = scanQuotedString(Offset); + if (!Quoted) + return token(MITokenKind::Error, Begin, Offset); + Offset = Quoted->End; + return ownedStringToken(Kind, Begin, Offset, std::move(Quoted->Value), Number); + } + while (!atEnd() && isIdentifierCharacter(peek())) + ++Offset; + return stringToken(Kind, Begin, Offset, slice(NameBegin, Offset), Number); + } + + [[nodiscard]] std::optional + lexIRReference(std::string_view Prefix, MITokenKind NumericKind, MITokenKind NamedKind) { + if (!startsWith(Prefix)) + return std::nullopt; + if (isASCIIDigit(peek(Prefix.size()))) + return lexIndex(Prefix, NumericKind, false); + return lexName(NamedKind, Prefix.size()); + } + + [[nodiscard]] std::optional lexRegister() { + if (peek() != '%' && peek() != '$') + return std::nullopt; + + const std::size_t Begin = Offset; + const char Prefix = peek(); + ++Offset; + if (Prefix == '%' && isASCIIDigit(peek())) { + const std::size_t NumberBegin = Offset; + while (!atEnd() && isASCIIDigit(peek())) + ++Offset; + return token(MITokenKind::VirtualRegister, Begin, Offset, slice(NumberBegin, Offset)); + } + + if (Prefix == '%' && !isRegisterCharacter(peek())) { + Offset = Begin; + return std::nullopt; + } + + const std::size_t NameBegin = Offset; + while (!atEnd() && isRegisterCharacter(peek())) + ++Offset; + return stringToken(Prefix == '%' ? MITokenKind::NamedVirtualRegister + : MITokenKind::NamedRegister, + Begin, Offset, slice(NameBegin, Offset)); + } + + [[nodiscard]] std::optional lexGlobalValue() { + if (peek() != '@') + return std::nullopt; + if (!isASCIIDigit(peek(1))) + return lexName(MITokenKind::NamedGlobalValue, 1); + + const std::size_t Begin = Offset++; + const std::size_t NumberBegin = Offset; + while (!atEnd() && isASCIIDigit(peek())) + ++Offset; + return token(MITokenKind::GlobalValue, Begin, Offset, slice(NumberBegin, Offset)); + } + + [[nodiscard]] std::optional lexMCSymbol() { + constexpr std::string_view Prefix = "'"); + } + ++Offset; + return stringToken(MITokenKind::MCSymbol, Begin, Offset, Name); + } + + auto Quoted = scanQuotedString(Offset); + if (!Quoted) + return token(MITokenKind::Error, Begin, Offset); + Offset = Quoted->End; + if (peek() != '>') { + return fail(Begin, Offset, "expected the ''"); + } + ++Offset; + return ownedStringToken(MITokenKind::MCSymbol, Begin, Offset, std::move(Quoted->Value)); + } + + [[nodiscard]] std::optional lexFloatHexBits() { + if (!startsWith("f0x") && !startsWith("f0X")) + return std::nullopt; + std::size_t End = Offset + 3; + while (End < text().size() && isASCIIHexDigit(text()[End])) + ++End; + if (End == Offset + 3) + return std::nullopt; + const std::size_t Begin = Offset; + Offset = End; + return token(MITokenKind::FloatingPointLiteral, Begin, Offset); + } + + [[nodiscard]] std::optional lexHexadecimalLiteral() { + if (peek() != '0' || (peek(1) != 'x' && peek(1) != 'X')) + return std::nullopt; + + std::size_t End = Offset + 2; + bool IsFloatingPoint = false; + if (End < text().size() && isHexFloatingPointPrefix(text()[End])) { + IsFloatingPoint = true; + ++End; + } + const std::size_t DigitsBegin = End; + while (End < text().size() && isASCIIHexDigit(text()[End])) + ++End; + if (End == DigitsBegin) + return std::nullopt; + + const std::size_t Begin = Offset; + Offset = End; + return token(IsFloatingPoint ? MITokenKind::FloatingPointLiteral : MITokenKind::HexLiteral, + Begin, Offset); + } + + [[nodiscard]] std::optional lexNumericalLiteral() { + if (!isASCIIDigit(peek()) && (peek() != '-' || !isASCIIDigit(peek(1)))) { + return std::nullopt; + } + + const std::size_t Begin = Offset; + if (peek() == '-') + ++Offset; + while (!atEnd() && isASCIIDigit(peek())) + ++Offset; + + if (peek() != '.') { + return token(MITokenKind::IntegerLiteral, Begin, Offset, slice(Begin, Offset)); + } + + ++Offset; + while (!atEnd() && isASCIIDigit(peek())) + ++Offset; + if ((peek() == 'e' || peek() == 'E') && + (isASCIIDigit(peek(1)) || ((peek(1) == '-' || peek(1) == '+') && isASCIIDigit(peek(2))))) { + ++Offset; + if (peek() == '-' || peek() == '+') + ++Offset; + while (!atEnd() && isASCIIDigit(peek())) + ++Offset; + } + return token(MITokenKind::FloatingPointLiteral, Begin, Offset); + } + + [[nodiscard]] std::optional lexExclaim() { + if (peek() != '!') + return std::nullopt; + + const std::size_t Begin = Offset++; + if (isASCIIDigit(peek()) || !isIdentifierCharacter(peek())) + return token(MITokenKind::Exclaim, Begin, Offset); + + while (!atEnd() && isIdentifierCharacter(peek())) + ++Offset; + const std::string_view Spelling = slice(Begin, Offset); + if (!isMIMetadataKeyword(Spelling)) { + return fail(Begin, Offset, "use of unknown metadata keyword '" + std::string(Spelling) + "'"); + } + return stringToken(MITokenKind::MetadataKeyword, Begin, Offset, Spelling); + } + + [[nodiscard]] std::optional lexSymbol() { + const std::size_t Begin = Offset; + if (startsWith("::")) { + Offset += 2; + return token(MITokenKind::DoubleColon, Begin, Offset); + } + + MITokenKind Kind = MITokenKind::Error; + switch (peek()) { + case ',': + Kind = MITokenKind::Comma; + break; + case '=': + Kind = MITokenKind::Equal; + break; + case ':': + Kind = MITokenKind::Colon; + break; + case '.': + Kind = MITokenKind::Dot; + break; + case '(': + Kind = MITokenKind::LeftParen; + break; + case ')': + Kind = MITokenKind::RightParen; + break; + case '{': + Kind = MITokenKind::LeftBrace; + break; + case '}': + Kind = MITokenKind::RightBrace; + break; + case '+': + Kind = MITokenKind::Plus; + break; + case '-': + Kind = MITokenKind::Minus; + break; + case '<': + Kind = MITokenKind::Less; + break; + case '>': + Kind = MITokenKind::Greater; + break; + default: + return std::nullopt; + } + ++Offset; + return token(Kind, Begin, Offset); + } + + [[nodiscard]] std::optional lexNewline() { + if (!isNewlineCharacter(peek())) + return std::nullopt; + const std::size_t Begin = Offset++; + if (text()[Begin] == '\r' && peek() == '\n') + ++Offset; + return token(MITokenKind::Newline, Begin, Offset); + } + + [[nodiscard]] std::optional lexQuotedIRValue() { + if (peek() != '`') + return std::nullopt; + + const std::size_t Begin = Offset++; + const std::size_t ValueBegin = Offset; + while (!atEnd() && peek() != '`' && !isNewlineCharacter(peek())) + ++Offset; + if (atEnd() || isNewlineCharacter(peek())) { + return fail(Begin, Offset, "end of machine instruction reached before the closing '`'"); + } + const std::size_t ValueEnd = Offset++; + return stringToken(MITokenKind::QuotedIRValue, Begin, Offset, slice(ValueBegin, ValueEnd)); + } + + [[nodiscard]] MIToken lexToken() { + if (auto Result = lexMachineBasicBlock()) + return std::move(*Result); + if (auto Result = lexFloatHexBits()) + return std::move(*Result); + + if (isASCIIAlpha(peek()) || peek() == '_') { + const std::size_t Begin = Offset; + while (!atEnd() && isIdentifierCharacter(peek())) + ++Offset; + const std::string_view Value = slice(Begin, Offset); + if (Value == "_") + return token(MITokenKind::Underscore, Begin, Offset); + return stringToken(isMIKeyword(Value) ? MITokenKind::Keyword : MITokenKind::Identifier, Begin, + Offset, Value); + } + + if (auto Result = lexIndex("%jump-table.", MITokenKind::JumpTableIndex, false)) + return std::move(*Result); + if (auto Result = lexIndex("%stack.", MITokenKind::StackObject, true)) + return std::move(*Result); + if (auto Result = lexIndex("%fixed-stack.", MITokenKind::FixedStackObject, false)) + return std::move(*Result); + if (auto Result = lexIndex("%const.", MITokenKind::ConstantPoolItem, false)) + return std::move(*Result); + if (startsWith("%subreg.")) + return lexName(MITokenKind::SubRegisterIndex, 8); + if (auto Result = lexIRReference("%ir-block.", MITokenKind::IRBlock, MITokenKind::NamedIRBlock)) + return std::move(*Result); + if (auto Result = lexIRReference("%ir.", MITokenKind::IRValue, MITokenKind::NamedIRValue)) + return std::move(*Result); + if (auto Result = lexRegister()) + return std::move(*Result); + if (auto Result = lexGlobalValue()) + return std::move(*Result); + if (peek() == '&') + return lexName(MITokenKind::ExternalSymbol, 1); + if (auto Result = lexMCSymbol()) + return std::move(*Result); + if (auto Result = lexHexadecimalLiteral()) + return std::move(*Result); + if (auto Result = lexNumericalLiteral()) + return std::move(*Result); + if (auto Result = lexExclaim()) + return std::move(*Result); + if (auto Result = lexSymbol()) + return std::move(*Result); + if (auto Result = lexNewline()) + return std::move(*Result); + if (auto Result = lexQuotedIRValue()) + return std::move(*Result); + if (peek() == '"') + return lexName(MITokenKind::StringConstant, 0); + + const std::size_t Begin = Offset++; + return fail(Begin, Offset, + unexpectedCharacterMessage(static_cast(text()[Begin]))); + } + + TextSource Source; + MILexerOptions Options; + std::size_t Offset = 0; + std::size_t TokenCount = 0; + bool Failed = false; + std::vector Diagnostics; +}; + +MILexer::MILexer(std::string SourceName, std::string SourceText, MILexerOptions Options) + : Implementation( + std::make_unique(std::move(SourceName), std::move(SourceText), Options)) {} + +MILexer::MILexer(MILexer &&) noexcept = default; +MILexer &MILexer::operator=(MILexer &&) noexcept = default; +MILexer::~MILexer() = default; + +MIToken MILexer::next() { + assert(Implementation != nullptr); + return Implementation->next(); +} + +const TextSource &MILexer::source() const noexcept { + assert(Implementation != nullptr); + return Implementation->source(); +} + +std::span MILexer::diagnostics() const noexcept { + assert(Implementation != nullptr); + return Implementation->diagnostics(); +} + +bool MILexer::failed() const noexcept { + assert(Implementation != nullptr); + return Implementation->failed(); +} + +std::size_t MILexer::tokenCount() const noexcept { + assert(Implementation != nullptr); + return Implementation->tokenCount(); +} + +} // namespace rcc::mir diff --git a/backend/lib/CodeGen/MIRParser/MILexer.h b/backend/lib/CodeGen/MIRParser/MILexer.h new file mode 100644 index 0000000..e3d50e7 --- /dev/null +++ b/backend/lib/CodeGen/MIRParser/MILexer.h @@ -0,0 +1,181 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_LIB_CODEGEN_MIRPARSER_MILEXER_H +#define RCC_LIB_CODEGEN_MIRPARSER_MILEXER_H + +#include "rcc/Support/TextDiagnostic.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace rcc::mir { + +enum class MITokenKind : std::uint8_t { + EndOfFile, + Error, + Newline, + + Comma, + Equal, + Underscore, + Colon, + DoubleColon, + Dot, + Exclaim, + LeftParen, + RightParen, + LeftBrace, + RightBrace, + Plus, + Minus, + Less, + Greater, + + Keyword, + MetadataKeyword, + Identifier, + NamedRegister, + NamedVirtualRegister, + MachineBasicBlockLabel, + MachineBasicBlock, + StackObject, + FixedStackObject, + NamedGlobalValue, + GlobalValue, + ExternalSymbol, + MCSymbol, + IntegerLiteral, + FloatingPointLiteral, + HexLiteral, + VirtualRegister, + ConstantPoolItem, + JumpTableIndex, + NamedIRBlock, + IRBlock, + NamedIRValue, + IRValue, + QuotedIRValue, + SubRegisterIndex, + StringConstant, +}; + +[[nodiscard]] std::string_view getMITokenKindName(MITokenKind Kind) noexcept; +[[nodiscard]] bool isMIKeyword(std::string_view Spelling) noexcept; +[[nodiscard]] bool isMIMetadataKeyword(std::string_view Spelling) noexcept; + +class MIToken final { +public: + [[nodiscard]] MITokenKind kind() const noexcept { + return Kind; + } + + [[nodiscard]] const TextRange &range() const noexcept { + return Range; + } + + [[nodiscard]] std::string_view spelling() const noexcept { + return Spelling; + } + + [[nodiscard]] bool hasStringValue() const noexcept { + return !std::holds_alternative(StringValue); + } + + [[nodiscard]] std::string_view stringValue() const noexcept; + + [[nodiscard]] bool hasIntegerValue() const noexcept { + return !IntegerValue.empty(); + } + + [[nodiscard]] std::string_view integerValue() const noexcept { + return IntegerValue; + } + + [[nodiscard]] bool is(MITokenKind Expected) const noexcept { + return Kind == Expected; + } + + [[nodiscard]] bool isKeyword(std::string_view Expected) const noexcept { + return Kind == MITokenKind::Keyword && stringValue() == Expected; + } + + [[nodiscard]] bool isMetadataKeyword(std::string_view Expected) const noexcept { + return Kind == MITokenKind::MetadataKeyword && stringValue() == Expected; + } + + [[nodiscard]] bool isNewlineOrEnd() const noexcept { + return Kind == MITokenKind::Newline || Kind == MITokenKind::EndOfFile; + } + + [[nodiscard]] bool isErrorOrEnd() const noexcept { + return Kind == MITokenKind::Error || Kind == MITokenKind::EndOfFile; + } + + [[nodiscard]] bool isRegister() const noexcept; + [[nodiscard]] bool isRegisterFlag() const noexcept; + [[nodiscard]] bool isMemoryOperandFlag() const noexcept; + +private: + using StringValueStorage = std::variant; + + MIToken(MITokenKind Kind, TextRange Range, std::string_view Spelling, + StringValueStorage StringValue = {}, std::string_view IntegerValue = {}) + : Kind(Kind), Range(Range), Spelling(Spelling), StringValue(std::move(StringValue)), + IntegerValue(IntegerValue) {} + + MITokenKind Kind = MITokenKind::Error; + TextRange Range; + std::string_view Spelling; + StringValueStorage StringValue; + std::string_view IntegerValue; + + friend class MILexer; +}; + +struct MILexerOptions final { + std::size_t MaximumTokenLength = std::size_t{1} << 24U; + std::size_t MaximumTokens = std::size_t{1} << 24U; +}; + +class MILexer final { +public: + MILexer(std::string SourceName, std::string SourceText, MILexerOptions Options = {}); + MILexer(const MILexer &) = delete; + MILexer &operator=(const MILexer &) = delete; + MILexer(MILexer &&) noexcept; + MILexer &operator=(MILexer &&) noexcept; + ~MILexer(); + + [[nodiscard]] MIToken next(); + + [[nodiscard]] const TextSource &source() const noexcept; + [[nodiscard]] std::span diagnostics() const noexcept; + [[nodiscard]] bool failed() const noexcept; + [[nodiscard]] std::size_t tokenCount() const noexcept; + +private: + [[nodiscard]] static MIToken makeToken(MITokenKind Kind, TextRange Range, + std::string_view Spelling, + std::string_view IntegerValue = {}); + [[nodiscard]] static MIToken makeStringToken(MITokenKind Kind, TextRange Range, + std::string_view Spelling, + std::string_view StringValue, + std::string_view IntegerValue = {}); + [[nodiscard]] static MIToken makeOwnedStringToken(MITokenKind Kind, TextRange Range, + std::string_view Spelling, + std::string StringValue, + std::string_view IntegerValue = {}); + + class Impl; + std::unique_ptr Implementation; +}; + +} // namespace rcc::mir + +#endif // RCC_LIB_CODEGEN_MIRPARSER_MILEXER_H diff --git a/backend/lib/CodeGen/MIRParser/MIParser.cpp b/backend/lib/CodeGen/MIRParser/MIParser.cpp new file mode 100644 index 0000000..e38b9f1 --- /dev/null +++ b/backend/lib/CodeGen/MIRParser/MIParser.cpp @@ -0,0 +1,2967 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "rcc/CodeGen/MIRParser/MIParser.h" + +#include "MILexer.h" +#include "rcc/CodeGen/GenericOpcodes.h" +#include "rcc/CodeGen/InlineAsm.h" +#include "rcc/CodeGen/InstructionDescriptor.h" +#include "rcc/CodeGen/MIRYamlMapping.h" +#include "rcc/CodeGen/MachineBasicBlock.h" +#include "rcc/CodeGen/MachineCFGUpdater.h" +#include "rcc/CodeGen/MachineFunction.h" +#include "rcc/CodeGen/MachineInstr.h" +#include "rcc/CodeGen/MachineOperand.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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace rcc::mir { +namespace { + +[[nodiscard]] bool parseUnsignedDecimal(std::string_view Spelling, std::uint64_t &Value) { + if (Spelling.empty() || Spelling.front() == '-') + return false; + const char *Begin = Spelling.data(); + const char *End = Begin + Spelling.size(); + const auto Result = std::from_chars(Begin, End, Value, 10); + return Result.ec == std::errc{} && Result.ptr == End; +} + +[[nodiscard]] bool parseSignedDecimal(std::string_view Spelling, std::int64_t &Value) { + if (Spelling.empty()) + return false; + const char *Begin = Spelling.data(); + const char *End = Begin + Spelling.size(); + const auto Result = std::from_chars(Begin, End, Value, 10); + return Result.ec == std::errc{} && Result.ptr == End; +} + +[[nodiscard]] bool parseHexadecimal(std::string_view Spelling, std::uint64_t &Value) { + if (Spelling.size() < 3 || Spelling[0] != '0' || (Spelling[1] != 'x' && Spelling[1] != 'X')) + return false; + const char *Begin = Spelling.data() + 2; + const char *End = Spelling.data() + Spelling.size(); + const auto Result = std::from_chars(Begin, End, Value, 16); + return Result.ec == std::errc{} && Result.ptr == End; +} + +[[nodiscard]] bool parseUnsignedDigits(std::string_view Spelling, std::uint64_t &Value) { + if (Spelling.empty()) + return false; + const char *Begin = Spelling.data(); + const char *End = Begin + Spelling.size(); + const auto Result = std::from_chars(Begin, End, Value, 10); + return Result.ec == std::errc{} && Result.ptr == End; +} + +[[nodiscard]] std::optional floatSemanticsForKeyword(std::string_view Keyword) { + if (Keyword == "half") + return FloatSemantics::IEEEHalf; + if (Keyword == "bfloat") + return FloatSemantics::BFloat; + if (Keyword == "float") + return FloatSemantics::IEEESingle; + if (Keyword == "double") + return FloatSemantics::IEEEDouble; + if (Keyword == "x86_fp80") + return FloatSemantics::X87DoubleExtended; + if (Keyword == "fp128") + return FloatSemantics::IEEEQuad; + if (Keyword == "ppc_fp128") + return FloatSemantics::PPCDoubleDouble; + return std::nullopt; +} + +[[nodiscard]] bool isFloatingSemanticsKeyword(const MIToken &Token) { + return Token.kind() == MITokenKind::Keyword && + floatSemanticsForKeyword(Token.stringValue()).has_value(); +} + +[[nodiscard]] bool isCFIOperationToken(const MIToken &Token) { + return Token.isKeyword("same_value") || Token.isKeyword("remember_state") || + Token.isKeyword("restore_state") || Token.isKeyword("offset") || + Token.isKeyword("llvm_def_aspace_cfa") || Token.isKeyword("def_cfa_register") || + Token.isKeyword("def_cfa_offset") || Token.isKeyword("def_cfa") || + Token.isKeyword("rel_offset") || Token.isKeyword("adjust_cfa_offset") || + Token.isKeyword("escape") || Token.isKeyword("restore") || Token.isKeyword("undefined") || + Token.isKeyword("register") || Token.isKeyword("window_save") || + Token.isKeyword("negate_ra_sign_state") || + Token.isKeyword("negate_ra_sign_state_with_pc") || Token.isKeyword("llvm_register_pair") || + Token.isKeyword("llvm_vector_registers") || Token.isKeyword("llvm_vector_offset") || + Token.isKeyword("llvm_vector_register_mask"); +} + +[[nodiscard]] Expected parseIntegerImmediateValue(std::uint32_t BitWidth, + std::string_view Spelling) { + if (BitWidth == 0 || BitWidth > IntegerImmediate::MaxBitWidth) + return makeUnexpected(ErrorCode::ParseError, "typed integer width is out of range"); + + const bool Negative = Spelling.starts_with('-'); + if (Negative) + Spelling.remove_prefix(1); + if (Spelling.empty()) + return makeUnexpected(ErrorCode::ParseError, "typed integer has no digits"); + + std::vector Words((static_cast(BitWidth) + 63U) / 64U, 0); + for (char Character : Spelling) { + if (Character < '0' || Character > '9') + return makeUnexpected(ErrorCode::ParseError, "typed integer contains a non-decimal digit"); + std::uint64_t Carry = static_cast(Character - '0'); + for (std::uint64_t &Word : Words) { + const unsigned __int128 Product = static_cast(Word) * 10U + Carry; + Word = static_cast(Product); + Carry = static_cast(Product >> 64U); + } + } + + // LLVM parses the arbitrary-precision literal first and then truncates it to + // the declared integer width. Keep exactly that modulo-2^N bit pattern. + if (BitWidth % 64U != 0) + Words.back() &= (std::uint64_t{1} << (BitWidth % 64U)) - 1U; + if (Negative) { + for (std::uint64_t &Word : Words) + Word = ~Word; + std::uint64_t Carry = 1; + for (std::uint64_t &Word : Words) { + const std::uint64_t Previous = Word; + Word += Carry; + Carry = Carry != 0 && Word < Previous ? 1 : 0; + } + if (BitWidth % 64U != 0) + Words.back() &= (std::uint64_t{1} << (BitWidth % 64U)) - 1U; + } + return IntegerImmediate::create(BitWidth, Words); +} + +[[nodiscard]] std::optional minimumTypeSizeInBits(LLT Type) noexcept { + if (!Type.isValid() || Type.isToken()) + return std::nullopt; + const std::uint64_t ScalarSize = Type.scalarBitWidth(); + if (!Type.isVector()) + return ScalarSize; + const std::uint64_t ElementCount = Type.minimumElementCount(); + if (ScalarSize != 0 && ElementCount > std::numeric_limits::max() / ScalarSize) + return std::nullopt; + return ScalarSize * ElementCount; +} + +[[nodiscard]] std::optional atomicOrdering(std::string_view Name) noexcept { + if (Name == "unordered") + return AtomicOrdering::Unordered; + if (Name == "monotonic") + return AtomicOrdering::Monotonic; + if (Name == "acquire") + return AtomicOrdering::Acquire; + if (Name == "release") + return AtomicOrdering::Release; + if (Name == "acq_rel") + return AtomicOrdering::AcquireRelease; + if (Name == "seq_cst") + return AtomicOrdering::SequentiallyConsistent; + return std::nullopt; +} + +[[nodiscard]] bool isInstructionFlag(const MIToken &Token) { + return Token.isKeyword("frame-setup") || Token.isKeyword("frame-destroy") || + Token.isKeyword("nnan") || Token.isKeyword("ninf") || Token.isKeyword("nsz") || + Token.isKeyword("arcp") || Token.isKeyword("contract") || Token.isKeyword("afn") || + Token.isKeyword("reassoc") || Token.isKeyword("nuw") || Token.isKeyword("nsw") || + Token.isKeyword("nusw") || Token.isKeyword("exact") || Token.isKeyword("nofpexcept") || + Token.isKeyword("unpredictable") || Token.isKeyword("noconvergent") || + Token.isKeyword("nneg") || Token.isKeyword("disjoint") || Token.isKeyword("samesign") || + Token.isKeyword("inbounds"); +} + +[[nodiscard]] std::optional instructionFlag(std::string_view Name) { + if (Name == "frame-setup") + return MachineInstrFlag::FrameSetup; + if (Name == "frame-destroy") + return MachineInstrFlag::FrameDestroy; + if (Name == "nnan") + return MachineInstrFlag::FastMathNoNaNs; + if (Name == "ninf") + return MachineInstrFlag::FastMathNoInfs; + if (Name == "nsz") + return MachineInstrFlag::FastMathNoSignedZeros; + if (Name == "arcp") + return MachineInstrFlag::FastMathAllowReciprocal; + if (Name == "contract") + return MachineInstrFlag::FastMathAllowContract; + if (Name == "afn") + return MachineInstrFlag::FastMathApproxFunc; + if (Name == "reassoc") + return MachineInstrFlag::FastMathAllowReassoc; + if (Name == "nuw") + return MachineInstrFlag::NoUnsignedWrap; + if (Name == "nsw") + return MachineInstrFlag::NoSignedWrap; + if (Name == "nusw") + return MachineInstrFlag::NoUSWrap; + if (Name == "exact") + return MachineInstrFlag::Exact; + if (Name == "nofpexcept") + return MachineInstrFlag::NoFPExcept; + if (Name == "unpredictable") + return MachineInstrFlag::Unpredictable; + if (Name == "noconvergent") + return MachineInstrFlag::NoConvergent; + if (Name == "nneg") + return MachineInstrFlag::NonNeg; + if (Name == "disjoint") + return MachineInstrFlag::Disjoint; + if (Name == "samesign") + return MachineInstrFlag::SameSign; + if (Name == "inbounds") + return MachineInstrFlag::InBounds; + return std::nullopt; +} + +[[nodiscard]] std::optional integerPredicate(std::string_view Name) { + if (Name == "eq") + return IntegerPredicate::Equal; + if (Name == "ne") + return IntegerPredicate::NotEqual; + if (Name == "sgt") + return IntegerPredicate::SignedGreaterThan; + if (Name == "sge") + return IntegerPredicate::SignedGreaterEqual; + if (Name == "slt") + return IntegerPredicate::SignedLessThan; + if (Name == "sle") + return IntegerPredicate::SignedLessEqual; + if (Name == "ugt") + return IntegerPredicate::UnsignedGreaterThan; + if (Name == "uge") + return IntegerPredicate::UnsignedGreaterEqual; + if (Name == "ult") + return IntegerPredicate::UnsignedLessThan; + if (Name == "ule") + return IntegerPredicate::UnsignedLessEqual; + return std::nullopt; +} + +[[nodiscard]] std::optional floatingPredicate(std::string_view Name) { + if (Name == "false") + return FloatingPredicate::AlwaysFalse; + if (Name == "oeq") + return FloatingPredicate::OrderedEqual; + if (Name == "ogt") + return FloatingPredicate::OrderedGreaterThan; + if (Name == "oge") + return FloatingPredicate::OrderedGreaterEqual; + if (Name == "olt") + return FloatingPredicate::OrderedLessThan; + if (Name == "ole") + return FloatingPredicate::OrderedLessEqual; + if (Name == "one") + return FloatingPredicate::OrderedNotEqual; + if (Name == "ord") + return FloatingPredicate::Ordered; + if (Name == "uno") + return FloatingPredicate::Unordered; + if (Name == "ueq") + return FloatingPredicate::UnorderedEqual; + if (Name == "ugt") + return FloatingPredicate::UnorderedGreaterThan; + if (Name == "uge") + return FloatingPredicate::UnorderedGreaterEqual; + if (Name == "ult") + return FloatingPredicate::UnorderedLessThan; + if (Name == "ule") + return FloatingPredicate::UnorderedLessEqual; + if (Name == "une") + return FloatingPredicate::UnorderedNotEqual; + if (Name == "true") + return FloatingPredicate::AlwaysTrue; + return std::nullopt; +} + +} // namespace + +class MIParser::Impl final { +public: + Impl(MachineFunction &Function, const TargetDescription &Target, std::string SourceName, + std::string SourceText, MIParserContext Context, MIParserOptions Options) + : Function(Function), Target(Target), + Lexer(std::move(SourceName), std::move(SourceText), + MILexerOptions{.MaximumTokenLength = Options.MaximumTokenLength, + .MaximumTokens = Options.MaximumTokens}), + Context(std::move(Context)), Options(Options) {} + + [[nodiscard]] Expected parse(); + + [[nodiscard]] const TextSource &source() const noexcept { + return Lexer.source(); + } + + [[nodiscard]] std::span diagnostics() const noexcept { + return Diagnostics; + } + + [[nodiscard]] bool failed() const noexcept { + return !Diagnostics.empty(); + } + +private: + struct ParsedOperand final { + MachineOperand Operand; + std::optional TiedDef; + TextRange Range; + }; + + [[nodiscard]] const MIToken &token() const noexcept { + return Tokens[Position]; + } + + [[nodiscard]] bool at(MITokenKind Kind) const noexcept { + return token().is(Kind); + } + + [[nodiscard]] bool nextIs(MITokenKind Kind) const noexcept { + return Position + 1U < Tokens.size() && Tokens[Position + 1U].is(Kind); + } + + [[nodiscard]] bool atKeyword(std::string_view Keyword) const noexcept { + return token().isKeyword(Keyword); + } + + void advance() noexcept { + if (!at(MITokenKind::EndOfFile)) + ++Position; + } + + [[nodiscard]] bool consume(MITokenKind Kind) noexcept { + if (!at(Kind)) + return false; + advance(); + return true; + } + + [[nodiscard]] bool expect(MITokenKind Kind, std::string_view Description); + [[nodiscard]] bool fail(const MIToken &At, std::string Message); + [[nodiscard]] bool fail(std::string Message) { + return fail(token(), std::move(Message)); + } + [[nodiscard]] Expected failureResult() const; + [[nodiscard]] bool importError(const MIToken &At, const Error &Failure); + [[nodiscard]] bool tokenize(); + + [[nodiscard]] bool parseUnsigned(const MIToken &Token, std::uint64_t &Value); + [[nodiscard]] bool parseUnsigned32(const MIToken &Token, std::uint32_t &Value); + [[nodiscard]] bool parseSigned64(const MIToken &Token, std::int64_t &Value); + [[nodiscard]] bool parseOptionalOffset(std::int64_t &Offset); + [[nodiscard]] bool parseAlignment(Alignment &Result); + [[nodiscard]] bool parseLowLevelType(LLT &Result); + [[nodiscard]] bool parseScalarLowLevelType(const MIToken &Token, LLT &Result); + + [[nodiscard]] bool parseFirstPass(); + [[nodiscard]] bool parseSecondPass(); + [[nodiscard]] bool parseBlockDefinition(bool Create, std::uint32_t &ID); + [[nodiscard]] bool parseBlockAttributes(MachineBasicBlock *Block); + [[nodiscard]] bool parseBlockLiveIns(MachineBasicBlock &Block); + [[nodiscard]] bool parseBlockSuccessors(MachineBasicBlock &Block); + [[nodiscard]] bool parseInstruction(MachineBasicBlock &Block, bool InBundle, + MachineInstr *PreviousInstruction, MachineInstr *&Result); + [[nodiscard]] bool inferSuccessors(); + + [[nodiscard]] bool parseRegister(Register &Result); + [[nodiscard]] bool parseRegisterOperand(bool IsDef, ParsedOperand &Result); + [[nodiscard]] bool parseTargetFlags(std::uint32_t &Flags); + [[nodiscard]] bool parseOperand(bool IsDef, ParsedOperand &Result); + [[nodiscard]] bool parseInlineAsmExtraInfoOperand(ParsedOperand &Result); + [[nodiscard]] bool parseInlineAsmOperandFlag(ParsedOperand &Result); + [[nodiscard]] bool parseTypedIntegerOperand(ParsedOperand &Result); + [[nodiscard]] bool parseFloatingImmediateOperand(ParsedOperand &Result); + [[nodiscard]] bool parseMetadataReference(MachineMetadataReference &Result); + [[nodiscard]] bool parseMetadataOperand(ParsedOperand &Result); + [[nodiscard]] bool + parseInlineDILocation(std::vector &InlineChain, + std::optional &TerminalInlinedAt); + [[nodiscard]] bool parseCFIRegister(DwarfRegisterNumber &Result); + [[nodiscard]] bool parseCFISigned(std::int64_t &Result); + [[nodiscard]] bool parseCFIUnsigned(std::uint32_t &Result); + [[nodiscard]] bool parseCFIOperand(ParsedOperand &Result); + [[nodiscard]] bool parseMemoryBase(MemoryBase &Result, std::int64_t &Offset); + [[nodiscard]] bool parseMemoryOperand(const MachineMemOperand *&Result, Opcode InstructionOpcode); + [[nodiscard]] bool parsePredicateOperand(ParsedOperand &Result, bool IsFloating); + [[nodiscard]] bool parseShuffleMaskOperand(ParsedOperand &Result); + [[nodiscard]] bool parseDebugInstrRefOperand(ParsedOperand &Result); + [[nodiscard]] bool parseLaneMaskOperand(ParsedOperand &Result); + [[nodiscard]] bool parseIntrinsicOperand(ParsedOperand &Result); + [[nodiscard]] bool parseTargetIndexOperand(ParsedOperand &Result); + [[nodiscard]] bool parseLiveOutMaskOperand(ParsedOperand &Result); + [[nodiscard]] bool parseCustomMaskOperand(ParsedOperand &Result); + [[nodiscard]] bool parseBlockAddressOperand(ParsedOperand &Result); + + [[nodiscard]] Register getOrCreateVirtualRegister(const MIToken &Token); + [[nodiscard]] MachineBasicBlock *blockFor(const MIToken &Token); + [[nodiscard]] std::optional frameIndexFor(const MIToken &Token, bool Fixed); + [[nodiscard]] std::optional constantPoolIndexFor(const MIToken &Token); + [[nodiscard]] std::optional jumpTableIndexFor(const MIToken &Token); + [[nodiscard]] std::string symbolName(const MIToken &Token) const; + + MachineFunction &Function; + const TargetDescription &Target; + MILexer Lexer; + MIParserContext Context; + MIParserOptions Options; + std::vector Tokens; + std::size_t Position = 0; + std::vector Diagnostics; + std::unordered_map Blocks; + std::unordered_map NumericVirtualRegisters; + std::unordered_map NamedVirtualRegisters; + std::unordered_set ExplicitSuccessorBlocks; + std::size_t ParsedInstructionCount = 0; + bool ParseAttempted = false; +}; + +bool MIParser::Impl::expect(MITokenKind Kind, std::string_view Description) { + if (consume(Kind)) + return false; + return fail("expected " + std::string(Description) + ", found " + + std::string(getMITokenKindName(token().kind()))); +} + +bool MIParser::Impl::fail(const MIToken &At, std::string Message) { + if (Diagnostics.empty()) { + Diagnostics.push_back(TextDiagnostic{ + .Severity = DiagnosticSeverity::Error, + .Category = "mir-parser", + .Message = std::move(Message), + .SourceName = std::string(Lexer.source().name()), + .Range = At.range(), + }); + } + return true; +} + +Expected MIParser::Impl::failureResult() const { + const std::string Message = + Diagnostics.empty() ? "machine instruction parsing failed" : Diagnostics.front().Message; + return makeUnexpected(ErrorCode::ParseError, Message); +} + +bool MIParser::Impl::importError(const MIToken &At, const Error &Failure) { + return fail(At, std::string(Failure.message())); +} + +bool MIParser::Impl::tokenize() { + while (true) { + MIToken Token = Lexer.next(); + const MITokenKind Kind = Token.kind(); + Tokens.push_back(std::move(Token)); + if (Kind == MITokenKind::Error) { + const auto LexerDiagnostics = Lexer.diagnostics(); + Diagnostics.assign(LexerDiagnostics.begin(), LexerDiagnostics.end()); + return true; + } + if (Kind == MITokenKind::EndOfFile) + return false; + } +} + +bool MIParser::Impl::parseUnsigned(const MIToken &Token, std::uint64_t &Value) { + if (Token.kind() == MITokenKind::HexLiteral) + return !parseHexadecimal(Token.spelling(), Value); + if (Token.hasIntegerValue()) + return !parseUnsignedDecimal(Token.integerValue(), Value); + return true; +} + +bool MIParser::Impl::parseUnsigned32(const MIToken &Token, std::uint32_t &Value) { + std::uint64_t Wide = 0; + if (parseUnsigned(Token, Wide) || Wide > std::numeric_limits::max()) + return true; + Value = static_cast(Wide); + return false; +} + +bool MIParser::Impl::parseSigned64(const MIToken &Token, std::int64_t &Value) { + if (Token.kind() != MITokenKind::IntegerLiteral) + return true; + if (parseSignedDecimal(Token.integerValue(), Value)) + return false; + std::uint64_t Unsigned = 0; + if (!parseUnsignedDecimal(Token.integerValue(), Unsigned)) + return true; + Value = std::bit_cast(Unsigned); + return false; +} + +bool MIParser::Impl::parseOptionalOffset(std::int64_t &Offset) { + Offset = 0; + if (!at(MITokenKind::Plus) && !at(MITokenKind::Minus)) + return false; + const bool Negative = at(MITokenKind::Minus); + advance(); + std::uint64_t Magnitude = 0; + if (!at(MITokenKind::IntegerLiteral) || parseUnsigned(token(), Magnitude) || + Magnitude > (std::uint64_t{1} << 63U)) { + return fail("expected a 64-bit integer offset"); + } + if (Negative) { + if (Magnitude == (std::uint64_t{1} << 63U)) + Offset = std::numeric_limits::min(); + else + Offset = -static_cast(Magnitude); + } else { + if (Magnitude > static_cast(std::numeric_limits::max())) + return fail("positive offset exceeds signed 64-bit range"); + Offset = static_cast(Magnitude); + } + advance(); + return false; +} + +bool MIParser::Impl::parseAlignment(Alignment &Result) { + advance(); + std::uint64_t Bytes = 0; + if (!at(MITokenKind::IntegerLiteral) || parseUnsigned(token(), Bytes)) + return fail("expected an unsigned alignment"); + auto Parsed = Alignment::create(Bytes); + if (!Parsed) + return importError(token(), Parsed.error()); + Result = *Parsed; + advance(); + return false; +} + +bool MIParser::Impl::parseScalarLowLevelType(const MIToken &TypeToken, LLT &Result) { + if (TypeToken.kind() != MITokenKind::Identifier) + return fail(TypeToken, "expected a scalar low-level type"); + std::string_view Spelling = TypeToken.stringValue(); + enum class ScalarKind : std::uint8_t { + Any, + Integer, + Float, + BFloat, + Pointer, + }; + ScalarKind Kind = ScalarKind::Any; + std::string_view Digits; + if (Spelling.starts_with("bf")) { + Kind = ScalarKind::BFloat; + Digits = Spelling.substr(2); + } else if (Spelling.starts_with('s')) { + Kind = ScalarKind::Any; + Digits = Spelling.substr(1); + } else if (Spelling.starts_with('i')) { + Kind = ScalarKind::Integer; + Digits = Spelling.substr(1); + } else if (Spelling.starts_with('f')) { + Kind = ScalarKind::Float; + Digits = Spelling.substr(1); + } else if (Spelling.starts_with('p')) { + Kind = ScalarKind::Pointer; + Digits = Spelling.substr(1); + } else { + return fail(TypeToken, "unknown scalar low-level type '" + std::string(Spelling) + "'"); + } + + std::uint64_t Value = 0; + if (!parseUnsignedDigits(Digits, Value)) + return fail(TypeToken, "expected integers after 's'/'i'/'f'/'bf'/'p' type identifier"); + + Expected Created = makeUnexpected(ErrorCode::ParseError, "invalid low-level type"); + switch (Kind) { + case ScalarKind::Any: + if (Value == 0) { + Result = LLT::token(); + return false; + } + Created = LLT::makeAnyScalar(Value); + break; + case ScalarKind::Integer: + Created = LLT::makeInteger(Value); + break; + case ScalarKind::Float: { + std::optional Semantics; + if (Value == 16) + Semantics = FloatSemantics::IEEEHalf; + else if (Value == 32) + Semantics = FloatSemantics::IEEESingle; + else if (Value == 64) + Semantics = FloatSemantics::IEEEDouble; + else if (Value == 128) + Semantics = FloatSemantics::IEEEQuad; + if (!Semantics) + return fail(TypeToken, "unsupported IEEE floating low-level type width"); + Created = LLT::makeFloat(*Semantics); + break; + } + case ScalarKind::BFloat: + if (Value != 16) + return fail(TypeToken, "bfloat low-level type must have width 16"); + Created = LLT::makeFloat(FloatSemantics::BFloat); + break; + case ScalarKind::Pointer: + if (!Context.PointerSizeInBits) + return fail(TypeToken, "pointer low-level type requires an explicit pointer size"); + if (auto PointerSize = Context.PointerSizeInBits(static_cast(Value))) + Created = LLT::makePointer(Value, *PointerSize); + else + return importError(TypeToken, PointerSize.error()); + break; + } + if (!Created) + return importError(TypeToken, Created.error()); + Result = *Created; + return false; +} + +bool MIParser::Impl::parseLowLevelType(LLT &Result) { + const MIToken Begin = token(); + const auto HasScalarPrefix = [](std::string_view Spelling) { + return Spelling.starts_with('s') || Spelling.starts_with('i') || Spelling.starts_with('f') || + Spelling.starts_with("bf") || Spelling.starts_with('p'); + }; + if (at(MITokenKind::Identifier)) { + if (!HasScalarPrefix(token().stringValue())) { + return fail(Begin, "expected tN, pA, , , , or " + " for GlobalISel type, where t = {'s', 'i', 'f', 'bf'}"); + } + if (parseScalarLowLevelType(token(), Result)) + return true; + advance(); + return false; + } + if (!consume(MITokenKind::Less)) { + return fail(Begin, "expected tN, pA, , , , or " + " for GlobalISel type, where t = {'s', 'i', 'f', 'bf'}"); + } + + bool Scalable = false; + if (at(MITokenKind::Identifier) && token().stringValue() == "vscale") { + Scalable = true; + advance(); + if (!at(MITokenKind::Identifier) || token().stringValue() != "x") { + return fail("expected , where t = {'s', 'i', 'f', 'bf', 'p'}"); + } + advance(); + } + + const auto VectorError = [this, &Begin, Scalable] { + if (Scalable) { + return fail(Begin, "expected for vector type, where t = " + "{'s', 'i', 'f', 'bf', 'p'}"); + } + return fail(Begin, "expected for vector type, where t = {'s', 'i', 'f', 'bf', 'p'}"); + }; + + std::uint64_t ElementCount = 0; + if (!at(MITokenKind::IntegerLiteral) || parseUnsigned(token(), ElementCount)) + return VectorError(); + advance(); + if (!at(MITokenKind::Identifier) || token().stringValue() != "x") + return VectorError(); + advance(); + + if (!at(MITokenKind::Identifier) || !HasScalarPrefix(token().stringValue())) + return VectorError(); + LLT ElementType; + if (parseScalarLowLevelType(token(), ElementType)) + return true; + if (ElementType.isToken()) + return fail("token cannot be a vector element type"); + advance(); + if (!consume(MITokenKind::Greater)) + return VectorError(); + + Expected Created = Scalable ? LLT::makeScalableVector(ElementCount, ElementType) + : LLT::makeFixedVector(ElementCount, ElementType); + if (!Created) + return importError(Begin, Created.error()); + Result = *Created; + return false; +} + +bool MIParser::Impl::parseBlockAttributes(MachineBasicBlock *Block) { + std::unordered_set Seen; + while (!at(MITokenKind::RightParen)) { + const MIToken AttributeToken = token(); + if (atKeyword("machine-block-address-taken") || atKeyword("landing-pad") || + atKeyword("inlineasm-br-indirect-target") || atKeyword("ehfunclet-entry") || + atKeyword("ehscope-entry")) { + const std::string Name(AttributeToken.stringValue()); + if (!Seen.insert(Name).second) + return fail("duplicate basic block attribute '" + Name + "'"); + if (Block != nullptr) { + if (Name == "machine-block-address-taken") + Block->attributes().set(MachineBasicBlockAttribute::MachineBlockAddressTaken); + else if (Name == "landing-pad") + Block->attributes().set(MachineBasicBlockAttribute::LandingPad); + else if (Name == "inlineasm-br-indirect-target") + Block->attributes().set(MachineBasicBlockAttribute::InlineAsmBrIndirectTarget); + else if (Name == "ehfunclet-entry") + Block->attributes().set(MachineBasicBlockAttribute::EHFuncletEntry); + else + Block->attributes().set(MachineBasicBlockAttribute::EHScopeEntry); + } + advance(); + } else if (atKeyword("ir-block-address-taken")) { + if (!Seen.insert("ir-block-address-taken").second) + return fail("duplicate basic block attribute 'ir-block-address-taken'"); + advance(); + if (!at(MITokenKind::IRBlock) && !at(MITokenKind::NamedIRBlock)) + return fail("expected an IR block after 'ir-block-address-taken'"); + if (Block != nullptr) { + Block->attributes().set(MachineBasicBlockAttribute::IRBlockAddressTaken); + auto SetName = Block->attributes().setIRBlockName(symbolName(token())); + if (!SetName) + return importError(token(), SetName.error()); + } + advance(); + } else if (at(MITokenKind::IRBlock) || at(MITokenKind::NamedIRBlock)) { + if (!Seen.insert("ir-block").second) + return fail("duplicate IR block attribute"); + if (Context.HasIRBlock && !Context.HasIRBlock(symbolName(token()))) + return fail(token(), "use of undefined IR block '" + std::string(token().spelling()) + "'"); + if (Block != nullptr) { + auto SetName = Block->attributes().setIRBlockName(symbolName(token())); + if (!SetName) + return importError(token(), SetName.error()); + } + advance(); + } else if (atKeyword("align")) { + if (!Seen.insert("align").second) + return fail("duplicate basic block alignment"); + Alignment Align; + if (parseAlignment(Align)) + return true; + if (Block != nullptr) + Block->attributes().setAlignment(Align); + } else if (atKeyword("bbsections")) { + if (!Seen.insert("bbsections").second) + return fail("duplicate basic block section"); + advance(); + MachineBasicBlockSectionID Section; + std::uint32_t Number = 0; + if (at(MITokenKind::IntegerLiteral) && !parseUnsigned32(token(), Number)) + Section = MachineBasicBlockSectionID::numbered(Number); + else if (at(MITokenKind::Identifier) && token().stringValue() == "Exception") + Section = MachineBasicBlockSectionID::exception(); + else if (at(MITokenKind::Identifier) && token().stringValue() == "Cold") + Section = MachineBasicBlockSectionID::cold(); + else + return fail("unknown basic block section ID"); + if (Block != nullptr) + Block->attributes().setSectionID(Section); + advance(); + } else if (atKeyword("bb_id")) { + if (!Seen.insert("bb_id").second) + return fail("duplicate basic block profile ID"); + advance(); + std::uint32_t BaseID = 0; + std::uint32_t CloneID = 0; + if (at(MITokenKind::FloatingPointLiteral)) { + const std::string_view Spelling = token().spelling(); + const std::size_t Dot = Spelling.find('.'); + std::uint64_t Base = 0; + std::uint64_t Clone = 0; + if (Dot == std::string_view::npos || !parseUnsignedDecimal(Spelling.substr(0, Dot), Base) || + !parseUnsignedDecimal(Spelling.substr(Dot + 1), Clone) || + Base > std::numeric_limits::max() || + Clone > std::numeric_limits::max()) { + return fail("invalid basic block profile ID"); + } + BaseID = static_cast(Base); + CloneID = static_cast(Clone); + advance(); + } else { + if (!at(MITokenKind::IntegerLiteral) || parseUnsigned32(token(), BaseID)) + return fail("invalid basic block base ID"); + advance(); + if (consume(MITokenKind::Dot) || consume(MITokenKind::Comma)) { + if (!at(MITokenKind::IntegerLiteral) || parseUnsigned32(token(), CloneID)) + return fail("invalid basic block clone ID"); + advance(); + } + } + if (Block != nullptr) + Block->attributes().setProfileID({BaseID, CloneID}); + } else if (atKeyword("call-frame-size")) { + if (!Seen.insert("call-frame-size").second) + return fail("duplicate basic block call frame size"); + advance(); + std::uint32_t Size = 0; + if (!at(MITokenKind::IntegerLiteral) || parseUnsigned32(token(), Size)) + return fail("invalid basic block call frame size"); + if (Block != nullptr) + Block->attributes().setCallFrameSize(Size); + advance(); + } else { + return fail("unknown basic block attribute '" + std::string(token().spelling()) + "'"); + } + + if (!consume(MITokenKind::Comma)) + break; + } + return expect(MITokenKind::RightParen, "')'"); +} + +bool MIParser::Impl::parseBlockDefinition(bool Create, std::uint32_t &ID) { + const MIToken Label = token(); + if (!at(MITokenKind::MachineBasicBlockLabel) || parseUnsigned32(Label, ID)) + return fail("invalid machine basic block label"); + advance(); + + MachineBasicBlock *Block = nullptr; + if (Create) { + if (Blocks.size() >= Options.MaximumBlocks) + return fail(Label, "machine basic block count exceeds parser limit"); + if (Blocks.contains(ID)) + return fail(Label, "redefinition of machine basic block with id #" + std::to_string(ID)); + if (!Label.stringValue().empty() && Context.HasIRBlock && + !Context.HasIRBlock(Label.stringValue())) { + return fail(Label, "basic block '" + std::string(Label.stringValue()) + + "' is not defined in the function '" + std::string(Function.name()) + + "'"); + } + auto Created = Function.createBasicBlock(); + if (!Created) + return importError(Label, Created.error()); + Block = *Created; + auto Appended = Function.appendBlock(*Block); + if (!Appended) + return importError(Label, Appended.error()); + if (!Label.stringValue().empty()) { + auto SetName = Block->attributes().setName(std::string(Label.stringValue())); + if (!SetName) + return importError(Label, SetName.error()); + } + Blocks.emplace(ID, Block); + } else { + const auto Found = Blocks.find(ID); + if (Found == Blocks.end()) + return fail(Label, "use of undefined machine basic block #" + std::to_string(ID)); + Block = Found->second; + } + + if (consume(MITokenKind::LeftParen) && parseBlockAttributes(Create ? Block : nullptr)) + return true; + return expect(MITokenKind::Colon, "':' after the basic block definition"); +} + +bool MIParser::Impl::parseFirstPass() { + Position = 0; + bool AtLineStart = true; + std::size_t BraceDepth = 0; + while (!at(MITokenKind::EndOfFile)) { + if (consume(MITokenKind::Newline)) { + AtLineStart = true; + continue; + } + if (at(MITokenKind::MachineBasicBlockLabel)) { + if (!AtLineStart || token().range().Begin.Column != 1) + return fail("basic block definition must be at the start of a line"); + std::uint32_t ID = 0; + if (parseBlockDefinition(true, ID)) + return true; + AtLineStart = false; + continue; + } + if (at(MITokenKind::LeftBrace)) + ++BraceDepth; + else if (at(MITokenKind::RightBrace)) { + if (BraceDepth == 0) + return fail("extraneous closing instruction bundle brace"); + --BraceDepth; + } + AtLineStart = false; + advance(); + } + if (BraceDepth != 0) + return fail("unterminated instruction bundle"); + return false; +} + +Register MIParser::Impl::getOrCreateVirtualRegister(const MIToken &Token) { + if (Token.kind() == MITokenKind::VirtualRegister) { + std::uint32_t ID = 0; + if (parseUnsigned32(Token, ID)) + return {}; + if (const auto Found = NumericVirtualRegisters.find(ID); + Found != NumericVirtualRegisters.end()) { + return Found->second; + } + auto Created = Function.registerInfo().createVirtualRegister(); + if (!Created) { + static_cast(importError(Token, Created.error())); + return {}; + } + NumericVirtualRegisters.emplace(ID, *Created); + return *Created; + } + + const std::string Name(Token.stringValue()); + if (const auto Found = NamedVirtualRegisters.find(Name); Found != NamedVirtualRegisters.end()) + return Found->second; + auto Created = Function.registerInfo().createVirtualRegister(); + if (!Created) { + static_cast(importError(Token, Created.error())); + return {}; + } + auto Info = Function.registerInfo().virtualRegisterInfo(*Created); + if (!Info) { + static_cast(importError(Token, Info.error())); + return {}; + } + Info->get().Name = Name; + NamedVirtualRegisters.emplace(Name, *Created); + return *Created; +} + +bool MIParser::Impl::parseRegister(Register &Result) { + const MIToken RegisterToken = token(); + if (consume(MITokenKind::Underscore)) { + Result = {}; + return false; + } + if (at(MITokenKind::NamedRegister)) { + if (RegisterToken.stringValue() == "noreg") { + Result = {}; + advance(); + return false; + } + const PhysicalRegisterDesc *Description = + Target.registers().physicalRegister(RegisterToken.stringValue()); + if (Description == nullptr) + return fail("unknown physical register '$" + std::string(RegisterToken.stringValue()) + "'"); + Result = Register::fromPhysical(Description->Reg); + advance(); + return false; + } + if (at(MITokenKind::VirtualRegister) || at(MITokenKind::NamedVirtualRegister)) { + Result = getOrCreateVirtualRegister(RegisterToken); + if (Result.isNoRegister()) + return true; + advance(); + return false; + } + return fail("expected a machine register"); +} + +bool MIParser::Impl::parseRegisterOperand(bool IsDef, ParsedOperand &Result) { + const MIToken Begin = token(); + RegisterOperandFlags Flags{IsDef ? RegisterOperandFlag::Define : RegisterOperandFlag::Use}; + std::unordered_set SeenFlags; + while (token().isRegisterFlag()) { + const std::string Name(token().stringValue()); + if (!SeenFlags.insert(Name).second) + return fail("duplicate '" + Name + "' register flag"); + if (Name == "implicit") + Flags.set(RegisterOperandFlag::Implicit); + else if (Name == "implicit-def") { + Flags.clear(RegisterOperandFlag::Use); + Flags.set(RegisterOperandFlag::Define); + Flags.set(RegisterOperandFlag::Implicit); + IsDef = true; + } else if (Name == "def") { + Flags.clear(RegisterOperandFlag::Use); + Flags.set(RegisterOperandFlag::Define); + IsDef = true; + } else if (Name == "dead") + Flags.set(RegisterOperandFlag::Dead); + else if (Name == "killed") + Flags.set(RegisterOperandFlag::Kill); + else if (Name == "undef") + Flags.set(RegisterOperandFlag::Undef); + else if (Name == "internal") + Flags.set(RegisterOperandFlag::InternalRead); + else if (Name == "early-clobber") + Flags.set(RegisterOperandFlag::EarlyClobber); + else if (Name == "debug-use") + Flags.set(RegisterOperandFlag::Debug); + else if (Name == "renamable") + Flags.set(RegisterOperandFlag::Renamable); + advance(); + } + + Register Reg; + if (parseRegister(Reg)) + return true; + + SubRegIndex SubReg; + if (consume(MITokenKind::Dot)) { + if (!Reg.isVirtual() || !at(MITokenKind::Identifier)) + return fail("subregister index requires a virtual register"); + const SubRegIndexDesc *Description = Target.registers().subRegIndex(token().stringValue()); + if (Description == nullptr) + return fail("unknown subregister index '" + std::string(token().stringValue()) + "'"); + SubReg = Description->ID; + advance(); + } + + if (consume(MITokenKind::Colon)) { + if (!Reg.isVirtual()) + return fail("register class or bank requires a virtual register"); + auto InfoResult = Function.registerInfo().virtualRegisterInfo(Reg); + if (!InfoResult) + return importError(Begin, InfoResult.error()); + VirtualRegisterInfo &Info = InfoResult->get(); + if (consume(MITokenKind::Underscore)) { + if (Info.RegClass || Info.Bank) + return fail("conflicting generic virtual register constraint"); + } else if (at(MITokenKind::Identifier)) { + if (const RegisterClassDesc *Class = + Target.registers().registerClass(token().stringValue())) { + 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())) { + 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) + return importError(token(), SetBank.error()); + } else { + return fail("unknown register class or bank '" + std::string(token().stringValue()) + "'"); + } + advance(); + } else { + return fail("expected '_', register class, or register bank name"); + } + } + + std::optional TiedDef; + if (consume(MITokenKind::LeftParen)) { + if (atKeyword("tied-def")) { + if (IsDef) + return fail("'tied-def' is only valid on register uses"); + advance(); + std::uint64_t Index = 0; + if (!at(MITokenKind::IntegerLiteral) || parseUnsigned(token(), Index) || + Index > std::numeric_limits::max()) { + return fail("invalid tied-def operand index"); + } + TiedDef = static_cast(Index); + advance(); + if (expect(MITokenKind::RightParen, "')' after tied-def")) + return true; + } else { + if (!Reg.isVirtual()) + return fail("low-level type is only valid on a virtual register"); + LLT Type; + if (parseLowLevelType(Type)) + return true; + if (expect(MITokenKind::RightParen, "')' after the low-level type")) + return true; + auto InfoResult = Function.registerInfo().virtualRegisterInfo(Reg); + if (!InfoResult) + return importError(Begin, InfoResult.error()); + VirtualRegisterInfo &Info = InfoResult->get(); + if (Info.Type && *Info.Type != Type) + return fail(Begin, "conflicting virtual register low-level type"); + auto SetType = Function.registerInfo().setType(Reg, Type); + if (!SetType) + return importError(Begin, SetType.error()); + } + } + + if (IsDef && Reg.isVirtual()) { + auto InfoResult = Function.registerInfo().virtualRegisterInfo(Reg); + if (!InfoResult) + return importError(Begin, InfoResult.error()); + const VirtualRegisterInfo &Info = InfoResult->get(); + if (!Info.RegClass && !Info.Type) + return fail(Begin, "generic virtual register definition requires a low-level type"); + } + + if (Flags.has(RegisterOperandFlag::Define) && Flags.has(RegisterOperandFlag::Kill)) + return fail(Begin, "defined register operand cannot be killed"); + if (!Flags.has(RegisterOperandFlag::Define) && Flags.has(RegisterOperandFlag::Dead)) + return fail(Begin, "register use cannot be dead"); + + auto Operand = MachineOperand::registerOperand(Reg, Flags, SubReg); + if (!Operand) + return importError(Begin, Operand.error()); + Result = + ParsedOperand{std::move(*Operand), TiedDef, {Begin.range().Begin, token().range().Begin}}; + return false; +} + +MachineBasicBlock *MIParser::Impl::blockFor(const MIToken &Token) { + std::uint32_t ID = 0; + if (parseUnsigned32(Token, ID)) { + static_cast(fail(Token, "invalid machine basic block reference")); + return nullptr; + } + const auto Found = Blocks.find(ID); + if (Found == Blocks.end()) { + static_cast(fail(Token, "use of undefined machine basic block #" + std::to_string(ID))); + return nullptr; + } + if (!Token.stringValue().empty()) { + const std::optional Name = Found->second->attributes().name(); + if (!Name || *Name != Token.stringValue()) { + static_cast(fail(Token, "machine basic block reference has a mismatched name")); + return nullptr; + } + } + return Found->second; +} + +std::optional MIParser::Impl::frameIndexFor(const MIToken &Token, bool Fixed) { + std::uint32_t ID = 0; + if (parseUnsigned32(Token, ID)) { + static_cast(fail(Token, "invalid stack object reference")); + return std::nullopt; + } + const auto &Slots = Fixed ? Context.FixedStackObjects : Context.StackObjects; + for (const MIFrameObjectSlot &Slot : Slots) { + if (Slot.TextID != ID) + continue; + if (!Token.stringValue().empty() && (!Slot.Name || *Slot.Name != Token.stringValue())) { + static_cast(fail(Token, "stack object reference has a mismatched name")); + return std::nullopt; + } + if (Function.frameInfo().object(Slot.Index) == nullptr) { + static_cast(fail(Token, "stack object slot maps to a missing frame object")); + return std::nullopt; + } + return Slot.Index; + } + static_cast(fail(Token, std::string("use of undefined ") + + (Fixed ? "fixed stack" : "stack") + " object #" + + std::to_string(ID))); + return std::nullopt; +} + +std::optional MIParser::Impl::constantPoolIndexFor(const MIToken &Token) { + std::uint32_t ID = 0; + if (parseUnsigned32(Token, ID)) { + static_cast(fail(Token, "invalid constant pool reference")); + return std::nullopt; + } + for (const MIConstantPoolSlot &Slot : Context.ConstantPoolItems) { + if (Slot.TextID == ID && Function.constantPool().entry(Slot.Index) != nullptr) + return Slot.Index; + } + static_cast(fail(Token, "use of undefined constant pool item #" + std::to_string(ID))); + return std::nullopt; +} + +std::optional MIParser::Impl::jumpTableIndexFor(const MIToken &Token) { + std::uint32_t ID = 0; + if (parseUnsigned32(Token, ID)) { + static_cast(fail(Token, "invalid jump table reference")); + return std::nullopt; + } + for (const MIJumpTableSlot &Slot : Context.JumpTables) { + if (Slot.TextID == ID && (Function.jumpTableInfo().table(Slot.Index) != nullptr || + (Slot.Index.value() < Function.jumpTableInfo().tables().size() && + Function.jumpTableInfo().tables()[Slot.Index.value()].isPending()))) + return Slot.Index; + } + static_cast(fail(Token, "use of undefined jump table #" + std::to_string(ID))); + return std::nullopt; +} + +std::string MIParser::Impl::symbolName(const MIToken &Token) const { + if (Token.hasStringValue()) + return std::string(Token.stringValue()); + return std::string(Token.integerValue()); +} + +bool MIParser::Impl::parseTargetFlags(std::uint32_t &Flags) { + Flags = 0; + if (!atKeyword("target-flags")) + return false; + advance(); + if (expect(MITokenKind::LeftParen, "'(' after target-flags")) + return true; + if (!at(MITokenKind::Identifier)) + return fail("expected a target flag name"); + + const std::string First(token().stringValue()); + if (const auto Direct = Target.mirText().directOperandFlag(First)) + Flags = *Direct; + else if (const auto Bit = Target.mirText().bitmaskOperandFlag(First)) + Flags = *Bit; + else + return fail("unknown target operand flag '" + First + "'"); + advance(); + + std::unordered_set Seen{First}; + while (consume(MITokenKind::Comma)) { + if (!at(MITokenKind::Identifier)) + return fail("expected a target bit flag name"); + const std::string Name(token().stringValue()); + const auto Bit = Target.mirText().bitmaskOperandFlag(Name); + if (!Bit) + return fail("unknown target bit operand flag '" + Name + "'"); + if (!Seen.insert(Name).second || (Flags & *Bit) == *Bit) + return fail("duplicate target operand flag '" + Name + "'"); + Flags |= *Bit; + advance(); + } + if (expect(MITokenKind::RightParen, "')' after target flags")) + return true; + const auto Valid = Target.mirText().validateOperandFlags(Flags); + return !Valid && importError(token(), Valid.error()); +} + +bool MIParser::Impl::parseTypedIntegerOperand(ParsedOperand &Result) { + const MIToken Begin = token(); + const std::string_view TypeSpelling = Begin.stringValue(); + if (!TypeSpelling.starts_with('i')) + return fail(Begin, "typed integer operand must use an 'iN' type"); + std::uint64_t WideBitWidth = 0; + if (!parseUnsignedDigits(TypeSpelling.substr(1), WideBitWidth) || + WideBitWidth > IntegerImmediate::MaxBitWidth) { + return fail(Begin, "typed integer operand has an invalid bit width"); + } + const auto BitWidth = static_cast(WideBitWidth); + advance(); + + std::string_view ValueSpelling; + if (at(MITokenKind::IntegerLiteral)) { + ValueSpelling = token().integerValue(); + } else if (at(MITokenKind::Identifier) && token().stringValue() == "true") { + ValueSpelling = "1"; + } else if (at(MITokenKind::Identifier) && token().stringValue() == "false") { + ValueSpelling = "0"; + } else { + return fail("expected a decimal integer, true, or false after the typed integer type"); + } + + auto Value = parseIntegerImmediateValue(BitWidth, ValueSpelling); + if (!Value) + return importError(token(), Value.error()); + advance(); + Result = ParsedOperand{ + MachineOperand::integerImmediate(std::move(*Value)), + std::nullopt, + {Begin.range().Begin, token().range().Begin}, + }; + return false; +} + +bool MIParser::Impl::parseFloatingImmediateOperand(ParsedOperand &Result) { + const MIToken Begin = token(); + const auto Semantics = floatSemanticsForKeyword(Begin.stringValue()); + if (!Semantics) + return fail(Begin, "unknown floating-point immediate type"); + advance(); + if (!at(MITokenKind::FloatingPointLiteral) && !at(MITokenKind::HexLiteral)) + return fail("expected a floating-point literal"); + + const MIToken Literal = token(); + std::string ConstantText(Begin.stringValue()); + ConstantText.push_back(' '); + ConstantText.append(Literal.spelling()); + auto Constant = parseLLVMConstant(ConstantText); + if (!Constant) + return fail(Literal, std::string(Constant.error().message())); + if (!Constant->FloatingValue || Constant->FloatingValue->semantics() != *Semantics) + return fail(Literal, "floating-point literal produced the wrong semantics"); + advance(); + Result = ParsedOperand{ + MachineOperand::floatingImmediate(*Constant->FloatingValue), + std::nullopt, + {Begin.range().Begin, token().range().Begin}, + }; + return false; +} + +bool MIParser::Impl::parseMetadataReference(MachineMetadataReference &Result) { + const MIToken Begin = token(); + if (expect(MITokenKind::Exclaim, "'!' before a metadata slot")) + return true; + std::uint32_t TextID = 0; + if (!at(MITokenKind::IntegerLiteral) || parseUnsigned32(token(), TextID)) + return fail("expected a 32-bit metadata slot number"); + + for (const MIMetadataSlot &Slot : Context.Metadata) { + if (Slot.TextID != TextID) + continue; + if (!Slot.Reference.isValid()) + return fail(Begin, "metadata slot maps to an invalid reference"); + Result = Slot.Reference; + advance(); + return false; + } + if (Function.metadata().node(TextID) != nullptr) { + Result = MachineMetadataReference::machineFunction(TextID); + advance(); + return false; + } + return fail(Begin, "use of undefined metadata slot !" + std::to_string(TextID)); +} + +bool MIParser::Impl::parseMetadataOperand(ParsedOperand &Result) { + const MIToken Begin = token(); + MachineMetadataValue Value; + if (at(MITokenKind::Exclaim)) { + MachineMetadataReference Reference; + if (parseMetadataReference(Reference)) + return true; + auto Created = MachineMetadataValue::reference(Reference); + if (!Created) + return importError(Begin, Created.error()); + Value = std::move(*Created); + } else if (at(MITokenKind::MetadataKeyword) && token().isMetadataKeyword("!DIExpression")) { + advance(); + if (expect(MITokenKind::LeftParen, "'(' after !DIExpression")) + return true; + std::vector Elements; + while (!at(MITokenKind::RightParen)) { + if (at(MITokenKind::Identifier)) { + const auto Opcode = parseDIExpressionOpcodeName(token().stringValue()); + const auto Encoding = parseDIExpressionAttributeEncodingName(token().stringValue()); + if (!Opcode && !Encoding) + return fail(invalidDIExpressionElementMessage(token().stringValue())); + Elements.push_back(Opcode ? *Opcode : *Encoding); + advance(); + } else if (at(MITokenKind::IntegerLiteral)) { + std::int64_t Signed = 0; + if (parseSigned64(token(), Signed)) + return fail("debug expression element exceeds 64 bits"); + Elements.push_back(std::bit_cast(Signed)); + advance(); + } else { + return fail("expected a debug expression opcode or integer"); + } + if (!consume(MITokenKind::Comma)) + break; + } + if (expect(MITokenKind::RightParen, "')' after !DIExpression")) + return true; + Value = MachineMetadataValue::expression(MachineDIExpression(std::move(Elements))); + } else { + return fail("expected a metadata reference or !DIExpression"); + } + + auto Operand = MachineOperand::metadata(std::move(Value)); + if (!Operand) + return importError(Begin, Operand.error()); + Result = ParsedOperand{ + std::move(*Operand), + std::nullopt, + {Begin.range().Begin, token().range().Begin}, + }; + return false; +} + +bool MIParser::Impl::parseInlineDILocation( + std::vector &InlineChain, + std::optional &TerminalInlinedAt) { + if (!at(MITokenKind::MetadataKeyword) || !token().isMetadataKeyword("!DILocation")) { + return fail("expected an inline !DILocation"); + } + advance(); + if (expect(MITokenKind::LeftParen, "'(' after !DILocation")) + return true; + + MachineDILocationFrame Frame; + bool HasLine = false; + bool HasScope = false; + std::vector NestedChain; + std::optional NestedTerminal; + std::unordered_set SeenFields; + while (!at(MITokenKind::RightParen)) { + if (!at(MITokenKind::Identifier)) + return fail("expected a DILocation field name"); + const MIToken FieldToken = token(); + const std::string Field(token().stringValue()); + if (!SeenFields.insert(Field).second) + return fail("duplicate DILocation field '" + Field + "'"); + advance(); + if (expect(MITokenKind::Colon, "':' after a DILocation field name")) + return true; + + if (Field == "line") { + std::uint32_t Line = 0; + if (!at(MITokenKind::IntegerLiteral) || parseUnsigned32(token(), Line)) + return fail("expected an unsigned 32-bit DILocation line"); + Frame.Line = Line; + HasLine = true; + advance(); + } else if (Field == "column") { + std::uint64_t Column = 0; + if (!at(MITokenKind::IntegerLiteral) || parseUnsigned(token(), Column) || + Column > std::numeric_limits::max()) { + return fail("expected an unsigned 16-bit DILocation column"); + } + Frame.Column = static_cast(Column); + advance(); + } else if (Field == "scope") { + MachineMetadataReference Scope; + if (parseMetadataReference(Scope)) + return true; + if (Scope.scope() != MachineMetadataScope::EmbeddedIR) + return fail(FieldToken, "DILocation scope must reference embedded IR metadata"); + Frame.Scope = Scope; + HasScope = true; + } else if (Field == "inlinedAt") { + if (at(MITokenKind::MetadataKeyword) && token().isMetadataKeyword("!DILocation")) { + if (parseInlineDILocation(NestedChain, NestedTerminal)) + return true; + } else { + MachineMetadataReference Reference; + if (parseMetadataReference(Reference)) + return true; + if (Reference.scope() != MachineMetadataScope::EmbeddedIR) { + return fail(FieldToken, "DILocation inlinedAt must reference embedded IR metadata"); + } + NestedTerminal = Reference; + } + } else if (Field == "isImplicitCode") { + if (!at(MITokenKind::Identifier) || + (token().stringValue() != "true" && token().stringValue() != "false")) { + return fail("expected 'true' or 'false' for DILocation isImplicitCode"); + } + Frame.IsImplicitCode = token().stringValue() == "true"; + advance(); + } else if (Field == "atomGroup") { + std::uint64_t Group = 0; + if (!at(MITokenKind::IntegerLiteral) || parseUnsigned(token(), Group) || + Group > MachineDILocation::MaxAtomGroup) { + return fail("DILocation atomGroup exceeds 61 bits"); + } + Frame.AtomGroup = Group; + advance(); + } else if (Field == "atomRank") { + std::uint64_t Rank = 0; + if (!at(MITokenKind::IntegerLiteral) || parseUnsigned(token(), Rank) || Rank > 7) { + return fail("DILocation atomRank exceeds three bits"); + } + Frame.AtomRank = static_cast(Rank); + advance(); + } else { + return fail(FieldToken, "unknown DILocation field '" + Field + "'"); + } + + if (!consume(MITokenKind::Comma)) + break; + } + if (expect(MITokenKind::RightParen, "')' after !DILocation")) + return true; + if (!HasLine) + return fail("DILocation requires a line field"); + if (!HasScope) + return fail("DILocation requires a scope field"); + + InlineChain.push_back(Frame); + InlineChain.insert(InlineChain.end(), NestedChain.begin(), NestedChain.end()); + TerminalInlinedAt = NestedTerminal; + return false; +} + +bool MIParser::Impl::parseCFIRegister(DwarfRegisterNumber &Result) { + if (!at(MITokenKind::NamedRegister)) + return fail("expected a physical register in the CFI instruction"); + const MIToken RegisterToken = token(); + Register Reg; + if (parseRegister(Reg) || !Reg.isPhysical()) + return true; + const auto Physical = Reg.physicalRegister(); + if (!Physical) + return fail(RegisterToken, "CFI register is not a physical register"); + const auto Dwarf = Target.mirText().dwarfRegister(*Physical); + if (!Dwarf) + return fail(RegisterToken, "physical register has no canonical DWARF register number"); + Result = DwarfRegisterNumber::fromValue(*Dwarf); + return false; +} + +bool MIParser::Impl::parseCFISigned(std::int64_t &Result) { + if (!at(MITokenKind::IntegerLiteral) || parseSigned64(token(), Result)) + return fail("expected a signed 64-bit CFI integer"); + advance(); + return false; +} + +bool MIParser::Impl::parseCFIUnsigned(std::uint32_t &Result) { + if (!at(MITokenKind::IntegerLiteral) || parseUnsigned32(token(), Result)) + return fail("expected an unsigned 32-bit CFI integer"); + advance(); + return false; +} + +bool MIParser::Impl::parseCFIOperand(ParsedOperand &Result) { + const MIToken Begin = token(); + const std::string Operation(token().stringValue()); + advance(); + + const auto Comma = [this]() { return expect(MITokenKind::Comma, "',' in CFI instruction"); }; + DwarfRegisterNumber Register; + DwarfRegisterNumber OtherRegister; + std::int64_t Offset = 0; + std::optional Instruction; + if (Operation == "same_value") { + if (parseCFIRegister(Register)) + return true; + Instruction = MachineCFIInstruction::sameValue(Register); + } else if (Operation == "remember_state") { + Instruction = MachineCFIInstruction::rememberState(); + } else if (Operation == "restore_state") { + Instruction = MachineCFIInstruction::restoreState(); + } else if (Operation == "offset" || Operation == "rel_offset") { + if (parseCFIRegister(Register) || Comma() || parseCFISigned(Offset)) + return true; + Instruction = Operation == "offset" ? MachineCFIInstruction::offset(Register, Offset) + : MachineCFIInstruction::relativeOffset(Register, Offset); + } else if (Operation == "def_cfa_register") { + if (parseCFIRegister(Register)) + return true; + Instruction = MachineCFIInstruction::defCFARegister(Register); + } else if (Operation == "def_cfa_offset" || Operation == "adjust_cfa_offset") { + if (parseCFISigned(Offset)) + return true; + Instruction = Operation == "def_cfa_offset" ? MachineCFIInstruction::defCFAOffset(Offset) + : MachineCFIInstruction::adjustCFAOffset(Offset); + } else if (Operation == "def_cfa") { + if (parseCFIRegister(Register) || Comma() || parseCFISigned(Offset)) + return true; + Instruction = MachineCFIInstruction::defCFA(Register, Offset); + } else if (Operation == "llvm_def_aspace_cfa") { + std::uint32_t AddressSpace = 0; + if (parseCFIRegister(Register) || Comma() || parseCFISigned(Offset) || Comma() || + parseCFIUnsigned(AddressSpace)) { + return true; + } + Instruction = MachineCFIInstruction::llvmDefAddressSpaceCFA(Register, Offset, AddressSpace); + } else if (Operation == "restore" || Operation == "undefined") { + if (parseCFIRegister(Register)) + return true; + Instruction = Operation == "restore" ? MachineCFIInstruction::restore(Register) + : MachineCFIInstruction::undefined(Register); + } else if (Operation == "register") { + if (parseCFIRegister(Register) || Comma() || parseCFIRegister(OtherRegister)) + return true; + Instruction = MachineCFIInstruction::registerRule(Register, OtherRegister); + } else if (Operation == "window_save") { + Instruction = MachineCFIInstruction::windowSave(); + } else if (Operation == "negate_ra_sign_state") { + Instruction = MachineCFIInstruction::negateReturnAddressState(); + } else if (Operation == "negate_ra_sign_state_with_pc") { + Instruction = MachineCFIInstruction::negateReturnAddressStateWithPC(); + } else if (Operation == "llvm_register_pair") { + DwarfRegisterNumber First; + DwarfRegisterNumber Second; + std::uint32_t FirstSize = 0; + std::uint32_t SecondSize = 0; + if (parseCFIRegister(Register) || Comma() || parseCFIRegister(First) || Comma() || + parseCFIUnsigned(FirstSize) || Comma() || parseCFIRegister(Second) || Comma() || + parseCFIUnsigned(SecondSize)) { + return true; + } + Instruction = MachineCFIInstruction::llvmRegisterPair({.Register = Register, + .FirstRegister = First, + .FirstSizeInBits = FirstSize, + .SecondRegister = Second, + .SecondSizeInBits = SecondSize}); + } else if (Operation == "llvm_vector_registers") { + if (parseCFIRegister(Register) || Comma()) + return true; + std::vector Registers; + do { + DwarfRegisterNumber VectorRegister; + std::uint32_t Lane = 0; + std::uint32_t Size = 0; + if (parseCFIRegister(VectorRegister) || Comma() || parseCFIUnsigned(Lane) || Comma() || + parseCFIUnsigned(Size)) { + return true; + } + Registers.push_back({.Register = VectorRegister, .Lane = Lane, .SizeInBits = Size}); + if (!at(MITokenKind::Comma)) + break; + advance(); + if (!at(MITokenKind::NamedRegister)) + return fail("expected another vector CFI register tuple"); + } while (true); + auto Created = MachineCFIInstruction::llvmVectorRegisters( + {.Register = Register, .VectorRegisters = std::move(Registers)}); + if (!Created) + return importError(Begin, Created.error()); + Instruction = std::move(*Created); + } else if (Operation == "llvm_vector_offset") { + DwarfRegisterNumber MaskRegister; + std::uint32_t RegisterSize = 0; + std::uint32_t MaskSize = 0; + if (parseCFIRegister(Register) || Comma() || parseCFIUnsigned(RegisterSize) || Comma() || + parseCFIRegister(MaskRegister) || Comma() || parseCFIUnsigned(MaskSize) || Comma() || + parseCFISigned(Offset)) { + return true; + } + Instruction = MachineCFIInstruction::llvmVectorOffset({.Register = Register, + .RegisterSizeInBits = RegisterSize, + .MaskRegister = MaskRegister, + .MaskRegisterSizeInBits = MaskSize, + .Offset = Offset}); + } else if (Operation == "llvm_vector_register_mask") { + DwarfRegisterNumber SpillRegister; + DwarfRegisterNumber MaskRegister; + std::uint32_t SpillLaneSize = 0; + std::uint32_t MaskSize = 0; + if (parseCFIRegister(Register) || Comma() || parseCFIRegister(SpillRegister) || Comma() || + parseCFIUnsigned(SpillLaneSize) || Comma() || parseCFIRegister(MaskRegister) || Comma() || + parseCFIUnsigned(MaskSize)) { + return true; + } + Instruction = + MachineCFIInstruction::llvmVectorRegisterMask({.Register = Register, + .SpillRegister = SpillRegister, + .SpillRegisterLaneSizeInBits = SpillLaneSize, + .MaskRegister = MaskRegister, + .MaskRegisterSizeInBits = MaskSize}); + } else if (Operation == "escape") { + std::vector Bytes; + while (true) { + std::uint64_t Byte = 0; + if (!at(MITokenKind::HexLiteral) || parseUnsigned(token(), Byte) || + Byte > std::numeric_limits::max()) { + return fail("CFI escape element must be an 8-bit hexadecimal literal"); + } + Bytes.push_back(static_cast(Byte)); + advance(); + if (!at(MITokenKind::Comma) || !nextIs(MITokenKind::HexLiteral)) + break; + advance(); + } + auto Created = MachineCFIInstruction::escape(std::move(Bytes)); + if (!Created) + return importError(Begin, Created.error()); + Instruction = std::move(*Created); + } else { + return fail(Begin, "unsupported CFI operation '" + Operation + "'"); + } + + auto Index = Function.cfiInfo().addInstruction(std::move(*Instruction)); + if (!Index) + return importError(Begin, Index.error()); + Result = ParsedOperand{ + MachineOperand::cfiIndex(*Index), + std::nullopt, + {Begin.range().Begin, token().range().Begin}, + }; + return false; +} + +bool MIParser::Impl::parseMemoryBase(MemoryBase &Result, std::int64_t &Offset) { + const MIToken BaseToken = token(); + switch (BaseToken.kind()) { + case MITokenKind::StackObject: + case MITokenKind::FixedStackObject: { + const auto Index = frameIndexFor(BaseToken, BaseToken.kind() == MITokenKind::FixedStackObject); + if (!Index) + return true; + Result = MemoryBase::frameIndex(*Index); + advance(); + break; + } + case MITokenKind::ConstantPoolItem: { + const auto Index = constantPoolIndexFor(BaseToken); + if (!Index) + return true; + Result = MemoryBase::constantPool(*Index); + advance(); + break; + } + case MITokenKind::JumpTableIndex: { + const auto Index = jumpTableIndexFor(BaseToken); + if (!Index) + return true; + Result = MemoryBase::jumpTable(*Index); + advance(); + break; + } + case MITokenKind::GlobalValue: + case MITokenKind::NamedGlobalValue: + Result = MemoryBase::globalSymbol(symbolName(BaseToken)); + advance(); + break; + case MITokenKind::ExternalSymbol: + Result = MemoryBase::externalSymbol(symbolName(BaseToken)); + advance(); + break; + case MITokenKind::IRValue: + case MITokenKind::NamedIRValue: + Result = MemoryBase::irValue(symbolName(BaseToken)); + advance(); + break; + case MITokenKind::QuotedIRValue: { + auto Constant = parseLLVMConstant(BaseToken.stringValue()); + if (!Constant) + return importError(BaseToken, Constant.error()); + auto Base = MemoryBase::irConstant(std::move(*Constant)); + if (!Base) + return importError(BaseToken, Base.error()); + Result = std::move(*Base); + advance(); + break; + } + case MITokenKind::Keyword: + if (BaseToken.isKeyword("unknown-address")) { + Result = MemoryBase(); + advance(); + } else if (BaseToken.isKeyword("stack")) { + Result = MemoryBase::stack(); + advance(); + } else if (BaseToken.isKeyword("got")) { + Result = MemoryBase::got(); + advance(); + } else if (BaseToken.isKeyword("constant-pool")) { + Result = MemoryBase::constantPoolPseudo(); + advance(); + } else if (BaseToken.isKeyword("jump-table")) { + Result = MemoryBase::jumpTablePseudo(); + advance(); + } else if (BaseToken.isKeyword("call-entry")) { + advance(); + if (at(MITokenKind::GlobalValue) || at(MITokenKind::NamedGlobalValue)) { + Result = MemoryBase::callEntryGlobal(symbolName(token())); + } else if (at(MITokenKind::ExternalSymbol)) { + Result = MemoryBase::callEntryExternal(symbolName(token())); + } else { + return fail("call-entry memory base requires a global or external symbol"); + } + advance(); + } else if (BaseToken.isKeyword("custom")) { + advance(); + if (!at(MITokenKind::Identifier) && !at(MITokenKind::StringConstant)) + return fail("custom memory base requires a target spelling"); + Result = MemoryBase::custom(std::string(token().stringValue())); + advance(); + } else { + return fail(BaseToken, "unsupported machine memory base"); + } + break; + default: + return fail(BaseToken, "expected a machine memory base"); + } + return parseOptionalOffset(Offset); +} + +bool MIParser::Impl::parseMemoryOperand(const MachineMemOperand *&Result, + Opcode InstructionOpcode) { + const MIToken Begin = token(); + if (expect(MITokenKind::LeftParen, "'(' before a machine memory operand")) + return true; + + MachineMemOperandDesc Description; + std::unordered_set SeenFlags; + while (token().isMemoryOperandFlag()) { + const std::string Name(token().stringValue()); + if (!SeenFlags.insert(Name).second) + return fail("duplicate machine memory operand flag '" + Name + "'"); + if (atKeyword("volatile")) + Description.Accesses.set(MemoryAccessFlag::Volatile); + else if (atKeyword("non-temporal")) + Description.Accesses.set(MemoryAccessFlag::NonTemporal); + else if (atKeyword("dereferenceable")) + Description.Accesses.set(MemoryAccessFlag::Dereferenceable); + else if (atKeyword("invariant")) + Description.Accesses.set(MemoryAccessFlag::Invariant); + else { + const auto Flag = Target.mirText().machineMemOperandFlag(token().stringValue()); + if (!Flag) + return fail("unknown target machine memory operand flag '" + Name + "'"); + if ((Description.TargetFlags & *Flag) != 0) + return fail("overlapping target machine memory operand flag '" + Name + "'"); + Description.TargetFlags |= *Flag; + } + advance(); + } + + if (!at(MITokenKind::Identifier) || + (token().stringValue() != "load" && token().stringValue() != "store")) { + return fail("expected load or store in a machine memory operand"); + } + if (token().stringValue() == "load") + Description.Accesses.set(MemoryAccessFlag::Load); + else + Description.Accesses.set(MemoryAccessFlag::Store); + advance(); + if (at(MITokenKind::Identifier) && token().stringValue() == "store") { + if (Description.Accesses.has(MemoryAccessFlag::Store)) + return fail("duplicate store memory operation"); + Description.Accesses.set(MemoryAccessFlag::Store); + advance(); + } + + if (at(MITokenKind::Identifier) && token().stringValue() == "syncscope") { + advance(); + if (expect(MITokenKind::LeftParen, "'(' after syncscope")) + return true; + if (!at(MITokenKind::StringConstant)) + return fail("syncscope requires a quoted scope name"); + const std::string_view Name = token().stringValue(); + auto Scope = std::ranges::find_if( + Context.SyncScopes, [Name](const MISyncScopeSlot &Slot) { return Slot.Name == Name; }); + if (Scope == Context.SyncScopes.end() && Context.InternSyncScope) { + const MIToken NameToken = token(); + auto ID = Context.InternSyncScope(Name); + if (!ID) + return importError(NameToken, ID.error()); + Context.SyncScopes.push_back({.Name = std::string(Name), .ID = *ID}); + Scope = std::prev(Context.SyncScopes.end()); + } + if (Scope == Context.SyncScopes.end()) + return fail("unknown synchronization scope '" + std::string(Name) + "'"); + Description.SyncScope = Scope->ID; + advance(); + if (expect(MITokenKind::RightParen, "')' after syncscope")) + return true; + } + + std::size_t OrderingCount = 0; + while (at(MITokenKind::Identifier)) { + const auto Ordering = atomicOrdering(token().stringValue()); + if (!Ordering) + break; + if (OrderingCount == 0) + Description.SuccessOrdering = *Ordering; + else if (OrderingCount == 1) + Description.FailureOrdering = *Ordering; + else + return fail("machine memory operand has more than two atomic orderings"); + ++OrderingCount; + advance(); + } + const bool IsLoad = Description.Accesses.has(MemoryAccessFlag::Load); + const bool IsStore = Description.Accesses.has(MemoryAccessFlag::Store); + if (OrderingCount == 1) { + if (InstructionOpcode == opcode(GenericOpcode::G_ATOMIC_CMPXCHG) || + InstructionOpcode == opcode(GenericOpcode::G_ATOMIC_CMPXCHG_WITH_SUCCESS)) { + Description.AtomicKind = AtomicOperation::CompareExchange; + } else { + Description.AtomicKind = IsLoad && IsStore + ? AtomicOperation::ReadModifyWrite + : (IsLoad ? AtomicOperation::Load : AtomicOperation::Store); + } + } else if (OrderingCount == 2) + Description.AtomicKind = AtomicOperation::CompareExchange; + + std::uint64_t MinimumBytes = 0; + if (at(MITokenKind::IntegerLiteral)) { + if (parseUnsigned(token(), MinimumBytes) || MinimumBytes == 0) + return fail("machine memory operand size must be a nonzero byte count"); + auto Size = MemorySize::fixed(MinimumBytes); + if (!Size) + return importError(token(), Size.error()); + Description.Size = *Size; + advance(); + } else if (atKeyword("unknown-size")) { + advance(); + } else if (consume(MITokenKind::LeftParen)) { + LLT Type; + if (parseLowLevelType(Type)) + return true; + if (expect(MITokenKind::RightParen, "')' after the memory low-level type")) + return true; + const auto MinimumBits = minimumTypeSizeInBits(Type); + if (!MinimumBits || *MinimumBits == 0) + return fail(Begin, "memory low-level type has no representable size"); + MinimumBytes = (*MinimumBits + 7U) / 8U; + auto Size = Type.isScalableVector() ? MemorySize::scalable(MinimumBytes) + : MemorySize::fixed(MinimumBytes); + if (!Size) + return importError(Begin, Size.error()); + Description.Size = *Size; + Description.MemoryType = Type; + } else { + return fail("expected a memory size, unknown-size, or memory low-level type"); + } + + if (MinimumBytes != 0) { + if (MinimumBytes > (std::uint64_t{1} << 63U)) + return fail(Begin, "machine memory operand size has no representable default alignment"); + auto DefaultAlignment = Alignment::create(std::bit_ceil(MinimumBytes)); + if (!DefaultAlignment) + return importError(Begin, DefaultAlignment.error()); + Description.Align = *DefaultAlignment; + } + + if (at(MITokenKind::Identifier)) { + const std::string_view ExpectedPreposition = + IsLoad && IsStore ? "on" : (IsLoad ? "from" : "into"); + if (token().stringValue() != ExpectedPreposition) { + return fail("expected '" + std::string(ExpectedPreposition) + + "' before the machine memory base"); + } + advance(); + if (parseMemoryBase(Description.Base, Description.Offset)) + return true; + if (const GenericConstantData *Constant = Description.Base.constantValue()) + Description.AddressSpace = Constant->Type.AddressSpace; + } + + Alignment BaseAlignment = Description.Align; + std::unordered_set SeenOptions; + while (consume(MITokenKind::Comma)) { + const std::string Name(token().spelling()); + if (!SeenOptions.insert(Name).second) + return fail("duplicate machine memory operand option '" + Name + "'"); + if (atKeyword("align")) { + Alignment ParsedAlignment; + if (parseAlignment(ParsedAlignment)) + return true; + if ((static_cast(Description.Offset) & (ParsedAlignment.bytes() - 1U)) != 0) { + return fail("specified alignment is more aligned than offset"); + } + BaseAlignment = ParsedAlignment; + } else if (atKeyword("basealign")) { + if (parseAlignment(BaseAlignment)) + return true; + } else if (atKeyword("addrspace")) { + advance(); + if (!at(MITokenKind::IntegerLiteral) || parseUnsigned32(token(), Description.AddressSpace) || + Description.AddressSpace > LLT::MaxAddressSpace) { + return fail("invalid machine memory operand address space"); + } + advance(); + } else if (at(MITokenKind::MetadataKeyword)) { + const std::string MetadataKind(token().stringValue()); + advance(); + MachineMetadataReference Reference; + if (parseMetadataReference(Reference)) + return true; + if (MetadataKind == "!tbaa") + Description.TBAA = Reference; + else if (MetadataKind == "!alias.scope") + Description.AliasScope = Reference; + else if (MetadataKind == "!noalias") + Description.NoAlias = Reference; + else if (MetadataKind == "!noalias.addrspace") + Description.NoAliasAddressSpace = Reference; + else if (MetadataKind == "!range") + Description.RangeMetadata = Reference; + else + return fail("unsupported machine memory operand metadata attachment"); + } else { + return fail("unknown machine memory operand option '" + Name + "'"); + } + } + + std::uint64_t EffectiveAlignment = BaseAlignment.bytes(); + if (Description.Offset != 0) { + const auto OffsetBits = static_cast(Description.Offset); + const std::uint64_t OffsetAlignment = std::uint64_t{1} << std::countr_zero(OffsetBits); + EffectiveAlignment = std::min(EffectiveAlignment, OffsetAlignment); + } + Description.Align = *Alignment::create(EffectiveAlignment); + if (BaseAlignment != Description.Align) + Description.BaseAlign = BaseAlignment; + else + Description.BaseAlign.reset(); + + if (expect(MITokenKind::RightParen, "')' after the machine memory operand")) + return true; + + auto Created = Function.createMemOperand(std::move(Description)); + if (!Created) + return importError(Begin, Created.error()); + Result = *Created; + return false; +} + +bool MIParser::Impl::parsePredicateOperand(ParsedOperand &Result, bool IsFloating) { + const MIToken Begin = token(); + advance(); + if (expect(MITokenKind::LeftParen, "'(' after predicate kind")) + return true; + if (!at(MITokenKind::Identifier)) + return fail("expected a predicate spelling"); + + Expected Operand = IsFloating ? [&]() -> Expected { + const auto Predicate = floatingPredicate(token().stringValue()); + if (!Predicate) { + return makeUnexpected(ErrorCode::ParseError, "invalid floating-point predicate"); + } + return MachineOperand::floatingPredicate(*Predicate); + }() + : [&]() -> Expected { + const auto Predicate = integerPredicate(token().stringValue()); + if (!Predicate) { + return makeUnexpected(ErrorCode::ParseError, "invalid integer predicate"); + } + return MachineOperand::integerPredicate(*Predicate); + }(); + if (!Operand) + return importError(token(), Operand.error()); + advance(); + if (expect(MITokenKind::RightParen, "')' after predicate")) + return true; + Result = ParsedOperand{ + std::move(*Operand), std::nullopt, {Begin.range().Begin, token().range().Begin}}; + return false; +} + +bool MIParser::Impl::parseShuffleMaskOperand(ParsedOperand &Result) { + const MIToken Begin = token(); + advance(); + if (expect(MITokenKind::LeftParen, "'(' after shufflemask")) + return true; + std::vector Elements; + while (!at(MITokenKind::RightParen)) { + if (atKeyword("undef")) { + Elements.push_back(ShuffleMaskOperand::Undef); + advance(); + } else { + std::int64_t Value = 0; + if (!at(MITokenKind::IntegerLiteral) || parseSigned64(token(), Value) || + Value < std::numeric_limits::min() || + Value > std::numeric_limits::max()) { + return fail("shuffle mask element must be a 32-bit integer or undef"); + } + Elements.push_back(static_cast(Value)); + advance(); + } + if (!consume(MITokenKind::Comma)) + break; + } + if (expect(MITokenKind::RightParen, "')' after shufflemask")) + return true; + auto Operand = MachineOperand::shuffleMask(std::move(Elements)); + if (!Operand) + return importError(Begin, Operand.error()); + Result = ParsedOperand{ + std::move(*Operand), std::nullopt, {Begin.range().Begin, token().range().Begin}}; + return false; +} + +bool MIParser::Impl::parseDebugInstrRefOperand(ParsedOperand &Result) { + const MIToken Begin = token(); + advance(); + if (expect(MITokenKind::LeftParen, "'(' after dbg-instr-ref")) + return true; + std::uint64_t Instruction = 0; + if (!at(MITokenKind::IntegerLiteral) || parseUnsigned(token(), Instruction)) + return fail("invalid debug instruction number"); + advance(); + if (expect(MITokenKind::Comma, "',' in dbg-instr-ref")) + return true; + std::uint64_t OperandNumber = 0; + if (!at(MITokenKind::IntegerLiteral) || parseUnsigned(token(), OperandNumber)) + return fail("invalid debug operand number"); + advance(); + if (expect(MITokenKind::RightParen, "')' after dbg-instr-ref")) + return true; + auto Operand = MachineOperand::debugInstrRef(Instruction, OperandNumber); + if (!Operand) + return importError(Begin, Operand.error()); + Result = ParsedOperand{ + std::move(*Operand), std::nullopt, {Begin.range().Begin, token().range().Begin}}; + return false; +} + +bool MIParser::Impl::parseLaneMaskOperand(ParsedOperand &Result) { + const MIToken Begin = token(); + advance(); + if (expect(MITokenKind::LeftParen, "'(' after lanemask")) + return true; + std::uint64_t Value = 0; + if (parseUnsigned(token(), Value)) + return fail("invalid 64-bit lane mask"); + advance(); + if (expect(MITokenKind::RightParen, "')' after lanemask")) + return true; + Result = ParsedOperand{MachineOperand::laneMask(LaneMask::fromRaw(Value)), + std::nullopt, + {Begin.range().Begin, token().range().Begin}}; + return false; +} + +bool MIParser::Impl::parseIntrinsicOperand(ParsedOperand &Result) { + const MIToken Begin = token(); + advance(); + if (expect(MITokenKind::LeftParen, "'(' after intrinsic")) + return true; + if (!at(MITokenKind::NamedGlobalValue)) + return fail("expected a named intrinsic"); + const auto ID = Target.mirText().intrinsic(token().stringValue()); + if (!ID) + return fail("unknown intrinsic '" + std::string(token().stringValue()) + "'"); + advance(); + if (expect(MITokenKind::RightParen, "')' after intrinsic")) + return true; + auto Operand = MachineOperand::intrinsic(*ID); + if (!Operand) + return importError(Begin, Operand.error()); + Result = ParsedOperand{ + std::move(*Operand), std::nullopt, {Begin.range().Begin, token().range().Begin}}; + return false; +} + +bool MIParser::Impl::parseTargetIndexOperand(ParsedOperand &Result) { + const MIToken Begin = token(); + advance(); + if (expect(MITokenKind::LeftParen, "'(' after target-index")) + return true; + if (!at(MITokenKind::Identifier)) + return fail("expected a target index name"); + const auto Index = Target.mirText().targetIndex(token().stringValue()); + if (!Index) + return fail("unknown target index '" + std::string(token().stringValue()) + "'"); + advance(); + if (expect(MITokenKind::RightParen, "')' after target index")) + return true; + std::int64_t Offset = 0; + if (parseOptionalOffset(Offset)) + return true; + Result = ParsedOperand{MachineOperand::targetIndex(*Index, Offset), + std::nullopt, + {Begin.range().Begin, token().range().Begin}}; + return false; +} + +bool MIParser::Impl::parseCustomMaskOperand(ParsedOperand &Result) { + const MIToken Begin = token(); + advance(); + if (expect(MITokenKind::LeftParen, "'(' after CustomRegMask")) + return true; + std::uint32_t MaximumRegister = 0; + for (const PhysicalRegisterDesc &Register : Target.registers().physicalRegisters()) + MaximumRegister = std::max(MaximumRegister, Register.Reg.number()); + std::vector Words(MaximumRegister / 64U + 1U, 0); + if (!at(MITokenKind::RightParen)) { + do { + Register Reg; + if (!at(MITokenKind::NamedRegister) || parseRegister(Reg) || !Reg.isPhysical()) + return fail("CustomRegMask requires physical registers"); + const auto Physical = Reg.physicalRegister(); + if (!Physical) + return fail("CustomRegMask requires physical registers"); + const std::uint32_t Number = Physical->number(); + Words[Number / 64U] |= std::uint64_t{1} << (Number % 64U); + } while (consume(MITokenKind::Comma)); + } + if (expect(MITokenKind::RightParen, "')' after CustomRegMask")) + return true; + auto Operand = + MachineOperand::registerMask(std::make_shared(std::move(Words))); + if (!Operand) + return importError(Begin, Operand.error()); + Result = ParsedOperand{ + std::move(*Operand), std::nullopt, {Begin.range().Begin, token().range().Begin}}; + return false; +} + +bool MIParser::Impl::parseLiveOutMaskOperand(ParsedOperand &Result) { + const MIToken Begin = token(); + advance(); + if (expect(MITokenKind::LeftParen, "'(' after liveout")) + return true; + std::uint32_t MaximumRegister = 0; + for (const PhysicalRegisterDesc &Register : Target.registers().physicalRegisters()) + MaximumRegister = std::max(MaximumRegister, Register.Reg.number()); + std::vector Words(MaximumRegister / 64U + 1U, 0); + do { + Register Reg; + if (!at(MITokenKind::NamedRegister) || parseRegister(Reg) || !Reg.isPhysical()) + return fail("liveout requires physical registers"); + const auto Physical = Reg.physicalRegister(); + if (!Physical) + return fail("liveout requires physical registers"); + const std::uint32_t Number = Physical->number(); + Words[Number / 64U] |= std::uint64_t{1} << (Number % 64U); + } while (consume(MITokenKind::Comma)); + if (expect(MITokenKind::RightParen, "')' after liveout")) + return true; + auto Operand = + MachineOperand::liveOutRegisterMask(std::make_shared(std::move(Words))); + if (!Operand) + return importError(Begin, Operand.error()); + Result = ParsedOperand{ + std::move(*Operand), std::nullopt, {Begin.range().Begin, token().range().Begin}}; + return false; +} + +bool MIParser::Impl::parseBlockAddressOperand(ParsedOperand &Result) { + const MIToken Begin = token(); + advance(); + if (expect(MITokenKind::LeftParen, "'(' after blockaddress")) + return true; + if (!at(MITokenKind::GlobalValue) && !at(MITokenKind::NamedGlobalValue)) + return fail("blockaddress requires a function reference"); + const std::string FunctionName = symbolName(token()); + advance(); + if (expect(MITokenKind::Comma, "',' in blockaddress")) + return true; + if (!at(MITokenKind::IRBlock) && !at(MITokenKind::NamedIRBlock)) + return fail("blockaddress requires an IR block reference"); + const std::string BlockName = symbolName(token()); + advance(); + if (expect(MITokenKind::RightParen, "')' after blockaddress")) + return true; + std::int64_t Offset = 0; + if (parseOptionalOffset(Offset)) + return true; + auto Operand = MachineOperand::blockAddress(FunctionName, BlockName, Offset); + if (!Operand) + return importError(Begin, Operand.error()); + Result = ParsedOperand{ + std::move(*Operand), std::nullopt, {Begin.range().Begin, token().range().Begin}}; + return false; +} + +bool MIParser::Impl::parseOperand(bool IsDef, ParsedOperand &Result) { + const MIToken Begin = token(); + if (token().isRegister() || token().isRegisterFlag()) + return parseRegisterOperand(IsDef, Result); + + std::uint32_t TargetFlags = 0; + if (parseTargetFlags(TargetFlags)) + return true; + const MIToken OperandToken = token(); + if (isFloatingSemanticsKeyword(OperandToken)) { + if (parseFloatingImmediateOperand(Result)) + return true; + Result.Operand.setTargetFlags(TargetFlags); + return false; + } + if (OperandToken.kind() == MITokenKind::Exclaim || + (OperandToken.kind() == MITokenKind::MetadataKeyword && + OperandToken.isMetadataKeyword("!DIExpression"))) { + if (parseMetadataOperand(Result)) + return true; + Result.Operand.setTargetFlags(TargetFlags); + return false; + } + if (isCFIOperationToken(OperandToken)) { + if (parseCFIOperand(Result)) + return true; + Result.Operand.setTargetFlags(TargetFlags); + return false; + } + MachineOperand Operand = MachineOperand::immediate(0); + bool HasOperand = true; + + switch (OperandToken.kind()) { + case MITokenKind::IntegerLiteral: { + std::int64_t Value = 0; + if (parseSigned64(OperandToken, Value)) + return fail("integer literal is too large for a machine immediate"); + Operand = MachineOperand::immediate(Value, TargetFlags); + advance(); + break; + } + case MITokenKind::MachineBasicBlock: { + MachineBasicBlock *Block = blockFor(OperandToken); + if (Block == nullptr) + return true; + Operand = MachineOperand::basicBlock(*Block, TargetFlags); + advance(); + break; + } + case MITokenKind::StackObject: + case MITokenKind::FixedStackObject: { + const bool Fixed = OperandToken.kind() == MITokenKind::FixedStackObject; + const auto Index = frameIndexFor(OperandToken, Fixed); + if (!Index) + return true; + advance(); + std::int64_t Offset = 0; + if (parseOptionalOffset(Offset)) + return true; + Operand = MachineOperand::frameIndex(*Index, Offset, TargetFlags); + break; + } + case MITokenKind::ConstantPoolItem: { + const auto Index = constantPoolIndexFor(OperandToken); + if (!Index) + return true; + advance(); + std::int64_t Offset = 0; + if (parseOptionalOffset(Offset)) + return true; + Operand = MachineOperand::constantPool(*Index, Offset, TargetFlags); + break; + } + case MITokenKind::JumpTableIndex: { + const auto Index = jumpTableIndexFor(OperandToken); + if (!Index) + return true; + Operand = MachineOperand::jumpTable(*Index, TargetFlags); + advance(); + break; + } + case MITokenKind::NamedGlobalValue: + case MITokenKind::GlobalValue: { + const std::string Name = symbolName(OperandToken); + advance(); + std::int64_t Offset = 0; + if (parseOptionalOffset(Offset)) + return true; + auto Created = MachineOperand::globalSymbol(Name, Offset, TargetFlags); + if (!Created) + return importError(OperandToken, Created.error()); + Operand = std::move(*Created); + break; + } + case MITokenKind::ExternalSymbol: { + const std::string Name = symbolName(OperandToken); + advance(); + std::int64_t Offset = 0; + if (parseOptionalOffset(Offset)) + return true; + auto Created = MachineOperand::externalSymbol(Name, Offset, TargetFlags); + if (!Created) + return importError(OperandToken, Created.error()); + Operand = std::move(*Created); + break; + } + case MITokenKind::MCSymbol: { + const std::string Name = symbolName(OperandToken); + advance(); + std::int64_t Offset = 0; + if (parseOptionalOffset(Offset)) + return true; + auto Created = MachineOperand::mcSymbol(Name, Offset, TargetFlags); + if (!Created) + return importError(OperandToken, Created.error()); + Operand = std::move(*Created); + break; + } + case MITokenKind::SubRegisterIndex: { + const SubRegIndexDesc *Description = Target.registers().subRegIndex(OperandToken.stringValue()); + if (Description == nullptr) + return fail("unknown subregister index '" + std::string(OperandToken.stringValue()) + "'"); + Operand = + MachineOperand::immediate(static_cast(Description->ID.value()), TargetFlags); + advance(); + break; + } + case MITokenKind::Identifier: { + if (const auto Mask = Target.mirText().registerMask(OperandToken.stringValue())) { + auto Created = MachineOperand::registerMask(Mask, TargetFlags); + if (!Created) + return importError(OperandToken, Created.error()); + Operand = std::move(*Created); + advance(); + } else if (OperandToken.stringValue() == "CustomRegMask") { + if (TargetFlags != 0) + return fail("custom register masks cannot have target flags"); + return parseCustomMaskOperand(Result); + } else if (OperandToken.stringValue().starts_with('i')) { + if (parseTypedIntegerOperand(Result)) + return true; + Result.Operand.setTargetFlags(TargetFlags); + return false; + } else { + return fail("unknown named machine operand '" + std::string(OperandToken.stringValue()) + + "'"); + } + break; + } + case MITokenKind::Keyword: + HasOperand = false; + break; + default: + return fail("expected a machine operand"); + } + + if (!HasOperand) { + bool Failed = false; + if (OperandToken.isKeyword("intpred")) + Failed = parsePredicateOperand(Result, false); + else if (OperandToken.isKeyword("floatpred")) + Failed = parsePredicateOperand(Result, true); + else if (OperandToken.isKeyword("shufflemask")) + Failed = parseShuffleMaskOperand(Result); + else if (OperandToken.isKeyword("dbg-instr-ref")) + Failed = parseDebugInstrRefOperand(Result); + else if (OperandToken.isKeyword("lanemask")) + Failed = parseLaneMaskOperand(Result); + else if (OperandToken.isKeyword("intrinsic")) + Failed = parseIntrinsicOperand(Result); + else if (OperandToken.isKeyword("target-index")) + Failed = parseTargetIndexOperand(Result); + else if (OperandToken.isKeyword("liveout")) + Failed = parseLiveOutMaskOperand(Result); + else if (OperandToken.isKeyword("blockaddress")) + Failed = parseBlockAddressOperand(Result); + else + return fail("unsupported machine operand spelling '" + std::string(OperandToken.spelling()) + + "'"); + if (!Failed) + Result.Operand.setTargetFlags(TargetFlags); + return Failed; + } + + Result = + ParsedOperand{std::move(Operand), std::nullopt, {Begin.range().Begin, token().range().Begin}}; + return false; +} + +bool MIParser::Impl::parseInlineAsmExtraInfoOperand(ParsedOperand &Result) { + const MIToken Begin = token(); + InlineAsmExtraInfo ExtraInfo; + while (at(MITokenKind::Identifier)) { + const auto Bits = InlineAsmExtraInfo::namedFlagBits(token().stringValue()); + if (!Bits) { + return fail("unknown inline asm extra info flag '" + std::string(token().stringValue()) + + "'"); + } + ExtraInfo = InlineAsmExtraInfo(ExtraInfo.rawValue() | *Bits); + advance(); + } + Result = ParsedOperand{ + MachineOperand::immediate(static_cast(ExtraInfo.rawValue())), + std::nullopt, + {Begin.range().Begin, token().range().Begin}, + }; + return false; +} + +bool MIParser::Impl::parseInlineAsmOperandFlag(ParsedOperand &Result) { + const MIToken Begin = token(); + const std::string KindName(token().stringValue()); + const auto Kind = InlineAsmOperandFlag::kindForName(KindName); + if (!Kind) + return fail("unknown inline asm operand kind '" + KindName + "'"); + advance(); + + InlineAsmOperandFlag Flag(*Kind); + if (at(MITokenKind::Identifier) && token().stringValue() == "tiedto") { + advance(); + if (!consume(MITokenKind::Colon)) + return fail("expected ':' after 'tiedto'"); + if (!at(MITokenKind::NamedRegister)) + return fail("expected '$N' operand number after 'tiedto:'"); + std::uint64_t OperandNumber = 0; + if (!parseUnsignedDecimal(token().stringValue(), OperandNumber) || + OperandNumber > std::numeric_limits::max() || + !Flag.setMatchingOperand(static_cast(OperandNumber))) { + return fail("invalid operand number in tiedto constraint"); + } + advance(); + } else if (consume(MITokenKind::Colon)) { + if (!at(MITokenKind::Identifier)) + return fail("expected register class or memory constraint name after ':'"); + const std::string Constraint(token().stringValue()); + if (*Kind == InlineAsmOperandKind::Memory) { + const auto MemoryConstraint = InlineAsmOperandFlag::memoryConstraintForName(Constraint); + if (!MemoryConstraint) + return fail("unknown memory constraint '" + Constraint + "'"); + if (!Flag.setMemoryConstraint(*MemoryConstraint)) + return fail("invalid memory constraint encoding"); + } else if (*Kind == InlineAsmOperandKind::RegisterDef || + *Kind == InlineAsmOperandKind::RegisterUse || + *Kind == InlineAsmOperandKind::RegisterDefEarlyClobber) { + const RegisterClassDesc *RegisterClass = Target.registers().registerClass(Constraint); + if (RegisterClass == nullptr) + return fail("unknown register class '" + Constraint + "'"); + if (!Flag.setRegisterClass(RegisterClass->ID.value())) + return fail("register class cannot be encoded in an inline asm operand flag"); + } + advance(); + } + + Result = ParsedOperand{ + MachineOperand::immediate(static_cast(Flag.rawValue())), + std::nullopt, + {Begin.range().Begin, token().range().Begin}, + }; + return false; +} + +bool MIParser::Impl::parseBlockLiveIns(MachineBasicBlock &Block) { + advance(); + if (expect(MITokenKind::Colon, "':' after liveins")) + return true; + if (token().isNewlineOrEnd()) + return false; + do { + if (!at(MITokenKind::NamedRegister)) + return fail("expected a physical register in liveins"); + Register Reg; + if (parseRegister(Reg) || !Reg.isPhysical()) + return true; + const auto Physical = Reg.physicalRegister(); + if (!Physical) + return fail("expected a physical register in liveins"); + LaneMask Lanes = LaneMask::all(); + if (consume(MITokenKind::Colon)) { + std::uint64_t Raw = 0; + if (parseUnsigned(token(), Raw)) + return fail("invalid live-in lane mask"); + Lanes = LaneMask::fromRaw(Raw); + advance(); + } + auto Added = Block.addLiveIn(*Physical, Lanes); + if (!Added) + return importError(token(), Added.error()); + } while (consume(MITokenKind::Comma)); + return false; +} + +bool MIParser::Impl::parseBlockSuccessors(MachineBasicBlock &Block) { + advance(); + if (expect(MITokenKind::Colon, "':' after successors")) + return true; + ExplicitSuccessorBlocks.insert(&Block); + if (token().isNewlineOrEnd()) + return false; + do { + if (!at(MITokenKind::MachineBasicBlock)) + return fail("expected a machine basic block reference"); + MachineBasicBlock *Successor = blockFor(token()); + if (Successor == nullptr) + return true; + advance(); + BranchProbability Probability = BranchProbability::zero(); + if (consume(MITokenKind::LeftParen)) { + std::uint32_t Weight = 0; + if (parseUnsigned32(token(), Weight)) + return fail("expected unsigned integer"); + Probability = BranchProbability::fromRaw(Weight); + advance(); + if (expect(MITokenKind::RightParen, "')' after successor weight")) + return true; + } + auto Added = Function.cfgUpdater().addSuccessor(Block, *Successor, Probability); + if (!Added) + return importError(token(), Added.error()); + } while (consume(MITokenKind::Comma)); + return false; +} + +bool MIParser::Impl::parseInstruction(MachineBasicBlock &Block, bool InBundle, + MachineInstr *PreviousInstruction, MachineInstr *&Result) { + if (ParsedInstructionCount >= Options.MaximumInstructions) + return fail("machine instruction count exceeds parser limit"); + + std::vector Operands; + while (token().isRegister() || token().isRegisterFlag()) { + ParsedOperand Operand{MachineOperand::immediate(0), std::nullopt, {}}; + if (parseRegisterOperand(true, Operand)) + return true; + Operands.push_back(std::move(Operand)); + if (!consume(MITokenKind::Comma)) + break; + } + if (!Operands.empty() && expect(MITokenKind::Equal, "'=' after register definitions")) + return true; + + MachineInstrFlags Flags; + while (isInstructionFlag(token())) { + const auto Flag = instructionFlag(token().stringValue()); + if (!Flag) + return fail("unknown instruction flag"); + if (Flags.has(*Flag)) + return fail("duplicate instruction flag '" + std::string(token().stringValue()) + "'"); + Flags.set(*Flag); + advance(); + } + if (!at(MITokenKind::Identifier)) + return fail("expected a machine instruction name"); + const MIToken OpcodeToken = token(); + const InstructionDescriptor *Descriptor = + Target.instructions().descriptor(OpcodeToken.stringValue()); + if (Descriptor == nullptr) + return fail("unknown machine instruction '" + std::string(OpcodeToken.stringValue()) + "'"); + advance(); + const bool IsInlineAsm = Descriptor->Name == "INLINEASM" || Descriptor->Name == "INLINEASM_BR"; + + while (!token().isNewlineOrEnd() && !at(MITokenKind::DoubleColon) && + !at(MITokenKind::LeftBrace) && !atKeyword("debug-instr-number") && + !atKeyword("debug-location") && !atKeyword("pre-instr-symbol") && + !atKeyword("post-instr-symbol") && !atKeyword("heap-alloc-marker") && + !atKeyword("pcsections") && !atKeyword("mmra") && !atKeyword("cfi-type") && + !atKeyword("deactivation-symbol")) { + if (Operands.size() >= Options.MaximumOperandsPerInstruction) + return fail("instruction operand count exceeds parser limit"); + ParsedOperand Operand{MachineOperand::immediate(0), std::nullopt, {}}; + const bool IsInlineAsmExtraInfo = IsInlineAsm && + Operands.size() == InlineAsmExtraInfoOperandIndex && + at(MITokenKind::Identifier); + const bool IsInlineAsmOperandFlag = IsInlineAsm && + Operands.size() >= InlineAsmFirstConstraintOperandIndex && + at(MITokenKind::Identifier); + if (IsInlineAsmExtraInfo ? parseInlineAsmExtraInfoOperand(Operand) + : (IsInlineAsmOperandFlag ? parseInlineAsmOperandFlag(Operand) + : parseOperand(false, Operand))) + return true; + Operands.push_back(std::move(Operand)); + if (token().isNewlineOrEnd() || at(MITokenKind::DoubleColon) || at(MITokenKind::LeftBrace) || + atKeyword("debug-instr-number") || atKeyword("debug-location")) + break; + if (expect(MITokenKind::Comma, "',' before the next machine operand")) + return true; + } + + std::uint32_t DebugNumber = 0; + std::optional DebugLocation; + std::optional PreInstrSymbol; + std::optional PostInstrSymbol; + std::optional HeapAllocMarker; + std::optional PCSections; + std::optional MMRA; + std::optional CFIType; + std::optional DeactivationSymbol; + std::unordered_set SeenAttachments; + while (atKeyword("debug-instr-number") || atKeyword("debug-location") || + atKeyword("pre-instr-symbol") || atKeyword("post-instr-symbol") || + atKeyword("heap-alloc-marker") || atKeyword("pcsections") || atKeyword("mmra") || + atKeyword("cfi-type") || atKeyword("deactivation-symbol")) { + const std::string Name(token().stringValue()); + if (!SeenAttachments.insert(Name).second) + return fail("duplicate instruction attachment '" + Name + "'"); + advance(); + if (Name == "debug-instr-number") { + if (!at(MITokenKind::IntegerLiteral) || parseUnsigned32(token(), DebugNumber) || + DebugNumber == 0) { + return fail("invalid debug instruction number"); + } + advance(); + } else if (Name == "debug-location") { + if (at(MITokenKind::MetadataKeyword) && token().isMetadataKeyword("!DILocation")) { + std::vector InlineChain; + std::optional TerminalInlinedAt; + if (parseInlineDILocation(InlineChain, TerminalInlinedAt)) + return true; + auto Location = MachineDILocation::create(std::move(InlineChain), TerminalInlinedAt); + if (!Location) + return importError(OpcodeToken, Location.error()); + DebugLocation = MachineDebugLocation::inlineLocation(std::move(*Location)); + } else { + MachineMetadataReference Reference; + if (parseMetadataReference(Reference)) + return true; + auto Created = MachineDebugLocation::reference(Reference); + if (!Created) + return importError(OpcodeToken, Created.error()); + DebugLocation = std::move(*Created); + } + } else if (Name == "pre-instr-symbol" || Name == "post-instr-symbol") { + if (!at(MITokenKind::MCSymbol)) + return fail("expected an MC symbol after '" + Name + "'"); + if (Name == "pre-instr-symbol") + PreInstrSymbol = symbolName(token()); + else + PostInstrSymbol = symbolName(token()); + advance(); + } else if (Name == "heap-alloc-marker" || Name == "pcsections" || Name == "mmra") { + MachineMetadataReference Reference; + if (parseMetadataReference(Reference)) + return true; + if (Name == "heap-alloc-marker") + HeapAllocMarker = Reference; + else if (Name == "pcsections") + PCSections = Reference; + else + MMRA = Reference; + } else if (Name == "cfi-type") { + std::uint32_t Type = 0; + if (!at(MITokenKind::IntegerLiteral) || parseUnsigned32(token(), Type) || Type == 0) + return fail("invalid nonzero CFI type"); + CFIType = Type; + advance(); + } else { + if (!at(MITokenKind::GlobalValue) && !at(MITokenKind::NamedGlobalValue)) + return fail("expected a global value after 'deactivation-symbol'"); + DeactivationSymbol = symbolName(token()); + advance(); + } + static_cast(consume(MITokenKind::Comma)); + } + std::vector MemOperands; + if (consume(MITokenKind::DoubleColon)) { + while (!token().isNewlineOrEnd() && !at(MITokenKind::LeftBrace)) { + if (MemOperands.size() >= Options.MaximumMemoryOperandsPerInstruction) + return fail("machine memory operand count exceeds parser limit"); + const MachineMemOperand *MemOperand = nullptr; + if (parseMemoryOperand(MemOperand, Descriptor->InstructionOpcode)) + return true; + MemOperands.push_back(MemOperand); + if (token().isNewlineOrEnd() || at(MITokenKind::LeftBrace)) + break; + if (expect(MITokenKind::Comma, "',' before the next machine memory operand")) + return true; + } + } + + auto Created = Function.createInstruction(Descriptor->InstructionOpcode); + if (!Created) + return importError(OpcodeToken, Created.error()); + MachineInstr *Instruction = *Created; + Instruction->setFlags(Flags); + for (ParsedOperand &Parsed : Operands) { + auto Added = Instruction->addOperand(std::move(Parsed.Operand)); + if (!Added) + return importError(OpcodeToken, Added.error()); + } + for (std::size_t Index = 0; Index < Operands.size(); ++Index) { + if (!Operands[Index].TiedDef) + continue; + if (*Operands[Index].TiedDef >= Operands.size()) + return fail(OpcodeToken, "tied-def operand index is out of range"); + auto Tied = Instruction->tieOperands(*Operands[Index].TiedDef, Index); + if (!Tied) + return importError(OpcodeToken, Tied.error()); + } + for (const MachineMemOperand *MemOperand : MemOperands) { + auto Added = Instruction->addMemOperand(*MemOperand); + if (!Added) + return importError(OpcodeToken, Added.error()); + } + if (DebugNumber != 0) { + auto SetNumber = Instruction->setDebugInstrNumber(DebugNumber); + if (!SetNumber) + return importError(OpcodeToken, SetNumber.error()); + } + if (DebugLocation) { + auto SetLocation = Instruction->setDebugLocation(std::move(*DebugLocation)); + if (!SetLocation) + return importError(OpcodeToken, SetLocation.error()); + } + if (PreInstrSymbol) { + auto SetSymbol = Instruction->setPreInstrSymbol(std::move(*PreInstrSymbol)); + if (!SetSymbol) + return importError(OpcodeToken, SetSymbol.error()); + } + if (PostInstrSymbol) { + auto SetSymbol = Instruction->setPostInstrSymbol(std::move(*PostInstrSymbol)); + if (!SetSymbol) + return importError(OpcodeToken, SetSymbol.error()); + } + if (HeapAllocMarker) { + auto SetMarker = Instruction->setHeapAllocMarker(*HeapAllocMarker); + if (!SetMarker) + return importError(OpcodeToken, SetMarker.error()); + } + if (PCSections) { + auto SetSections = Instruction->setPCSections(*PCSections); + if (!SetSections) + return importError(OpcodeToken, SetSections.error()); + } + if (MMRA) { + auto SetMMRA = Instruction->setMMRA(*MMRA); + if (!SetMMRA) + return importError(OpcodeToken, SetMMRA.error()); + } + if (CFIType) { + auto SetType = Instruction->setCFIType(*CFIType); + if (!SetType) + return importError(OpcodeToken, SetType.error()); + } + if (DeactivationSymbol) { + auto SetSymbol = Instruction->setDeactivationSymbol(std::move(*DeactivationSymbol)); + if (!SetSymbol) + return importError(OpcodeToken, SetSymbol.error()); + } + if (InBundle) { + Flags.set(MachineInstrFlag::BundledWithPred); + Instruction->setFlags(Flags); + if (PreviousInstruction != nullptr) { + MachineInstrFlags PreviousFlags = PreviousInstruction->flags(); + PreviousFlags.set(MachineInstrFlag::BundledWithSucc); + PreviousInstruction->setFlags(PreviousFlags); + } + } + auto Appended = Block.appendInstruction(*Instruction); + if (!Appended) + return importError(OpcodeToken, Appended.error()); + + ++ParsedInstructionCount; + Result = Instruction; + return false; +} + +bool MIParser::Impl::parseSecondPass() { + Position = 0; + while (consume(MITokenKind::Newline)) { + } + while (!at(MITokenKind::EndOfFile)) { + if (!at(MITokenKind::MachineBasicBlockLabel)) + return fail("expected a machine basic block definition"); + std::uint32_t ID = 0; + if (parseBlockDefinition(false, ID)) + return true; + MachineBasicBlock *Block = Blocks.at(ID); + + while (consume(MITokenKind::Newline)) { + } + while (atKeyword("liveins") || atKeyword("successors")) { + if ((atKeyword("liveins") && parseBlockLiveIns(*Block)) || + (atKeyword("successors") && parseBlockSuccessors(*Block))) { + return true; + } + if (!token().isNewlineOrEnd()) + return fail("expected a line break after the block list"); + while (consume(MITokenKind::Newline)) { + } + } + + bool InBundle = false; + MachineInstr *PreviousInstruction = nullptr; + while (!at(MITokenKind::MachineBasicBlockLabel) && !at(MITokenKind::EndOfFile)) { + if (consume(MITokenKind::Newline)) + continue; + if (consume(MITokenKind::RightBrace)) { + if (!InBundle) + return fail("closing brace without an active instruction bundle"); + InBundle = false; + continue; + } + + MachineInstr *Instruction = nullptr; + if (parseInstruction(*Block, InBundle, PreviousInstruction, Instruction)) + return true; + PreviousInstruction = Instruction; + if (consume(MITokenKind::LeftBrace)) { + if (InBundle) + return fail("nested instruction bundles are not allowed"); + MachineInstrFlags BundleFlags = Instruction->flags(); + BundleFlags.set(MachineInstrFlag::BundledWithSucc); + Instruction->setFlags(BundleFlags); + InBundle = true; + } + if (!token().isNewlineOrEnd() && !at(MITokenKind::RightBrace)) + return fail("expected a line break after the machine instruction"); + } + if (InBundle) + return fail("unterminated instruction bundle"); + } + return false; +} + +bool MIParser::Impl::inferSuccessors() { + for (MachineBasicBlock *Block = Function.firstBlock(); Block != nullptr; Block = Block->next()) { + if (ExplicitSuccessorBlocks.contains(Block)) + continue; + + bool BlocksFallthrough = true; + std::vector Successors; + for (MachineInstr *Instruction = Block->firstInstruction(); Instruction != nullptr; + Instruction = Instruction->next()) { + const InstructionDescriptor *Descriptor = + Target.instructions().descriptor(Instruction->opcode()); + if (Descriptor == nullptr) + return fail("instruction has no target descriptor while inferring successors"); + if (Descriptor->Flags.has(InstructionFlag::Branch)) { + for (const MachineOperand &Operand : Instruction->operands()) { + if (const BasicBlockOperand *TargetBlock = Operand.getIf()) { + if (std::ranges::find(Successors, TargetBlock->Block) == Successors.end()) + Successors.push_back(TargetBlock->Block); + } + } + } + if (Descriptor->Flags.has(InstructionFlag::Barrier)) + BlocksFallthrough = false; + } + if (BlocksFallthrough && Block->next() != nullptr && + std::ranges::find(Successors, Block->next()) == Successors.end()) + Successors.push_back(Block->next()); + if (Successors.empty()) + continue; + const auto Probability = + BranchProbability::fromFraction(1U, static_cast(Successors.size())); + if (!Probability) + return importError(token(), Probability.error()); + for (MachineBasicBlock *Successor : Successors) { + auto Added = Function.cfgUpdater().addSuccessor(*Block, *Successor, *Probability); + if (!Added) + return importError(token(), Added.error()); + } + } + return false; +} + +Expected MIParser::Impl::parse() { + if (ParseAttempted) + return makeUnexpected(ErrorCode::InvalidArgument, "MI parser can only be run once"); + ParseAttempted = true; + if (Function.blockCount() != 0 || !Function.allInstructions().empty()) { + return makeUnexpected(ErrorCode::InvalidArgument, + "MI parser requires a staging machine function with an empty body"); + } + for (const MIVirtualRegisterSlot &Slot : Context.VirtualRegisters) { + if (!Slot.Reg.isVirtual() || !Function.registerInfo().isKnownVirtualRegister(Slot.Reg)) { + return makeUnexpected(ErrorCode::InvalidArgument, + "MI parser virtual-register slot maps to an unknown register"); + } + if (!NumericVirtualRegisters.emplace(Slot.TextID, Slot.Reg).second) { + return makeUnexpected(ErrorCode::InvalidArgument, + "MI parser virtual-register text IDs must be unique"); + } + } + if (tokenize()) + return failureResult(); + if (parseFirstPass() || parseSecondPass() || inferSuccessors()) + return failureResult(); + + Function.renumberBlocks(); + if (Options.VerifyAfterParse) { + auto Verified = + MachineVerifier::verify(Function, Target, + MachineVerifierOptions{.Mode = MachineVerifierMode::Generic, + .Stage = std::nullopt, + .VerifyProperties = false}); + if (!Verified) { + static_cast(fail(token(), "parsed machine function failed verification: " + + std::string(Verified.error().message()))); + return failureResult(); + } + } + return {}; +} + +MIParser::MIParser(MachineFunction &Function, const TargetDescription &Target, + std::string SourceName, std::string SourceText, MIParserContext Context, + MIParserOptions Options) + : Implementation(std::make_unique(Function, Target, std::move(SourceName), + std::move(SourceText), std::move(Context), Options)) {} + +MIParser::MIParser(MIParser &&) noexcept = default; + +MIParser &MIParser::operator=(MIParser &&) noexcept = default; + +MIParser::~MIParser() = default; + +Expected MIParser::parse() { + return Implementation->parse(); +} + +const TextSource &MIParser::source() const noexcept { + return Implementation->source(); +} + +std::span MIParser::diagnostics() const noexcept { + return Implementation->diagnostics(); +} + +bool MIParser::failed() const noexcept { + return Implementation->failed(); +} + +} // namespace rcc::mir diff --git a/backend/lib/CodeGen/MIRParser/MIRParser.cpp b/backend/lib/CodeGen/MIRParser/MIRParser.cpp new file mode 100644 index 0000000..e22f1bc --- /dev/null +++ b/backend/lib/CodeGen/MIRParser/MIRParser.cpp @@ -0,0 +1,334 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "rcc/CodeGen/MIRParser/MIRParser.h" + +#include "rcc/CodeGen/MIRYamlMapping.h" +#include "rcc/CodeGen/MachineBasicBlock.h" +#include "rcc/CodeGen/MachineConstantPool.h" +#include "rcc/CodeGen/MachineFrameInfo.h" +#include "rcc/CodeGen/MachineFunction.h" +#include "rcc/CodeGen/MachineFunctionAttributes.h" +#include "rcc/CodeGen/MachineFunctionProperties.h" +#include "rcc/CodeGen/MachineFunctionRecords.h" +#include "rcc/CodeGen/MachineInstr.h" +#include "rcc/CodeGen/MachineJumpTableInfo.h" +#include "rcc/CodeGen/MachineMetadata.h" +#include "rcc/CodeGen/MachineRegisterInfo.h" +#include "rcc/CodeGen/MachineVerifier.h" +#include "rcc/CodeGen/TargetDescription.h" +#include "rcc/CodeGen/TargetMIRTextInfo.h" +#include "rcc/CodeGen/TargetRegisterInfo.h" +#include "rcc/MIR/LLVMConstantSyntax.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace rcc::mir { + +bool MIRDocumentParser::fail(const TextRange &Range, std::string Message, std::string Category) { + if (Result.Diagnostics.empty()) { + Result.Diagnostics.push_back(TextDiagnostic{ + .Severity = DiagnosticSeverity::Error, + .Category = std::move(Category), + .Message = std::move(Message), + .SourceName = std::string(Result.Source.name()), + .Range = Range, + }); + } + return true; +} + +bool MIRDocumentParser::importError(const TextRange &Range, const Error &Failure, + std::string Category) { + return fail(Range, std::string(Failure.message()), std::move(Category)); +} + +const yaml::Node *MIRDocumentParser::mappingValue(const yaml::Node &Mapping, + std::string_view Key) const noexcept { + const auto Value = Mapping.mappingValue(Key); + return Value ? node(*Value) : nullptr; +} + +std::string_view MIRDocumentParser::effectiveDataLayout() const noexcept { + if (!Options.DataLayout.empty()) + return Options.DataLayout; + if (ActiveDocument != nullptr) { + if (const EmbeddedIRReferenceIndex *Embedded = ActiveDocument->embeddedIR()) { + if (const auto DataLayout = Embedded->dataLayout()) + return *DataLayout; + } + } + return {}; +} + +LLVMConstantSyntaxOptions MIRDocumentParser::pointerLayoutOptions() const noexcept { + return { + .DataLayout = effectiveDataLayout(), + .NamedTypes = {}, + }; +} + +bool MIRDocumentParser::requireKind(const yaml::Node &Node, yaml::NodeKind Kind, + std::string_view Description) { + if (Node.kind() == Kind) + return false; + return fail(Node.range(), std::string(Description) + " has the wrong YAML node kind"); +} + +bool MIRDocumentParser::validateKeys(const yaml::Node &Mapping, + std::span Keys, + std::string_view Description) { + for (const yaml::MappingEntry &Entry : Mapping.mapping()) { + if (std::ranges::find(Keys, Entry.Key) == Keys.end()) { + return fail(Entry.KeyRange, + "unknown " + std::string(Description) + " key '" + Entry.Key + "'"); + } + } + return false; +} + +bool MIRDocumentParser::scalar(const yaml::Node &Node, std::string_view &Value, + std::string_view Description) { + if (requireKind(Node, yaml::NodeKind::Scalar, Description)) + return true; + Value = Node.scalar(); + return false; +} + +MIRDocumentParseResult MIRDocumentParser::run() { + yaml::ParseResult YAMLResult = yaml::parse(std::string(Result.Source.name()), + std::string(Result.Source.text()), Options.YAML); + if (!YAMLResult.succeeded()) { + const auto Diagnostics = YAMLResult.diagnostics(); + Result.Diagnostics.assign(Diagnostics.begin(), Diagnostics.end()); + return std::move(Result); + } + Stream = YAMLResult.stream(); + if (Stream == nullptr || Stream->documents().empty()) { + static_cast(fail(Result.Source.range(0, 0), "MIR stream contains no YAML documents")); + return std::move(Result); + } + + MIRDocument Staging; + ActiveDocument = &Staging; + const auto IsEmptyDocument = [this](std::size_t Index) { + const yaml::Node *Root = node(Stream->documents()[Index].Root); + return Root != nullptr && Root->kind() == yaml::NodeKind::Null && + Root->range().Begin.Offset == Root->range().End.Offset; + }; + std::size_t FirstContent = 0; + while (FirstContent < Stream->documents().size() && IsEmptyDocument(FirstContent)) + ++FirstContent; + if (FirstContent == Stream->documents().size()) { + static_cast(fail(Stream->documents().front().Range, + "MIR stream must contain at least one machine function document")); + return std::move(Result); + } + + std::size_t FirstFunction = FirstContent; + const yaml::Node *FirstRoot = node(Stream->documents()[FirstContent].Root); + if (FirstRoot != nullptr && FirstRoot->kind() == yaml::NodeKind::Scalar) { + EmbeddedIRIndexResult Embedded = indexEmbeddedIR( + std::string(Result.Source.name()) + ":embedded-ir", std::string(FirstRoot->scalar())); + if (!Embedded.succeeded()) { + const auto Diagnostics = Embedded.diagnostics(); + if (Diagnostics.empty()) { + static_cast( + fail(FirstRoot->range(), "embedded LLVM IR indexing failed", "mir-embedded-ir")); + } else { + const TextDiagnostic &Diagnostic = Diagnostics.front(); + const TextRange Range = scalarDiagnosticRange(*FirstRoot, Diagnostic.Range); + static_cast(fail(Range, Diagnostic.Message, Diagnostic.Category)); + } + return std::move(Result); + } + Staging.EmbeddedIR = std::move(Embedded).takeIndex(); + FirstFunction = FirstContent + 1; + } + + if (!effectiveDataLayout().empty()) { + auto PointerSize = llvmPointerSizeInBits(0, pointerLayoutOptions()); + if (!PointerSize) { + static_cast(importError(Stream->documents()[FirstContent].Range, PointerSize.error(), + "mir-data-layout")); + return std::move(Result); + } + } + + bool SawFunction = false; + for (std::size_t Index = FirstFunction; Index < Stream->documents().size(); ++Index) { + if (IsEmptyDocument(Index)) + continue; + const yaml::Node *Root = node(Stream->documents()[Index].Root); + if (Root == nullptr || requireKind(*Root, yaml::NodeKind::Mapping, "machine function") || + parseFunction(*Root, Staging)) { + return std::move(Result); + } + SawFunction = true; + } + if (!SawFunction) { + static_cast(fail(Stream->documents()[FirstContent].Range, + "MIR stream must contain at least one machine function document")); + return std::move(Result); + } + + auto UnitResult = Staging.Unit.verify(); + if (!UnitResult) { + static_cast( + fail(Stream->documents().back().Range, "parsed compilation unit failed verification: " + + std::string(UnitResult.error().message()))); + return std::move(Result); + } + ActiveDocument = nullptr; + Result.Document = std::move(Staging); + return std::move(Result); +} + +bool MIRDocumentParser::parseFunction(const yaml::Node &Root, MIRDocument &Document) { + constexpr std::array Keys{ + std::string_view{"name"}, + std::string_view{"alignment"}, + std::string_view{"exposesReturnsTwice"}, + std::string_view{"legalized"}, + std::string_view{"regBankSelected"}, + std::string_view{"selected"}, + std::string_view{"failedISel"}, + std::string_view{"tracksRegLiveness"}, + std::string_view{"hasWinCFI"}, + std::string_view{"noPhis"}, + std::string_view{"isSSA"}, + std::string_view{"noVRegs"}, + std::string_view{"hasFakeUses"}, + std::string_view{"callsEHReturn"}, + std::string_view{"callsUnwindInit"}, + std::string_view{"hasEHContTarget"}, + std::string_view{"hasEHScopes"}, + std::string_view{"hasEHFunclets"}, + std::string_view{"isOutlined"}, + std::string_view{"debugInstrRef"}, + std::string_view{"failsVerification"}, + std::string_view{"tracksDebugUserValues"}, + std::string_view{"registers"}, + std::string_view{"liveins"}, + std::string_view{"calleeSavedRegisters"}, + std::string_view{"frameInfo"}, + std::string_view{"fixedStack"}, + std::string_view{"stack"}, + std::string_view{"entry_values"}, + std::string_view{"callSites"}, + std::string_view{"debugValueSubstitutions"}, + std::string_view{"constants"}, + std::string_view{"machineFunctionInfo"}, + std::string_view{"jumpTable"}, + std::string_view{"machineMetadataNodes"}, + std::string_view{"calledGlobals"}, + std::string_view{"prefetch-targets"}, + std::string_view{"body"}, + }; + const yaml::Node *NameNode = mappingValue(Root, "name"); + if (NameNode == nullptr) + return fail(Root.range(), "missing required key 'name'"); + if (validateKeys(Root, Keys, "machine function")) + return true; + std::string_view Name; + if (scalar(*NameNode, Name, "machine function name")) + return true; + + const EmbeddedIRFunction *IRFunction = nullptr; + if (Document.EmbeddedIR) { + if (Name.empty()) { + std::size_t AnonymousOrdinal = 0; + for (std::size_t Index = 0; Index < Document.Unit.functionCount(); ++Index) { + const MachineFunction *Existing = Document.Unit.functionAt(Index); + if (Existing != nullptr && Existing->name().empty()) + ++AnonymousOrdinal; + } + IRFunction = Document.EmbeddedIR->unnamedFunction(AnonymousOrdinal); + } else { + IRFunction = Document.EmbeddedIR->function(Name); + } + if (IRFunction == nullptr) + return fail(NameNode->range(), + "function '" + std::string(Name) + "' isn't defined in the provided LLVM IR"); + } + + if (!Name.empty() && Document.Unit.function(Name) != nullptr) + return fail(NameNode->range(), "redefinition of machine function '" + std::string(Name) + "'"); + + auto Created = Document.Unit.createMachineFunction(std::string(Name)); + if (!Created) + return importError(NameNode->range(), Created.error()); + MachineFunction &Function = **Created; + ActiveFunction = &Function; + + MIRFunctionDocumentInfo DocumentInfo{ + .Function = Function.stableID(), + .NoPHIs = std::nullopt, + .IsSSA = std::nullopt, + .NoVRegs = std::nullopt, + .HasFakeUses = std::nullopt, + }; + MIParserContext Context; + if (IRFunction != nullptr) { + Context.HasIRBlock = [IRFunction](std::string_view BlockName) { + return IRFunction->block(BlockName) != nullptr; + }; + } + + std::vector PendingJumpTables; + if (parseFunctionScalars(Root, Function, DocumentInfo) || + parseMetadata(Root, Function, Context) || parseRegisters(Root, Function, Context) || + parseStackObjects(Root, Function, Context) || parseConstants(Root, Function, Context) || + reserveJumpTables(Root, Function, Context, PendingJumpTables) || + parseBody(Root, Function, Context) || defineJumpTables(Function, PendingJumpTables) || + parseFrameState(Root, Function, Context) || + recomputeProperties(Root, Function, DocumentInfo) || parseTargetState(Root, Function) || + parseFunctionRecords(Root, Function) || validateDocumentReferences(Function)) { + return true; + } + + if (Options.VerifyMachineFunctions) { + auto Verified = MachineVerifier::verify(Function, Target); + if (!Verified) + return importError(Root.range(), Verified.error(), "mir-verifier"); + } + Document.FunctionInfo.push_back(DocumentInfo); + ActiveFunction = nullptr; + return false; +} + +} // namespace rcc::mir + +namespace rcc::mir { + +MIRDocument::MIRDocument() = default; +MIRDocument::MIRDocument(CompilationUnit Unit) : Unit(std::move(Unit)) {} +MIRDocument::MIRDocument(MIRDocument &&) noexcept = default; +MIRDocument &MIRDocument::operator=(MIRDocument &&) noexcept = default; +MIRDocument::~MIRDocument() = default; + +MIRDocumentParseResult::MIRDocumentParseResult(MIRDocumentParseResult &&) noexcept = default; +MIRDocumentParseResult & +MIRDocumentParseResult::operator=(MIRDocumentParseResult &&) noexcept = default; +MIRDocumentParseResult::~MIRDocumentParseResult() = default; + +MIRDocumentParseResult parseMIRDocument(const TargetDescription &Target, std::string SourceName, + std::string SourceText, MIRDocumentParserOptions Options) { + return MIRDocumentParser(Target, std::move(SourceName), std::move(SourceText), std::move(Options)) + .run(); +} + +} // namespace rcc::mir diff --git a/backend/lib/CodeGen/MIRPrinter.cpp b/backend/lib/CodeGen/MIRPrinter.cpp new file mode 100644 index 0000000..d510c83 --- /dev/null +++ b/backend/lib/CodeGen/MIRPrinter.cpp @@ -0,0 +1,3167 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "rcc/CodeGen/MIRPrinter.h" + +#include "rcc/CodeGen/InlineAsm.h" +#include "rcc/CodeGen/InstructionDescriptor.h" +#include "rcc/CodeGen/MIRYamlMapping.h" +#include "rcc/CodeGen/MachineBasicBlock.h" +#include "rcc/CodeGen/MachineCFIInfo.h" +#include "rcc/CodeGen/MachineCompilationStatus.h" +#include "rcc/CodeGen/MachineConstantPool.h" +#include "rcc/CodeGen/MachineFrameInfo.h" +#include "rcc/CodeGen/MachineFunction.h" +#include "rcc/CodeGen/MachineFunctionAttributes.h" +#include "rcc/CodeGen/MachineFunctionProperties.h" +#include "rcc/CodeGen/MachineFunctionRecords.h" +#include "rcc/CodeGen/MachineInstr.h" +#include "rcc/CodeGen/MachineJumpTableInfo.h" +#include "rcc/CodeGen/MachineMemOperand.h" +#include "rcc/CodeGen/MachineMetadata.h" +#include "rcc/CodeGen/MachineOperand.h" +#include "rcc/CodeGen/MachineRegisterInfo.h" +#include "rcc/CodeGen/MachineVerifier.h" +#include "rcc/CodeGen/TargetDescription.h" +#include "rcc/CodeGen/TargetMIRTextInfo.h" +#include "rcc/CodeGen/TargetRegisterInfo.h" +#include "rcc/MIR/LLVMConstantSyntax.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace rcc::mir { +namespace { + +enum class YAMLQuoting : std::uint8_t { + None, + Single, + Double, +}; + +[[nodiscard]] bool isASCIIAlphanumeric(unsigned char Character) noexcept { + return (Character >= '0' && Character <= '9') || (Character >= 'A' && Character <= 'Z') || + (Character >= 'a' && Character <= 'z'); +} + +[[nodiscard]] bool isYAMLNumeric(std::string_view Value) noexcept { + if (Value.empty() || Value == "+" || Value == "-") + return false; + if (Value == ".nan" || Value == ".NaN" || Value == ".NAN") + return true; + + std::string_view Tail = Value; + if (Tail.front() == '+' || Tail.front() == '-') + Tail.remove_prefix(1); + if (Tail == ".inf" || Tail == ".Inf" || Tail == ".INF") + return true; + if (Value.starts_with("0o")) { + return Value.size() > 2U && + Value.substr(2).find_first_not_of("01234567") == std::string_view::npos; + } + if (Value.starts_with("0x")) { + return Value.size() > 2U && + Value.substr(2).find_first_not_of("0123456789abcdefABCDEF") == std::string_view::npos; + } + + if (Tail.empty() || Tail.front() == 'e' || Tail.front() == 'E') + return false; + std::size_t Position = 0; + if (Tail.front() == '.') { + ++Position; + if (Position == Tail.size() || Tail[Position] < '0' || Tail[Position] > '9') + return false; + while (Position < Tail.size() && Tail[Position] >= '0' && Tail[Position] <= '9') + ++Position; + } else { + while (Position < Tail.size() && Tail[Position] >= '0' && Tail[Position] <= '9') + ++Position; + if (Position == 0) + return false; + } + + if (Position < Tail.size() && Tail[Position] == '.') { + ++Position; + while (Position < Tail.size() && Tail[Position] >= '0' && Tail[Position] <= '9') + ++Position; + } + if (Position == Tail.size()) + return true; + if (Tail[Position] != 'e' && Tail[Position] != 'E') + return false; + ++Position; + if (Position < Tail.size() && (Tail[Position] == '+' || Tail[Position] == '-')) + ++Position; + const std::size_t ExponentBegin = Position; + while (Position < Tail.size() && Tail[Position] >= '0' && Tail[Position] <= '9') + ++Position; + return Position == Tail.size() && Position != ExponentBegin; +} + +[[nodiscard]] YAMLQuoting yamlQuoting(std::string_view Value) noexcept { + if (Value.empty()) + return YAMLQuoting::Single; + + YAMLQuoting Result = YAMLQuoting::None; + const auto IsSpace = [](unsigned char Character) { + return Character == ' ' || Character == '\t' || Character == '\r' || Character == '\n' || + Character == '\f' || Character == '\v'; + }; + if (IsSpace(static_cast(Value.front())) || + IsSpace(static_cast(Value.back())) || Value == "null" || Value == "Null" || + Value == "NULL" || Value == "~" || Value == "true" || Value == "True" || Value == "TRUE" || + Value == "false" || Value == "False" || Value == "FALSE" || isYAMLNumeric(Value)) { + Result = YAMLQuoting::Single; + } + + switch (Value.front()) { + case '-': + case '?': + case ':': + case '\\': + case ',': + case '[': + case ']': + case '{': + case '}': + case '#': + case '&': + case '*': + case '!': + case '|': + case '>': + case '\'': + case '"': + case '%': + case '@': + case '`': + Result = YAMLQuoting::Single; + break; + default: + break; + } + + for (const char RawCharacter : Value) { + const auto Character = static_cast(RawCharacter); + if (isASCIIAlphanumeric(Character)) + continue; + switch (Character) { + case '_': + case '-': + case '^': + case '.': + case ',': + case ' ': + case '\t': + continue; + case '\n': + case '\r': + case 0x7f: + return YAMLQuoting::Double; + default: + if (Character <= 0x1fU || (Character & 0x80U) != 0) + return YAMLQuoting::Double; + Result = YAMLQuoting::Single; + break; + } + } + return Result; +} + +[[nodiscard]] std::string yamlScalar(std::string_view Value) { + const YAMLQuoting Quoting = yamlQuoting(Value); + if (Quoting == YAMLQuoting::None) + return std::string(Value); + + std::string Result; + Result.reserve(Value.size() + 2U); + const char Quote = Quoting == YAMLQuoting::Single ? '\'' : '"'; + Result.push_back(Quote); + for (const char Character : Value) { + if (Quoting == YAMLQuoting::Single) { + Result.push_back(Character); + if (Character == '\'') + Result.push_back('\''); + continue; + } + switch (Character) { + case '\\': + Result.append("\\\\"); + break; + case '"': + Result.append("\\\""); + break; + case '\0': + Result.append("\\0"); + break; + case '\a': + Result.append("\\a"); + break; + case '\b': + Result.append("\\b"); + break; + case '\t': + Result.append("\\t"); + break; + case '\n': + Result.append("\\n"); + break; + case '\v': + Result.append("\\v"); + break; + case '\f': + Result.append("\\f"); + break; + case '\r': + Result.append("\\r"); + break; + case '\x1b': + Result.append("\\e"); + break; + default: { + const auto Byte = static_cast(Character); + if (Byte < 0x20U || Byte == 0x7fU) { + static constexpr std::string_view Hex = "0123456789ABCDEF"; + Result.append("\\x"); + Result.push_back(Hex[Byte >> 4U]); + Result.push_back(Hex[Byte & 0xfU]); + } else { + Result.push_back(Character); + } + break; + } + } + } + Result.push_back(Quote); + return Result; +} + +void appendBlockScalar(std::string &Output, std::string_view Text, std::size_t Indent) { + std::size_t Position = 0; + do { + Output.append(Indent, ' '); + const std::size_t End = Text.find('\n', Position); + if (End == std::string_view::npos) { + Output.append(Text.substr(Position)); + Output.push_back('\n'); + return; + } + Output.append(Text.substr(Position, End - Position)); + Output.push_back('\n'); + Position = End + 1U; + } while (Position < Text.size()); +} + +[[nodiscard]] std::optional parseDecimal32(std::string_view Text) noexcept { + std::uint32_t Value = 0; + if (Text.empty()) + return std::nullopt; + const auto [End, Error] = std::from_chars(Text.data(), Text.data() + Text.size(), Value, 10); + if (Error != std::errc{} || End != Text.data() + Text.size()) + return std::nullopt; + return Value; +} + +[[nodiscard]] bool isDefaultFrameState(const MachineFrameState &State) { + return State == MachineFrameState{}; +} + +class MIRDocumentPrinter final { +public: + MIRDocumentPrinter(const MIRDocument &Document, const TargetDescription &Target, + MIRDocumentPrinterOptions Options) + : Document(Document), Target(Target), Options(std::move(Options)) {} + + [[nodiscard]] Expected run(); + +private: + struct InstructionLocation final { + std::uint32_t Block = 0; + std::uint32_t Offset = 0; + }; + + [[nodiscard]] Expected printFunction(const MachineFunction &Function, + const MIRFunctionDocumentInfo *DocumentInfo); + [[nodiscard]] Expected createBodyContext(const MachineFunction &Function) const; + [[nodiscard]] Expected printRegisters(const MachineFunction &Function, + const MIParserContext &Context); + [[nodiscard]] Expected printFrameInfo(const MachineFunction &Function, + const MIParserContext &Context); + [[nodiscard]] Expected printStackObjects(const MachineFunction &Function, + const MIParserContext &Context, bool Fixed); + [[nodiscard]] Expected printConstants(const MachineFunction &Function, + const MIParserContext &Context); + [[nodiscard]] Expected printJumpTable(const MachineFunction &Function, + const MIParserContext &Context); + [[nodiscard]] Expected printMetadata(const MachineFunction &Function, + const MIParserContext &Context); + [[nodiscard]] Expected printTargetState(const MachineFunction &Function); + [[nodiscard]] Expected printFunctionRecords(const MachineFunction &Function, + const MIParserContext &Context); + [[nodiscard]] Expected verifyOmittedEmbeddedIRLayout(const MachineFunction &Function) const; + + [[nodiscard]] Expected physicalRegister(PhysicalRegister Register) const; + [[nodiscard]] Expected frameReference(FrameIndex Index, + const MIParserContext &Context) const; + [[nodiscard]] Expected metadataReference(MachineMetadataReference Reference, + const MIParserContext &Context) const; + [[nodiscard]] Expected metadataValue(const MachineMetadataValue &Value, + const MIParserContext &Context) const; + [[nodiscard]] Expected debugLocation(const MachineDebugLocation &Location, + const MIParserContext &Context) const; + [[nodiscard]] Expected diLocation(const MachineDILocation &Location, + std::size_t FrameIndex, + const MIParserContext &Context) const; + [[nodiscard]] Expected instructionLocation(const MachineFunction &Function, + MachineInstrStableID ID) const; + [[nodiscard]] Expected blockReference(const MachineFunction &Function, + MachineBlockStableID ID) const; + [[nodiscard]] Expected 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 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 +MIRDocumentPrinter::verifyOmittedEmbeddedIRLayout(const MachineFunction &Function) const { + if (Options.EmitEmbeddedIR) + return {}; + + const EmbeddedIRReferenceIndex *Embedded = Document.embeddedIR(); + if (Embedded == nullptr) + return {}; + + bool HasLayoutDependentContent = false; + for (const VirtualRegisterInfo &RegisterInfo : Function.registerInfo().virtualRegisters()) { + if (!RegisterInfo.IsTombstone && RegisterInfo.Type && + RegisterInfo.Type->isPointerOrPointerVector()) { + HasLayoutDependentContent = true; + break; + } + } + if (!HasLayoutDependentContent) { + for (const MachineConstantPoolEntry &Entry : Function.constantPool().entries()) { + const MachineConstantPoolEntryDesc *Description = Entry.description(); + if (Description != nullptr && + std::holds_alternative(Description->Value)) { + HasLayoutDependentContent = true; + break; + } + } + } + if (!HasLayoutDependentContent) + return {}; + + if (Options.DataLayout.empty()) { + return makeUnexpected( + ErrorCode::InvalidArgument, + "omitting embedded LLVM IR with layout-dependent MIR requires an explicit printer " + "DataLayout"); + } + + const LLVMConstantSyntaxOptions LayoutOptions{ + .DataLayout = Options.DataLayout, + .NamedTypes = {}, + }; + for (const VirtualRegisterInfo &RegisterInfo : Function.registerInfo().virtualRegisters()) { + if (RegisterInfo.IsTombstone || !RegisterInfo.Type || + !RegisterInfo.Type->isPointerOrPointerVector()) { + continue; + } + const auto AddressSpace = RegisterInfo.Type->addressSpace(); + if (!AddressSpace) { + return makeUnexpected(ErrorCode::VerificationError, + "pointer virtual register has no address space"); + } + auto PointerSize = llvmPointerSizeInBits(*AddressSpace, LayoutOptions); + if (!PointerSize) { + return makeUnexpected(ErrorCode::InvalidArgument, + "printer DataLayout cannot determine pointer width: " + + std::string(PointerSize.error().message())); + } + if (*PointerSize != RegisterInfo.Type->scalarBitWidth()) { + return makeUnexpected(ErrorCode::VerificationError, + "printer DataLayout does not match pointer virtual-register width"); + } + } + + for (const MachineConstantPoolEntry &Entry : Function.constantPool().entries()) { + const MachineConstantPoolEntryDesc *Description = Entry.description(); + if (Description == nullptr) + continue; + const auto *Generic = std::get_if(&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 +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::max()) { + return makeUnexpected(ErrorCode::Unsupported, + "MIR cannot represent more than 2^32 virtual registers"); + } + Context.VirtualRegisters.push_back({.TextID = static_cast(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 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 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::max() && + UsedMetadataSlots.contains(static_cast(NextMetadataSlot))) { + ++NextMetadataSlot; + } + if (NextMetadataSlot > std::numeric_limits::max()) { + return makeUnexpected(ErrorCode::Unsupported, "MIR metadata text slot space is exhausted"); + } + TextID = static_cast(NextMetadataSlot); + UsedMetadataSlots.insert(TextID); + ++NextMetadataSlot; + } + Context.Metadata.push_back( + {.TextID = TextID, .Reference = MachineMetadataReference::machineFunction(Node.Slot)}); + } + return Context; +} + +Expected 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 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 MIRDocumentPrinter::metadataValue(const MachineMetadataValue &Value, + const MIParserContext &Context) const { + if (const MachineMetadataReference *Reference = Value.reference()) + return metadataReference(*Reference, Context); + const MachineDIExpression *Expression = Value.expression(); + if (Expression == nullptr) + return makeUnexpected(ErrorCode::VerificationError, "metadata value is not printable"); + return formatDIExpression(Expression->elements()); +} + +Expected 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 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(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::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::max()) { + return makeUnexpected(ErrorCode::Unsupported, "MIR instruction offset exceeds 32 bits"); + } + return InstructionLocation{static_cast(BlockNumber), + static_cast(Offset)}; + } + } + return makeUnexpected(ErrorCode::VerificationError, + "function record references an unlinked instruction"); +} + +Expected 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::max()) + break; + return "%bb." + std::to_string(Number); + } + } + return makeUnexpected(ErrorCode::VerificationError, + "frame record references an unlinked basic block"); +} + +Expected 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 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 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::max()) { + return makeUnexpected(ErrorCode::Unsupported, + "fixed LLVM MIR maxCallFrameSize is limited to 32 bits"); + } + Output.append("frameInfo:\n"); + const auto Bool = [this](std::string_view Key, bool Value) { + if (Options.Mode == MIPrintMode::Readable && !Value) + return; + Output.append(" "); + Output.append(Key); + Output.append(Value ? ": true\n" : ": false\n"); + }; + const auto Unsigned = [this](std::string_view Key, std::uint64_t Value) { + if (Options.Mode == MIPrintMode::Readable && Value == 0) + return; + Output.append(" "); + Output.append(Key); + Output.append(": "); + Output.append(std::to_string(Value)); + Output.push_back('\n'); + }; + + Bool("isFrameAddressTaken", State.IsFrameAddressTaken); + Bool("isReturnAddressTaken", State.IsReturnAddressTaken); + Bool("hasStackMap", State.HasStackMap); + Bool("hasPatchPoint", State.HasPatchPoint); + Unsigned("stackSize", State.StackSize); + if (Options.Mode == MIPrintMode::Complete || State.OffsetAdjustment != 0) { + Output.append(" offsetAdjustment: "); + Output.append(std::to_string(State.OffsetAdjustment)); + Output.push_back('\n'); + } + Unsigned("maxAlignment", State.MaxAlignment ? State.MaxAlignment->bytes() : 0); + Bool("adjustsStack", State.AdjustsStack); + Bool("hasCalls", State.HasCalls); + if (Options.Mode == MIPrintMode::Complete || + State.FramePointerPolicy != MachineFramePointerPolicy::None) { + Output.append(" framePointerPolicy: "); + Output.append(getMachineFramePointerPolicyName(State.FramePointerPolicy)); + Output.push_back('\n'); + } + if (Options.Mode == MIPrintMode::Complete || State.StackProtector) { + std::string Reference; + if (State.StackProtector) { + auto PrintedReference = frameReference(*State.StackProtector, Context); + if (!PrintedReference) + return std::unexpected(PrintedReference.error()); + Reference = std::move(*PrintedReference); + } + Output.append(" stackProtector: "); + Output.append(yamlScalar(Reference)); + Output.push_back('\n'); + } + if (Options.Mode == MIPrintMode::Complete || State.FunctionContext) { + std::string Reference; + if (State.FunctionContext) { + auto PrintedReference = frameReference(*State.FunctionContext, Context); + if (!PrintedReference) + return std::unexpected(PrintedReference.error()); + Reference = std::move(*PrintedReference); + } + Output.append(" functionContext: "); + Output.append(yamlScalar(Reference)); + Output.push_back('\n'); + } + if (Options.Mode == MIPrintMode::Complete || State.MaxCallFrameSize) { + Unsigned("maxCallFrameSize", + State.MaxCallFrameSize.value_or(std::numeric_limits::max())); + } + Unsigned("cvBytesOfCalleeSavedRegisters", State.CVBytesOfCalleeSavedRegisters); + Bool("hasOpaqueSPAdjustment", State.HasOpaqueSPAdjustment); + Bool("hasVAStart", State.HasVAStart); + Bool("hasMustTailInVarArgFunc", State.HasMustTailInVarArgFunc); + Bool("hasTailCall", State.HasTailCall); + Bool("isCalleeSavedInfoValid", State.IsCalleeSavedInfoValid); + Unsigned("localFrameSize", State.LocalFrameSize); + + const auto PrintPoints = + [this, &Function](std::string_view Key, + std::span Points) -> Expected { + 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 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 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 Value = + makeUnexpected(ErrorCode::Unsupported, "constant-pool kind is not MIR serializable"); + if (const auto *Generic = std::get_if(&Description->Value)) { + Value = printLLVMConstant(*Generic); + } else if (const auto *TargetConstantValue = std::get_if(&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 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 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 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 MIRDocumentPrinter::printFunctionRecords(const MachineFunction &Function, + const MIParserContext &Context) { + const MachineFunctionRecords &Records = Function.records(); + if (!Records.entryValues().empty()) { + Output.append("entry_values:\n"); + for (const MachineEntryValueInfo &Entry : Records.entryValues()) { + auto RegisterName = physicalRegister(Entry.Reg); + if (!RegisterName) + return std::unexpected(RegisterName.error()); + if (!Entry.DebugInfo.Variable || !Entry.DebugInfo.Expression || !Entry.DebugInfo.Location) { + return makeUnexpected(ErrorCode::Unsupported, + "MIR entry value requires complete debug metadata"); + } + auto Variable = metadataReference(*Entry.DebugInfo.Variable, Context); + auto Expression = metadataValue(*Entry.DebugInfo.Expression, Context); + auto Location = debugLocation(*Entry.DebugInfo.Location, Context); + if (!Variable) + return std::unexpected(Variable.error()); + if (!Expression) + return std::unexpected(Expression.error()); + if (!Location) + return std::unexpected(Location.error()); + Output.append(" - { entry-value-register: "); + Output.append(yamlScalar(*RegisterName)); + Output.append(", debug-info-variable: "); + Output.append(yamlScalar(*Variable)); + Output.append(", debug-info-expression: "); + Output.append(yamlScalar(*Expression)); + Output.append(", debug-info-location: "); + Output.append(yamlScalar(*Location)); + Output.append(" }\n"); + } + } else if (Options.Mode == MIPrintMode::Complete) { + Output.append("entry_values: []\n"); + } + + if (!Records.callSites().empty()) { + Output.append("callSites:\n"); + for (const MachineCallSiteInfo &CallSite : Records.callSites()) { + auto Location = instructionLocation(Function, CallSite.CallInstruction); + if (!Location) + return std::unexpected(Location.error()); + Output.append(" - { bb: "); + Output.append(std::to_string(Location->Block)); + Output.append(", offset: "); + Output.append(std::to_string(Location->Offset)); + if (!CallSite.ForwardedArguments.empty()) { + Output.append(", fwdArgRegs: [ "); + for (std::size_t Index = 0; Index < CallSite.ForwardedArguments.size(); ++Index) { + if (Index != 0) + Output.append(", "); + const MachineCallSiteArgument &Argument = CallSite.ForwardedArguments[Index]; + auto RegisterName = physicalRegister(Argument.Reg); + if (!RegisterName) + return std::unexpected(RegisterName.error()); + Output.append("{ arg: "); + Output.append(std::to_string(Argument.ArgumentNumber)); + Output.append(", reg: "); + Output.append(yamlScalar(*RegisterName)); + Output.append(" }"); + } + Output.append(" ]"); + } + if (!CallSite.CalleeTypeIDs.empty()) { + Output.append(", calleeTypeIds: [ "); + for (std::size_t Index = 0; Index < CallSite.CalleeTypeIDs.size(); ++Index) { + if (Index != 0) + Output.append(", "); + Output.append(std::to_string(CallSite.CalleeTypeIDs[Index])); + } + Output.append(" ]"); + } + Output.append(" }\n"); + } + } else if (Options.Mode == MIPrintMode::Complete) { + Output.append("callSites: []\n"); + } + + if (!Records.debugValueSubstitutions().empty()) { + Output.append("debugValueSubstitutions:\n"); + for (const MachineDebugValueSubstitution &Substitution : Records.debugValueSubstitutions()) { + Output.append(" - { srcinst: "); + Output.append(std::to_string(Substitution.Source.InstructionNumber)); + Output.append(", srcop: "); + Output.append(std::to_string(Substitution.Source.OperandNumber)); + Output.append(", dstinst: "); + Output.append(std::to_string(Substitution.Destination.InstructionNumber)); + Output.append(", dstop: "); + Output.append(std::to_string(Substitution.Destination.OperandNumber)); + Output.append(", subreg: "); + Output.append(std::to_string(Substitution.Subregister.value())); + Output.append(" }\n"); + } + } else if (Options.Mode == MIPrintMode::Complete) { + Output.append("debugValueSubstitutions: []\n"); + } + + if (!Records.calledGlobals().empty()) { + Output.append("calledGlobals:\n"); + for (const MachineCalledGlobal &Called : Records.calledGlobals()) { + auto Location = instructionLocation(Function, Called.CallInstruction); + if (!Location) + return std::unexpected(Location.error()); + Output.append(" - { bb: "); + Output.append(std::to_string(Location->Block)); + Output.append(", offset: "); + Output.append(std::to_string(Location->Offset)); + Output.append(", callee: "); + Output.append(yamlScalar(Called.Callee)); + Output.append(", flags: "); + Output.append(std::to_string(Called.Flags)); + Output.append(" }\n"); + } + } + + if (!Records.prefetchTargets().empty()) { + Output.append("prefetch-targets: [ "); + const auto Targets = Records.prefetchTargets(); + for (std::size_t Index = 0; Index < Targets.size(); ++Index) { + if (Index != 0) + Output.append(", "); + const MachinePrefetchTarget &TargetValue = Targets[Index]; + std::string Text = "bb_id " + std::to_string(TargetValue.Block.BaseID) + ", " + + std::to_string(TargetValue.Block.CloneID) + ", " + + std::to_string(TargetValue.CallsiteIndex); + Output.append(yamlScalar(Text)); + } + Output.append(" ]\n"); + } + return {}; +} + +Expected MIRDocumentPrinter::printFunction(const MachineFunction &Function, + const MIRFunctionDocumentInfo *) { + if (Function.compilationStatus().state() != MachineCompilationState::Success) { + return makeUnexpected(ErrorCode::Unsupported, + "fixed LLVM MIR cannot serialize this compilation failure state"); + } + auto Context = createBodyContext(Function); + if (!Context) + return std::unexpected(Context.error()); + + Output.append("---\nname: "); + Output.append(yamlScalar(Function.name())); + Output.push_back('\n'); + const auto Alignment = Function.attributes().alignment(); + if (Options.Mode == MIPrintMode::Complete || Alignment) { + Output.append("alignment: "); + Output.append(std::to_string(Alignment ? Alignment->bytes() : 1U)); + Output.push_back('\n'); + } + + const MachineFunctionProperties &Properties = Function.properties(); + const MachineFunctionAttributes &Attributes = Function.attributes(); + printBoolean("exposesReturnsTwice", + Attributes.has(MachineFunctionAttribute::ExposesReturnsTwice)); + printBoolean("legalized", Properties.has(MachineFunctionProperty::Legalized)); + printBoolean("regBankSelected", Properties.has(MachineFunctionProperty::RegBankSelected)); + printBoolean("selected", Properties.has(MachineFunctionProperty::Selected)); + printBoolean("failedISel", Properties.has(MachineFunctionProperty::FailedISel)); + printBoolean("tracksRegLiveness", Properties.has(MachineFunctionProperty::TracksLiveness)); + printBoolean("hasWinCFI", Attributes.has(MachineFunctionAttribute::HasWinCFI)); + printBoolean("noPhis", Properties.has(MachineFunctionProperty::NoPHIs), true); + printBoolean("isSSA", Properties.has(MachineFunctionProperty::IsSSA), true); + printBoolean("noVRegs", Properties.has(MachineFunctionProperty::NoVRegs), true); + printBoolean("hasFakeUses", Attributes.has(MachineFunctionAttribute::HasFakeUses), true); + printBoolean("callsEHReturn", Attributes.has(MachineFunctionAttribute::CallsEHReturn)); + printBoolean("callsUnwindInit", Attributes.has(MachineFunctionAttribute::CallsUnwindInit)); + printBoolean("hasEHContTarget", Attributes.has(MachineFunctionAttribute::HasEHContTarget)); + printBoolean("hasEHScopes", Attributes.has(MachineFunctionAttribute::HasEHScopes)); + printBoolean("hasEHFunclets", Attributes.has(MachineFunctionAttribute::HasEHFunclets)); + printBoolean("isOutlined", Attributes.has(MachineFunctionAttribute::IsOutlined)); + printBoolean("debugInstrRef", Attributes.has(MachineFunctionAttribute::DebugInstrRef)); + printBoolean("failsVerification", Properties.has(MachineFunctionProperty::FailsVerification)); + printBoolean("tracksDebugUserValues", + Attributes.has(MachineFunctionAttribute::TracksDebugUserValues)); + + if (auto Printed = printRegisters(Function, *Context); !Printed) + return Printed; + if (auto Printed = printFrameInfo(Function, *Context); !Printed) + return Printed; + if (auto Printed = printStackObjects(Function, *Context, true); !Printed) + return Printed; + if (auto Printed = printStackObjects(Function, *Context, false); !Printed) + return Printed; + if (auto Printed = printFunctionRecords(Function, *Context); !Printed) + return Printed; + if (auto Printed = printConstants(Function, *Context); !Printed) + return Printed; + if (auto Printed = printTargetState(Function); !Printed) + return Printed; + if (auto Printed = printJumpTable(Function, *Context); !Printed) + return Printed; + if (auto Printed = printMetadata(Function, *Context); !Printed) + return Printed; + + auto Body = + printMachineFunctionBody(Function, Target, *Context, MIPrinterOptions{.Mode = Options.Mode}); + if (!Body) + return std::unexpected(Body.error()); + Output.append("body: |\n"); + appendBlockScalar(Output, *Body, 2); + Output.append("...\n"); + return {}; +} + +Expected MIRDocumentPrinter::run() { + auto UnitVerification = Document.unit().verify(); + if (!UnitVerification) + return std::unexpected(UnitVerification.error()); + if (Document.unit().functionCount() == 0) { + return makeUnexpected(ErrorCode::InvalidArgument, + "MIR document must contain at least one machine function"); + } + + for (const MIRFunctionDocumentInfo &Info : Document.functionDocumentInfo()) { + if (Document.unit().function(Info.Function) == nullptr) { + return makeUnexpected(ErrorCode::VerificationError, + "MIR document info references an unknown machine function"); + } + } + + for (std::size_t Index = 0; Index < Document.unit().functionCount(); ++Index) { + const MachineFunction *Function = Document.unit().functionAt(Index); + if (Function == nullptr) { + return makeUnexpected(ErrorCode::VerificationError, + "compilation unit contains a null machine function"); + } + auto ReferenceVerification = + verifyMIRDocumentReferences(Document, *Function, Options.EmitEmbeddedIR); + if (!ReferenceVerification) + return std::unexpected(ReferenceVerification.error()); + auto LayoutVerification = verifyOmittedEmbeddedIRLayout(*Function); + if (!LayoutVerification) + return std::unexpected(LayoutVerification.error()); + } + + if (Options.EmitEmbeddedIR && Document.embeddedIR() != nullptr) { + Output.append("--- |\n"); + appendBlockScalar(Output, Document.embeddedIR()->source().text(), 2); + Output.append("...\n"); + } + for (std::size_t Index = 0; Index < Document.unit().functionCount(); ++Index) { + const MachineFunction *Function = Document.unit().functionAt(Index); + if (Function == nullptr) + return makeUnexpected(ErrorCode::VerificationError, + "compilation unit contains a null machine function"); + if (auto Printed = printFunction(*Function, documentInfo(*Function)); !Printed) + return std::unexpected(Printed.error()); + } + return std::move(Output); +} + +} // namespace + +Expected printMIRDocument(const MIRDocument &Document, const TargetDescription &Target, + MIRDocumentPrinterOptions Options) { + return MIRDocumentPrinter(Document, Target, std::move(Options)).run(); +} + +} // namespace rcc::mir + +namespace rcc::mir { +namespace { + +[[nodiscard]] bool isBareNameCharacter(char Character) noexcept { + return (Character >= 'a' && Character <= 'z') || (Character >= 'A' && Character <= 'Z') || + (Character >= '0' && Character <= '9') || Character == '_' || Character == '-' || + Character == '.' || Character == '$'; +} + +[[nodiscard]] std::string quoteName(std::string_view Name) { + if (!Name.empty() && std::ranges::all_of(Name, isBareNameCharacter)) + return std::string(Name); + + constexpr std::string_view Hex = "0123456789ABCDEF"; + std::string Result; + Result.reserve(Name.size() + 2U); + Result.push_back('"'); + for (const char RawCharacter : Name) { + const auto Character = static_cast(RawCharacter); + if (Character >= 0x20U && Character <= 0x7EU && Character != '"' && Character != '\\') { + Result.push_back(static_cast(Character)); + continue; + } + Result.push_back('\\'); + Result.push_back(Hex[Character >> 4U]); + Result.push_back(Hex[Character & 0xFU]); + } + Result.push_back('"'); + return Result; +} + +[[nodiscard]] std::string quoteString(std::string_view Value) { + std::string Result = quoteName(Value); + if (!Result.empty() && Result.front() == '"') + return Result; + Result.insert(Result.begin(), '"'); + Result.push_back('"'); + return Result; +} + +[[nodiscard]] std::string hexadecimalWords(std::span Words, + std::uint32_t BitWidth) { + constexpr std::string_view Hex = "0123456789ABCDEF"; + const std::size_t DigitCount = (static_cast(BitWidth) + 3U) / 4U; + std::string Result(DigitCount, '0'); + for (std::size_t Digit = 0; Digit < DigitCount; ++Digit) { + const std::size_t BitOffset = Digit * 4U; + const std::uint64_t Nibble = (Words[BitOffset / 64U] >> (BitOffset % 64U)) & 0xFU; + Result[DigitCount - Digit - 1U] = Hex[Nibble]; + } + return Result; +} + +[[nodiscard]] std::string_view integerPredicateName(IntegerPredicate Predicate) noexcept { + switch (Predicate) { + case IntegerPredicate::Equal: + return "eq"; + case IntegerPredicate::NotEqual: + return "ne"; + case IntegerPredicate::SignedGreaterThan: + return "sgt"; + case IntegerPredicate::SignedGreaterEqual: + return "sge"; + case IntegerPredicate::SignedLessThan: + return "slt"; + case IntegerPredicate::SignedLessEqual: + return "sle"; + case IntegerPredicate::UnsignedGreaterThan: + return "ugt"; + case IntegerPredicate::UnsignedGreaterEqual: + return "uge"; + case IntegerPredicate::UnsignedLessThan: + return "ult"; + case IntegerPredicate::UnsignedLessEqual: + return "ule"; + case IntegerPredicate::Count: + return {}; + } + return {}; +} + +[[nodiscard]] std::string_view floatingPredicateName(FloatingPredicate Predicate) noexcept { + switch (Predicate) { + case FloatingPredicate::AlwaysFalse: + return "false"; + case FloatingPredicate::OrderedEqual: + return "oeq"; + case FloatingPredicate::OrderedGreaterThan: + return "ogt"; + case FloatingPredicate::OrderedGreaterEqual: + return "oge"; + case FloatingPredicate::OrderedLessThan: + return "olt"; + case FloatingPredicate::OrderedLessEqual: + return "ole"; + case FloatingPredicate::OrderedNotEqual: + return "one"; + case FloatingPredicate::Ordered: + return "ord"; + case FloatingPredicate::Unordered: + return "uno"; + case FloatingPredicate::UnorderedEqual: + return "ueq"; + case FloatingPredicate::UnorderedGreaterThan: + return "ugt"; + case FloatingPredicate::UnorderedGreaterEqual: + return "uge"; + case FloatingPredicate::UnorderedLessThan: + return "ult"; + case FloatingPredicate::UnorderedLessEqual: + return "ule"; + case FloatingPredicate::UnorderedNotEqual: + return "une"; + case FloatingPredicate::AlwaysTrue: + return "true"; + case FloatingPredicate::Count: + return {}; + } + return {}; +} + +[[nodiscard]] std::string_view atomicOrderingName(AtomicOrdering Ordering) noexcept { + switch (Ordering) { + case AtomicOrdering::Unordered: + return "unordered"; + case AtomicOrdering::Monotonic: + return "monotonic"; + case AtomicOrdering::Acquire: + return "acquire"; + case AtomicOrdering::Release: + return "release"; + case AtomicOrdering::AcquireRelease: + return "acq_rel"; + case AtomicOrdering::SequentiallyConsistent: + return "seq_cst"; + case AtomicOrdering::NotAtomic: + return {}; + } + return {}; +} + +[[nodiscard]] Expected llvmMIRScalarLowLevelType(LLT Type) { + if (Type.isToken()) + return "s0"; + if (Type.isAnyScalar()) + return "s" + std::to_string(Type.scalarBitWidth()); + if (Type.isInteger()) + return "i" + std::to_string(Type.scalarBitWidth()); + if (Type.isPointer()) { + const auto AddressSpace = Type.addressSpace(); + if (!AddressSpace) { + return makeUnexpected(ErrorCode::VerificationError, + "pointer low-level type has no address space"); + } + return "p" + std::to_string(*AddressSpace); + } + if (!Type.isFloat()) { + return makeUnexpected(ErrorCode::VerificationError, + "invalid scalar low-level type cannot be printed as LLVM MIR"); + } + + const auto Semantics = Type.floatSemantics(); + if (!Semantics) { + return makeUnexpected(ErrorCode::VerificationError, + "floating low-level type has no floating-point semantics"); + } + switch (*Semantics) { + case FloatSemantics::IEEEHalf: + return "f16"; + case FloatSemantics::BFloat: + return "bf16"; + case FloatSemantics::IEEESingle: + return "f32"; + case FloatSemantics::IEEEDouble: + return "f64"; + case FloatSemantics::IEEEQuad: + return "f128"; + case FloatSemantics::PPCDoubleDouble: + case FloatSemantics::Float8E5M2: + case FloatSemantics::Float8E5M2FNUZ: + case FloatSemantics::Float8E4M3: + case FloatSemantics::Float8E4M3FN: + case FloatSemantics::Float8E4M3FNUZ: + case FloatSemantics::Float8E4M3B11FNUZ: + case FloatSemantics::Float8E3M4: + case FloatSemantics::FloatTF32: + case FloatSemantics::Float8E8M0FNU: + case FloatSemantics::Float6E3M2FN: + case FloatSemantics::Float6E2M3FN: + case FloatSemantics::Float4E2M1FN: + case FloatSemantics::X87DoubleExtended: + case FloatSemantics::Count: + return makeUnexpected(ErrorCode::Unsupported, + "fixed LLVM MIR cannot spell this floating low-level type"); + } + return makeUnexpected(ErrorCode::InternalError, "floating low-level type is unhandled"); +} + +[[nodiscard]] Expected llvmMIRLowLevelType(LLT Type) { + if (!Type.isValid()) { + return makeUnexpected(ErrorCode::VerificationError, + "invalid low-level type cannot be printed as LLVM MIR"); + } + if (!Type.isVector()) + return llvmMIRScalarLowLevelType(Type); + + const auto ElementType = Type.scalarElementType(); + if (!ElementType) { + return makeUnexpected(ErrorCode::VerificationError, + "vector low-level type has no scalar element type"); + } + auto Scalar = llvmMIRScalarLowLevelType(*ElementType); + if (!Scalar) + return std::unexpected(Scalar.error()); + + std::string Result = "<"; + if (Type.isScalableVector()) + Result.append("vscale x "); + Result.append(std::to_string(Type.minimumElementCount())); + Result.append(" x "); + Result.append(*Scalar); + Result.push_back('>'); + return Result; +} + +class BodyPrinter final { +public: + BodyPrinter(const MachineFunction &Function, const TargetDescription &Target, + const MIParserContext &Context, MIPrinterOptions Options) + : Function(Function), Target(Target), Context(Context), Options(Options) {} + + [[nodiscard]] Expected run(); + +private: + [[nodiscard]] Expected initialize(); + [[nodiscard]] Expected printBlock(const MachineBasicBlock &Block); + [[nodiscard]] Expected printInstruction(const MachineInstr &Instruction); + [[nodiscard]] Expected printOperand(const MachineOperand &Operand, bool IsExplicitDef, + bool PrintVirtualType = true); + [[nodiscard]] Expected printInlineAsmExtraInfo(const MachineOperand &Operand); + [[nodiscard]] Expected printInlineAsmOperandFlag(const MachineOperand &Operand); + [[nodiscard]] Expected printRegisterOperand(const MachineOperand &Operand, + bool IsExplicitDef, bool PrintVirtualType); + [[nodiscard]] Expected printTargetFlags(std::uint32_t Flags); + [[nodiscard]] Expected printMetadataReference(MachineMetadataReference Reference); + [[nodiscard]] Expected printDILocation(const MachineDILocation &Location, + std::size_t FrameIndex); + [[nodiscard]] Expected printCFIInstruction(CFIInstructionIndex Index); + [[nodiscard]] Expected printMemoryOperand(const MachineMemOperand &Operand); + [[nodiscard]] Expected printMemoryBase(const MemoryBase &Base, std::int64_t Offset); + + [[nodiscard]] Expected blockID(const MachineBasicBlock &Block) const; + [[nodiscard]] Expected virtualRegisterID(Register Reg) const; + [[nodiscard]] Expected frameID(FrameIndex Index) const; + [[nodiscard]] Expected constantPoolID(ConstantPoolIndex Index) const; + [[nodiscard]] Expected jumpTableID(JumpTableIndex Index) const; + [[nodiscard]] Expected metadataID(MachineMetadataReference Reference) const; + [[nodiscard]] Expected appendPhysicalRegister(PhysicalRegister Register); + void appendBlockReference(const MachineBasicBlock &Block); + void appendOffset(std::int64_t Offset); + + const MachineFunction &Function; + const TargetDescription &Target; + const MIParserContext &Context; + MIPrinterOptions Options; + std::string Output; + std::unordered_map BlockIDs; +}; + +Expected BodyPrinter::initialize() { + std::uint64_t NextID = 0; + for (const MachineBasicBlock *Block = Function.firstBlock(); Block != nullptr; + Block = Block->next()) { + if (NextID > std::numeric_limits::max()) { + return makeUnexpected(ErrorCode::InvalidArgument, + "machine function has too many blocks to print"); + } + BlockIDs.emplace(Block, static_cast(NextID)); + ++NextID; + } + return {}; +} + +Expected BodyPrinter::blockID(const MachineBasicBlock &Block) const { + const auto Found = BlockIDs.find(&Block); + if (Found == BlockIDs.end()) + return makeUnexpected(ErrorCode::VerificationError, + "machine operand references an unlinked basic block"); + return Found->second; +} + +Expected BodyPrinter::virtualRegisterID(Register Reg) const { + if (!Context.VirtualRegisters.empty()) { + const auto Found = + std::ranges::find_if(Context.VirtualRegisters, + [Reg](const MIVirtualRegisterSlot &Slot) { return Slot.Reg == Reg; }); + if (Found == Context.VirtualRegisters.end()) { + return makeUnexpected(ErrorCode::VerificationError, "virtual register has no MIR text slot"); + } + return Found->TextID; + } + const auto VirtualIndex = Reg.virtualIndex(); + if (!VirtualIndex) + return makeUnexpected(ErrorCode::VerificationError, "virtual register has no numeric index"); + return *VirtualIndex; +} + +Expected BodyPrinter::frameID(FrameIndex Index) const { + const auto InStack = std::ranges::find_if( + Context.StackObjects, [Index](const MIFrameObjectSlot &Slot) { return Slot.Index == Index; }); + if (InStack != Context.StackObjects.end()) + return InStack->TextID; + const auto InFixed = + std::ranges::find_if(Context.FixedStackObjects, + [Index](const MIFrameObjectSlot &Slot) { return Slot.Index == Index; }); + if (InFixed != Context.FixedStackObjects.end()) + return InFixed->TextID; + return makeUnexpected(ErrorCode::VerificationError, "frame index has no MIR text slot"); +} + +Expected BodyPrinter::constantPoolID(ConstantPoolIndex Index) const { + const auto Found = + std::ranges::find_if(Context.ConstantPoolItems, + [Index](const MIConstantPoolSlot &Slot) { return Slot.Index == Index; }); + if (Found == Context.ConstantPoolItems.end()) { + return makeUnexpected(ErrorCode::VerificationError, "constant-pool index has no MIR text slot"); + } + return Found->TextID; +} + +Expected BodyPrinter::jumpTableID(JumpTableIndex Index) const { + const auto Found = std::ranges::find_if( + Context.JumpTables, [Index](const MIJumpTableSlot &Slot) { return Slot.Index == Index; }); + if (Found == Context.JumpTables.end()) + return makeUnexpected(ErrorCode::VerificationError, "jump-table index has no MIR text slot"); + return Found->TextID; +} + +Expected BodyPrinter::metadataID(MachineMetadataReference Reference) const { + const auto Found = + std::ranges::find_if(Context.Metadata, [Reference](const MIMetadataSlot &Slot) { + return Slot.Reference == Reference; + }); + if (Found != Context.Metadata.end()) + return Found->TextID; + if (Reference.scope() == MachineMetadataScope::MachineFunction) + return Reference.slot(); + return makeUnexpected(ErrorCode::VerificationError, + "embedded metadata reference has no MIR text slot"); +} + +Expected BodyPrinter::appendPhysicalRegister(PhysicalRegister Register) { + const auto Name = Target.registers().physicalRegisterMIRName(Register); + if (!Name) + return makeUnexpected(ErrorCode::VerificationError, "physical register has no MIR name"); + Output.push_back('$'); + Output.append(*Name); + return {}; +} + +void BodyPrinter::appendBlockReference(const MachineBasicBlock &Block) { + Output.append("%bb."); + Output.append(std::to_string(BlockIDs.at(&Block))); +} + +void BodyPrinter::appendOffset(std::int64_t Offset) { + if (Offset == 0) + return; + if (Offset > 0) { + Output.append(" + "); + Output.append(std::to_string(Offset)); + return; + } + Output.append(" - "); + const std::uint64_t Magnitude = Offset == std::numeric_limits::min() + ? std::uint64_t{1} << 63U + : static_cast(-Offset); + Output.append(std::to_string(Magnitude)); +} + +Expected BodyPrinter::printTargetFlags(std::uint32_t Flags) { + if (Flags == 0) + return {}; + auto Names = Target.mirText().operandFlagNames(Flags); + if (!Names) + return std::unexpected(Names.error()); + Output.append("target-flags("); + for (std::size_t Index = 0; Index < Names->size(); ++Index) { + if (Index != 0) + Output.append(", "); + Output.append((*Names)[Index]); + } + Output.append(") "); + return {}; +} + +Expected BodyPrinter::printMetadataReference(MachineMetadataReference Reference) { + auto ID = metadataID(Reference); + if (!ID) + return std::unexpected(ID.error()); + Output.push_back('!'); + Output.append(std::to_string(*ID)); + return {}; +} + +Expected BodyPrinter::printDILocation(const MachineDILocation &Location, + std::size_t FrameIndex) { + const auto Frames = Location.inlineChain(); + if (FrameIndex >= Frames.size()) { + return makeUnexpected(ErrorCode::VerificationError, + "inline DILocation has an invalid frame index"); + } + const MachineDILocationFrame &Frame = Frames[FrameIndex]; + Output.append("!DILocation(line: "); + Output.append(std::to_string(Frame.Line)); + if (Options.Mode == MIPrintMode::Complete || Frame.Column != 0) { + Output.append(", column: "); + Output.append(std::to_string(Frame.Column)); + } + Output.append(", scope: "); + if (auto Printed = printMetadataReference(Frame.Scope); !Printed) + return Printed; + if (FrameIndex + 1U < Frames.size()) { + Output.append(", inlinedAt: "); + if (auto Printed = printDILocation(Location, FrameIndex + 1U); !Printed) + return Printed; + } else if (Location.terminalInlinedAt()) { + Output.append(", inlinedAt: "); + if (auto Printed = printMetadataReference(*Location.terminalInlinedAt()); !Printed) { + return Printed; + } + } + if (Options.Mode == MIPrintMode::Complete || Frame.IsImplicitCode) { + Output.append(", isImplicitCode: "); + Output.append(Frame.IsImplicitCode ? "true" : "false"); + } + if (Options.Mode == MIPrintMode::Complete || Frame.AtomGroup != 0) { + Output.append(", atomGroup: "); + Output.append(std::to_string(Frame.AtomGroup)); + } + if (Options.Mode == MIPrintMode::Complete || Frame.AtomRank != 0) { + Output.append(", atomRank: "); + Output.append(std::to_string(Frame.AtomRank)); + } + Output.push_back(')'); + return {}; +} + +Expected BodyPrinter::printRegisterOperand(const MachineOperand &Operand, bool IsExplicitDef, + bool PrintVirtualType) { + const RegisterOperandFlags Flags = Operand.registerFlags(); + if (Flags.has(RegisterOperandFlag::Implicit) && Flags.has(RegisterOperandFlag::Define)) { + Output.append("implicit-def "); + } else { + if (!IsExplicitDef && Flags.has(RegisterOperandFlag::Define)) + Output.append("def "); + if (Flags.has(RegisterOperandFlag::Implicit)) + Output.append("implicit "); + } + if (Flags.has(RegisterOperandFlag::Dead)) + Output.append("dead "); + if (Flags.has(RegisterOperandFlag::Kill)) + Output.append("killed "); + if (Flags.has(RegisterOperandFlag::Undef)) + Output.append("undef "); + if (Flags.has(RegisterOperandFlag::InternalRead)) + Output.append("internal "); + if (Flags.has(RegisterOperandFlag::EarlyClobber)) + Output.append("early-clobber "); + if (Flags.has(RegisterOperandFlag::Debug)) + Output.append("debug-use "); + if (Flags.has(RegisterOperandFlag::Renamable)) + Output.append("renamable "); + + const auto RegisterValue = Operand.registerValue(); + if (!RegisterValue) { + return makeUnexpected(ErrorCode::VerificationError, "register operand has no register value"); + } + const Register Reg = *RegisterValue; + if (Reg.isNoRegister()) { + Output.append("$noreg"); + } else if (Reg.isPhysical()) { + const auto Physical = Reg.physicalRegister(); + if (!Physical) { + return makeUnexpected(ErrorCode::VerificationError, + "physical register has no physical-register value"); + } + auto Printed = appendPhysicalRegister(*Physical); + if (!Printed) + return Printed; + } else { + auto Info = Function.registerInfo().virtualRegisterInfo(Reg); + if (!Info) + return std::unexpected(Info.error()); + const VirtualRegisterInfo &RegisterInfo = Info->get(); + Output.push_back('%'); + if (RegisterInfo.Name) { + Output.append(*RegisterInfo.Name); + } else { + auto VirtualIndex = virtualRegisterID(Reg); + if (!VirtualIndex) + return std::unexpected(VirtualIndex.error()); + Output.append(std::to_string(*VirtualIndex)); + } + if (Operand.subReg().value() != 0) { + const SubRegIndexDesc *SubReg = Target.registers().subRegIndex(Operand.subReg()); + if (SubReg == nullptr) + return makeUnexpected(ErrorCode::VerificationError, + "virtual register has an unknown subregister"); + Output.push_back('.'); + Output.append(SubReg->Name); + } + + const bool PrintRegisterConstraint = + IsExplicitDef || Function.registerInfo().getUniqueVRegDef(Reg) == nullptr; + if (PrintRegisterConstraint && RegisterInfo.RegClass) { + const auto Name = Target.registers().registerClassMIRName(*RegisterInfo.RegClass); + if (!Name) + return makeUnexpected(ErrorCode::VerificationError, + "virtual register class has no MIR name"); + Output.push_back(':'); + Output.append(*Name); + } else if (PrintRegisterConstraint && RegisterInfo.Bank) { + const auto Name = Target.registers().registerBankMIRName(*RegisterInfo.Bank); + if (!Name) + return makeUnexpected(ErrorCode::VerificationError, + "virtual register bank has no MIR name"); + Output.push_back(':'); + Output.append(*Name); + } else if (PrintRegisterConstraint && RegisterInfo.Type) { + Output.append(":_"); + } + if (PrintVirtualType && RegisterInfo.Type) { + auto Type = llvmMIRLowLevelType(*RegisterInfo.Type); + if (!Type) + return std::unexpected(Type.error()); + Output.push_back('('); + Output.append(*Type); + Output.push_back(')'); + } + } + + if (Operand.tiedOperandIndex() && Operand.isUse()) { + Output.append("(tied-def "); + Output.append(std::to_string(*Operand.tiedOperandIndex())); + Output.push_back(')'); + } + return {}; +} + +Expected BodyPrinter::printCFIInstruction(CFIInstructionIndex Index) { + const MachineCFIInstruction *Instruction = Function.cfiInfo().instruction(Index); + if (Instruction == nullptr) + return makeUnexpected(ErrorCode::VerificationError, + "CFI operand references an unknown instruction"); + const auto PrintRegister = [this](DwarfRegisterNumber Dwarf) -> Expected { + const auto Register = Target.mirText().physicalRegister(Dwarf.value()); + if (!Register) { + return makeUnexpected(ErrorCode::VerificationError, + "CFI instruction has no canonical physical register"); + } + return appendPhysicalRegister(*Register); + }; + const auto PrintRegisterOffset = + [this, &PrintRegister](std::string_view Name, + const MachineCFIRegisterOffsetRule &Rule) -> Expected { + Output.append(Name); + Output.push_back(' '); + if (auto Printed = PrintRegister(Rule.Register); !Printed) + return Printed; + Output.append(", "); + Output.append(std::to_string(Rule.Offset)); + return {}; + }; + + switch (Instruction->operation()) { + case MachineCFIOperation::SameValue: { + Output.append("same_value "); + return PrintRegister(Instruction->getIf()->Register); + } + case MachineCFIOperation::RememberState: + Output.append("remember_state"); + return {}; + case MachineCFIOperation::RestoreState: + Output.append("restore_state"); + return {}; + case MachineCFIOperation::Offset: + return PrintRegisterOffset("offset", *Instruction->getIf()); + case MachineCFIOperation::LLVMDefAddressSpaceCFA: { + const auto &Rule = *Instruction->getIf(); + Output.append("llvm_def_aspace_cfa "); + if (auto Printed = PrintRegister(Rule.Register); !Printed) + return Printed; + Output.append(", "); + Output.append(std::to_string(Rule.Offset)); + Output.append(", "); + Output.append(std::to_string(Rule.AddressSpace)); + return {}; + } + case MachineCFIOperation::DefCFARegister: + Output.append("def_cfa_register "); + return PrintRegister(Instruction->getIf()->Register); + case MachineCFIOperation::DefCFAOffset: + Output.append("def_cfa_offset "); + Output.append(std::to_string(Instruction->getIf()->Offset)); + return {}; + case MachineCFIOperation::DefCFA: + return PrintRegisterOffset("def_cfa", *Instruction->getIf()); + case MachineCFIOperation::RelativeOffset: + return PrintRegisterOffset("rel_offset", *Instruction->getIf()); + case MachineCFIOperation::AdjustCFAOffset: + Output.append("adjust_cfa_offset "); + Output.append(std::to_string(Instruction->getIf()->Offset)); + return {}; + case MachineCFIOperation::Escape: { + Output.append("escape "); + constexpr std::string_view Hex = "0123456789ABCDEF"; + const auto &Bytes = Instruction->getIf()->Bytes; + for (std::size_t ByteIndex = 0; ByteIndex < Bytes.size(); ++ByteIndex) { + if (ByteIndex != 0) + Output.append(", "); + Output.append("0x"); + Output.push_back(Hex[Bytes[ByteIndex] >> 4U]); + Output.push_back(Hex[Bytes[ByteIndex] & 0xFU]); + } + return {}; + } + case MachineCFIOperation::Restore: + Output.append("restore "); + return PrintRegister(Instruction->getIf()->Register); + case MachineCFIOperation::Undefined: + Output.append("undefined "); + return PrintRegister(Instruction->getIf()->Register); + case MachineCFIOperation::Register: { + const auto &Rule = *Instruction->getIf(); + Output.append("register "); + if (auto Printed = PrintRegister(Rule.Register); !Printed) + return Printed; + Output.append(", "); + return PrintRegister(Rule.SourceRegister); + } + case MachineCFIOperation::WindowSave: + Output.append("window_save"); + return {}; + case MachineCFIOperation::NegateReturnAddressState: + Output.append("negate_ra_sign_state"); + return {}; + case MachineCFIOperation::NegateReturnAddressStateWithPC: + Output.append("negate_ra_sign_state_with_pc"); + return {}; + case MachineCFIOperation::GNUArgsSize: + case MachineCFIOperation::Label: + case MachineCFIOperation::ValueOffset: + return makeUnexpected( + ErrorCode::Unsupported, + "CFI operation has no textual representation in the fixed LLVM MIR baseline"); + case MachineCFIOperation::LLVMRegisterPair: { + const auto &Rule = *Instruction->getIf(); + Output.append("llvm_register_pair "); + if (auto Printed = PrintRegister(Rule.Register); !Printed) + return Printed; + Output.append(", "); + if (auto Printed = PrintRegister(Rule.FirstRegister); !Printed) + return Printed; + Output.append(", "); + Output.append(std::to_string(Rule.FirstSizeInBits)); + Output.append(", "); + if (auto Printed = PrintRegister(Rule.SecondRegister); !Printed) + return Printed; + Output.append(", "); + Output.append(std::to_string(Rule.SecondSizeInBits)); + return {}; + } + case MachineCFIOperation::LLVMVectorRegisters: { + const auto &Rule = *Instruction->getIf(); + Output.append("llvm_vector_registers "); + if (auto Printed = PrintRegister(Rule.Register); !Printed) + return Printed; + for (const MachineCFIVectorRegisterLane &Lane : Rule.VectorRegisters) { + Output.append(", "); + if (auto Printed = PrintRegister(Lane.Register); !Printed) + return Printed; + Output.append(", "); + Output.append(std::to_string(Lane.Lane)); + Output.append(", "); + Output.append(std::to_string(Lane.SizeInBits)); + } + return {}; + } + case MachineCFIOperation::LLVMVectorOffset: { + const auto &Rule = *Instruction->getIf(); + Output.append("llvm_vector_offset "); + if (auto Printed = PrintRegister(Rule.Register); !Printed) + return Printed; + Output.append(", "); + Output.append(std::to_string(Rule.RegisterSizeInBits)); + Output.append(", "); + if (auto Printed = PrintRegister(Rule.MaskRegister); !Printed) + return Printed; + Output.append(", "); + Output.append(std::to_string(Rule.MaskRegisterSizeInBits)); + Output.append(", "); + Output.append(std::to_string(Rule.Offset)); + return {}; + } + case MachineCFIOperation::LLVMVectorRegisterMask: { + const auto &Rule = *Instruction->getIf(); + Output.append("llvm_vector_register_mask "); + if (auto Printed = PrintRegister(Rule.Register); !Printed) + return Printed; + Output.append(", "); + if (auto Printed = PrintRegister(Rule.SpillRegister); !Printed) + return Printed; + Output.append(", "); + Output.append(std::to_string(Rule.SpillRegisterLaneSizeInBits)); + Output.append(", "); + if (auto Printed = PrintRegister(Rule.MaskRegister); !Printed) + return Printed; + Output.append(", "); + Output.append(std::to_string(Rule.MaskRegisterSizeInBits)); + return {}; + } + case MachineCFIOperation::Count: + return makeUnexpected(ErrorCode::VerificationError, "CFI instruction has an invalid operation"); + } + return makeUnexpected(ErrorCode::InternalError, "CFI printer did not handle an operation"); +} + +Expected BodyPrinter::printOperand(const MachineOperand &Operand, bool IsExplicitDef, + bool PrintVirtualType) { + if (Operand.isRegister()) + return printRegisterOperand(Operand, IsExplicitDef, PrintVirtualType); + if (auto Flags = printTargetFlags(Operand.targetFlags()); !Flags) + return Flags; + + switch (Operand.kind()) { + case MachineOperand::Kind::Register: + return makeUnexpected(ErrorCode::InternalError, + "register operand reached the non-register printer"); + case MachineOperand::Kind::Immediate: + Output.append(std::to_string(*Operand.getIf())); + return {}; + case MachineOperand::Kind::IntegerImmediate: { + const IntegerImmediate &Value = Operand.getIf()->Value; + Output.push_back('i'); + Output.append(std::to_string(Value.bitWidth())); + Output.push_back(' '); + if (Value.bitWidth() == 1U) + Output.append(Value.isZero() ? "false" : "true"); + else + Output.append(Value.toSignedDecimal()); + return {}; + } + case MachineOperand::Kind::FloatingImmediate: { + const FloatingPointBits &Bits = Operand.getIf()->Bits; + auto Constant = GenericConstantData::floating(Bits); + if (!Constant) + return std::unexpected(Constant.error()); + auto Text = printLLVMConstant(*Constant); + if (!Text) + return std::unexpected(Text.error()); + Output.append(*Text); + return {}; + } + case MachineOperand::Kind::BasicBlock: { + const MachineBasicBlock *Block = Operand.getIf()->Block; + if (Block == nullptr || !BlockIDs.contains(Block)) { + return makeUnexpected(ErrorCode::VerificationError, + "basic-block operand references an unlinked block"); + } + appendBlockReference(*Block); + return {}; + } + case MachineOperand::Kind::FrameIndex: { + const auto &Frame = *Operand.getIf(); + auto ID = frameID(Frame.Index); + if (!ID) + return std::unexpected(ID.error()); + const auto Stack = std::ranges::find_if( + Context.StackObjects, + [Index = Frame.Index](const MIFrameObjectSlot &Slot) { return Slot.Index == Index; }); + if (Stack != Context.StackObjects.end()) { + Output.append("%stack."); + Output.append(std::to_string(*ID)); + if (Stack->Name) { + Output.push_back('.'); + Output.append(quoteName(*Stack->Name)); + } + } else { + Output.append("%fixed-stack."); + Output.append(std::to_string(*ID)); + } + appendOffset(Frame.Offset); + return {}; + } + case MachineOperand::Kind::ConstantPoolIndex: { + const auto &Constant = *Operand.getIf(); + auto ID = constantPoolID(Constant.Index); + if (!ID) + return std::unexpected(ID.error()); + Output.append("%const."); + Output.append(std::to_string(*ID)); + appendOffset(Constant.Offset); + return {}; + } + case MachineOperand::Kind::JumpTableIndex: { + auto ID = jumpTableID(Operand.getIf()->Index); + if (!ID) + return std::unexpected(ID.error()); + Output.append("%jump-table."); + Output.append(std::to_string(*ID)); + return {}; + } + case MachineOperand::Kind::GlobalSymbol: { + const auto &Global = *Operand.getIf(); + Output.push_back('@'); + Output.append(quoteName(Global.Name)); + appendOffset(Global.Offset); + return {}; + } + case MachineOperand::Kind::ExternalSymbol: { + const auto &External = *Operand.getIf(); + Output.push_back('&'); + Output.append(quoteName(External.Name)); + appendOffset(External.Offset); + return {}; + } + case MachineOperand::Kind::BlockAddress: { + const auto &Address = *Operand.getIf(); + Output.append("blockaddress(@"); + Output.append(quoteName(Address.FunctionName)); + Output.append(", %ir-block."); + Output.append(quoteName(Address.BlockName)); + Output.push_back(')'); + appendOffset(Address.Offset); + return {}; + } + case MachineOperand::Kind::RegisterMask: { + const RegisterMask &Mask = *Operand.getIf()->Mask; + if (const auto Name = Target.mirText().registerMaskName(Mask)) { + Output.append(*Name); + return {}; + } + Output.append("CustomRegMask("); + bool First = true; + for (const PhysicalRegisterDesc &Register : Target.registers().physicalRegisters()) { + if (!Mask.preserves(Register.Reg)) + continue; + if (!First) + Output.append(", "); + First = false; + if (auto Printed = appendPhysicalRegister(Register.Reg); !Printed) + return Printed; + } + Output.push_back(')'); + return {}; + } + case MachineOperand::Kind::LiveOutRegisterMask: { + const RegisterMask &Mask = *Operand.getIf()->Mask; + Output.append("liveout("); + bool First = true; + for (const PhysicalRegisterDesc &Register : Target.registers().physicalRegisters()) { + if (!Mask.preserves(Register.Reg)) + continue; + if (!First) + Output.append(", "); + First = false; + if (auto Printed = appendPhysicalRegister(Register.Reg); !Printed) + return Printed; + } + Output.push_back(')'); + return {}; + } + case MachineOperand::Kind::Metadata: { + const MachineMetadataValue &Value = Operand.getIf()->Value; + if (const auto *Reference = Value.reference()) + return printMetadataReference(*Reference); + const MachineDIExpression *Expression = Value.expression(); + if (Expression == nullptr) { + return makeUnexpected(ErrorCode::VerificationError, + "metadata operand has no printable value"); + } + Output.append(formatDIExpression(Expression->elements())); + return {}; + } + case MachineOperand::Kind::TargetIndex: { + const auto &TargetIndexValue = *Operand.getIf(); + const auto Name = Target.mirText().targetIndexName(TargetIndexValue.Index); + if (!Name) + return makeUnexpected(ErrorCode::VerificationError, "target index has no MIR name"); + Output.append("target-index("); + Output.append(*Name); + Output.push_back(')'); + appendOffset(TargetIndexValue.Offset); + return {}; + } + case MachineOperand::Kind::MCSymbol: { + const auto &Symbol = *Operand.getIf(); + Output.append("'); + appendOffset(Symbol.Offset); + return {}; + } + case MachineOperand::Kind::CFIIndex: + return printCFIInstruction(Operand.getIf()->Index); + case MachineOperand::Kind::Intrinsic: { + const auto Name = Target.mirText().intrinsicName(Operand.getIf()->ID); + if (!Name) + return makeUnexpected(ErrorCode::VerificationError, "intrinsic has no MIR name"); + Output.append("intrinsic(@"); + Output.append(*Name); + Output.push_back(')'); + return {}; + } + case MachineOperand::Kind::Predicate: { + const PredicateOperand &Predicate = *Operand.getIf(); + if (const auto *Integer = Predicate.integerPredicate()) { + Output.append("intpred("); + Output.append(integerPredicateName(*Integer)); + } else { + Output.append("floatpred("); + Output.append(floatingPredicateName(*Predicate.floatingPredicate())); + } + Output.push_back(')'); + return {}; + } + case MachineOperand::Kind::ShuffleMask: { + Output.append("shufflemask("); + const auto &Elements = Operand.getIf()->Elements; + for (std::size_t Index = 0; Index < Elements.size(); ++Index) { + if (Index != 0) + Output.append(", "); + if (Elements[Index] == ShuffleMaskOperand::Undef) + Output.append("undef"); + else + Output.append(std::to_string(Elements[Index])); + } + Output.push_back(')'); + return {}; + } + case MachineOperand::Kind::DebugInstrRef: { + const auto &Reference = *Operand.getIf(); + Output.append("dbg-instr-ref("); + Output.append(std::to_string(Reference.InstructionNumber)); + Output.append(", "); + Output.append(std::to_string(Reference.OperandNumber)); + Output.push_back(')'); + return {}; + } + case MachineOperand::Kind::LaneMask: + Output.append("lanemask(0x"); + Output.append( + hexadecimalWords(std::array{Operand.getIf()->Mask.rawValue()}, 64)); + Output.push_back(')'); + return {}; + case MachineOperand::Kind::Count: + return makeUnexpected(ErrorCode::VerificationError, "machine operand has an invalid kind"); + } + return makeUnexpected(ErrorCode::InternalError, "machine operand printer did not handle a kind"); +} + +Expected BodyPrinter::printInlineAsmExtraInfo(const MachineOperand &Operand) { + const auto *Value = Operand.getIf(); + if (Value == nullptr || Operand.targetFlags() != 0) { + return makeUnexpected(ErrorCode::VerificationError, + "inline assembly extra info must be an unflagged immediate"); + } + const InlineAsmExtraInfo ExtraInfo(static_cast(*Value)); + const std::vector Names = ExtraInfo.names(); + for (std::size_t Index = 0; Index < Names.size(); ++Index) { + if (Index != 0) + Output.push_back(' '); + Output.append(Names[Index]); + } + return {}; +} + +Expected BodyPrinter::printInlineAsmOperandFlag(const MachineOperand &Operand) { + const auto *Value = Operand.getIf(); + if (Value == nullptr || *Value < 0 || + static_cast(*Value) > std::numeric_limits::max() || + Operand.targetFlags() != 0) { + return makeUnexpected(ErrorCode::VerificationError, + "inline assembly operand flag must be an unflagged 32-bit immediate"); + } + + const InlineAsmOperandFlag Flag(static_cast(*Value)); + const std::string_view KindName = InlineAsmOperandFlag::kindName(Flag.kind()); + if (KindName.empty() || Flag.operandCount() == 0) { + return makeUnexpected(ErrorCode::VerificationError, + "inline assembly operand flag has an invalid kind or operand count"); + } + Output.append(KindName); + + if (const auto MatchingOperand = Flag.matchingOperand()) { + Output.append(" tiedto:$"); + Output.append(std::to_string(*MatchingOperand)); + return Flag.operandCount(); + } + if (Flag.kind() == InlineAsmOperandKind::Memory || + Flag.kind() == InlineAsmOperandKind::Function) { + const auto Constraint = InlineAsmOperandFlag::memoryConstraintName(Flag.memoryConstraint()); + if (Constraint) { + Output.push_back(':'); + Output.append(*Constraint); + } + return Flag.operandCount(); + } + if (Flag.isRegisterKind()) { + if (const auto RegisterClass = Flag.registerClass()) { + const auto Name = + Target.registers().registerClassMIRName(RegisterClassID::fromValue(*RegisterClass)); + if (!Name) { + return makeUnexpected(ErrorCode::VerificationError, + "inline assembly operand flag references an unknown register class"); + } + Output.push_back(':'); + Output.append(*Name); + } + } + return Flag.operandCount(); +} + +Expected BodyPrinter::printMemoryBase(const MemoryBase &Base, std::int64_t Offset) { + switch (Base.kind()) { + case MemoryBase::Kind::Unknown: + Output.append("unknown-address"); + break; + case MemoryBase::Kind::FrameIndex: { + const auto Frame = Base.frameIndex(); + if (!Frame) { + return makeUnexpected(ErrorCode::VerificationError, + "frame-index memory base has no frame index"); + } + const FrameIndex Index = *Frame; + auto ID = frameID(Index); + if (!ID) + return std::unexpected(ID.error()); + const auto Stack = + std::ranges::find_if(Context.StackObjects, [Index](const MIFrameObjectSlot &Slot) { + return Slot.Index == Index; + }); + if (Stack != Context.StackObjects.end()) { + Output.append("%stack."); + Output.append(std::to_string(*ID)); + if (Stack->Name) { + Output.push_back('.'); + Output.append(quoteName(*Stack->Name)); + } + } else { + Output.append("%fixed-stack."); + Output.append(std::to_string(*ID)); + } + break; + } + case MemoryBase::Kind::ConstantPool: { + const auto Constant = Base.constantPoolIndex(); + if (!Constant) { + return makeUnexpected(ErrorCode::VerificationError, "constant-pool memory base has no index"); + } + auto ID = constantPoolID(*Constant); + if (!ID) + return std::unexpected(ID.error()); + Output.append("%const."); + Output.append(std::to_string(*ID)); + break; + } + case MemoryBase::Kind::JumpTable: { + const auto JumpTable = Base.jumpTableIndex(); + if (!JumpTable) { + return makeUnexpected(ErrorCode::VerificationError, "jump-table memory base has no index"); + } + auto ID = jumpTableID(*JumpTable); + if (!ID) + return std::unexpected(ID.error()); + Output.append("%jump-table."); + Output.append(std::to_string(*ID)); + break; + } + case MemoryBase::Kind::GlobalSymbol: { + const auto &Name = Base.symbolName(); + if (!Name) { + return makeUnexpected(ErrorCode::VerificationError, "global memory base has no symbol name"); + } + Output.push_back('@'); + Output.append(quoteName(*Name)); + break; + } + case MemoryBase::Kind::ExternalSymbol: { + const auto &Name = Base.symbolName(); + if (!Name) { + return makeUnexpected(ErrorCode::VerificationError, + "external memory base has no symbol name"); + } + Output.push_back('&'); + Output.append(quoteName(*Name)); + break; + } + case MemoryBase::Kind::IRValue: { + const auto &Name = Base.symbolName(); + if (!Name) { + return makeUnexpected(ErrorCode::VerificationError, "IR-value memory base has no value name"); + } + Output.append("%ir."); + Output.append(quoteName(*Name)); + break; + } + case MemoryBase::Kind::IRConstant: { + const GenericConstantData *Constant = Base.constantValue(); + if (Constant == nullptr) { + return makeUnexpected(ErrorCode::VerificationError, + "IR-constant memory base has no constant value"); + } + auto Printed = printLLVMConstant(*Constant); + if (!Printed) + return std::unexpected(Printed.error()); + Output.push_back('`'); + Output.append(*Printed); + Output.push_back('`'); + break; + } + case MemoryBase::Kind::Stack: + Output.append("stack"); + break; + case MemoryBase::Kind::GOT: + Output.append("got"); + break; + case MemoryBase::Kind::ConstantPoolPseudo: + Output.append("constant-pool"); + break; + case MemoryBase::Kind::JumpTablePseudo: + Output.append("jump-table"); + break; + case MemoryBase::Kind::CallEntryGlobal: { + const auto &Name = Base.symbolName(); + if (!Name) { + return makeUnexpected(ErrorCode::VerificationError, + "global call-entry memory base has no symbol name"); + } + Output.append("call-entry @"); + Output.append(quoteName(*Name)); + break; + } + case MemoryBase::Kind::CallEntryExternal: { + const auto &Name = Base.symbolName(); + if (!Name) { + return makeUnexpected(ErrorCode::VerificationError, + "external call-entry memory base has no symbol name"); + } + Output.append("call-entry &"); + Output.append(quoteName(*Name)); + break; + } + case MemoryBase::Kind::Custom: { + const auto &Name = Base.symbolName(); + if (!Name) { + return makeUnexpected(ErrorCode::VerificationError, "custom memory base has no name"); + } + Output.append("custom "); + Output.append(quoteName(*Name)); + break; + } + } + appendOffset(Offset); + return {}; +} + +Expected BodyPrinter::printMemoryOperand(const MachineMemOperand &Operand) { + const MachineMemOperandDesc &Description = Operand.description(); + Output.push_back('('); + if (Description.Accesses.has(MemoryAccessFlag::Volatile)) + Output.append("volatile "); + if (Description.Accesses.has(MemoryAccessFlag::NonTemporal)) + Output.append("non-temporal "); + if (Description.Accesses.has(MemoryAccessFlag::Dereferenceable)) + Output.append("dereferenceable "); + if (Description.Accesses.has(MemoryAccessFlag::Invariant)) + Output.append("invariant "); + if (Description.TargetFlags != 0) { + auto Names = Target.mirText().machineMemOperandFlagNames(Description.TargetFlags); + if (!Names) + return std::unexpected(Names.error()); + for (std::string_view Name : *Names) { + Output.append(quoteName(Name)); + Output.push_back(' '); + } + } + + if (Operand.isLoad()) + Output.append("load"); + else + Output.append("store"); + if (Operand.isLoad() && Operand.isStore()) + Output.append(" store"); + + if (Description.SyncScope.value() != 0) { + const auto Scope = + std::ranges::find_if(Context.SyncScopes, [&Description](const MISyncScopeSlot &Slot) { + return Slot.ID == Description.SyncScope; + }); + if (Scope == Context.SyncScopes.end()) { + return makeUnexpected(ErrorCode::VerificationError, "synchronization scope has no MIR name"); + } + Output.append(" syncscope("); + Output.append(quoteString(Scope->Name)); + Output.push_back(')'); + } + if (Description.SuccessOrdering != AtomicOrdering::NotAtomic) { + Output.push_back(' '); + Output.append(atomicOrderingName(Description.SuccessOrdering)); + } + if (Description.FailureOrdering != AtomicOrdering::NotAtomic) { + Output.push_back(' '); + Output.append(atomicOrderingName(Description.FailureOrdering)); + } + + Output.push_back(' '); + if (Description.MemoryType) { + auto Type = llvmMIRLowLevelType(*Description.MemoryType); + if (!Type) + return std::unexpected(Type.error()); + Output.push_back('('); + Output.append(*Type); + Output.push_back(')'); + } else { + switch (Description.Size.kind()) { + case MemorySize::Kind::Unknown: + Output.append("unknown-size"); + break; + case MemorySize::Kind::Fixed: + Output.append(std::to_string(Description.Size.minimumBytes())); + break; + case MemorySize::Kind::Scalable: + Output.append("()"); + break; + } + } + + const std::string_view Preposition = + Operand.isLoad() && Operand.isStore() ? " on " : (Operand.isLoad() ? " from " : " into "); + if (Description.Base.kind() != MemoryBase::Kind::Unknown || Description.Offset != 0) { + Output.append(Preposition); + if (auto Printed = printMemoryBase(Description.Base, Description.Offset); !Printed) { + return Printed; + } + } + + if (!Description.Size.isKnown() || + (Description.Size.minimumBytes() != 0 && + Description.Align.bytes() != Description.Size.minimumBytes())) { + Output.append(", align "); + Output.append(std::to_string(Description.Align.bytes())); + } + if (Description.BaseAlign && *Description.BaseAlign != Description.Align) { + Output.append(", basealign "); + Output.append(std::to_string(Description.BaseAlign->bytes())); + } + if (Description.AddressSpace != 0) { + Output.append(", addrspace "); + Output.append(std::to_string(Description.AddressSpace)); + } + + const auto PrintMetadata = + [this](std::string_view Name, + const std::optional &Reference) -> Expected { + if (!Reference) + return {}; + Output.append(", "); + Output.append(Name); + Output.push_back(' '); + return printMetadataReference(*Reference); + }; + if (auto Printed = PrintMetadata("!tbaa", Description.TBAA); !Printed) + return Printed; + if (auto Printed = PrintMetadata("!alias.scope", Description.AliasScope); !Printed) { + return Printed; + } + if (auto Printed = PrintMetadata("!noalias", Description.NoAlias); !Printed) + return Printed; + if (auto Printed = PrintMetadata("!noalias.addrspace", Description.NoAliasAddressSpace); + !Printed) { + return Printed; + } + if (auto Printed = PrintMetadata("!range", Description.RangeMetadata); !Printed) { + return Printed; + } + Output.push_back(')'); + return {}; +} + +Expected BodyPrinter::printInstruction(const MachineInstr &Instruction) { + const InstructionDescriptor *Descriptor = Target.instructions().descriptor(Instruction.opcode()); + if (Descriptor == nullptr) { + return makeUnexpected(ErrorCode::VerificationError, + "machine instruction has no target descriptor"); + } + std::size_t ExplicitDefs = 0; + while (ExplicitDefs < Instruction.operandCount()) { + const MachineOperand *Operand = Instruction.operandAt(ExplicitDefs); + if (Operand == nullptr || !Operand->isRegister() || Operand->isImplicit() || + !Operand->isDef() || Operand->isUse()) + break; + ++ExplicitDefs; + } + + Output.append(" "); + for (std::size_t Index = 0; Index < ExplicitDefs; ++Index) { + if (Index != 0) + Output.append(", "); + if (auto Printed = printOperand(*Instruction.operandAt(Index), true); !Printed) { + return Printed; + } + } + if (ExplicitDefs != 0) + Output.append(" = "); + + const MachineInstrFlags Flags = Instruction.flags(); + constexpr std::array FlagSpellings{ + std::pair{MachineInstrFlag::FrameSetup, std::string_view{"frame-setup"}}, + std::pair{MachineInstrFlag::FrameDestroy, std::string_view{"frame-destroy"}}, + std::pair{MachineInstrFlag::FastMathNoNaNs, std::string_view{"nnan"}}, + std::pair{MachineInstrFlag::FastMathNoInfs, std::string_view{"ninf"}}, + std::pair{MachineInstrFlag::FastMathNoSignedZeros, std::string_view{"nsz"}}, + std::pair{MachineInstrFlag::FastMathAllowReciprocal, std::string_view{"arcp"}}, + std::pair{MachineInstrFlag::FastMathAllowContract, std::string_view{"contract"}}, + std::pair{MachineInstrFlag::FastMathApproxFunc, std::string_view{"afn"}}, + std::pair{MachineInstrFlag::FastMathAllowReassoc, std::string_view{"reassoc"}}, + std::pair{MachineInstrFlag::NoUnsignedWrap, std::string_view{"nuw"}}, + std::pair{MachineInstrFlag::NoSignedWrap, std::string_view{"nsw"}}, + std::pair{MachineInstrFlag::NoUSWrap, std::string_view{"nusw"}}, + std::pair{MachineInstrFlag::Exact, std::string_view{"exact"}}, + std::pair{MachineInstrFlag::NoFPExcept, std::string_view{"nofpexcept"}}, + std::pair{MachineInstrFlag::Unpredictable, std::string_view{"unpredictable"}}, + std::pair{MachineInstrFlag::NoConvergent, std::string_view{"noconvergent"}}, + std::pair{MachineInstrFlag::NonNeg, std::string_view{"nneg"}}, + std::pair{MachineInstrFlag::Disjoint, std::string_view{"disjoint"}}, + std::pair{MachineInstrFlag::SameSign, std::string_view{"samesign"}}, + std::pair{MachineInstrFlag::InBounds, std::string_view{"inbounds"}}, + }; + for (const auto &[Flag, Spelling] : FlagSpellings) { + if (Flags.has(Flag)) { + Output.append(Spelling); + Output.push_back(' '); + } + } + Output.append(Descriptor->Name); + + bool HasPrintedTail = false; + const bool IsInlineAsm = Descriptor->Name == "INLINEASM" || Descriptor->Name == "INLINEASM_BR"; + std::size_t InlineAsmPayloadRemaining = 0; + for (std::size_t Index = ExplicitDefs; Index < Instruction.operandCount(); ++Index) { + Output.append(HasPrintedTail ? ", " : " "); + HasPrintedTail = true; + const MachineOperand &Operand = *Instruction.operandAt(Index); + if (IsInlineAsm && Index == InlineAsmExtraInfoOperandIndex) { + auto Printed = printInlineAsmExtraInfo(Operand); + if (!Printed) + return Printed; + continue; + } + if (IsInlineAsm && Index >= InlineAsmFirstConstraintOperandIndex && + InlineAsmPayloadRemaining == 0) { + auto OperandCount = printInlineAsmOperandFlag(Operand); + if (!OperandCount) + return std::unexpected(OperandCount.error()); + InlineAsmPayloadRemaining = *OperandCount; + continue; + } + bool PrintVirtualType = true; + if (Descriptor->Flags.has(InstructionFlag::Generic) && + !Descriptor->Flags.has(InstructionFlag::Phi) && Index < Descriptor->Operands.size()) { + const std::optional &UseConstraint = + Descriptor->Operands[Index].TypeConstraint; + if (UseConstraint) { + for (std::size_t DefIndex = 0; DefIndex < ExplicitDefs; ++DefIndex) { + if (DefIndex >= Descriptor->Operands.size()) + break; + const std::optional &DefConstraint = + Descriptor->Operands[DefIndex].TypeConstraint; + if (DefConstraint && DefConstraint->EqualityGroup == UseConstraint->EqualityGroup) { + PrintVirtualType = false; + break; + } + } + } else if (const Register *UseRegister = Operand.getIf(); + UseRegister != nullptr && UseRegister->isVirtual()) { + const auto UseInfo = Function.registerInfo().virtualRegisterInfo(*UseRegister); + for (std::size_t DefIndex = 0; DefIndex < ExplicitDefs && UseInfo && UseInfo->get().Type; + ++DefIndex) { + if (DefIndex >= Descriptor->Operands.size() || + Descriptor->Operands[DefIndex].TypeConstraint) + continue; + const MachineOperand *DefOperand = Instruction.operandAt(DefIndex); + const Register *DefRegister = + DefOperand == nullptr ? nullptr : DefOperand->getIf(); + if (DefRegister == nullptr || !DefRegister->isVirtual()) + continue; + const auto DefInfo = Function.registerInfo().virtualRegisterInfo(*DefRegister); + if (DefInfo && DefInfo->get().Type == UseInfo->get().Type) { + PrintVirtualType = false; + break; + } + } + } + } + auto Printed = printOperand(Operand, false, PrintVirtualType); + if (!Printed) + return Printed; + if (IsInlineAsm && InlineAsmPayloadRemaining != 0) + --InlineAsmPayloadRemaining; + } + if (InlineAsmPayloadRemaining != 0) + return makeUnexpected(ErrorCode::VerificationError, + "inline assembly operand flag has missing payload operands"); + + const auto BeginAttachment = [this, &HasPrintedTail](std::string_view Name) { + Output.append(HasPrintedTail ? ", " : " "); + HasPrintedTail = true; + Output.append(Name); + Output.push_back(' '); + }; + if (Instruction.preInstrSymbol()) { + BeginAttachment("pre-instr-symbol"); + Output.append("'); + } + if (Instruction.postInstrSymbol()) { + BeginAttachment("post-instr-symbol"); + Output.append("'); + } + if (Instruction.heapAllocMarker()) { + BeginAttachment("heap-alloc-marker"); + if (auto Printed = printMetadataReference(*Instruction.heapAllocMarker()); !Printed) { + return Printed; + } + } + if (Instruction.pcSections()) { + BeginAttachment("pcsections"); + if (auto Printed = printMetadataReference(*Instruction.pcSections()); !Printed) { + return Printed; + } + } + if (Instruction.mmra()) { + BeginAttachment("mmra"); + if (auto Printed = printMetadataReference(*Instruction.mmra()); !Printed) + return Printed; + } + if (Instruction.cfiType()) { + BeginAttachment("cfi-type"); + Output.append(std::to_string(*Instruction.cfiType())); + } + if (Instruction.deactivationSymbol()) { + BeginAttachment("deactivation-symbol"); + Output.push_back('@'); + Output.append(quoteName(*Instruction.deactivationSymbol())); + } + if (Instruction.debugInstrNumber() != 0) { + BeginAttachment("debug-instr-number"); + Output.append(std::to_string(Instruction.debugInstrNumber())); + } + if (Instruction.debugLocation()) { + BeginAttachment("debug-location"); + if (const MachineMetadataReference *Reference = Instruction.debugLocation()->reference()) { + if (auto Printed = printMetadataReference(*Reference); !Printed) + return Printed; + } else if (const MachineDILocation *Location = Instruction.debugLocation()->inlineLocation()) { + if (auto Printed = printDILocation(*Location, 0); !Printed) + return Printed; + } else { + return makeUnexpected(ErrorCode::VerificationError, + "machine debug location has no printable value"); + } + } + + if (!Instruction.memOperands().empty()) { + Output.append(" :: "); + for (std::size_t Index = 0; Index < Instruction.memOperands().size(); ++Index) { + if (Index != 0) + Output.append(", "); + if (auto Printed = printMemoryOperand(*Instruction.memOperands()[Index]); !Printed) { + return Printed; + } + } + } + return {}; +} + +Expected BodyPrinter::printBlock(const MachineBasicBlock &Block) { + auto ID = blockID(Block); + if (!ID) + return std::unexpected(ID.error()); + Output.append("bb."); + Output.append(std::to_string(*ID)); + const auto &Name = Block.attributes().name(); + if (Name) { + Output.push_back('.'); + Output.append(quoteName(*Name)); + } + + std::vector Attributes; + const auto AddFlag = [&Block, &Attributes](MachineBasicBlockAttribute Attribute, + std::string_view Spelling) { + if (Block.attributes().has(Attribute)) + Attributes.emplace_back(Spelling); + }; + AddFlag(MachineBasicBlockAttribute::MachineBlockAddressTaken, "machine-block-address-taken"); + if (Block.attributes().has(MachineBasicBlockAttribute::IRBlockAddressTaken)) { + std::string Attribute = "ir-block-address-taken"; + const auto &IRBlockName = Block.attributes().irBlockName(); + if (IRBlockName) { + Attribute.append(" %ir-block."); + Attribute.append(quoteName(*IRBlockName)); + } + Attributes.push_back(std::move(Attribute)); + } else { + const auto &IRBlockName = Block.attributes().irBlockName(); + if (IRBlockName) + Attributes.push_back("%ir-block." + quoteName(*IRBlockName)); + } + AddFlag(MachineBasicBlockAttribute::LandingPad, "landing-pad"); + AddFlag(MachineBasicBlockAttribute::InlineAsmBrIndirectTarget, "inlineasm-br-indirect-target"); + AddFlag(MachineBasicBlockAttribute::EHFuncletEntry, "ehfunclet-entry"); + AddFlag(MachineBasicBlockAttribute::EHScopeEntry, "ehscope-entry"); + if (Block.attributes().alignment().bytes() != 1) { + Attributes.push_back("align " + std::to_string(Block.attributes().alignment().bytes())); + } + const MachineBasicBlockSectionID Section = Block.attributes().sectionID(); + if (!Section.isDefault()) { + std::string Value; + if (Section.kind() == MachineBasicBlockSectionKind::Exception) + Value = "Exception"; + else if (Section.kind() == MachineBasicBlockSectionKind::Cold) + Value = "Cold"; + else + Value = std::to_string(Section.number()); + Attributes.push_back("bbsections " + Value); + } + const auto &ProfileID = Block.attributes().profileID(); + if (ProfileID) { + const MachineBasicBlockProfileID Profile = *ProfileID; + Attributes.push_back("bb_id " + std::to_string(Profile.BaseID) + "." + + std::to_string(Profile.CloneID)); + } + if (Block.attributes().callFrameSize() != 0) { + Attributes.push_back("call-frame-size " + std::to_string(Block.attributes().callFrameSize())); + } + if (!Attributes.empty()) { + Output.append(" ("); + for (std::size_t Index = 0; Index < Attributes.size(); ++Index) { + if (Index != 0) + Output.append(", "); + Output.append(Attributes[Index]); + } + Output.push_back(')'); + } + Output.append(":\n"); + + bool HasLineAttributes = false; + if (!Block.successors().empty()) { + Output.append(" successors: "); + for (std::size_t Index = 0; Index < Block.successors().size(); ++Index) { + if (Index != 0) + Output.append(", "); + const MachineSuccessor &Successor = Block.successors()[Index]; + if (Successor.Block == nullptr || !BlockIDs.contains(Successor.Block)) { + return makeUnexpected(ErrorCode::VerificationError, + "successor references an unlinked block"); + } + appendBlockReference(*Successor.Block); + Output.append("(0x"); + Output.append(hexadecimalWords( + std::array{static_cast(Successor.Probability.rawValue())}, 32)); + Output.push_back(')'); + } + Output.push_back('\n'); + HasLineAttributes = true; + } + if (!Block.liveIns().empty()) { + Output.append(" liveins: "); + for (std::size_t Index = 0; Index < Block.liveIns().size(); ++Index) { + if (Index != 0) + Output.append(", "); + const MachineBasicBlockLiveIn &LiveIn = Block.liveIns()[Index]; + if (auto Printed = appendPhysicalRegister(LiveIn.PhysReg); !Printed) + return Printed; + if (LiveIn.Lanes != LaneMask::all()) { + Output.append(": 0x"); + Output.append(hexadecimalWords(std::array{LiveIn.Lanes.rawValue()}, 64)); + } + } + Output.push_back('\n'); + HasLineAttributes = true; + } + + if (HasLineAttributes && Block.firstInstruction() != nullptr) + Output.push_back('\n'); + + bool InBundle = false; + for (const MachineInstr *Instruction = Block.firstInstruction(); Instruction != nullptr; + Instruction = Instruction->next()) { + if (auto Printed = printInstruction(*Instruction); !Printed) + return Printed; + const bool HasPredecessor = Instruction->flags().has(MachineInstrFlag::BundledWithPred); + const bool HasSuccessor = Instruction->flags().has(MachineInstrFlag::BundledWithSucc); + if (!InBundle && HasSuccessor && !HasPredecessor) { + Output.append(" {"); + InBundle = true; + } + Output.push_back('\n'); + if (InBundle && !HasSuccessor) { + Output.append(" }\n"); + InBundle = false; + } + } + if (InBundle) { + return makeUnexpected(ErrorCode::VerificationError, + "unterminated instruction bundle cannot be printed"); + } + return {}; +} + +Expected BodyPrinter::run() { + auto Verification = MachineVerifier::verify( + Function, Target, + MachineVerifierOptions{ + .Mode = MachineVerifierMode::Generic, .Stage = std::nullopt, .VerifyProperties = false}); + if (!Verification) + return std::unexpected(Verification.error()); + if (auto Initialized = initialize(); !Initialized) + return std::unexpected(Initialized.error()); + + bool First = true; + for (const MachineBasicBlock *Block = Function.firstBlock(); Block != nullptr; + Block = Block->next()) { + if (!First) + Output.push_back('\n'); + First = false; + if (auto Printed = printBlock(*Block); !Printed) + return std::unexpected(Printed.error()); + } + return std::move(Output); +} + +} // namespace + +Expected printMachineFunctionBody(const MachineFunction &Function, + const TargetDescription &Target, + const MIParserContext &Context, + MIPrinterOptions Options) { + return BodyPrinter(Function, Target, Context, Options).run(); +} + +} // namespace rcc::mir diff --git a/backend/lib/CodeGen/MIRYamlMapping.cpp b/backend/lib/CodeGen/MIRYamlMapping.cpp new file mode 100644 index 0000000..9ca8cad --- /dev/null +++ b/backend/lib/CodeGen/MIRYamlMapping.cpp @@ -0,0 +1,2926 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "rcc/CodeGen/MIRYamlMapping.h" + +#include "MIRParser/MILexer.h" +#include "rcc/CodeGen/MachineBasicBlock.h" +#include "rcc/CodeGen/MachineConstantPool.h" +#include "rcc/CodeGen/MachineFrameInfo.h" +#include "rcc/CodeGen/MachineFunction.h" +#include "rcc/CodeGen/MachineFunctionAttributes.h" +#include "rcc/CodeGen/MachineFunctionProperties.h" +#include "rcc/CodeGen/MachineFunctionRecords.h" +#include "rcc/CodeGen/MachineInstr.h" +#include "rcc/CodeGen/MachineJumpTableInfo.h" +#include "rcc/CodeGen/MachineMetadata.h" +#include "rcc/CodeGen/MachineRegisterInfo.h" +#include "rcc/CodeGen/MachineVerifier.h" +#include "rcc/CodeGen/TargetDescription.h" +#include "rcc/CodeGen/TargetMIRTextInfo.h" +#include "rcc/CodeGen/TargetRegisterInfo.h" +#include "rcc/MIR/LLVMConstantSyntax.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace rcc::mir { + +namespace parser_detail { + +[[nodiscard]] inline std::string_view trim(std::string_view Text) noexcept { + while (!Text.empty() && (Text.front() == ' ' || Text.front() == '\t' || Text.front() == '\r' || + Text.front() == '\n')) { + Text.remove_prefix(1); + } + while (!Text.empty() && (Text.back() == ' ' || Text.back() == '\t' || Text.back() == '\r' || + Text.back() == '\n')) { + Text.remove_suffix(1); + } + return Text; +} + +[[nodiscard]] inline bool parseUnsigned(std::string_view Text, std::uint64_t &Value) noexcept { + Text = trim(Text); + int Base = 10; + if (Text.starts_with("0x") || Text.starts_with("0X")) { + Text.remove_prefix(2); + Base = 16; + } + if (Text.empty()) + return false; + const auto [End, Error] = std::from_chars(Text.data(), Text.data() + Text.size(), Value, Base); + return Error == std::errc{} && End == Text.data() + Text.size(); +} + +[[nodiscard]] inline bool parseSigned(std::string_view Text, std::int64_t &Value) noexcept { + Text = trim(Text); + if (Text.empty()) + return false; + const auto [End, Error] = std::from_chars(Text.data(), Text.data() + Text.size(), Value, 10); + return Error == std::errc{} && End == Text.data() + Text.size(); +} + +[[nodiscard]] inline bool parseUnsigned32(std::string_view Text, std::uint32_t &Value) noexcept { + std::uint64_t Wide = 0; + if (!parseUnsigned(Text, Wide) || Wide > std::numeric_limits::max()) + return false; + Value = static_cast(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 +parsePrefixedID(std::string_view Text, std::string_view Prefix) noexcept { + Text = trim(Text); + if (!Text.starts_with(Prefix)) + return std::nullopt; + Text.remove_prefix(Prefix.size()); + const std::size_t Suffix = Text.find_first_of(". \t"); + if (Suffix != std::string_view::npos) + Text = Text.substr(0, Suffix); + std::uint32_t ID = 0; + if (!parseUnsigned32(Text, ID)) + return std::nullopt; + return ID; +} + +} // namespace parser_detail + +class MetadataTextParser final { +public: + using Resolver = std::function(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 parseReferenceOnly(); + [[nodiscard]] Expected parseValue(); + [[nodiscard]] Expected parseLocation(); + + [[nodiscard]] const TextRange &diagnosticRange() const noexcept { + return token().range(); + } + +private: + [[nodiscard]] const MIToken &token() const noexcept { + return Tokens[Position]; + } + [[nodiscard]] bool at(MITokenKind Kind) const noexcept { + return token().is(Kind); + } + void advance() noexcept { + if (Position + 1U < Tokens.size()) + ++Position; + } + [[nodiscard]] bool consume(MITokenKind Kind) noexcept { + if (!at(Kind)) + return false; + advance(); + return true; + } + [[nodiscard]] Expected expect(MITokenKind Kind, std::string_view Description); + [[nodiscard]] Expected finish(); + [[nodiscard]] Expected parseReference(); + [[nodiscard]] Expected parseExpression(); + [[nodiscard]] Expected parseDILocation(std::vector &Frames, + std::optional &Terminal); + + MILexer Lexer; + Resolver Resolve; + std::vector Tokens; + std::size_t Position = 0; +}; + +using parser_detail::parseBool; +using parser_detail::parsePrefixedID; +using parser_detail::parseSigned; +using parser_detail::parseUnsigned; +using parser_detail::parseUnsigned32; +using parser_detail::trim; + +bool MIRDocumentParser::boolean(const yaml::Node &Node, bool &Value) { + std::string_view Text; + if (scalar(Node, Text, "boolean") || !parseBool(Text, Value)) + return fail(Node.range(), "expected 'true' or 'false'"); + return false; +} + +bool MIRDocumentParser::unsignedValue(const yaml::Node &Node, std::uint64_t &Value, + std::string_view Description) { + std::string_view Text; + if (scalar(Node, Text, Description) || !parseUnsigned(Text, Value)) + return fail(Node.range(), "expected " + std::string(Description)); + return false; +} + +bool MIRDocumentParser::signedValue(const yaml::Node &Node, std::int64_t &Value, + std::string_view Description) { + std::string_view Text; + if (scalar(Node, Text, Description) || !parseSigned(Text, Value)) + return fail(Node.range(), "expected " + std::string(Description)); + return false; +} + +bool MIRDocumentParser::alignment(const yaml::Node &Node, Alignment &Value, bool AllowZero) { + std::uint64_t Bytes = 0; + if (unsignedValue(Node, Bytes, "an unsigned alignment")) + return true; + if (Bytes == 0 && AllowZero) { + Value = Alignment{}; + return false; + } + auto Parsed = Alignment::create(Bytes); + if (!Parsed) + return importError(Node.range(), Parsed.error()); + Value = *Parsed; + return false; +} + +bool MIRDocumentParser::physicalRegister(std::string_view Text, const TextRange &Range, + PhysicalRegister &Reg) { + Text = trim(Text); + if (!Text.starts_with('$') || Text.size() == 1) + return fail(Range, "physical register must use the '$name' MIR spelling"); + const PhysicalRegisterDesc *Description = Target.registers().physicalRegister(Text.substr(1)); + if (Description == nullptr) + return fail(Range, "unknown physical register '" + std::string(Text) + "'"); + Reg = Description->Reg; + return false; +} + +bool MIRDocumentParser::physicalRegister(const yaml::Node &Node, PhysicalRegister &Reg) { + std::string_view Text; + return scalar(Node, Text, "physical register") || physicalRegister(Text, Node.range(), Reg); +} + +bool MIRDocumentParser::virtualRegister(std::string_view Text, const TextRange &Range, + MachineFunction &Function, MIParserContext &Context, + Register &Reg) { + const auto ID = parsePrefixedID(Text, "%"); + if (!ID) + return fail(Range, "virtual register must use the '%N' MIR spelling"); + auto Found = + std::ranges::find_if(Context.VirtualRegisters, + [ID](const MIVirtualRegisterSlot &Slot) { return Slot.TextID == *ID; }); + if (Found == Context.VirtualRegisters.end()) { + auto Created = Function.registerInfo().createVirtualRegister(); + if (!Created) + return importError(Range, Created.error()); + Context.VirtualRegisters.push_back({*ID, *Created}); + Reg = *Created; + return false; + } + Reg = Found->Reg; + return false; +} + +bool MIRDocumentParser::parseConstants(const yaml::Node &Root, MachineFunction &Function, + MIParserContext &Context) { + const yaml::Node *Constants = mappingValue(Root, "constants"); + if (Constants == nullptr) + return false; + if (requireKind(*Constants, yaml::NodeKind::Sequence, "constant pool")) + return true; + constexpr std::array Keys{ + std::string_view{"id"}, + std::string_view{"value"}, + std::string_view{"alignment"}, + std::string_view{"isTargetSpecific"}, + }; + std::vector 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 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::max()) { + return true; + } + const auto TextID = static_cast(WideID); + if (!IDs.insert(TextID).second) + return fail(IDNode->range(), "duplicate constant pool ID"); + + bool IsTargetSpecific = false; + if (const yaml::Node *TargetNode = mappingValue(*Constant, "isTargetSpecific")) { + if (boolean(*TargetNode, IsTargetSpecific)) + return true; + } + if (IsTargetSpecific) { + std::string_view ValueText; + if (scalar(*ValueNode, ValueText, "target constant")) + return true; + auto TargetConstantValue = Target.mirText().parseTargetConstant(ValueText); + if (!TargetConstantValue) + return importError(ValueNode->range(), TargetConstantValue.error(), "mir-target-constant"); + auto Emission = TargetConstantValue->emit(); + if (!Emission) + return importError(ValueNode->range(), Emission.error(), "mir-target-constant"); + auto RequiredAlignment = TargetConstantValue->requiredAlignment(); + if (!RequiredAlignment) + return importError(ValueNode->range(), RequiredAlignment.error(), "mir-target-constant"); + + MachineConstantPoolEntryDesc Description; + Description.Size = Emission->Bytes.size(); + Description.Align = *RequiredAlignment; + Description.Value = std::move(*TargetConstantValue); + if (const yaml::Node *AlignmentNode = mappingValue(*Constant, "alignment")) { + if (alignment(*AlignmentNode, Description.Align, true)) + return true; + } + auto Added = Function.constantPool().addEntry(std::move(Description)); + if (!Added) + return importError(Constant->range(), Added.error()); + Context.ConstantPoolItems.push_back({.TextID = TextID, .Index = *Added}); + continue; + } + + std::string_view ValueText; + if (scalar(*ValueNode, ValueText, "LLVM constant")) + return true; + const LLVMConstantSyntaxOptions ConstantOptions{ + .DataLayout = DataLayout, + .NamedTypes = NamedTypes, + }; + auto Data = parseLLVMConstant(trim(ValueText), ConstantOptions); + if (!Data) + return importError(ValueNode->range(), Data.error(), "mir-llvm-constant"); + auto Size = llvmConstantStorageSize(Data->Type, ConstantOptions); + if (!Size) + return importError(ValueNode->range(), Size.error(), "mir-llvm-constant"); + + MachineConstantPoolEntryDesc Description; + Description.Size = *Size; + Description.Value = std::move(*Data); + if (const yaml::Node *AlignmentNode = mappingValue(*Constant, "alignment")) { + if (alignment(*AlignmentNode, Description.Align, true)) + return true; + } else { + auto Preferred = llvmConstantPreferredAlignment( + std::get(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 &Pending) { + const yaml::Node *JumpTable = mappingValue(Root, "jumpTable"); + if (JumpTable == nullptr) + return false; + if (requireKind(*JumpTable, yaml::NodeKind::Mapping, "jump table")) + return true; + constexpr std::array JumpTableKeys{ + std::string_view{"kind"}, + std::string_view{"entries"}, + }; + if (validateKeys(*JumpTable, JumpTableKeys, "jump table")) + return true; + const yaml::Node *KindNode = mappingValue(*JumpTable, "kind"); + if (KindNode == nullptr) + return fail(JumpTable->range(), "jump table requires 'kind'"); + std::string_view KindName; + if (scalar(*KindNode, KindName, "jump table kind")) + return true; + + JumpTableEntryEncoding Encoding; + std::uint8_t EntrySize = 8; + if (KindName == "block-address") + Encoding = JumpTableEntryEncoding::BlockAddress; + else if (KindName == "gp-rel64-block-address") + Encoding = JumpTableEntryEncoding::GPRelative64BlockAddress; + else if (KindName == "gp-rel32-block-address") { + Encoding = JumpTableEntryEncoding::GPRelative32BlockAddress; + EntrySize = 4; + } else if (KindName == "label-difference32") { + Encoding = JumpTableEntryEncoding::LabelDifference32; + EntrySize = 4; + } else if (KindName == "label-difference64") + Encoding = JumpTableEntryEncoding::LabelDifference64; + else if (KindName == "inline") { + Encoding = JumpTableEntryEncoding::Inline; + EntrySize = 4; + } else if (KindName == "custom32") { + Encoding = JumpTableEntryEncoding::Custom32; + EntrySize = 4; + } else { + return fail(KindNode->range(), "unknown enumerated scalar"); + } + + const yaml::Node *Entries = mappingValue(*JumpTable, "entries"); + if (Entries == nullptr) + return false; + if (requireKind(*Entries, yaml::NodeKind::Sequence, "jump table entries")) + return true; + constexpr std::array EntryKeys{ + std::string_view{"id"}, + std::string_view{"blocks"}, + }; + std::unordered_set 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::max()) { + return true; + } + const auto TextID = static_cast(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 Pending) { + for (const PendingJumpTable &Table : Pending) { + MachineJumpTableDesc Description; + Description.Encoding = Table.Encoding; + Description.EntrySize = Table.EntrySize; + Description.Align = *Alignment::create(Table.EntrySize); + for (const std::string &Text : Table.Blocks) { + MachineBasicBlock *Block = nullptr; + if (parseBlockReference(Text, Result.Source.range(0, 0), Function, Block)) + return true; + if (Block == nullptr) + return fail(Result.Source.range(0, 0), "reference to undefined machine basic block"); + Description.Targets.push_back(Block); + } + auto DefineResult = + Function.jumpTableInfo().defineReservedTable(Table.Index, std::move(Description)); + if (!DefineResult) + return importError(Result.Source.range(0, 0), DefineResult.error()); + } + return false; +} + +} // namespace rcc::mir + +namespace rcc::mir { +namespace { + +[[nodiscard]] bool parseIndex(std::string_view Text, std::uint64_t &Value) noexcept { + if (Text.empty()) + return false; + const auto [End, Error] = std::from_chars(Text.data(), Text.data() + Text.size(), Value, 10); + return Error == std::errc{} && End == Text.data() + Text.size(); +} + +[[nodiscard]] const auto &namedOpcodes() { + static constexpr auto Opcodes = std::to_array>({ + {"DW_OP_addr", 0x03}, + {"DW_OP_deref", 0x06}, + {"DW_OP_const1u", 0x08}, + {"DW_OP_const1s", 0x09}, + {"DW_OP_const2u", 0x0A}, + {"DW_OP_const2s", 0x0B}, + {"DW_OP_const4u", 0x0C}, + {"DW_OP_const4s", 0x0D}, + {"DW_OP_const8u", 0x0E}, + {"DW_OP_const8s", 0x0F}, + {"DW_OP_constu", 0x10}, + {"DW_OP_consts", 0x11}, + {"DW_OP_dup", 0x12}, + {"DW_OP_drop", 0x13}, + {"DW_OP_over", 0x14}, + {"DW_OP_pick", 0x15}, + {"DW_OP_swap", 0x16}, + {"DW_OP_rot", 0x17}, + {"DW_OP_xderef", 0x18}, + {"DW_OP_abs", 0x19}, + {"DW_OP_and", 0x1A}, + {"DW_OP_div", 0x1B}, + {"DW_OP_minus", 0x1C}, + {"DW_OP_mod", 0x1D}, + {"DW_OP_mul", 0x1E}, + {"DW_OP_neg", 0x1F}, + {"DW_OP_not", 0x20}, + {"DW_OP_or", 0x21}, + {"DW_OP_plus", 0x22}, + {"DW_OP_plus_uconst", 0x23}, + {"DW_OP_shl", 0x24}, + {"DW_OP_shr", 0x25}, + {"DW_OP_shra", 0x26}, + {"DW_OP_xor", 0x27}, + {"DW_OP_bra", 0x28}, + {"DW_OP_eq", 0x29}, + {"DW_OP_ge", 0x2A}, + {"DW_OP_gt", 0x2B}, + {"DW_OP_le", 0x2C}, + {"DW_OP_lt", 0x2D}, + {"DW_OP_ne", 0x2E}, + {"DW_OP_skip", 0x2F}, + {"DW_OP_regx", 0x90}, + {"DW_OP_fbreg", 0x91}, + {"DW_OP_bregx", 0x92}, + {"DW_OP_piece", 0x93}, + {"DW_OP_deref_size", 0x94}, + {"DW_OP_xderef_size", 0x95}, + {"DW_OP_nop", 0x96}, + {"DW_OP_push_object_address", 0x97}, + {"DW_OP_call2", 0x98}, + {"DW_OP_call4", 0x99}, + {"DW_OP_call_ref", 0x9A}, + {"DW_OP_form_tls_address", 0x9B}, + {"DW_OP_call_frame_cfa", 0x9C}, + {"DW_OP_bit_piece", 0x9D}, + {"DW_OP_implicit_value", 0x9E}, + {"DW_OP_stack_value", 0x9F}, + {"DW_OP_implicit_pointer", 0xA0}, + {"DW_OP_addrx", 0xA1}, + {"DW_OP_constx", 0xA2}, + {"DW_OP_entry_value", 0xA3}, + {"DW_OP_const_type", 0xA4}, + {"DW_OP_regval_type", 0xA5}, + {"DW_OP_deref_type", 0xA6}, + {"DW_OP_xderef_type", 0xA7}, + {"DW_OP_convert", 0xA8}, + {"DW_OP_reinterpret", 0xA9}, + {"DW_OP_LLVM_fragment", 0x1000}, + {"DW_OP_LLVM_convert", 0x1001}, + {"DW_OP_LLVM_tag_offset", 0x1002}, + {"DW_OP_LLVM_entry_value", 0x1003}, + {"DW_OP_LLVM_implicit_pointer", 0x1004}, + {"DW_OP_LLVM_arg", 0x1005}, + {"DW_OP_LLVM_extract_bits_sext", 0x1006}, + {"DW_OP_LLVM_extract_bits_zext", 0x1007}, + }); + return Opcodes; +} + +[[nodiscard]] const auto &attributeEncodings() { + static constexpr auto Encodings = std::to_array>({ + {"DW_ATE_address", 0x01}, + {"DW_ATE_boolean", 0x02}, + {"DW_ATE_complex_float", 0x03}, + {"DW_ATE_float", 0x04}, + {"DW_ATE_signed", 0x05}, + {"DW_ATE_signed_char", 0x06}, + {"DW_ATE_unsigned", 0x07}, + {"DW_ATE_unsigned_char", 0x08}, + {"DW_ATE_imaginary_float", 0x09}, + {"DW_ATE_packed_decimal", 0x0A}, + {"DW_ATE_numeric_string", 0x0B}, + {"DW_ATE_edited", 0x0C}, + {"DW_ATE_signed_fixed", 0x0D}, + {"DW_ATE_unsigned_fixed", 0x0E}, + {"DW_ATE_decimal_float", 0x0F}, + {"DW_ATE_UTF", 0x10}, + {"DW_ATE_UCS", 0x11}, + {"DW_ATE_ASCII", 0x12}, + }); + return Encodings; +} + +[[nodiscard]] std::optional opcodeName(std::uint64_t Opcode) { + if (Opcode >= 0x30U && Opcode <= 0x4FU) + return "DW_OP_lit" + std::to_string(Opcode - 0x30U); + if (Opcode >= 0x50U && Opcode <= 0x6FU) + return "DW_OP_reg" + std::to_string(Opcode - 0x50U); + if (Opcode >= 0x70U && Opcode <= 0x8FU) + return "DW_OP_breg" + std::to_string(Opcode - 0x70U); + const auto &Opcodes = namedOpcodes(); + const auto Found = + std::ranges::find_if(Opcodes, [Opcode](const auto &Entry) { return Entry.second == Opcode; }); + if (Found == Opcodes.end()) + return std::nullopt; + return std::string(Found->first); +} + +[[nodiscard]] std::size_t opcodeSize(std::uint64_t Opcode) noexcept { + if (Opcode >= 0x70U && Opcode <= 0x8FU) + return 2; + switch (Opcode) { + case 0x92: + case 0x1000: + case 0x1001: + case 0x1006: + case 0x1007: + return 3; + case 0x10: + case 0x11: + case 0x23: + case 0x90: + case 0x94: + case 0x1002: + case 0x1003: + case 0x1005: + return 2; + default: + return 1; + } +} + +[[nodiscard]] std::optional attributeName(std::uint64_t Encoding) noexcept { + const auto &Encodings = attributeEncodings(); + const auto Found = std::ranges::find_if( + Encodings, [Encoding](const auto &Entry) { return Entry.second == Encoding; }); + if (Found == Encodings.end()) + return std::nullopt; + return Found->first; +} + +} // namespace + +std::optional parseDIExpressionOpcodeName(std::string_view Name) noexcept { + const auto NumberedOpcode = [Name](std::string_view Prefix, + std::uint64_t Base) -> std::optional { + if (!Name.starts_with(Prefix)) + return std::nullopt; + std::uint64_t Index = 0; + if (!parseIndex(Name.substr(Prefix.size()), Index) || Index > 31) + return std::nullopt; + return Base + Index; + }; + if (auto Opcode = NumberedOpcode("DW_OP_lit", 0x30U)) + return Opcode; + if (auto Opcode = NumberedOpcode("DW_OP_reg", 0x50U)) + return Opcode; + if (auto Opcode = NumberedOpcode("DW_OP_breg", 0x70U)) + return Opcode; + + const auto &NamedOpcodes = namedOpcodes(); + const auto *const Found = + std::ranges::find_if(NamedOpcodes, [Name](const auto &Entry) { return Entry.first == Name; }); + if (Found == NamedOpcodes.end()) + return std::nullopt; + return Found->second; +} + +std::optional +parseDIExpressionAttributeEncodingName(std::string_view Name) noexcept { + const auto &Encodings = attributeEncodings(); + const auto Found = + std::ranges::find_if(Encodings, [Name](const auto &Entry) { return Entry.first == Name; }); + if (Found == Encodings.end()) + return std::nullopt; + return Found->second; +} + +std::string invalidDIExpressionElementMessage(std::string_view Name) { + if (Name.starts_with("DW_OP_")) + return "invalid DWARF op '" + std::string(Name) + "'"; + if (Name.starts_with("DW_ATE_")) + return "invalid DWARF attribute encoding '" + std::string(Name) + "'"; + return "expected unsigned integer"; +} + +std::string formatDIExpression(std::span Elements) { + std::string Result = "!DIExpression("; + std::size_t Position = 0; + bool First = true; + while (Position < Elements.size()) { + const std::uint64_t Opcode = Elements[Position]; + const auto Name = opcodeName(Opcode); + const std::size_t Size = opcodeSize(Opcode); + if (!Name || Position + Size > Elements.size()) { + Result = "!DIExpression("; + for (std::size_t Index = 0; Index < Elements.size(); ++Index) { + if (Index != 0) + Result.append(", "); + Result.append(std::to_string(Elements[Index])); + } + Result.push_back(')'); + return Result; + } + + if (!First) + Result.append(", "); + First = false; + Result.append(*Name); + for (std::size_t Argument = 1; Argument < Size; ++Argument) { + Result.append(", "); + if (Opcode == 0x1001U && Argument == 2U) { + if (const auto Encoding = attributeName(Elements[Position + Argument])) { + Result.append(*Encoding); + continue; + } + } + Result.append(std::to_string(Elements[Position + Argument])); + } + Position += Size; + } + Result.push_back(')'); + return Result; +} + +} // namespace rcc::mir + +namespace rcc::mir { + +using parser_detail::parseSigned; +using parser_detail::parseUnsigned; +using parser_detail::parseUnsigned32; + +Expected 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 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 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 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 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 Elements; + while (!at(MITokenKind::RightParen)) { + if (at(MITokenKind::Identifier)) { + const auto Opcode = parseDIExpressionOpcodeName(token().stringValue()); + const auto Encoding = parseDIExpressionAttributeEncodingName(token().stringValue()); + if (!Opcode && !Encoding) + return makeUnexpected(ErrorCode::ParseError, + invalidDIExpressionElementMessage(token().stringValue())); + Elements.push_back(Opcode ? *Opcode : *Encoding); + advance(); + } else if (at(MITokenKind::IntegerLiteral)) { + std::int64_t Signed = 0; + std::uint64_t Unsigned = 0; + if (parseSigned(token().integerValue(), Signed)) + Elements.push_back(static_cast(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 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 +MetadataTextParser::parseDILocation(std::vector &Frames, + std::optional &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 NestedFrames; + std::optional NestedTerminal; + std::unordered_set 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::max()) { + return makeUnexpected(ErrorCode::ParseError, + "DILocation column must be an unsigned 16-bit integer"); + } + Frame.Column = static_cast(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(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 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 Frames; + std::optional Terminal; + if (auto Parsed = parseDILocation(Frames, Terminal); !Parsed) + return std::unexpected(Parsed.error()); + auto Created = MachineDILocation::create(std::move(Frames), Terminal); + if (!Created) + return std::unexpected(Created.error()); + Location = MachineDebugLocation::inlineLocation(std::move(*Created)); + } + if (auto Finished = finish(); !Finished) + return std::unexpected(Finished.error()); + return Location; +} + +} // namespace rcc::mir + +namespace rcc::mir { + +Expected verifyMIRDocumentReferences(const MIRDocument &Document, + const MachineFunction &Function, bool UseEmbeddedIR) { + const EmbeddedIRReferenceIndex *Embedded = UseEmbeddedIR ? Document.embeddedIR() : nullptr; + const EmbeddedIRFunction *IRFunction = nullptr; + if (Embedded != nullptr) { + if (Function.name().empty()) { + std::size_t AnonymousOrdinal = 0; + bool FoundFunction = false; + for (std::size_t Index = 0; Index < Document.unit().functionCount(); ++Index) { + const MachineFunction *Candidate = Document.unit().functionAt(Index); + if (Candidate == &Function) { + FoundFunction = true; + break; + } + if (Candidate != nullptr && Candidate->name().empty()) + ++AnonymousOrdinal; + } + if (FoundFunction) + IRFunction = Embedded->unnamedFunction(AnonymousOrdinal); + } else { + IRFunction = Embedded->function(Function.name()); + } + if (IRFunction == nullptr) { + return makeUnexpected(ErrorCode::VerificationError, + "machine function has no matching embedded LLVM IR function"); + } + } + + const auto ValidateGlobal = [Embedded](std::string_view Name, + std::string_view Description) -> Expected { + 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 { + 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 { + 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 ExpectedTypes, + std::string_view Description) -> Expected { + 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 { + const MachineMetadataReference *Reference = Value.reference(); + return Reference == nullptr ? Expected{} : ValidateMetadata(*Reference, {}, Description); + }; + const auto ValidateLocation = + [&ValidateMetadata](const MachineDebugLocation &Location, + std::string_view Description) -> Expected { + 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 { + 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 { + 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 { + 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(&Description->Value)) { + auto Verification = ValidateGenericConstant(ValidateGenericConstant, *Generic); + if (!Verification) + return Verification; + } else if (const auto *Address = std::get_if(&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(); Global != nullptr) { + auto Verification = ValidateGlobal(Global->Name, "global-symbol operand"); + if (!Verification) + return Verification; + } + if (const auto *Address = Operand.getIf(); Address != nullptr) { + auto Verification = ValidateBlockAddress(Address->FunctionName, Address->BlockName, + "block-address operand"); + if (!Verification) + return Verification; + } + if (const auto *Metadata = Operand.getIf(); 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 BaseName = Description.Base.symbolName(); + switch (Description.Base.kind()) { + case MemoryBase::Kind::GlobalSymbol: + case MemoryBase::Kind::CallEntryGlobal: + if (BaseName) { + auto Verification = ValidateGlobal(*BaseName, "machine memory base"); + if (!Verification) + return Verification; + } + break; + case MemoryBase::Kind::IRValue: + if (BaseName) { + auto Verification = ValidateIRValue(*BaseName, "machine memory base"); + if (!Verification) + return Verification; + } + break; + default: + break; + } + const std::array MetadataReferences{ + Description.TBAA, Description.AliasScope, + Description.NoAlias, Description.NoAliasAddressSpace, + Description.RangeMetadata, + }; + for (const auto &Reference : MetadataReferences) { + if (!Reference) + continue; + auto Verification = ValidateMetadata(*Reference, {}, "machine memory operand metadata"); + if (!Verification) + return Verification; + } + if (Description.SyncScope.value() == 0) + continue; + const auto Scope = + std::ranges::find_if(Document.syncScopes(), [&Description](const auto &Slot) { + return Slot.ID == Description.SyncScope; + }); + if (Scope == Document.syncScopes().end()) { + return makeUnexpected( + ErrorCode::VerificationError, + "machine memory operand synchronization scope has no document name"); + } + } + } + } + return {}; +} + +} // namespace rcc::mir + +namespace rcc::mir { + +using parser_detail::parseBool; +using parser_detail::parsePrefixedID; +using parser_detail::parseSigned; +using parser_detail::parseUnsigned; +using parser_detail::parseUnsigned32; +using parser_detail::trim; + +bool MIRDocumentParser::parseStackObjects(const yaml::Node &Root, MachineFunction &Function, + MIParserContext &Context) { + auto ParseList = [this, &Function, &Context](const yaml::Node &List, bool Fixed) -> bool { + if (requireKind(List, yaml::NodeKind::Sequence, + Fixed ? "fixed stack objects" : "stack objects")) { + return true; + } + constexpr std::array FixedKeys{ + std::string_view{"id"}, + std::string_view{"type"}, + std::string_view{"offset"}, + std::string_view{"size"}, + std::string_view{"alignment"}, + std::string_view{"stack-id"}, + std::string_view{"isImmutable"}, + std::string_view{"isAliased"}, + std::string_view{"callee-saved-register"}, + std::string_view{"callee-saved-restored"}, + std::string_view{"debug-info-variable"}, + std::string_view{"debug-info-expression"}, + std::string_view{"debug-info-location"}, + }; + constexpr std::array StackKeys{ + std::string_view{"id"}, + std::string_view{"name"}, + std::string_view{"type"}, + std::string_view{"offset"}, + std::string_view{"size"}, + std::string_view{"alignment"}, + std::string_view{"stack-id"}, + std::string_view{"callee-saved-register"}, + std::string_view{"callee-saved-restored"}, + std::string_view{"local-offset"}, + std::string_view{"debug-info-variable"}, + std::string_view{"debug-info-expression"}, + std::string_view{"debug-info-location"}, + }; + std::unordered_set 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::max()) { + return true; + } + const auto TextID = static_cast(WideID); + if (!IDs.insert(TextID).second) + return fail(IDNode->range(), "duplicate stack object ID"); + + StackObjectDesc Description; + Description.Kind = Fixed ? StackObjectKind::FixedABI : StackObjectKind::Default; + Description.Offset = 0; + if (const yaml::Node *NameNode = mappingValue(*Element, "name")) { + std::string_view Name; + if (scalar(*NameNode, Name, "stack object name")) + return true; + if (!Name.empty()) + Description.AllocaName = std::string(Name); + } + if (const yaml::Node *TypeNode = mappingValue(*Element, "type")) { + std::string_view Type; + if (scalar(*TypeNode, Type, "stack object type")) + return true; + if (Type == "default") + Description.Kind = Fixed ? StackObjectKind::FixedABI : StackObjectKind::Default; + else if (Type == "spill-slot") + Description.Kind = Fixed ? StackObjectKind::FixedSpillSlot : StackObjectKind::SpillSlot; + else if (!Fixed && Type == "variable-sized") + Description.Kind = StackObjectKind::VariableSized; + else + return fail(TypeNode->range(), "unknown stack object type '" + std::string(Type) + "'"); + } + if (const yaml::Node *OffsetNode = mappingValue(*Element, "offset")) { + std::int64_t Offset = 0; + if (signedValue(*OffsetNode, Offset, "a signed stack object offset")) + return true; + Description.Offset = Offset; + } else if (Fixed) { + Description.Offset = 0; + } + if (Description.Kind != StackObjectKind::VariableSized) { + const yaml::Node *SizeNode = mappingValue(*Element, "size"); + if (SizeNode == nullptr) { + if (!Fixed) + return fail(Element->range(), "fixed-size stack object requires 'size'"); + Description.Size = 0; + } else { + std::uint64_t Size = 0; + if (unsignedValue(*SizeNode, Size, "an unsigned stack object size")) + return true; + Description.Size = Size; + } + } + if (const yaml::Node *AlignmentNode = mappingValue(*Element, "alignment")) { + if (alignment(*AlignmentNode, Description.Align, true)) + return true; + } + if (const yaml::Node *StackIDNode = mappingValue(*Element, "stack-id")) { + std::string_view Name; + if (scalar(*StackIDNode, Name, "stack ID")) + return true; + const auto Stack = parseStackIDName(Name); + if (!Stack) + return fail(StackIDNode->range(), "unknown stack ID '" + std::string(Name) + "'"); + Description.Stack = *Stack; + } + if (const yaml::Node *ImmutableNode = mappingValue(*Element, "isImmutable")) { + if (boolean(*ImmutableNode, Description.IsImmutable)) + return true; + } + if (const yaml::Node *AliasedNode = mappingValue(*Element, "isAliased")) { + if (boolean(*AliasedNode, Description.IsAliased)) + return true; + } + if (const yaml::Node *SavedNode = mappingValue(*Element, "callee-saved-register")) { + std::string_view SavedName; + if (scalar(*SavedNode, SavedName, "physical register")) + return true; + if (!trim(SavedName).empty()) { + PhysicalRegister Reg; + if (physicalRegister(SavedName, SavedNode->range(), Reg)) + return true; + Description.CalleeSavedRegister = Reg; + } + } + if (const yaml::Node *RestoredNode = mappingValue(*Element, "callee-saved-restored")) { + if (boolean(*RestoredNode, Description.CalleeSavedRestored)) + return true; + } + if (const yaml::Node *LocalNode = mappingValue(*Element, "local-offset")) { + std::int64_t Offset = 0; + if (signedValue(*LocalNode, Offset, "a signed local frame offset")) + return true; + Description.LocalOffset = Offset; + } + MachineVariableDebugInfo DebugInfo; + if (parseVariableDebugInfo(*Element, Function, DebugInfo)) + return true; + if (DebugInfo.Variable || DebugInfo.Expression || DebugInfo.Location) + Description.DebugInfo = std::move(DebugInfo); + + auto Created = Function.frameInfo().createObject(std::move(Description)); + if (!Created) + return importError(Element->range(), Created.error()); + MIFrameObjectSlot Slot{ + .TextID = TextID, + .Index = *Created, + .Name = std::nullopt, + }; + if (const StackObjectDesc *CreatedDescription = + Function.frameInfo().object(*Created)->description(); + CreatedDescription != nullptr && CreatedDescription->AllocaName) { + Slot.Name = *CreatedDescription->AllocaName; + } + (Fixed ? Context.FixedStackObjects : Context.StackObjects).push_back(std::move(Slot)); + } + return false; + }; + + if (const yaml::Node *Fixed = mappingValue(Root, "fixedStack")) { + if (ParseList(*Fixed, true)) + return true; + } + if (const yaml::Node *Stack = mappingValue(Root, "stack")) { + if (ParseList(*Stack, false)) + return true; + } + return false; +} + +bool MIRDocumentParser::parseFrameReference(const yaml::Node &Node, const MIParserContext &Context, + FrameIndex &Index) { + std::string_view Text; + if (scalar(Node, Text, "frame index")) + return true; + bool Fixed = false; + std::optional 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 &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::min() || + Wide > std::numeric_limits::max()) { + return fail(Value->range(), "offset adjustment is outside the 32-bit signed range"); + } + State.OffsetAdjustment = static_cast(Wide); + } + if (const yaml::Node *Value = mappingValue(*Frame, "maxAlignment")) { + std::uint64_t Bytes = 0; + if (unsignedValue(*Value, Bytes, "an unsigned maximum alignment")) + return true; + if (Bytes == 0) { + State.MaxAlignment.reset(); + } else { + auto Parsed = Alignment::create(Bytes); + if (!Parsed) + return importError(Value->range(), Parsed.error()); + State.MaxAlignment = *Parsed; + } + } + if (const yaml::Node *Value = mappingValue(*Frame, "framePointerPolicy")) { + std::string_view Name; + if (scalar(*Value, Name, "frame-pointer policy")) + return true; + const auto Policy = parseMachineFramePointerPolicy(Name); + if (!Policy) + return fail(Value->range(), "unknown frame-pointer policy '" + std::string(Name) + "'"); + State.FramePointerPolicy = *Policy; + } + for (const auto &[Key, Destination] : {std::pair{"stackProtector", &State.StackProtector}, + std::pair{"functionContext", &State.FunctionContext}}) { + if (const yaml::Node *Value = mappingValue(*Frame, Key)) { + std::string_view Reference; + if (scalar(*Value, Reference, "frame index")) + return true; + if (Reference.empty()) { + Destination->reset(); + continue; + } + FrameIndex Index; + if (parseFrameReference(*Value, Context, Index)) + return true; + *Destination = Index; + } + } + if (const yaml::Node *Value = mappingValue(*Frame, "maxCallFrameSize")) { + std::uint64_t Size = 0; + if (unsignedValue(*Value, Size, "a 32-bit unsigned maximum call-frame size") || + Size > std::numeric_limits::max()) { + return fail(Value->range(), "maximum call-frame size is outside the 32-bit unsigned range"); + } + if (Size == std::numeric_limits::max()) + State.MaxCallFrameSize.reset(); + else + State.MaxCallFrameSize = Size; + } + for (const auto &[Key, Destination] : + {std::pair{"cvBytesOfCalleeSavedRegisters", &State.CVBytesOfCalleeSavedRegisters}, + std::pair{"localFrameSize", &State.LocalFrameSize}}) { + if (const yaml::Node *Value = mappingValue(*Frame, Key)) { + std::uint64_t Wide = 0; + if (unsignedValue(*Value, Wide, "a 32-bit unsigned frame value") || + Wide > std::numeric_limits::max()) { + return fail(Value->range(), "frame value is outside the 32-bit unsigned range"); + } + *Destination = static_cast(Wide); + } + } + if (const yaml::Node *Value = mappingValue(*Frame, "savePoint")) { + if (parseSaveRestorePoints(*Value, Function, State.SavePoints)) + return true; + } + if (const yaml::Node *Value = mappingValue(*Frame, "restorePoint")) { + if (parseSaveRestorePoints(*Value, Function, State.RestorePoints)) + return true; + } + auto StateResult = Function.frameInfo().setState(std::move(State)); + if (!StateResult) + return importError(Frame->range(), StateResult.error()); + return false; +} + +} // namespace rcc::mir + +namespace rcc::mir { + +using parser_detail::parseBool; +using parser_detail::parsePrefixedID; +using parser_detail::parseSigned; +using parser_detail::parseUnsigned; +using parser_detail::parseUnsigned32; +using parser_detail::trim; + +bool MIRDocumentParser::parseFunctionScalars(const yaml::Node &Root, MachineFunction &Function, + MIRFunctionDocumentInfo &DocumentInfo) { + if (const yaml::Node *AlignNode = mappingValue(Root, "alignment")) { + Alignment Align; + if (alignment(*AlignNode, Align, true)) + return true; + if (Align.bytes() != 1 || AlignNode->scalar() != "0") + Function.attributes().setAlignment(Align); + } + + constexpr std::array PropertyBindings{ + std::pair{"legalized", MachineFunctionProperty::Legalized}, + std::pair{"regBankSelected", MachineFunctionProperty::RegBankSelected}, + std::pair{"selected", MachineFunctionProperty::Selected}, + std::pair{"failedISel", MachineFunctionProperty::FailedISel}, + std::pair{"tracksRegLiveness", MachineFunctionProperty::TracksLiveness}, + std::pair{"noPhis", MachineFunctionProperty::NoPHIs}, + std::pair{"isSSA", MachineFunctionProperty::IsSSA}, + std::pair{"noVRegs", MachineFunctionProperty::NoVRegs}, + std::pair{"failsVerification", MachineFunctionProperty::FailsVerification}, + }; + for (const auto &[Key, Property] : PropertyBindings) { + const yaml::Node *ValueNode = mappingValue(Root, Key); + if (ValueNode == nullptr) + continue; + bool Value = false; + if (boolean(*ValueNode, Value)) + return true; + if (Value) + Function.properties().set(Property); + else + Function.properties().clear(Property); + if (Key == std::string_view("noPhis")) + DocumentInfo.NoPHIs = Value; + else if (Key == std::string_view("isSSA")) + DocumentInfo.IsSSA = Value; + else if (Key == std::string_view("noVRegs")) + DocumentInfo.NoVRegs = Value; + } + + constexpr std::array AttributeBindings{ + std::pair{"exposesReturnsTwice", MachineFunctionAttribute::ExposesReturnsTwice}, + std::pair{"hasWinCFI", MachineFunctionAttribute::HasWinCFI}, + std::pair{"hasFakeUses", MachineFunctionAttribute::HasFakeUses}, + std::pair{"callsEHReturn", MachineFunctionAttribute::CallsEHReturn}, + std::pair{"callsUnwindInit", MachineFunctionAttribute::CallsUnwindInit}, + std::pair{"hasEHContTarget", MachineFunctionAttribute::HasEHContTarget}, + std::pair{"hasEHScopes", MachineFunctionAttribute::HasEHScopes}, + std::pair{"hasEHFunclets", MachineFunctionAttribute::HasEHFunclets}, + std::pair{"isOutlined", MachineFunctionAttribute::IsOutlined}, + std::pair{"debugInstrRef", MachineFunctionAttribute::DebugInstrRef}, + std::pair{"tracksDebugUserValues", MachineFunctionAttribute::TracksDebugUserValues}, + }; + for (const auto &[Key, Attribute] : AttributeBindings) { + const yaml::Node *ValueNode = mappingValue(Root, Key); + if (ValueNode == nullptr) + continue; + bool Value = false; + if (boolean(*ValueNode, Value)) + return true; + if (Value) + Function.attributes().set(Attribute); + else + Function.attributes().clear(Attribute); + if (Key == std::string_view("hasFakeUses")) + DocumentInfo.HasFakeUses = Value; + } + + return false; +} + +bool MIRDocumentParser::recomputeProperties(const yaml::Node &Root, MachineFunction &Function, + const MIRFunctionDocumentInfo &DocumentInfo) { + bool HasPHI = false; + bool HasFakeUse = false; + bool IsSSA = true; + bool HasTiedOperands = false; + bool AllTiedOperandsRewritten = true; + bool HasFrameIndex = false; + bool HasExecutablePseudo = false; + std::unordered_set 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() != 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 Explicit, bool Computed, + MachineFunctionProperty Property, + std::string_view Conflict) -> bool { + if (Explicit && *Explicit && !Computed) { + return fail(Root.range(), std::string(Function.name()) + std::string(Conflict)); + } + if (Explicit.value_or(Computed)) + Function.properties().set(Property); + else + Function.properties().clear(Property); + return false; + }; + if (ApplyComputed(DocumentInfo.NoPHIs, NoPHIs, MachineFunctionProperty::NoPHIs, + " has explicit property NoPhi, but contains at least one PHI") || + ApplyComputed(DocumentInfo.IsSSA, IsSSA, MachineFunctionProperty::IsSSA, + " has explicit property IsSSA, but is not valid SSA") || + ApplyComputed(DocumentInfo.NoVRegs, NoVRegs, MachineFunctionProperty::NoVRegs, + " has explicit property NoVRegs, but contains virtual registers")) { + return true; + } + + if (DocumentInfo.HasFakeUses && !*DocumentInfo.HasFakeUses && HasFakeUse) { + return fail(Root.range(), + std::string(Function.name()) + + " has explicit property hasFakeUses=false, but contains fake uses"); + } + if (DocumentInfo.HasFakeUses.value_or(HasFakeUse)) + Function.attributes().set(MachineFunctionAttribute::HasFakeUses); + else + Function.attributes().clear(MachineFunctionAttribute::HasFakeUses); + + const bool Selected = Function.properties().has(MachineFunctionProperty::Selected); + const bool StructurallyPostSSA = + Selected && NoPHIs && !Function.properties().has(MachineFunctionProperty::IsSSA); + if (AllTiedOperandsRewritten && (HasTiedOperands || StructurallyPostSSA)) + Function.properties().set(MachineFunctionProperty::TiedOpsRewritten); + else + Function.properties().clear(MachineFunctionProperty::TiedOpsRewritten); + + if (HasFrameIndex) + Function.properties().clear(MachineFunctionProperty::NoFrameIndices); + else + Function.properties().set(MachineFunctionProperty::NoFrameIndices); + + const MachineFrameState &FrameState = Function.frameInfo().state(); + const bool StructurallyFinalizedFrame = Selected && NoVRegs && !HasFrameIndex && + FrameState.IsCalleeSavedInfoValid && + FrameState.MaxAlignment.has_value(); + if (StructurallyFinalizedFrame) { + if (!Function.frameInfo().isFrozen()) { + auto Frozen = Function.frameInfo().freeze(FrameState.StackSize, *FrameState.MaxAlignment); + if (!Frozen) + return importError(Root.range(), Frozen.error(), "mir-frame-finalization"); + } + Function.properties().set(MachineFunctionProperty::FrameFinalized); + } else { + Function.properties().clear(MachineFunctionProperty::FrameFinalized); + } + + if (StructurallyFinalizedFrame && !HasExecutablePseudo) + Function.properties().set(MachineFunctionProperty::NoExecutablePseudos); + else + Function.properties().clear(MachineFunctionProperty::NoExecutablePseudos); + return false; +} + +bool MIRDocumentParser::parseRegisters(const yaml::Node &Root, MachineFunction &Function, + MIParserContext &Context) { + const yaml::Node *Registers = mappingValue(Root, "registers"); + if (Registers != nullptr) { + if (requireKind(*Registers, yaml::NodeKind::Sequence, "registers")) + return true; + constexpr std::array RegisterKeys{ + std::string_view{"id"}, + std::string_view{"class"}, + std::string_view{"preferred-register"}, + std::string_view{"flags"}, + }; + std::unordered_set 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::max() || + scalar(*ClassNode, ClassName, "register class or bank")) { + return true; + } + const auto TextID = static_cast(WideID); + if (!IDs.insert(TextID).second) + return fail(IDNode->range(), "duplicate virtual register ID"); + auto Created = Function.registerInfo().createVirtualRegister(); + if (!Created) + return importError(IDNode->range(), Created.error()); + Context.VirtualRegisters.push_back({TextID, *Created}); + + if (ClassName != "_") { + if (const RegisterClassDesc *Class = Target.registers().registerClass(ClassName)) { + auto SetResult = Function.registerInfo().setRegisterClass(*Created, Class->ID); + if (!SetResult) + return importError(ClassNode->range(), SetResult.error()); + SetResult = Function.registerInfo().setRegisterBank(*Created, Class->DefaultBank); + if (!SetResult) + return importError(ClassNode->range(), SetResult.error()); + } else if (const RegisterBankDesc *Bank = Target.registers().registerBank(ClassName)) { + auto SetResult = Function.registerInfo().setRegisterBank(*Created, Bank->ID); + if (!SetResult) + return importError(ClassNode->range(), SetResult.error()); + } else { + return fail(ClassNode->range(), + "unknown register class or bank '" + std::string(ClassName) + "'"); + } + } + + if (const yaml::Node *Preferred = mappingValue(*Element, "preferred-register")) { + std::string_view PreferredName; + PhysicalRegister Reg; + if (scalar(*Preferred, PreferredName, "physical register")) + return true; + if (!trim(PreferredName).empty()) { + if (physicalRegister(PreferredName, Preferred->range(), Reg)) + return true; + auto AddResult = Function.registerInfo().addAllocationHint(*Created, Reg); + if (!AddResult) + return importError(Preferred->range(), AddResult.error()); + } + } + if (const yaml::Node *Flags = mappingValue(*Element, "flags")) { + if (requireKind(*Flags, yaml::NodeKind::Sequence, "virtual-register flags")) + return true; + std::uint8_t Mask = 0; + for (const yaml::NodeID FlagID : Flags->sequence()) { + const yaml::Node *FlagNode = node(FlagID); + std::string_view Name; + if (FlagNode == nullptr || scalar(*FlagNode, Name, "virtual-register flag")) + return true; + const auto Flag = Target.mirText().virtualRegisterFlag(Name); + if (!Flag) + return fail(FlagNode->range(), + "use of undefined register flag '" + std::string(Name) + "'"); + if ((Mask & *Flag) != 0) + return fail(FlagNode->range(), "duplicate virtual-register flag"); + Mask = static_cast(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 Initial; + if (const yaml::Node *VirtualNode = mappingValue(*Element, "virtual-reg")) { + std::string_view Text; + if (scalar(*VirtualNode, Text, "virtual register")) + return true; + if (!trim(Text).empty()) { + Register Virtual; + if (virtualRegister(Text, VirtualNode->range(), Function, Context, Virtual)) + return true; + Initial = Virtual; + } + } + auto AddResult = Function.registerInfo().addLiveIn(Reg, Initial); + if (!AddResult) + return importError(Element->range(), AddResult.error()); + } + } + + if (const yaml::Node *CalleeSaved = mappingValue(Root, "calleeSavedRegisters")) { + if (requireKind(*CalleeSaved, yaml::NodeKind::Sequence, "callee-saved registers")) + return true; + std::vector RegistersValue; + for (const yaml::NodeID RegID : CalleeSaved->sequence()) { + const yaml::Node *RegNode = node(RegID); + PhysicalRegister Reg; + if (RegNode == nullptr || physicalRegister(*RegNode, Reg)) + return true; + RegistersValue.push_back(Reg); + } + auto SetResult = Function.records().setCalleeSavedRegisters(std::move(RegistersValue)); + if (!SetResult) + return importError(CalleeSaved->range(), SetResult.error()); + } + return false; +} + +TextRange MIRDocumentParser::scalarDiagnosticRange(const yaml::Node &Scalar, + const TextRange &ScalarRange) const noexcept { + if (Scalar.scalarStyle() != yaml::ScalarStyle::Literal && + Scalar.scalarStyle() != yaml::ScalarStyle::Folded) { + std::size_t ContentOffset = Scalar.range().Begin.Offset; + if (Scalar.scalarStyle() == yaml::ScalarStyle::SingleQuoted || + Scalar.scalarStyle() == yaml::ScalarStyle::DoubleQuoted) { + ++ContentOffset; + } + return Result.Source.range(ContentOffset + ScalarRange.Begin.Offset, + ContentOffset + ScalarRange.End.Offset); + } + + const std::size_t FirstBodyLine = Scalar.range().Begin.Line + 1U; + const std::size_t LastBodyLine = Scalar.range().End.Line; + if (FirstBodyLine > LastBodyLine || FirstBodyLine == 0) + return Scalar.range(); + + std::size_t Indent = 0; + bool FoundContent = false; + for (std::size_t Line = FirstBodyLine; Line <= LastBodyLine; ++Line) { + const std::string_view Text = Result.Source.line(Line); + std::size_t CurrentIndent = 0; + while (CurrentIndent < Text.size() && Text[CurrentIndent] == ' ') + ++CurrentIndent; + if (CurrentIndent == Text.size()) + continue; + Indent = CurrentIndent; + FoundContent = true; + break; + } + if (!FoundContent) + return Scalar.range(); + + const TextPosition DefaultPosition = Scalar.range().Begin; + const auto MapPosition = [this, FirstBodyLine, LastBodyLine, Indent, + DefaultPosition](TextPosition Position) { + const std::size_t SourceLine = FirstBodyLine + Position.Line - 1U; + if (Position.Line == 0 || SourceLine > LastBodyLine || SourceLine > Result.Source.lineCount()) { + return DefaultPosition; + } + const std::string_view SourceLineText = Result.Source.line(SourceLine); + const std::size_t BodyColumn = Position.Column == 0 ? 0 : Position.Column - 1U; + const std::size_t SourceColumn = std::min(Indent + BodyColumn, SourceLineText.size()); + const std::size_t Offset = Result.Source.lineOffsets()[SourceLine - 1U] + SourceColumn; + return Result.Source.positionAt(Offset); + }; + return {.Begin = MapPosition(ScalarRange.Begin), .End = MapPosition(ScalarRange.End)}; +} + +bool MIRDocumentParser::parseBody(const yaml::Node &Root, MachineFunction &Function, + MIParserContext &Context) { + const yaml::Node *Body = mappingValue(Root, "body"); + std::string BodyText; + TextRange BodyRange = Root.range(); + if (Body != nullptr) { + std::string_view Text; + if (scalar(*Body, Text, "machine function body")) + return true; + BodyText = Text; + BodyRange = Body->range(); + } + + if (!effectiveDataLayout().empty()) { + const LLVMConstantSyntaxOptions PointerOptions = pointerLayoutOptions(); + Context.PointerSizeInBits = [PointerOptions](std::uint32_t AddressSpace) { + return llvmPointerSizeInBits(AddressSpace, PointerOptions); + }; + } + Context.SyncScopes = ActiveDocument->SyncScopes; + Context.InternSyncScope = [this](std::string_view Name) -> Expected { + 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::numeric_limits::max())) { + return makeUnexpected(ErrorCode::Unsupported, + "MIR document contains too many synchronization scopes"); + } + const auto ID = + SyncScopeID::fromValue(static_cast(ActiveDocument->SyncScopes.size() + 1U)); + ActiveDocument->SyncScopes.push_back({.Name = std::string(Name), .ID = ID}); + return ID; + }; + MIParserOptions BodyOptions = Options.MachineInstructions; + BodyOptions.VerifyAfterParse = false; + MIParser Parser(Function, Target, + std::string(Result.Source.name()) + ":" + std::string(Function.name()), + std::move(BodyText), Context, BodyOptions); + auto Parsed = Parser.parse(); + if (!Parsed) { + if (!Parser.diagnostics().empty()) { + const TextRange Range = + Body == nullptr ? BodyRange + : scalarDiagnosticRange(*Body, Parser.diagnostics().front().Range); + return fail(Range, Parser.diagnostics().front().Message, "mir-body"); + } + return importError(BodyRange, Parsed.error(), "mir-body"); + } + return false; +} + +bool MIRDocumentParser::convertTargetNode(const yaml::Node &Source, MIRTargetNode &Destination) { + switch (Source.kind()) { + case yaml::NodeKind::Null: + Destination = MIRTargetNode::null(); + return false; + case yaml::NodeKind::Scalar: + Destination = MIRTargetNode::scalar(std::string(Source.scalar())); + return false; + case yaml::NodeKind::Sequence: { + std::vector 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 Entries; + Entries.reserve(Source.mapping().size()); + for (const yaml::MappingEntry &SourceEntry : Source.mapping()) { + const yaml::Node *ValueNode = node(SourceEntry.Value); + if (ValueNode == nullptr) + return fail(SourceEntry.KeyRange, "target MIR mapping contains an invalid node"); + MIRTargetNode Value; + if (convertTargetNode(*ValueNode, Value)) + return true; + Entries.push_back({SourceEntry.Key, std::move(Value)}); + } + Destination = MIRTargetNode::mapping(std::move(Entries)); + return false; + } + } + return fail(Source.range(), "target MIR node has an unknown kind"); +} + +bool MIRDocumentParser::parseTargetState(const yaml::Node &Root, MachineFunction &Function) { + const yaml::Node *StateNode = mappingValue(Root, "machineFunctionInfo"); + if (StateNode == nullptr) + return false; + MIRTargetNode TargetNode; + if (convertTargetNode(*StateNode, TargetNode)) + return true; + auto State = Target.mirText().parseFunctionState(TargetNode); + if (!State) + return importError(StateNode->range(), State.error(), "mir-target-state"); + auto SetResult = Function.setTargetState(std::move(*State)); + if (!SetResult) + return importError(StateNode->range(), SetResult.error(), "mir-target-state"); + return false; +} + +bool MIRDocumentParser::validateDocumentReferences(const MachineFunction &Function) { + auto Verification = verifyMIRDocumentReferences(*ActiveDocument, Function, true); + if (Verification) + return false; + return importError(Result.Source.range(0, 0), Verification.error()); +} + +} // namespace rcc::mir + +namespace rcc::mir { + +using parser_detail::parseBool; +using parser_detail::parsePrefixedID; +using parser_detail::parseSigned; +using parser_detail::parseUnsigned; +using parser_detail::parseUnsigned32; +using parser_detail::trim; + +bool MIRDocumentParser::parseFunctionRecords(const yaml::Node &Root, MachineFunction &Function) { + auto ParseLocation = [this, &Function](const yaml::Node &Entry, + MachineInstr *&Instruction) -> bool { + const yaml::Node *BlockNode = mappingValue(Entry, "bb"); + const yaml::Node *OffsetNode = mappingValue(Entry, "offset"); + if (BlockNode == nullptr || OffsetNode == nullptr) + return fail(Entry.range(), "instruction location requires 'bb' and 'offset'"); + std::uint64_t Block = 0; + std::uint64_t Offset = 0; + if (unsignedValue(*BlockNode, Block, "a 32-bit basic block number") || + unsignedValue(*OffsetNode, Offset, "a 32-bit instruction offset") || + Block > std::numeric_limits::max() || + Offset > std::numeric_limits::max()) { + return true; + } + Instruction = instructionAt(Function, static_cast(Block), + static_cast(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::max() || + physicalRegister(*RegNode, Reg)) { + return true; + } + Number = static_cast(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::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(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 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::max()) { + return true; + } + Values[Index] = static_cast(Wide); + } + auto AddResult = Function.records().addDebugValueSubstitution( + {.Source = {Values[0], Values[1]}, + .Destination = {Values[2], Values[3]}, + .Subregister = SubRegIndex::fromValue(Values[4])}); + if (!AddResult) + return importError(Substitution->range(), AddResult.error()); + } + } + + if (const yaml::Node *PrefetchTargets = mappingValue(Root, "prefetch-targets")) { + if (requireKind(*PrefetchTargets, yaml::NodeKind::Sequence, "prefetch targets")) + return true; + for (const yaml::NodeID TargetID : PrefetchTargets->sequence()) { + const yaml::Node *TargetNode = node(TargetID); + std::string_view Text; + if (TargetNode == nullptr || scalar(*TargetNode, Text, "prefetch target")) + return true; + + std::size_t ContentOffset = TargetNode->range().Begin.Offset; + if (TargetNode->scalarStyle() == yaml::ScalarStyle::SingleQuoted || + TargetNode->scalarStyle() == yaml::ScalarStyle::DoubleQuoted) { + ++ContentOffset; + } + const auto ErrorRange = [this, ContentOffset](std::size_t Begin, std::size_t End = 1U) { + return Result.Source.range(ContentOffset + Begin, ContentOffset + Begin + End); + }; + const auto SkipSpace = [&Text](std::size_t &Cursor) { + while (Cursor < Text.size() && (Text[Cursor] == ' ' || Text[Cursor] == '\t')) + ++Cursor; + }; + const auto ParseNumber = [&Text, &SkipSpace](std::size_t &Cursor, std::uint32_t &Value) { + SkipSpace(Cursor); + const std::size_t Begin = Cursor; + while (Cursor < Text.size() && Text[Cursor] >= '0' && Text[Cursor] <= '9') + ++Cursor; + return Begin != Cursor && parseUnsigned32(Text.substr(Begin, Cursor - Begin), Value); + }; + + std::size_t Cursor = 0; + SkipSpace(Cursor); + if (!Text.substr(Cursor).starts_with("bb_id") || + (Cursor + std::string_view("bb_id").size() < Text.size() && + Text[Cursor + std::string_view("bb_id").size()] != ' ' && + Text[Cursor + std::string_view("bb_id").size()] != '\t')) { + return fail(ErrorRange(Cursor), "expected 'bb_id'"); + } + Cursor += std::string_view("bb_id").size(); + + std::array Values{}; + if (!ParseNumber(Cursor, Values[0])) + return fail(ErrorRange(0), "expected unsigned integer"); + + SkipSpace(Cursor); + if (Cursor < Text.size() && (Text[Cursor] == ',' || Text[Cursor] == '.')) { + ++Cursor; + if (!ParseNumber(Cursor, Values[1])) + return fail(ErrorRange(0), "expected unsigned integer"); + } else if (Cursor < Text.size() && Text[Cursor] >= '0' && Text[Cursor] <= '9') { + if (!ParseNumber(Cursor, Values[1])) + return fail(ErrorRange(0), "expected unsigned integer"); + } + + SkipSpace(Cursor); + if (Cursor >= Text.size() || Text[Cursor] != ',') + return fail(ErrorRange(Cursor), "expected ','"); + ++Cursor; + if (!ParseNumber(Cursor, Values[2])) + return fail(ErrorRange(0), "expected unsigned integer"); + SkipSpace(Cursor); + if (Cursor != Text.size()) + return fail(ErrorRange(0), "unexpected content after prefetch target"); + + auto AddResult = Function.records().addPrefetchTarget( + {.Block = {Values[0], Values[1]}, .CallsiteIndex = Values[2]}); + if (!AddResult) + return importError(TargetNode->range(), AddResult.error()); + } + } + + if (const yaml::Node *EntryValues = mappingValue(Root, "entry_values")) { + if (requireKind(*EntryValues, yaml::NodeKind::Sequence, "entry values")) + return true; + constexpr std::array Keys{ + std::string_view{"entry-value-register"}, + std::string_view{"debug-info-variable"}, + std::string_view{"debug-info-expression"}, + std::string_view{"debug-info-location"}, + }; + for (const yaml::NodeID EntryID : EntryValues->sequence()) { + const yaml::Node *Entry = node(EntryID); + if (Entry == nullptr || requireKind(*Entry, yaml::NodeKind::Mapping, "entry value") || + validateKeys(*Entry, Keys, "entry value")) { + return true; + } + const yaml::Node *RegNode = mappingValue(*Entry, "entry-value-register"); + const yaml::Node *VariableNode = mappingValue(*Entry, "debug-info-variable"); + const yaml::Node *ExpressionNode = mappingValue(*Entry, "debug-info-expression"); + const yaml::Node *LocationNode = mappingValue(*Entry, "debug-info-location"); + if (RegNode == nullptr || VariableNode == nullptr || ExpressionNode == nullptr || + LocationNode == nullptr) { + return fail(Entry->range(), "entry value requires all four fixed fields"); + } + PhysicalRegister Reg; + if (physicalRegister(*RegNode, Reg)) + return true; + MachineVariableDebugInfo DebugInfo; + if (parseVariableDebugInfo(*Entry, Function, DebugInfo)) + return true; + if (!DebugInfo.Variable || !DebugInfo.Expression || !DebugInfo.Location) { + return fail(Entry->range(), "entry value debug metadata cannot be empty", + "mir-debug-metadata"); + } + auto AddResult = + Function.records().addEntryValue({.Reg = Reg, .DebugInfo = std::move(DebugInfo)}); + if (!AddResult) + return importError(Entry->range(), AddResult.error()); + } + } + return false; +} + +} // namespace rcc::mir + +namespace rcc::mir { + +using parser_detail::parseBool; +using parser_detail::parsePrefixedID; +using parser_detail::parseSigned; +using parser_detail::parseUnsigned; +using parser_detail::parseUnsigned32; +using parser_detail::trim; + +Expected +MIRDocumentParser::resolveMetadataReference(const MachineFunction &Function, + std::uint32_t Slot) const { + if (ActiveDocument != nullptr && ActiveDocument->EmbeddedIR && + ActiveDocument->EmbeddedIR->metadata(std::to_string(Slot)) != nullptr) { + return MachineMetadataReference::embeddedIR(Slot); + } + if (Function.metadata().node(Slot) != nullptr) + return MachineMetadataReference::machineFunction(Slot); + return makeUnexpected(ErrorCode::ParseError, + "metadata reference uses undefined slot '!" + std::to_string(Slot) + "'"); +} + +bool MIRDocumentParser::parseVariableDebugInfo(const yaml::Node &Entry, MachineFunction &Function, + MachineVariableDebugInfo &DebugInfo) { + const auto Resolve = [this, &Function](std::uint32_t Slot) { + return resolveMetadataReference(Function, Slot); + }; + if (const yaml::Node *VariableNode = mappingValue(Entry, "debug-info-variable")) { + std::string_view Text; + if (scalar(*VariableNode, Text, "debug variable")) + return true; + if (!Text.empty()) { + MetadataTextParser Parser(std::string(Text), Resolve); + auto Reference = Parser.parseReferenceOnly(); + if (!Reference) + return importError(VariableNode->range(), Reference.error(), "mir-debug-metadata"); + if (Reference->scope() != MachineMetadataScope::EmbeddedIR) { + return fail(VariableNode->range(), + "debug variable must reference embedded LLVM IR metadata", + "mir-debug-metadata"); + } + DebugInfo.Variable = *Reference; + } + } + if (const yaml::Node *ExpressionNode = mappingValue(Entry, "debug-info-expression")) { + std::string_view Text; + if (scalar(*ExpressionNode, Text, "debug expression")) + return true; + if (!Text.empty()) { + MetadataTextParser Parser(std::string(Text), Resolve); + auto Value = Parser.parseValue(); + if (!Value) + return importError(scalarDiagnosticRange(*ExpressionNode, Parser.diagnosticRange()), + Value.error(), "mir-debug-metadata"); + if (Value->reference() != nullptr && + Value->reference()->scope() != MachineMetadataScope::EmbeddedIR) { + return fail(ExpressionNode->range(), + "debug expression reference must use embedded LLVM IR metadata", + "mir-debug-metadata"); + } + DebugInfo.Expression = std::move(*Value); + } + } + if (const yaml::Node *LocationNode = mappingValue(Entry, "debug-info-location")) { + std::string_view Text; + if (scalar(*LocationNode, Text, "debug location")) + return true; + if (!Text.empty()) { + MetadataTextParser Parser(std::string(Text), Resolve); + auto Location = Parser.parseLocation(); + if (!Location) + return importError(LocationNode->range(), Location.error(), "mir-debug-metadata"); + if (Location->reference() != nullptr && + Location->reference()->scope() != MachineMetadataScope::EmbeddedIR) { + return fail(LocationNode->range(), + "variable debug location must reference embedded LLVM IR metadata", + "mir-debug-metadata"); + } + DebugInfo.Location = std::move(*Location); + } + } + if (!DebugInfo.Variable && !DebugInfo.Expression && !DebugInfo.Location) + return false; + auto Verification = DebugInfo.verifyStructure(); + if (!Verification) + return importError(Entry.range(), Verification.error(), "mir-debug-metadata"); + return false; +} + +bool MIRDocumentParser::parseMetadata(const yaml::Node &Root, MachineFunction &Function, + MIParserContext &Context) { + if (ActiveDocument->EmbeddedIR) { + for (const EmbeddedIRMetadataDefinition &Reference : + ActiveDocument->EmbeddedIR->metadataDefinitions()) { + std::uint32_t ID = 0; + if (!parseUnsigned32(Reference.Name, ID)) + continue; + if (std::ranges::any_of(Context.Metadata, + [ID](const MIMetadataSlot &Slot) { return Slot.TextID == ID; })) { + return fail(Root.range(), "embedded LLVM IR metadata slots are not unique"); + } + Context.Metadata.push_back( + {.TextID = ID, .Reference = MachineMetadataReference::embeddedIR(ID)}); + } + } + + const yaml::Node *Metadata = mappingValue(Root, "machineMetadataNodes"); + if (Metadata == nullptr) + return false; + if (requireKind(*Metadata, yaml::NodeKind::Sequence, "machine metadata nodes")) + return true; + + enum class ParsedElementKind : std::uint8_t { + Null, + Reference, + String, + }; + struct ParsedElement final { + ParsedElementKind Kind = ParsedElementKind::Reference; + std::uint32_t Reference = 0; + std::string String; + TextRange Range; + }; + struct Definition final { + const yaml::Node *Node = nullptr; + std::uint32_t Slot = 0; + bool IsDistinct = false; + std::vector Elements; + }; + std::vector Definitions; + std::unordered_set Slots; + + const auto TranslateRange = [this](const yaml::Node &Node, const TextRange &Range) { + std::size_t ContentBegin = Node.range().Begin.Offset; + if (Node.scalarStyle() == yaml::ScalarStyle::SingleQuoted || + Node.scalarStyle() == yaml::ScalarStyle::DoubleQuoted) { + ++ContentBegin; + } + return Result.Source.range(ContentBegin + Range.Begin.Offset, ContentBegin + Range.End.Offset); + }; + + for (const yaml::NodeID DefinitionID : Metadata->sequence()) { + const yaml::Node *DefinitionNode = node(DefinitionID); + std::string_view Text; + if (DefinitionNode == nullptr || scalar(*DefinitionNode, Text, "machine metadata definition")) + return true; + + MILexer Lexer("machine-metadata", std::string(Text)); + MIToken Token = Lexer.next(); + const auto LexerFailed = [this, &Lexer, DefinitionNode, &TranslateRange, &Token]() -> bool { + if (!Token.is(MITokenKind::Error)) + return false; + const auto Diagnostics = Lexer.diagnostics(); + const TextRange Range = Diagnostics.empty() + ? TranslateRange(*DefinitionNode, Token.range()) + : TranslateRange(*DefinitionNode, Diagnostics.front().Range); + return fail(Range, Diagnostics.empty() ? "invalid machine metadata node" + : Diagnostics.front().Message); + }; + const auto Advance = [&Lexer, &Token, &LexerFailed]() -> bool { + Token = Lexer.next(); + return LexerFailed(); + }; + const auto FailAtToken = [this, DefinitionNode, &TranslateRange](const MIToken &At, + std::string Message) { + return fail(TranslateRange(*DefinitionNode, At.range()), std::move(Message)); + }; + + if (LexerFailed()) + return true; + if (!Token.is(MITokenKind::Exclaim)) + return FailAtToken(Token, "expected a metadata node"); + if (Advance()) + return true; + std::uint32_t Slot = 0; + if (!Token.is(MITokenKind::IntegerLiteral) || !parseUnsigned32(Token.integerValue(), Slot)) { + return FailAtToken(Token, "expected metadata id after '!'"); + } + if (Advance()) + return true; + if (!Token.is(MITokenKind::Equal)) + return FailAtToken(Token, "expected '='"); + if (Advance()) + return true; + + const bool IsDistinct = Token.isKeyword("distinct"); + if (IsDistinct && Advance()) + return true; + if (!Token.is(MITokenKind::Exclaim)) + return FailAtToken(Token, "expected a metadata node"); + if (Advance()) + return true; + if (!Token.is(MITokenKind::LeftBrace)) + return FailAtToken(Token, "expected '{' here"); + if (Advance()) + return true; + + std::vector Elements; + if (!Token.is(MITokenKind::RightBrace)) { + while (true) { + if (Token.hasStringValue() && Token.stringValue() == "null") { + Elements.push_back({.Kind = ParsedElementKind::Null, + .Reference = 0, + .String = {}, + .Range = TranslateRange(*DefinitionNode, Token.range())}); + } else { + if (!Token.is(MITokenKind::Exclaim)) + return FailAtToken(Token, "expected '!' here"); + const TextRange ReferenceRange = TranslateRange(*DefinitionNode, Token.range()); + if (Advance()) + return true; + + if (Token.is(MITokenKind::StringConstant)) { + Elements.push_back({.Kind = ParsedElementKind::String, + .Reference = 0, + .String = std::string(Token.stringValue()), + .Range = TranslateRange(*DefinitionNode, Token.range())}); + } else { + std::uint32_t Reference = 0; + if (!Token.is(MITokenKind::IntegerLiteral) || + !parseUnsigned32(Token.integerValue(), Reference)) { + return FailAtToken(Token, "expected metadata id after '!'"); + } + Elements.push_back({.Kind = ParsedElementKind::Reference, + .Reference = Reference, + .String = {}, + .Range = ReferenceRange}); + } + } + if (Advance()) + return true; + if (!Token.is(MITokenKind::Comma)) + break; + if (Advance()) + return true; + } + } + if (!Token.is(MITokenKind::RightBrace)) + return FailAtToken(Token, "expected end of metadata node"); + if (Advance()) + return true; + if (!Token.is(MITokenKind::EndOfFile)) + return FailAtToken(Token, "expected end of metadata node"); + if (!Slots.insert(Slot).second) + return FailAtToken(Token, "Metadata id is already used"); + + Definitions.push_back({.Node = DefinitionNode, + .Slot = Slot, + .IsDistinct = IsDistinct, + .Elements = std::move(Elements)}); + Context.Metadata.push_back( + {.TextID = Slot, .Reference = MachineMetadataReference::machineFunction(Slot)}); + } + + for (const Definition &Definition : Definitions) { + MachineMetadataNode NewNode{ + .Slot = Definition.Slot, + .IsDistinct = Definition.IsDistinct, + .Elements = {}, + }; + for (const ParsedElement &Element : Definition.Elements) { + if (Element.Kind == ParsedElementKind::Null) { + NewNode.Elements.push_back(MachineMetadataElement::null()); + continue; + } + if (Element.Kind == ParsedElementKind::String) { + NewNode.Elements.push_back(MachineMetadataElement::string(Element.String)); + continue; + } + + MachineMetadataReference Reference; + if (ActiveDocument->EmbeddedIR != std::nullopt && + ActiveDocument->EmbeddedIR->metadata(std::to_string(Element.Reference)) != nullptr) { + Reference = MachineMetadataReference::embeddedIR(Element.Reference); + } else if (Slots.contains(Element.Reference)) { + Reference = MachineMetadataReference::machineFunction(Element.Reference); + } else { + return fail(Element.Range, + "use of undefined metadata '!" + std::to_string(Element.Reference) + "'"); + } + auto Resolved = MachineMetadataElement::reference(Reference); + if (!Resolved) + return importError(Element.Range, Resolved.error()); + NewNode.Elements.push_back(std::move(*Resolved)); + } + auto AddResult = Function.metadata().addNode(std::move(NewNode)); + if (!AddResult) + return importError(Definition.Node->range(), AddResult.error()); + } + return false; +} + +} // namespace rcc::mir diff --git a/backend/lib/MIR/CMakeLists.txt b/backend/lib/MIR/CMakeLists.txt index f227390..cf5dbcd 100644 --- a/backend/lib/MIR/CMakeLists.txt +++ b/backend/lib/MIR/CMakeLists.txt @@ -4,30 +4,20 @@ set( RCCBackendMIRSources EmbeddedIR.cpp LLVMConstantSyntax.cpp - MIDebugSyntax.cpp - MILexer.cpp - MIRDataParser.cpp - MIRDocument.cpp - MIRDocumentParser.cpp - MIRDocumentReferenceValidation.cpp - MIRDocumentPrinter.cpp - MIRFrameParser.cpp - MIRFunctionParser.cpp - MIRFunctionRecordsParser.cpp - MIRMetadataParser.cpp - MIRMetadataTextParser.cpp - MIParser.cpp - MIPrinter.cpp + "${RCC_BACKEND_SOURCE_DIR}/lib/CodeGen/MIRParser/MILexer.cpp" + "${RCC_BACKEND_SOURCE_DIR}/lib/CodeGen/MIRParser/MIParser.cpp" + "${RCC_BACKEND_SOURCE_DIR}/lib/CodeGen/MIRParser/MIRParser.cpp" + "${RCC_BACKEND_SOURCE_DIR}/lib/CodeGen/MIRPrinter.cpp" + "${RCC_BACKEND_SOURCE_DIR}/lib/CodeGen/MIRYamlMapping.cpp" ) set( RCCBackendMIRHeaders + "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/MIRParser/MIParser.h" + "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/MIRParser/MIRParser.h" + "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/MIRPrinter.h" + "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/MIRYamlMapping.h" "${RCC_BACKEND_SOURCE_DIR}/include/rcc/MIR/EmbeddedIR.h" "${RCC_BACKEND_SOURCE_DIR}/include/rcc/MIR/LLVMConstantSyntax.h" - "${RCC_BACKEND_SOURCE_DIR}/include/rcc/MIR/MIDebugSyntax.h" - "${RCC_BACKEND_SOURCE_DIR}/include/rcc/MIR/MILexer.h" - "${RCC_BACKEND_SOURCE_DIR}/include/rcc/MIR/MIRDocument.h" - "${RCC_BACKEND_SOURCE_DIR}/include/rcc/MIR/MIParser.h" - "${RCC_BACKEND_SOURCE_DIR}/include/rcc/MIR/MIPrinter.h" ) add_library(RCCBackendMIR ${RCCBackendMIRSources}) @@ -61,41 +51,20 @@ install( rcc_backend_register_format_files( "${CMAKE_CURRENT_SOURCE_DIR}/EmbeddedIR.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/LLVMConstantSyntax.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/MIDebugSyntax.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/MILexer.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/MIRDataParser.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/MIRDocument.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/MIRDocumentParser.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/MIRDocumentParser.h" - "${CMAKE_CURRENT_SOURCE_DIR}/MIRDocumentReferenceValidation.h" - "${CMAKE_CURRENT_SOURCE_DIR}/MIRDocumentReferenceValidation.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/MIRDocumentPrinter.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/MIRFrameParser.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/MIRFunctionParser.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/MIRFunctionRecordsParser.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/MIRMetadataParser.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/MIRMetadataTextParser.h" - "${CMAKE_CURRENT_SOURCE_DIR}/MIRMetadataTextParser.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/MIRParserUtilities.h" - "${CMAKE_CURRENT_SOURCE_DIR}/MIParser.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/MIPrinter.cpp" + "${RCC_BACKEND_SOURCE_DIR}/lib/CodeGen/MIRParser/MILexer.cpp" + "${RCC_BACKEND_SOURCE_DIR}/lib/CodeGen/MIRParser/MILexer.h" + "${RCC_BACKEND_SOURCE_DIR}/lib/CodeGen/MIRParser/MIParser.cpp" + "${RCC_BACKEND_SOURCE_DIR}/lib/CodeGen/MIRParser/MIRParser.cpp" + "${RCC_BACKEND_SOURCE_DIR}/lib/CodeGen/MIRPrinter.cpp" + "${RCC_BACKEND_SOURCE_DIR}/lib/CodeGen/MIRYamlMapping.cpp" ${RCCBackendMIRHeaders} ) rcc_backend_register_tidy_files( "${CMAKE_CURRENT_SOURCE_DIR}/EmbeddedIR.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/LLVMConstantSyntax.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/MIDebugSyntax.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/MILexer.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/MIRDataParser.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/MIRDocument.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/MIRDocumentParser.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/MIRDocumentReferenceValidation.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/MIRDocumentPrinter.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/MIRFrameParser.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/MIRFunctionParser.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/MIRFunctionRecordsParser.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/MIRMetadataParser.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/MIRMetadataTextParser.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/MIParser.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/MIPrinter.cpp" + "${RCC_BACKEND_SOURCE_DIR}/lib/CodeGen/MIRParser/MILexer.cpp" + "${RCC_BACKEND_SOURCE_DIR}/lib/CodeGen/MIRParser/MIParser.cpp" + "${RCC_BACKEND_SOURCE_DIR}/lib/CodeGen/MIRParser/MIRParser.cpp" + "${RCC_BACKEND_SOURCE_DIR}/lib/CodeGen/MIRPrinter.cpp" + "${RCC_BACKEND_SOURCE_DIR}/lib/CodeGen/MIRYamlMapping.cpp" ) diff --git a/backend/lib/MIR/MIDebugSyntax.cpp b/backend/lib/MIR/MIDebugSyntax.cpp deleted file mode 100644 index e614a21..0000000 --- a/backend/lib/MIR/MIDebugSyntax.cpp +++ /dev/null @@ -1,264 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "rcc/MIR/MIDebugSyntax.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace rcc::mir { -namespace { - -[[nodiscard]] bool parseIndex(std::string_view Text, std::uint64_t &Value) noexcept { - if (Text.empty()) - return false; - const auto [End, Error] = std::from_chars(Text.data(), Text.data() + Text.size(), Value, 10); - return Error == std::errc{} && End == Text.data() + Text.size(); -} - -[[nodiscard]] const auto &namedOpcodes() { - static constexpr auto Opcodes = std::to_array>({ - {"DW_OP_addr", 0x03}, - {"DW_OP_deref", 0x06}, - {"DW_OP_const1u", 0x08}, - {"DW_OP_const1s", 0x09}, - {"DW_OP_const2u", 0x0A}, - {"DW_OP_const2s", 0x0B}, - {"DW_OP_const4u", 0x0C}, - {"DW_OP_const4s", 0x0D}, - {"DW_OP_const8u", 0x0E}, - {"DW_OP_const8s", 0x0F}, - {"DW_OP_constu", 0x10}, - {"DW_OP_consts", 0x11}, - {"DW_OP_dup", 0x12}, - {"DW_OP_drop", 0x13}, - {"DW_OP_over", 0x14}, - {"DW_OP_pick", 0x15}, - {"DW_OP_swap", 0x16}, - {"DW_OP_rot", 0x17}, - {"DW_OP_xderef", 0x18}, - {"DW_OP_abs", 0x19}, - {"DW_OP_and", 0x1A}, - {"DW_OP_div", 0x1B}, - {"DW_OP_minus", 0x1C}, - {"DW_OP_mod", 0x1D}, - {"DW_OP_mul", 0x1E}, - {"DW_OP_neg", 0x1F}, - {"DW_OP_not", 0x20}, - {"DW_OP_or", 0x21}, - {"DW_OP_plus", 0x22}, - {"DW_OP_plus_uconst", 0x23}, - {"DW_OP_shl", 0x24}, - {"DW_OP_shr", 0x25}, - {"DW_OP_shra", 0x26}, - {"DW_OP_xor", 0x27}, - {"DW_OP_bra", 0x28}, - {"DW_OP_eq", 0x29}, - {"DW_OP_ge", 0x2A}, - {"DW_OP_gt", 0x2B}, - {"DW_OP_le", 0x2C}, - {"DW_OP_lt", 0x2D}, - {"DW_OP_ne", 0x2E}, - {"DW_OP_skip", 0x2F}, - {"DW_OP_regx", 0x90}, - {"DW_OP_fbreg", 0x91}, - {"DW_OP_bregx", 0x92}, - {"DW_OP_piece", 0x93}, - {"DW_OP_deref_size", 0x94}, - {"DW_OP_xderef_size", 0x95}, - {"DW_OP_nop", 0x96}, - {"DW_OP_push_object_address", 0x97}, - {"DW_OP_call2", 0x98}, - {"DW_OP_call4", 0x99}, - {"DW_OP_call_ref", 0x9A}, - {"DW_OP_form_tls_address", 0x9B}, - {"DW_OP_call_frame_cfa", 0x9C}, - {"DW_OP_bit_piece", 0x9D}, - {"DW_OP_implicit_value", 0x9E}, - {"DW_OP_stack_value", 0x9F}, - {"DW_OP_implicit_pointer", 0xA0}, - {"DW_OP_addrx", 0xA1}, - {"DW_OP_constx", 0xA2}, - {"DW_OP_entry_value", 0xA3}, - {"DW_OP_const_type", 0xA4}, - {"DW_OP_regval_type", 0xA5}, - {"DW_OP_deref_type", 0xA6}, - {"DW_OP_xderef_type", 0xA7}, - {"DW_OP_convert", 0xA8}, - {"DW_OP_reinterpret", 0xA9}, - {"DW_OP_LLVM_fragment", 0x1000}, - {"DW_OP_LLVM_convert", 0x1001}, - {"DW_OP_LLVM_tag_offset", 0x1002}, - {"DW_OP_LLVM_entry_value", 0x1003}, - {"DW_OP_LLVM_implicit_pointer", 0x1004}, - {"DW_OP_LLVM_arg", 0x1005}, - {"DW_OP_LLVM_extract_bits_sext", 0x1006}, - {"DW_OP_LLVM_extract_bits_zext", 0x1007}, - }); - return Opcodes; -} - -[[nodiscard]] const auto &attributeEncodings() { - static constexpr auto Encodings = std::to_array>({ - {"DW_ATE_address", 0x01}, - {"DW_ATE_boolean", 0x02}, - {"DW_ATE_complex_float", 0x03}, - {"DW_ATE_float", 0x04}, - {"DW_ATE_signed", 0x05}, - {"DW_ATE_signed_char", 0x06}, - {"DW_ATE_unsigned", 0x07}, - {"DW_ATE_unsigned_char", 0x08}, - {"DW_ATE_imaginary_float", 0x09}, - {"DW_ATE_packed_decimal", 0x0A}, - {"DW_ATE_numeric_string", 0x0B}, - {"DW_ATE_edited", 0x0C}, - {"DW_ATE_signed_fixed", 0x0D}, - {"DW_ATE_unsigned_fixed", 0x0E}, - {"DW_ATE_decimal_float", 0x0F}, - {"DW_ATE_UTF", 0x10}, - {"DW_ATE_UCS", 0x11}, - {"DW_ATE_ASCII", 0x12}, - }); - return Encodings; -} - -[[nodiscard]] std::optional opcodeName(std::uint64_t Opcode) { - if (Opcode >= 0x30U && Opcode <= 0x4FU) - return "DW_OP_lit" + std::to_string(Opcode - 0x30U); - if (Opcode >= 0x50U && Opcode <= 0x6FU) - return "DW_OP_reg" + std::to_string(Opcode - 0x50U); - if (Opcode >= 0x70U && Opcode <= 0x8FU) - return "DW_OP_breg" + std::to_string(Opcode - 0x70U); - const auto &Opcodes = namedOpcodes(); - const auto Found = - std::ranges::find_if(Opcodes, [Opcode](const auto &Entry) { return Entry.second == Opcode; }); - if (Found == Opcodes.end()) - return std::nullopt; - return std::string(Found->first); -} - -[[nodiscard]] std::size_t opcodeSize(std::uint64_t Opcode) noexcept { - if (Opcode >= 0x70U && Opcode <= 0x8FU) - return 2; - switch (Opcode) { - case 0x92: - case 0x1000: - case 0x1001: - case 0x1006: - case 0x1007: - return 3; - case 0x10: - case 0x11: - case 0x23: - case 0x90: - case 0x94: - case 0x1002: - case 0x1003: - case 0x1005: - return 2; - default: - return 1; - } -} - -[[nodiscard]] std::optional attributeName(std::uint64_t Encoding) noexcept { - const auto &Encodings = attributeEncodings(); - const auto Found = std::ranges::find_if( - Encodings, [Encoding](const auto &Entry) { return Entry.second == Encoding; }); - if (Found == Encodings.end()) - return std::nullopt; - return Found->first; -} - -} // namespace - -std::optional parseDIExpressionOpcodeName(std::string_view Name) noexcept { - const auto NumberedOpcode = [Name](std::string_view Prefix, - std::uint64_t Base) -> std::optional { - if (!Name.starts_with(Prefix)) - return std::nullopt; - std::uint64_t Index = 0; - if (!parseIndex(Name.substr(Prefix.size()), Index) || Index > 31) - return std::nullopt; - return Base + Index; - }; - if (auto Opcode = NumberedOpcode("DW_OP_lit", 0x30U)) - return Opcode; - if (auto Opcode = NumberedOpcode("DW_OP_reg", 0x50U)) - return Opcode; - if (auto Opcode = NumberedOpcode("DW_OP_breg", 0x70U)) - return Opcode; - - const auto &NamedOpcodes = namedOpcodes(); - const auto *const Found = - std::ranges::find_if(NamedOpcodes, [Name](const auto &Entry) { return Entry.first == Name; }); - if (Found == NamedOpcodes.end()) - return std::nullopt; - return Found->second; -} - -std::optional -parseDIExpressionAttributeEncodingName(std::string_view Name) noexcept { - const auto &Encodings = attributeEncodings(); - const auto Found = - std::ranges::find_if(Encodings, [Name](const auto &Entry) { return Entry.first == Name; }); - if (Found == Encodings.end()) - return std::nullopt; - return Found->second; -} - -std::string invalidDIExpressionElementMessage(std::string_view Name) { - if (Name.starts_with("DW_OP_")) - return "invalid DWARF op '" + std::string(Name) + "'"; - if (Name.starts_with("DW_ATE_")) - return "invalid DWARF attribute encoding '" + std::string(Name) + "'"; - return "expected unsigned integer"; -} - -std::string formatDIExpression(std::span Elements) { - std::string Result = "!DIExpression("; - std::size_t Position = 0; - bool First = true; - while (Position < Elements.size()) { - const std::uint64_t Opcode = Elements[Position]; - const auto Name = opcodeName(Opcode); - const std::size_t Size = opcodeSize(Opcode); - if (!Name || Position + Size > Elements.size()) { - Result = "!DIExpression("; - for (std::size_t Index = 0; Index < Elements.size(); ++Index) { - if (Index != 0) - Result.append(", "); - Result.append(std::to_string(Elements[Index])); - } - Result.push_back(')'); - return Result; - } - - if (!First) - Result.append(", "); - First = false; - Result.append(*Name); - for (std::size_t Argument = 1; Argument < Size; ++Argument) { - Result.append(", "); - if (Opcode == 0x1001U && Argument == 2U) { - if (const auto Encoding = attributeName(Elements[Position + Argument])) { - Result.append(*Encoding); - continue; - } - } - Result.append(std::to_string(Elements[Position + Argument])); - } - Position += Size; - } - Result.push_back(')'); - return Result; -} - -} // namespace rcc::mir diff --git a/backend/lib/MIR/MILexer.cpp b/backend/lib/MIR/MILexer.cpp deleted file mode 100644 index f568cef..0000000 --- a/backend/lib/MIR/MILexer.cpp +++ /dev/null @@ -1,964 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "rcc/MIR/MILexer.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace rcc::mir { -namespace { - -template -[[nodiscard]] consteval std::array -sortSpellings(std::array Spellings) { - for (std::size_t Left = 0; Left < Spellings.size(); ++Left) { - for (std::size_t Right = Left + 1; Right < Spellings.size(); ++Right) { - if (Spellings[Right] < Spellings[Left]) - std::swap(Spellings[Left], Spellings[Right]); - } - } - return Spellings; -} - -template -[[nodiscard]] consteval bool -hasUniqueSpellings(const std::array &Spellings) { - for (std::size_t Index = 1; Index < Spellings.size(); ++Index) { - if (Spellings[Index - 1] == Spellings[Index]) - return false; - } - return true; -} - -constexpr auto Keywords = sortSpellings(std::array{ - std::string_view{"addrspace"}, - std::string_view{"adjust_cfa_offset"}, - std::string_view{"afn"}, - std::string_view{"align"}, - std::string_view{"arcp"}, - std::string_view{"basealign"}, - std::string_view{"bb_id"}, - std::string_view{"bbsections"}, - std::string_view{"bfloat"}, - std::string_view{"blockaddress"}, - std::string_view{"call-entry"}, - std::string_view{"call-frame-size"}, - std::string_view{"cfi-type"}, - std::string_view{"constant-pool"}, - std::string_view{"contract"}, - std::string_view{"custom"}, - std::string_view{"dead"}, - std::string_view{"deactivation-symbol"}, - std::string_view{"debug-instr-number"}, - std::string_view{"debug-location"}, - std::string_view{"debug-use"}, - std::string_view{"dbg-instr-ref"}, - std::string_view{"def"}, - std::string_view{"def_cfa"}, - std::string_view{"def_cfa_offset"}, - std::string_view{"def_cfa_register"}, - std::string_view{"dereferenceable"}, - std::string_view{"disjoint"}, - std::string_view{"distinct"}, - std::string_view{"double"}, - std::string_view{"early-clobber"}, - std::string_view{"ehfunclet-entry"}, - std::string_view{"ehscope-entry"}, - std::string_view{"escape"}, - std::string_view{"exact"}, - std::string_view{"float"}, - std::string_view{"floatpred"}, - std::string_view{"fp128"}, - std::string_view{"frame-destroy"}, - std::string_view{"frame-setup"}, - std::string_view{"got"}, - std::string_view{"half"}, - std::string_view{"heap-alloc-marker"}, - std::string_view{"implicit"}, - std::string_view{"implicit-def"}, - std::string_view{"inbounds"}, - std::string_view{"inlineasm-br-indirect-target"}, - std::string_view{"internal"}, - std::string_view{"intpred"}, - std::string_view{"intrinsic"}, - std::string_view{"invariant"}, - std::string_view{"ir-block-address-taken"}, - std::string_view{"jump-table"}, - std::string_view{"killed"}, - std::string_view{"landing-pad"}, - std::string_view{"lanemask"}, - std::string_view{"liveins"}, - std::string_view{"liveout"}, - std::string_view{"llvm_def_aspace_cfa"}, - std::string_view{"llvm_register_pair"}, - std::string_view{"llvm_vector_offset"}, - std::string_view{"llvm_vector_register_mask"}, - std::string_view{"llvm_vector_registers"}, - std::string_view{"machine-block-address-taken"}, - std::string_view{"mmra"}, - std::string_view{"negate_ra_sign_state"}, - std::string_view{"negate_ra_sign_state_with_pc"}, - std::string_view{"ninf"}, - std::string_view{"nnan"}, - std::string_view{"noconvergent"}, - std::string_view{"nofpexcept"}, - std::string_view{"non-temporal"}, - std::string_view{"nneg"}, - std::string_view{"nsw"}, - std::string_view{"nsz"}, - std::string_view{"nusw"}, - std::string_view{"nuw"}, - std::string_view{"offset"}, - std::string_view{"pcsections"}, - std::string_view{"post-instr-symbol"}, - std::string_view{"ppc_fp128"}, - std::string_view{"pre-instr-symbol"}, - std::string_view{"reassoc"}, - std::string_view{"rel_offset"}, - std::string_view{"remember_state"}, - std::string_view{"renamable"}, - std::string_view{"register"}, - std::string_view{"restore"}, - std::string_view{"restore_state"}, - std::string_view{"same_value"}, - std::string_view{"samesign"}, - std::string_view{"shufflemask"}, - std::string_view{"stack"}, - std::string_view{"successors"}, - std::string_view{"target-flags"}, - std::string_view{"target-index"}, - std::string_view{"tied-def"}, - std::string_view{"undef"}, - std::string_view{"undefined"}, - std::string_view{"unknown-address"}, - std::string_view{"unknown-size"}, - std::string_view{"unpredictable"}, - std::string_view{"volatile"}, - std::string_view{"window_save"}, - std::string_view{"x86_fp80"}, -}); - -constexpr auto MetadataKeywords = sortSpellings(std::array{ - std::string_view{"!DIExpression"}, - std::string_view{"!DILocation"}, - std::string_view{"!alias.scope"}, - std::string_view{"!noalias"}, - std::string_view{"!noalias.addrspace"}, - std::string_view{"!range"}, - std::string_view{"!tbaa"}, -}); - -static_assert(hasUniqueSpellings(Keywords)); -static_assert(hasUniqueSpellings(MetadataKeywords)); - -[[nodiscard]] constexpr bool isASCIIAlpha(char Character) noexcept { - return (Character >= 'a' && Character <= 'z') || (Character >= 'A' && Character <= 'Z'); -} - -[[nodiscard]] constexpr bool isASCIIDigit(char Character) noexcept { - return Character >= '0' && Character <= '9'; -} - -[[nodiscard]] constexpr bool isASCIIHexDigit(char Character) noexcept { - return isASCIIDigit(Character) || (Character >= 'a' && Character <= 'f') || - (Character >= 'A' && Character <= 'F'); -} - -[[nodiscard]] constexpr unsigned hexDigitValue(char Character) noexcept { - if (isASCIIDigit(Character)) - return static_cast(Character - '0'); - if (Character >= 'a' && Character <= 'f') - return static_cast(Character - 'a' + 10); - return static_cast(Character - 'A' + 10); -} - -[[nodiscard]] constexpr bool isIdentifierCharacter(char Character) noexcept { - return isASCIIAlpha(Character) || isASCIIDigit(Character) || Character == '_' || - Character == '-' || Character == '.' || Character == '$'; -} - -[[nodiscard]] constexpr bool isRegisterCharacter(char Character) noexcept { - return isIdentifierCharacter(Character) && Character != '.'; -} - -[[nodiscard]] constexpr bool isHorizontalWhitespace(char Character) noexcept { - return Character == ' ' || Character == '\t'; -} - -[[nodiscard]] constexpr bool isNewlineCharacter(char Character) noexcept { - return Character == '\n' || Character == '\r'; -} - -[[nodiscard]] constexpr bool isHexFloatingPointPrefix(char Character) noexcept { - return Character == 'H' || Character == 'K' || Character == 'L' || Character == 'M' || - Character == 'R'; -} - -[[nodiscard]] std::string unexpectedCharacterMessage(unsigned char Character) { - if (Character >= 0x20U && Character <= 0x7EU) { - return "unexpected character '" + std::string(1, static_cast(Character)) + "'"; - } - std::ostringstream Message; - Message << "unexpected byte 0x" << std::uppercase << std::hex << std::setw(2) << std::setfill('0') - << static_cast(Character); - return Message.str(); -} - -} // namespace - -std::string_view getMITokenKindName(MITokenKind Kind) noexcept { - switch (Kind) { - case MITokenKind::EndOfFile: - return "end-of-file"; - case MITokenKind::Error: - return "error"; - case MITokenKind::Newline: - return "newline"; - case MITokenKind::Comma: - return "comma"; - case MITokenKind::Equal: - return "equal"; - case MITokenKind::Underscore: - return "underscore"; - case MITokenKind::Colon: - return "colon"; - case MITokenKind::DoubleColon: - return "double-colon"; - case MITokenKind::Dot: - return "dot"; - case MITokenKind::Exclaim: - return "exclaim"; - case MITokenKind::LeftParen: - return "left-paren"; - case MITokenKind::RightParen: - return "right-paren"; - case MITokenKind::LeftBrace: - return "left-brace"; - case MITokenKind::RightBrace: - return "right-brace"; - case MITokenKind::Plus: - return "plus"; - case MITokenKind::Minus: - return "minus"; - case MITokenKind::Less: - return "less"; - case MITokenKind::Greater: - return "greater"; - case MITokenKind::Keyword: - return "keyword"; - case MITokenKind::MetadataKeyword: - return "metadata-keyword"; - case MITokenKind::Identifier: - return "identifier"; - case MITokenKind::NamedRegister: - return "named-register"; - case MITokenKind::NamedVirtualRegister: - return "named-virtual-register"; - case MITokenKind::MachineBasicBlockLabel: - return "machine-basic-block-label"; - case MITokenKind::MachineBasicBlock: - return "machine-basic-block"; - case MITokenKind::StackObject: - return "stack-object"; - case MITokenKind::FixedStackObject: - return "fixed-stack-object"; - case MITokenKind::NamedGlobalValue: - return "named-global-value"; - case MITokenKind::GlobalValue: - return "global-value"; - case MITokenKind::ExternalSymbol: - return "external-symbol"; - case MITokenKind::MCSymbol: - return "mc-symbol"; - case MITokenKind::IntegerLiteral: - return "integer-literal"; - case MITokenKind::FloatingPointLiteral: - return "floating-point-literal"; - case MITokenKind::HexLiteral: - return "hex-literal"; - case MITokenKind::VirtualRegister: - return "virtual-register"; - case MITokenKind::ConstantPoolItem: - return "constant-pool-item"; - case MITokenKind::JumpTableIndex: - return "jump-table-index"; - case MITokenKind::NamedIRBlock: - return "named-ir-block"; - case MITokenKind::IRBlock: - return "ir-block"; - case MITokenKind::NamedIRValue: - return "named-ir-value"; - case MITokenKind::IRValue: - return "ir-value"; - case MITokenKind::QuotedIRValue: - return "quoted-ir-value"; - case MITokenKind::SubRegisterIndex: - return "subregister-index"; - case MITokenKind::StringConstant: - return "string-constant"; - } - return "unknown"; -} - -bool isMIKeyword(std::string_view Spelling) noexcept { - return std::ranges::binary_search(Keywords, Spelling); -} - -bool isMIMetadataKeyword(std::string_view Spelling) noexcept { - return std::ranges::binary_search(MetadataKeywords, Spelling); -} - -std::string_view MIToken::stringValue() const noexcept { - if (const auto *View = std::get_if(&StringValue)) - return *View; - if (const auto *Owned = std::get_if(&StringValue)) - return *Owned; - return {}; -} - -bool MIToken::isRegister() const noexcept { - return Kind == MITokenKind::NamedRegister || Kind == MITokenKind::NamedVirtualRegister || - Kind == MITokenKind::VirtualRegister || Kind == MITokenKind::Underscore; -} - -bool MIToken::isRegisterFlag() const noexcept { - return isKeyword("implicit") || isKeyword("implicit-def") || isKeyword("def") || - isKeyword("dead") || isKeyword("killed") || isKeyword("undef") || isKeyword("internal") || - isKeyword("early-clobber") || isKeyword("debug-use") || isKeyword("renamable"); -} - -bool MIToken::isMemoryOperandFlag() const noexcept { - return isKeyword("volatile") || isKeyword("non-temporal") || isKeyword("dereferenceable") || - isKeyword("invariant") || Kind == MITokenKind::StringConstant; -} - -MIToken MILexer::makeToken(MITokenKind Kind, TextRange Range, std::string_view Spelling, - std::string_view IntegerValue) { - return MIToken(Kind, Range, Spelling, {}, IntegerValue); -} - -MIToken MILexer::makeStringToken(MITokenKind Kind, TextRange Range, std::string_view Spelling, - std::string_view StringValue, std::string_view IntegerValue) { - return MIToken(Kind, Range, Spelling, StringValue, IntegerValue); -} - -MIToken MILexer::makeOwnedStringToken(MITokenKind Kind, TextRange Range, std::string_view Spelling, - std::string StringValue, std::string_view IntegerValue) { - return MIToken(Kind, Range, Spelling, std::move(StringValue), IntegerValue); -} - -class MILexer::Impl final { -public: - Impl(std::string SourceName, std::string SourceText, MILexerOptions Options) - : Source(std::move(SourceName), std::move(SourceText)), Options(Options) {} - - [[nodiscard]] MIToken next() { - if (Failed) - return token(MITokenKind::EndOfFile, Offset, Offset); - - if (auto TriviaError = skipTrivia()) - return std::move(*TriviaError); - - if (atEnd()) - return token(MITokenKind::EndOfFile, Offset, Offset); - - const std::size_t Begin = Offset; - MIToken Result = lexToken(); - if (Result.kind() == MITokenKind::Error) - return Result; - - if (Options.MaximumTokenLength != 0 && - Result.range().End.Offset - Result.range().Begin.Offset > Options.MaximumTokenLength) { - return fail(Begin, Offset, "machine instruction token exceeds the configured length limit"); - } - if (Options.MaximumTokens != 0 && TokenCount >= Options.MaximumTokens) { - return fail(Begin, Offset, "machine instruction token count exceeds the configured limit"); - } - ++TokenCount; - return Result; - } - - [[nodiscard]] const TextSource &source() const noexcept { - return Source; - } - - [[nodiscard]] std::span diagnostics() const noexcept { - return Diagnostics; - } - - [[nodiscard]] bool failed() const noexcept { - return Failed; - } - - [[nodiscard]] std::size_t tokenCount() const noexcept { - return TokenCount; - } - -private: - struct QuotedValue final { - std::size_t End = 0; - std::string Value; - }; - - [[nodiscard]] std::string_view text() const noexcept { - return Source.text(); - } - - [[nodiscard]] bool atEnd(std::size_t Lookahead = 0) const noexcept { - return Offset >= text().size() || Lookahead >= text().size() - Offset; - } - - [[nodiscard]] char peek(std::size_t Lookahead = 0) const noexcept { - if (Offset >= text().size() || Lookahead >= text().size() - Offset) - return '\0'; - return text()[Offset + Lookahead]; - } - - [[nodiscard]] bool startsWith(std::string_view Prefix) const noexcept { - return text().substr(Offset).starts_with(Prefix); - } - - [[nodiscard]] std::string_view slice(std::size_t Begin, std::size_t End) const noexcept { - assert(Begin <= End && End <= text().size()); - return text().substr(Begin, End - Begin); - } - - [[nodiscard]] MIToken token(MITokenKind Kind, std::size_t Begin, std::size_t End, - std::string_view IntegerValue = {}) const { - return MILexer::makeToken(Kind, Source.range(Begin, End), slice(Begin, End), IntegerValue); - } - - [[nodiscard]] MIToken stringToken(MITokenKind Kind, std::size_t Begin, std::size_t End, - std::string_view Value, - std::string_view IntegerValue = {}) const { - return MILexer::makeStringToken(Kind, Source.range(Begin, End), slice(Begin, End), Value, - IntegerValue); - } - - [[nodiscard]] MIToken ownedStringToken(MITokenKind Kind, std::size_t Begin, std::size_t End, - std::string Value, - std::string_view IntegerValue = {}) const { - return MILexer::makeOwnedStringToken(Kind, Source.range(Begin, End), slice(Begin, End), - std::move(Value), IntegerValue); - } - - [[nodiscard]] MIToken fail(std::size_t Begin, std::size_t End, std::string Message) { - End = std::clamp(End, Begin, text().size()); - Diagnostics.push_back(TextDiagnostic{ - .Severity = DiagnosticSeverity::Error, - .Category = "mir-lexer", - .Message = std::move(Message), - .SourceName = std::string(Source.name()), - .Range = Source.range(Begin, End), - }); - Failed = true; - return token(MITokenKind::Error, Begin, End); - } - - [[nodiscard]] std::optional skipTrivia() { - while (true) { - while (!atEnd() && isHorizontalWhitespace(peek())) - ++Offset; - - if (peek() == ';') { - while (!atEnd() && !isNewlineCharacter(peek())) - ++Offset; - continue; - } - - if (!startsWith("/*")) - return std::nullopt; - - const std::size_t Begin = Offset; - Offset += 2; - while (!atEnd() && !startsWith("*/")) - ++Offset; - if (atEnd()) { - return fail(Begin, Offset, - "end of machine instruction input reached before the closing '*/'"); - } - Offset += 2; - } - } - - [[nodiscard]] std::optional scanQuotedString(std::size_t QuoteOffset) { - assert(QuoteOffset < text().size() && text()[QuoteOffset] == '"'); - std::size_t Cursor = QuoteOffset + 1; - while (Cursor < text().size() && text()[Cursor] != '"') { - if (isNewlineCharacter(text()[Cursor])) { - Offset = Cursor; - static_cast(fail(QuoteOffset, Cursor, - "end of machine instruction reached before the closing '\"'")); - return std::nullopt; - } - ++Cursor; - } - if (Cursor == text().size()) { - Offset = Cursor; - static_cast( - fail(QuoteOffset, Cursor, "end of machine instruction reached before the closing '\"'")); - return std::nullopt; - } - - std::string Value; - Value.reserve(Cursor - QuoteOffset - 1); - for (std::size_t Index = QuoteOffset + 1; Index < Cursor; ++Index) { - const char Character = text()[Index]; - if (Character == '\\' && Index + 1 < Cursor && text()[Index + 1] == '\\') { - Value.push_back('\\'); - ++Index; - continue; - } - if (Character == '\\' && Index + 2 < Cursor && isASCIIHexDigit(text()[Index + 1]) && - isASCIIHexDigit(text()[Index + 2])) { - const unsigned Decoded = - hexDigitValue(text()[Index + 1]) * 16U + hexDigitValue(text()[Index + 2]); - Value.push_back(static_cast(Decoded)); - Index += 2; - continue; - } - Value.push_back(Character); - } - return QuotedValue{.End = Cursor + 1, .Value = std::move(Value)}; - } - - [[nodiscard]] MIToken lexName(MITokenKind Kind, std::size_t PrefixLength) { - const std::size_t Begin = Offset; - Offset += PrefixLength; - if (peek() == '"') { - auto Quoted = scanQuotedString(Offset); - if (!Quoted) - return token(MITokenKind::Error, Begin, Offset); - Offset = Quoted->End; - return ownedStringToken(Kind, Begin, Offset, std::move(Quoted->Value)); - } - - const std::size_t ValueBegin = Offset; - while (!atEnd() && isIdentifierCharacter(peek())) - ++Offset; - return stringToken(Kind, Begin, Offset, slice(ValueBegin, Offset)); - } - - [[nodiscard]] std::optional lexMachineBasicBlock() { - const bool IsReference = startsWith("%bb."); - if (!IsReference && !startsWith("bb.")) - return std::nullopt; - - const std::size_t Begin = Offset; - const std::size_t PrefixLength = IsReference ? 4 : 3; - Offset += PrefixLength; - if (!isASCIIDigit(peek())) { - return fail(Offset, std::min(Offset + 1, text().size()), - "expected a decimal block number after '" + - std::string(IsReference ? "%bb.'" : "bb.'")); - } - - const std::size_t NumberBegin = Offset; - while (!atEnd() && isASCIIDigit(peek())) - ++Offset; - const std::string_view Number = slice(NumberBegin, Offset); - - if (peek() != '.') { - return stringToken(IsReference ? MITokenKind::MachineBasicBlock - : MITokenKind::MachineBasicBlockLabel, - Begin, Offset, {}, Number); - } - - ++Offset; - const std::size_t NameBegin = Offset; - if (peek() == '"') { - auto Quoted = scanQuotedString(Offset); - if (!Quoted) - return token(MITokenKind::Error, Begin, Offset); - Offset = Quoted->End; - return ownedStringToken(IsReference ? MITokenKind::MachineBasicBlock - : MITokenKind::MachineBasicBlockLabel, - Begin, Offset, std::move(Quoted->Value), Number); - } - while (!atEnd() && isIdentifierCharacter(peek())) - ++Offset; - return stringToken(IsReference ? MITokenKind::MachineBasicBlock - : MITokenKind::MachineBasicBlockLabel, - Begin, Offset, slice(NameBegin, Offset), Number); - } - - [[nodiscard]] std::optional lexIndex(std::string_view Prefix, MITokenKind Kind, - bool HasOptionalName) { - if (!startsWith(Prefix) || !isASCIIDigit(peek(Prefix.size()))) - return std::nullopt; - - const std::size_t Begin = Offset; - Offset += Prefix.size(); - const std::size_t NumberBegin = Offset; - while (!atEnd() && isASCIIDigit(peek())) - ++Offset; - const std::string_view Number = slice(NumberBegin, Offset); - - if (!HasOptionalName) - return token(Kind, Begin, Offset, Number); - - if (peek() != '.') - return stringToken(Kind, Begin, Offset, {}, Number); - - ++Offset; - const std::size_t NameBegin = Offset; - if (peek() == '"') { - auto Quoted = scanQuotedString(Offset); - if (!Quoted) - return token(MITokenKind::Error, Begin, Offset); - Offset = Quoted->End; - return ownedStringToken(Kind, Begin, Offset, std::move(Quoted->Value), Number); - } - while (!atEnd() && isIdentifierCharacter(peek())) - ++Offset; - return stringToken(Kind, Begin, Offset, slice(NameBegin, Offset), Number); - } - - [[nodiscard]] std::optional - lexIRReference(std::string_view Prefix, MITokenKind NumericKind, MITokenKind NamedKind) { - if (!startsWith(Prefix)) - return std::nullopt; - if (isASCIIDigit(peek(Prefix.size()))) - return lexIndex(Prefix, NumericKind, false); - return lexName(NamedKind, Prefix.size()); - } - - [[nodiscard]] std::optional lexRegister() { - if (peek() != '%' && peek() != '$') - return std::nullopt; - - const std::size_t Begin = Offset; - const char Prefix = peek(); - ++Offset; - if (Prefix == '%' && isASCIIDigit(peek())) { - const std::size_t NumberBegin = Offset; - while (!atEnd() && isASCIIDigit(peek())) - ++Offset; - return token(MITokenKind::VirtualRegister, Begin, Offset, slice(NumberBegin, Offset)); - } - - if (Prefix == '%' && !isRegisterCharacter(peek())) { - Offset = Begin; - return std::nullopt; - } - - const std::size_t NameBegin = Offset; - while (!atEnd() && isRegisterCharacter(peek())) - ++Offset; - return stringToken(Prefix == '%' ? MITokenKind::NamedVirtualRegister - : MITokenKind::NamedRegister, - Begin, Offset, slice(NameBegin, Offset)); - } - - [[nodiscard]] std::optional lexGlobalValue() { - if (peek() != '@') - return std::nullopt; - if (!isASCIIDigit(peek(1))) - return lexName(MITokenKind::NamedGlobalValue, 1); - - const std::size_t Begin = Offset++; - const std::size_t NumberBegin = Offset; - while (!atEnd() && isASCIIDigit(peek())) - ++Offset; - return token(MITokenKind::GlobalValue, Begin, Offset, slice(NumberBegin, Offset)); - } - - [[nodiscard]] std::optional lexMCSymbol() { - constexpr std::string_view Prefix = "'"); - } - ++Offset; - return stringToken(MITokenKind::MCSymbol, Begin, Offset, Name); - } - - auto Quoted = scanQuotedString(Offset); - if (!Quoted) - return token(MITokenKind::Error, Begin, Offset); - Offset = Quoted->End; - if (peek() != '>') { - return fail(Begin, Offset, "expected the ''"); - } - ++Offset; - return ownedStringToken(MITokenKind::MCSymbol, Begin, Offset, std::move(Quoted->Value)); - } - - [[nodiscard]] std::optional lexFloatHexBits() { - if (!startsWith("f0x") && !startsWith("f0X")) - return std::nullopt; - std::size_t End = Offset + 3; - while (End < text().size() && isASCIIHexDigit(text()[End])) - ++End; - if (End == Offset + 3) - return std::nullopt; - const std::size_t Begin = Offset; - Offset = End; - return token(MITokenKind::FloatingPointLiteral, Begin, Offset); - } - - [[nodiscard]] std::optional lexHexadecimalLiteral() { - if (peek() != '0' || (peek(1) != 'x' && peek(1) != 'X')) - return std::nullopt; - - std::size_t End = Offset + 2; - bool IsFloatingPoint = false; - if (End < text().size() && isHexFloatingPointPrefix(text()[End])) { - IsFloatingPoint = true; - ++End; - } - const std::size_t DigitsBegin = End; - while (End < text().size() && isASCIIHexDigit(text()[End])) - ++End; - if (End == DigitsBegin) - return std::nullopt; - - const std::size_t Begin = Offset; - Offset = End; - return token(IsFloatingPoint ? MITokenKind::FloatingPointLiteral : MITokenKind::HexLiteral, - Begin, Offset); - } - - [[nodiscard]] std::optional lexNumericalLiteral() { - if (!isASCIIDigit(peek()) && (peek() != '-' || !isASCIIDigit(peek(1)))) { - return std::nullopt; - } - - const std::size_t Begin = Offset; - if (peek() == '-') - ++Offset; - while (!atEnd() && isASCIIDigit(peek())) - ++Offset; - - if (peek() != '.') { - return token(MITokenKind::IntegerLiteral, Begin, Offset, slice(Begin, Offset)); - } - - ++Offset; - while (!atEnd() && isASCIIDigit(peek())) - ++Offset; - if ((peek() == 'e' || peek() == 'E') && - (isASCIIDigit(peek(1)) || ((peek(1) == '-' || peek(1) == '+') && isASCIIDigit(peek(2))))) { - ++Offset; - if (peek() == '-' || peek() == '+') - ++Offset; - while (!atEnd() && isASCIIDigit(peek())) - ++Offset; - } - return token(MITokenKind::FloatingPointLiteral, Begin, Offset); - } - - [[nodiscard]] std::optional lexExclaim() { - if (peek() != '!') - return std::nullopt; - - const std::size_t Begin = Offset++; - if (isASCIIDigit(peek()) || !isIdentifierCharacter(peek())) - return token(MITokenKind::Exclaim, Begin, Offset); - - while (!atEnd() && isIdentifierCharacter(peek())) - ++Offset; - const std::string_view Spelling = slice(Begin, Offset); - if (!isMIMetadataKeyword(Spelling)) { - return fail(Begin, Offset, "use of unknown metadata keyword '" + std::string(Spelling) + "'"); - } - return stringToken(MITokenKind::MetadataKeyword, Begin, Offset, Spelling); - } - - [[nodiscard]] std::optional lexSymbol() { - const std::size_t Begin = Offset; - if (startsWith("::")) { - Offset += 2; - return token(MITokenKind::DoubleColon, Begin, Offset); - } - - MITokenKind Kind = MITokenKind::Error; - switch (peek()) { - case ',': - Kind = MITokenKind::Comma; - break; - case '=': - Kind = MITokenKind::Equal; - break; - case ':': - Kind = MITokenKind::Colon; - break; - case '.': - Kind = MITokenKind::Dot; - break; - case '(': - Kind = MITokenKind::LeftParen; - break; - case ')': - Kind = MITokenKind::RightParen; - break; - case '{': - Kind = MITokenKind::LeftBrace; - break; - case '}': - Kind = MITokenKind::RightBrace; - break; - case '+': - Kind = MITokenKind::Plus; - break; - case '-': - Kind = MITokenKind::Minus; - break; - case '<': - Kind = MITokenKind::Less; - break; - case '>': - Kind = MITokenKind::Greater; - break; - default: - return std::nullopt; - } - ++Offset; - return token(Kind, Begin, Offset); - } - - [[nodiscard]] std::optional lexNewline() { - if (!isNewlineCharacter(peek())) - return std::nullopt; - const std::size_t Begin = Offset++; - if (text()[Begin] == '\r' && peek() == '\n') - ++Offset; - return token(MITokenKind::Newline, Begin, Offset); - } - - [[nodiscard]] std::optional lexQuotedIRValue() { - if (peek() != '`') - return std::nullopt; - - const std::size_t Begin = Offset++; - const std::size_t ValueBegin = Offset; - while (!atEnd() && peek() != '`' && !isNewlineCharacter(peek())) - ++Offset; - if (atEnd() || isNewlineCharacter(peek())) { - return fail(Begin, Offset, "end of machine instruction reached before the closing '`'"); - } - const std::size_t ValueEnd = Offset++; - return stringToken(MITokenKind::QuotedIRValue, Begin, Offset, slice(ValueBegin, ValueEnd)); - } - - [[nodiscard]] MIToken lexToken() { - if (auto Result = lexMachineBasicBlock()) - return std::move(*Result); - if (auto Result = lexFloatHexBits()) - return std::move(*Result); - - if (isASCIIAlpha(peek()) || peek() == '_') { - const std::size_t Begin = Offset; - while (!atEnd() && isIdentifierCharacter(peek())) - ++Offset; - const std::string_view Value = slice(Begin, Offset); - if (Value == "_") - return token(MITokenKind::Underscore, Begin, Offset); - return stringToken(isMIKeyword(Value) ? MITokenKind::Keyword : MITokenKind::Identifier, Begin, - Offset, Value); - } - - if (auto Result = lexIndex("%jump-table.", MITokenKind::JumpTableIndex, false)) - return std::move(*Result); - if (auto Result = lexIndex("%stack.", MITokenKind::StackObject, true)) - return std::move(*Result); - if (auto Result = lexIndex("%fixed-stack.", MITokenKind::FixedStackObject, false)) - return std::move(*Result); - if (auto Result = lexIndex("%const.", MITokenKind::ConstantPoolItem, false)) - return std::move(*Result); - if (startsWith("%subreg.")) - return lexName(MITokenKind::SubRegisterIndex, 8); - if (auto Result = lexIRReference("%ir-block.", MITokenKind::IRBlock, MITokenKind::NamedIRBlock)) - return std::move(*Result); - if (auto Result = lexIRReference("%ir.", MITokenKind::IRValue, MITokenKind::NamedIRValue)) - return std::move(*Result); - if (auto Result = lexRegister()) - return std::move(*Result); - if (auto Result = lexGlobalValue()) - return std::move(*Result); - if (peek() == '&') - return lexName(MITokenKind::ExternalSymbol, 1); - if (auto Result = lexMCSymbol()) - return std::move(*Result); - if (auto Result = lexHexadecimalLiteral()) - return std::move(*Result); - if (auto Result = lexNumericalLiteral()) - return std::move(*Result); - if (auto Result = lexExclaim()) - return std::move(*Result); - if (auto Result = lexSymbol()) - return std::move(*Result); - if (auto Result = lexNewline()) - return std::move(*Result); - if (auto Result = lexQuotedIRValue()) - return std::move(*Result); - if (peek() == '"') - return lexName(MITokenKind::StringConstant, 0); - - const std::size_t Begin = Offset++; - return fail(Begin, Offset, - unexpectedCharacterMessage(static_cast(text()[Begin]))); - } - - TextSource Source; - MILexerOptions Options; - std::size_t Offset = 0; - std::size_t TokenCount = 0; - bool Failed = false; - std::vector Diagnostics; -}; - -MILexer::MILexer(std::string SourceName, std::string SourceText, MILexerOptions Options) - : Implementation( - std::make_unique(std::move(SourceName), std::move(SourceText), Options)) {} - -MILexer::MILexer(MILexer &&) noexcept = default; -MILexer &MILexer::operator=(MILexer &&) noexcept = default; -MILexer::~MILexer() = default; - -MIToken MILexer::next() { - assert(Implementation != nullptr); - return Implementation->next(); -} - -const TextSource &MILexer::source() const noexcept { - assert(Implementation != nullptr); - return Implementation->source(); -} - -std::span MILexer::diagnostics() const noexcept { - assert(Implementation != nullptr); - return Implementation->diagnostics(); -} - -bool MILexer::failed() const noexcept { - assert(Implementation != nullptr); - return Implementation->failed(); -} - -std::size_t MILexer::tokenCount() const noexcept { - assert(Implementation != nullptr); - return Implementation->tokenCount(); -} - -} // namespace rcc::mir diff --git a/backend/lib/MIR/MIParser.cpp b/backend/lib/MIR/MIParser.cpp deleted file mode 100644 index 3748187..0000000 --- a/backend/lib/MIR/MIParser.cpp +++ /dev/null @@ -1,2967 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "rcc/MIR/MIParser.h" - -#include "rcc/CodeGen/GenericOpcodes.h" -#include "rcc/CodeGen/InlineAsm.h" -#include "rcc/CodeGen/InstructionDescriptor.h" -#include "rcc/CodeGen/MachineBasicBlock.h" -#include "rcc/CodeGen/MachineCFGUpdater.h" -#include "rcc/CodeGen/MachineFunction.h" -#include "rcc/CodeGen/MachineInstr.h" -#include "rcc/CodeGen/MachineOperand.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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace rcc::mir { -namespace { - -[[nodiscard]] bool parseUnsignedDecimal(std::string_view Spelling, std::uint64_t &Value) { - if (Spelling.empty() || Spelling.front() == '-') - return false; - const char *Begin = Spelling.data(); - const char *End = Begin + Spelling.size(); - const auto Result = std::from_chars(Begin, End, Value, 10); - return Result.ec == std::errc{} && Result.ptr == End; -} - -[[nodiscard]] bool parseSignedDecimal(std::string_view Spelling, std::int64_t &Value) { - if (Spelling.empty()) - return false; - const char *Begin = Spelling.data(); - const char *End = Begin + Spelling.size(); - const auto Result = std::from_chars(Begin, End, Value, 10); - return Result.ec == std::errc{} && Result.ptr == End; -} - -[[nodiscard]] bool parseHexadecimal(std::string_view Spelling, std::uint64_t &Value) { - if (Spelling.size() < 3 || Spelling[0] != '0' || (Spelling[1] != 'x' && Spelling[1] != 'X')) - return false; - const char *Begin = Spelling.data() + 2; - const char *End = Spelling.data() + Spelling.size(); - const auto Result = std::from_chars(Begin, End, Value, 16); - return Result.ec == std::errc{} && Result.ptr == End; -} - -[[nodiscard]] bool parseUnsignedDigits(std::string_view Spelling, std::uint64_t &Value) { - if (Spelling.empty()) - return false; - const char *Begin = Spelling.data(); - const char *End = Begin + Spelling.size(); - const auto Result = std::from_chars(Begin, End, Value, 10); - return Result.ec == std::errc{} && Result.ptr == End; -} - -[[nodiscard]] std::optional floatSemanticsForKeyword(std::string_view Keyword) { - if (Keyword == "half") - return FloatSemantics::IEEEHalf; - if (Keyword == "bfloat") - return FloatSemantics::BFloat; - if (Keyword == "float") - return FloatSemantics::IEEESingle; - if (Keyword == "double") - return FloatSemantics::IEEEDouble; - if (Keyword == "x86_fp80") - return FloatSemantics::X87DoubleExtended; - if (Keyword == "fp128") - return FloatSemantics::IEEEQuad; - if (Keyword == "ppc_fp128") - return FloatSemantics::PPCDoubleDouble; - return std::nullopt; -} - -[[nodiscard]] bool isFloatingSemanticsKeyword(const MIToken &Token) { - return Token.kind() == MITokenKind::Keyword && - floatSemanticsForKeyword(Token.stringValue()).has_value(); -} - -[[nodiscard]] bool isCFIOperationToken(const MIToken &Token) { - return Token.isKeyword("same_value") || Token.isKeyword("remember_state") || - Token.isKeyword("restore_state") || Token.isKeyword("offset") || - Token.isKeyword("llvm_def_aspace_cfa") || Token.isKeyword("def_cfa_register") || - Token.isKeyword("def_cfa_offset") || Token.isKeyword("def_cfa") || - Token.isKeyword("rel_offset") || Token.isKeyword("adjust_cfa_offset") || - Token.isKeyword("escape") || Token.isKeyword("restore") || Token.isKeyword("undefined") || - Token.isKeyword("register") || Token.isKeyword("window_save") || - Token.isKeyword("negate_ra_sign_state") || - Token.isKeyword("negate_ra_sign_state_with_pc") || Token.isKeyword("llvm_register_pair") || - Token.isKeyword("llvm_vector_registers") || Token.isKeyword("llvm_vector_offset") || - Token.isKeyword("llvm_vector_register_mask"); -} - -[[nodiscard]] Expected parseIntegerImmediateValue(std::uint32_t BitWidth, - std::string_view Spelling) { - if (BitWidth == 0 || BitWidth > IntegerImmediate::MaxBitWidth) - return makeUnexpected(ErrorCode::ParseError, "typed integer width is out of range"); - - const bool Negative = Spelling.starts_with('-'); - if (Negative) - Spelling.remove_prefix(1); - if (Spelling.empty()) - return makeUnexpected(ErrorCode::ParseError, "typed integer has no digits"); - - std::vector Words((static_cast(BitWidth) + 63U) / 64U, 0); - for (char Character : Spelling) { - if (Character < '0' || Character > '9') - return makeUnexpected(ErrorCode::ParseError, "typed integer contains a non-decimal digit"); - std::uint64_t Carry = static_cast(Character - '0'); - for (std::uint64_t &Word : Words) { - const unsigned __int128 Product = static_cast(Word) * 10U + Carry; - Word = static_cast(Product); - Carry = static_cast(Product >> 64U); - } - } - - // LLVM parses the arbitrary-precision literal first and then truncates it to - // the declared integer width. Keep exactly that modulo-2^N bit pattern. - if (BitWidth % 64U != 0) - Words.back() &= (std::uint64_t{1} << (BitWidth % 64U)) - 1U; - if (Negative) { - for (std::uint64_t &Word : Words) - Word = ~Word; - std::uint64_t Carry = 1; - for (std::uint64_t &Word : Words) { - const std::uint64_t Previous = Word; - Word += Carry; - Carry = Carry != 0 && Word < Previous ? 1 : 0; - } - if (BitWidth % 64U != 0) - Words.back() &= (std::uint64_t{1} << (BitWidth % 64U)) - 1U; - } - return IntegerImmediate::create(BitWidth, Words); -} - -[[nodiscard]] std::optional minimumTypeSizeInBits(LLT Type) noexcept { - if (!Type.isValid() || Type.isToken()) - return std::nullopt; - const std::uint64_t ScalarSize = Type.scalarBitWidth(); - if (!Type.isVector()) - return ScalarSize; - const std::uint64_t ElementCount = Type.minimumElementCount(); - if (ScalarSize != 0 && ElementCount > std::numeric_limits::max() / ScalarSize) - return std::nullopt; - return ScalarSize * ElementCount; -} - -[[nodiscard]] std::optional atomicOrdering(std::string_view Name) noexcept { - if (Name == "unordered") - return AtomicOrdering::Unordered; - if (Name == "monotonic") - return AtomicOrdering::Monotonic; - if (Name == "acquire") - return AtomicOrdering::Acquire; - if (Name == "release") - return AtomicOrdering::Release; - if (Name == "acq_rel") - return AtomicOrdering::AcquireRelease; - if (Name == "seq_cst") - return AtomicOrdering::SequentiallyConsistent; - return std::nullopt; -} - -[[nodiscard]] bool isInstructionFlag(const MIToken &Token) { - return Token.isKeyword("frame-setup") || Token.isKeyword("frame-destroy") || - Token.isKeyword("nnan") || Token.isKeyword("ninf") || Token.isKeyword("nsz") || - Token.isKeyword("arcp") || Token.isKeyword("contract") || Token.isKeyword("afn") || - Token.isKeyword("reassoc") || Token.isKeyword("nuw") || Token.isKeyword("nsw") || - Token.isKeyword("nusw") || Token.isKeyword("exact") || Token.isKeyword("nofpexcept") || - Token.isKeyword("unpredictable") || Token.isKeyword("noconvergent") || - Token.isKeyword("nneg") || Token.isKeyword("disjoint") || Token.isKeyword("samesign") || - Token.isKeyword("inbounds"); -} - -[[nodiscard]] std::optional instructionFlag(std::string_view Name) { - if (Name == "frame-setup") - return MachineInstrFlag::FrameSetup; - if (Name == "frame-destroy") - return MachineInstrFlag::FrameDestroy; - if (Name == "nnan") - return MachineInstrFlag::FastMathNoNaNs; - if (Name == "ninf") - return MachineInstrFlag::FastMathNoInfs; - if (Name == "nsz") - return MachineInstrFlag::FastMathNoSignedZeros; - if (Name == "arcp") - return MachineInstrFlag::FastMathAllowReciprocal; - if (Name == "contract") - return MachineInstrFlag::FastMathAllowContract; - if (Name == "afn") - return MachineInstrFlag::FastMathApproxFunc; - if (Name == "reassoc") - return MachineInstrFlag::FastMathAllowReassoc; - if (Name == "nuw") - return MachineInstrFlag::NoUnsignedWrap; - if (Name == "nsw") - return MachineInstrFlag::NoSignedWrap; - if (Name == "nusw") - return MachineInstrFlag::NoUSWrap; - if (Name == "exact") - return MachineInstrFlag::Exact; - if (Name == "nofpexcept") - return MachineInstrFlag::NoFPExcept; - if (Name == "unpredictable") - return MachineInstrFlag::Unpredictable; - if (Name == "noconvergent") - return MachineInstrFlag::NoConvergent; - if (Name == "nneg") - return MachineInstrFlag::NonNeg; - if (Name == "disjoint") - return MachineInstrFlag::Disjoint; - if (Name == "samesign") - return MachineInstrFlag::SameSign; - if (Name == "inbounds") - return MachineInstrFlag::InBounds; - return std::nullopt; -} - -[[nodiscard]] std::optional integerPredicate(std::string_view Name) { - if (Name == "eq") - return IntegerPredicate::Equal; - if (Name == "ne") - return IntegerPredicate::NotEqual; - if (Name == "sgt") - return IntegerPredicate::SignedGreaterThan; - if (Name == "sge") - return IntegerPredicate::SignedGreaterEqual; - if (Name == "slt") - return IntegerPredicate::SignedLessThan; - if (Name == "sle") - return IntegerPredicate::SignedLessEqual; - if (Name == "ugt") - return IntegerPredicate::UnsignedGreaterThan; - if (Name == "uge") - return IntegerPredicate::UnsignedGreaterEqual; - if (Name == "ult") - return IntegerPredicate::UnsignedLessThan; - if (Name == "ule") - return IntegerPredicate::UnsignedLessEqual; - return std::nullopt; -} - -[[nodiscard]] std::optional floatingPredicate(std::string_view Name) { - if (Name == "false") - return FloatingPredicate::AlwaysFalse; - if (Name == "oeq") - return FloatingPredicate::OrderedEqual; - if (Name == "ogt") - return FloatingPredicate::OrderedGreaterThan; - if (Name == "oge") - return FloatingPredicate::OrderedGreaterEqual; - if (Name == "olt") - return FloatingPredicate::OrderedLessThan; - if (Name == "ole") - return FloatingPredicate::OrderedLessEqual; - if (Name == "one") - return FloatingPredicate::OrderedNotEqual; - if (Name == "ord") - return FloatingPredicate::Ordered; - if (Name == "uno") - return FloatingPredicate::Unordered; - if (Name == "ueq") - return FloatingPredicate::UnorderedEqual; - if (Name == "ugt") - return FloatingPredicate::UnorderedGreaterThan; - if (Name == "uge") - return FloatingPredicate::UnorderedGreaterEqual; - if (Name == "ult") - return FloatingPredicate::UnorderedLessThan; - if (Name == "ule") - return FloatingPredicate::UnorderedLessEqual; - if (Name == "une") - return FloatingPredicate::UnorderedNotEqual; - if (Name == "true") - return FloatingPredicate::AlwaysTrue; - return std::nullopt; -} - -} // namespace - -class MIParser::Impl final { -public: - Impl(MachineFunction &Function, const TargetDescription &Target, std::string SourceName, - std::string SourceText, MIParserContext Context, MIParserOptions Options) - : Function(Function), Target(Target), - Lexer(std::move(SourceName), std::move(SourceText), - MILexerOptions{.MaximumTokenLength = Options.MaximumTokenLength, - .MaximumTokens = Options.MaximumTokens}), - Context(std::move(Context)), Options(Options) {} - - [[nodiscard]] Expected parse(); - - [[nodiscard]] const TextSource &source() const noexcept { - return Lexer.source(); - } - - [[nodiscard]] std::span diagnostics() const noexcept { - return Diagnostics; - } - - [[nodiscard]] bool failed() const noexcept { - return !Diagnostics.empty(); - } - -private: - struct ParsedOperand final { - MachineOperand Operand; - std::optional TiedDef; - TextRange Range; - }; - - [[nodiscard]] const MIToken &token() const noexcept { - return Tokens[Position]; - } - - [[nodiscard]] bool at(MITokenKind Kind) const noexcept { - return token().is(Kind); - } - - [[nodiscard]] bool nextIs(MITokenKind Kind) const noexcept { - return Position + 1U < Tokens.size() && Tokens[Position + 1U].is(Kind); - } - - [[nodiscard]] bool atKeyword(std::string_view Keyword) const noexcept { - return token().isKeyword(Keyword); - } - - void advance() noexcept { - if (!at(MITokenKind::EndOfFile)) - ++Position; - } - - [[nodiscard]] bool consume(MITokenKind Kind) noexcept { - if (!at(Kind)) - return false; - advance(); - return true; - } - - [[nodiscard]] bool expect(MITokenKind Kind, std::string_view Description); - [[nodiscard]] bool fail(const MIToken &At, std::string Message); - [[nodiscard]] bool fail(std::string Message) { - return fail(token(), std::move(Message)); - } - [[nodiscard]] Expected failureResult() const; - [[nodiscard]] bool importError(const MIToken &At, const Error &Failure); - [[nodiscard]] bool tokenize(); - - [[nodiscard]] bool parseUnsigned(const MIToken &Token, std::uint64_t &Value); - [[nodiscard]] bool parseUnsigned32(const MIToken &Token, std::uint32_t &Value); - [[nodiscard]] bool parseSigned64(const MIToken &Token, std::int64_t &Value); - [[nodiscard]] bool parseOptionalOffset(std::int64_t &Offset); - [[nodiscard]] bool parseAlignment(Alignment &Result); - [[nodiscard]] bool parseLowLevelType(LLT &Result); - [[nodiscard]] bool parseScalarLowLevelType(const MIToken &Token, LLT &Result); - - [[nodiscard]] bool parseFirstPass(); - [[nodiscard]] bool parseSecondPass(); - [[nodiscard]] bool parseBlockDefinition(bool Create, std::uint32_t &ID); - [[nodiscard]] bool parseBlockAttributes(MachineBasicBlock *Block); - [[nodiscard]] bool parseBlockLiveIns(MachineBasicBlock &Block); - [[nodiscard]] bool parseBlockSuccessors(MachineBasicBlock &Block); - [[nodiscard]] bool parseInstruction(MachineBasicBlock &Block, bool InBundle, - MachineInstr *PreviousInstruction, MachineInstr *&Result); - [[nodiscard]] bool inferSuccessors(); - - [[nodiscard]] bool parseRegister(Register &Result); - [[nodiscard]] bool parseRegisterOperand(bool IsDef, ParsedOperand &Result); - [[nodiscard]] bool parseTargetFlags(std::uint32_t &Flags); - [[nodiscard]] bool parseOperand(bool IsDef, ParsedOperand &Result); - [[nodiscard]] bool parseInlineAsmExtraInfoOperand(ParsedOperand &Result); - [[nodiscard]] bool parseInlineAsmOperandFlag(ParsedOperand &Result); - [[nodiscard]] bool parseTypedIntegerOperand(ParsedOperand &Result); - [[nodiscard]] bool parseFloatingImmediateOperand(ParsedOperand &Result); - [[nodiscard]] bool parseMetadataReference(MachineMetadataReference &Result); - [[nodiscard]] bool parseMetadataOperand(ParsedOperand &Result); - [[nodiscard]] bool - parseInlineDILocation(std::vector &InlineChain, - std::optional &TerminalInlinedAt); - [[nodiscard]] bool parseCFIRegister(DwarfRegisterNumber &Result); - [[nodiscard]] bool parseCFISigned(std::int64_t &Result); - [[nodiscard]] bool parseCFIUnsigned(std::uint32_t &Result); - [[nodiscard]] bool parseCFIOperand(ParsedOperand &Result); - [[nodiscard]] bool parseMemoryBase(MemoryBase &Result, std::int64_t &Offset); - [[nodiscard]] bool parseMemoryOperand(const MachineMemOperand *&Result, Opcode InstructionOpcode); - [[nodiscard]] bool parsePredicateOperand(ParsedOperand &Result, bool IsFloating); - [[nodiscard]] bool parseShuffleMaskOperand(ParsedOperand &Result); - [[nodiscard]] bool parseDebugInstrRefOperand(ParsedOperand &Result); - [[nodiscard]] bool parseLaneMaskOperand(ParsedOperand &Result); - [[nodiscard]] bool parseIntrinsicOperand(ParsedOperand &Result); - [[nodiscard]] bool parseTargetIndexOperand(ParsedOperand &Result); - [[nodiscard]] bool parseLiveOutMaskOperand(ParsedOperand &Result); - [[nodiscard]] bool parseCustomMaskOperand(ParsedOperand &Result); - [[nodiscard]] bool parseBlockAddressOperand(ParsedOperand &Result); - - [[nodiscard]] Register getOrCreateVirtualRegister(const MIToken &Token); - [[nodiscard]] MachineBasicBlock *blockFor(const MIToken &Token); - [[nodiscard]] std::optional frameIndexFor(const MIToken &Token, bool Fixed); - [[nodiscard]] std::optional constantPoolIndexFor(const MIToken &Token); - [[nodiscard]] std::optional jumpTableIndexFor(const MIToken &Token); - [[nodiscard]] std::string symbolName(const MIToken &Token) const; - - MachineFunction &Function; - const TargetDescription &Target; - MILexer Lexer; - MIParserContext Context; - MIParserOptions Options; - std::vector Tokens; - std::size_t Position = 0; - std::vector Diagnostics; - std::unordered_map Blocks; - std::unordered_map NumericVirtualRegisters; - std::unordered_map NamedVirtualRegisters; - std::unordered_set ExplicitSuccessorBlocks; - std::size_t ParsedInstructionCount = 0; - bool ParseAttempted = false; -}; - -bool MIParser::Impl::expect(MITokenKind Kind, std::string_view Description) { - if (consume(Kind)) - return false; - return fail("expected " + std::string(Description) + ", found " + - std::string(getMITokenKindName(token().kind()))); -} - -bool MIParser::Impl::fail(const MIToken &At, std::string Message) { - if (Diagnostics.empty()) { - Diagnostics.push_back(TextDiagnostic{ - .Severity = DiagnosticSeverity::Error, - .Category = "mir-parser", - .Message = std::move(Message), - .SourceName = std::string(Lexer.source().name()), - .Range = At.range(), - }); - } - return true; -} - -Expected MIParser::Impl::failureResult() const { - const std::string Message = - Diagnostics.empty() ? "machine instruction parsing failed" : Diagnostics.front().Message; - return makeUnexpected(ErrorCode::ParseError, Message); -} - -bool MIParser::Impl::importError(const MIToken &At, const Error &Failure) { - return fail(At, std::string(Failure.message())); -} - -bool MIParser::Impl::tokenize() { - while (true) { - MIToken Token = Lexer.next(); - const MITokenKind Kind = Token.kind(); - Tokens.push_back(std::move(Token)); - if (Kind == MITokenKind::Error) { - const auto LexerDiagnostics = Lexer.diagnostics(); - Diagnostics.assign(LexerDiagnostics.begin(), LexerDiagnostics.end()); - return true; - } - if (Kind == MITokenKind::EndOfFile) - return false; - } -} - -bool MIParser::Impl::parseUnsigned(const MIToken &Token, std::uint64_t &Value) { - if (Token.kind() == MITokenKind::HexLiteral) - return !parseHexadecimal(Token.spelling(), Value); - if (Token.hasIntegerValue()) - return !parseUnsignedDecimal(Token.integerValue(), Value); - return true; -} - -bool MIParser::Impl::parseUnsigned32(const MIToken &Token, std::uint32_t &Value) { - std::uint64_t Wide = 0; - if (parseUnsigned(Token, Wide) || Wide > std::numeric_limits::max()) - return true; - Value = static_cast(Wide); - return false; -} - -bool MIParser::Impl::parseSigned64(const MIToken &Token, std::int64_t &Value) { - if (Token.kind() != MITokenKind::IntegerLiteral) - return true; - if (parseSignedDecimal(Token.integerValue(), Value)) - return false; - std::uint64_t Unsigned = 0; - if (!parseUnsignedDecimal(Token.integerValue(), Unsigned)) - return true; - Value = std::bit_cast(Unsigned); - return false; -} - -bool MIParser::Impl::parseOptionalOffset(std::int64_t &Offset) { - Offset = 0; - if (!at(MITokenKind::Plus) && !at(MITokenKind::Minus)) - return false; - const bool Negative = at(MITokenKind::Minus); - advance(); - std::uint64_t Magnitude = 0; - if (!at(MITokenKind::IntegerLiteral) || parseUnsigned(token(), Magnitude) || - Magnitude > (std::uint64_t{1} << 63U)) { - return fail("expected a 64-bit integer offset"); - } - if (Negative) { - if (Magnitude == (std::uint64_t{1} << 63U)) - Offset = std::numeric_limits::min(); - else - Offset = -static_cast(Magnitude); - } else { - if (Magnitude > static_cast(std::numeric_limits::max())) - return fail("positive offset exceeds signed 64-bit range"); - Offset = static_cast(Magnitude); - } - advance(); - return false; -} - -bool MIParser::Impl::parseAlignment(Alignment &Result) { - advance(); - std::uint64_t Bytes = 0; - if (!at(MITokenKind::IntegerLiteral) || parseUnsigned(token(), Bytes)) - return fail("expected an unsigned alignment"); - auto Parsed = Alignment::create(Bytes); - if (!Parsed) - return importError(token(), Parsed.error()); - Result = *Parsed; - advance(); - return false; -} - -bool MIParser::Impl::parseScalarLowLevelType(const MIToken &TypeToken, LLT &Result) { - if (TypeToken.kind() != MITokenKind::Identifier) - return fail(TypeToken, "expected a scalar low-level type"); - std::string_view Spelling = TypeToken.stringValue(); - enum class ScalarKind : std::uint8_t { - Any, - Integer, - Float, - BFloat, - Pointer, - }; - ScalarKind Kind = ScalarKind::Any; - std::string_view Digits; - if (Spelling.starts_with("bf")) { - Kind = ScalarKind::BFloat; - Digits = Spelling.substr(2); - } else if (Spelling.starts_with('s')) { - Kind = ScalarKind::Any; - Digits = Spelling.substr(1); - } else if (Spelling.starts_with('i')) { - Kind = ScalarKind::Integer; - Digits = Spelling.substr(1); - } else if (Spelling.starts_with('f')) { - Kind = ScalarKind::Float; - Digits = Spelling.substr(1); - } else if (Spelling.starts_with('p')) { - Kind = ScalarKind::Pointer; - Digits = Spelling.substr(1); - } else { - return fail(TypeToken, "unknown scalar low-level type '" + std::string(Spelling) + "'"); - } - - std::uint64_t Value = 0; - if (!parseUnsignedDigits(Digits, Value)) - return fail(TypeToken, "expected integers after 's'/'i'/'f'/'bf'/'p' type identifier"); - - Expected Created = makeUnexpected(ErrorCode::ParseError, "invalid low-level type"); - switch (Kind) { - case ScalarKind::Any: - if (Value == 0) { - Result = LLT::token(); - return false; - } - Created = LLT::makeAnyScalar(Value); - break; - case ScalarKind::Integer: - Created = LLT::makeInteger(Value); - break; - case ScalarKind::Float: { - std::optional Semantics; - if (Value == 16) - Semantics = FloatSemantics::IEEEHalf; - else if (Value == 32) - Semantics = FloatSemantics::IEEESingle; - else if (Value == 64) - Semantics = FloatSemantics::IEEEDouble; - else if (Value == 128) - Semantics = FloatSemantics::IEEEQuad; - if (!Semantics) - return fail(TypeToken, "unsupported IEEE floating low-level type width"); - Created = LLT::makeFloat(*Semantics); - break; - } - case ScalarKind::BFloat: - if (Value != 16) - return fail(TypeToken, "bfloat low-level type must have width 16"); - Created = LLT::makeFloat(FloatSemantics::BFloat); - break; - case ScalarKind::Pointer: - if (!Context.PointerSizeInBits) - return fail(TypeToken, "pointer low-level type requires an explicit pointer size"); - if (auto PointerSize = Context.PointerSizeInBits(static_cast(Value))) - Created = LLT::makePointer(Value, *PointerSize); - else - return importError(TypeToken, PointerSize.error()); - break; - } - if (!Created) - return importError(TypeToken, Created.error()); - Result = *Created; - return false; -} - -bool MIParser::Impl::parseLowLevelType(LLT &Result) { - const MIToken Begin = token(); - const auto HasScalarPrefix = [](std::string_view Spelling) { - return Spelling.starts_with('s') || Spelling.starts_with('i') || Spelling.starts_with('f') || - Spelling.starts_with("bf") || Spelling.starts_with('p'); - }; - if (at(MITokenKind::Identifier)) { - if (!HasScalarPrefix(token().stringValue())) { - return fail(Begin, "expected tN, pA, , , , or " - " for GlobalISel type, where t = {'s', 'i', 'f', 'bf'}"); - } - if (parseScalarLowLevelType(token(), Result)) - return true; - advance(); - return false; - } - if (!consume(MITokenKind::Less)) { - return fail(Begin, "expected tN, pA, , , , or " - " for GlobalISel type, where t = {'s', 'i', 'f', 'bf'}"); - } - - bool Scalable = false; - if (at(MITokenKind::Identifier) && token().stringValue() == "vscale") { - Scalable = true; - advance(); - if (!at(MITokenKind::Identifier) || token().stringValue() != "x") { - return fail("expected , where t = {'s', 'i', 'f', 'bf', 'p'}"); - } - advance(); - } - - const auto VectorError = [this, &Begin, Scalable] { - if (Scalable) { - return fail(Begin, "expected for vector type, where t = " - "{'s', 'i', 'f', 'bf', 'p'}"); - } - return fail(Begin, "expected for vector type, where t = {'s', 'i', 'f', 'bf', 'p'}"); - }; - - std::uint64_t ElementCount = 0; - if (!at(MITokenKind::IntegerLiteral) || parseUnsigned(token(), ElementCount)) - return VectorError(); - advance(); - if (!at(MITokenKind::Identifier) || token().stringValue() != "x") - return VectorError(); - advance(); - - if (!at(MITokenKind::Identifier) || !HasScalarPrefix(token().stringValue())) - return VectorError(); - LLT ElementType; - if (parseScalarLowLevelType(token(), ElementType)) - return true; - if (ElementType.isToken()) - return fail("token cannot be a vector element type"); - advance(); - if (!consume(MITokenKind::Greater)) - return VectorError(); - - Expected Created = Scalable ? LLT::makeScalableVector(ElementCount, ElementType) - : LLT::makeFixedVector(ElementCount, ElementType); - if (!Created) - return importError(Begin, Created.error()); - Result = *Created; - return false; -} - -bool MIParser::Impl::parseBlockAttributes(MachineBasicBlock *Block) { - std::unordered_set Seen; - while (!at(MITokenKind::RightParen)) { - const MIToken AttributeToken = token(); - if (atKeyword("machine-block-address-taken") || atKeyword("landing-pad") || - atKeyword("inlineasm-br-indirect-target") || atKeyword("ehfunclet-entry") || - atKeyword("ehscope-entry")) { - const std::string Name(AttributeToken.stringValue()); - if (!Seen.insert(Name).second) - return fail("duplicate basic block attribute '" + Name + "'"); - if (Block != nullptr) { - if (Name == "machine-block-address-taken") - Block->attributes().set(MachineBasicBlockAttribute::MachineBlockAddressTaken); - else if (Name == "landing-pad") - Block->attributes().set(MachineBasicBlockAttribute::LandingPad); - else if (Name == "inlineasm-br-indirect-target") - Block->attributes().set(MachineBasicBlockAttribute::InlineAsmBrIndirectTarget); - else if (Name == "ehfunclet-entry") - Block->attributes().set(MachineBasicBlockAttribute::EHFuncletEntry); - else - Block->attributes().set(MachineBasicBlockAttribute::EHScopeEntry); - } - advance(); - } else if (atKeyword("ir-block-address-taken")) { - if (!Seen.insert("ir-block-address-taken").second) - return fail("duplicate basic block attribute 'ir-block-address-taken'"); - advance(); - if (!at(MITokenKind::IRBlock) && !at(MITokenKind::NamedIRBlock)) - return fail("expected an IR block after 'ir-block-address-taken'"); - if (Block != nullptr) { - Block->attributes().set(MachineBasicBlockAttribute::IRBlockAddressTaken); - auto SetName = Block->attributes().setIRBlockName(symbolName(token())); - if (!SetName) - return importError(token(), SetName.error()); - } - advance(); - } else if (at(MITokenKind::IRBlock) || at(MITokenKind::NamedIRBlock)) { - if (!Seen.insert("ir-block").second) - return fail("duplicate IR block attribute"); - if (Context.HasIRBlock && !Context.HasIRBlock(symbolName(token()))) - return fail(token(), "use of undefined IR block '" + std::string(token().spelling()) + "'"); - if (Block != nullptr) { - auto SetName = Block->attributes().setIRBlockName(symbolName(token())); - if (!SetName) - return importError(token(), SetName.error()); - } - advance(); - } else if (atKeyword("align")) { - if (!Seen.insert("align").second) - return fail("duplicate basic block alignment"); - Alignment Align; - if (parseAlignment(Align)) - return true; - if (Block != nullptr) - Block->attributes().setAlignment(Align); - } else if (atKeyword("bbsections")) { - if (!Seen.insert("bbsections").second) - return fail("duplicate basic block section"); - advance(); - MachineBasicBlockSectionID Section; - std::uint32_t Number = 0; - if (at(MITokenKind::IntegerLiteral) && !parseUnsigned32(token(), Number)) - Section = MachineBasicBlockSectionID::numbered(Number); - else if (at(MITokenKind::Identifier) && token().stringValue() == "Exception") - Section = MachineBasicBlockSectionID::exception(); - else if (at(MITokenKind::Identifier) && token().stringValue() == "Cold") - Section = MachineBasicBlockSectionID::cold(); - else - return fail("unknown basic block section ID"); - if (Block != nullptr) - Block->attributes().setSectionID(Section); - advance(); - } else if (atKeyword("bb_id")) { - if (!Seen.insert("bb_id").second) - return fail("duplicate basic block profile ID"); - advance(); - std::uint32_t BaseID = 0; - std::uint32_t CloneID = 0; - if (at(MITokenKind::FloatingPointLiteral)) { - const std::string_view Spelling = token().spelling(); - const std::size_t Dot = Spelling.find('.'); - std::uint64_t Base = 0; - std::uint64_t Clone = 0; - if (Dot == std::string_view::npos || !parseUnsignedDecimal(Spelling.substr(0, Dot), Base) || - !parseUnsignedDecimal(Spelling.substr(Dot + 1), Clone) || - Base > std::numeric_limits::max() || - Clone > std::numeric_limits::max()) { - return fail("invalid basic block profile ID"); - } - BaseID = static_cast(Base); - CloneID = static_cast(Clone); - advance(); - } else { - if (!at(MITokenKind::IntegerLiteral) || parseUnsigned32(token(), BaseID)) - return fail("invalid basic block base ID"); - advance(); - if (consume(MITokenKind::Dot) || consume(MITokenKind::Comma)) { - if (!at(MITokenKind::IntegerLiteral) || parseUnsigned32(token(), CloneID)) - return fail("invalid basic block clone ID"); - advance(); - } - } - if (Block != nullptr) - Block->attributes().setProfileID({BaseID, CloneID}); - } else if (atKeyword("call-frame-size")) { - if (!Seen.insert("call-frame-size").second) - return fail("duplicate basic block call frame size"); - advance(); - std::uint32_t Size = 0; - if (!at(MITokenKind::IntegerLiteral) || parseUnsigned32(token(), Size)) - return fail("invalid basic block call frame size"); - if (Block != nullptr) - Block->attributes().setCallFrameSize(Size); - advance(); - } else { - return fail("unknown basic block attribute '" + std::string(token().spelling()) + "'"); - } - - if (!consume(MITokenKind::Comma)) - break; - } - return expect(MITokenKind::RightParen, "')'"); -} - -bool MIParser::Impl::parseBlockDefinition(bool Create, std::uint32_t &ID) { - const MIToken Label = token(); - if (!at(MITokenKind::MachineBasicBlockLabel) || parseUnsigned32(Label, ID)) - return fail("invalid machine basic block label"); - advance(); - - MachineBasicBlock *Block = nullptr; - if (Create) { - if (Blocks.size() >= Options.MaximumBlocks) - return fail(Label, "machine basic block count exceeds parser limit"); - if (Blocks.contains(ID)) - return fail(Label, "redefinition of machine basic block with id #" + std::to_string(ID)); - if (!Label.stringValue().empty() && Context.HasIRBlock && - !Context.HasIRBlock(Label.stringValue())) { - return fail(Label, "basic block '" + std::string(Label.stringValue()) + - "' is not defined in the function '" + std::string(Function.name()) + - "'"); - } - auto Created = Function.createBasicBlock(); - if (!Created) - return importError(Label, Created.error()); - Block = *Created; - auto Appended = Function.appendBlock(*Block); - if (!Appended) - return importError(Label, Appended.error()); - if (!Label.stringValue().empty()) { - auto SetName = Block->attributes().setName(std::string(Label.stringValue())); - if (!SetName) - return importError(Label, SetName.error()); - } - Blocks.emplace(ID, Block); - } else { - const auto Found = Blocks.find(ID); - if (Found == Blocks.end()) - return fail(Label, "use of undefined machine basic block #" + std::to_string(ID)); - Block = Found->second; - } - - if (consume(MITokenKind::LeftParen) && parseBlockAttributes(Create ? Block : nullptr)) - return true; - return expect(MITokenKind::Colon, "':' after the basic block definition"); -} - -bool MIParser::Impl::parseFirstPass() { - Position = 0; - bool AtLineStart = true; - std::size_t BraceDepth = 0; - while (!at(MITokenKind::EndOfFile)) { - if (consume(MITokenKind::Newline)) { - AtLineStart = true; - continue; - } - if (at(MITokenKind::MachineBasicBlockLabel)) { - if (!AtLineStart || token().range().Begin.Column != 1) - return fail("basic block definition must be at the start of a line"); - std::uint32_t ID = 0; - if (parseBlockDefinition(true, ID)) - return true; - AtLineStart = false; - continue; - } - if (at(MITokenKind::LeftBrace)) - ++BraceDepth; - else if (at(MITokenKind::RightBrace)) { - if (BraceDepth == 0) - return fail("extraneous closing instruction bundle brace"); - --BraceDepth; - } - AtLineStart = false; - advance(); - } - if (BraceDepth != 0) - return fail("unterminated instruction bundle"); - return false; -} - -Register MIParser::Impl::getOrCreateVirtualRegister(const MIToken &Token) { - if (Token.kind() == MITokenKind::VirtualRegister) { - std::uint32_t ID = 0; - if (parseUnsigned32(Token, ID)) - return {}; - if (const auto Found = NumericVirtualRegisters.find(ID); - Found != NumericVirtualRegisters.end()) { - return Found->second; - } - auto Created = Function.registerInfo().createVirtualRegister(); - if (!Created) { - static_cast(importError(Token, Created.error())); - return {}; - } - NumericVirtualRegisters.emplace(ID, *Created); - return *Created; - } - - const std::string Name(Token.stringValue()); - if (const auto Found = NamedVirtualRegisters.find(Name); Found != NamedVirtualRegisters.end()) - return Found->second; - auto Created = Function.registerInfo().createVirtualRegister(); - if (!Created) { - static_cast(importError(Token, Created.error())); - return {}; - } - auto Info = Function.registerInfo().virtualRegisterInfo(*Created); - if (!Info) { - static_cast(importError(Token, Info.error())); - return {}; - } - Info->get().Name = Name; - NamedVirtualRegisters.emplace(Name, *Created); - return *Created; -} - -bool MIParser::Impl::parseRegister(Register &Result) { - const MIToken RegisterToken = token(); - if (consume(MITokenKind::Underscore)) { - Result = {}; - return false; - } - if (at(MITokenKind::NamedRegister)) { - if (RegisterToken.stringValue() == "noreg") { - Result = {}; - advance(); - return false; - } - const PhysicalRegisterDesc *Description = - Target.registers().physicalRegister(RegisterToken.stringValue()); - if (Description == nullptr) - return fail("unknown physical register '$" + std::string(RegisterToken.stringValue()) + "'"); - Result = Register::fromPhysical(Description->Reg); - advance(); - return false; - } - if (at(MITokenKind::VirtualRegister) || at(MITokenKind::NamedVirtualRegister)) { - Result = getOrCreateVirtualRegister(RegisterToken); - if (Result.isNoRegister()) - return true; - advance(); - return false; - } - return fail("expected a machine register"); -} - -bool MIParser::Impl::parseRegisterOperand(bool IsDef, ParsedOperand &Result) { - const MIToken Begin = token(); - RegisterOperandFlags Flags{IsDef ? RegisterOperandFlag::Define : RegisterOperandFlag::Use}; - std::unordered_set SeenFlags; - while (token().isRegisterFlag()) { - const std::string Name(token().stringValue()); - if (!SeenFlags.insert(Name).second) - return fail("duplicate '" + Name + "' register flag"); - if (Name == "implicit") - Flags.set(RegisterOperandFlag::Implicit); - else if (Name == "implicit-def") { - Flags.clear(RegisterOperandFlag::Use); - Flags.set(RegisterOperandFlag::Define); - Flags.set(RegisterOperandFlag::Implicit); - IsDef = true; - } else if (Name == "def") { - Flags.clear(RegisterOperandFlag::Use); - Flags.set(RegisterOperandFlag::Define); - IsDef = true; - } else if (Name == "dead") - Flags.set(RegisterOperandFlag::Dead); - else if (Name == "killed") - Flags.set(RegisterOperandFlag::Kill); - else if (Name == "undef") - Flags.set(RegisterOperandFlag::Undef); - else if (Name == "internal") - Flags.set(RegisterOperandFlag::InternalRead); - else if (Name == "early-clobber") - Flags.set(RegisterOperandFlag::EarlyClobber); - else if (Name == "debug-use") - Flags.set(RegisterOperandFlag::Debug); - else if (Name == "renamable") - Flags.set(RegisterOperandFlag::Renamable); - advance(); - } - - Register Reg; - if (parseRegister(Reg)) - return true; - - SubRegIndex SubReg; - if (consume(MITokenKind::Dot)) { - if (!Reg.isVirtual() || !at(MITokenKind::Identifier)) - return fail("subregister index requires a virtual register"); - const SubRegIndexDesc *Description = Target.registers().subRegIndex(token().stringValue()); - if (Description == nullptr) - return fail("unknown subregister index '" + std::string(token().stringValue()) + "'"); - SubReg = Description->ID; - advance(); - } - - if (consume(MITokenKind::Colon)) { - if (!Reg.isVirtual()) - return fail("register class or bank requires a virtual register"); - auto InfoResult = Function.registerInfo().virtualRegisterInfo(Reg); - if (!InfoResult) - return importError(Begin, InfoResult.error()); - VirtualRegisterInfo &Info = InfoResult->get(); - if (consume(MITokenKind::Underscore)) { - if (Info.RegClass || Info.Bank) - return fail("conflicting generic virtual register constraint"); - } else if (at(MITokenKind::Identifier)) { - if (const RegisterClassDesc *Class = - Target.registers().registerClass(token().stringValue())) { - 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())) { - 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) - return importError(token(), SetBank.error()); - } else { - return fail("unknown register class or bank '" + std::string(token().stringValue()) + "'"); - } - advance(); - } else { - return fail("expected '_', register class, or register bank name"); - } - } - - std::optional TiedDef; - if (consume(MITokenKind::LeftParen)) { - if (atKeyword("tied-def")) { - if (IsDef) - return fail("'tied-def' is only valid on register uses"); - advance(); - std::uint64_t Index = 0; - if (!at(MITokenKind::IntegerLiteral) || parseUnsigned(token(), Index) || - Index > std::numeric_limits::max()) { - return fail("invalid tied-def operand index"); - } - TiedDef = static_cast(Index); - advance(); - if (expect(MITokenKind::RightParen, "')' after tied-def")) - return true; - } else { - if (!Reg.isVirtual()) - return fail("low-level type is only valid on a virtual register"); - LLT Type; - if (parseLowLevelType(Type)) - return true; - if (expect(MITokenKind::RightParen, "')' after the low-level type")) - return true; - auto InfoResult = Function.registerInfo().virtualRegisterInfo(Reg); - if (!InfoResult) - return importError(Begin, InfoResult.error()); - VirtualRegisterInfo &Info = InfoResult->get(); - if (Info.Type && *Info.Type != Type) - return fail(Begin, "conflicting virtual register low-level type"); - auto SetType = Function.registerInfo().setType(Reg, Type); - if (!SetType) - return importError(Begin, SetType.error()); - } - } - - if (IsDef && Reg.isVirtual()) { - auto InfoResult = Function.registerInfo().virtualRegisterInfo(Reg); - if (!InfoResult) - return importError(Begin, InfoResult.error()); - const VirtualRegisterInfo &Info = InfoResult->get(); - if (!Info.RegClass && !Info.Type) - return fail(Begin, "generic virtual register definition requires a low-level type"); - } - - if (Flags.has(RegisterOperandFlag::Define) && Flags.has(RegisterOperandFlag::Kill)) - return fail(Begin, "defined register operand cannot be killed"); - if (!Flags.has(RegisterOperandFlag::Define) && Flags.has(RegisterOperandFlag::Dead)) - return fail(Begin, "register use cannot be dead"); - - auto Operand = MachineOperand::registerOperand(Reg, Flags, SubReg); - if (!Operand) - return importError(Begin, Operand.error()); - Result = - ParsedOperand{std::move(*Operand), TiedDef, {Begin.range().Begin, token().range().Begin}}; - return false; -} - -MachineBasicBlock *MIParser::Impl::blockFor(const MIToken &Token) { - std::uint32_t ID = 0; - if (parseUnsigned32(Token, ID)) { - static_cast(fail(Token, "invalid machine basic block reference")); - return nullptr; - } - const auto Found = Blocks.find(ID); - if (Found == Blocks.end()) { - static_cast(fail(Token, "use of undefined machine basic block #" + std::to_string(ID))); - return nullptr; - } - if (!Token.stringValue().empty()) { - const std::optional Name = Found->second->attributes().name(); - if (!Name || *Name != Token.stringValue()) { - static_cast(fail(Token, "machine basic block reference has a mismatched name")); - return nullptr; - } - } - return Found->second; -} - -std::optional MIParser::Impl::frameIndexFor(const MIToken &Token, bool Fixed) { - std::uint32_t ID = 0; - if (parseUnsigned32(Token, ID)) { - static_cast(fail(Token, "invalid stack object reference")); - return std::nullopt; - } - const auto &Slots = Fixed ? Context.FixedStackObjects : Context.StackObjects; - for (const MIFrameObjectSlot &Slot : Slots) { - if (Slot.TextID != ID) - continue; - if (!Token.stringValue().empty() && (!Slot.Name || *Slot.Name != Token.stringValue())) { - static_cast(fail(Token, "stack object reference has a mismatched name")); - return std::nullopt; - } - if (Function.frameInfo().object(Slot.Index) == nullptr) { - static_cast(fail(Token, "stack object slot maps to a missing frame object")); - return std::nullopt; - } - return Slot.Index; - } - static_cast(fail(Token, std::string("use of undefined ") + - (Fixed ? "fixed stack" : "stack") + " object #" + - std::to_string(ID))); - return std::nullopt; -} - -std::optional MIParser::Impl::constantPoolIndexFor(const MIToken &Token) { - std::uint32_t ID = 0; - if (parseUnsigned32(Token, ID)) { - static_cast(fail(Token, "invalid constant pool reference")); - return std::nullopt; - } - for (const MIConstantPoolSlot &Slot : Context.ConstantPoolItems) { - if (Slot.TextID == ID && Function.constantPool().entry(Slot.Index) != nullptr) - return Slot.Index; - } - static_cast(fail(Token, "use of undefined constant pool item #" + std::to_string(ID))); - return std::nullopt; -} - -std::optional MIParser::Impl::jumpTableIndexFor(const MIToken &Token) { - std::uint32_t ID = 0; - if (parseUnsigned32(Token, ID)) { - static_cast(fail(Token, "invalid jump table reference")); - return std::nullopt; - } - for (const MIJumpTableSlot &Slot : Context.JumpTables) { - if (Slot.TextID == ID && (Function.jumpTableInfo().table(Slot.Index) != nullptr || - (Slot.Index.value() < Function.jumpTableInfo().tables().size() && - Function.jumpTableInfo().tables()[Slot.Index.value()].isPending()))) - return Slot.Index; - } - static_cast(fail(Token, "use of undefined jump table #" + std::to_string(ID))); - return std::nullopt; -} - -std::string MIParser::Impl::symbolName(const MIToken &Token) const { - if (Token.hasStringValue()) - return std::string(Token.stringValue()); - return std::string(Token.integerValue()); -} - -bool MIParser::Impl::parseTargetFlags(std::uint32_t &Flags) { - Flags = 0; - if (!atKeyword("target-flags")) - return false; - advance(); - if (expect(MITokenKind::LeftParen, "'(' after target-flags")) - return true; - if (!at(MITokenKind::Identifier)) - return fail("expected a target flag name"); - - const std::string First(token().stringValue()); - if (const auto Direct = Target.mirText().directOperandFlag(First)) - Flags = *Direct; - else if (const auto Bit = Target.mirText().bitmaskOperandFlag(First)) - Flags = *Bit; - else - return fail("unknown target operand flag '" + First + "'"); - advance(); - - std::unordered_set Seen{First}; - while (consume(MITokenKind::Comma)) { - if (!at(MITokenKind::Identifier)) - return fail("expected a target bit flag name"); - const std::string Name(token().stringValue()); - const auto Bit = Target.mirText().bitmaskOperandFlag(Name); - if (!Bit) - return fail("unknown target bit operand flag '" + Name + "'"); - if (!Seen.insert(Name).second || (Flags & *Bit) == *Bit) - return fail("duplicate target operand flag '" + Name + "'"); - Flags |= *Bit; - advance(); - } - if (expect(MITokenKind::RightParen, "')' after target flags")) - return true; - const auto Valid = Target.mirText().validateOperandFlags(Flags); - return !Valid && importError(token(), Valid.error()); -} - -bool MIParser::Impl::parseTypedIntegerOperand(ParsedOperand &Result) { - const MIToken Begin = token(); - const std::string_view TypeSpelling = Begin.stringValue(); - if (!TypeSpelling.starts_with('i')) - return fail(Begin, "typed integer operand must use an 'iN' type"); - std::uint64_t WideBitWidth = 0; - if (!parseUnsignedDigits(TypeSpelling.substr(1), WideBitWidth) || - WideBitWidth > IntegerImmediate::MaxBitWidth) { - return fail(Begin, "typed integer operand has an invalid bit width"); - } - const auto BitWidth = static_cast(WideBitWidth); - advance(); - - std::string_view ValueSpelling; - if (at(MITokenKind::IntegerLiteral)) { - ValueSpelling = token().integerValue(); - } else if (at(MITokenKind::Identifier) && token().stringValue() == "true") { - ValueSpelling = "1"; - } else if (at(MITokenKind::Identifier) && token().stringValue() == "false") { - ValueSpelling = "0"; - } else { - return fail("expected a decimal integer, true, or false after the typed integer type"); - } - - auto Value = parseIntegerImmediateValue(BitWidth, ValueSpelling); - if (!Value) - return importError(token(), Value.error()); - advance(); - Result = ParsedOperand{ - MachineOperand::integerImmediate(std::move(*Value)), - std::nullopt, - {Begin.range().Begin, token().range().Begin}, - }; - return false; -} - -bool MIParser::Impl::parseFloatingImmediateOperand(ParsedOperand &Result) { - const MIToken Begin = token(); - const auto Semantics = floatSemanticsForKeyword(Begin.stringValue()); - if (!Semantics) - return fail(Begin, "unknown floating-point immediate type"); - advance(); - if (!at(MITokenKind::FloatingPointLiteral) && !at(MITokenKind::HexLiteral)) - return fail("expected a floating-point literal"); - - const MIToken Literal = token(); - std::string ConstantText(Begin.stringValue()); - ConstantText.push_back(' '); - ConstantText.append(Literal.spelling()); - auto Constant = parseLLVMConstant(ConstantText); - if (!Constant) - return fail(Literal, std::string(Constant.error().message())); - if (!Constant->FloatingValue || Constant->FloatingValue->semantics() != *Semantics) - return fail(Literal, "floating-point literal produced the wrong semantics"); - advance(); - Result = ParsedOperand{ - MachineOperand::floatingImmediate(*Constant->FloatingValue), - std::nullopt, - {Begin.range().Begin, token().range().Begin}, - }; - return false; -} - -bool MIParser::Impl::parseMetadataReference(MachineMetadataReference &Result) { - const MIToken Begin = token(); - if (expect(MITokenKind::Exclaim, "'!' before a metadata slot")) - return true; - std::uint32_t TextID = 0; - if (!at(MITokenKind::IntegerLiteral) || parseUnsigned32(token(), TextID)) - return fail("expected a 32-bit metadata slot number"); - - for (const MIMetadataSlot &Slot : Context.Metadata) { - if (Slot.TextID != TextID) - continue; - if (!Slot.Reference.isValid()) - return fail(Begin, "metadata slot maps to an invalid reference"); - Result = Slot.Reference; - advance(); - return false; - } - if (Function.metadata().node(TextID) != nullptr) { - Result = MachineMetadataReference::machineFunction(TextID); - advance(); - return false; - } - return fail(Begin, "use of undefined metadata slot !" + std::to_string(TextID)); -} - -bool MIParser::Impl::parseMetadataOperand(ParsedOperand &Result) { - const MIToken Begin = token(); - MachineMetadataValue Value; - if (at(MITokenKind::Exclaim)) { - MachineMetadataReference Reference; - if (parseMetadataReference(Reference)) - return true; - auto Created = MachineMetadataValue::reference(Reference); - if (!Created) - return importError(Begin, Created.error()); - Value = std::move(*Created); - } else if (at(MITokenKind::MetadataKeyword) && token().isMetadataKeyword("!DIExpression")) { - advance(); - if (expect(MITokenKind::LeftParen, "'(' after !DIExpression")) - return true; - std::vector Elements; - while (!at(MITokenKind::RightParen)) { - if (at(MITokenKind::Identifier)) { - const auto Opcode = parseDIExpressionOpcodeName(token().stringValue()); - const auto Encoding = parseDIExpressionAttributeEncodingName(token().stringValue()); - if (!Opcode && !Encoding) - return fail(invalidDIExpressionElementMessage(token().stringValue())); - Elements.push_back(Opcode ? *Opcode : *Encoding); - advance(); - } else if (at(MITokenKind::IntegerLiteral)) { - std::int64_t Signed = 0; - if (parseSigned64(token(), Signed)) - return fail("debug expression element exceeds 64 bits"); - Elements.push_back(std::bit_cast(Signed)); - advance(); - } else { - return fail("expected a debug expression opcode or integer"); - } - if (!consume(MITokenKind::Comma)) - break; - } - if (expect(MITokenKind::RightParen, "')' after !DIExpression")) - return true; - Value = MachineMetadataValue::expression(MachineDIExpression(std::move(Elements))); - } else { - return fail("expected a metadata reference or !DIExpression"); - } - - auto Operand = MachineOperand::metadata(std::move(Value)); - if (!Operand) - return importError(Begin, Operand.error()); - Result = ParsedOperand{ - std::move(*Operand), - std::nullopt, - {Begin.range().Begin, token().range().Begin}, - }; - return false; -} - -bool MIParser::Impl::parseInlineDILocation( - std::vector &InlineChain, - std::optional &TerminalInlinedAt) { - if (!at(MITokenKind::MetadataKeyword) || !token().isMetadataKeyword("!DILocation")) { - return fail("expected an inline !DILocation"); - } - advance(); - if (expect(MITokenKind::LeftParen, "'(' after !DILocation")) - return true; - - MachineDILocationFrame Frame; - bool HasLine = false; - bool HasScope = false; - std::vector NestedChain; - std::optional NestedTerminal; - std::unordered_set SeenFields; - while (!at(MITokenKind::RightParen)) { - if (!at(MITokenKind::Identifier)) - return fail("expected a DILocation field name"); - const MIToken FieldToken = token(); - const std::string Field(token().stringValue()); - if (!SeenFields.insert(Field).second) - return fail("duplicate DILocation field '" + Field + "'"); - advance(); - if (expect(MITokenKind::Colon, "':' after a DILocation field name")) - return true; - - if (Field == "line") { - std::uint32_t Line = 0; - if (!at(MITokenKind::IntegerLiteral) || parseUnsigned32(token(), Line)) - return fail("expected an unsigned 32-bit DILocation line"); - Frame.Line = Line; - HasLine = true; - advance(); - } else if (Field == "column") { - std::uint64_t Column = 0; - if (!at(MITokenKind::IntegerLiteral) || parseUnsigned(token(), Column) || - Column > std::numeric_limits::max()) { - return fail("expected an unsigned 16-bit DILocation column"); - } - Frame.Column = static_cast(Column); - advance(); - } else if (Field == "scope") { - MachineMetadataReference Scope; - if (parseMetadataReference(Scope)) - return true; - if (Scope.scope() != MachineMetadataScope::EmbeddedIR) - return fail(FieldToken, "DILocation scope must reference embedded IR metadata"); - Frame.Scope = Scope; - HasScope = true; - } else if (Field == "inlinedAt") { - if (at(MITokenKind::MetadataKeyword) && token().isMetadataKeyword("!DILocation")) { - if (parseInlineDILocation(NestedChain, NestedTerminal)) - return true; - } else { - MachineMetadataReference Reference; - if (parseMetadataReference(Reference)) - return true; - if (Reference.scope() != MachineMetadataScope::EmbeddedIR) { - return fail(FieldToken, "DILocation inlinedAt must reference embedded IR metadata"); - } - NestedTerminal = Reference; - } - } else if (Field == "isImplicitCode") { - if (!at(MITokenKind::Identifier) || - (token().stringValue() != "true" && token().stringValue() != "false")) { - return fail("expected 'true' or 'false' for DILocation isImplicitCode"); - } - Frame.IsImplicitCode = token().stringValue() == "true"; - advance(); - } else if (Field == "atomGroup") { - std::uint64_t Group = 0; - if (!at(MITokenKind::IntegerLiteral) || parseUnsigned(token(), Group) || - Group > MachineDILocation::MaxAtomGroup) { - return fail("DILocation atomGroup exceeds 61 bits"); - } - Frame.AtomGroup = Group; - advance(); - } else if (Field == "atomRank") { - std::uint64_t Rank = 0; - if (!at(MITokenKind::IntegerLiteral) || parseUnsigned(token(), Rank) || Rank > 7) { - return fail("DILocation atomRank exceeds three bits"); - } - Frame.AtomRank = static_cast(Rank); - advance(); - } else { - return fail(FieldToken, "unknown DILocation field '" + Field + "'"); - } - - if (!consume(MITokenKind::Comma)) - break; - } - if (expect(MITokenKind::RightParen, "')' after !DILocation")) - return true; - if (!HasLine) - return fail("DILocation requires a line field"); - if (!HasScope) - return fail("DILocation requires a scope field"); - - InlineChain.push_back(Frame); - InlineChain.insert(InlineChain.end(), NestedChain.begin(), NestedChain.end()); - TerminalInlinedAt = NestedTerminal; - return false; -} - -bool MIParser::Impl::parseCFIRegister(DwarfRegisterNumber &Result) { - if (!at(MITokenKind::NamedRegister)) - return fail("expected a physical register in the CFI instruction"); - const MIToken RegisterToken = token(); - Register Reg; - if (parseRegister(Reg) || !Reg.isPhysical()) - return true; - const auto Physical = Reg.physicalRegister(); - if (!Physical) - return fail(RegisterToken, "CFI register is not a physical register"); - const auto Dwarf = Target.mirText().dwarfRegister(*Physical); - if (!Dwarf) - return fail(RegisterToken, "physical register has no canonical DWARF register number"); - Result = DwarfRegisterNumber::fromValue(*Dwarf); - return false; -} - -bool MIParser::Impl::parseCFISigned(std::int64_t &Result) { - if (!at(MITokenKind::IntegerLiteral) || parseSigned64(token(), Result)) - return fail("expected a signed 64-bit CFI integer"); - advance(); - return false; -} - -bool MIParser::Impl::parseCFIUnsigned(std::uint32_t &Result) { - if (!at(MITokenKind::IntegerLiteral) || parseUnsigned32(token(), Result)) - return fail("expected an unsigned 32-bit CFI integer"); - advance(); - return false; -} - -bool MIParser::Impl::parseCFIOperand(ParsedOperand &Result) { - const MIToken Begin = token(); - const std::string Operation(token().stringValue()); - advance(); - - const auto Comma = [this]() { return expect(MITokenKind::Comma, "',' in CFI instruction"); }; - DwarfRegisterNumber Register; - DwarfRegisterNumber OtherRegister; - std::int64_t Offset = 0; - std::optional Instruction; - if (Operation == "same_value") { - if (parseCFIRegister(Register)) - return true; - Instruction = MachineCFIInstruction::sameValue(Register); - } else if (Operation == "remember_state") { - Instruction = MachineCFIInstruction::rememberState(); - } else if (Operation == "restore_state") { - Instruction = MachineCFIInstruction::restoreState(); - } else if (Operation == "offset" || Operation == "rel_offset") { - if (parseCFIRegister(Register) || Comma() || parseCFISigned(Offset)) - return true; - Instruction = Operation == "offset" ? MachineCFIInstruction::offset(Register, Offset) - : MachineCFIInstruction::relativeOffset(Register, Offset); - } else if (Operation == "def_cfa_register") { - if (parseCFIRegister(Register)) - return true; - Instruction = MachineCFIInstruction::defCFARegister(Register); - } else if (Operation == "def_cfa_offset" || Operation == "adjust_cfa_offset") { - if (parseCFISigned(Offset)) - return true; - Instruction = Operation == "def_cfa_offset" ? MachineCFIInstruction::defCFAOffset(Offset) - : MachineCFIInstruction::adjustCFAOffset(Offset); - } else if (Operation == "def_cfa") { - if (parseCFIRegister(Register) || Comma() || parseCFISigned(Offset)) - return true; - Instruction = MachineCFIInstruction::defCFA(Register, Offset); - } else if (Operation == "llvm_def_aspace_cfa") { - std::uint32_t AddressSpace = 0; - if (parseCFIRegister(Register) || Comma() || parseCFISigned(Offset) || Comma() || - parseCFIUnsigned(AddressSpace)) { - return true; - } - Instruction = MachineCFIInstruction::llvmDefAddressSpaceCFA(Register, Offset, AddressSpace); - } else if (Operation == "restore" || Operation == "undefined") { - if (parseCFIRegister(Register)) - return true; - Instruction = Operation == "restore" ? MachineCFIInstruction::restore(Register) - : MachineCFIInstruction::undefined(Register); - } else if (Operation == "register") { - if (parseCFIRegister(Register) || Comma() || parseCFIRegister(OtherRegister)) - return true; - Instruction = MachineCFIInstruction::registerRule(Register, OtherRegister); - } else if (Operation == "window_save") { - Instruction = MachineCFIInstruction::windowSave(); - } else if (Operation == "negate_ra_sign_state") { - Instruction = MachineCFIInstruction::negateReturnAddressState(); - } else if (Operation == "negate_ra_sign_state_with_pc") { - Instruction = MachineCFIInstruction::negateReturnAddressStateWithPC(); - } else if (Operation == "llvm_register_pair") { - DwarfRegisterNumber First; - DwarfRegisterNumber Second; - std::uint32_t FirstSize = 0; - std::uint32_t SecondSize = 0; - if (parseCFIRegister(Register) || Comma() || parseCFIRegister(First) || Comma() || - parseCFIUnsigned(FirstSize) || Comma() || parseCFIRegister(Second) || Comma() || - parseCFIUnsigned(SecondSize)) { - return true; - } - Instruction = MachineCFIInstruction::llvmRegisterPair({.Register = Register, - .FirstRegister = First, - .FirstSizeInBits = FirstSize, - .SecondRegister = Second, - .SecondSizeInBits = SecondSize}); - } else if (Operation == "llvm_vector_registers") { - if (parseCFIRegister(Register) || Comma()) - return true; - std::vector Registers; - do { - DwarfRegisterNumber VectorRegister; - std::uint32_t Lane = 0; - std::uint32_t Size = 0; - if (parseCFIRegister(VectorRegister) || Comma() || parseCFIUnsigned(Lane) || Comma() || - parseCFIUnsigned(Size)) { - return true; - } - Registers.push_back({.Register = VectorRegister, .Lane = Lane, .SizeInBits = Size}); - if (!at(MITokenKind::Comma)) - break; - advance(); - if (!at(MITokenKind::NamedRegister)) - return fail("expected another vector CFI register tuple"); - } while (true); - auto Created = MachineCFIInstruction::llvmVectorRegisters( - {.Register = Register, .VectorRegisters = std::move(Registers)}); - if (!Created) - return importError(Begin, Created.error()); - Instruction = std::move(*Created); - } else if (Operation == "llvm_vector_offset") { - DwarfRegisterNumber MaskRegister; - std::uint32_t RegisterSize = 0; - std::uint32_t MaskSize = 0; - if (parseCFIRegister(Register) || Comma() || parseCFIUnsigned(RegisterSize) || Comma() || - parseCFIRegister(MaskRegister) || Comma() || parseCFIUnsigned(MaskSize) || Comma() || - parseCFISigned(Offset)) { - return true; - } - Instruction = MachineCFIInstruction::llvmVectorOffset({.Register = Register, - .RegisterSizeInBits = RegisterSize, - .MaskRegister = MaskRegister, - .MaskRegisterSizeInBits = MaskSize, - .Offset = Offset}); - } else if (Operation == "llvm_vector_register_mask") { - DwarfRegisterNumber SpillRegister; - DwarfRegisterNumber MaskRegister; - std::uint32_t SpillLaneSize = 0; - std::uint32_t MaskSize = 0; - if (parseCFIRegister(Register) || Comma() || parseCFIRegister(SpillRegister) || Comma() || - parseCFIUnsigned(SpillLaneSize) || Comma() || parseCFIRegister(MaskRegister) || Comma() || - parseCFIUnsigned(MaskSize)) { - return true; - } - Instruction = - MachineCFIInstruction::llvmVectorRegisterMask({.Register = Register, - .SpillRegister = SpillRegister, - .SpillRegisterLaneSizeInBits = SpillLaneSize, - .MaskRegister = MaskRegister, - .MaskRegisterSizeInBits = MaskSize}); - } else if (Operation == "escape") { - std::vector Bytes; - while (true) { - std::uint64_t Byte = 0; - if (!at(MITokenKind::HexLiteral) || parseUnsigned(token(), Byte) || - Byte > std::numeric_limits::max()) { - return fail("CFI escape element must be an 8-bit hexadecimal literal"); - } - Bytes.push_back(static_cast(Byte)); - advance(); - if (!at(MITokenKind::Comma) || !nextIs(MITokenKind::HexLiteral)) - break; - advance(); - } - auto Created = MachineCFIInstruction::escape(std::move(Bytes)); - if (!Created) - return importError(Begin, Created.error()); - Instruction = std::move(*Created); - } else { - return fail(Begin, "unsupported CFI operation '" + Operation + "'"); - } - - auto Index = Function.cfiInfo().addInstruction(std::move(*Instruction)); - if (!Index) - return importError(Begin, Index.error()); - Result = ParsedOperand{ - MachineOperand::cfiIndex(*Index), - std::nullopt, - {Begin.range().Begin, token().range().Begin}, - }; - return false; -} - -bool MIParser::Impl::parseMemoryBase(MemoryBase &Result, std::int64_t &Offset) { - const MIToken BaseToken = token(); - switch (BaseToken.kind()) { - case MITokenKind::StackObject: - case MITokenKind::FixedStackObject: { - const auto Index = frameIndexFor(BaseToken, BaseToken.kind() == MITokenKind::FixedStackObject); - if (!Index) - return true; - Result = MemoryBase::frameIndex(*Index); - advance(); - break; - } - case MITokenKind::ConstantPoolItem: { - const auto Index = constantPoolIndexFor(BaseToken); - if (!Index) - return true; - Result = MemoryBase::constantPool(*Index); - advance(); - break; - } - case MITokenKind::JumpTableIndex: { - const auto Index = jumpTableIndexFor(BaseToken); - if (!Index) - return true; - Result = MemoryBase::jumpTable(*Index); - advance(); - break; - } - case MITokenKind::GlobalValue: - case MITokenKind::NamedGlobalValue: - Result = MemoryBase::globalSymbol(symbolName(BaseToken)); - advance(); - break; - case MITokenKind::ExternalSymbol: - Result = MemoryBase::externalSymbol(symbolName(BaseToken)); - advance(); - break; - case MITokenKind::IRValue: - case MITokenKind::NamedIRValue: - Result = MemoryBase::irValue(symbolName(BaseToken)); - advance(); - break; - case MITokenKind::QuotedIRValue: { - auto Constant = parseLLVMConstant(BaseToken.stringValue()); - if (!Constant) - return importError(BaseToken, Constant.error()); - auto Base = MemoryBase::irConstant(std::move(*Constant)); - if (!Base) - return importError(BaseToken, Base.error()); - Result = std::move(*Base); - advance(); - break; - } - case MITokenKind::Keyword: - if (BaseToken.isKeyword("unknown-address")) { - Result = MemoryBase(); - advance(); - } else if (BaseToken.isKeyword("stack")) { - Result = MemoryBase::stack(); - advance(); - } else if (BaseToken.isKeyword("got")) { - Result = MemoryBase::got(); - advance(); - } else if (BaseToken.isKeyword("constant-pool")) { - Result = MemoryBase::constantPoolPseudo(); - advance(); - } else if (BaseToken.isKeyword("jump-table")) { - Result = MemoryBase::jumpTablePseudo(); - advance(); - } else if (BaseToken.isKeyword("call-entry")) { - advance(); - if (at(MITokenKind::GlobalValue) || at(MITokenKind::NamedGlobalValue)) { - Result = MemoryBase::callEntryGlobal(symbolName(token())); - } else if (at(MITokenKind::ExternalSymbol)) { - Result = MemoryBase::callEntryExternal(symbolName(token())); - } else { - return fail("call-entry memory base requires a global or external symbol"); - } - advance(); - } else if (BaseToken.isKeyword("custom")) { - advance(); - if (!at(MITokenKind::Identifier) && !at(MITokenKind::StringConstant)) - return fail("custom memory base requires a target spelling"); - Result = MemoryBase::custom(std::string(token().stringValue())); - advance(); - } else { - return fail(BaseToken, "unsupported machine memory base"); - } - break; - default: - return fail(BaseToken, "expected a machine memory base"); - } - return parseOptionalOffset(Offset); -} - -bool MIParser::Impl::parseMemoryOperand(const MachineMemOperand *&Result, - Opcode InstructionOpcode) { - const MIToken Begin = token(); - if (expect(MITokenKind::LeftParen, "'(' before a machine memory operand")) - return true; - - MachineMemOperandDesc Description; - std::unordered_set SeenFlags; - while (token().isMemoryOperandFlag()) { - const std::string Name(token().stringValue()); - if (!SeenFlags.insert(Name).second) - return fail("duplicate machine memory operand flag '" + Name + "'"); - if (atKeyword("volatile")) - Description.Accesses.set(MemoryAccessFlag::Volatile); - else if (atKeyword("non-temporal")) - Description.Accesses.set(MemoryAccessFlag::NonTemporal); - else if (atKeyword("dereferenceable")) - Description.Accesses.set(MemoryAccessFlag::Dereferenceable); - else if (atKeyword("invariant")) - Description.Accesses.set(MemoryAccessFlag::Invariant); - else { - const auto Flag = Target.mirText().machineMemOperandFlag(token().stringValue()); - if (!Flag) - return fail("unknown target machine memory operand flag '" + Name + "'"); - if ((Description.TargetFlags & *Flag) != 0) - return fail("overlapping target machine memory operand flag '" + Name + "'"); - Description.TargetFlags |= *Flag; - } - advance(); - } - - if (!at(MITokenKind::Identifier) || - (token().stringValue() != "load" && token().stringValue() != "store")) { - return fail("expected load or store in a machine memory operand"); - } - if (token().stringValue() == "load") - Description.Accesses.set(MemoryAccessFlag::Load); - else - Description.Accesses.set(MemoryAccessFlag::Store); - advance(); - if (at(MITokenKind::Identifier) && token().stringValue() == "store") { - if (Description.Accesses.has(MemoryAccessFlag::Store)) - return fail("duplicate store memory operation"); - Description.Accesses.set(MemoryAccessFlag::Store); - advance(); - } - - if (at(MITokenKind::Identifier) && token().stringValue() == "syncscope") { - advance(); - if (expect(MITokenKind::LeftParen, "'(' after syncscope")) - return true; - if (!at(MITokenKind::StringConstant)) - return fail("syncscope requires a quoted scope name"); - const std::string_view Name = token().stringValue(); - auto Scope = std::ranges::find_if( - Context.SyncScopes, [Name](const MISyncScopeSlot &Slot) { return Slot.Name == Name; }); - if (Scope == Context.SyncScopes.end() && Context.InternSyncScope) { - const MIToken NameToken = token(); - auto ID = Context.InternSyncScope(Name); - if (!ID) - return importError(NameToken, ID.error()); - Context.SyncScopes.push_back({.Name = std::string(Name), .ID = *ID}); - Scope = std::prev(Context.SyncScopes.end()); - } - if (Scope == Context.SyncScopes.end()) - return fail("unknown synchronization scope '" + std::string(Name) + "'"); - Description.SyncScope = Scope->ID; - advance(); - if (expect(MITokenKind::RightParen, "')' after syncscope")) - return true; - } - - std::size_t OrderingCount = 0; - while (at(MITokenKind::Identifier)) { - const auto Ordering = atomicOrdering(token().stringValue()); - if (!Ordering) - break; - if (OrderingCount == 0) - Description.SuccessOrdering = *Ordering; - else if (OrderingCount == 1) - Description.FailureOrdering = *Ordering; - else - return fail("machine memory operand has more than two atomic orderings"); - ++OrderingCount; - advance(); - } - const bool IsLoad = Description.Accesses.has(MemoryAccessFlag::Load); - const bool IsStore = Description.Accesses.has(MemoryAccessFlag::Store); - if (OrderingCount == 1) { - if (InstructionOpcode == opcode(GenericOpcode::G_ATOMIC_CMPXCHG) || - InstructionOpcode == opcode(GenericOpcode::G_ATOMIC_CMPXCHG_WITH_SUCCESS)) { - Description.AtomicKind = AtomicOperation::CompareExchange; - } else { - Description.AtomicKind = IsLoad && IsStore - ? AtomicOperation::ReadModifyWrite - : (IsLoad ? AtomicOperation::Load : AtomicOperation::Store); - } - } else if (OrderingCount == 2) - Description.AtomicKind = AtomicOperation::CompareExchange; - - std::uint64_t MinimumBytes = 0; - if (at(MITokenKind::IntegerLiteral)) { - if (parseUnsigned(token(), MinimumBytes) || MinimumBytes == 0) - return fail("machine memory operand size must be a nonzero byte count"); - auto Size = MemorySize::fixed(MinimumBytes); - if (!Size) - return importError(token(), Size.error()); - Description.Size = *Size; - advance(); - } else if (atKeyword("unknown-size")) { - advance(); - } else if (consume(MITokenKind::LeftParen)) { - LLT Type; - if (parseLowLevelType(Type)) - return true; - if (expect(MITokenKind::RightParen, "')' after the memory low-level type")) - return true; - const auto MinimumBits = minimumTypeSizeInBits(Type); - if (!MinimumBits || *MinimumBits == 0) - return fail(Begin, "memory low-level type has no representable size"); - MinimumBytes = (*MinimumBits + 7U) / 8U; - auto Size = Type.isScalableVector() ? MemorySize::scalable(MinimumBytes) - : MemorySize::fixed(MinimumBytes); - if (!Size) - return importError(Begin, Size.error()); - Description.Size = *Size; - Description.MemoryType = Type; - } else { - return fail("expected a memory size, unknown-size, or memory low-level type"); - } - - if (MinimumBytes != 0) { - if (MinimumBytes > (std::uint64_t{1} << 63U)) - return fail(Begin, "machine memory operand size has no representable default alignment"); - auto DefaultAlignment = Alignment::create(std::bit_ceil(MinimumBytes)); - if (!DefaultAlignment) - return importError(Begin, DefaultAlignment.error()); - Description.Align = *DefaultAlignment; - } - - if (at(MITokenKind::Identifier)) { - const std::string_view ExpectedPreposition = - IsLoad && IsStore ? "on" : (IsLoad ? "from" : "into"); - if (token().stringValue() != ExpectedPreposition) { - return fail("expected '" + std::string(ExpectedPreposition) + - "' before the machine memory base"); - } - advance(); - if (parseMemoryBase(Description.Base, Description.Offset)) - return true; - if (const GenericConstantData *Constant = Description.Base.constantValue()) - Description.AddressSpace = Constant->Type.AddressSpace; - } - - Alignment BaseAlignment = Description.Align; - std::unordered_set SeenOptions; - while (consume(MITokenKind::Comma)) { - const std::string Name(token().spelling()); - if (!SeenOptions.insert(Name).second) - return fail("duplicate machine memory operand option '" + Name + "'"); - if (atKeyword("align")) { - Alignment ParsedAlignment; - if (parseAlignment(ParsedAlignment)) - return true; - if ((static_cast(Description.Offset) & (ParsedAlignment.bytes() - 1U)) != 0) { - return fail("specified alignment is more aligned than offset"); - } - BaseAlignment = ParsedAlignment; - } else if (atKeyword("basealign")) { - if (parseAlignment(BaseAlignment)) - return true; - } else if (atKeyword("addrspace")) { - advance(); - if (!at(MITokenKind::IntegerLiteral) || parseUnsigned32(token(), Description.AddressSpace) || - Description.AddressSpace > LLT::MaxAddressSpace) { - return fail("invalid machine memory operand address space"); - } - advance(); - } else if (at(MITokenKind::MetadataKeyword)) { - const std::string MetadataKind(token().stringValue()); - advance(); - MachineMetadataReference Reference; - if (parseMetadataReference(Reference)) - return true; - if (MetadataKind == "!tbaa") - Description.TBAA = Reference; - else if (MetadataKind == "!alias.scope") - Description.AliasScope = Reference; - else if (MetadataKind == "!noalias") - Description.NoAlias = Reference; - else if (MetadataKind == "!noalias.addrspace") - Description.NoAliasAddressSpace = Reference; - else if (MetadataKind == "!range") - Description.RangeMetadata = Reference; - else - return fail("unsupported machine memory operand metadata attachment"); - } else { - return fail("unknown machine memory operand option '" + Name + "'"); - } - } - - std::uint64_t EffectiveAlignment = BaseAlignment.bytes(); - if (Description.Offset != 0) { - const auto OffsetBits = static_cast(Description.Offset); - const std::uint64_t OffsetAlignment = std::uint64_t{1} << std::countr_zero(OffsetBits); - EffectiveAlignment = std::min(EffectiveAlignment, OffsetAlignment); - } - Description.Align = *Alignment::create(EffectiveAlignment); - if (BaseAlignment != Description.Align) - Description.BaseAlign = BaseAlignment; - else - Description.BaseAlign.reset(); - - if (expect(MITokenKind::RightParen, "')' after the machine memory operand")) - return true; - - auto Created = Function.createMemOperand(std::move(Description)); - if (!Created) - return importError(Begin, Created.error()); - Result = *Created; - return false; -} - -bool MIParser::Impl::parsePredicateOperand(ParsedOperand &Result, bool IsFloating) { - const MIToken Begin = token(); - advance(); - if (expect(MITokenKind::LeftParen, "'(' after predicate kind")) - return true; - if (!at(MITokenKind::Identifier)) - return fail("expected a predicate spelling"); - - Expected Operand = IsFloating ? [&]() -> Expected { - const auto Predicate = floatingPredicate(token().stringValue()); - if (!Predicate) { - return makeUnexpected(ErrorCode::ParseError, "invalid floating-point predicate"); - } - return MachineOperand::floatingPredicate(*Predicate); - }() - : [&]() -> Expected { - const auto Predicate = integerPredicate(token().stringValue()); - if (!Predicate) { - return makeUnexpected(ErrorCode::ParseError, "invalid integer predicate"); - } - return MachineOperand::integerPredicate(*Predicate); - }(); - if (!Operand) - return importError(token(), Operand.error()); - advance(); - if (expect(MITokenKind::RightParen, "')' after predicate")) - return true; - Result = ParsedOperand{ - std::move(*Operand), std::nullopt, {Begin.range().Begin, token().range().Begin}}; - return false; -} - -bool MIParser::Impl::parseShuffleMaskOperand(ParsedOperand &Result) { - const MIToken Begin = token(); - advance(); - if (expect(MITokenKind::LeftParen, "'(' after shufflemask")) - return true; - std::vector Elements; - while (!at(MITokenKind::RightParen)) { - if (atKeyword("undef")) { - Elements.push_back(ShuffleMaskOperand::Undef); - advance(); - } else { - std::int64_t Value = 0; - if (!at(MITokenKind::IntegerLiteral) || parseSigned64(token(), Value) || - Value < std::numeric_limits::min() || - Value > std::numeric_limits::max()) { - return fail("shuffle mask element must be a 32-bit integer or undef"); - } - Elements.push_back(static_cast(Value)); - advance(); - } - if (!consume(MITokenKind::Comma)) - break; - } - if (expect(MITokenKind::RightParen, "')' after shufflemask")) - return true; - auto Operand = MachineOperand::shuffleMask(std::move(Elements)); - if (!Operand) - return importError(Begin, Operand.error()); - Result = ParsedOperand{ - std::move(*Operand), std::nullopt, {Begin.range().Begin, token().range().Begin}}; - return false; -} - -bool MIParser::Impl::parseDebugInstrRefOperand(ParsedOperand &Result) { - const MIToken Begin = token(); - advance(); - if (expect(MITokenKind::LeftParen, "'(' after dbg-instr-ref")) - return true; - std::uint64_t Instruction = 0; - if (!at(MITokenKind::IntegerLiteral) || parseUnsigned(token(), Instruction)) - return fail("invalid debug instruction number"); - advance(); - if (expect(MITokenKind::Comma, "',' in dbg-instr-ref")) - return true; - std::uint64_t OperandNumber = 0; - if (!at(MITokenKind::IntegerLiteral) || parseUnsigned(token(), OperandNumber)) - return fail("invalid debug operand number"); - advance(); - if (expect(MITokenKind::RightParen, "')' after dbg-instr-ref")) - return true; - auto Operand = MachineOperand::debugInstrRef(Instruction, OperandNumber); - if (!Operand) - return importError(Begin, Operand.error()); - Result = ParsedOperand{ - std::move(*Operand), std::nullopt, {Begin.range().Begin, token().range().Begin}}; - return false; -} - -bool MIParser::Impl::parseLaneMaskOperand(ParsedOperand &Result) { - const MIToken Begin = token(); - advance(); - if (expect(MITokenKind::LeftParen, "'(' after lanemask")) - return true; - std::uint64_t Value = 0; - if (parseUnsigned(token(), Value)) - return fail("invalid 64-bit lane mask"); - advance(); - if (expect(MITokenKind::RightParen, "')' after lanemask")) - return true; - Result = ParsedOperand{MachineOperand::laneMask(LaneMask::fromRaw(Value)), - std::nullopt, - {Begin.range().Begin, token().range().Begin}}; - return false; -} - -bool MIParser::Impl::parseIntrinsicOperand(ParsedOperand &Result) { - const MIToken Begin = token(); - advance(); - if (expect(MITokenKind::LeftParen, "'(' after intrinsic")) - return true; - if (!at(MITokenKind::NamedGlobalValue)) - return fail("expected a named intrinsic"); - const auto ID = Target.mirText().intrinsic(token().stringValue()); - if (!ID) - return fail("unknown intrinsic '" + std::string(token().stringValue()) + "'"); - advance(); - if (expect(MITokenKind::RightParen, "')' after intrinsic")) - return true; - auto Operand = MachineOperand::intrinsic(*ID); - if (!Operand) - return importError(Begin, Operand.error()); - Result = ParsedOperand{ - std::move(*Operand), std::nullopt, {Begin.range().Begin, token().range().Begin}}; - return false; -} - -bool MIParser::Impl::parseTargetIndexOperand(ParsedOperand &Result) { - const MIToken Begin = token(); - advance(); - if (expect(MITokenKind::LeftParen, "'(' after target-index")) - return true; - if (!at(MITokenKind::Identifier)) - return fail("expected a target index name"); - const auto Index = Target.mirText().targetIndex(token().stringValue()); - if (!Index) - return fail("unknown target index '" + std::string(token().stringValue()) + "'"); - advance(); - if (expect(MITokenKind::RightParen, "')' after target index")) - return true; - std::int64_t Offset = 0; - if (parseOptionalOffset(Offset)) - return true; - Result = ParsedOperand{MachineOperand::targetIndex(*Index, Offset), - std::nullopt, - {Begin.range().Begin, token().range().Begin}}; - return false; -} - -bool MIParser::Impl::parseCustomMaskOperand(ParsedOperand &Result) { - const MIToken Begin = token(); - advance(); - if (expect(MITokenKind::LeftParen, "'(' after CustomRegMask")) - return true; - std::uint32_t MaximumRegister = 0; - for (const PhysicalRegisterDesc &Register : Target.registers().physicalRegisters()) - MaximumRegister = std::max(MaximumRegister, Register.Reg.number()); - std::vector Words(MaximumRegister / 64U + 1U, 0); - if (!at(MITokenKind::RightParen)) { - do { - Register Reg; - if (!at(MITokenKind::NamedRegister) || parseRegister(Reg) || !Reg.isPhysical()) - return fail("CustomRegMask requires physical registers"); - const auto Physical = Reg.physicalRegister(); - if (!Physical) - return fail("CustomRegMask requires physical registers"); - const std::uint32_t Number = Physical->number(); - Words[Number / 64U] |= std::uint64_t{1} << (Number % 64U); - } while (consume(MITokenKind::Comma)); - } - if (expect(MITokenKind::RightParen, "')' after CustomRegMask")) - return true; - auto Operand = - MachineOperand::registerMask(std::make_shared(std::move(Words))); - if (!Operand) - return importError(Begin, Operand.error()); - Result = ParsedOperand{ - std::move(*Operand), std::nullopt, {Begin.range().Begin, token().range().Begin}}; - return false; -} - -bool MIParser::Impl::parseLiveOutMaskOperand(ParsedOperand &Result) { - const MIToken Begin = token(); - advance(); - if (expect(MITokenKind::LeftParen, "'(' after liveout")) - return true; - std::uint32_t MaximumRegister = 0; - for (const PhysicalRegisterDesc &Register : Target.registers().physicalRegisters()) - MaximumRegister = std::max(MaximumRegister, Register.Reg.number()); - std::vector Words(MaximumRegister / 64U + 1U, 0); - do { - Register Reg; - if (!at(MITokenKind::NamedRegister) || parseRegister(Reg) || !Reg.isPhysical()) - return fail("liveout requires physical registers"); - const auto Physical = Reg.physicalRegister(); - if (!Physical) - return fail("liveout requires physical registers"); - const std::uint32_t Number = Physical->number(); - Words[Number / 64U] |= std::uint64_t{1} << (Number % 64U); - } while (consume(MITokenKind::Comma)); - if (expect(MITokenKind::RightParen, "')' after liveout")) - return true; - auto Operand = - MachineOperand::liveOutRegisterMask(std::make_shared(std::move(Words))); - if (!Operand) - return importError(Begin, Operand.error()); - Result = ParsedOperand{ - std::move(*Operand), std::nullopt, {Begin.range().Begin, token().range().Begin}}; - return false; -} - -bool MIParser::Impl::parseBlockAddressOperand(ParsedOperand &Result) { - const MIToken Begin = token(); - advance(); - if (expect(MITokenKind::LeftParen, "'(' after blockaddress")) - return true; - if (!at(MITokenKind::GlobalValue) && !at(MITokenKind::NamedGlobalValue)) - return fail("blockaddress requires a function reference"); - const std::string FunctionName = symbolName(token()); - advance(); - if (expect(MITokenKind::Comma, "',' in blockaddress")) - return true; - if (!at(MITokenKind::IRBlock) && !at(MITokenKind::NamedIRBlock)) - return fail("blockaddress requires an IR block reference"); - const std::string BlockName = symbolName(token()); - advance(); - if (expect(MITokenKind::RightParen, "')' after blockaddress")) - return true; - std::int64_t Offset = 0; - if (parseOptionalOffset(Offset)) - return true; - auto Operand = MachineOperand::blockAddress(FunctionName, BlockName, Offset); - if (!Operand) - return importError(Begin, Operand.error()); - Result = ParsedOperand{ - std::move(*Operand), std::nullopt, {Begin.range().Begin, token().range().Begin}}; - return false; -} - -bool MIParser::Impl::parseOperand(bool IsDef, ParsedOperand &Result) { - const MIToken Begin = token(); - if (token().isRegister() || token().isRegisterFlag()) - return parseRegisterOperand(IsDef, Result); - - std::uint32_t TargetFlags = 0; - if (parseTargetFlags(TargetFlags)) - return true; - const MIToken OperandToken = token(); - if (isFloatingSemanticsKeyword(OperandToken)) { - if (parseFloatingImmediateOperand(Result)) - return true; - Result.Operand.setTargetFlags(TargetFlags); - return false; - } - if (OperandToken.kind() == MITokenKind::Exclaim || - (OperandToken.kind() == MITokenKind::MetadataKeyword && - OperandToken.isMetadataKeyword("!DIExpression"))) { - if (parseMetadataOperand(Result)) - return true; - Result.Operand.setTargetFlags(TargetFlags); - return false; - } - if (isCFIOperationToken(OperandToken)) { - if (parseCFIOperand(Result)) - return true; - Result.Operand.setTargetFlags(TargetFlags); - return false; - } - MachineOperand Operand = MachineOperand::immediate(0); - bool HasOperand = true; - - switch (OperandToken.kind()) { - case MITokenKind::IntegerLiteral: { - std::int64_t Value = 0; - if (parseSigned64(OperandToken, Value)) - return fail("integer literal is too large for a machine immediate"); - Operand = MachineOperand::immediate(Value, TargetFlags); - advance(); - break; - } - case MITokenKind::MachineBasicBlock: { - MachineBasicBlock *Block = blockFor(OperandToken); - if (Block == nullptr) - return true; - Operand = MachineOperand::basicBlock(*Block, TargetFlags); - advance(); - break; - } - case MITokenKind::StackObject: - case MITokenKind::FixedStackObject: { - const bool Fixed = OperandToken.kind() == MITokenKind::FixedStackObject; - const auto Index = frameIndexFor(OperandToken, Fixed); - if (!Index) - return true; - advance(); - std::int64_t Offset = 0; - if (parseOptionalOffset(Offset)) - return true; - Operand = MachineOperand::frameIndex(*Index, Offset, TargetFlags); - break; - } - case MITokenKind::ConstantPoolItem: { - const auto Index = constantPoolIndexFor(OperandToken); - if (!Index) - return true; - advance(); - std::int64_t Offset = 0; - if (parseOptionalOffset(Offset)) - return true; - Operand = MachineOperand::constantPool(*Index, Offset, TargetFlags); - break; - } - case MITokenKind::JumpTableIndex: { - const auto Index = jumpTableIndexFor(OperandToken); - if (!Index) - return true; - Operand = MachineOperand::jumpTable(*Index, TargetFlags); - advance(); - break; - } - case MITokenKind::NamedGlobalValue: - case MITokenKind::GlobalValue: { - const std::string Name = symbolName(OperandToken); - advance(); - std::int64_t Offset = 0; - if (parseOptionalOffset(Offset)) - return true; - auto Created = MachineOperand::globalSymbol(Name, Offset, TargetFlags); - if (!Created) - return importError(OperandToken, Created.error()); - Operand = std::move(*Created); - break; - } - case MITokenKind::ExternalSymbol: { - const std::string Name = symbolName(OperandToken); - advance(); - std::int64_t Offset = 0; - if (parseOptionalOffset(Offset)) - return true; - auto Created = MachineOperand::externalSymbol(Name, Offset, TargetFlags); - if (!Created) - return importError(OperandToken, Created.error()); - Operand = std::move(*Created); - break; - } - case MITokenKind::MCSymbol: { - const std::string Name = symbolName(OperandToken); - advance(); - std::int64_t Offset = 0; - if (parseOptionalOffset(Offset)) - return true; - auto Created = MachineOperand::mcSymbol(Name, Offset, TargetFlags); - if (!Created) - return importError(OperandToken, Created.error()); - Operand = std::move(*Created); - break; - } - case MITokenKind::SubRegisterIndex: { - const SubRegIndexDesc *Description = Target.registers().subRegIndex(OperandToken.stringValue()); - if (Description == nullptr) - return fail("unknown subregister index '" + std::string(OperandToken.stringValue()) + "'"); - Operand = - MachineOperand::immediate(static_cast(Description->ID.value()), TargetFlags); - advance(); - break; - } - case MITokenKind::Identifier: { - if (const auto Mask = Target.mirText().registerMask(OperandToken.stringValue())) { - auto Created = MachineOperand::registerMask(Mask, TargetFlags); - if (!Created) - return importError(OperandToken, Created.error()); - Operand = std::move(*Created); - advance(); - } else if (OperandToken.stringValue() == "CustomRegMask") { - if (TargetFlags != 0) - return fail("custom register masks cannot have target flags"); - return parseCustomMaskOperand(Result); - } else if (OperandToken.stringValue().starts_with('i')) { - if (parseTypedIntegerOperand(Result)) - return true; - Result.Operand.setTargetFlags(TargetFlags); - return false; - } else { - return fail("unknown named machine operand '" + std::string(OperandToken.stringValue()) + - "'"); - } - break; - } - case MITokenKind::Keyword: - HasOperand = false; - break; - default: - return fail("expected a machine operand"); - } - - if (!HasOperand) { - bool Failed = false; - if (OperandToken.isKeyword("intpred")) - Failed = parsePredicateOperand(Result, false); - else if (OperandToken.isKeyword("floatpred")) - Failed = parsePredicateOperand(Result, true); - else if (OperandToken.isKeyword("shufflemask")) - Failed = parseShuffleMaskOperand(Result); - else if (OperandToken.isKeyword("dbg-instr-ref")) - Failed = parseDebugInstrRefOperand(Result); - else if (OperandToken.isKeyword("lanemask")) - Failed = parseLaneMaskOperand(Result); - else if (OperandToken.isKeyword("intrinsic")) - Failed = parseIntrinsicOperand(Result); - else if (OperandToken.isKeyword("target-index")) - Failed = parseTargetIndexOperand(Result); - else if (OperandToken.isKeyword("liveout")) - Failed = parseLiveOutMaskOperand(Result); - else if (OperandToken.isKeyword("blockaddress")) - Failed = parseBlockAddressOperand(Result); - else - return fail("unsupported machine operand spelling '" + std::string(OperandToken.spelling()) + - "'"); - if (!Failed) - Result.Operand.setTargetFlags(TargetFlags); - return Failed; - } - - Result = - ParsedOperand{std::move(Operand), std::nullopt, {Begin.range().Begin, token().range().Begin}}; - return false; -} - -bool MIParser::Impl::parseInlineAsmExtraInfoOperand(ParsedOperand &Result) { - const MIToken Begin = token(); - InlineAsmExtraInfo ExtraInfo; - while (at(MITokenKind::Identifier)) { - const auto Bits = InlineAsmExtraInfo::namedFlagBits(token().stringValue()); - if (!Bits) { - return fail("unknown inline asm extra info flag '" + std::string(token().stringValue()) + - "'"); - } - ExtraInfo = InlineAsmExtraInfo(ExtraInfo.rawValue() | *Bits); - advance(); - } - Result = ParsedOperand{ - MachineOperand::immediate(static_cast(ExtraInfo.rawValue())), - std::nullopt, - {Begin.range().Begin, token().range().Begin}, - }; - return false; -} - -bool MIParser::Impl::parseInlineAsmOperandFlag(ParsedOperand &Result) { - const MIToken Begin = token(); - const std::string KindName(token().stringValue()); - const auto Kind = InlineAsmOperandFlag::kindForName(KindName); - if (!Kind) - return fail("unknown inline asm operand kind '" + KindName + "'"); - advance(); - - InlineAsmOperandFlag Flag(*Kind); - if (at(MITokenKind::Identifier) && token().stringValue() == "tiedto") { - advance(); - if (!consume(MITokenKind::Colon)) - return fail("expected ':' after 'tiedto'"); - if (!at(MITokenKind::NamedRegister)) - return fail("expected '$N' operand number after 'tiedto:'"); - std::uint64_t OperandNumber = 0; - if (!parseUnsignedDecimal(token().stringValue(), OperandNumber) || - OperandNumber > std::numeric_limits::max() || - !Flag.setMatchingOperand(static_cast(OperandNumber))) { - return fail("invalid operand number in tiedto constraint"); - } - advance(); - } else if (consume(MITokenKind::Colon)) { - if (!at(MITokenKind::Identifier)) - return fail("expected register class or memory constraint name after ':'"); - const std::string Constraint(token().stringValue()); - if (*Kind == InlineAsmOperandKind::Memory) { - const auto MemoryConstraint = InlineAsmOperandFlag::memoryConstraintForName(Constraint); - if (!MemoryConstraint) - return fail("unknown memory constraint '" + Constraint + "'"); - if (!Flag.setMemoryConstraint(*MemoryConstraint)) - return fail("invalid memory constraint encoding"); - } else if (*Kind == InlineAsmOperandKind::RegisterDef || - *Kind == InlineAsmOperandKind::RegisterUse || - *Kind == InlineAsmOperandKind::RegisterDefEarlyClobber) { - const RegisterClassDesc *RegisterClass = Target.registers().registerClass(Constraint); - if (RegisterClass == nullptr) - return fail("unknown register class '" + Constraint + "'"); - if (!Flag.setRegisterClass(RegisterClass->ID.value())) - return fail("register class cannot be encoded in an inline asm operand flag"); - } - advance(); - } - - Result = ParsedOperand{ - MachineOperand::immediate(static_cast(Flag.rawValue())), - std::nullopt, - {Begin.range().Begin, token().range().Begin}, - }; - return false; -} - -bool MIParser::Impl::parseBlockLiveIns(MachineBasicBlock &Block) { - advance(); - if (expect(MITokenKind::Colon, "':' after liveins")) - return true; - if (token().isNewlineOrEnd()) - return false; - do { - if (!at(MITokenKind::NamedRegister)) - return fail("expected a physical register in liveins"); - Register Reg; - if (parseRegister(Reg) || !Reg.isPhysical()) - return true; - const auto Physical = Reg.physicalRegister(); - if (!Physical) - return fail("expected a physical register in liveins"); - LaneMask Lanes = LaneMask::all(); - if (consume(MITokenKind::Colon)) { - std::uint64_t Raw = 0; - if (parseUnsigned(token(), Raw)) - return fail("invalid live-in lane mask"); - Lanes = LaneMask::fromRaw(Raw); - advance(); - } - auto Added = Block.addLiveIn(*Physical, Lanes); - if (!Added) - return importError(token(), Added.error()); - } while (consume(MITokenKind::Comma)); - return false; -} - -bool MIParser::Impl::parseBlockSuccessors(MachineBasicBlock &Block) { - advance(); - if (expect(MITokenKind::Colon, "':' after successors")) - return true; - ExplicitSuccessorBlocks.insert(&Block); - if (token().isNewlineOrEnd()) - return false; - do { - if (!at(MITokenKind::MachineBasicBlock)) - return fail("expected a machine basic block reference"); - MachineBasicBlock *Successor = blockFor(token()); - if (Successor == nullptr) - return true; - advance(); - BranchProbability Probability = BranchProbability::zero(); - if (consume(MITokenKind::LeftParen)) { - std::uint32_t Weight = 0; - if (parseUnsigned32(token(), Weight)) - return fail("expected unsigned integer"); - Probability = BranchProbability::fromRaw(Weight); - advance(); - if (expect(MITokenKind::RightParen, "')' after successor weight")) - return true; - } - auto Added = Function.cfgUpdater().addSuccessor(Block, *Successor, Probability); - if (!Added) - return importError(token(), Added.error()); - } while (consume(MITokenKind::Comma)); - return false; -} - -bool MIParser::Impl::parseInstruction(MachineBasicBlock &Block, bool InBundle, - MachineInstr *PreviousInstruction, MachineInstr *&Result) { - if (ParsedInstructionCount >= Options.MaximumInstructions) - return fail("machine instruction count exceeds parser limit"); - - std::vector Operands; - while (token().isRegister() || token().isRegisterFlag()) { - ParsedOperand Operand{MachineOperand::immediate(0), std::nullopt, {}}; - if (parseRegisterOperand(true, Operand)) - return true; - Operands.push_back(std::move(Operand)); - if (!consume(MITokenKind::Comma)) - break; - } - if (!Operands.empty() && expect(MITokenKind::Equal, "'=' after register definitions")) - return true; - - MachineInstrFlags Flags; - while (isInstructionFlag(token())) { - const auto Flag = instructionFlag(token().stringValue()); - if (!Flag) - return fail("unknown instruction flag"); - if (Flags.has(*Flag)) - return fail("duplicate instruction flag '" + std::string(token().stringValue()) + "'"); - Flags.set(*Flag); - advance(); - } - if (!at(MITokenKind::Identifier)) - return fail("expected a machine instruction name"); - const MIToken OpcodeToken = token(); - const InstructionDescriptor *Descriptor = - Target.instructions().descriptor(OpcodeToken.stringValue()); - if (Descriptor == nullptr) - return fail("unknown machine instruction '" + std::string(OpcodeToken.stringValue()) + "'"); - advance(); - const bool IsInlineAsm = Descriptor->Name == "INLINEASM" || Descriptor->Name == "INLINEASM_BR"; - - while (!token().isNewlineOrEnd() && !at(MITokenKind::DoubleColon) && - !at(MITokenKind::LeftBrace) && !atKeyword("debug-instr-number") && - !atKeyword("debug-location") && !atKeyword("pre-instr-symbol") && - !atKeyword("post-instr-symbol") && !atKeyword("heap-alloc-marker") && - !atKeyword("pcsections") && !atKeyword("mmra") && !atKeyword("cfi-type") && - !atKeyword("deactivation-symbol")) { - if (Operands.size() >= Options.MaximumOperandsPerInstruction) - return fail("instruction operand count exceeds parser limit"); - ParsedOperand Operand{MachineOperand::immediate(0), std::nullopt, {}}; - const bool IsInlineAsmExtraInfo = IsInlineAsm && - Operands.size() == InlineAsmExtraInfoOperandIndex && - at(MITokenKind::Identifier); - const bool IsInlineAsmOperandFlag = IsInlineAsm && - Operands.size() >= InlineAsmFirstConstraintOperandIndex && - at(MITokenKind::Identifier); - if (IsInlineAsmExtraInfo ? parseInlineAsmExtraInfoOperand(Operand) - : (IsInlineAsmOperandFlag ? parseInlineAsmOperandFlag(Operand) - : parseOperand(false, Operand))) - return true; - Operands.push_back(std::move(Operand)); - if (token().isNewlineOrEnd() || at(MITokenKind::DoubleColon) || at(MITokenKind::LeftBrace) || - atKeyword("debug-instr-number") || atKeyword("debug-location")) - break; - if (expect(MITokenKind::Comma, "',' before the next machine operand")) - return true; - } - - std::uint32_t DebugNumber = 0; - std::optional DebugLocation; - std::optional PreInstrSymbol; - std::optional PostInstrSymbol; - std::optional HeapAllocMarker; - std::optional PCSections; - std::optional MMRA; - std::optional CFIType; - std::optional DeactivationSymbol; - std::unordered_set SeenAttachments; - while (atKeyword("debug-instr-number") || atKeyword("debug-location") || - atKeyword("pre-instr-symbol") || atKeyword("post-instr-symbol") || - atKeyword("heap-alloc-marker") || atKeyword("pcsections") || atKeyword("mmra") || - atKeyword("cfi-type") || atKeyword("deactivation-symbol")) { - const std::string Name(token().stringValue()); - if (!SeenAttachments.insert(Name).second) - return fail("duplicate instruction attachment '" + Name + "'"); - advance(); - if (Name == "debug-instr-number") { - if (!at(MITokenKind::IntegerLiteral) || parseUnsigned32(token(), DebugNumber) || - DebugNumber == 0) { - return fail("invalid debug instruction number"); - } - advance(); - } else if (Name == "debug-location") { - if (at(MITokenKind::MetadataKeyword) && token().isMetadataKeyword("!DILocation")) { - std::vector InlineChain; - std::optional TerminalInlinedAt; - if (parseInlineDILocation(InlineChain, TerminalInlinedAt)) - return true; - auto Location = MachineDILocation::create(std::move(InlineChain), TerminalInlinedAt); - if (!Location) - return importError(OpcodeToken, Location.error()); - DebugLocation = MachineDebugLocation::inlineLocation(std::move(*Location)); - } else { - MachineMetadataReference Reference; - if (parseMetadataReference(Reference)) - return true; - auto Created = MachineDebugLocation::reference(Reference); - if (!Created) - return importError(OpcodeToken, Created.error()); - DebugLocation = std::move(*Created); - } - } else if (Name == "pre-instr-symbol" || Name == "post-instr-symbol") { - if (!at(MITokenKind::MCSymbol)) - return fail("expected an MC symbol after '" + Name + "'"); - if (Name == "pre-instr-symbol") - PreInstrSymbol = symbolName(token()); - else - PostInstrSymbol = symbolName(token()); - advance(); - } else if (Name == "heap-alloc-marker" || Name == "pcsections" || Name == "mmra") { - MachineMetadataReference Reference; - if (parseMetadataReference(Reference)) - return true; - if (Name == "heap-alloc-marker") - HeapAllocMarker = Reference; - else if (Name == "pcsections") - PCSections = Reference; - else - MMRA = Reference; - } else if (Name == "cfi-type") { - std::uint32_t Type = 0; - if (!at(MITokenKind::IntegerLiteral) || parseUnsigned32(token(), Type) || Type == 0) - return fail("invalid nonzero CFI type"); - CFIType = Type; - advance(); - } else { - if (!at(MITokenKind::GlobalValue) && !at(MITokenKind::NamedGlobalValue)) - return fail("expected a global value after 'deactivation-symbol'"); - DeactivationSymbol = symbolName(token()); - advance(); - } - static_cast(consume(MITokenKind::Comma)); - } - std::vector MemOperands; - if (consume(MITokenKind::DoubleColon)) { - while (!token().isNewlineOrEnd() && !at(MITokenKind::LeftBrace)) { - if (MemOperands.size() >= Options.MaximumMemoryOperandsPerInstruction) - return fail("machine memory operand count exceeds parser limit"); - const MachineMemOperand *MemOperand = nullptr; - if (parseMemoryOperand(MemOperand, Descriptor->InstructionOpcode)) - return true; - MemOperands.push_back(MemOperand); - if (token().isNewlineOrEnd() || at(MITokenKind::LeftBrace)) - break; - if (expect(MITokenKind::Comma, "',' before the next machine memory operand")) - return true; - } - } - - auto Created = Function.createInstruction(Descriptor->InstructionOpcode); - if (!Created) - return importError(OpcodeToken, Created.error()); - MachineInstr *Instruction = *Created; - Instruction->setFlags(Flags); - for (ParsedOperand &Parsed : Operands) { - auto Added = Instruction->addOperand(std::move(Parsed.Operand)); - if (!Added) - return importError(OpcodeToken, Added.error()); - } - for (std::size_t Index = 0; Index < Operands.size(); ++Index) { - if (!Operands[Index].TiedDef) - continue; - if (*Operands[Index].TiedDef >= Operands.size()) - return fail(OpcodeToken, "tied-def operand index is out of range"); - auto Tied = Instruction->tieOperands(*Operands[Index].TiedDef, Index); - if (!Tied) - return importError(OpcodeToken, Tied.error()); - } - for (const MachineMemOperand *MemOperand : MemOperands) { - auto Added = Instruction->addMemOperand(*MemOperand); - if (!Added) - return importError(OpcodeToken, Added.error()); - } - if (DebugNumber != 0) { - auto SetNumber = Instruction->setDebugInstrNumber(DebugNumber); - if (!SetNumber) - return importError(OpcodeToken, SetNumber.error()); - } - if (DebugLocation) { - auto SetLocation = Instruction->setDebugLocation(std::move(*DebugLocation)); - if (!SetLocation) - return importError(OpcodeToken, SetLocation.error()); - } - if (PreInstrSymbol) { - auto SetSymbol = Instruction->setPreInstrSymbol(std::move(*PreInstrSymbol)); - if (!SetSymbol) - return importError(OpcodeToken, SetSymbol.error()); - } - if (PostInstrSymbol) { - auto SetSymbol = Instruction->setPostInstrSymbol(std::move(*PostInstrSymbol)); - if (!SetSymbol) - return importError(OpcodeToken, SetSymbol.error()); - } - if (HeapAllocMarker) { - auto SetMarker = Instruction->setHeapAllocMarker(*HeapAllocMarker); - if (!SetMarker) - return importError(OpcodeToken, SetMarker.error()); - } - if (PCSections) { - auto SetSections = Instruction->setPCSections(*PCSections); - if (!SetSections) - return importError(OpcodeToken, SetSections.error()); - } - if (MMRA) { - auto SetMMRA = Instruction->setMMRA(*MMRA); - if (!SetMMRA) - return importError(OpcodeToken, SetMMRA.error()); - } - if (CFIType) { - auto SetType = Instruction->setCFIType(*CFIType); - if (!SetType) - return importError(OpcodeToken, SetType.error()); - } - if (DeactivationSymbol) { - auto SetSymbol = Instruction->setDeactivationSymbol(std::move(*DeactivationSymbol)); - if (!SetSymbol) - return importError(OpcodeToken, SetSymbol.error()); - } - if (InBundle) { - Flags.set(MachineInstrFlag::BundledWithPred); - Instruction->setFlags(Flags); - if (PreviousInstruction != nullptr) { - MachineInstrFlags PreviousFlags = PreviousInstruction->flags(); - PreviousFlags.set(MachineInstrFlag::BundledWithSucc); - PreviousInstruction->setFlags(PreviousFlags); - } - } - auto Appended = Block.appendInstruction(*Instruction); - if (!Appended) - return importError(OpcodeToken, Appended.error()); - - ++ParsedInstructionCount; - Result = Instruction; - return false; -} - -bool MIParser::Impl::parseSecondPass() { - Position = 0; - while (consume(MITokenKind::Newline)) { - } - while (!at(MITokenKind::EndOfFile)) { - if (!at(MITokenKind::MachineBasicBlockLabel)) - return fail("expected a machine basic block definition"); - std::uint32_t ID = 0; - if (parseBlockDefinition(false, ID)) - return true; - MachineBasicBlock *Block = Blocks.at(ID); - - while (consume(MITokenKind::Newline)) { - } - while (atKeyword("liveins") || atKeyword("successors")) { - if ((atKeyword("liveins") && parseBlockLiveIns(*Block)) || - (atKeyword("successors") && parseBlockSuccessors(*Block))) { - return true; - } - if (!token().isNewlineOrEnd()) - return fail("expected a line break after the block list"); - while (consume(MITokenKind::Newline)) { - } - } - - bool InBundle = false; - MachineInstr *PreviousInstruction = nullptr; - while (!at(MITokenKind::MachineBasicBlockLabel) && !at(MITokenKind::EndOfFile)) { - if (consume(MITokenKind::Newline)) - continue; - if (consume(MITokenKind::RightBrace)) { - if (!InBundle) - return fail("closing brace without an active instruction bundle"); - InBundle = false; - continue; - } - - MachineInstr *Instruction = nullptr; - if (parseInstruction(*Block, InBundle, PreviousInstruction, Instruction)) - return true; - PreviousInstruction = Instruction; - if (consume(MITokenKind::LeftBrace)) { - if (InBundle) - return fail("nested instruction bundles are not allowed"); - MachineInstrFlags BundleFlags = Instruction->flags(); - BundleFlags.set(MachineInstrFlag::BundledWithSucc); - Instruction->setFlags(BundleFlags); - InBundle = true; - } - if (!token().isNewlineOrEnd() && !at(MITokenKind::RightBrace)) - return fail("expected a line break after the machine instruction"); - } - if (InBundle) - return fail("unterminated instruction bundle"); - } - return false; -} - -bool MIParser::Impl::inferSuccessors() { - for (MachineBasicBlock *Block = Function.firstBlock(); Block != nullptr; Block = Block->next()) { - if (ExplicitSuccessorBlocks.contains(Block)) - continue; - - bool BlocksFallthrough = true; - std::vector Successors; - for (MachineInstr *Instruction = Block->firstInstruction(); Instruction != nullptr; - Instruction = Instruction->next()) { - const InstructionDescriptor *Descriptor = - Target.instructions().descriptor(Instruction->opcode()); - if (Descriptor == nullptr) - return fail("instruction has no target descriptor while inferring successors"); - if (Descriptor->Flags.has(InstructionFlag::Branch)) { - for (const MachineOperand &Operand : Instruction->operands()) { - if (const BasicBlockOperand *TargetBlock = Operand.getIf()) { - if (std::ranges::find(Successors, TargetBlock->Block) == Successors.end()) - Successors.push_back(TargetBlock->Block); - } - } - } - if (Descriptor->Flags.has(InstructionFlag::Barrier)) - BlocksFallthrough = false; - } - if (BlocksFallthrough && Block->next() != nullptr && - std::ranges::find(Successors, Block->next()) == Successors.end()) - Successors.push_back(Block->next()); - if (Successors.empty()) - continue; - const auto Probability = - BranchProbability::fromFraction(1U, static_cast(Successors.size())); - if (!Probability) - return importError(token(), Probability.error()); - for (MachineBasicBlock *Successor : Successors) { - auto Added = Function.cfgUpdater().addSuccessor(*Block, *Successor, *Probability); - if (!Added) - return importError(token(), Added.error()); - } - } - return false; -} - -Expected MIParser::Impl::parse() { - if (ParseAttempted) - return makeUnexpected(ErrorCode::InvalidArgument, "MI parser can only be run once"); - ParseAttempted = true; - if (Function.blockCount() != 0 || !Function.allInstructions().empty()) { - return makeUnexpected(ErrorCode::InvalidArgument, - "MI parser requires a staging machine function with an empty body"); - } - for (const MIVirtualRegisterSlot &Slot : Context.VirtualRegisters) { - if (!Slot.Reg.isVirtual() || !Function.registerInfo().isKnownVirtualRegister(Slot.Reg)) { - return makeUnexpected(ErrorCode::InvalidArgument, - "MI parser virtual-register slot maps to an unknown register"); - } - if (!NumericVirtualRegisters.emplace(Slot.TextID, Slot.Reg).second) { - return makeUnexpected(ErrorCode::InvalidArgument, - "MI parser virtual-register text IDs must be unique"); - } - } - if (tokenize()) - return failureResult(); - if (parseFirstPass() || parseSecondPass() || inferSuccessors()) - return failureResult(); - - Function.renumberBlocks(); - if (Options.VerifyAfterParse) { - auto Verified = - MachineVerifier::verify(Function, Target, - MachineVerifierOptions{.Mode = MachineVerifierMode::Generic, - .Stage = std::nullopt, - .VerifyProperties = false}); - if (!Verified) { - static_cast(fail(token(), "parsed machine function failed verification: " + - std::string(Verified.error().message()))); - return failureResult(); - } - } - return {}; -} - -MIParser::MIParser(MachineFunction &Function, const TargetDescription &Target, - std::string SourceName, std::string SourceText, MIParserContext Context, - MIParserOptions Options) - : Implementation(std::make_unique(Function, Target, std::move(SourceName), - std::move(SourceText), std::move(Context), Options)) {} - -MIParser::MIParser(MIParser &&) noexcept = default; - -MIParser &MIParser::operator=(MIParser &&) noexcept = default; - -MIParser::~MIParser() = default; - -Expected MIParser::parse() { - return Implementation->parse(); -} - -const TextSource &MIParser::source() const noexcept { - return Implementation->source(); -} - -std::span MIParser::diagnostics() const noexcept { - return Implementation->diagnostics(); -} - -bool MIParser::failed() const noexcept { - return Implementation->failed(); -} - -} // namespace rcc::mir diff --git a/backend/lib/MIR/MIPrinter.cpp b/backend/lib/MIR/MIPrinter.cpp deleted file mode 100644 index 0265564..0000000 --- a/backend/lib/MIR/MIPrinter.cpp +++ /dev/null @@ -1,1694 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "rcc/MIR/MIPrinter.h" - -#include "rcc/CodeGen/InlineAsm.h" -#include "rcc/CodeGen/InstructionDescriptor.h" -#include "rcc/CodeGen/MachineBasicBlock.h" -#include "rcc/CodeGen/MachineCFIInfo.h" -#include "rcc/CodeGen/MachineFunction.h" -#include "rcc/CodeGen/MachineInstr.h" -#include "rcc/CodeGen/MachineMemOperand.h" -#include "rcc/CodeGen/MachineOperand.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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace rcc::mir { -namespace { - -[[nodiscard]] bool isBareNameCharacter(char Character) noexcept { - return (Character >= 'a' && Character <= 'z') || (Character >= 'A' && Character <= 'Z') || - (Character >= '0' && Character <= '9') || Character == '_' || Character == '-' || - Character == '.' || Character == '$'; -} - -[[nodiscard]] std::string quoteName(std::string_view Name) { - if (!Name.empty() && std::ranges::all_of(Name, isBareNameCharacter)) - return std::string(Name); - - constexpr std::string_view Hex = "0123456789ABCDEF"; - std::string Result; - Result.reserve(Name.size() + 2U); - Result.push_back('"'); - for (const char RawCharacter : Name) { - const auto Character = static_cast(RawCharacter); - if (Character >= 0x20U && Character <= 0x7EU && Character != '"' && Character != '\\') { - Result.push_back(static_cast(Character)); - continue; - } - Result.push_back('\\'); - Result.push_back(Hex[Character >> 4U]); - Result.push_back(Hex[Character & 0xFU]); - } - Result.push_back('"'); - return Result; -} - -[[nodiscard]] std::string quoteString(std::string_view Value) { - std::string Result = quoteName(Value); - if (!Result.empty() && Result.front() == '"') - return Result; - Result.insert(Result.begin(), '"'); - Result.push_back('"'); - return Result; -} - -[[nodiscard]] std::string hexadecimalWords(std::span Words, - std::uint32_t BitWidth) { - constexpr std::string_view Hex = "0123456789ABCDEF"; - const std::size_t DigitCount = (static_cast(BitWidth) + 3U) / 4U; - std::string Result(DigitCount, '0'); - for (std::size_t Digit = 0; Digit < DigitCount; ++Digit) { - const std::size_t BitOffset = Digit * 4U; - const std::uint64_t Nibble = (Words[BitOffset / 64U] >> (BitOffset % 64U)) & 0xFU; - Result[DigitCount - Digit - 1U] = Hex[Nibble]; - } - return Result; -} - -[[nodiscard]] std::string_view integerPredicateName(IntegerPredicate Predicate) noexcept { - switch (Predicate) { - case IntegerPredicate::Equal: - return "eq"; - case IntegerPredicate::NotEqual: - return "ne"; - case IntegerPredicate::SignedGreaterThan: - return "sgt"; - case IntegerPredicate::SignedGreaterEqual: - return "sge"; - case IntegerPredicate::SignedLessThan: - return "slt"; - case IntegerPredicate::SignedLessEqual: - return "sle"; - case IntegerPredicate::UnsignedGreaterThan: - return "ugt"; - case IntegerPredicate::UnsignedGreaterEqual: - return "uge"; - case IntegerPredicate::UnsignedLessThan: - return "ult"; - case IntegerPredicate::UnsignedLessEqual: - return "ule"; - case IntegerPredicate::Count: - return {}; - } - return {}; -} - -[[nodiscard]] std::string_view floatingPredicateName(FloatingPredicate Predicate) noexcept { - switch (Predicate) { - case FloatingPredicate::AlwaysFalse: - return "false"; - case FloatingPredicate::OrderedEqual: - return "oeq"; - case FloatingPredicate::OrderedGreaterThan: - return "ogt"; - case FloatingPredicate::OrderedGreaterEqual: - return "oge"; - case FloatingPredicate::OrderedLessThan: - return "olt"; - case FloatingPredicate::OrderedLessEqual: - return "ole"; - case FloatingPredicate::OrderedNotEqual: - return "one"; - case FloatingPredicate::Ordered: - return "ord"; - case FloatingPredicate::Unordered: - return "uno"; - case FloatingPredicate::UnorderedEqual: - return "ueq"; - case FloatingPredicate::UnorderedGreaterThan: - return "ugt"; - case FloatingPredicate::UnorderedGreaterEqual: - return "uge"; - case FloatingPredicate::UnorderedLessThan: - return "ult"; - case FloatingPredicate::UnorderedLessEqual: - return "ule"; - case FloatingPredicate::UnorderedNotEqual: - return "une"; - case FloatingPredicate::AlwaysTrue: - return "true"; - case FloatingPredicate::Count: - return {}; - } - return {}; -} - -[[nodiscard]] std::string_view atomicOrderingName(AtomicOrdering Ordering) noexcept { - switch (Ordering) { - case AtomicOrdering::Unordered: - return "unordered"; - case AtomicOrdering::Monotonic: - return "monotonic"; - case AtomicOrdering::Acquire: - return "acquire"; - case AtomicOrdering::Release: - return "release"; - case AtomicOrdering::AcquireRelease: - return "acq_rel"; - case AtomicOrdering::SequentiallyConsistent: - return "seq_cst"; - case AtomicOrdering::NotAtomic: - return {}; - } - return {}; -} - -[[nodiscard]] Expected llvmMIRScalarLowLevelType(LLT Type) { - if (Type.isToken()) - return "s0"; - if (Type.isAnyScalar()) - return "s" + std::to_string(Type.scalarBitWidth()); - if (Type.isInteger()) - return "i" + std::to_string(Type.scalarBitWidth()); - if (Type.isPointer()) { - const auto AddressSpace = Type.addressSpace(); - if (!AddressSpace) { - return makeUnexpected(ErrorCode::VerificationError, - "pointer low-level type has no address space"); - } - return "p" + std::to_string(*AddressSpace); - } - if (!Type.isFloat()) { - return makeUnexpected(ErrorCode::VerificationError, - "invalid scalar low-level type cannot be printed as LLVM MIR"); - } - - const auto Semantics = Type.floatSemantics(); - if (!Semantics) { - return makeUnexpected(ErrorCode::VerificationError, - "floating low-level type has no floating-point semantics"); - } - switch (*Semantics) { - case FloatSemantics::IEEEHalf: - return "f16"; - case FloatSemantics::BFloat: - return "bf16"; - case FloatSemantics::IEEESingle: - return "f32"; - case FloatSemantics::IEEEDouble: - return "f64"; - case FloatSemantics::IEEEQuad: - return "f128"; - case FloatSemantics::PPCDoubleDouble: - case FloatSemantics::Float8E5M2: - case FloatSemantics::Float8E5M2FNUZ: - case FloatSemantics::Float8E4M3: - case FloatSemantics::Float8E4M3FN: - case FloatSemantics::Float8E4M3FNUZ: - case FloatSemantics::Float8E4M3B11FNUZ: - case FloatSemantics::Float8E3M4: - case FloatSemantics::FloatTF32: - case FloatSemantics::Float8E8M0FNU: - case FloatSemantics::Float6E3M2FN: - case FloatSemantics::Float6E2M3FN: - case FloatSemantics::Float4E2M1FN: - case FloatSemantics::X87DoubleExtended: - case FloatSemantics::Count: - return makeUnexpected(ErrorCode::Unsupported, - "fixed LLVM MIR cannot spell this floating low-level type"); - } - return makeUnexpected(ErrorCode::InternalError, "floating low-level type is unhandled"); -} - -[[nodiscard]] Expected llvmMIRLowLevelType(LLT Type) { - if (!Type.isValid()) { - return makeUnexpected(ErrorCode::VerificationError, - "invalid low-level type cannot be printed as LLVM MIR"); - } - if (!Type.isVector()) - return llvmMIRScalarLowLevelType(Type); - - const auto ElementType = Type.scalarElementType(); - if (!ElementType) { - return makeUnexpected(ErrorCode::VerificationError, - "vector low-level type has no scalar element type"); - } - auto Scalar = llvmMIRScalarLowLevelType(*ElementType); - if (!Scalar) - return std::unexpected(Scalar.error()); - - std::string Result = "<"; - if (Type.isScalableVector()) - Result.append("vscale x "); - Result.append(std::to_string(Type.minimumElementCount())); - Result.append(" x "); - Result.append(*Scalar); - Result.push_back('>'); - return Result; -} - -class BodyPrinter final { -public: - BodyPrinter(const MachineFunction &Function, const TargetDescription &Target, - const MIParserContext &Context, MIPrinterOptions Options) - : Function(Function), Target(Target), Context(Context), Options(Options) {} - - [[nodiscard]] Expected run(); - -private: - [[nodiscard]] Expected initialize(); - [[nodiscard]] Expected printBlock(const MachineBasicBlock &Block); - [[nodiscard]] Expected printInstruction(const MachineInstr &Instruction); - [[nodiscard]] Expected printOperand(const MachineOperand &Operand, bool IsExplicitDef, - bool PrintVirtualType = true); - [[nodiscard]] Expected printInlineAsmExtraInfo(const MachineOperand &Operand); - [[nodiscard]] Expected printInlineAsmOperandFlag(const MachineOperand &Operand); - [[nodiscard]] Expected printRegisterOperand(const MachineOperand &Operand, - bool IsExplicitDef, bool PrintVirtualType); - [[nodiscard]] Expected printTargetFlags(std::uint32_t Flags); - [[nodiscard]] Expected printMetadataReference(MachineMetadataReference Reference); - [[nodiscard]] Expected printDILocation(const MachineDILocation &Location, - std::size_t FrameIndex); - [[nodiscard]] Expected printCFIInstruction(CFIInstructionIndex Index); - [[nodiscard]] Expected printMemoryOperand(const MachineMemOperand &Operand); - [[nodiscard]] Expected printMemoryBase(const MemoryBase &Base, std::int64_t Offset); - - [[nodiscard]] Expected blockID(const MachineBasicBlock &Block) const; - [[nodiscard]] Expected virtualRegisterID(Register Reg) const; - [[nodiscard]] Expected frameID(FrameIndex Index) const; - [[nodiscard]] Expected constantPoolID(ConstantPoolIndex Index) const; - [[nodiscard]] Expected jumpTableID(JumpTableIndex Index) const; - [[nodiscard]] Expected metadataID(MachineMetadataReference Reference) const; - [[nodiscard]] Expected appendPhysicalRegister(PhysicalRegister Register); - void appendBlockReference(const MachineBasicBlock &Block); - void appendOffset(std::int64_t Offset); - - const MachineFunction &Function; - const TargetDescription &Target; - const MIParserContext &Context; - MIPrinterOptions Options; - std::string Output; - std::unordered_map BlockIDs; -}; - -Expected BodyPrinter::initialize() { - std::uint64_t NextID = 0; - for (const MachineBasicBlock *Block = Function.firstBlock(); Block != nullptr; - Block = Block->next()) { - if (NextID > std::numeric_limits::max()) { - return makeUnexpected(ErrorCode::InvalidArgument, - "machine function has too many blocks to print"); - } - BlockIDs.emplace(Block, static_cast(NextID)); - ++NextID; - } - return {}; -} - -Expected BodyPrinter::blockID(const MachineBasicBlock &Block) const { - const auto Found = BlockIDs.find(&Block); - if (Found == BlockIDs.end()) - return makeUnexpected(ErrorCode::VerificationError, - "machine operand references an unlinked basic block"); - return Found->second; -} - -Expected BodyPrinter::virtualRegisterID(Register Reg) const { - if (!Context.VirtualRegisters.empty()) { - const auto Found = - std::ranges::find_if(Context.VirtualRegisters, - [Reg](const MIVirtualRegisterSlot &Slot) { return Slot.Reg == Reg; }); - if (Found == Context.VirtualRegisters.end()) { - return makeUnexpected(ErrorCode::VerificationError, "virtual register has no MIR text slot"); - } - return Found->TextID; - } - const auto VirtualIndex = Reg.virtualIndex(); - if (!VirtualIndex) - return makeUnexpected(ErrorCode::VerificationError, "virtual register has no numeric index"); - return *VirtualIndex; -} - -Expected BodyPrinter::frameID(FrameIndex Index) const { - const auto InStack = std::ranges::find_if( - Context.StackObjects, [Index](const MIFrameObjectSlot &Slot) { return Slot.Index == Index; }); - if (InStack != Context.StackObjects.end()) - return InStack->TextID; - const auto InFixed = - std::ranges::find_if(Context.FixedStackObjects, - [Index](const MIFrameObjectSlot &Slot) { return Slot.Index == Index; }); - if (InFixed != Context.FixedStackObjects.end()) - return InFixed->TextID; - return makeUnexpected(ErrorCode::VerificationError, "frame index has no MIR text slot"); -} - -Expected BodyPrinter::constantPoolID(ConstantPoolIndex Index) const { - const auto Found = - std::ranges::find_if(Context.ConstantPoolItems, - [Index](const MIConstantPoolSlot &Slot) { return Slot.Index == Index; }); - if (Found == Context.ConstantPoolItems.end()) { - return makeUnexpected(ErrorCode::VerificationError, "constant-pool index has no MIR text slot"); - } - return Found->TextID; -} - -Expected BodyPrinter::jumpTableID(JumpTableIndex Index) const { - const auto Found = std::ranges::find_if( - Context.JumpTables, [Index](const MIJumpTableSlot &Slot) { return Slot.Index == Index; }); - if (Found == Context.JumpTables.end()) - return makeUnexpected(ErrorCode::VerificationError, "jump-table index has no MIR text slot"); - return Found->TextID; -} - -Expected BodyPrinter::metadataID(MachineMetadataReference Reference) const { - const auto Found = - std::ranges::find_if(Context.Metadata, [Reference](const MIMetadataSlot &Slot) { - return Slot.Reference == Reference; - }); - if (Found != Context.Metadata.end()) - return Found->TextID; - if (Reference.scope() == MachineMetadataScope::MachineFunction) - return Reference.slot(); - return makeUnexpected(ErrorCode::VerificationError, - "embedded metadata reference has no MIR text slot"); -} - -Expected BodyPrinter::appendPhysicalRegister(PhysicalRegister Register) { - const auto Name = Target.registers().physicalRegisterMIRName(Register); - if (!Name) - return makeUnexpected(ErrorCode::VerificationError, "physical register has no MIR name"); - Output.push_back('$'); - Output.append(*Name); - return {}; -} - -void BodyPrinter::appendBlockReference(const MachineBasicBlock &Block) { - Output.append("%bb."); - Output.append(std::to_string(BlockIDs.at(&Block))); -} - -void BodyPrinter::appendOffset(std::int64_t Offset) { - if (Offset == 0) - return; - if (Offset > 0) { - Output.append(" + "); - Output.append(std::to_string(Offset)); - return; - } - Output.append(" - "); - const std::uint64_t Magnitude = Offset == std::numeric_limits::min() - ? std::uint64_t{1} << 63U - : static_cast(-Offset); - Output.append(std::to_string(Magnitude)); -} - -Expected BodyPrinter::printTargetFlags(std::uint32_t Flags) { - if (Flags == 0) - return {}; - auto Names = Target.mirText().operandFlagNames(Flags); - if (!Names) - return std::unexpected(Names.error()); - Output.append("target-flags("); - for (std::size_t Index = 0; Index < Names->size(); ++Index) { - if (Index != 0) - Output.append(", "); - Output.append((*Names)[Index]); - } - Output.append(") "); - return {}; -} - -Expected BodyPrinter::printMetadataReference(MachineMetadataReference Reference) { - auto ID = metadataID(Reference); - if (!ID) - return std::unexpected(ID.error()); - Output.push_back('!'); - Output.append(std::to_string(*ID)); - return {}; -} - -Expected BodyPrinter::printDILocation(const MachineDILocation &Location, - std::size_t FrameIndex) { - const auto Frames = Location.inlineChain(); - if (FrameIndex >= Frames.size()) { - return makeUnexpected(ErrorCode::VerificationError, - "inline DILocation has an invalid frame index"); - } - const MachineDILocationFrame &Frame = Frames[FrameIndex]; - Output.append("!DILocation(line: "); - Output.append(std::to_string(Frame.Line)); - if (Options.Mode == MIPrintMode::Complete || Frame.Column != 0) { - Output.append(", column: "); - Output.append(std::to_string(Frame.Column)); - } - Output.append(", scope: "); - if (auto Printed = printMetadataReference(Frame.Scope); !Printed) - return Printed; - if (FrameIndex + 1U < Frames.size()) { - Output.append(", inlinedAt: "); - if (auto Printed = printDILocation(Location, FrameIndex + 1U); !Printed) - return Printed; - } else if (Location.terminalInlinedAt()) { - Output.append(", inlinedAt: "); - if (auto Printed = printMetadataReference(*Location.terminalInlinedAt()); !Printed) { - return Printed; - } - } - if (Options.Mode == MIPrintMode::Complete || Frame.IsImplicitCode) { - Output.append(", isImplicitCode: "); - Output.append(Frame.IsImplicitCode ? "true" : "false"); - } - if (Options.Mode == MIPrintMode::Complete || Frame.AtomGroup != 0) { - Output.append(", atomGroup: "); - Output.append(std::to_string(Frame.AtomGroup)); - } - if (Options.Mode == MIPrintMode::Complete || Frame.AtomRank != 0) { - Output.append(", atomRank: "); - Output.append(std::to_string(Frame.AtomRank)); - } - Output.push_back(')'); - return {}; -} - -Expected BodyPrinter::printRegisterOperand(const MachineOperand &Operand, bool IsExplicitDef, - bool PrintVirtualType) { - const RegisterOperandFlags Flags = Operand.registerFlags(); - if (Flags.has(RegisterOperandFlag::Implicit) && Flags.has(RegisterOperandFlag::Define)) { - Output.append("implicit-def "); - } else { - if (!IsExplicitDef && Flags.has(RegisterOperandFlag::Define)) - Output.append("def "); - if (Flags.has(RegisterOperandFlag::Implicit)) - Output.append("implicit "); - } - if (Flags.has(RegisterOperandFlag::Dead)) - Output.append("dead "); - if (Flags.has(RegisterOperandFlag::Kill)) - Output.append("killed "); - if (Flags.has(RegisterOperandFlag::Undef)) - Output.append("undef "); - if (Flags.has(RegisterOperandFlag::InternalRead)) - Output.append("internal "); - if (Flags.has(RegisterOperandFlag::EarlyClobber)) - Output.append("early-clobber "); - if (Flags.has(RegisterOperandFlag::Debug)) - Output.append("debug-use "); - if (Flags.has(RegisterOperandFlag::Renamable)) - Output.append("renamable "); - - const auto RegisterValue = Operand.registerValue(); - if (!RegisterValue) { - return makeUnexpected(ErrorCode::VerificationError, "register operand has no register value"); - } - const Register Reg = *RegisterValue; - if (Reg.isNoRegister()) { - Output.append("$noreg"); - } else if (Reg.isPhysical()) { - const auto Physical = Reg.physicalRegister(); - if (!Physical) { - return makeUnexpected(ErrorCode::VerificationError, - "physical register has no physical-register value"); - } - auto Printed = appendPhysicalRegister(*Physical); - if (!Printed) - return Printed; - } else { - auto Info = Function.registerInfo().virtualRegisterInfo(Reg); - if (!Info) - return std::unexpected(Info.error()); - const VirtualRegisterInfo &RegisterInfo = Info->get(); - Output.push_back('%'); - if (RegisterInfo.Name) { - Output.append(*RegisterInfo.Name); - } else { - auto VirtualIndex = virtualRegisterID(Reg); - if (!VirtualIndex) - return std::unexpected(VirtualIndex.error()); - Output.append(std::to_string(*VirtualIndex)); - } - if (Operand.subReg().value() != 0) { - const SubRegIndexDesc *SubReg = Target.registers().subRegIndex(Operand.subReg()); - if (SubReg == nullptr) - return makeUnexpected(ErrorCode::VerificationError, - "virtual register has an unknown subregister"); - Output.push_back('.'); - Output.append(SubReg->Name); - } - - const bool PrintRegisterConstraint = - IsExplicitDef || Function.registerInfo().getUniqueVRegDef(Reg) == nullptr; - if (PrintRegisterConstraint && RegisterInfo.RegClass) { - const auto Name = Target.registers().registerClassMIRName(*RegisterInfo.RegClass); - if (!Name) - return makeUnexpected(ErrorCode::VerificationError, - "virtual register class has no MIR name"); - Output.push_back(':'); - Output.append(*Name); - } else if (PrintRegisterConstraint && RegisterInfo.Bank) { - const auto Name = Target.registers().registerBankMIRName(*RegisterInfo.Bank); - if (!Name) - return makeUnexpected(ErrorCode::VerificationError, - "virtual register bank has no MIR name"); - Output.push_back(':'); - Output.append(*Name); - } else if (PrintRegisterConstraint && RegisterInfo.Type) { - Output.append(":_"); - } - if (PrintVirtualType && RegisterInfo.Type) { - auto Type = llvmMIRLowLevelType(*RegisterInfo.Type); - if (!Type) - return std::unexpected(Type.error()); - Output.push_back('('); - Output.append(*Type); - Output.push_back(')'); - } - } - - if (Operand.tiedOperandIndex() && Operand.isUse()) { - Output.append("(tied-def "); - Output.append(std::to_string(*Operand.tiedOperandIndex())); - Output.push_back(')'); - } - return {}; -} - -Expected BodyPrinter::printCFIInstruction(CFIInstructionIndex Index) { - const MachineCFIInstruction *Instruction = Function.cfiInfo().instruction(Index); - if (Instruction == nullptr) - return makeUnexpected(ErrorCode::VerificationError, - "CFI operand references an unknown instruction"); - const auto PrintRegister = [this](DwarfRegisterNumber Dwarf) -> Expected { - const auto Register = Target.mirText().physicalRegister(Dwarf.value()); - if (!Register) { - return makeUnexpected(ErrorCode::VerificationError, - "CFI instruction has no canonical physical register"); - } - return appendPhysicalRegister(*Register); - }; - const auto PrintRegisterOffset = - [this, &PrintRegister](std::string_view Name, - const MachineCFIRegisterOffsetRule &Rule) -> Expected { - Output.append(Name); - Output.push_back(' '); - if (auto Printed = PrintRegister(Rule.Register); !Printed) - return Printed; - Output.append(", "); - Output.append(std::to_string(Rule.Offset)); - return {}; - }; - - switch (Instruction->operation()) { - case MachineCFIOperation::SameValue: { - Output.append("same_value "); - return PrintRegister(Instruction->getIf()->Register); - } - case MachineCFIOperation::RememberState: - Output.append("remember_state"); - return {}; - case MachineCFIOperation::RestoreState: - Output.append("restore_state"); - return {}; - case MachineCFIOperation::Offset: - return PrintRegisterOffset("offset", *Instruction->getIf()); - case MachineCFIOperation::LLVMDefAddressSpaceCFA: { - const auto &Rule = *Instruction->getIf(); - Output.append("llvm_def_aspace_cfa "); - if (auto Printed = PrintRegister(Rule.Register); !Printed) - return Printed; - Output.append(", "); - Output.append(std::to_string(Rule.Offset)); - Output.append(", "); - Output.append(std::to_string(Rule.AddressSpace)); - return {}; - } - case MachineCFIOperation::DefCFARegister: - Output.append("def_cfa_register "); - return PrintRegister(Instruction->getIf()->Register); - case MachineCFIOperation::DefCFAOffset: - Output.append("def_cfa_offset "); - Output.append(std::to_string(Instruction->getIf()->Offset)); - return {}; - case MachineCFIOperation::DefCFA: - return PrintRegisterOffset("def_cfa", *Instruction->getIf()); - case MachineCFIOperation::RelativeOffset: - return PrintRegisterOffset("rel_offset", *Instruction->getIf()); - case MachineCFIOperation::AdjustCFAOffset: - Output.append("adjust_cfa_offset "); - Output.append(std::to_string(Instruction->getIf()->Offset)); - return {}; - case MachineCFIOperation::Escape: { - Output.append("escape "); - constexpr std::string_view Hex = "0123456789ABCDEF"; - const auto &Bytes = Instruction->getIf()->Bytes; - for (std::size_t ByteIndex = 0; ByteIndex < Bytes.size(); ++ByteIndex) { - if (ByteIndex != 0) - Output.append(", "); - Output.append("0x"); - Output.push_back(Hex[Bytes[ByteIndex] >> 4U]); - Output.push_back(Hex[Bytes[ByteIndex] & 0xFU]); - } - return {}; - } - case MachineCFIOperation::Restore: - Output.append("restore "); - return PrintRegister(Instruction->getIf()->Register); - case MachineCFIOperation::Undefined: - Output.append("undefined "); - return PrintRegister(Instruction->getIf()->Register); - case MachineCFIOperation::Register: { - const auto &Rule = *Instruction->getIf(); - Output.append("register "); - if (auto Printed = PrintRegister(Rule.Register); !Printed) - return Printed; - Output.append(", "); - return PrintRegister(Rule.SourceRegister); - } - case MachineCFIOperation::WindowSave: - Output.append("window_save"); - return {}; - case MachineCFIOperation::NegateReturnAddressState: - Output.append("negate_ra_sign_state"); - return {}; - case MachineCFIOperation::NegateReturnAddressStateWithPC: - Output.append("negate_ra_sign_state_with_pc"); - return {}; - case MachineCFIOperation::GNUArgsSize: - case MachineCFIOperation::Label: - case MachineCFIOperation::ValueOffset: - return makeUnexpected( - ErrorCode::Unsupported, - "CFI operation has no textual representation in the fixed LLVM MIR baseline"); - case MachineCFIOperation::LLVMRegisterPair: { - const auto &Rule = *Instruction->getIf(); - Output.append("llvm_register_pair "); - if (auto Printed = PrintRegister(Rule.Register); !Printed) - return Printed; - Output.append(", "); - if (auto Printed = PrintRegister(Rule.FirstRegister); !Printed) - return Printed; - Output.append(", "); - Output.append(std::to_string(Rule.FirstSizeInBits)); - Output.append(", "); - if (auto Printed = PrintRegister(Rule.SecondRegister); !Printed) - return Printed; - Output.append(", "); - Output.append(std::to_string(Rule.SecondSizeInBits)); - return {}; - } - case MachineCFIOperation::LLVMVectorRegisters: { - const auto &Rule = *Instruction->getIf(); - Output.append("llvm_vector_registers "); - if (auto Printed = PrintRegister(Rule.Register); !Printed) - return Printed; - for (const MachineCFIVectorRegisterLane &Lane : Rule.VectorRegisters) { - Output.append(", "); - if (auto Printed = PrintRegister(Lane.Register); !Printed) - return Printed; - Output.append(", "); - Output.append(std::to_string(Lane.Lane)); - Output.append(", "); - Output.append(std::to_string(Lane.SizeInBits)); - } - return {}; - } - case MachineCFIOperation::LLVMVectorOffset: { - const auto &Rule = *Instruction->getIf(); - Output.append("llvm_vector_offset "); - if (auto Printed = PrintRegister(Rule.Register); !Printed) - return Printed; - Output.append(", "); - Output.append(std::to_string(Rule.RegisterSizeInBits)); - Output.append(", "); - if (auto Printed = PrintRegister(Rule.MaskRegister); !Printed) - return Printed; - Output.append(", "); - Output.append(std::to_string(Rule.MaskRegisterSizeInBits)); - Output.append(", "); - Output.append(std::to_string(Rule.Offset)); - return {}; - } - case MachineCFIOperation::LLVMVectorRegisterMask: { - const auto &Rule = *Instruction->getIf(); - Output.append("llvm_vector_register_mask "); - if (auto Printed = PrintRegister(Rule.Register); !Printed) - return Printed; - Output.append(", "); - if (auto Printed = PrintRegister(Rule.SpillRegister); !Printed) - return Printed; - Output.append(", "); - Output.append(std::to_string(Rule.SpillRegisterLaneSizeInBits)); - Output.append(", "); - if (auto Printed = PrintRegister(Rule.MaskRegister); !Printed) - return Printed; - Output.append(", "); - Output.append(std::to_string(Rule.MaskRegisterSizeInBits)); - return {}; - } - case MachineCFIOperation::Count: - return makeUnexpected(ErrorCode::VerificationError, "CFI instruction has an invalid operation"); - } - return makeUnexpected(ErrorCode::InternalError, "CFI printer did not handle an operation"); -} - -Expected BodyPrinter::printOperand(const MachineOperand &Operand, bool IsExplicitDef, - bool PrintVirtualType) { - if (Operand.isRegister()) - return printRegisterOperand(Operand, IsExplicitDef, PrintVirtualType); - if (auto Flags = printTargetFlags(Operand.targetFlags()); !Flags) - return Flags; - - switch (Operand.kind()) { - case MachineOperand::Kind::Register: - return makeUnexpected(ErrorCode::InternalError, - "register operand reached the non-register printer"); - case MachineOperand::Kind::Immediate: - Output.append(std::to_string(*Operand.getIf())); - return {}; - case MachineOperand::Kind::IntegerImmediate: { - const IntegerImmediate &Value = Operand.getIf()->Value; - Output.push_back('i'); - Output.append(std::to_string(Value.bitWidth())); - Output.push_back(' '); - if (Value.bitWidth() == 1U) - Output.append(Value.isZero() ? "false" : "true"); - else - Output.append(Value.toSignedDecimal()); - return {}; - } - case MachineOperand::Kind::FloatingImmediate: { - const FloatingPointBits &Bits = Operand.getIf()->Bits; - auto Constant = GenericConstantData::floating(Bits); - if (!Constant) - return std::unexpected(Constant.error()); - auto Text = printLLVMConstant(*Constant); - if (!Text) - return std::unexpected(Text.error()); - Output.append(*Text); - return {}; - } - case MachineOperand::Kind::BasicBlock: { - const MachineBasicBlock *Block = Operand.getIf()->Block; - if (Block == nullptr || !BlockIDs.contains(Block)) { - return makeUnexpected(ErrorCode::VerificationError, - "basic-block operand references an unlinked block"); - } - appendBlockReference(*Block); - return {}; - } - case MachineOperand::Kind::FrameIndex: { - const auto &Frame = *Operand.getIf(); - auto ID = frameID(Frame.Index); - if (!ID) - return std::unexpected(ID.error()); - const auto Stack = std::ranges::find_if( - Context.StackObjects, - [Index = Frame.Index](const MIFrameObjectSlot &Slot) { return Slot.Index == Index; }); - if (Stack != Context.StackObjects.end()) { - Output.append("%stack."); - Output.append(std::to_string(*ID)); - if (Stack->Name) { - Output.push_back('.'); - Output.append(quoteName(*Stack->Name)); - } - } else { - Output.append("%fixed-stack."); - Output.append(std::to_string(*ID)); - } - appendOffset(Frame.Offset); - return {}; - } - case MachineOperand::Kind::ConstantPoolIndex: { - const auto &Constant = *Operand.getIf(); - auto ID = constantPoolID(Constant.Index); - if (!ID) - return std::unexpected(ID.error()); - Output.append("%const."); - Output.append(std::to_string(*ID)); - appendOffset(Constant.Offset); - return {}; - } - case MachineOperand::Kind::JumpTableIndex: { - auto ID = jumpTableID(Operand.getIf()->Index); - if (!ID) - return std::unexpected(ID.error()); - Output.append("%jump-table."); - Output.append(std::to_string(*ID)); - return {}; - } - case MachineOperand::Kind::GlobalSymbol: { - const auto &Global = *Operand.getIf(); - Output.push_back('@'); - Output.append(quoteName(Global.Name)); - appendOffset(Global.Offset); - return {}; - } - case MachineOperand::Kind::ExternalSymbol: { - const auto &External = *Operand.getIf(); - Output.push_back('&'); - Output.append(quoteName(External.Name)); - appendOffset(External.Offset); - return {}; - } - case MachineOperand::Kind::BlockAddress: { - const auto &Address = *Operand.getIf(); - Output.append("blockaddress(@"); - Output.append(quoteName(Address.FunctionName)); - Output.append(", %ir-block."); - Output.append(quoteName(Address.BlockName)); - Output.push_back(')'); - appendOffset(Address.Offset); - return {}; - } - case MachineOperand::Kind::RegisterMask: { - const RegisterMask &Mask = *Operand.getIf()->Mask; - if (const auto Name = Target.mirText().registerMaskName(Mask)) { - Output.append(*Name); - return {}; - } - Output.append("CustomRegMask("); - bool First = true; - for (const PhysicalRegisterDesc &Register : Target.registers().physicalRegisters()) { - if (!Mask.preserves(Register.Reg)) - continue; - if (!First) - Output.append(", "); - First = false; - if (auto Printed = appendPhysicalRegister(Register.Reg); !Printed) - return Printed; - } - Output.push_back(')'); - return {}; - } - case MachineOperand::Kind::LiveOutRegisterMask: { - const RegisterMask &Mask = *Operand.getIf()->Mask; - Output.append("liveout("); - bool First = true; - for (const PhysicalRegisterDesc &Register : Target.registers().physicalRegisters()) { - if (!Mask.preserves(Register.Reg)) - continue; - if (!First) - Output.append(", "); - First = false; - if (auto Printed = appendPhysicalRegister(Register.Reg); !Printed) - return Printed; - } - Output.push_back(')'); - return {}; - } - case MachineOperand::Kind::Metadata: { - const MachineMetadataValue &Value = Operand.getIf()->Value; - if (const auto *Reference = Value.reference()) - return printMetadataReference(*Reference); - const MachineDIExpression *Expression = Value.expression(); - if (Expression == nullptr) { - return makeUnexpected(ErrorCode::VerificationError, - "metadata operand has no printable value"); - } - Output.append(formatDIExpression(Expression->elements())); - return {}; - } - case MachineOperand::Kind::TargetIndex: { - const auto &TargetIndexValue = *Operand.getIf(); - const auto Name = Target.mirText().targetIndexName(TargetIndexValue.Index); - if (!Name) - return makeUnexpected(ErrorCode::VerificationError, "target index has no MIR name"); - Output.append("target-index("); - Output.append(*Name); - Output.push_back(')'); - appendOffset(TargetIndexValue.Offset); - return {}; - } - case MachineOperand::Kind::MCSymbol: { - const auto &Symbol = *Operand.getIf(); - Output.append("'); - appendOffset(Symbol.Offset); - return {}; - } - case MachineOperand::Kind::CFIIndex: - return printCFIInstruction(Operand.getIf()->Index); - case MachineOperand::Kind::Intrinsic: { - const auto Name = Target.mirText().intrinsicName(Operand.getIf()->ID); - if (!Name) - return makeUnexpected(ErrorCode::VerificationError, "intrinsic has no MIR name"); - Output.append("intrinsic(@"); - Output.append(*Name); - Output.push_back(')'); - return {}; - } - case MachineOperand::Kind::Predicate: { - const PredicateOperand &Predicate = *Operand.getIf(); - if (const auto *Integer = Predicate.integerPredicate()) { - Output.append("intpred("); - Output.append(integerPredicateName(*Integer)); - } else { - Output.append("floatpred("); - Output.append(floatingPredicateName(*Predicate.floatingPredicate())); - } - Output.push_back(')'); - return {}; - } - case MachineOperand::Kind::ShuffleMask: { - Output.append("shufflemask("); - const auto &Elements = Operand.getIf()->Elements; - for (std::size_t Index = 0; Index < Elements.size(); ++Index) { - if (Index != 0) - Output.append(", "); - if (Elements[Index] == ShuffleMaskOperand::Undef) - Output.append("undef"); - else - Output.append(std::to_string(Elements[Index])); - } - Output.push_back(')'); - return {}; - } - case MachineOperand::Kind::DebugInstrRef: { - const auto &Reference = *Operand.getIf(); - Output.append("dbg-instr-ref("); - Output.append(std::to_string(Reference.InstructionNumber)); - Output.append(", "); - Output.append(std::to_string(Reference.OperandNumber)); - Output.push_back(')'); - return {}; - } - case MachineOperand::Kind::LaneMask: - Output.append("lanemask(0x"); - Output.append( - hexadecimalWords(std::array{Operand.getIf()->Mask.rawValue()}, 64)); - Output.push_back(')'); - return {}; - case MachineOperand::Kind::Count: - return makeUnexpected(ErrorCode::VerificationError, "machine operand has an invalid kind"); - } - return makeUnexpected(ErrorCode::InternalError, "machine operand printer did not handle a kind"); -} - -Expected BodyPrinter::printInlineAsmExtraInfo(const MachineOperand &Operand) { - const auto *Value = Operand.getIf(); - if (Value == nullptr || Operand.targetFlags() != 0) { - return makeUnexpected(ErrorCode::VerificationError, - "inline assembly extra info must be an unflagged immediate"); - } - const InlineAsmExtraInfo ExtraInfo(static_cast(*Value)); - const std::vector Names = ExtraInfo.names(); - for (std::size_t Index = 0; Index < Names.size(); ++Index) { - if (Index != 0) - Output.push_back(' '); - Output.append(Names[Index]); - } - return {}; -} - -Expected BodyPrinter::printInlineAsmOperandFlag(const MachineOperand &Operand) { - const auto *Value = Operand.getIf(); - if (Value == nullptr || *Value < 0 || - static_cast(*Value) > std::numeric_limits::max() || - Operand.targetFlags() != 0) { - return makeUnexpected(ErrorCode::VerificationError, - "inline assembly operand flag must be an unflagged 32-bit immediate"); - } - - const InlineAsmOperandFlag Flag(static_cast(*Value)); - const std::string_view KindName = InlineAsmOperandFlag::kindName(Flag.kind()); - if (KindName.empty() || Flag.operandCount() == 0) { - return makeUnexpected(ErrorCode::VerificationError, - "inline assembly operand flag has an invalid kind or operand count"); - } - Output.append(KindName); - - if (const auto MatchingOperand = Flag.matchingOperand()) { - Output.append(" tiedto:$"); - Output.append(std::to_string(*MatchingOperand)); - return Flag.operandCount(); - } - if (Flag.kind() == InlineAsmOperandKind::Memory || - Flag.kind() == InlineAsmOperandKind::Function) { - const auto Constraint = InlineAsmOperandFlag::memoryConstraintName(Flag.memoryConstraint()); - if (Constraint) { - Output.push_back(':'); - Output.append(*Constraint); - } - return Flag.operandCount(); - } - if (Flag.isRegisterKind()) { - if (const auto RegisterClass = Flag.registerClass()) { - const auto Name = - Target.registers().registerClassMIRName(RegisterClassID::fromValue(*RegisterClass)); - if (!Name) { - return makeUnexpected(ErrorCode::VerificationError, - "inline assembly operand flag references an unknown register class"); - } - Output.push_back(':'); - Output.append(*Name); - } - } - return Flag.operandCount(); -} - -Expected BodyPrinter::printMemoryBase(const MemoryBase &Base, std::int64_t Offset) { - switch (Base.kind()) { - case MemoryBase::Kind::Unknown: - Output.append("unknown-address"); - break; - case MemoryBase::Kind::FrameIndex: { - const auto Frame = Base.frameIndex(); - if (!Frame) { - return makeUnexpected(ErrorCode::VerificationError, - "frame-index memory base has no frame index"); - } - const FrameIndex Index = *Frame; - auto ID = frameID(Index); - if (!ID) - return std::unexpected(ID.error()); - const auto Stack = - std::ranges::find_if(Context.StackObjects, [Index](const MIFrameObjectSlot &Slot) { - return Slot.Index == Index; - }); - if (Stack != Context.StackObjects.end()) { - Output.append("%stack."); - Output.append(std::to_string(*ID)); - if (Stack->Name) { - Output.push_back('.'); - Output.append(quoteName(*Stack->Name)); - } - } else { - Output.append("%fixed-stack."); - Output.append(std::to_string(*ID)); - } - break; - } - case MemoryBase::Kind::ConstantPool: { - const auto Constant = Base.constantPoolIndex(); - if (!Constant) { - return makeUnexpected(ErrorCode::VerificationError, "constant-pool memory base has no index"); - } - auto ID = constantPoolID(*Constant); - if (!ID) - return std::unexpected(ID.error()); - Output.append("%const."); - Output.append(std::to_string(*ID)); - break; - } - case MemoryBase::Kind::JumpTable: { - const auto JumpTable = Base.jumpTableIndex(); - if (!JumpTable) { - return makeUnexpected(ErrorCode::VerificationError, "jump-table memory base has no index"); - } - auto ID = jumpTableID(*JumpTable); - if (!ID) - return std::unexpected(ID.error()); - Output.append("%jump-table."); - Output.append(std::to_string(*ID)); - break; - } - case MemoryBase::Kind::GlobalSymbol: { - const auto &Name = Base.symbolName(); - if (!Name) { - return makeUnexpected(ErrorCode::VerificationError, "global memory base has no symbol name"); - } - Output.push_back('@'); - Output.append(quoteName(*Name)); - break; - } - case MemoryBase::Kind::ExternalSymbol: { - const auto &Name = Base.symbolName(); - if (!Name) { - return makeUnexpected(ErrorCode::VerificationError, - "external memory base has no symbol name"); - } - Output.push_back('&'); - Output.append(quoteName(*Name)); - break; - } - case MemoryBase::Kind::IRValue: { - const auto &Name = Base.symbolName(); - if (!Name) { - return makeUnexpected(ErrorCode::VerificationError, "IR-value memory base has no value name"); - } - Output.append("%ir."); - Output.append(quoteName(*Name)); - break; - } - case MemoryBase::Kind::IRConstant: { - const GenericConstantData *Constant = Base.constantValue(); - if (Constant == nullptr) { - return makeUnexpected(ErrorCode::VerificationError, - "IR-constant memory base has no constant value"); - } - auto Printed = printLLVMConstant(*Constant); - if (!Printed) - return std::unexpected(Printed.error()); - Output.push_back('`'); - Output.append(*Printed); - Output.push_back('`'); - break; - } - case MemoryBase::Kind::Stack: - Output.append("stack"); - break; - case MemoryBase::Kind::GOT: - Output.append("got"); - break; - case MemoryBase::Kind::ConstantPoolPseudo: - Output.append("constant-pool"); - break; - case MemoryBase::Kind::JumpTablePseudo: - Output.append("jump-table"); - break; - case MemoryBase::Kind::CallEntryGlobal: { - const auto &Name = Base.symbolName(); - if (!Name) { - return makeUnexpected(ErrorCode::VerificationError, - "global call-entry memory base has no symbol name"); - } - Output.append("call-entry @"); - Output.append(quoteName(*Name)); - break; - } - case MemoryBase::Kind::CallEntryExternal: { - const auto &Name = Base.symbolName(); - if (!Name) { - return makeUnexpected(ErrorCode::VerificationError, - "external call-entry memory base has no symbol name"); - } - Output.append("call-entry &"); - Output.append(quoteName(*Name)); - break; - } - case MemoryBase::Kind::Custom: { - const auto &Name = Base.symbolName(); - if (!Name) { - return makeUnexpected(ErrorCode::VerificationError, "custom memory base has no name"); - } - Output.append("custom "); - Output.append(quoteName(*Name)); - break; - } - } - appendOffset(Offset); - return {}; -} - -Expected BodyPrinter::printMemoryOperand(const MachineMemOperand &Operand) { - const MachineMemOperandDesc &Description = Operand.description(); - Output.push_back('('); - if (Description.Accesses.has(MemoryAccessFlag::Volatile)) - Output.append("volatile "); - if (Description.Accesses.has(MemoryAccessFlag::NonTemporal)) - Output.append("non-temporal "); - if (Description.Accesses.has(MemoryAccessFlag::Dereferenceable)) - Output.append("dereferenceable "); - if (Description.Accesses.has(MemoryAccessFlag::Invariant)) - Output.append("invariant "); - if (Description.TargetFlags != 0) { - auto Names = Target.mirText().machineMemOperandFlagNames(Description.TargetFlags); - if (!Names) - return std::unexpected(Names.error()); - for (std::string_view Name : *Names) { - Output.append(quoteName(Name)); - Output.push_back(' '); - } - } - - if (Operand.isLoad()) - Output.append("load"); - else - Output.append("store"); - if (Operand.isLoad() && Operand.isStore()) - Output.append(" store"); - - if (Description.SyncScope.value() != 0) { - const auto Scope = - std::ranges::find_if(Context.SyncScopes, [&Description](const MISyncScopeSlot &Slot) { - return Slot.ID == Description.SyncScope; - }); - if (Scope == Context.SyncScopes.end()) { - return makeUnexpected(ErrorCode::VerificationError, "synchronization scope has no MIR name"); - } - Output.append(" syncscope("); - Output.append(quoteString(Scope->Name)); - Output.push_back(')'); - } - if (Description.SuccessOrdering != AtomicOrdering::NotAtomic) { - Output.push_back(' '); - Output.append(atomicOrderingName(Description.SuccessOrdering)); - } - if (Description.FailureOrdering != AtomicOrdering::NotAtomic) { - Output.push_back(' '); - Output.append(atomicOrderingName(Description.FailureOrdering)); - } - - Output.push_back(' '); - if (Description.MemoryType) { - auto Type = llvmMIRLowLevelType(*Description.MemoryType); - if (!Type) - return std::unexpected(Type.error()); - Output.push_back('('); - Output.append(*Type); - Output.push_back(')'); - } else { - switch (Description.Size.kind()) { - case MemorySize::Kind::Unknown: - Output.append("unknown-size"); - break; - case MemorySize::Kind::Fixed: - Output.append(std::to_string(Description.Size.minimumBytes())); - break; - case MemorySize::Kind::Scalable: - Output.append("()"); - break; - } - } - - const std::string_view Preposition = - Operand.isLoad() && Operand.isStore() ? " on " : (Operand.isLoad() ? " from " : " into "); - if (Description.Base.kind() != MemoryBase::Kind::Unknown || Description.Offset != 0) { - Output.append(Preposition); - if (auto Printed = printMemoryBase(Description.Base, Description.Offset); !Printed) { - return Printed; - } - } - - if (!Description.Size.isKnown() || - (Description.Size.minimumBytes() != 0 && - Description.Align.bytes() != Description.Size.minimumBytes())) { - Output.append(", align "); - Output.append(std::to_string(Description.Align.bytes())); - } - if (Description.BaseAlign && *Description.BaseAlign != Description.Align) { - Output.append(", basealign "); - Output.append(std::to_string(Description.BaseAlign->bytes())); - } - if (Description.AddressSpace != 0) { - Output.append(", addrspace "); - Output.append(std::to_string(Description.AddressSpace)); - } - - const auto PrintMetadata = - [this](std::string_view Name, - const std::optional &Reference) -> Expected { - if (!Reference) - return {}; - Output.append(", "); - Output.append(Name); - Output.push_back(' '); - return printMetadataReference(*Reference); - }; - if (auto Printed = PrintMetadata("!tbaa", Description.TBAA); !Printed) - return Printed; - if (auto Printed = PrintMetadata("!alias.scope", Description.AliasScope); !Printed) { - return Printed; - } - if (auto Printed = PrintMetadata("!noalias", Description.NoAlias); !Printed) - return Printed; - if (auto Printed = PrintMetadata("!noalias.addrspace", Description.NoAliasAddressSpace); - !Printed) { - return Printed; - } - if (auto Printed = PrintMetadata("!range", Description.RangeMetadata); !Printed) { - return Printed; - } - Output.push_back(')'); - return {}; -} - -Expected BodyPrinter::printInstruction(const MachineInstr &Instruction) { - const InstructionDescriptor *Descriptor = Target.instructions().descriptor(Instruction.opcode()); - if (Descriptor == nullptr) { - return makeUnexpected(ErrorCode::VerificationError, - "machine instruction has no target descriptor"); - } - std::size_t ExplicitDefs = 0; - while (ExplicitDefs < Instruction.operandCount()) { - const MachineOperand *Operand = Instruction.operandAt(ExplicitDefs); - if (Operand == nullptr || !Operand->isRegister() || Operand->isImplicit() || - !Operand->isDef() || Operand->isUse()) - break; - ++ExplicitDefs; - } - - Output.append(" "); - for (std::size_t Index = 0; Index < ExplicitDefs; ++Index) { - if (Index != 0) - Output.append(", "); - if (auto Printed = printOperand(*Instruction.operandAt(Index), true); !Printed) { - return Printed; - } - } - if (ExplicitDefs != 0) - Output.append(" = "); - - const MachineInstrFlags Flags = Instruction.flags(); - constexpr std::array FlagSpellings{ - std::pair{MachineInstrFlag::FrameSetup, std::string_view{"frame-setup"}}, - std::pair{MachineInstrFlag::FrameDestroy, std::string_view{"frame-destroy"}}, - std::pair{MachineInstrFlag::FastMathNoNaNs, std::string_view{"nnan"}}, - std::pair{MachineInstrFlag::FastMathNoInfs, std::string_view{"ninf"}}, - std::pair{MachineInstrFlag::FastMathNoSignedZeros, std::string_view{"nsz"}}, - std::pair{MachineInstrFlag::FastMathAllowReciprocal, std::string_view{"arcp"}}, - std::pair{MachineInstrFlag::FastMathAllowContract, std::string_view{"contract"}}, - std::pair{MachineInstrFlag::FastMathApproxFunc, std::string_view{"afn"}}, - std::pair{MachineInstrFlag::FastMathAllowReassoc, std::string_view{"reassoc"}}, - std::pair{MachineInstrFlag::NoUnsignedWrap, std::string_view{"nuw"}}, - std::pair{MachineInstrFlag::NoSignedWrap, std::string_view{"nsw"}}, - std::pair{MachineInstrFlag::NoUSWrap, std::string_view{"nusw"}}, - std::pair{MachineInstrFlag::Exact, std::string_view{"exact"}}, - std::pair{MachineInstrFlag::NoFPExcept, std::string_view{"nofpexcept"}}, - std::pair{MachineInstrFlag::Unpredictable, std::string_view{"unpredictable"}}, - std::pair{MachineInstrFlag::NoConvergent, std::string_view{"noconvergent"}}, - std::pair{MachineInstrFlag::NonNeg, std::string_view{"nneg"}}, - std::pair{MachineInstrFlag::Disjoint, std::string_view{"disjoint"}}, - std::pair{MachineInstrFlag::SameSign, std::string_view{"samesign"}}, - std::pair{MachineInstrFlag::InBounds, std::string_view{"inbounds"}}, - }; - for (const auto &[Flag, Spelling] : FlagSpellings) { - if (Flags.has(Flag)) { - Output.append(Spelling); - Output.push_back(' '); - } - } - Output.append(Descriptor->Name); - - bool HasPrintedTail = false; - const bool IsInlineAsm = Descriptor->Name == "INLINEASM" || Descriptor->Name == "INLINEASM_BR"; - std::size_t InlineAsmPayloadRemaining = 0; - for (std::size_t Index = ExplicitDefs; Index < Instruction.operandCount(); ++Index) { - Output.append(HasPrintedTail ? ", " : " "); - HasPrintedTail = true; - const MachineOperand &Operand = *Instruction.operandAt(Index); - if (IsInlineAsm && Index == InlineAsmExtraInfoOperandIndex) { - auto Printed = printInlineAsmExtraInfo(Operand); - if (!Printed) - return Printed; - continue; - } - if (IsInlineAsm && Index >= InlineAsmFirstConstraintOperandIndex && - InlineAsmPayloadRemaining == 0) { - auto OperandCount = printInlineAsmOperandFlag(Operand); - if (!OperandCount) - return std::unexpected(OperandCount.error()); - InlineAsmPayloadRemaining = *OperandCount; - continue; - } - bool PrintVirtualType = true; - if (Descriptor->Flags.has(InstructionFlag::Generic) && - !Descriptor->Flags.has(InstructionFlag::Phi) && Index < Descriptor->Operands.size()) { - const std::optional &UseConstraint = - Descriptor->Operands[Index].TypeConstraint; - if (UseConstraint) { - for (std::size_t DefIndex = 0; DefIndex < ExplicitDefs; ++DefIndex) { - if (DefIndex >= Descriptor->Operands.size()) - break; - const std::optional &DefConstraint = - Descriptor->Operands[DefIndex].TypeConstraint; - if (DefConstraint && DefConstraint->EqualityGroup == UseConstraint->EqualityGroup) { - PrintVirtualType = false; - break; - } - } - } else if (const Register *UseRegister = Operand.getIf(); - UseRegister != nullptr && UseRegister->isVirtual()) { - const auto UseInfo = Function.registerInfo().virtualRegisterInfo(*UseRegister); - for (std::size_t DefIndex = 0; DefIndex < ExplicitDefs && UseInfo && UseInfo->get().Type; - ++DefIndex) { - if (DefIndex >= Descriptor->Operands.size() || - Descriptor->Operands[DefIndex].TypeConstraint) - continue; - const MachineOperand *DefOperand = Instruction.operandAt(DefIndex); - const Register *DefRegister = - DefOperand == nullptr ? nullptr : DefOperand->getIf(); - if (DefRegister == nullptr || !DefRegister->isVirtual()) - continue; - const auto DefInfo = Function.registerInfo().virtualRegisterInfo(*DefRegister); - if (DefInfo && DefInfo->get().Type == UseInfo->get().Type) { - PrintVirtualType = false; - break; - } - } - } - } - auto Printed = printOperand(Operand, false, PrintVirtualType); - if (!Printed) - return Printed; - if (IsInlineAsm && InlineAsmPayloadRemaining != 0) - --InlineAsmPayloadRemaining; - } - if (InlineAsmPayloadRemaining != 0) - return makeUnexpected(ErrorCode::VerificationError, - "inline assembly operand flag has missing payload operands"); - - const auto BeginAttachment = [this, &HasPrintedTail](std::string_view Name) { - Output.append(HasPrintedTail ? ", " : " "); - HasPrintedTail = true; - Output.append(Name); - Output.push_back(' '); - }; - if (Instruction.preInstrSymbol()) { - BeginAttachment("pre-instr-symbol"); - Output.append("'); - } - if (Instruction.postInstrSymbol()) { - BeginAttachment("post-instr-symbol"); - Output.append("'); - } - if (Instruction.heapAllocMarker()) { - BeginAttachment("heap-alloc-marker"); - if (auto Printed = printMetadataReference(*Instruction.heapAllocMarker()); !Printed) { - return Printed; - } - } - if (Instruction.pcSections()) { - BeginAttachment("pcsections"); - if (auto Printed = printMetadataReference(*Instruction.pcSections()); !Printed) { - return Printed; - } - } - if (Instruction.mmra()) { - BeginAttachment("mmra"); - if (auto Printed = printMetadataReference(*Instruction.mmra()); !Printed) - return Printed; - } - if (Instruction.cfiType()) { - BeginAttachment("cfi-type"); - Output.append(std::to_string(*Instruction.cfiType())); - } - if (Instruction.deactivationSymbol()) { - BeginAttachment("deactivation-symbol"); - Output.push_back('@'); - Output.append(quoteName(*Instruction.deactivationSymbol())); - } - if (Instruction.debugInstrNumber() != 0) { - BeginAttachment("debug-instr-number"); - Output.append(std::to_string(Instruction.debugInstrNumber())); - } - if (Instruction.debugLocation()) { - BeginAttachment("debug-location"); - if (const MachineMetadataReference *Reference = Instruction.debugLocation()->reference()) { - if (auto Printed = printMetadataReference(*Reference); !Printed) - return Printed; - } else if (const MachineDILocation *Location = Instruction.debugLocation()->inlineLocation()) { - if (auto Printed = printDILocation(*Location, 0); !Printed) - return Printed; - } else { - return makeUnexpected(ErrorCode::VerificationError, - "machine debug location has no printable value"); - } - } - - if (!Instruction.memOperands().empty()) { - Output.append(" :: "); - for (std::size_t Index = 0; Index < Instruction.memOperands().size(); ++Index) { - if (Index != 0) - Output.append(", "); - if (auto Printed = printMemoryOperand(*Instruction.memOperands()[Index]); !Printed) { - return Printed; - } - } - } - return {}; -} - -Expected BodyPrinter::printBlock(const MachineBasicBlock &Block) { - auto ID = blockID(Block); - if (!ID) - return std::unexpected(ID.error()); - Output.append("bb."); - Output.append(std::to_string(*ID)); - const auto &Name = Block.attributes().name(); - if (Name) { - Output.push_back('.'); - Output.append(quoteName(*Name)); - } - - std::vector Attributes; - const auto AddFlag = [&Block, &Attributes](MachineBasicBlockAttribute Attribute, - std::string_view Spelling) { - if (Block.attributes().has(Attribute)) - Attributes.emplace_back(Spelling); - }; - AddFlag(MachineBasicBlockAttribute::MachineBlockAddressTaken, "machine-block-address-taken"); - if (Block.attributes().has(MachineBasicBlockAttribute::IRBlockAddressTaken)) { - std::string Attribute = "ir-block-address-taken"; - const auto &IRBlockName = Block.attributes().irBlockName(); - if (IRBlockName) { - Attribute.append(" %ir-block."); - Attribute.append(quoteName(*IRBlockName)); - } - Attributes.push_back(std::move(Attribute)); - } else { - const auto &IRBlockName = Block.attributes().irBlockName(); - if (IRBlockName) - Attributes.push_back("%ir-block." + quoteName(*IRBlockName)); - } - AddFlag(MachineBasicBlockAttribute::LandingPad, "landing-pad"); - AddFlag(MachineBasicBlockAttribute::InlineAsmBrIndirectTarget, "inlineasm-br-indirect-target"); - AddFlag(MachineBasicBlockAttribute::EHFuncletEntry, "ehfunclet-entry"); - AddFlag(MachineBasicBlockAttribute::EHScopeEntry, "ehscope-entry"); - if (Block.attributes().alignment().bytes() != 1) { - Attributes.push_back("align " + std::to_string(Block.attributes().alignment().bytes())); - } - const MachineBasicBlockSectionID Section = Block.attributes().sectionID(); - if (!Section.isDefault()) { - std::string Value; - if (Section.kind() == MachineBasicBlockSectionKind::Exception) - Value = "Exception"; - else if (Section.kind() == MachineBasicBlockSectionKind::Cold) - Value = "Cold"; - else - Value = std::to_string(Section.number()); - Attributes.push_back("bbsections " + Value); - } - const auto &ProfileID = Block.attributes().profileID(); - if (ProfileID) { - const MachineBasicBlockProfileID Profile = *ProfileID; - Attributes.push_back("bb_id " + std::to_string(Profile.BaseID) + "." + - std::to_string(Profile.CloneID)); - } - if (Block.attributes().callFrameSize() != 0) { - Attributes.push_back("call-frame-size " + std::to_string(Block.attributes().callFrameSize())); - } - if (!Attributes.empty()) { - Output.append(" ("); - for (std::size_t Index = 0; Index < Attributes.size(); ++Index) { - if (Index != 0) - Output.append(", "); - Output.append(Attributes[Index]); - } - Output.push_back(')'); - } - Output.append(":\n"); - - bool HasLineAttributes = false; - if (!Block.successors().empty()) { - Output.append(" successors: "); - for (std::size_t Index = 0; Index < Block.successors().size(); ++Index) { - if (Index != 0) - Output.append(", "); - const MachineSuccessor &Successor = Block.successors()[Index]; - if (Successor.Block == nullptr || !BlockIDs.contains(Successor.Block)) { - return makeUnexpected(ErrorCode::VerificationError, - "successor references an unlinked block"); - } - appendBlockReference(*Successor.Block); - Output.append("(0x"); - Output.append(hexadecimalWords( - std::array{static_cast(Successor.Probability.rawValue())}, 32)); - Output.push_back(')'); - } - Output.push_back('\n'); - HasLineAttributes = true; - } - if (!Block.liveIns().empty()) { - Output.append(" liveins: "); - for (std::size_t Index = 0; Index < Block.liveIns().size(); ++Index) { - if (Index != 0) - Output.append(", "); - const MachineBasicBlockLiveIn &LiveIn = Block.liveIns()[Index]; - if (auto Printed = appendPhysicalRegister(LiveIn.PhysReg); !Printed) - return Printed; - if (LiveIn.Lanes != LaneMask::all()) { - Output.append(": 0x"); - Output.append(hexadecimalWords(std::array{LiveIn.Lanes.rawValue()}, 64)); - } - } - Output.push_back('\n'); - HasLineAttributes = true; - } - - if (HasLineAttributes && Block.firstInstruction() != nullptr) - Output.push_back('\n'); - - bool InBundle = false; - for (const MachineInstr *Instruction = Block.firstInstruction(); Instruction != nullptr; - Instruction = Instruction->next()) { - if (auto Printed = printInstruction(*Instruction); !Printed) - return Printed; - const bool HasPredecessor = Instruction->flags().has(MachineInstrFlag::BundledWithPred); - const bool HasSuccessor = Instruction->flags().has(MachineInstrFlag::BundledWithSucc); - if (!InBundle && HasSuccessor && !HasPredecessor) { - Output.append(" {"); - InBundle = true; - } - Output.push_back('\n'); - if (InBundle && !HasSuccessor) { - Output.append(" }\n"); - InBundle = false; - } - } - if (InBundle) { - return makeUnexpected(ErrorCode::VerificationError, - "unterminated instruction bundle cannot be printed"); - } - return {}; -} - -Expected BodyPrinter::run() { - auto Verification = MachineVerifier::verify( - Function, Target, - MachineVerifierOptions{ - .Mode = MachineVerifierMode::Generic, .Stage = std::nullopt, .VerifyProperties = false}); - if (!Verification) - return std::unexpected(Verification.error()); - if (auto Initialized = initialize(); !Initialized) - return std::unexpected(Initialized.error()); - - bool First = true; - for (const MachineBasicBlock *Block = Function.firstBlock(); Block != nullptr; - Block = Block->next()) { - if (!First) - Output.push_back('\n'); - First = false; - if (auto Printed = printBlock(*Block); !Printed) - return std::unexpected(Printed.error()); - } - return std::move(Output); -} - -} // namespace - -Expected printMachineFunctionBody(const MachineFunction &Function, - const TargetDescription &Target, - const MIParserContext &Context, - MIPrinterOptions Options) { - return BodyPrinter(Function, Target, Context, Options).run(); -} - -} // namespace rcc::mir diff --git a/backend/lib/MIR/MIRDataParser.cpp b/backend/lib/MIR/MIRDataParser.cpp deleted file mode 100644 index 2ac1f2d..0000000 --- a/backend/lib/MIR/MIRDataParser.cpp +++ /dev/null @@ -1,287 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "MIRDocumentParser.h" -#include "MIRDocumentReferenceValidation.h" -#include "MIRMetadataTextParser.h" -#include "MIRParserUtilities.h" -#include "rcc/CodeGen/MachineBasicBlock.h" -#include "rcc/CodeGen/MachineConstantPool.h" -#include "rcc/CodeGen/MachineFrameInfo.h" -#include "rcc/CodeGen/MachineFunction.h" -#include "rcc/CodeGen/MachineFunctionAttributes.h" -#include "rcc/CodeGen/MachineFunctionProperties.h" -#include "rcc/CodeGen/MachineFunctionRecords.h" -#include "rcc/CodeGen/MachineInstr.h" -#include "rcc/CodeGen/MachineJumpTableInfo.h" -#include "rcc/CodeGen/MachineMetadata.h" -#include "rcc/CodeGen/MachineRegisterInfo.h" -#include "rcc/CodeGen/MachineVerifier.h" -#include "rcc/CodeGen/TargetDescription.h" -#include "rcc/CodeGen/TargetMIRTextInfo.h" -#include "rcc/CodeGen/TargetRegisterInfo.h" -#include "rcc/MIR/LLVMConstantSyntax.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -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 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 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::max()) { - return true; - } - const auto TextID = static_cast(WideID); - if (!IDs.insert(TextID).second) - return fail(IDNode->range(), "duplicate constant pool ID"); - - bool IsTargetSpecific = false; - if (const yaml::Node *TargetNode = mappingValue(*Constant, "isTargetSpecific")) { - if (boolean(*TargetNode, IsTargetSpecific)) - return true; - } - if (IsTargetSpecific) { - std::string_view ValueText; - if (scalar(*ValueNode, ValueText, "target constant")) - return true; - auto TargetConstantValue = Target.mirText().parseTargetConstant(ValueText); - if (!TargetConstantValue) - return importError(ValueNode->range(), TargetConstantValue.error(), "mir-target-constant"); - auto Emission = TargetConstantValue->emit(); - if (!Emission) - return importError(ValueNode->range(), Emission.error(), "mir-target-constant"); - auto RequiredAlignment = TargetConstantValue->requiredAlignment(); - if (!RequiredAlignment) - return importError(ValueNode->range(), RequiredAlignment.error(), "mir-target-constant"); - - MachineConstantPoolEntryDesc Description; - Description.Size = Emission->Bytes.size(); - Description.Align = *RequiredAlignment; - Description.Value = std::move(*TargetConstantValue); - if (const yaml::Node *AlignmentNode = mappingValue(*Constant, "alignment")) { - if (alignment(*AlignmentNode, Description.Align, true)) - return true; - } - auto Added = Function.constantPool().addEntry(std::move(Description)); - if (!Added) - return importError(Constant->range(), Added.error()); - Context.ConstantPoolItems.push_back({.TextID = TextID, .Index = *Added}); - continue; - } - - std::string_view ValueText; - if (scalar(*ValueNode, ValueText, "LLVM constant")) - return true; - const LLVMConstantSyntaxOptions ConstantOptions{ - .DataLayout = DataLayout, - .NamedTypes = NamedTypes, - }; - auto Data = parseLLVMConstant(trim(ValueText), ConstantOptions); - if (!Data) - return importError(ValueNode->range(), Data.error(), "mir-llvm-constant"); - auto Size = llvmConstantStorageSize(Data->Type, ConstantOptions); - if (!Size) - return importError(ValueNode->range(), Size.error(), "mir-llvm-constant"); - - MachineConstantPoolEntryDesc Description; - Description.Size = *Size; - Description.Value = std::move(*Data); - if (const yaml::Node *AlignmentNode = mappingValue(*Constant, "alignment")) { - if (alignment(*AlignmentNode, Description.Align, true)) - return true; - } else { - auto Preferred = llvmConstantPreferredAlignment( - std::get(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 &Pending) { - const yaml::Node *JumpTable = mappingValue(Root, "jumpTable"); - if (JumpTable == nullptr) - return false; - if (requireKind(*JumpTable, yaml::NodeKind::Mapping, "jump table")) - return true; - constexpr std::array JumpTableKeys{ - std::string_view{"kind"}, - std::string_view{"entries"}, - }; - if (validateKeys(*JumpTable, JumpTableKeys, "jump table")) - return true; - const yaml::Node *KindNode = mappingValue(*JumpTable, "kind"); - if (KindNode == nullptr) - return fail(JumpTable->range(), "jump table requires 'kind'"); - std::string_view KindName; - if (scalar(*KindNode, KindName, "jump table kind")) - return true; - - JumpTableEntryEncoding Encoding; - std::uint8_t EntrySize = 8; - if (KindName == "block-address") - Encoding = JumpTableEntryEncoding::BlockAddress; - else if (KindName == "gp-rel64-block-address") - Encoding = JumpTableEntryEncoding::GPRelative64BlockAddress; - else if (KindName == "gp-rel32-block-address") { - Encoding = JumpTableEntryEncoding::GPRelative32BlockAddress; - EntrySize = 4; - } else if (KindName == "label-difference32") { - Encoding = JumpTableEntryEncoding::LabelDifference32; - EntrySize = 4; - } else if (KindName == "label-difference64") - Encoding = JumpTableEntryEncoding::LabelDifference64; - else if (KindName == "inline") { - Encoding = JumpTableEntryEncoding::Inline; - EntrySize = 4; - } else if (KindName == "custom32") { - Encoding = JumpTableEntryEncoding::Custom32; - EntrySize = 4; - } else { - return fail(KindNode->range(), "unknown enumerated scalar"); - } - - const yaml::Node *Entries = mappingValue(*JumpTable, "entries"); - if (Entries == nullptr) - return false; - if (requireKind(*Entries, yaml::NodeKind::Sequence, "jump table entries")) - return true; - constexpr std::array EntryKeys{ - std::string_view{"id"}, - std::string_view{"blocks"}, - }; - std::unordered_set 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::max()) { - return true; - } - const auto TextID = static_cast(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 Pending) { - for (const PendingJumpTable &Table : Pending) { - MachineJumpTableDesc Description; - Description.Encoding = Table.Encoding; - Description.EntrySize = Table.EntrySize; - Description.Align = *Alignment::create(Table.EntrySize); - for (const std::string &Text : Table.Blocks) { - MachineBasicBlock *Block = nullptr; - if (parseBlockReference(Text, Result.Source.range(0, 0), Function, Block)) - return true; - if (Block == nullptr) - return fail(Result.Source.range(0, 0), "reference to undefined machine basic block"); - Description.Targets.push_back(Block); - } - auto DefineResult = - Function.jumpTableInfo().defineReservedTable(Table.Index, std::move(Description)); - if (!DefineResult) - return importError(Result.Source.range(0, 0), DefineResult.error()); - } - return false; -} - -} // namespace rcc::mir diff --git a/backend/lib/MIR/MIRDocument.cpp b/backend/lib/MIR/MIRDocument.cpp deleted file mode 100644 index ad9ec40..0000000 --- a/backend/lib/MIR/MIRDocument.cpp +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "rcc/MIR/MIRDocument.h" - -#include "MIRDocumentParser.h" - -#include -#include - -namespace rcc::mir { - -MIRDocument::MIRDocument() = default; -MIRDocument::MIRDocument(CompilationUnit Unit) : Unit(std::move(Unit)) {} -MIRDocument::MIRDocument(MIRDocument &&) noexcept = default; -MIRDocument &MIRDocument::operator=(MIRDocument &&) noexcept = default; -MIRDocument::~MIRDocument() = default; - -MIRDocumentParseResult::MIRDocumentParseResult(MIRDocumentParseResult &&) noexcept = default; -MIRDocumentParseResult & -MIRDocumentParseResult::operator=(MIRDocumentParseResult &&) noexcept = default; -MIRDocumentParseResult::~MIRDocumentParseResult() = default; - -MIRDocumentParseResult parseMIRDocument(const TargetDescription &Target, std::string SourceName, - std::string SourceText, MIRDocumentParserOptions Options) { - return MIRDocumentParser(Target, std::move(SourceName), std::move(SourceText), std::move(Options)) - .run(); -} - -} // namespace rcc::mir diff --git a/backend/lib/MIR/MIRDocumentParser.cpp b/backend/lib/MIR/MIRDocumentParser.cpp deleted file mode 100644 index 08627d5..0000000 --- a/backend/lib/MIR/MIRDocumentParser.cpp +++ /dev/null @@ -1,398 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "MIRDocumentParser.h" - -#include "MIRDocumentReferenceValidation.h" -#include "MIRMetadataTextParser.h" -#include "MIRParserUtilities.h" -#include "rcc/CodeGen/MachineBasicBlock.h" -#include "rcc/CodeGen/MachineConstantPool.h" -#include "rcc/CodeGen/MachineFrameInfo.h" -#include "rcc/CodeGen/MachineFunction.h" -#include "rcc/CodeGen/MachineFunctionAttributes.h" -#include "rcc/CodeGen/MachineFunctionProperties.h" -#include "rcc/CodeGen/MachineFunctionRecords.h" -#include "rcc/CodeGen/MachineInstr.h" -#include "rcc/CodeGen/MachineJumpTableInfo.h" -#include "rcc/CodeGen/MachineMetadata.h" -#include "rcc/CodeGen/MachineRegisterInfo.h" -#include "rcc/CodeGen/MachineVerifier.h" -#include "rcc/CodeGen/TargetDescription.h" -#include "rcc/CodeGen/TargetMIRTextInfo.h" -#include "rcc/CodeGen/TargetRegisterInfo.h" -#include "rcc/MIR/LLVMConstantSyntax.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace rcc::mir { - -using parser_detail::parseBool; -using parser_detail::parsePrefixedID; -using parser_detail::parseSigned; -using parser_detail::parseUnsigned; -using parser_detail::parseUnsigned32; -using parser_detail::trim; - -bool MIRDocumentParser::fail(const TextRange &Range, std::string Message, std::string Category) { - if (Result.Diagnostics.empty()) { - Result.Diagnostics.push_back(TextDiagnostic{ - .Severity = DiagnosticSeverity::Error, - .Category = std::move(Category), - .Message = std::move(Message), - .SourceName = std::string(Result.Source.name()), - .Range = Range, - }); - } - return true; -} - -bool MIRDocumentParser::importError(const TextRange &Range, const Error &Failure, - std::string Category) { - return fail(Range, std::string(Failure.message()), std::move(Category)); -} - -const yaml::Node *MIRDocumentParser::mappingValue(const yaml::Node &Mapping, - std::string_view Key) const noexcept { - const auto Value = Mapping.mappingValue(Key); - return Value ? node(*Value) : nullptr; -} - -std::string_view MIRDocumentParser::effectiveDataLayout() const noexcept { - if (!Options.DataLayout.empty()) - return Options.DataLayout; - if (ActiveDocument != nullptr) { - if (const EmbeddedIRReferenceIndex *Embedded = ActiveDocument->embeddedIR()) { - if (const auto DataLayout = Embedded->dataLayout()) - return *DataLayout; - } - } - return {}; -} - -LLVMConstantSyntaxOptions MIRDocumentParser::pointerLayoutOptions() const noexcept { - return { - .DataLayout = effectiveDataLayout(), - .NamedTypes = {}, - }; -} - -bool MIRDocumentParser::requireKind(const yaml::Node &Node, yaml::NodeKind Kind, - std::string_view Description) { - if (Node.kind() == Kind) - return false; - return fail(Node.range(), std::string(Description) + " has the wrong YAML node kind"); -} - -bool MIRDocumentParser::validateKeys(const yaml::Node &Mapping, - std::span 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, - MachineFunction &Function, MIParserContext &Context, - Register &Reg) { - const auto ID = parsePrefixedID(Text, "%"); - if (!ID) - return fail(Range, "virtual register must use the '%N' MIR spelling"); - auto Found = - std::ranges::find_if(Context.VirtualRegisters, - [ID](const MIVirtualRegisterSlot &Slot) { return Slot.TextID == *ID; }); - if (Found == Context.VirtualRegisters.end()) { - auto Created = Function.registerInfo().createVirtualRegister(); - if (!Created) - return importError(Range, Created.error()); - Context.VirtualRegisters.push_back({*ID, *Created}); - Reg = *Created; - return false; - } - Reg = Found->Reg; - return false; -} - -MIRDocumentParseResult MIRDocumentParser::run() { - yaml::ParseResult YAMLResult = yaml::parse(std::string(Result.Source.name()), - std::string(Result.Source.text()), Options.YAML); - 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(fail(Result.Source.range(0, 0), "MIR stream contains no YAML documents")); - return std::move(Result); - } - - MIRDocument Staging; - ActiveDocument = &Staging; - const auto IsEmptyDocument = [this](std::size_t Index) { - const yaml::Node *Root = node(Stream->documents()[Index].Root); - return Root != nullptr && Root->kind() == yaml::NodeKind::Null && - Root->range().Begin.Offset == Root->range().End.Offset; - }; - std::size_t FirstContent = 0; - while (FirstContent < Stream->documents().size() && IsEmptyDocument(FirstContent)) - ++FirstContent; - if (FirstContent == Stream->documents().size()) { - static_cast(fail(Stream->documents().front().Range, - "MIR stream must contain at least one machine function document")); - return std::move(Result); - } - - std::size_t FirstFunction = FirstContent; - const yaml::Node *FirstRoot = node(Stream->documents()[FirstContent].Root); - if (FirstRoot != nullptr && FirstRoot->kind() == yaml::NodeKind::Scalar) { - EmbeddedIRIndexResult Embedded = indexEmbeddedIR( - std::string(Result.Source.name()) + ":embedded-ir", std::string(FirstRoot->scalar())); - if (!Embedded.succeeded()) { - const auto Diagnostics = Embedded.diagnostics(); - if (Diagnostics.empty()) { - static_cast( - fail(FirstRoot->range(), "embedded LLVM IR indexing failed", "mir-embedded-ir")); - } else { - const TextDiagnostic &Diagnostic = Diagnostics.front(); - const TextRange Range = scalarDiagnosticRange(*FirstRoot, Diagnostic.Range); - static_cast(fail(Range, Diagnostic.Message, Diagnostic.Category)); - } - return std::move(Result); - } - Staging.EmbeddedIR = std::move(Embedded).takeIndex(); - FirstFunction = FirstContent + 1; - } - - if (!effectiveDataLayout().empty()) { - auto PointerSize = llvmPointerSizeInBits(0, pointerLayoutOptions()); - if (!PointerSize) { - static_cast(importError(Stream->documents()[FirstContent].Range, PointerSize.error(), - "mir-data-layout")); - return std::move(Result); - } - } - - bool SawFunction = false; - for (std::size_t Index = FirstFunction; Index < Stream->documents().size(); ++Index) { - if (IsEmptyDocument(Index)) - continue; - const yaml::Node *Root = node(Stream->documents()[Index].Root); - if (Root == nullptr || requireKind(*Root, yaml::NodeKind::Mapping, "machine function") || - parseFunction(*Root, Staging)) { - return std::move(Result); - } - SawFunction = true; - } - if (!SawFunction) { - static_cast(fail(Stream->documents()[FirstContent].Range, - "MIR stream must contain at least one machine function document")); - return std::move(Result); - } - - auto UnitResult = Staging.Unit.verify(); - if (!UnitResult) { - static_cast( - fail(Stream->documents().back().Range, "parsed compilation unit failed verification: " + - std::string(UnitResult.error().message()))); - return std::move(Result); - } - ActiveDocument = nullptr; - Result.Document = std::move(Staging); - return std::move(Result); -} - -bool MIRDocumentParser::parseFunction(const yaml::Node &Root, MIRDocument &Document) { - constexpr std::array Keys{ - std::string_view{"name"}, - std::string_view{"alignment"}, - std::string_view{"exposesReturnsTwice"}, - std::string_view{"legalized"}, - std::string_view{"regBankSelected"}, - std::string_view{"selected"}, - std::string_view{"failedISel"}, - std::string_view{"tracksRegLiveness"}, - std::string_view{"hasWinCFI"}, - std::string_view{"noPhis"}, - std::string_view{"isSSA"}, - std::string_view{"noVRegs"}, - std::string_view{"hasFakeUses"}, - std::string_view{"callsEHReturn"}, - std::string_view{"callsUnwindInit"}, - std::string_view{"hasEHContTarget"}, - std::string_view{"hasEHScopes"}, - std::string_view{"hasEHFunclets"}, - std::string_view{"isOutlined"}, - std::string_view{"debugInstrRef"}, - std::string_view{"failsVerification"}, - std::string_view{"tracksDebugUserValues"}, - std::string_view{"registers"}, - std::string_view{"liveins"}, - std::string_view{"calleeSavedRegisters"}, - std::string_view{"frameInfo"}, - std::string_view{"fixedStack"}, - std::string_view{"stack"}, - std::string_view{"entry_values"}, - std::string_view{"callSites"}, - std::string_view{"debugValueSubstitutions"}, - std::string_view{"constants"}, - std::string_view{"machineFunctionInfo"}, - std::string_view{"jumpTable"}, - std::string_view{"machineMetadataNodes"}, - std::string_view{"calledGlobals"}, - std::string_view{"prefetch-targets"}, - std::string_view{"body"}, - }; - const yaml::Node *NameNode = mappingValue(Root, "name"); - if (NameNode == nullptr) - return fail(Root.range(), "missing required key 'name'"); - if (validateKeys(Root, Keys, "machine function")) - return true; - std::string_view Name; - if (scalar(*NameNode, Name, "machine function name")) - return true; - - const EmbeddedIRFunction *IRFunction = nullptr; - if (Document.EmbeddedIR) { - if (Name.empty()) { - std::size_t AnonymousOrdinal = 0; - for (std::size_t Index = 0; Index < Document.Unit.functionCount(); ++Index) { - const MachineFunction *Existing = Document.Unit.functionAt(Index); - if (Existing != nullptr && Existing->name().empty()) - ++AnonymousOrdinal; - } - IRFunction = Document.EmbeddedIR->unnamedFunction(AnonymousOrdinal); - } else { - IRFunction = Document.EmbeddedIR->function(Name); - } - if (IRFunction == nullptr) - return fail(NameNode->range(), - "function '" + std::string(Name) + "' isn't defined in the provided LLVM IR"); - } - - if (!Name.empty() && Document.Unit.function(Name) != nullptr) - return fail(NameNode->range(), "redefinition of machine function '" + std::string(Name) + "'"); - - auto Created = Document.Unit.createMachineFunction(std::string(Name)); - if (!Created) - return importError(NameNode->range(), Created.error()); - MachineFunction &Function = **Created; - ActiveFunction = &Function; - - MIRFunctionDocumentInfo DocumentInfo{ - .Function = Function.stableID(), - .NoPHIs = std::nullopt, - .IsSSA = std::nullopt, - .NoVRegs = std::nullopt, - .HasFakeUses = std::nullopt, - }; - MIParserContext Context; - if (IRFunction != nullptr) { - Context.HasIRBlock = [IRFunction](std::string_view BlockName) { - return IRFunction->block(BlockName) != nullptr; - }; - } - - std::vector 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 deleted file mode 100644 index 4d1d1ea..0000000 --- a/backend/lib/MIR/MIRDocumentParser.h +++ /dev/null @@ -1,132 +0,0 @@ -// 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 -#include -#include -#include -#include -#include - -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 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 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, - MachineFunction &Function, MIParserContext &Context, - Register &Reg); - [[nodiscard]] Expected - 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 &Pending); - [[nodiscard]] bool defineJumpTables(MachineFunction &Function, - std::span Pending); - [[nodiscard]] bool parseBody(const yaml::Node &Root, MachineFunction &Function, - MIParserContext &Context); - [[nodiscard]] TextRange scalarDiagnosticRange(const yaml::Node &Scalar, - const TextRange &ScalarRange) 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 &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/MIRDocumentPrinter.cpp b/backend/lib/MIR/MIRDocumentPrinter.cpp deleted file mode 100644 index 06998bb..0000000 --- a/backend/lib/MIR/MIRDocumentPrinter.cpp +++ /dev/null @@ -1,1499 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "MIRDocumentReferenceValidation.h" -#include "rcc/CodeGen/MachineBasicBlock.h" -#include "rcc/CodeGen/MachineCompilationStatus.h" -#include "rcc/CodeGen/MachineConstantPool.h" -#include "rcc/CodeGen/MachineFrameInfo.h" -#include "rcc/CodeGen/MachineFunction.h" -#include "rcc/CodeGen/MachineFunctionAttributes.h" -#include "rcc/CodeGen/MachineFunctionProperties.h" -#include "rcc/CodeGen/MachineFunctionRecords.h" -#include "rcc/CodeGen/MachineInstr.h" -#include "rcc/CodeGen/MachineJumpTableInfo.h" -#include "rcc/CodeGen/MachineMetadata.h" -#include "rcc/CodeGen/MachineRegisterInfo.h" -#include "rcc/CodeGen/TargetDescription.h" -#include "rcc/CodeGen/TargetMIRTextInfo.h" -#include "rcc/CodeGen/TargetRegisterInfo.h" -#include "rcc/MIR/LLVMConstantSyntax.h" -#include "rcc/MIR/MIDebugSyntax.h" -#include "rcc/MIR/MIRDocument.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace rcc::mir { -namespace { - -enum class YAMLQuoting : std::uint8_t { - None, - Single, - Double, -}; - -[[nodiscard]] bool isASCIIAlphanumeric(unsigned char Character) noexcept { - return (Character >= '0' && Character <= '9') || (Character >= 'A' && Character <= 'Z') || - (Character >= 'a' && Character <= 'z'); -} - -[[nodiscard]] bool isYAMLNumeric(std::string_view Value) noexcept { - if (Value.empty() || Value == "+" || Value == "-") - return false; - if (Value == ".nan" || Value == ".NaN" || Value == ".NAN") - return true; - - std::string_view Tail = Value; - if (Tail.front() == '+' || Tail.front() == '-') - Tail.remove_prefix(1); - if (Tail == ".inf" || Tail == ".Inf" || Tail == ".INF") - return true; - if (Value.starts_with("0o")) { - return Value.size() > 2U && - Value.substr(2).find_first_not_of("01234567") == std::string_view::npos; - } - if (Value.starts_with("0x")) { - return Value.size() > 2U && - Value.substr(2).find_first_not_of("0123456789abcdefABCDEF") == std::string_view::npos; - } - - if (Tail.empty() || Tail.front() == 'e' || Tail.front() == 'E') - return false; - std::size_t Position = 0; - if (Tail.front() == '.') { - ++Position; - if (Position == Tail.size() || Tail[Position] < '0' || Tail[Position] > '9') - return false; - while (Position < Tail.size() && Tail[Position] >= '0' && Tail[Position] <= '9') - ++Position; - } else { - while (Position < Tail.size() && Tail[Position] >= '0' && Tail[Position] <= '9') - ++Position; - if (Position == 0) - return false; - } - - if (Position < Tail.size() && Tail[Position] == '.') { - ++Position; - while (Position < Tail.size() && Tail[Position] >= '0' && Tail[Position] <= '9') - ++Position; - } - if (Position == Tail.size()) - return true; - if (Tail[Position] != 'e' && Tail[Position] != 'E') - return false; - ++Position; - if (Position < Tail.size() && (Tail[Position] == '+' || Tail[Position] == '-')) - ++Position; - const std::size_t ExponentBegin = Position; - while (Position < Tail.size() && Tail[Position] >= '0' && Tail[Position] <= '9') - ++Position; - return Position == Tail.size() && Position != ExponentBegin; -} - -[[nodiscard]] YAMLQuoting yamlQuoting(std::string_view Value) noexcept { - if (Value.empty()) - return YAMLQuoting::Single; - - YAMLQuoting Result = YAMLQuoting::None; - const auto IsSpace = [](unsigned char Character) { - return Character == ' ' || Character == '\t' || Character == '\r' || Character == '\n' || - Character == '\f' || Character == '\v'; - }; - if (IsSpace(static_cast(Value.front())) || - IsSpace(static_cast(Value.back())) || Value == "null" || Value == "Null" || - Value == "NULL" || Value == "~" || Value == "true" || Value == "True" || Value == "TRUE" || - Value == "false" || Value == "False" || Value == "FALSE" || isYAMLNumeric(Value)) { - Result = YAMLQuoting::Single; - } - - switch (Value.front()) { - case '-': - case '?': - case ':': - case '\\': - case ',': - case '[': - case ']': - case '{': - case '}': - case '#': - case '&': - case '*': - case '!': - case '|': - case '>': - case '\'': - case '"': - case '%': - case '@': - case '`': - Result = YAMLQuoting::Single; - break; - default: - break; - } - - for (const char RawCharacter : Value) { - const auto Character = static_cast(RawCharacter); - if (isASCIIAlphanumeric(Character)) - continue; - switch (Character) { - case '_': - case '-': - case '^': - case '.': - case ',': - case ' ': - case '\t': - continue; - case '\n': - case '\r': - case 0x7f: - return YAMLQuoting::Double; - default: - if (Character <= 0x1fU || (Character & 0x80U) != 0) - return YAMLQuoting::Double; - Result = YAMLQuoting::Single; - break; - } - } - return Result; -} - -[[nodiscard]] std::string yamlScalar(std::string_view Value) { - const YAMLQuoting Quoting = yamlQuoting(Value); - if (Quoting == YAMLQuoting::None) - return std::string(Value); - - std::string Result; - Result.reserve(Value.size() + 2U); - const char Quote = Quoting == YAMLQuoting::Single ? '\'' : '"'; - Result.push_back(Quote); - for (const char Character : Value) { - if (Quoting == YAMLQuoting::Single) { - Result.push_back(Character); - if (Character == '\'') - Result.push_back('\''); - continue; - } - switch (Character) { - case '\\': - Result.append("\\\\"); - break; - case '"': - Result.append("\\\""); - break; - case '\0': - Result.append("\\0"); - break; - case '\a': - Result.append("\\a"); - break; - case '\b': - Result.append("\\b"); - break; - case '\t': - Result.append("\\t"); - break; - case '\n': - Result.append("\\n"); - break; - case '\v': - Result.append("\\v"); - break; - case '\f': - Result.append("\\f"); - break; - case '\r': - Result.append("\\r"); - break; - case '\x1b': - Result.append("\\e"); - break; - default: { - const auto Byte = static_cast(Character); - if (Byte < 0x20U || Byte == 0x7fU) { - static constexpr std::string_view Hex = "0123456789ABCDEF"; - Result.append("\\x"); - Result.push_back(Hex[Byte >> 4U]); - Result.push_back(Hex[Byte & 0xfU]); - } else { - Result.push_back(Character); - } - break; - } - } - } - Result.push_back(Quote); - return Result; -} - -void appendBlockScalar(std::string &Output, std::string_view Text, std::size_t Indent) { - std::size_t Position = 0; - do { - Output.append(Indent, ' '); - const std::size_t End = Text.find('\n', Position); - if (End == std::string_view::npos) { - Output.append(Text.substr(Position)); - Output.push_back('\n'); - return; - } - Output.append(Text.substr(Position, End - Position)); - Output.push_back('\n'); - Position = End + 1U; - } while (Position < Text.size()); -} - -[[nodiscard]] std::optional parseDecimal32(std::string_view Text) noexcept { - std::uint32_t Value = 0; - if (Text.empty()) - return std::nullopt; - const auto [End, Error] = std::from_chars(Text.data(), Text.data() + Text.size(), Value, 10); - if (Error != std::errc{} || End != Text.data() + Text.size()) - return std::nullopt; - return Value; -} - -[[nodiscard]] bool isDefaultFrameState(const MachineFrameState &State) { - return State == MachineFrameState{}; -} - -class MIRDocumentPrinter final { -public: - MIRDocumentPrinter(const MIRDocument &Document, const TargetDescription &Target, - MIRDocumentPrinterOptions Options) - : Document(Document), Target(Target), Options(std::move(Options)) {} - - [[nodiscard]] Expected run(); - -private: - struct InstructionLocation final { - std::uint32_t Block = 0; - std::uint32_t Offset = 0; - }; - - [[nodiscard]] Expected printFunction(const MachineFunction &Function, - const MIRFunctionDocumentInfo *DocumentInfo); - [[nodiscard]] Expected createBodyContext(const MachineFunction &Function) const; - [[nodiscard]] Expected printRegisters(const MachineFunction &Function, - const MIParserContext &Context); - [[nodiscard]] Expected printFrameInfo(const MachineFunction &Function, - const MIParserContext &Context); - [[nodiscard]] Expected printStackObjects(const MachineFunction &Function, - const MIParserContext &Context, bool Fixed); - [[nodiscard]] Expected printConstants(const MachineFunction &Function, - const MIParserContext &Context); - [[nodiscard]] Expected printJumpTable(const MachineFunction &Function, - const MIParserContext &Context); - [[nodiscard]] Expected printMetadata(const MachineFunction &Function, - const MIParserContext &Context); - [[nodiscard]] Expected printTargetState(const MachineFunction &Function); - [[nodiscard]] Expected printFunctionRecords(const MachineFunction &Function, - const MIParserContext &Context); - [[nodiscard]] Expected verifyOmittedEmbeddedIRLayout(const MachineFunction &Function) const; - - [[nodiscard]] Expected physicalRegister(PhysicalRegister Register) const; - [[nodiscard]] Expected frameReference(FrameIndex Index, - const MIParserContext &Context) const; - [[nodiscard]] Expected metadataReference(MachineMetadataReference Reference, - const MIParserContext &Context) const; - [[nodiscard]] Expected metadataValue(const MachineMetadataValue &Value, - const MIParserContext &Context) const; - [[nodiscard]] Expected debugLocation(const MachineDebugLocation &Location, - const MIParserContext &Context) const; - [[nodiscard]] Expected diLocation(const MachineDILocation &Location, - std::size_t FrameIndex, - const MIParserContext &Context) const; - [[nodiscard]] Expected instructionLocation(const MachineFunction &Function, - MachineInstrStableID ID) const; - [[nodiscard]] Expected blockReference(const MachineFunction &Function, - MachineBlockStableID ID) const; - [[nodiscard]] Expected 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 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 -MIRDocumentPrinter::verifyOmittedEmbeddedIRLayout(const MachineFunction &Function) const { - if (Options.EmitEmbeddedIR) - return {}; - - const EmbeddedIRReferenceIndex *Embedded = Document.embeddedIR(); - if (Embedded == nullptr) - return {}; - - bool HasLayoutDependentContent = false; - for (const VirtualRegisterInfo &RegisterInfo : Function.registerInfo().virtualRegisters()) { - if (!RegisterInfo.IsTombstone && RegisterInfo.Type && - RegisterInfo.Type->isPointerOrPointerVector()) { - HasLayoutDependentContent = true; - break; - } - } - if (!HasLayoutDependentContent) { - for (const MachineConstantPoolEntry &Entry : Function.constantPool().entries()) { - const MachineConstantPoolEntryDesc *Description = Entry.description(); - if (Description != nullptr && - std::holds_alternative(Description->Value)) { - HasLayoutDependentContent = true; - break; - } - } - } - if (!HasLayoutDependentContent) - return {}; - - if (Options.DataLayout.empty()) { - return makeUnexpected( - ErrorCode::InvalidArgument, - "omitting embedded LLVM IR with layout-dependent MIR requires an explicit printer " - "DataLayout"); - } - - const LLVMConstantSyntaxOptions LayoutOptions{ - .DataLayout = Options.DataLayout, - .NamedTypes = {}, - }; - for (const VirtualRegisterInfo &RegisterInfo : Function.registerInfo().virtualRegisters()) { - if (RegisterInfo.IsTombstone || !RegisterInfo.Type || - !RegisterInfo.Type->isPointerOrPointerVector()) { - continue; - } - const auto AddressSpace = RegisterInfo.Type->addressSpace(); - if (!AddressSpace) { - return makeUnexpected(ErrorCode::VerificationError, - "pointer virtual register has no address space"); - } - auto PointerSize = llvmPointerSizeInBits(*AddressSpace, LayoutOptions); - if (!PointerSize) { - return makeUnexpected(ErrorCode::InvalidArgument, - "printer DataLayout cannot determine pointer width: " + - std::string(PointerSize.error().message())); - } - if (*PointerSize != RegisterInfo.Type->scalarBitWidth()) { - return makeUnexpected(ErrorCode::VerificationError, - "printer DataLayout does not match pointer virtual-register width"); - } - } - - for (const MachineConstantPoolEntry &Entry : Function.constantPool().entries()) { - const MachineConstantPoolEntryDesc *Description = Entry.description(); - if (Description == nullptr) - continue; - const auto *Generic = std::get_if(&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 -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::max()) { - return makeUnexpected(ErrorCode::Unsupported, - "MIR cannot represent more than 2^32 virtual registers"); - } - Context.VirtualRegisters.push_back({.TextID = static_cast(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 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 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::max() && - UsedMetadataSlots.contains(static_cast(NextMetadataSlot))) { - ++NextMetadataSlot; - } - if (NextMetadataSlot > std::numeric_limits::max()) { - return makeUnexpected(ErrorCode::Unsupported, "MIR metadata text slot space is exhausted"); - } - TextID = static_cast(NextMetadataSlot); - UsedMetadataSlots.insert(TextID); - ++NextMetadataSlot; - } - Context.Metadata.push_back( - {.TextID = TextID, .Reference = MachineMetadataReference::machineFunction(Node.Slot)}); - } - return Context; -} - -Expected 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 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 MIRDocumentPrinter::metadataValue(const MachineMetadataValue &Value, - const MIParserContext &Context) const { - if (const MachineMetadataReference *Reference = Value.reference()) - return metadataReference(*Reference, Context); - const MachineDIExpression *Expression = Value.expression(); - if (Expression == nullptr) - return makeUnexpected(ErrorCode::VerificationError, "metadata value is not printable"); - return formatDIExpression(Expression->elements()); -} - -Expected 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 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(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::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::max()) { - return makeUnexpected(ErrorCode::Unsupported, "MIR instruction offset exceeds 32 bits"); - } - return InstructionLocation{static_cast(BlockNumber), - static_cast(Offset)}; - } - } - return makeUnexpected(ErrorCode::VerificationError, - "function record references an unlinked instruction"); -} - -Expected 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::max()) - break; - return "%bb." + std::to_string(Number); - } - } - return makeUnexpected(ErrorCode::VerificationError, - "frame record references an unlinked basic block"); -} - -Expected 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 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 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::max()) { - return makeUnexpected(ErrorCode::Unsupported, - "fixed LLVM MIR maxCallFrameSize is limited to 32 bits"); - } - Output.append("frameInfo:\n"); - const auto Bool = [this](std::string_view Key, bool Value) { - if (Options.Mode == MIPrintMode::Readable && !Value) - return; - Output.append(" "); - Output.append(Key); - Output.append(Value ? ": true\n" : ": false\n"); - }; - const auto Unsigned = [this](std::string_view Key, std::uint64_t Value) { - if (Options.Mode == MIPrintMode::Readable && Value == 0) - return; - Output.append(" "); - Output.append(Key); - Output.append(": "); - Output.append(std::to_string(Value)); - Output.push_back('\n'); - }; - - Bool("isFrameAddressTaken", State.IsFrameAddressTaken); - Bool("isReturnAddressTaken", State.IsReturnAddressTaken); - Bool("hasStackMap", State.HasStackMap); - Bool("hasPatchPoint", State.HasPatchPoint); - Unsigned("stackSize", State.StackSize); - if (Options.Mode == MIPrintMode::Complete || State.OffsetAdjustment != 0) { - Output.append(" offsetAdjustment: "); - Output.append(std::to_string(State.OffsetAdjustment)); - Output.push_back('\n'); - } - Unsigned("maxAlignment", State.MaxAlignment ? State.MaxAlignment->bytes() : 0); - Bool("adjustsStack", State.AdjustsStack); - Bool("hasCalls", State.HasCalls); - if (Options.Mode == MIPrintMode::Complete || - State.FramePointerPolicy != MachineFramePointerPolicy::None) { - Output.append(" framePointerPolicy: "); - Output.append(getMachineFramePointerPolicyName(State.FramePointerPolicy)); - Output.push_back('\n'); - } - if (Options.Mode == MIPrintMode::Complete || State.StackProtector) { - std::string Reference; - if (State.StackProtector) { - auto PrintedReference = frameReference(*State.StackProtector, Context); - if (!PrintedReference) - return std::unexpected(PrintedReference.error()); - Reference = std::move(*PrintedReference); - } - Output.append(" stackProtector: "); - Output.append(yamlScalar(Reference)); - Output.push_back('\n'); - } - if (Options.Mode == MIPrintMode::Complete || State.FunctionContext) { - std::string Reference; - if (State.FunctionContext) { - auto PrintedReference = frameReference(*State.FunctionContext, Context); - if (!PrintedReference) - return std::unexpected(PrintedReference.error()); - Reference = std::move(*PrintedReference); - } - Output.append(" functionContext: "); - Output.append(yamlScalar(Reference)); - Output.push_back('\n'); - } - if (Options.Mode == MIPrintMode::Complete || State.MaxCallFrameSize) { - Unsigned("maxCallFrameSize", - State.MaxCallFrameSize.value_or(std::numeric_limits::max())); - } - Unsigned("cvBytesOfCalleeSavedRegisters", State.CVBytesOfCalleeSavedRegisters); - Bool("hasOpaqueSPAdjustment", State.HasOpaqueSPAdjustment); - Bool("hasVAStart", State.HasVAStart); - Bool("hasMustTailInVarArgFunc", State.HasMustTailInVarArgFunc); - Bool("hasTailCall", State.HasTailCall); - Bool("isCalleeSavedInfoValid", State.IsCalleeSavedInfoValid); - Unsigned("localFrameSize", State.LocalFrameSize); - - const auto PrintPoints = - [this, &Function](std::string_view Key, - std::span Points) -> Expected { - 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 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 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 Value = - makeUnexpected(ErrorCode::Unsupported, "constant-pool kind is not MIR serializable"); - if (const auto *Generic = std::get_if(&Description->Value)) { - Value = printLLVMConstant(*Generic); - } else if (const auto *TargetConstantValue = std::get_if(&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 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 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 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 MIRDocumentPrinter::printFunctionRecords(const MachineFunction &Function, - const MIParserContext &Context) { - const MachineFunctionRecords &Records = Function.records(); - if (!Records.entryValues().empty()) { - Output.append("entry_values:\n"); - for (const MachineEntryValueInfo &Entry : Records.entryValues()) { - auto RegisterName = physicalRegister(Entry.Reg); - if (!RegisterName) - return std::unexpected(RegisterName.error()); - if (!Entry.DebugInfo.Variable || !Entry.DebugInfo.Expression || !Entry.DebugInfo.Location) { - return makeUnexpected(ErrorCode::Unsupported, - "MIR entry value requires complete debug metadata"); - } - auto Variable = metadataReference(*Entry.DebugInfo.Variable, Context); - auto Expression = metadataValue(*Entry.DebugInfo.Expression, Context); - auto Location = debugLocation(*Entry.DebugInfo.Location, Context); - if (!Variable) - return std::unexpected(Variable.error()); - if (!Expression) - return std::unexpected(Expression.error()); - if (!Location) - return std::unexpected(Location.error()); - Output.append(" - { entry-value-register: "); - Output.append(yamlScalar(*RegisterName)); - Output.append(", debug-info-variable: "); - Output.append(yamlScalar(*Variable)); - Output.append(", debug-info-expression: "); - Output.append(yamlScalar(*Expression)); - Output.append(", debug-info-location: "); - Output.append(yamlScalar(*Location)); - Output.append(" }\n"); - } - } else if (Options.Mode == MIPrintMode::Complete) { - Output.append("entry_values: []\n"); - } - - if (!Records.callSites().empty()) { - Output.append("callSites:\n"); - for (const MachineCallSiteInfo &CallSite : Records.callSites()) { - auto Location = instructionLocation(Function, CallSite.CallInstruction); - if (!Location) - return std::unexpected(Location.error()); - Output.append(" - { bb: "); - Output.append(std::to_string(Location->Block)); - Output.append(", offset: "); - Output.append(std::to_string(Location->Offset)); - if (!CallSite.ForwardedArguments.empty()) { - Output.append(", fwdArgRegs: [ "); - for (std::size_t Index = 0; Index < CallSite.ForwardedArguments.size(); ++Index) { - if (Index != 0) - Output.append(", "); - const MachineCallSiteArgument &Argument = CallSite.ForwardedArguments[Index]; - auto RegisterName = physicalRegister(Argument.Reg); - if (!RegisterName) - return std::unexpected(RegisterName.error()); - Output.append("{ arg: "); - Output.append(std::to_string(Argument.ArgumentNumber)); - Output.append(", reg: "); - Output.append(yamlScalar(*RegisterName)); - Output.append(" }"); - } - Output.append(" ]"); - } - if (!CallSite.CalleeTypeIDs.empty()) { - Output.append(", calleeTypeIds: [ "); - for (std::size_t Index = 0; Index < CallSite.CalleeTypeIDs.size(); ++Index) { - if (Index != 0) - Output.append(", "); - Output.append(std::to_string(CallSite.CalleeTypeIDs[Index])); - } - Output.append(" ]"); - } - Output.append(" }\n"); - } - } else if (Options.Mode == MIPrintMode::Complete) { - Output.append("callSites: []\n"); - } - - if (!Records.debugValueSubstitutions().empty()) { - Output.append("debugValueSubstitutions:\n"); - for (const MachineDebugValueSubstitution &Substitution : Records.debugValueSubstitutions()) { - Output.append(" - { srcinst: "); - Output.append(std::to_string(Substitution.Source.InstructionNumber)); - Output.append(", srcop: "); - Output.append(std::to_string(Substitution.Source.OperandNumber)); - Output.append(", dstinst: "); - Output.append(std::to_string(Substitution.Destination.InstructionNumber)); - Output.append(", dstop: "); - Output.append(std::to_string(Substitution.Destination.OperandNumber)); - Output.append(", subreg: "); - Output.append(std::to_string(Substitution.Subregister.value())); - Output.append(" }\n"); - } - } else if (Options.Mode == MIPrintMode::Complete) { - Output.append("debugValueSubstitutions: []\n"); - } - - if (!Records.calledGlobals().empty()) { - Output.append("calledGlobals:\n"); - for (const MachineCalledGlobal &Called : Records.calledGlobals()) { - auto Location = instructionLocation(Function, Called.CallInstruction); - if (!Location) - return std::unexpected(Location.error()); - Output.append(" - { bb: "); - Output.append(std::to_string(Location->Block)); - Output.append(", offset: "); - Output.append(std::to_string(Location->Offset)); - Output.append(", callee: "); - Output.append(yamlScalar(Called.Callee)); - Output.append(", flags: "); - Output.append(std::to_string(Called.Flags)); - Output.append(" }\n"); - } - } - - if (!Records.prefetchTargets().empty()) { - Output.append("prefetch-targets: [ "); - const auto Targets = Records.prefetchTargets(); - for (std::size_t Index = 0; Index < Targets.size(); ++Index) { - if (Index != 0) - Output.append(", "); - const MachinePrefetchTarget &TargetValue = Targets[Index]; - std::string Text = "bb_id " + std::to_string(TargetValue.Block.BaseID) + ", " + - std::to_string(TargetValue.Block.CloneID) + ", " + - std::to_string(TargetValue.CallsiteIndex); - Output.append(yamlScalar(Text)); - } - Output.append(" ]\n"); - } - return {}; -} - -Expected MIRDocumentPrinter::printFunction(const MachineFunction &Function, - const MIRFunctionDocumentInfo *) { - if (Function.compilationStatus().state() != MachineCompilationState::Success) { - return makeUnexpected(ErrorCode::Unsupported, - "fixed LLVM MIR cannot serialize this compilation failure state"); - } - auto Context = createBodyContext(Function); - if (!Context) - return std::unexpected(Context.error()); - - Output.append("---\nname: "); - Output.append(yamlScalar(Function.name())); - Output.push_back('\n'); - const auto Alignment = Function.attributes().alignment(); - if (Options.Mode == MIPrintMode::Complete || Alignment) { - Output.append("alignment: "); - Output.append(std::to_string(Alignment ? Alignment->bytes() : 1U)); - Output.push_back('\n'); - } - - const MachineFunctionProperties &Properties = Function.properties(); - const MachineFunctionAttributes &Attributes = Function.attributes(); - printBoolean("exposesReturnsTwice", - Attributes.has(MachineFunctionAttribute::ExposesReturnsTwice)); - printBoolean("legalized", Properties.has(MachineFunctionProperty::Legalized)); - printBoolean("regBankSelected", Properties.has(MachineFunctionProperty::RegBankSelected)); - printBoolean("selected", Properties.has(MachineFunctionProperty::Selected)); - printBoolean("failedISel", Properties.has(MachineFunctionProperty::FailedISel)); - printBoolean("tracksRegLiveness", Properties.has(MachineFunctionProperty::TracksLiveness)); - printBoolean("hasWinCFI", Attributes.has(MachineFunctionAttribute::HasWinCFI)); - printBoolean("noPhis", Properties.has(MachineFunctionProperty::NoPHIs), true); - printBoolean("isSSA", Properties.has(MachineFunctionProperty::IsSSA), true); - printBoolean("noVRegs", Properties.has(MachineFunctionProperty::NoVRegs), true); - printBoolean("hasFakeUses", Attributes.has(MachineFunctionAttribute::HasFakeUses), true); - printBoolean("callsEHReturn", Attributes.has(MachineFunctionAttribute::CallsEHReturn)); - printBoolean("callsUnwindInit", Attributes.has(MachineFunctionAttribute::CallsUnwindInit)); - printBoolean("hasEHContTarget", Attributes.has(MachineFunctionAttribute::HasEHContTarget)); - printBoolean("hasEHScopes", Attributes.has(MachineFunctionAttribute::HasEHScopes)); - printBoolean("hasEHFunclets", Attributes.has(MachineFunctionAttribute::HasEHFunclets)); - printBoolean("isOutlined", Attributes.has(MachineFunctionAttribute::IsOutlined)); - printBoolean("debugInstrRef", Attributes.has(MachineFunctionAttribute::DebugInstrRef)); - printBoolean("failsVerification", Properties.has(MachineFunctionProperty::FailsVerification)); - printBoolean("tracksDebugUserValues", - Attributes.has(MachineFunctionAttribute::TracksDebugUserValues)); - - if (auto Printed = printRegisters(Function, *Context); !Printed) - return Printed; - if (auto Printed = printFrameInfo(Function, *Context); !Printed) - return Printed; - if (auto Printed = printStackObjects(Function, *Context, true); !Printed) - return Printed; - if (auto Printed = printStackObjects(Function, *Context, false); !Printed) - return Printed; - if (auto Printed = printFunctionRecords(Function, *Context); !Printed) - return Printed; - if (auto Printed = printConstants(Function, *Context); !Printed) - return Printed; - if (auto Printed = printTargetState(Function); !Printed) - return Printed; - if (auto Printed = printJumpTable(Function, *Context); !Printed) - return Printed; - if (auto Printed = printMetadata(Function, *Context); !Printed) - return Printed; - - auto Body = - printMachineFunctionBody(Function, Target, *Context, MIPrinterOptions{.Mode = Options.Mode}); - if (!Body) - return std::unexpected(Body.error()); - Output.append("body: |\n"); - appendBlockScalar(Output, *Body, 2); - Output.append("...\n"); - return {}; -} - -Expected MIRDocumentPrinter::run() { - auto UnitVerification = Document.unit().verify(); - if (!UnitVerification) - return std::unexpected(UnitVerification.error()); - if (Document.unit().functionCount() == 0) { - return makeUnexpected(ErrorCode::InvalidArgument, - "MIR document must contain at least one machine function"); - } - - for (const MIRFunctionDocumentInfo &Info : Document.functionDocumentInfo()) { - if (Document.unit().function(Info.Function) == nullptr) { - return makeUnexpected(ErrorCode::VerificationError, - "MIR document info references an unknown machine function"); - } - } - - for (std::size_t Index = 0; Index < Document.unit().functionCount(); ++Index) { - const MachineFunction *Function = Document.unit().functionAt(Index); - if (Function == nullptr) { - return makeUnexpected(ErrorCode::VerificationError, - "compilation unit contains a null machine function"); - } - auto ReferenceVerification = - verifyMIRDocumentReferences(Document, *Function, Options.EmitEmbeddedIR); - if (!ReferenceVerification) - return std::unexpected(ReferenceVerification.error()); - auto LayoutVerification = verifyOmittedEmbeddedIRLayout(*Function); - if (!LayoutVerification) - return std::unexpected(LayoutVerification.error()); - } - - if (Options.EmitEmbeddedIR && Document.embeddedIR() != nullptr) { - Output.append("--- |\n"); - appendBlockScalar(Output, Document.embeddedIR()->source().text(), 2); - Output.append("...\n"); - } - for (std::size_t Index = 0; Index < Document.unit().functionCount(); ++Index) { - const MachineFunction *Function = Document.unit().functionAt(Index); - if (Function == nullptr) - return makeUnexpected(ErrorCode::VerificationError, - "compilation unit contains a null machine function"); - if (auto Printed = printFunction(*Function, documentInfo(*Function)); !Printed) - return std::unexpected(Printed.error()); - } - return std::move(Output); -} - -} // namespace - -Expected printMIRDocument(const MIRDocument &Document, const TargetDescription &Target, - MIRDocumentPrinterOptions Options) { - return MIRDocumentPrinter(Document, Target, std::move(Options)).run(); -} - -} // namespace rcc::mir diff --git a/backend/lib/MIR/MIRDocumentReferenceValidation.cpp b/backend/lib/MIR/MIRDocumentReferenceValidation.cpp deleted file mode 100644 index 69b0dd1..0000000 --- a/backend/lib/MIR/MIRDocumentReferenceValidation.cpp +++ /dev/null @@ -1,426 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "MIRDocumentReferenceValidation.h" - -#include "rcc/CodeGen/MachineBasicBlock.h" -#include "rcc/CodeGen/MachineConstantPool.h" -#include "rcc/CodeGen/MachineFrameInfo.h" -#include "rcc/CodeGen/MachineFunction.h" -#include "rcc/CodeGen/MachineFunctionRecords.h" -#include "rcc/CodeGen/MachineInstr.h" -#include "rcc/CodeGen/MachineMetadata.h" -#include "rcc/MIR/MIRDocument.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace rcc::mir { - -Expected verifyMIRDocumentReferences(const MIRDocument &Document, - const MachineFunction &Function, bool UseEmbeddedIR) { - const EmbeddedIRReferenceIndex *Embedded = UseEmbeddedIR ? Document.embeddedIR() : nullptr; - const EmbeddedIRFunction *IRFunction = nullptr; - if (Embedded != nullptr) { - if (Function.name().empty()) { - std::size_t AnonymousOrdinal = 0; - bool FoundFunction = false; - for (std::size_t Index = 0; Index < Document.unit().functionCount(); ++Index) { - const MachineFunction *Candidate = Document.unit().functionAt(Index); - if (Candidate == &Function) { - FoundFunction = true; - break; - } - if (Candidate != nullptr && Candidate->name().empty()) - ++AnonymousOrdinal; - } - if (FoundFunction) - IRFunction = Embedded->unnamedFunction(AnonymousOrdinal); - } else { - IRFunction = Embedded->function(Function.name()); - } - if (IRFunction == nullptr) { - return makeUnexpected(ErrorCode::VerificationError, - "machine function has no matching embedded LLVM IR function"); - } - } - - const auto ValidateGlobal = [Embedded](std::string_view Name, - std::string_view Description) -> Expected { - 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 { - 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 { - 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 ExpectedTypes, - std::string_view Description) -> Expected { - 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 { - const MachineMetadataReference *Reference = Value.reference(); - return Reference == nullptr ? Expected{} : ValidateMetadata(*Reference, {}, Description); - }; - const auto ValidateLocation = - [&ValidateMetadata](const MachineDebugLocation &Location, - std::string_view Description) -> Expected { - 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 { - 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 { - 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 { - 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(&Description->Value)) { - auto Verification = ValidateGenericConstant(ValidateGenericConstant, *Generic); - if (!Verification) - return Verification; - } else if (const auto *Address = std::get_if(&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(); Global != nullptr) { - auto Verification = ValidateGlobal(Global->Name, "global-symbol operand"); - if (!Verification) - return Verification; - } - if (const auto *Address = Operand.getIf(); Address != nullptr) { - auto Verification = ValidateBlockAddress(Address->FunctionName, Address->BlockName, - "block-address operand"); - if (!Verification) - return Verification; - } - if (const auto *Metadata = Operand.getIf(); 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 BaseName = Description.Base.symbolName(); - switch (Description.Base.kind()) { - case MemoryBase::Kind::GlobalSymbol: - case MemoryBase::Kind::CallEntryGlobal: - if (BaseName) { - auto Verification = ValidateGlobal(*BaseName, "machine memory base"); - if (!Verification) - return Verification; - } - break; - case MemoryBase::Kind::IRValue: - if (BaseName) { - auto Verification = ValidateIRValue(*BaseName, "machine memory base"); - if (!Verification) - return Verification; - } - break; - default: - break; - } - const std::array MetadataReferences{ - Description.TBAA, Description.AliasScope, - Description.NoAlias, Description.NoAliasAddressSpace, - Description.RangeMetadata, - }; - for (const auto &Reference : MetadataReferences) { - if (!Reference) - continue; - auto Verification = ValidateMetadata(*Reference, {}, "machine memory operand metadata"); - if (!Verification) - return Verification; - } - if (Description.SyncScope.value() == 0) - continue; - const auto Scope = - std::ranges::find_if(Document.syncScopes(), [&Description](const auto &Slot) { - return Slot.ID == Description.SyncScope; - }); - if (Scope == Document.syncScopes().end()) { - return makeUnexpected( - ErrorCode::VerificationError, - "machine memory operand synchronization scope has no document name"); - } - } - } - } - return {}; -} - -} // namespace rcc::mir diff --git a/backend/lib/MIR/MIRDocumentReferenceValidation.h b/backend/lib/MIR/MIRDocumentReferenceValidation.h deleted file mode 100644 index 2ab6291..0000000 --- a/backend/lib/MIR/MIRDocumentReferenceValidation.h +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -// The private header has no stable include-root-relative guard name. -// NOLINTNEXTLINE(llvm-header-guard) -#ifndef RCC_LIB_MIR_MIRDOCUMENTREFERENCEVALIDATION_H -#define RCC_LIB_MIR_MIRDOCUMENTREFERENCEVALIDATION_H - -#include "rcc/Support/Error.h" - -namespace rcc { - -class MachineFunction; - -namespace mir { - -class MIRDocument; - -[[nodiscard]] Expected verifyMIRDocumentReferences(const MIRDocument &Document, - const MachineFunction &Function, - bool UseEmbeddedIR); - -} // namespace mir -} // namespace rcc - -#endif // RCC_LIB_MIR_MIRDOCUMENTREFERENCEVALIDATION_H diff --git a/backend/lib/MIR/MIRFrameParser.cpp b/backend/lib/MIR/MIRFrameParser.cpp deleted file mode 100644 index 4367fbd..0000000 --- a/backend/lib/MIR/MIRFrameParser.cpp +++ /dev/null @@ -1,467 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "MIRDocumentParser.h" -#include "MIRDocumentReferenceValidation.h" -#include "MIRMetadataTextParser.h" -#include "MIRParserUtilities.h" -#include "rcc/CodeGen/MachineBasicBlock.h" -#include "rcc/CodeGen/MachineConstantPool.h" -#include "rcc/CodeGen/MachineFrameInfo.h" -#include "rcc/CodeGen/MachineFunction.h" -#include "rcc/CodeGen/MachineFunctionAttributes.h" -#include "rcc/CodeGen/MachineFunctionProperties.h" -#include "rcc/CodeGen/MachineFunctionRecords.h" -#include "rcc/CodeGen/MachineInstr.h" -#include "rcc/CodeGen/MachineJumpTableInfo.h" -#include "rcc/CodeGen/MachineMetadata.h" -#include "rcc/CodeGen/MachineRegisterInfo.h" -#include "rcc/CodeGen/MachineVerifier.h" -#include "rcc/CodeGen/TargetDescription.h" -#include "rcc/CodeGen/TargetMIRTextInfo.h" -#include "rcc/CodeGen/TargetRegisterInfo.h" -#include "rcc/MIR/LLVMConstantSyntax.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -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 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::max()) { - return true; - } - const auto TextID = static_cast(WideID); - if (!IDs.insert(TextID).second) - return fail(IDNode->range(), "duplicate stack object ID"); - - StackObjectDesc Description; - Description.Kind = Fixed ? StackObjectKind::FixedABI : StackObjectKind::Default; - Description.Offset = 0; - if (const yaml::Node *NameNode = mappingValue(*Element, "name")) { - std::string_view Name; - if (scalar(*NameNode, Name, "stack object name")) - return true; - if (!Name.empty()) - Description.AllocaName = std::string(Name); - } - if (const yaml::Node *TypeNode = mappingValue(*Element, "type")) { - std::string_view Type; - if (scalar(*TypeNode, Type, "stack object type")) - return true; - if (Type == "default") - Description.Kind = Fixed ? StackObjectKind::FixedABI : StackObjectKind::Default; - else if (Type == "spill-slot") - Description.Kind = Fixed ? StackObjectKind::FixedSpillSlot : StackObjectKind::SpillSlot; - else if (!Fixed && Type == "variable-sized") - Description.Kind = StackObjectKind::VariableSized; - else - return fail(TypeNode->range(), "unknown stack object type '" + std::string(Type) + "'"); - } - if (const yaml::Node *OffsetNode = mappingValue(*Element, "offset")) { - std::int64_t Offset = 0; - if (signedValue(*OffsetNode, Offset, "a signed stack object offset")) - return true; - Description.Offset = Offset; - } else if (Fixed) { - Description.Offset = 0; - } - if (Description.Kind != StackObjectKind::VariableSized) { - const yaml::Node *SizeNode = mappingValue(*Element, "size"); - if (SizeNode == nullptr) { - if (!Fixed) - return fail(Element->range(), "fixed-size stack object requires 'size'"); - Description.Size = 0; - } else { - std::uint64_t Size = 0; - if (unsignedValue(*SizeNode, Size, "an unsigned stack object size")) - return true; - Description.Size = Size; - } - } - if (const yaml::Node *AlignmentNode = mappingValue(*Element, "alignment")) { - if (alignment(*AlignmentNode, Description.Align, true)) - return true; - } - if (const yaml::Node *StackIDNode = mappingValue(*Element, "stack-id")) { - std::string_view Name; - if (scalar(*StackIDNode, Name, "stack ID")) - return true; - const auto Stack = parseStackIDName(Name); - if (!Stack) - return fail(StackIDNode->range(), "unknown stack ID '" + std::string(Name) + "'"); - Description.Stack = *Stack; - } - if (const yaml::Node *ImmutableNode = mappingValue(*Element, "isImmutable")) { - if (boolean(*ImmutableNode, Description.IsImmutable)) - return true; - } - if (const yaml::Node *AliasedNode = mappingValue(*Element, "isAliased")) { - if (boolean(*AliasedNode, Description.IsAliased)) - return true; - } - if (const yaml::Node *SavedNode = mappingValue(*Element, "callee-saved-register")) { - std::string_view SavedName; - if (scalar(*SavedNode, SavedName, "physical register")) - return true; - if (!trim(SavedName).empty()) { - PhysicalRegister Reg; - if (physicalRegister(SavedName, SavedNode->range(), Reg)) - return true; - Description.CalleeSavedRegister = Reg; - } - } - if (const yaml::Node *RestoredNode = mappingValue(*Element, "callee-saved-restored")) { - if (boolean(*RestoredNode, Description.CalleeSavedRestored)) - return true; - } - if (const yaml::Node *LocalNode = mappingValue(*Element, "local-offset")) { - std::int64_t Offset = 0; - if (signedValue(*LocalNode, Offset, "a signed local frame offset")) - return true; - Description.LocalOffset = Offset; - } - MachineVariableDebugInfo DebugInfo; - if (parseVariableDebugInfo(*Element, Function, DebugInfo)) - return true; - if (DebugInfo.Variable || DebugInfo.Expression || DebugInfo.Location) - Description.DebugInfo = std::move(DebugInfo); - - auto Created = Function.frameInfo().createObject(std::move(Description)); - if (!Created) - return importError(Element->range(), Created.error()); - MIFrameObjectSlot Slot{ - .TextID = TextID, - .Index = *Created, - .Name = std::nullopt, - }; - if (const StackObjectDesc *CreatedDescription = - Function.frameInfo().object(*Created)->description(); - CreatedDescription != nullptr && CreatedDescription->AllocaName) { - Slot.Name = *CreatedDescription->AllocaName; - } - (Fixed ? Context.FixedStackObjects : Context.StackObjects).push_back(std::move(Slot)); - } - return false; - }; - - if (const yaml::Node *Fixed = mappingValue(Root, "fixedStack")) { - if (ParseList(*Fixed, true)) - return true; - } - if (const yaml::Node *Stack = mappingValue(Root, "stack")) { - if (ParseList(*Stack, false)) - return true; - } - return false; -} - -bool MIRDocumentParser::parseFrameReference(const yaml::Node &Node, const MIParserContext &Context, - FrameIndex &Index) { - std::string_view Text; - if (scalar(Node, Text, "frame index")) - return true; - bool Fixed = false; - std::optional 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 &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::min() || - Wide > std::numeric_limits::max()) { - return fail(Value->range(), "offset adjustment is outside the 32-bit signed range"); - } - State.OffsetAdjustment = static_cast(Wide); - } - if (const yaml::Node *Value = mappingValue(*Frame, "maxAlignment")) { - std::uint64_t Bytes = 0; - if (unsignedValue(*Value, Bytes, "an unsigned maximum alignment")) - return true; - if (Bytes == 0) { - State.MaxAlignment.reset(); - } else { - auto Parsed = Alignment::create(Bytes); - if (!Parsed) - return importError(Value->range(), Parsed.error()); - State.MaxAlignment = *Parsed; - } - } - if (const yaml::Node *Value = mappingValue(*Frame, "framePointerPolicy")) { - std::string_view Name; - if (scalar(*Value, Name, "frame-pointer policy")) - return true; - const auto Policy = parseMachineFramePointerPolicy(Name); - if (!Policy) - return fail(Value->range(), "unknown frame-pointer policy '" + std::string(Name) + "'"); - State.FramePointerPolicy = *Policy; - } - for (const auto &[Key, Destination] : {std::pair{"stackProtector", &State.StackProtector}, - std::pair{"functionContext", &State.FunctionContext}}) { - if (const yaml::Node *Value = mappingValue(*Frame, Key)) { - std::string_view Reference; - if (scalar(*Value, Reference, "frame index")) - return true; - if (Reference.empty()) { - Destination->reset(); - continue; - } - FrameIndex Index; - if (parseFrameReference(*Value, Context, Index)) - return true; - *Destination = Index; - } - } - if (const yaml::Node *Value = mappingValue(*Frame, "maxCallFrameSize")) { - std::uint64_t Size = 0; - if (unsignedValue(*Value, Size, "a 32-bit unsigned maximum call-frame size") || - Size > std::numeric_limits::max()) { - return fail(Value->range(), "maximum call-frame size is outside the 32-bit unsigned range"); - } - if (Size == std::numeric_limits::max()) - State.MaxCallFrameSize.reset(); - else - State.MaxCallFrameSize = Size; - } - for (const auto &[Key, Destination] : - {std::pair{"cvBytesOfCalleeSavedRegisters", &State.CVBytesOfCalleeSavedRegisters}, - std::pair{"localFrameSize", &State.LocalFrameSize}}) { - if (const yaml::Node *Value = mappingValue(*Frame, Key)) { - std::uint64_t Wide = 0; - if (unsignedValue(*Value, Wide, "a 32-bit unsigned frame value") || - Wide > std::numeric_limits::max()) { - return fail(Value->range(), "frame value is outside the 32-bit unsigned range"); - } - *Destination = static_cast(Wide); - } - } - if (const yaml::Node *Value = mappingValue(*Frame, "savePoint")) { - if (parseSaveRestorePoints(*Value, Function, State.SavePoints)) - return true; - } - if (const yaml::Node *Value = mappingValue(*Frame, "restorePoint")) { - if (parseSaveRestorePoints(*Value, Function, State.RestorePoints)) - return true; - } - auto StateResult = Function.frameInfo().setState(std::move(State)); - if (!StateResult) - return importError(Frame->range(), StateResult.error()); - return false; -} - -} // namespace rcc::mir diff --git a/backend/lib/MIR/MIRFunctionParser.cpp b/backend/lib/MIR/MIRFunctionParser.cpp deleted file mode 100644 index 6acf774..0000000 --- a/backend/lib/MIR/MIRFunctionParser.cpp +++ /dev/null @@ -1,552 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "MIRDocumentParser.h" -#include "MIRDocumentReferenceValidation.h" -#include "MIRMetadataTextParser.h" -#include "MIRParserUtilities.h" -#include "rcc/CodeGen/MachineBasicBlock.h" -#include "rcc/CodeGen/MachineConstantPool.h" -#include "rcc/CodeGen/MachineFrameInfo.h" -#include "rcc/CodeGen/MachineFunction.h" -#include "rcc/CodeGen/MachineFunctionAttributes.h" -#include "rcc/CodeGen/MachineFunctionProperties.h" -#include "rcc/CodeGen/MachineFunctionRecords.h" -#include "rcc/CodeGen/MachineInstr.h" -#include "rcc/CodeGen/MachineJumpTableInfo.h" -#include "rcc/CodeGen/MachineMetadata.h" -#include "rcc/CodeGen/MachineRegisterInfo.h" -#include "rcc/CodeGen/MachineVerifier.h" -#include "rcc/CodeGen/TargetDescription.h" -#include "rcc/CodeGen/TargetMIRTextInfo.h" -#include "rcc/CodeGen/TargetRegisterInfo.h" -#include "rcc/MIR/LLVMConstantSyntax.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -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 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() != 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 Explicit, bool Computed, - MachineFunctionProperty Property, - std::string_view Conflict) -> bool { - if (Explicit && *Explicit && !Computed) { - return fail(Root.range(), std::string(Function.name()) + std::string(Conflict)); - } - if (Explicit.value_or(Computed)) - Function.properties().set(Property); - else - Function.properties().clear(Property); - return false; - }; - if (ApplyComputed(DocumentInfo.NoPHIs, NoPHIs, MachineFunctionProperty::NoPHIs, - " has explicit property NoPhi, but contains at least one PHI") || - ApplyComputed(DocumentInfo.IsSSA, IsSSA, MachineFunctionProperty::IsSSA, - " has explicit property IsSSA, but is not valid SSA") || - ApplyComputed(DocumentInfo.NoVRegs, NoVRegs, MachineFunctionProperty::NoVRegs, - " has explicit property NoVRegs, but contains virtual registers")) { - return true; - } - - if (DocumentInfo.HasFakeUses && !*DocumentInfo.HasFakeUses && HasFakeUse) { - return fail(Root.range(), - std::string(Function.name()) + - " has explicit property hasFakeUses=false, but contains fake uses"); - } - if (DocumentInfo.HasFakeUses.value_or(HasFakeUse)) - Function.attributes().set(MachineFunctionAttribute::HasFakeUses); - else - Function.attributes().clear(MachineFunctionAttribute::HasFakeUses); - - const bool Selected = Function.properties().has(MachineFunctionProperty::Selected); - const bool StructurallyPostSSA = - Selected && NoPHIs && !Function.properties().has(MachineFunctionProperty::IsSSA); - if (AllTiedOperandsRewritten && (HasTiedOperands || StructurallyPostSSA)) - Function.properties().set(MachineFunctionProperty::TiedOpsRewritten); - else - Function.properties().clear(MachineFunctionProperty::TiedOpsRewritten); - - if (HasFrameIndex) - Function.properties().clear(MachineFunctionProperty::NoFrameIndices); - else - Function.properties().set(MachineFunctionProperty::NoFrameIndices); - - const MachineFrameState &FrameState = Function.frameInfo().state(); - const bool StructurallyFinalizedFrame = Selected && NoVRegs && !HasFrameIndex && - FrameState.IsCalleeSavedInfoValid && - FrameState.MaxAlignment.has_value(); - if (StructurallyFinalizedFrame) { - if (!Function.frameInfo().isFrozen()) { - auto Frozen = Function.frameInfo().freeze(FrameState.StackSize, *FrameState.MaxAlignment); - if (!Frozen) - return importError(Root.range(), Frozen.error(), "mir-frame-finalization"); - } - Function.properties().set(MachineFunctionProperty::FrameFinalized); - } else { - Function.properties().clear(MachineFunctionProperty::FrameFinalized); - } - - if (StructurallyFinalizedFrame && !HasExecutablePseudo) - Function.properties().set(MachineFunctionProperty::NoExecutablePseudos); - else - Function.properties().clear(MachineFunctionProperty::NoExecutablePseudos); - return false; -} - -bool MIRDocumentParser::parseRegisters(const yaml::Node &Root, MachineFunction &Function, - MIParserContext &Context) { - const yaml::Node *Registers = mappingValue(Root, "registers"); - if (Registers != nullptr) { - if (requireKind(*Registers, yaml::NodeKind::Sequence, "registers")) - return true; - constexpr std::array RegisterKeys{ - std::string_view{"id"}, - std::string_view{"class"}, - std::string_view{"preferred-register"}, - std::string_view{"flags"}, - }; - std::unordered_set 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::max() || - scalar(*ClassNode, ClassName, "register class or bank")) { - return true; - } - const auto TextID = static_cast(WideID); - if (!IDs.insert(TextID).second) - return fail(IDNode->range(), "duplicate virtual register ID"); - auto Created = Function.registerInfo().createVirtualRegister(); - if (!Created) - return importError(IDNode->range(), Created.error()); - Context.VirtualRegisters.push_back({TextID, *Created}); - - if (ClassName != "_") { - if (const RegisterClassDesc *Class = Target.registers().registerClass(ClassName)) { - auto SetResult = Function.registerInfo().setRegisterClass(*Created, Class->ID); - if (!SetResult) - return importError(ClassNode->range(), SetResult.error()); - SetResult = Function.registerInfo().setRegisterBank(*Created, Class->DefaultBank); - if (!SetResult) - return importError(ClassNode->range(), SetResult.error()); - } else if (const RegisterBankDesc *Bank = Target.registers().registerBank(ClassName)) { - auto SetResult = Function.registerInfo().setRegisterBank(*Created, Bank->ID); - if (!SetResult) - return importError(ClassNode->range(), SetResult.error()); - } else { - return fail(ClassNode->range(), - "unknown register class or bank '" + std::string(ClassName) + "'"); - } - } - - if (const yaml::Node *Preferred = mappingValue(*Element, "preferred-register")) { - std::string_view PreferredName; - PhysicalRegister Reg; - if (scalar(*Preferred, PreferredName, "physical register")) - return true; - if (!trim(PreferredName).empty()) { - if (physicalRegister(PreferredName, Preferred->range(), Reg)) - return true; - auto AddResult = Function.registerInfo().addAllocationHint(*Created, Reg); - if (!AddResult) - return importError(Preferred->range(), AddResult.error()); - } - } - if (const yaml::Node *Flags = mappingValue(*Element, "flags")) { - if (requireKind(*Flags, yaml::NodeKind::Sequence, "virtual-register flags")) - return true; - std::uint8_t Mask = 0; - for (const yaml::NodeID FlagID : Flags->sequence()) { - const yaml::Node *FlagNode = node(FlagID); - std::string_view Name; - if (FlagNode == nullptr || scalar(*FlagNode, Name, "virtual-register flag")) - return true; - const auto Flag = Target.mirText().virtualRegisterFlag(Name); - if (!Flag) - return fail(FlagNode->range(), - "use of undefined register flag '" + std::string(Name) + "'"); - if ((Mask & *Flag) != 0) - return fail(FlagNode->range(), "duplicate virtual-register flag"); - Mask = static_cast(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 Initial; - if (const yaml::Node *VirtualNode = mappingValue(*Element, "virtual-reg")) { - std::string_view Text; - if (scalar(*VirtualNode, Text, "virtual register")) - return true; - if (!trim(Text).empty()) { - Register Virtual; - if (virtualRegister(Text, VirtualNode->range(), Function, Context, Virtual)) - return true; - Initial = Virtual; - } - } - auto AddResult = Function.registerInfo().addLiveIn(Reg, Initial); - if (!AddResult) - return importError(Element->range(), AddResult.error()); - } - } - - if (const yaml::Node *CalleeSaved = mappingValue(Root, "calleeSavedRegisters")) { - if (requireKind(*CalleeSaved, yaml::NodeKind::Sequence, "callee-saved registers")) - return true; - std::vector RegistersValue; - for (const yaml::NodeID RegID : CalleeSaved->sequence()) { - const yaml::Node *RegNode = node(RegID); - PhysicalRegister Reg; - if (RegNode == nullptr || physicalRegister(*RegNode, Reg)) - return true; - RegistersValue.push_back(Reg); - } - auto SetResult = Function.records().setCalleeSavedRegisters(std::move(RegistersValue)); - if (!SetResult) - return importError(CalleeSaved->range(), SetResult.error()); - } - return false; -} - -TextRange MIRDocumentParser::scalarDiagnosticRange(const yaml::Node &Scalar, - const TextRange &ScalarRange) const noexcept { - if (Scalar.scalarStyle() != yaml::ScalarStyle::Literal && - Scalar.scalarStyle() != yaml::ScalarStyle::Folded) { - std::size_t ContentOffset = Scalar.range().Begin.Offset; - if (Scalar.scalarStyle() == yaml::ScalarStyle::SingleQuoted || - Scalar.scalarStyle() == yaml::ScalarStyle::DoubleQuoted) { - ++ContentOffset; - } - return Result.Source.range(ContentOffset + ScalarRange.Begin.Offset, - ContentOffset + ScalarRange.End.Offset); - } - - const std::size_t FirstBodyLine = Scalar.range().Begin.Line + 1U; - const std::size_t LastBodyLine = Scalar.range().End.Line; - if (FirstBodyLine > LastBodyLine || FirstBodyLine == 0) - return Scalar.range(); - - std::size_t Indent = 0; - bool FoundContent = false; - for (std::size_t Line = FirstBodyLine; Line <= LastBodyLine; ++Line) { - const std::string_view Text = Result.Source.line(Line); - std::size_t CurrentIndent = 0; - while (CurrentIndent < Text.size() && Text[CurrentIndent] == ' ') - ++CurrentIndent; - if (CurrentIndent == Text.size()) - continue; - Indent = CurrentIndent; - FoundContent = true; - break; - } - if (!FoundContent) - return Scalar.range(); - - const TextPosition DefaultPosition = Scalar.range().Begin; - const auto MapPosition = [this, FirstBodyLine, LastBodyLine, Indent, - DefaultPosition](TextPosition Position) { - const std::size_t SourceLine = FirstBodyLine + Position.Line - 1U; - if (Position.Line == 0 || SourceLine > LastBodyLine || SourceLine > Result.Source.lineCount()) { - return DefaultPosition; - } - const std::string_view SourceLineText = Result.Source.line(SourceLine); - const std::size_t BodyColumn = Position.Column == 0 ? 0 : Position.Column - 1U; - const std::size_t SourceColumn = std::min(Indent + BodyColumn, SourceLineText.size()); - const std::size_t Offset = Result.Source.lineOffsets()[SourceLine - 1U] + SourceColumn; - return Result.Source.positionAt(Offset); - }; - return {.Begin = MapPosition(ScalarRange.Begin), .End = MapPosition(ScalarRange.End)}; -} - -bool MIRDocumentParser::parseBody(const yaml::Node &Root, MachineFunction &Function, - MIParserContext &Context) { - const yaml::Node *Body = mappingValue(Root, "body"); - std::string BodyText; - TextRange BodyRange = Root.range(); - if (Body != nullptr) { - std::string_view Text; - if (scalar(*Body, Text, "machine function body")) - return true; - BodyText = Text; - BodyRange = Body->range(); - } - - if (!effectiveDataLayout().empty()) { - const LLVMConstantSyntaxOptions PointerOptions = pointerLayoutOptions(); - Context.PointerSizeInBits = [PointerOptions](std::uint32_t AddressSpace) { - return llvmPointerSizeInBits(AddressSpace, PointerOptions); - }; - } - Context.SyncScopes = ActiveDocument->SyncScopes; - Context.InternSyncScope = [this](std::string_view Name) -> Expected { - 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::numeric_limits::max())) { - return makeUnexpected(ErrorCode::Unsupported, - "MIR document contains too many synchronization scopes"); - } - const auto ID = - SyncScopeID::fromValue(static_cast(ActiveDocument->SyncScopes.size() + 1U)); - ActiveDocument->SyncScopes.push_back({.Name = std::string(Name), .ID = ID}); - return ID; - }; - MIParserOptions BodyOptions = Options.MachineInstructions; - BodyOptions.VerifyAfterParse = false; - MIParser Parser(Function, Target, - std::string(Result.Source.name()) + ":" + std::string(Function.name()), - std::move(BodyText), Context, BodyOptions); - auto Parsed = Parser.parse(); - if (!Parsed) { - if (!Parser.diagnostics().empty()) { - const TextRange Range = - Body == nullptr ? BodyRange - : scalarDiagnosticRange(*Body, Parser.diagnostics().front().Range); - return fail(Range, Parser.diagnostics().front().Message, "mir-body"); - } - return importError(BodyRange, Parsed.error(), "mir-body"); - } - return false; -} - -bool MIRDocumentParser::convertTargetNode(const yaml::Node &Source, MIRTargetNode &Destination) { - switch (Source.kind()) { - case yaml::NodeKind::Null: - Destination = MIRTargetNode::null(); - return false; - case yaml::NodeKind::Scalar: - Destination = MIRTargetNode::scalar(std::string(Source.scalar())); - return false; - case yaml::NodeKind::Sequence: { - std::vector 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 Entries; - Entries.reserve(Source.mapping().size()); - for (const yaml::MappingEntry &SourceEntry : Source.mapping()) { - const yaml::Node *ValueNode = node(SourceEntry.Value); - if (ValueNode == nullptr) - return fail(SourceEntry.KeyRange, "target MIR mapping contains an invalid node"); - MIRTargetNode Value; - if (convertTargetNode(*ValueNode, Value)) - return true; - Entries.push_back({SourceEntry.Key, std::move(Value)}); - } - Destination = MIRTargetNode::mapping(std::move(Entries)); - return false; - } - } - return fail(Source.range(), "target MIR node has an unknown kind"); -} - -bool MIRDocumentParser::parseTargetState(const yaml::Node &Root, MachineFunction &Function) { - const yaml::Node *StateNode = mappingValue(Root, "machineFunctionInfo"); - if (StateNode == nullptr) - return false; - MIRTargetNode TargetNode; - if (convertTargetNode(*StateNode, TargetNode)) - return true; - auto State = Target.mirText().parseFunctionState(TargetNode); - if (!State) - return importError(StateNode->range(), State.error(), "mir-target-state"); - auto SetResult = Function.setTargetState(std::move(*State)); - if (!SetResult) - return importError(StateNode->range(), SetResult.error(), "mir-target-state"); - return false; -} - -bool MIRDocumentParser::validateDocumentReferences(const MachineFunction &Function) { - auto Verification = verifyMIRDocumentReferences(*ActiveDocument, Function, true); - if (Verification) - return false; - return importError(Result.Source.range(0, 0), Verification.error()); -} - -} // namespace rcc::mir diff --git a/backend/lib/MIR/MIRFunctionRecordsParser.cpp b/backend/lib/MIR/MIRFunctionRecordsParser.cpp deleted file mode 100644 index 04857bb..0000000 --- a/backend/lib/MIR/MIRFunctionRecordsParser.cpp +++ /dev/null @@ -1,341 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "MIRDocumentParser.h" -#include "MIRDocumentReferenceValidation.h" -#include "MIRMetadataTextParser.h" -#include "MIRParserUtilities.h" -#include "rcc/CodeGen/MachineBasicBlock.h" -#include "rcc/CodeGen/MachineConstantPool.h" -#include "rcc/CodeGen/MachineFrameInfo.h" -#include "rcc/CodeGen/MachineFunction.h" -#include "rcc/CodeGen/MachineFunctionAttributes.h" -#include "rcc/CodeGen/MachineFunctionProperties.h" -#include "rcc/CodeGen/MachineFunctionRecords.h" -#include "rcc/CodeGen/MachineInstr.h" -#include "rcc/CodeGen/MachineJumpTableInfo.h" -#include "rcc/CodeGen/MachineMetadata.h" -#include "rcc/CodeGen/MachineRegisterInfo.h" -#include "rcc/CodeGen/MachineVerifier.h" -#include "rcc/CodeGen/TargetDescription.h" -#include "rcc/CodeGen/TargetMIRTextInfo.h" -#include "rcc/CodeGen/TargetRegisterInfo.h" -#include "rcc/MIR/LLVMConstantSyntax.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -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::max() || - Offset > std::numeric_limits::max()) { - return true; - } - Instruction = instructionAt(Function, static_cast(Block), - static_cast(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::max() || - physicalRegister(*RegNode, Reg)) { - return true; - } - Number = static_cast(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::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(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 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::max()) { - return true; - } - Values[Index] = static_cast(Wide); - } - auto AddResult = Function.records().addDebugValueSubstitution( - {.Source = {Values[0], Values[1]}, - .Destination = {Values[2], Values[3]}, - .Subregister = SubRegIndex::fromValue(Values[4])}); - if (!AddResult) - return importError(Substitution->range(), AddResult.error()); - } - } - - if (const yaml::Node *PrefetchTargets = mappingValue(Root, "prefetch-targets")) { - if (requireKind(*PrefetchTargets, yaml::NodeKind::Sequence, "prefetch targets")) - return true; - for (const yaml::NodeID TargetID : PrefetchTargets->sequence()) { - const yaml::Node *TargetNode = node(TargetID); - std::string_view Text; - if (TargetNode == nullptr || scalar(*TargetNode, Text, "prefetch target")) - return true; - - std::size_t ContentOffset = TargetNode->range().Begin.Offset; - if (TargetNode->scalarStyle() == yaml::ScalarStyle::SingleQuoted || - TargetNode->scalarStyle() == yaml::ScalarStyle::DoubleQuoted) { - ++ContentOffset; - } - const auto ErrorRange = [this, ContentOffset](std::size_t Begin, std::size_t End = 1U) { - return Result.Source.range(ContentOffset + Begin, ContentOffset + Begin + End); - }; - const auto SkipSpace = [&Text](std::size_t &Cursor) { - while (Cursor < Text.size() && (Text[Cursor] == ' ' || Text[Cursor] == '\t')) - ++Cursor; - }; - const auto ParseNumber = [&Text, &SkipSpace](std::size_t &Cursor, std::uint32_t &Value) { - SkipSpace(Cursor); - const std::size_t Begin = Cursor; - while (Cursor < Text.size() && Text[Cursor] >= '0' && Text[Cursor] <= '9') - ++Cursor; - return Begin != Cursor && parseUnsigned32(Text.substr(Begin, Cursor - Begin), Value); - }; - - std::size_t Cursor = 0; - SkipSpace(Cursor); - if (!Text.substr(Cursor).starts_with("bb_id") || - (Cursor + std::string_view("bb_id").size() < Text.size() && - Text[Cursor + std::string_view("bb_id").size()] != ' ' && - Text[Cursor + std::string_view("bb_id").size()] != '\t')) { - return fail(ErrorRange(Cursor), "expected 'bb_id'"); - } - Cursor += std::string_view("bb_id").size(); - - std::array Values{}; - if (!ParseNumber(Cursor, Values[0])) - return fail(ErrorRange(0), "expected unsigned integer"); - - SkipSpace(Cursor); - if (Cursor < Text.size() && (Text[Cursor] == ',' || Text[Cursor] == '.')) { - ++Cursor; - if (!ParseNumber(Cursor, Values[1])) - return fail(ErrorRange(0), "expected unsigned integer"); - } else if (Cursor < Text.size() && Text[Cursor] >= '0' && Text[Cursor] <= '9') { - if (!ParseNumber(Cursor, Values[1])) - return fail(ErrorRange(0), "expected unsigned integer"); - } - - SkipSpace(Cursor); - if (Cursor >= Text.size() || Text[Cursor] != ',') - return fail(ErrorRange(Cursor), "expected ','"); - ++Cursor; - if (!ParseNumber(Cursor, Values[2])) - return fail(ErrorRange(0), "expected unsigned integer"); - SkipSpace(Cursor); - if (Cursor != Text.size()) - return fail(ErrorRange(0), "unexpected content after prefetch target"); - - auto AddResult = Function.records().addPrefetchTarget( - {.Block = {Values[0], Values[1]}, .CallsiteIndex = Values[2]}); - if (!AddResult) - return importError(TargetNode->range(), AddResult.error()); - } - } - - if (const yaml::Node *EntryValues = mappingValue(Root, "entry_values")) { - if (requireKind(*EntryValues, yaml::NodeKind::Sequence, "entry values")) - return true; - constexpr std::array Keys{ - std::string_view{"entry-value-register"}, - std::string_view{"debug-info-variable"}, - std::string_view{"debug-info-expression"}, - std::string_view{"debug-info-location"}, - }; - for (const yaml::NodeID EntryID : EntryValues->sequence()) { - const yaml::Node *Entry = node(EntryID); - if (Entry == nullptr || requireKind(*Entry, yaml::NodeKind::Mapping, "entry value") || - validateKeys(*Entry, Keys, "entry value")) { - return true; - } - const yaml::Node *RegNode = mappingValue(*Entry, "entry-value-register"); - const yaml::Node *VariableNode = mappingValue(*Entry, "debug-info-variable"); - const yaml::Node *ExpressionNode = mappingValue(*Entry, "debug-info-expression"); - const yaml::Node *LocationNode = mappingValue(*Entry, "debug-info-location"); - if (RegNode == nullptr || VariableNode == nullptr || ExpressionNode == nullptr || - LocationNode == nullptr) { - return fail(Entry->range(), "entry value requires all four fixed fields"); - } - PhysicalRegister Reg; - if (physicalRegister(*RegNode, Reg)) - return true; - MachineVariableDebugInfo DebugInfo; - if (parseVariableDebugInfo(*Entry, Function, DebugInfo)) - return true; - if (!DebugInfo.Variable || !DebugInfo.Expression || !DebugInfo.Location) { - return fail(Entry->range(), "entry value debug metadata cannot be empty", - "mir-debug-metadata"); - } - auto AddResult = - Function.records().addEntryValue({.Reg = Reg, .DebugInfo = std::move(DebugInfo)}); - if (!AddResult) - return importError(Entry->range(), AddResult.error()); - } - } - return false; -} - -} // namespace rcc::mir diff --git a/backend/lib/MIR/MIRMetadataParser.cpp b/backend/lib/MIR/MIRMetadataParser.cpp deleted file mode 100644 index 63727ee..0000000 --- a/backend/lib/MIR/MIRMetadataParser.cpp +++ /dev/null @@ -1,333 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "MIRDocumentParser.h" -#include "MIRDocumentReferenceValidation.h" -#include "MIRMetadataTextParser.h" -#include "MIRParserUtilities.h" -#include "rcc/CodeGen/MachineBasicBlock.h" -#include "rcc/CodeGen/MachineConstantPool.h" -#include "rcc/CodeGen/MachineFrameInfo.h" -#include "rcc/CodeGen/MachineFunction.h" -#include "rcc/CodeGen/MachineFunctionAttributes.h" -#include "rcc/CodeGen/MachineFunctionProperties.h" -#include "rcc/CodeGen/MachineFunctionRecords.h" -#include "rcc/CodeGen/MachineInstr.h" -#include "rcc/CodeGen/MachineJumpTableInfo.h" -#include "rcc/CodeGen/MachineMetadata.h" -#include "rcc/CodeGen/MachineRegisterInfo.h" -#include "rcc/CodeGen/MachineVerifier.h" -#include "rcc/CodeGen/TargetDescription.h" -#include "rcc/CodeGen/TargetMIRTextInfo.h" -#include "rcc/CodeGen/TargetRegisterInfo.h" -#include "rcc/MIR/LLVMConstantSyntax.h" -#include "rcc/MIR/MILexer.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -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 -MIRDocumentParser::resolveMetadataReference(const MachineFunction &Function, - std::uint32_t Slot) const { - if (ActiveDocument != nullptr && ActiveDocument->EmbeddedIR && - ActiveDocument->EmbeddedIR->metadata(std::to_string(Slot)) != nullptr) { - return MachineMetadataReference::embeddedIR(Slot); - } - if (Function.metadata().node(Slot) != nullptr) - return MachineMetadataReference::machineFunction(Slot); - return makeUnexpected(ErrorCode::ParseError, - "metadata reference uses undefined slot '!" + std::to_string(Slot) + "'"); -} - -bool MIRDocumentParser::parseVariableDebugInfo(const yaml::Node &Entry, MachineFunction &Function, - MachineVariableDebugInfo &DebugInfo) { - const auto Resolve = [this, &Function](std::uint32_t Slot) { - return resolveMetadataReference(Function, Slot); - }; - if (const yaml::Node *VariableNode = mappingValue(Entry, "debug-info-variable")) { - std::string_view Text; - if (scalar(*VariableNode, Text, "debug variable")) - return true; - if (!Text.empty()) { - MetadataTextParser Parser(std::string(Text), Resolve); - auto Reference = Parser.parseReferenceOnly(); - if (!Reference) - return importError(VariableNode->range(), Reference.error(), "mir-debug-metadata"); - if (Reference->scope() != MachineMetadataScope::EmbeddedIR) { - return fail(VariableNode->range(), - "debug variable must reference embedded LLVM IR metadata", - "mir-debug-metadata"); - } - DebugInfo.Variable = *Reference; - } - } - if (const yaml::Node *ExpressionNode = mappingValue(Entry, "debug-info-expression")) { - std::string_view Text; - if (scalar(*ExpressionNode, Text, "debug expression")) - return true; - if (!Text.empty()) { - MetadataTextParser Parser(std::string(Text), Resolve); - auto Value = Parser.parseValue(); - if (!Value) - return importError(scalarDiagnosticRange(*ExpressionNode, Parser.diagnosticRange()), - Value.error(), "mir-debug-metadata"); - if (Value->reference() != nullptr && - Value->reference()->scope() != MachineMetadataScope::EmbeddedIR) { - return fail(ExpressionNode->range(), - "debug expression reference must use embedded LLVM IR metadata", - "mir-debug-metadata"); - } - DebugInfo.Expression = std::move(*Value); - } - } - if (const yaml::Node *LocationNode = mappingValue(Entry, "debug-info-location")) { - std::string_view Text; - if (scalar(*LocationNode, Text, "debug location")) - return true; - if (!Text.empty()) { - MetadataTextParser Parser(std::string(Text), Resolve); - auto Location = Parser.parseLocation(); - if (!Location) - return importError(LocationNode->range(), Location.error(), "mir-debug-metadata"); - if (Location->reference() != nullptr && - Location->reference()->scope() != MachineMetadataScope::EmbeddedIR) { - return fail(LocationNode->range(), - "variable debug location must reference embedded LLVM IR metadata", - "mir-debug-metadata"); - } - DebugInfo.Location = std::move(*Location); - } - } - if (!DebugInfo.Variable && !DebugInfo.Expression && !DebugInfo.Location) - return false; - auto Verification = DebugInfo.verifyStructure(); - if (!Verification) - return importError(Entry.range(), Verification.error(), "mir-debug-metadata"); - return false; -} - -bool MIRDocumentParser::parseMetadata(const yaml::Node &Root, MachineFunction &Function, - MIParserContext &Context) { - if (ActiveDocument->EmbeddedIR) { - for (const EmbeddedIRMetadataDefinition &Reference : - ActiveDocument->EmbeddedIR->metadataDefinitions()) { - std::uint32_t ID = 0; - if (!parseUnsigned32(Reference.Name, ID)) - continue; - if (std::ranges::any_of(Context.Metadata, - [ID](const MIMetadataSlot &Slot) { return Slot.TextID == ID; })) { - return fail(Root.range(), "embedded LLVM IR metadata slots are not unique"); - } - Context.Metadata.push_back( - {.TextID = ID, .Reference = MachineMetadataReference::embeddedIR(ID)}); - } - } - - const yaml::Node *Metadata = mappingValue(Root, "machineMetadataNodes"); - if (Metadata == nullptr) - return false; - if (requireKind(*Metadata, yaml::NodeKind::Sequence, "machine metadata nodes")) - return true; - - enum class ParsedElementKind : std::uint8_t { - Null, - Reference, - String, - }; - struct ParsedElement final { - ParsedElementKind Kind = ParsedElementKind::Reference; - std::uint32_t Reference = 0; - std::string String; - TextRange Range; - }; - struct Definition final { - const yaml::Node *Node = nullptr; - std::uint32_t Slot = 0; - bool IsDistinct = false; - std::vector Elements; - }; - std::vector Definitions; - std::unordered_set Slots; - - const auto TranslateRange = [this](const yaml::Node &Node, const TextRange &Range) { - std::size_t ContentBegin = Node.range().Begin.Offset; - if (Node.scalarStyle() == yaml::ScalarStyle::SingleQuoted || - Node.scalarStyle() == yaml::ScalarStyle::DoubleQuoted) { - ++ContentBegin; - } - return Result.Source.range(ContentBegin + Range.Begin.Offset, ContentBegin + Range.End.Offset); - }; - - for (const yaml::NodeID DefinitionID : Metadata->sequence()) { - const yaml::Node *DefinitionNode = node(DefinitionID); - std::string_view Text; - if (DefinitionNode == nullptr || scalar(*DefinitionNode, Text, "machine metadata definition")) - return true; - - MILexer Lexer("machine-metadata", std::string(Text)); - MIToken Token = Lexer.next(); - const auto LexerFailed = [this, &Lexer, DefinitionNode, &TranslateRange, &Token]() -> bool { - if (!Token.is(MITokenKind::Error)) - return false; - const auto Diagnostics = Lexer.diagnostics(); - const TextRange Range = Diagnostics.empty() - ? TranslateRange(*DefinitionNode, Token.range()) - : TranslateRange(*DefinitionNode, Diagnostics.front().Range); - return fail(Range, Diagnostics.empty() ? "invalid machine metadata node" - : Diagnostics.front().Message); - }; - const auto Advance = [&Lexer, &Token, &LexerFailed]() -> bool { - Token = Lexer.next(); - return LexerFailed(); - }; - const auto FailAtToken = [this, DefinitionNode, &TranslateRange](const MIToken &At, - std::string Message) { - return fail(TranslateRange(*DefinitionNode, At.range()), std::move(Message)); - }; - - if (LexerFailed()) - return true; - if (!Token.is(MITokenKind::Exclaim)) - return FailAtToken(Token, "expected a metadata node"); - if (Advance()) - return true; - std::uint32_t Slot = 0; - if (!Token.is(MITokenKind::IntegerLiteral) || !parseUnsigned32(Token.integerValue(), Slot)) { - return FailAtToken(Token, "expected metadata id after '!'"); - } - if (Advance()) - return true; - if (!Token.is(MITokenKind::Equal)) - return FailAtToken(Token, "expected '='"); - if (Advance()) - return true; - - const bool IsDistinct = Token.isKeyword("distinct"); - if (IsDistinct && Advance()) - return true; - if (!Token.is(MITokenKind::Exclaim)) - return FailAtToken(Token, "expected a metadata node"); - if (Advance()) - return true; - if (!Token.is(MITokenKind::LeftBrace)) - return FailAtToken(Token, "expected '{' here"); - if (Advance()) - return true; - - std::vector Elements; - if (!Token.is(MITokenKind::RightBrace)) { - while (true) { - if (Token.hasStringValue() && Token.stringValue() == "null") { - Elements.push_back({.Kind = ParsedElementKind::Null, - .Reference = 0, - .String = {}, - .Range = TranslateRange(*DefinitionNode, Token.range())}); - } else { - if (!Token.is(MITokenKind::Exclaim)) - return FailAtToken(Token, "expected '!' here"); - const TextRange ReferenceRange = TranslateRange(*DefinitionNode, Token.range()); - if (Advance()) - return true; - - if (Token.is(MITokenKind::StringConstant)) { - Elements.push_back({.Kind = ParsedElementKind::String, - .Reference = 0, - .String = std::string(Token.stringValue()), - .Range = TranslateRange(*DefinitionNode, Token.range())}); - } else { - std::uint32_t Reference = 0; - if (!Token.is(MITokenKind::IntegerLiteral) || - !parseUnsigned32(Token.integerValue(), Reference)) { - return FailAtToken(Token, "expected metadata id after '!'"); - } - Elements.push_back({.Kind = ParsedElementKind::Reference, - .Reference = Reference, - .String = {}, - .Range = ReferenceRange}); - } - } - if (Advance()) - return true; - if (!Token.is(MITokenKind::Comma)) - break; - if (Advance()) - return true; - } - } - if (!Token.is(MITokenKind::RightBrace)) - return FailAtToken(Token, "expected end of metadata node"); - if (Advance()) - return true; - if (!Token.is(MITokenKind::EndOfFile)) - return FailAtToken(Token, "expected end of metadata node"); - if (!Slots.insert(Slot).second) - return FailAtToken(Token, "Metadata id is already used"); - - Definitions.push_back({.Node = DefinitionNode, - .Slot = Slot, - .IsDistinct = IsDistinct, - .Elements = std::move(Elements)}); - Context.Metadata.push_back( - {.TextID = Slot, .Reference = MachineMetadataReference::machineFunction(Slot)}); - } - - for (const Definition &Definition : Definitions) { - MachineMetadataNode NewNode{ - .Slot = Definition.Slot, - .IsDistinct = Definition.IsDistinct, - .Elements = {}, - }; - for (const ParsedElement &Element : Definition.Elements) { - if (Element.Kind == ParsedElementKind::Null) { - NewNode.Elements.push_back(MachineMetadataElement::null()); - continue; - } - if (Element.Kind == ParsedElementKind::String) { - NewNode.Elements.push_back(MachineMetadataElement::string(Element.String)); - continue; - } - - MachineMetadataReference Reference; - if (ActiveDocument->EmbeddedIR != std::nullopt && - ActiveDocument->EmbeddedIR->metadata(std::to_string(Element.Reference)) != nullptr) { - Reference = MachineMetadataReference::embeddedIR(Element.Reference); - } else if (Slots.contains(Element.Reference)) { - Reference = MachineMetadataReference::machineFunction(Element.Reference); - } else { - return fail(Element.Range, - "use of undefined metadata '!" + std::to_string(Element.Reference) + "'"); - } - auto Resolved = MachineMetadataElement::reference(Reference); - if (!Resolved) - return importError(Element.Range, Resolved.error()); - NewNode.Elements.push_back(std::move(*Resolved)); - } - auto AddResult = Function.metadata().addNode(std::move(NewNode)); - if (!AddResult) - return importError(Definition.Node->range(), AddResult.error()); - } - return false; -} - -} // namespace rcc::mir diff --git a/backend/lib/MIR/MIRMetadataTextParser.cpp b/backend/lib/MIR/MIRMetadataTextParser.cpp deleted file mode 100644 index 7875b06..0000000 --- a/backend/lib/MIR/MIRMetadataTextParser.cpp +++ /dev/null @@ -1,259 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "MIRMetadataTextParser.h" - -#include "MIRParserUtilities.h" -#include "rcc/MIR/MIDebugSyntax.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace rcc::mir { - -using parser_detail::parseSigned; -using parser_detail::parseUnsigned; -using parser_detail::parseUnsigned32; - -Expected 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 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 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 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 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 Elements; - while (!at(MITokenKind::RightParen)) { - if (at(MITokenKind::Identifier)) { - const auto Opcode = parseDIExpressionOpcodeName(token().stringValue()); - const auto Encoding = parseDIExpressionAttributeEncodingName(token().stringValue()); - if (!Opcode && !Encoding) - return makeUnexpected(ErrorCode::ParseError, - invalidDIExpressionElementMessage(token().stringValue())); - Elements.push_back(Opcode ? *Opcode : *Encoding); - advance(); - } else if (at(MITokenKind::IntegerLiteral)) { - std::int64_t Signed = 0; - std::uint64_t Unsigned = 0; - if (parseSigned(token().integerValue(), Signed)) - Elements.push_back(static_cast(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 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 -MetadataTextParser::parseDILocation(std::vector &Frames, - std::optional &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 NestedFrames; - std::optional NestedTerminal; - std::unordered_set 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::max()) { - return makeUnexpected(ErrorCode::ParseError, - "DILocation column must be an unsigned 16-bit integer"); - } - Frame.Column = static_cast(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(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 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 Frames; - std::optional Terminal; - if (auto Parsed = parseDILocation(Frames, Terminal); !Parsed) - return std::unexpected(Parsed.error()); - auto Created = MachineDILocation::create(std::move(Frames), Terminal); - if (!Created) - return std::unexpected(Created.error()); - Location = MachineDebugLocation::inlineLocation(std::move(*Created)); - } - if (auto Finished = finish(); !Finished) - return std::unexpected(Finished.error()); - return Location; -} - -} // namespace rcc::mir diff --git a/backend/lib/MIR/MIRMetadataTextParser.h b/backend/lib/MIR/MIRMetadataTextParser.h deleted file mode 100644 index 032523d..0000000 --- a/backend/lib/MIR/MIRMetadataTextParser.h +++ /dev/null @@ -1,74 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -// NOLINTNEXTLINE(llvm-header-guard) -#ifndef RCC_LIB_MIR_MIRMETADATATEXTPARSER_H -#define RCC_LIB_MIR_MIRMETADATATEXTPARSER_H - -#include "rcc/CodeGen/MachineDebugInfo.h" -#include "rcc/CodeGen/MachineMetadata.h" -#include "rcc/MIR/MILexer.h" -#include "rcc/Support/Error.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace rcc::mir { - -class MetadataTextParser final { -public: - using Resolver = std::function(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 parseReferenceOnly(); - [[nodiscard]] Expected parseValue(); - [[nodiscard]] Expected parseLocation(); - - [[nodiscard]] const TextRange &diagnosticRange() const noexcept { - return token().range(); - } - -private: - [[nodiscard]] const MIToken &token() const noexcept { - return Tokens[Position]; - } - [[nodiscard]] bool at(MITokenKind Kind) const noexcept { - return token().is(Kind); - } - void advance() noexcept { - if (Position + 1U < Tokens.size()) - ++Position; - } - [[nodiscard]] bool consume(MITokenKind Kind) noexcept { - if (!at(Kind)) - return false; - advance(); - return true; - } - [[nodiscard]] Expected expect(MITokenKind Kind, std::string_view Description); - [[nodiscard]] Expected finish(); - [[nodiscard]] Expected parseReference(); - [[nodiscard]] Expected parseExpression(); - [[nodiscard]] Expected parseDILocation(std::vector &Frames, - std::optional &Terminal); - - MILexer Lexer; - Resolver Resolve; - std::vector Tokens; - std::size_t Position = 0; -}; - -} // namespace rcc::mir - -#endif // RCC_LIB_MIR_MIRMETADATATEXTPARSER_H diff --git a/backend/lib/MIR/MIRParserUtilities.h b/backend/lib/MIR/MIRParserUtilities.h deleted file mode 100644 index 2f69548..0000000 --- a/backend/lib/MIR/MIRParserUtilities.h +++ /dev/null @@ -1,85 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -// NOLINTNEXTLINE(llvm-header-guard) -#ifndef RCC_LIB_MIR_MIRPARSERUTILITIES_H -#define RCC_LIB_MIR_MIRPARSERUTILITIES_H - -#include -#include -#include -#include -#include - -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::max()) - return false; - Value = static_cast(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 -parsePrefixedID(std::string_view Text, std::string_view Prefix) noexcept { - Text = trim(Text); - if (!Text.starts_with(Prefix)) - return std::nullopt; - Text.remove_prefix(Prefix.size()); - const std::size_t Suffix = Text.find_first_of(". \t"); - if (Suffix != std::string_view::npos) - Text = Text.substr(0, Suffix); - std::uint32_t ID = 0; - if (!parseUnsigned32(Text, ID)) - return std::nullopt; - return ID; -} - -} // namespace rcc::mir::parser_detail - -#endif // RCC_LIB_MIR_MIRPARSERUTILITIES_H diff --git a/backend/lib/Tooling/ArtifactStore.cpp b/backend/lib/Tooling/ArtifactStore.cpp index db330b1..115f58f 100644 --- a/backend/lib/Tooling/ArtifactStore.cpp +++ b/backend/lib/Tooling/ArtifactStore.cpp @@ -3,10 +3,10 @@ #include "rcc/Tooling/ArtifactStore.h" #include "rcc/CodeGen/CompilationUnit.h" +#include "rcc/CodeGen/MIRPrinter.h" #include "rcc/CodeGen/MachineFunction.h" #include "rcc/CodeGen/MachineVerifier.h" #include "rcc/CodeGen/TargetDescription.h" -#include "rcc/MIR/MIRDocument.h" #include "rcc/Support/SHA256.h" #include "rcc/Support/TextDiagnostic.h" #include "rcc/Tooling/BackendInvocation.h" diff --git a/backend/lib/Tooling/BackendDriver.cpp b/backend/lib/Tooling/BackendDriver.cpp index c726fd3..0402f5a 100644 --- a/backend/lib/Tooling/BackendDriver.cpp +++ b/backend/lib/Tooling/BackendDriver.cpp @@ -2,8 +2,8 @@ #include "rcc/Tooling/BackendDriver.h" +#include "rcc/CodeGen/MIRPrinter.h" #include "rcc/CodeGen/MachineFunction.h" -#include "rcc/MIR/MIRDocument.h" #include "rcc/Target/RISCV/RISCVAssemblyPrinter.h" #include "rcc/Target/RISCV/RISCVObjectFile.h" diff --git a/backend/lib/Tooling/MIRFuzzer.cpp b/backend/lib/Tooling/MIRFuzzer.cpp index e75e9f9..83762c4 100644 --- a/backend/lib/Tooling/MIRFuzzer.cpp +++ b/backend/lib/Tooling/MIRFuzzer.cpp @@ -5,6 +5,7 @@ #include "rcc/CodeGen/GenericOpcodes.h" #include "rcc/CodeGen/InstructionDescriptor.h" #include "rcc/CodeGen/LowLevelType.h" +#include "rcc/CodeGen/MIRPrinter.h" #include "rcc/CodeGen/MachineBasicBlock.h" #include "rcc/CodeGen/MachineFunction.h" #include "rcc/CodeGen/MachineIRBuilder.h" @@ -13,7 +14,6 @@ #include "rcc/CodeGen/MachineRegisterInfo.h" #include "rcc/CodeGen/MachineVerifier.h" #include "rcc/CodeGen/TargetDescription.h" -#include "rcc/MIR/MIRDocument.h" #include "rcc/Target/RISCV/RISCVPipeline.h" #include "rcc/Tooling/ArtifactStore.h" #include "rcc/Tooling/BackendDriver.h" diff --git a/backend/lib/Tooling/PipelineDriver.cpp b/backend/lib/Tooling/PipelineDriver.cpp index d7a7d16..68cbb57 100644 --- a/backend/lib/Tooling/PipelineDriver.cpp +++ b/backend/lib/Tooling/PipelineDriver.cpp @@ -3,10 +3,10 @@ #include "rcc/Tooling/PipelineDriver.h" #include "rcc/CodeGen/CompilationUnit.h" +#include "rcc/CodeGen/MIRPrinter.h" #include "rcc/CodeGen/MachineFunction.h" #include "rcc/CodeGen/MachineOptimizationPipeline.h" #include "rcc/CodeGen/MachineVerifier.h" -#include "rcc/MIR/MIRDocument.h" #include "rcc/Target/RISCV/RISCVPipeline.h" #include -- cgit v1.2.3