From 6a7b083f322f4ac47e58cbf9d4d5fd4d1f6fecd0 Mon Sep 17 00:00:00 2001 From: Richard-Qin-X Date: Tue, 11 Aug 2026 17:37:42 +0800 Subject: [MIR] Accept fixed-baseline LLVM syntax --- backend/lib/MIR/EmbeddedIR.cpp | 121 +++++- backend/lib/MIR/LLVMConstantSyntax.cpp | 279 +++++++++++++- backend/lib/MIR/MIDebugSyntax.cpp | 179 ++++++++- backend/lib/MIR/MIParser.cpp | 425 ++++++++++----------- backend/lib/MIR/MIPrinter.cpp | 351 +++++++++++------ backend/lib/MIR/MIRDataParser.cpp | 2 +- backend/lib/MIR/MIRDocumentParser.cpp | 46 ++- backend/lib/MIR/MIRDocumentParser.h | 4 +- backend/lib/MIR/MIRDocumentPrinter.cpp | 261 +++++++++++-- backend/lib/MIR/MIRDocumentReferenceValidation.cpp | 18 +- backend/lib/MIR/MIRFrameParser.cpp | 12 +- backend/lib/MIR/MIRFunctionParser.cpp | 50 ++- backend/lib/MIR/MIRFunctionRecordsParser.cpp | 68 +++- backend/lib/MIR/MIRMetadataParser.cpp | 242 +++++++----- backend/lib/MIR/MIRMetadataTextParser.cpp | 10 +- backend/lib/MIR/MIRMetadataTextParser.h | 4 + 16 files changed, 1533 insertions(+), 539 deletions(-) (limited to 'backend/lib/MIR') diff --git a/backend/lib/MIR/EmbeddedIR.cpp b/backend/lib/MIR/EmbeddedIR.cpp index 662faac..93cd534 100644 --- a/backend/lib/MIR/EmbeddedIR.cpp +++ b/backend/lib/MIR/EmbeddedIR.cpp @@ -18,6 +18,7 @@ struct ParsedIdentifier final { std::string Name; TextRange Range; std::size_t End = 0; + bool IsNumericSlot = false; }; [[nodiscard]] bool isHorizontalSpace(char Character) noexcept { @@ -96,6 +97,18 @@ const EmbeddedIRFunction *EmbeddedIRReferenceIndex::function(std::string_view Na return &Functions[Position->second]; } +const EmbeddedIRFunction * +EmbeddedIRReferenceIndex::unnamedFunction(std::size_t Ordinal) const noexcept { + for (const EmbeddedIRFunction &Function : Functions) { + if (!Function.isUnnamed()) + continue; + if (Ordinal == 0) + return &Function; + --Ordinal; + } + return nullptr; +} + const EmbeddedIRReference * EmbeddedIRReferenceIndex::globalObject(std::string_view Name) const noexcept { const auto Position = GlobalNameToIndex.find(Name); @@ -152,6 +165,10 @@ public: return failureResult(); } + validateLocalReferences(); + if (Failed) + return failureResult(); + EmbeddedIRIndexResult Result; Result.Index.emplace(std::move(Index)); return Result; @@ -209,6 +226,7 @@ private: return std::nullopt; std::string Name; + bool IsNumericSlot = false; if (Line[Position] == '"') { ++Position; while (Position < End && Line[Position] != '"') { @@ -237,6 +255,7 @@ private: } ++Position; } else if (Line[Position] >= '0' && Line[Position] <= '9') { + IsNumericSlot = true; const std::size_t NameBegin = Position; while (Position < End && Line[Position] >= '0' && Line[Position] <= '9') ++Position; @@ -254,6 +273,7 @@ private: .Name = std::move(Name), .Range = Index.Source.range(LineOffset + Begin, LineOffset + Position), .End = Position, + .IsNumericSlot = IsNumericSlot, }; } @@ -464,6 +484,7 @@ private: Function.Name = Name.Name; Function.NameRange = Name.Range; Function.IsDefinition = IsDefinition; + Function.IsUnnamed = Name.IsNumericSlot; Index.Functions.push_back(std::move(Function)); Index.FunctionNameToIndex.emplace(Name.Name, IndexValue); return IndexValue; @@ -491,6 +512,9 @@ private: if (!Name || Failed) return; const std::size_t Following = skipHorizontalSpace(Line, Name->End, End); + recordLocalReference(*Name, Index.Functions[ActiveFunction.value()]); + if (Failed) + return; std::size_t Previous = IdentifierBegin; while (Previous > 0 && isHorizontalSpace(Line[Previous - 1])) --Previous; @@ -537,6 +561,7 @@ private: .Name = Name.Name, .Range = Name.Range, }); + observeNumericSlot(Name, Function); } void parseActiveFunctionLine(std::string_view Line, std::size_t LineOffset, std::size_t End) { @@ -587,10 +612,21 @@ private: return; } EmbeddedIRFunction &Function = Index.Functions[ActiveFunction.value()]; + const std::size_t PreviousBlockCount = Function.Blocks.size(); parseBlockLabel(Line, LineOffset, Begin, BodyEnd, Function); if (Failed) return; + const std::size_t FirstContent = skipHorizontalSpace(Line, Begin, BodyEnd); + if (Function.Blocks.empty() && Function.Blocks.size() == PreviousBlockCount && + FirstContent < BodyEnd) { + addImplicitEntryBlock(LineOffset + FirstContent, Function); + if (Failed) + return; + } parseLocalValueDefinition(Line, LineOffset, Begin, BodyEnd, Function); + if (Failed) + return; + parseLocalReferences(Line, LineOffset, Begin, BodyEnd, Function); if (Failed) return; @@ -624,9 +660,45 @@ private: const std::size_t BlockIndex = Function.Blocks.size(); Function.BlockNameToIndex.emplace(Name->Name, BlockIndex); Function.Blocks.push_back(EmbeddedIRReference{ - .Name = std::move(Name->Name), + .Name = Name->Name, .Range = Name->Range, }); + observeNumericSlot(*Name, Function); + } + + void addImplicitEntryBlock(std::size_t Offset, EmbeddedIRFunction &Function) { + const std::string Name = std::to_string(Function.NextUnnamedSlot++); + if (Function.BlockNameToIndex.contains(Name) || Function.ValueNameToIndex.contains(Name)) { + diagnose(Offset, Offset, "embedded LLVM IR unnamed entry block slot is already in use"); + return; + } + const std::size_t BlockIndex = Function.Blocks.size(); + Function.BlockNameToIndex.emplace(Name, BlockIndex); + Function.Blocks.push_back(EmbeddedIRReference{ + .Name = Name, + .Range = Index.Source.range(Offset, Offset), + }); + } + + void observeNumericSlot(const ParsedIdentifier &Name, EmbeddedIRFunction &Function) { + if (!Name.IsNumericSlot) + return; + std::uint64_t Slot = 0; + for (const char Digit : Name.Name) { + const std::uint64_t Value = static_cast(Digit - '0'); + if (Slot > (std::numeric_limits::max() - Value) / 10U) { + diagnose(Name.Range.Begin.Offset, Name.Range.End.Offset, + "embedded LLVM IR numeric slot is too large"); + return; + } + Slot = Slot * 10U + Value; + } + if (Slot == std::numeric_limits::max()) { + diagnose(Name.Range.Begin.Offset, Name.Range.End.Offset, + "embedded LLVM IR numeric slot is too large"); + return; + } + Function.NextUnnamedSlot = std::max(Function.NextUnnamedSlot, Slot + 1U); } void parseLocalValueDefinition(std::string_view Line, std::size_t LineOffset, std::size_t Begin, @@ -657,6 +729,53 @@ private: }); } + void recordLocalReference(const ParsedIdentifier &Name, EmbeddedIRFunction &Function) { + Function.LocalReferences.push_back(EmbeddedIRReference{ + .Name = Name.Name, + .Range = Name.Range, + }); + } + + void parseLocalReferences(std::string_view Line, std::size_t LineOffset, std::size_t Begin, + std::size_t End, EmbeddedIRFunction &Function) { + bool InQuote = false; + for (std::size_t Position = Begin; Position < End; ++Position) { + const char Character = Line[Position]; + if (InQuote && Character == '\\' && Position + 2 < End && + isHexDigit(Line[Position + 1]) && isHexDigit(Line[Position + 2])) { + Position += 2; + continue; + } + if (Character == '"') { + InQuote = !InQuote; + continue; + } + if (InQuote || Character != '%') + continue; + auto Name = parseIdentifier(Line, LineOffset, Position, End, '%'); + if (!Name || Failed) + continue; + recordLocalReference(*Name, Function); + Position = Name->End - 1; + } + } + + void validateLocalReferences() { + for (const EmbeddedIRFunction &Function : Index.Functions) { + if (!Function.IsDefinition) + continue; + for (const EmbeddedIRReference &Reference : Function.LocalReferences) { + if (Function.ValueNameToIndex.contains(Reference.Name) || + Function.BlockNameToIndex.contains(Reference.Name) || + Index.TypeNameToIndex.contains(Reference.Name)) + continue; + diagnose(Reference.Range.Begin.Offset, Reference.Range.End.Offset, + "use of undefined value '%" + Reference.Name + "'"); + return; + } + } + } + void parseGlobalObject(std::string_view Line, std::size_t LineOffset, std::size_t Position, std::size_t End) { auto Name = parseIdentifier(Line, LineOffset, Position, End, '@'); diff --git a/backend/lib/MIR/LLVMConstantSyntax.cpp b/backend/lib/MIR/LLVMConstantSyntax.cpp index 8bc2c18..a2025dc 100644 --- a/backend/lib/MIR/LLVMConstantSyntax.cpp +++ b/backend/lib/MIR/LLVMConstantSyntax.cpp @@ -121,6 +121,17 @@ public: Limbs.push_back(static_cast(Carry)); } + [[nodiscard]] std::uint32_t divideSmall(std::uint32_t Divisor) { + std::uint64_t Remainder = 0; + for (std::size_t Index = Limbs.size(); Index != 0; --Index) { + const std::uint64_t Dividend = (Remainder << 32U) | Limbs[Index - 1U]; + Limbs[Index - 1U] = static_cast(Dividend / Divisor); + Remainder = Dividend % Divisor; + } + normalize(); + return static_cast(Remainder); + } + void addSmall(std::uint32_t Value) { std::uint64_t Carry = Value; std::size_t Index = 0; @@ -159,6 +170,51 @@ public: normalize(); } + [[nodiscard]] std::size_t countrZero() const noexcept { + std::size_t Result = 0; + for (const std::uint32_t Limb : Limbs) { + if (Limb == 0) { + Result += 32U; + continue; + } + return Result + static_cast(std::countr_zero(Limb)); + } + return Result; + } + + void shiftRight(std::size_t Amount) noexcept { + if (Amount == 0 || isZero()) + return; + const std::size_t LimbOffset = Amount / 32U; + if (LimbOffset >= Limbs.size()) { + Limbs.clear(); + return; + } + if (LimbOffset != 0) + Limbs.erase(Limbs.begin(), Limbs.begin() + static_cast(LimbOffset)); + const unsigned BitOffset = static_cast(Amount % 32U); + if (BitOffset != 0) { + std::uint32_t Carry = 0; + for (std::size_t Index = Limbs.size(); Index != 0; --Index) { + const std::uint32_t Current = Limbs[Index - 1U]; + Limbs[Index - 1U] = (Current >> BitOffset) | Carry; + Carry = Current << (32U - BitOffset); + } + } + normalize(); + } + + [[nodiscard]] std::string toDecimal() const { + if (isZero()) + return "0"; + BigUnsigned Remaining = *this; + std::string Digits; + while (!Remaining.isZero()) + Digits.push_back(static_cast('0' + Remaining.divideSmall(10))); + std::ranges::reverse(Digits); + return Digits; + } + [[nodiscard]] BigUnsigned shiftedLeft(std::size_t Amount) const { if (isZero() || Amount == 0) return *this; @@ -2382,6 +2438,225 @@ Expected printType(const GenericConstantType &Type) { return makeUnexpected(ErrorCode::VerificationError, "generic constant type is invalid"); } +enum class PrintedFloatCategory : std::uint8_t { + Zero, + Finite, + Infinity, + QuietNaN, + SignalingNaN, +}; + +struct PrintedBinaryFloat final { + PrintedFloatCategory Category = PrintedFloatCategory::Zero; + bool Negative = false; + BigUnsigned Significand; + std::int32_t BinaryExponent = 0; + BigUnsigned NaNPayload; + std::uint32_t QuietNaNBit = 0; +}; + +[[nodiscard]] std::uint64_t extractFloatField(std::span Words, + std::uint32_t Begin, std::uint32_t Width) noexcept { + std::uint64_t Result = 0; + for (std::uint32_t Bit = 0; Bit < Width; ++Bit) { + if (hasFloatBit(Words, Begin + Bit)) + Result |= std::uint64_t{1} << Bit; + } + return Result; +} + +[[nodiscard]] BigUnsigned extractFloatBits(std::span Words, + std::uint32_t Begin, std::uint32_t Width) { + BigUnsigned Result; + for (std::uint32_t Bit = 0; Bit < Width; ++Bit) { + if (hasFloatBit(Words, Begin + Bit)) + Result.setBit(Bit); + } + return Result; +} + +[[nodiscard]] std::optional decodePrintedFloat(const FloatingPointBits &Bits) { + std::optional Format = binaryFloatFormat(Bits.semantics()); + std::span Words = Bits.words(); + if (!Format) + return std::nullopt; + + if (Bits.semantics() == FloatSemantics::PPCDoubleDouble || + Bits.semantics() == FloatSemantics::PPCDoubleDoubleLegacy) { + if (Words.size() != 2 || Words[1] != 0) + return std::nullopt; + Format = binaryFloatFormat(FloatSemantics::IEEEDouble); + Words = Words.first(1); + } + if (!Format || Format->ExponentBitWidth == 0) + return std::nullopt; + + PrintedBinaryFloat Result; + Result.Negative = hasFloatBit(Words, Format->StorageBitWidth - 1U); + const std::uint32_t StoredSignificandWidth = + Format->HasExplicitIntegerBit ? Format->Precision : Format->Precision - 1U; + const std::uint64_t Exponent = + extractFloatField(Words, StoredSignificandWidth, Format->ExponentBitWidth); + const std::uint64_t MaximumExponent = (std::uint64_t{1} << Format->ExponentBitWidth) - 1U; + + if (Exponent == MaximumExponent) { + const std::uint32_t PayloadWidth = Format->Precision - 1U; + Result.NaNPayload = extractFloatBits(Words, 0, PayloadWidth); + if (Result.NaNPayload.isZero()) { + Result.Category = PrintedFloatCategory::Infinity; + return Result; + } + Result.QuietNaNBit = PayloadWidth - 1U; + Result.Category = hasFloatBit(Words, Result.QuietNaNBit) ? PrintedFloatCategory::QuietNaN + : PrintedFloatCategory::SignalingNaN; + return Result; + } + + Result.Significand = extractFloatBits(Words, 0, StoredSignificandWidth); + if (Exponent != 0 && !Format->HasExplicitIntegerBit) + Result.Significand.setBit(Format->Precision - 1U); + if (Result.Significand.isZero()) { + Result.Category = PrintedFloatCategory::Zero; + return Result; + } + + const std::int64_t Bias = 1 - static_cast(Format->MinExponent); + const std::int64_t UnbiasedExponent = + Exponent == 0 ? Format->MinExponent : static_cast(Exponent) - Bias; + Result.BinaryExponent = static_cast(UnbiasedExponent - (Format->Precision - 1U)); + Result.Category = PrintedFloatCategory::Finite; + return Result; +} + +[[nodiscard]] std::string printNaNPayload(BigUnsigned Payload, std::uint32_t QuietNaNBit) { + Payload.clearBit(QuietNaNBit); + const auto Width = static_cast(std::max(Payload.bitWidth(), 1U)); + std::array Words{Payload.word(0), Payload.word(1)}; + std::string Digits = hexadecimalWords(Words, Width); + const std::size_t FirstNonzero = Digits.find_first_not_of('0'); + if (FirstNonzero == std::string::npos) + return "0x0"; + Digits.erase(0, FirstNonzero); + return "0x" + Digits; +} + +[[nodiscard]] std::string formatFiniteFloat(PrintedBinaryFloat Value) { + std::string Result; + if (Value.Negative) + Result.push_back('-'); + if (Value.Category == PrintedFloatCategory::Zero) { + Result.append("0.000000e+00"); + return Result; + } + + const std::size_t TrailingZeros = Value.Significand.countrZero(); + Value.Significand.shiftRight(TrailingZeros); + Value.BinaryExponent += static_cast(TrailingZeros); + + std::int32_t DecimalExponent = 0; + if (Value.BinaryExponent >= 0) { + Value.Significand = + Value.Significand.shiftedLeft(static_cast(Value.BinaryExponent)); + } else { + DecimalExponent = Value.BinaryExponent; + for (std::int32_t Power = 0; Power < -Value.BinaryExponent; ++Power) + Value.Significand.multiplySmall(5); + } + + std::string Digits = Value.Significand.toDecimal(); + while (Digits.size() > 1U && Digits.back() == '0') { + Digits.pop_back(); + ++DecimalExponent; + } + + constexpr std::size_t FormatPrecision = 6; + if (Digits.size() > FormatPrecision) { + std::size_t Removed = Digits.size() - FormatPrecision; + const bool RoundUp = Digits[FormatPrecision] >= '5'; + Digits.resize(FormatPrecision); + if (RoundUp) { + std::size_t Position = Digits.size(); + while (Position != 0 && Digits[Position - 1U] == '9') { + Digits[Position - 1U] = '0'; + --Position; + } + if (Position == 0) + Digits.insert(Digits.begin(), '1'); + else + ++Digits[Position - 1U]; + } + while (Digits.size() > 1U && Digits.back() == '0') { + Digits.pop_back(); + ++Removed; + } + DecimalExponent += static_cast(Removed); + } + + DecimalExponent += static_cast(Digits.size() - 1U); + Result.push_back(Digits.front()); + Result.push_back('.'); + Result.append(Digits.substr(1)); + Result.append(7U - Digits.size(), '0'); + Result.push_back('e'); + Result.push_back(DecimalExponent < 0 ? '-' : '+'); + const std::uint32_t Magnitude = static_cast( + DecimalExponent < 0 ? -static_cast(DecimalExponent) : DecimalExponent); + std::string Exponent = std::to_string(Magnitude); + if (Exponent.size() < 2U) + Result.push_back('0'); + Result.append(Exponent); + return Result; +} + +[[nodiscard]] std::string exactFloatingText(const FloatingPointBits &Bits) { + return "f0x" + hexadecimalWords(Bits.words(), Bits.bitWidth()); +} + +[[nodiscard]] std::string canonicalFloatingText(const FloatingPointBits &Bits) { + auto Decoded = decodePrintedFloat(Bits); + if (!Decoded) + return exactFloatingText(Bits); + + std::string Candidate; + switch (Decoded->Category) { + case PrintedFloatCategory::Zero: + case PrintedFloatCategory::Finite: + Candidate = formatFiniteFloat(std::move(*Decoded)); + break; + case PrintedFloatCategory::Infinity: + Candidate = Decoded->Negative ? "-inf" : "+inf"; + break; + case PrintedFloatCategory::QuietNaN: { + BigUnsigned Payload = Decoded->NaNPayload; + Payload.clearBit(Decoded->QuietNaNBit); + if (Payload.isZero()) { + Candidate = Decoded->Negative ? "-qnan" : "+qnan"; + break; + } + Candidate = Decoded->Negative ? "-nan(" : "+nan("; + Candidate.append(printNaNPayload(Decoded->NaNPayload, Decoded->QuietNaNBit)); + Candidate.push_back(')'); + break; + } + case PrintedFloatCategory::SignalingNaN: + Candidate = Decoded->Negative ? "-snan(" : "+snan("; + Candidate.append(printNaNPayload(Decoded->NaNPayload, Decoded->QuietNaNBit)); + Candidate.push_back(')'); + break; + } + + auto Type = GenericConstantType::floating(Bits.semantics()); + if (!Type) + return exactFloatingText(Bits); + auto TypeText = printType(*Type); + if (!TypeText) + return exactFloatingText(Bits); + auto Parsed = parseLLVMConstant(*TypeText + " " + Candidate); + if (!Parsed || !Parsed->FloatingValue || *Parsed->FloatingValue != Bits) + return exactFloatingText(Bits); + return Candidate; +} + [[nodiscard]] std::string_view expressionOpcodeName(GenericConstantExpressionOpcode Opcode) noexcept { switch (Opcode) { @@ -2464,9 +2739,7 @@ Expected printValue(const GenericConstantData &Value, bool IncludeT if (!Value.FloatingValue) return makeUnexpected(ErrorCode::VerificationError, "floating constant is missing its floating payload"); - Result.append("f0x"); - Result.append(hexadecimalWords(Value.FloatingValue.value().words(), - Value.FloatingValue.value().bitWidth())); + Result.append(canonicalFloatingText(*Value.FloatingValue)); return Result; case GenericConstantKind::NullPointer: Result.append("null"); diff --git a/backend/lib/MIR/MIDebugSyntax.cpp b/backend/lib/MIR/MIDebugSyntax.cpp index fc08df6..e614a21 100644 --- a/backend/lib/MIR/MIDebugSyntax.cpp +++ b/backend/lib/MIR/MIDebugSyntax.cpp @@ -5,7 +5,10 @@ #include #include #include +#include #include +#include +#include #include #include #include @@ -20,26 +23,8 @@ namespace { return Error == std::errc{} && End == Text.data() + Text.size(); } -} // 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; - - constexpr auto NamedOpcodes = std::to_array>({ +[[nodiscard]] const auto &namedOpcodes() { + static constexpr auto Opcodes = std::to_array>({ {"DW_OP_addr", 0x03}, {"DW_OP_deref", 0x06}, {"DW_OP_const1u", 0x08}, @@ -114,7 +99,104 @@ std::optional parseDIExpressionOpcodeName(std::string_view Name) {"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()) @@ -122,4 +204,61 @@ std::optional parseDIExpressionOpcodeName(std::string_view Name) 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/MIParser.cpp b/backend/lib/MIR/MIParser.cpp index 483c2f2..01ecbf5 100644 --- a/backend/lib/MIR/MIParser.cpp +++ b/backend/lib/MIR/MIParser.cpp @@ -2,6 +2,7 @@ #include "rcc/MIR/MIParser.h" +#include "rcc/CodeGen/InlineAsm.h" #include "rcc/CodeGen/InstructionDescriptor.h" #include "rcc/CodeGen/MachineBasicBlock.h" #include "rcc/CodeGen/MachineCFGUpdater.h" @@ -13,6 +14,7 @@ #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" @@ -113,60 +115,6 @@ namespace { Token.isKeyword("llvm_vector_register_mask"); } -[[nodiscard]] std::optional hexDigit(char Character) noexcept { - if (Character >= '0' && Character <= '9') - return static_cast(Character - '0'); - if (Character >= 'a' && Character <= 'f') - return static_cast(Character - 'a') + 10U; - if (Character >= 'A' && Character <= 'F') - return static_cast(Character - 'A') + 10U; - return std::nullopt; -} - -[[nodiscard]] bool parseHexWords(std::string_view Digits, std::uint32_t BitWidth, - std::vector &Words) { - if (Digits.empty() || Digits.size() > (static_cast(BitWidth) + 3U) / 4U) - return false; - Words.assign((static_cast(BitWidth) + 63U) / 64U, 0); - std::size_t BitOffset = 0; - for (auto Iterator = Digits.rbegin(); Iterator != Digits.rend(); ++Iterator) { - const auto Digit = hexDigit(*Iterator); - if (!Digit) - return false; - const std::size_t WordIndex = BitOffset / 64U; - const unsigned Shift = static_cast(BitOffset % 64U); - Words[WordIndex] |= static_cast(*Digit) << Shift; - BitOffset += 4U; - } - if (BitWidth % 64U != 0) - Words.back() &= (std::uint64_t{1} << (BitWidth % 64U)) - 1U; - return true; -} - -[[nodiscard]] bool isZeroWords(std::span Words) noexcept { - return std::ranges::all_of(Words, [](std::uint64_t Word) { return Word == 0; }); -} - -[[nodiscard]] bool magnitudeFitsSignedNegative(std::span Words, - std::uint32_t BitWidth) noexcept { - const std::size_t SignWord = (BitWidth - 1U) / 64U; - const unsigned SignBit = (BitWidth - 1U) % 64U; - for (std::size_t Index = SignWord + 1U; Index < Words.size(); ++Index) { - if (Words[Index] != 0) - return false; - } - const std::uint64_t SignMask = std::uint64_t{1} << SignBit; - if ((Words[SignWord] & ~SignMask) != 0) - return false; - if ((Words[SignWord] & SignMask) == 0) - return true; - for (std::size_t Index = 0; Index < SignWord; ++Index) { - if (Words[Index] != 0) - return false; - } - return true; -} - [[nodiscard]] Expected parseIntegerImmediateValue(std::uint32_t BitWidth, std::string_view Spelling) { if (BitWidth == 0 || BitWidth > IntegerImmediate::MaxBitWidth) @@ -188,17 +136,13 @@ namespace { Word = static_cast(Product); Carry = static_cast(Product >> 64U); } - if (Carry != 0) - return makeUnexpected(ErrorCode::ParseError, "typed integer does not fit its bit width"); - if (BitWidth % 64U != 0 && (Words.back() >> (BitWidth % 64U)) != 0) { - return makeUnexpected(ErrorCode::ParseError, "typed integer does not fit its bit width"); - } } + // 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) { - if (isZeroWords(Words) || !magnitudeFitsSignedNegative(Words, BitWidth)) - return makeUnexpected(ErrorCode::ParseError, - "negative typed integer does not fit its bit width"); for (std::uint64_t &Word : Words) Word = ~Word; std::uint64_t Carry = 1; @@ -213,51 +157,6 @@ namespace { return IntegerImmediate::create(BitWidth, Words); } -[[nodiscard]] std::uint16_t floatToHalfBits(float Value) noexcept { - const std::uint32_t Bits = std::bit_cast(Value); - const std::uint32_t Sign = (Bits >> 16U) & 0x8000U; - const std::uint32_t Exponent = (Bits >> 23U) & 0xFFU; - const std::uint32_t Fraction = Bits & 0x7FFFFFU; - if (Exponent == 0xFFU) { - const std::uint32_t Payload = Fraction == 0 ? 0 : std::max(1U, Fraction >> 13U); - return static_cast(Sign | 0x7C00U | Payload); - } - - const int HalfExponent = static_cast(Exponent) - 127 + 15; - if (HalfExponent >= 31) - return static_cast(Sign | 0x7C00U); - if (HalfExponent <= 0) { - if (HalfExponent < -10) - return static_cast(Sign); - const std::uint32_t Mantissa = Fraction | 0x800000U; - const unsigned Shift = static_cast(14 - HalfExponent); - std::uint32_t Rounded = Mantissa >> Shift; - const std::uint32_t Remainder = Mantissa & ((std::uint32_t{1} << Shift) - 1U); - const std::uint32_t Halfway = std::uint32_t{1} << (Shift - 1U); - if (Remainder > Halfway || (Remainder == Halfway && (Rounded & 1U) != 0)) - ++Rounded; - return static_cast(Sign | Rounded); - } - - std::uint32_t Rounded = Fraction >> 13U; - const std::uint32_t Remainder = Fraction & 0x1FFFU; - if (Remainder > 0x1000U || (Remainder == 0x1000U && (Rounded & 1U) != 0)) { - ++Rounded; - if (Rounded == 0x400U) - return static_cast(Sign | - (static_cast(HalfExponent + 1) << 10U)); - } - return static_cast(Sign | (static_cast(HalfExponent) << 10U) | - Rounded); -} - -[[nodiscard]] std::uint16_t floatToBFloatBits(float Value) noexcept { - std::uint32_t Bits = std::bit_cast(Value); - const std::uint32_t Bias = 0x7FFFU + ((Bits >> 16U) & 1U); - Bits += Bias; - return static_cast(Bits >> 16U); -} - [[nodiscard]] std::optional minimumTypeSizeInBits(LLT Type) noexcept { if (!Type.isValid() || Type.isToken()) return std::nullopt; @@ -493,6 +392,8 @@ private: [[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); @@ -684,7 +585,8 @@ bool MIParser::Impl::parseScalarLowLevelType(const MIToken &TypeToken, LLT &Resu std::uint64_t Value = 0; if (!parseUnsignedDigits(Digits, Value)) - return fail(TypeToken, "low-level type suffix must be an unsigned decimal integer"); + return fail(TypeToken, + "expected integers after 's'/'i'/'f'/'bf'/'p' type identifier"); Expected Created = makeUnexpected(ErrorCode::ParseError, "invalid low-level type"); switch (Kind) { @@ -739,40 +641,67 @@ bool MIParser::Impl::parseScalarLowLevelType(const MIToken &TypeToken, LLT &Resu 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 a scalar, pointer, or vector low-level type"); + 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 'x' after 'vscale' in a scalable vector type"); + 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 fail("expected an unsigned vector element count"); + return VectorError(); advance(); if (!at(MITokenKind::Identifier) || token().stringValue() != "x") - return fail("expected 'x' after the vector element count"); + 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 (expect(MITokenKind::Greater, "'>' after the vector low-level type")) - return true; + if (!consume(MITokenKind::Greater)) + return VectorError(); Expected Created = Scalable ? LLT::makeScalableVector(ElementCount, ElementType) : LLT::makeFixedVector(ElementCount, ElementType); @@ -821,6 +750,8 @@ bool MIParser::Impl::parseBlockAttributes(MachineBasicBlock *Block) { } 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) @@ -915,7 +846,14 @@ bool MIParser::Impl::parseBlockDefinition(bool Create, std::uint32_t &ID) { 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 #" + std::to_string(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()); @@ -1000,6 +938,12 @@ Register MIParser::Impl::getOrCreateVirtualRegister(const MIToken &Token) { 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; } @@ -1345,96 +1289,22 @@ bool MIParser::Impl::parseFloatingImmediateOperand(ParsedOperand &Result) { const auto Semantics = floatSemanticsForKeyword(Begin.stringValue()); if (!Semantics) return fail(Begin, "unknown floating-point immediate type"); - const std::uint32_t BitWidth = getFloatSemanticsBitWidth(*Semantics).value_or(0); advance(); if (!at(MITokenKind::FloatingPointLiteral) && !at(MITokenKind::HexLiteral)) return fail("expected a floating-point literal"); const MIToken Literal = token(); - const std::string_view Spelling = Literal.spelling(); - std::vector Words; - if (Spelling.starts_with("f0x") || Spelling.starts_with("f0X")) { - if (!parseHexWords(Spelling.substr(3), BitWidth, Words)) - return fail(Literal, "raw floating-point literal does not fit its semantics"); - } else if (Spelling.starts_with("0x") || Spelling.starts_with("0X")) { - std::string_view Digits = Spelling.substr(2); - char Prefix = '\0'; - if (!Digits.empty() && !hexDigit(Digits.front())) { - Prefix = Digits.front(); - Digits.remove_prefix(1); - } - char ExpectedPrefix = '\0'; - switch (*Semantics) { - case FloatSemantics::IEEEHalf: - ExpectedPrefix = 'H'; - break; - case FloatSemantics::BFloat: - ExpectedPrefix = 'R'; - break; - case FloatSemantics::X87DoubleExtended: - ExpectedPrefix = 'K'; - break; - case FloatSemantics::IEEEQuad: - ExpectedPrefix = 'L'; - break; - case FloatSemantics::PPCDoubleDouble: - case FloatSemantics::PPCDoubleDoubleLegacy: - ExpectedPrefix = 'M'; - break; - default: - break; - } - if (ExpectedPrefix != '\0') { - if (Prefix != ExpectedPrefix || !parseHexWords(Digits, BitWidth, Words)) - return fail(Literal, "floating-point hexadecimal prefix does not match its semantics"); - } else if (Prefix != '\0') { - return fail(Literal, "unexpected floating-point hexadecimal prefix"); - } else if (*Semantics == FloatSemantics::IEEESingle) { - std::uint64_t DoubleBits = 0; - if (!parseHexadecimal(Spelling, DoubleBits)) - return fail(Literal, "invalid IEEE single hexadecimal literal"); - const float Value = static_cast(std::bit_cast(DoubleBits)); - Words = {std::bit_cast(Value)}; - } else if (*Semantics == FloatSemantics::IEEEDouble) { - std::uint64_t DoubleBits = 0; - if (!parseHexadecimal(Spelling, DoubleBits)) - return fail(Literal, "invalid IEEE double hexadecimal literal"); - Words = {DoubleBits}; - } else { - return fail(Literal, "floating-point literal requires its semantics prefix"); - } - } else { - const char *First = Spelling.data(); - const char *Last = First + Spelling.size(); - if (*Semantics == FloatSemantics::IEEEDouble) { - double Value = 0; - const auto Parsed = std::from_chars(First, Last, Value, std::chars_format::general); - if (Parsed.ec != std::errc{} || Parsed.ptr != Last) - return fail(Literal, "invalid IEEE double decimal literal"); - Words = {std::bit_cast(Value)}; - } else if (*Semantics == FloatSemantics::IEEESingle || *Semantics == FloatSemantics::IEEEHalf || - *Semantics == FloatSemantics::BFloat) { - float Value = 0; - const auto Parsed = std::from_chars(First, Last, Value, std::chars_format::general); - if (Parsed.ec != std::errc{} || Parsed.ptr != Last) - return fail(Literal, "invalid floating-point decimal literal"); - if (*Semantics == FloatSemantics::IEEESingle) - Words = {std::bit_cast(Value)}; - else if (*Semantics == FloatSemantics::IEEEHalf) - Words = {floatToHalfBits(Value)}; - else - Words = {floatToBFloatBits(Value)}; - } else { - return fail(Literal, "this floating-point semantics requires an exact hexadecimal literal"); - } - } - - auto Bits = FloatingPointBits::create(*Semantics, Words); - if (!Bits) - return importError(Literal, Bits.error()); + 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(*Bits), + MachineOperand::floatingImmediate(*Constant->FloatingValue), std::nullopt, {Begin.range().Begin, token().range().Begin}, }; @@ -1485,10 +1355,10 @@ bool MIParser::Impl::parseMetadataOperand(ParsedOperand &Result) { while (!at(MITokenKind::RightParen)) { if (at(MITokenKind::Identifier)) { const auto Opcode = parseDIExpressionOpcodeName(token().stringValue()); - if (!Opcode) - return fail("unknown debug expression opcode '" + std::string(token().stringValue()) + - "'"); - Elements.push_back(*Opcode); + 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; @@ -1855,10 +1725,22 @@ bool MIParser::Impl::parseMemoryBase(MemoryBase &Result, std::int64_t &Offset) { break; case MITokenKind::IRValue: case MITokenKind::NamedIRValue: - case MITokenKind::QuotedIRValue: Result = MemoryBase::irValue(symbolName(BaseToken)); advance(); break; + case MITokenKind::QuotedIRValue: { + LLVMConstantSyntaxOptions ConstantOptions; + ConstantOptions.DefaultPointerSizeInBits = Context.DefaultPointerSizeInBits; + auto Constant = parseLLVMConstant(BaseToken.stringValue(), ConstantOptions); + 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(); @@ -2021,6 +1903,7 @@ bool MIParser::Impl::parseMemoryOperand(const MachineMemOperand *&Result) { 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"); } @@ -2044,21 +1927,27 @@ bool MIParser::Impl::parseMemoryOperand(const MachineMemOperand *&Result) { 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")) { - if (parseAlignment(Description.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")) { - Alignment BaseAlignment; if (parseAlignment(BaseAlignment)) return true; - Description.BaseAlign = BaseAlignment; } else if (atKeyword("addrspace")) { advance(); if (!at(MITokenKind::IntegerLiteral) || parseUnsigned32(token(), Description.AddressSpace) || @@ -2088,6 +1977,19 @@ bool MIParser::Impl::parseMemoryOperand(const MachineMemOperand *&Result) { 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; @@ -2527,6 +2429,78 @@ bool MIParser::Impl::parseOperand(bool IsDef, ParsedOperand &Result) { 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")) @@ -2566,7 +2540,7 @@ bool MIParser::Impl::parseBlockSuccessors(MachineBasicBlock &Block) { return false; do { if (!at(MITokenKind::MachineBasicBlock)) - return fail("expected a machine basic block successor"); + return fail("expected a machine basic block reference"); MachineBasicBlock *Successor = blockFor(token()); if (Successor == nullptr) return true; @@ -2575,7 +2549,7 @@ bool MIParser::Impl::parseBlockSuccessors(MachineBasicBlock &Block) { if (consume(MITokenKind::LeftParen)) { std::uint32_t Weight = 0; if (parseUnsigned32(token(), Weight)) - return fail("invalid successor probability weight"); + return fail("expected unsigned integer"); Probability = BranchProbability::fromRaw(Weight); advance(); if (expect(MITokenKind::RightParen, "')' after successor weight")) @@ -2623,6 +2597,7 @@ bool MIParser::Impl::parseInstruction(MachineBasicBlock &Block, bool InBundle, 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") && @@ -2633,7 +2608,16 @@ bool MIParser::Impl::parseInstruction(MachineBasicBlock &Block, bool InBundle, if (Operands.size() >= Options.MaximumOperandsPerInstruction) return fail("instruction operand count exceeds parser limit"); ParsedOperand Operand{MachineOperand::immediate(0), std::nullopt, {}}; - if (parseOperand(false, Operand)) + 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) || @@ -2885,6 +2869,7 @@ bool MIParser::Impl::inferSuccessors() { continue; bool BlocksFallthrough = true; + std::vector Successors; for (MachineInstr *Instruction = Block->firstInstruction(); Instruction != nullptr; Instruction = Instruction->next()) { const InstructionDescriptor *Descriptor = @@ -2894,17 +2879,25 @@ bool MIParser::Impl::inferSuccessors() { if (Descriptor->Flags.has(InstructionFlag::Branch)) { for (const MachineOperand &Operand : Instruction->operands()) { if (const BasicBlockOperand *TargetBlock = Operand.getIf()) { - auto Added = Function.cfgUpdater().addSuccessor(*Block, *TargetBlock->Block); - if (!Added) - return importError(token(), Added.error()); + 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) { - auto Added = Function.cfgUpdater().addSuccessor(*Block, *Block->next()); + 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()); } diff --git a/backend/lib/MIR/MIPrinter.cpp b/backend/lib/MIR/MIPrinter.cpp index 8ad6d6c..9e86b72 100644 --- a/backend/lib/MIR/MIPrinter.cpp +++ b/backend/lib/MIR/MIPrinter.cpp @@ -2,6 +2,7 @@ #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" @@ -14,6 +15,8 @@ #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 @@ -83,52 +86,6 @@ namespace { return Result; } -[[nodiscard]] std::string unsignedDecimal(std::span InputWords) { - std::vector Words(InputWords.begin(), InputWords.end()); - while (!Words.empty() && Words.back() == 0) - Words.pop_back(); - if (Words.empty()) - return "0"; - - std::string Reversed; - while (!Words.empty()) { - std::uint64_t Remainder = 0; - for (std::size_t Index = Words.size(); Index != 0; --Index) { - const unsigned __int128 Dividend = - (static_cast(Remainder) << 64U) | Words[Index - 1U]; - Words[Index - 1U] = static_cast(Dividend / 10U); - Remainder = static_cast(Dividend % 10U); - } - Reversed.push_back(static_cast('0' + Remainder)); - while (!Words.empty() && Words.back() == 0) - Words.pop_back(); - } - std::ranges::reverse(Reversed); - return Reversed; -} - -[[nodiscard]] std::string_view floatKeyword(FloatSemantics Semantics) noexcept { - switch (Semantics) { - case FloatSemantics::IEEEHalf: - return "half"; - case FloatSemantics::BFloat: - return "bfloat"; - case FloatSemantics::IEEESingle: - return "float"; - case FloatSemantics::IEEEDouble: - return "double"; - case FloatSemantics::IEEEQuad: - return "fp128"; - case FloatSemantics::PPCDoubleDouble: - case FloatSemantics::PPCDoubleDoubleLegacy: - return "ppc_fp128"; - case FloatSemantics::X87DoubleExtended: - return "x86_fp80"; - default: - return {}; - } -} - [[nodiscard]] std::string_view integerPredicateName(IntegerPredicate Predicate) noexcept { switch (Predicate) { case IntegerPredicate::Equal: @@ -314,9 +271,13 @@ private: [[nodiscard]] Expected initialize(); [[nodiscard]] Expected printBlock(const MachineBasicBlock &Block); [[nodiscard]] Expected printInstruction(const MachineInstr &Instruction); - [[nodiscard]] Expected printOperand(const MachineOperand &Operand, bool IsExplicitDef); + [[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 IsExplicitDef, bool PrintVirtualType); [[nodiscard]] Expected printTargetFlags(std::uint32_t Flags); [[nodiscard]] Expected printMetadataReference(MachineMetadataReference Reference); [[nodiscard]] Expected printDILocation(const MachineDILocation &Location, @@ -437,11 +398,6 @@ Expected BodyPrinter::appendPhysicalRegister(PhysicalRegister Register) { void BodyPrinter::appendBlockReference(const MachineBasicBlock &Block) { Output.append("%bb."); Output.append(std::to_string(BlockIDs.at(&Block))); - const auto &Name = Block.attributes().name(); - if (Name) { - Output.push_back('.'); - Output.append(quoteName(*Name)); - } } void BodyPrinter::appendOffset(std::int64_t Offset) { @@ -528,7 +484,7 @@ Expected BodyPrinter::printDILocation(const MachineDILocation &Location, } Expected BodyPrinter::printRegisterOperand(const MachineOperand &Operand, - bool IsExplicitDef) { + bool IsExplicitDef, bool PrintVirtualType) { const RegisterOperandFlags Flags = Operand.registerFlags(); if (Flags.has(RegisterOperandFlag::Implicit) && Flags.has(RegisterOperandFlag::Define)) { Output.append("implicit-def "); @@ -570,11 +526,19 @@ Expected BodyPrinter::printRegisterOperand(const MachineOperand &Operand, if (!Printed) return Printed; } else { - auto VirtualIndex = virtualRegisterID(Reg); - if (!VirtualIndex) - return std::unexpected(VirtualIndex.error()); + auto Info = Function.registerInfo().virtualRegisterInfo(Reg); + if (!Info) + return std::unexpected(Info.error()); + const VirtualRegisterInfo &RegisterInfo = Info->get(); Output.push_back('%'); - Output.append(std::to_string(*VirtualIndex)); + 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) @@ -584,28 +548,26 @@ Expected BodyPrinter::printRegisterOperand(const MachineOperand &Operand, Output.append(SubReg->Name); } - auto Info = Function.registerInfo().virtualRegisterInfo(Reg); - if (!Info) - return std::unexpected(Info.error()); - const VirtualRegisterInfo &RegisterInfo = Info->get(); - if (RegisterInfo.RegClass) { + 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 (RegisterInfo.Bank) { + } 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 (RegisterInfo.Type) { + } else if (PrintRegisterConstraint && RegisterInfo.Type) { Output.append(":_"); } - if (RegisterInfo.Type) { + if (PrintVirtualType && RegisterInfo.Type) { auto Type = llvmMIRLowLevelType(*RegisterInfo.Type); if (!Type) return std::unexpected(Type.error()); @@ -801,9 +763,10 @@ Expected BodyPrinter::printCFIInstruction(CFIInstructionIndex Index) { return makeUnexpected(ErrorCode::InternalError, "CFI printer did not handle an operation"); } -Expected BodyPrinter::printOperand(const MachineOperand &Operand, bool IsExplicitDef) { +Expected BodyPrinter::printOperand(const MachineOperand &Operand, bool IsExplicitDef, + bool PrintVirtualType) { if (Operand.isRegister()) - return printRegisterOperand(Operand, IsExplicitDef); + return printRegisterOperand(Operand, IsExplicitDef, PrintVirtualType); if (auto Flags = printTargetFlags(Operand.targetFlags()); !Flags) return Flags; @@ -819,19 +782,21 @@ Expected BodyPrinter::printOperand(const MachineOperand &Operand, bool IsE Output.push_back('i'); Output.append(std::to_string(Value.bitWidth())); Output.push_back(' '); - Output.append(unsignedDecimal(Value.words())); + 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; - const std::string_view Keyword = floatKeyword(Bits.semantics()); - if (Keyword.empty()) { - return makeUnexpected(ErrorCode::Unsupported, - "floating-point semantics has no LLVM MIR spelling"); - } - Output.append(Keyword); - Output.append(" f0x"); - Output.append(hexadecimalWords(Bits.words(), Bits.bitWidth())); + 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: { @@ -952,14 +917,7 @@ Expected BodyPrinter::printOperand(const MachineOperand &Operand, bool IsE return makeUnexpected(ErrorCode::VerificationError, "metadata operand has no printable value"); } - Output.append("!DIExpression("); - const auto Elements = Expression->elements(); - for (std::size_t Index = 0; Index < Elements.size(); ++Index) { - if (Index != 0) - Output.append(", "); - Output.append(std::to_string(Elements[Index])); - } - Output.push_back(')'); + Output.append(formatDIExpression(Expression->elements())); return {}; } case MachineOperand::Kind::TargetIndex: { @@ -1039,6 +997,68 @@ Expected BodyPrinter::printOperand(const MachineOperand &Operand, bool IsE 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: @@ -1123,6 +1143,20 @@ Expected BodyPrinter::printMemoryBase(const MemoryBase &Base, std::int64_t 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; @@ -1219,18 +1253,27 @@ Expected BodyPrinter::printMemoryOperand(const MachineMemOperand &Operand) } Output.push_back(' '); - 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; + 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 = @@ -1240,9 +1283,13 @@ Expected BodyPrinter::printMemoryOperand(const MachineMemOperand &Operand) return Printed; } - Output.append(", align "); - Output.append(std::to_string(Description.Align.bytes())); - if (Description.BaseAlign) { + 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())); } @@ -1337,13 +1384,74 @@ Expected BodyPrinter::printInstruction(const MachineInstr &Instruction) { 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; - if (auto Printed = printOperand(*Instruction.operandAt(Index), false); !Printed) { - return Printed; + 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 ? ", " : " "); @@ -1490,6 +1598,26 @@ Expected BodyPrinter::printBlock(const MachineBasicBlock &Block) { } 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) { @@ -1504,24 +1632,11 @@ Expected BodyPrinter::printBlock(const MachineBasicBlock &Block) { } } Output.push_back('\n'); + HasLineAttributes = true; } - 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.push_back('('); - Output.append(std::to_string(Successor.Probability.rawValue())); - Output.push_back(')'); - } + + if (HasLineAttributes && Block.firstInstruction() != nullptr) Output.push_back('\n'); - } bool InBundle = false; for (const MachineInstr *Instruction = Block.firstInstruction(); Instruction != nullptr; diff --git a/backend/lib/MIR/MIRDataParser.cpp b/backend/lib/MIR/MIRDataParser.cpp index fc70f0f..7e04ace 100644 --- a/backend/lib/MIR/MIRDataParser.cpp +++ b/backend/lib/MIR/MIRDataParser.cpp @@ -203,7 +203,7 @@ bool MIRDocumentParser::reserveJumpTables(const yaml::Node &Root, MachineFunctio Encoding = JumpTableEntryEncoding::Custom32; EntrySize = 4; } else { - return fail(KindNode->range(), "unknown jump table kind '" + std::string(KindName) + "'"); + return fail(KindNode->range(), "unknown enumerated scalar"); } const yaml::Node *Entries = mappingValue(*JumpTable, "entries"); diff --git a/backend/lib/MIR/MIRDocumentParser.cpp b/backend/lib/MIR/MIRDocumentParser.cpp index defcce9..ca2c6cd 100644 --- a/backend/lib/MIR/MIRDocumentParser.cpp +++ b/backend/lib/MIR/MIRDocumentParser.cpp @@ -49,8 +49,6 @@ using parser_detail::trim; bool MIRDocumentParser::fail(const TextRange &Range, std::string Message, std::string Category) { if (Result.Diagnostics.empty()) { - if (ActiveFunction != nullptr) - Message = "function '" + std::string(ActiveFunction->name()) + "': " + std::move(Message); Result.Diagnostics.push_back(TextDiagnostic{ .Severity = DiagnosticSeverity::Error, .Category = std::move(Category), @@ -210,7 +208,14 @@ MIRDocumentParseResult MIRDocumentParser::run() { std::string(Result.Source.name()) + ":embedded-ir", std::string(FirstRoot->scalar())); if (!Embedded.succeeded()) { const auto Diagnostics = Embedded.diagnostics(); - Result.Diagnostics.assign(Diagnostics.begin(), Diagnostics.end()); + 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(); @@ -301,22 +306,36 @@ bool MIRDocumentParser::parseFunction(const yaml::Node &Root, MIRDocument &Docum std::string_view{"prefetch-targets"}, std::string_view{"body"}, }; - if (validateKeys(Root, Keys, "machine function")) - return true; const yaml::Node *NameNode = mappingValue(Root, "name"); if (NameNode == nullptr) - return fail(Root.range(), "machine function requires 'name'"); + 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") || Name.empty()) - return fail(NameNode->range(), "machine function name cannot be empty"); + if (scalar(*NameNode, Name, "machine function name")) + return true; + const EmbeddedIRFunction *IRFunction = nullptr; if (Document.EmbeddedIR) { - const EmbeddedIRFunction *IRFunction = Document.EmbeddedIR->function(Name); + 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(), - "machine function has no corresponding embedded LLVM IR function"); + 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()); @@ -332,6 +351,11 @@ bool MIRDocumentParser::parseFunction(const yaml::Node &Root, MIRDocument &Docum }; MIParserContext Context; Context.DefaultPointerSizeInBits = Options.DefaultPointerSizeInBits; + if (IRFunction != nullptr) { + Context.HasIRBlock = [IRFunction](std::string_view BlockName) { + return IRFunction->block(BlockName) != nullptr; + }; + } std::vector PendingJumpTables; if (parseFunctionScalars(Root, Function, DocumentInfo) || diff --git a/backend/lib/MIR/MIRDocumentParser.h b/backend/lib/MIR/MIRDocumentParser.h index 53a31b4..3fade39 100644 --- a/backend/lib/MIR/MIRDocumentParser.h +++ b/backend/lib/MIR/MIRDocumentParser.h @@ -97,8 +97,8 @@ private: std::span Pending); [[nodiscard]] bool parseBody(const yaml::Node &Root, MachineFunction &Function, MIParserContext &Context); - [[nodiscard]] TextRange bodyDiagnosticRange(const yaml::Node &Body, - const TextRange &BodyRange) const noexcept; + [[nodiscard]] 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); diff --git a/backend/lib/MIR/MIRDocumentPrinter.cpp b/backend/lib/MIR/MIRDocumentPrinter.cpp index dba1ec7..5cc0173 100644 --- a/backend/lib/MIR/MIRDocumentPrinter.cpp +++ b/backend/lib/MIR/MIRDocumentPrinter.cpp @@ -17,6 +17,7 @@ #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 @@ -38,16 +39,206 @@ 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); - Result.push_back('\''); + const char Quote = Quoting == YAMLQuoting::Single ? '\'' : '"'; + Result.push_back(Quote); for (const char Character : Value) { - Result.push_back(Character); - if (Character == '\'') - Result.push_back('\''); + 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('\''); + Result.push_back(Quote); return Result; } @@ -396,14 +587,7 @@ Expected MIRDocumentPrinter::metadataValue(const MachineMetadataVal const MachineDIExpression *Expression = Value.expression(); if (Expression == nullptr) return makeUnexpected(ErrorCode::VerificationError, "metadata value is not printable"); - std::string Text = "!DIExpression("; - for (std::size_t Index = 0; Index < Expression->elements().size(); ++Index) { - if (Index != 0) - Text.append(", "); - Text.append(std::to_string(Expression->elements()[Index])); - } - Text.push_back(')'); - return Text; + return formatDIExpression(Expression->elements()); } Expected MIRDocumentPrinter::diLocation(const MachineDILocation &Location, @@ -679,24 +863,34 @@ Expected MIRDocumentPrinter::printFrameInfo(const MachineFunction &Functio Output.append(getMachineFramePointerPolicyName(State.FramePointerPolicy)); Output.push_back('\n'); } - if (State.StackProtector) { - auto Reference = frameReference(*State.StackProtector, Context); - if (!Reference) - return std::unexpected(Reference.error()); + 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.append(yamlScalar(Reference)); Output.push_back('\n'); } - if (State.FunctionContext) { - auto Reference = frameReference(*State.FunctionContext, Context); - if (!Reference) - return std::unexpected(Reference.error()); + 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.append(yamlScalar(Reference)); Output.push_back('\n'); } - if (State.MaxCallFrameSize) - Unsigned("maxCallFrameSize", *State.MaxCallFrameSize); + 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); @@ -1161,15 +1355,18 @@ Expected MIRDocumentPrinter::printFunctionRecords(const MachineFunction &F } if (!Records.prefetchTargets().empty()) { - Output.append("prefetch-targets:\n"); - for (const MachinePrefetchTarget &TargetValue : Records.prefetchTargets()) { + 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(" - "); Output.append(yamlScalar(Text)); - Output.push_back('\n'); } + Output.append(" ]\n"); } return {}; } @@ -1188,9 +1385,9 @@ Expected MIRDocumentPrinter::printFunction(const MachineFunction &Function Output.append(yamlScalar(Function.name())); Output.push_back('\n'); const auto Alignment = Function.attributes().alignment(); - if (Alignment) { + if (Options.Mode == MIPrintMode::Complete || Alignment) { Output.append("alignment: "); - Output.append(std::to_string(Alignment->bytes())); + Output.append(std::to_string(Alignment ? Alignment->bytes() : 1U)); Output.push_back('\n'); } @@ -1244,6 +1441,7 @@ Expected MIRDocumentPrinter::printFunction(const MachineFunction &Function return std::unexpected(Body.error()); Output.append("body: |\n"); appendBlockScalar(Output, *Body, 2); + Output.append("...\n"); return {}; } @@ -1281,6 +1479,7 @@ Expected MIRDocumentPrinter::run() { 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); diff --git a/backend/lib/MIR/MIRDocumentReferenceValidation.cpp b/backend/lib/MIR/MIRDocumentReferenceValidation.cpp index a6dbb56..69b0dd1 100644 --- a/backend/lib/MIR/MIRDocumentReferenceValidation.cpp +++ b/backend/lib/MIR/MIRDocumentReferenceValidation.cpp @@ -27,7 +27,23 @@ Expected verifyMIRDocumentReferences(const MIRDocument &Document, const EmbeddedIRReferenceIndex *Embedded = UseEmbeddedIR ? Document.embeddedIR() : nullptr; const EmbeddedIRFunction *IRFunction = nullptr; if (Embedded != nullptr) { - IRFunction = Embedded->function(Function.name()); + 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"); diff --git a/backend/lib/MIR/MIRFrameParser.cpp b/backend/lib/MIR/MIRFrameParser.cpp index 76ace8f..9e21dfc 100644 --- a/backend/lib/MIR/MIRFrameParser.cpp +++ b/backend/lib/MIR/MIRFrameParser.cpp @@ -408,6 +408,13 @@ bool MIRDocumentParser::parseFrameState(const yaml::Node &Root, MachineFunction 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; @@ -420,7 +427,10 @@ bool MIRDocumentParser::parseFrameState(const yaml::Node &Root, MachineFunction Size > std::numeric_limits::max()) { return fail(Value->range(), "maximum call-frame size is outside the 32-bit unsigned range"); } - State.MaxCallFrameSize = Size; + if (Size == std::numeric_limits::max()) + State.MaxCallFrameSize.reset(); + else + State.MaxCallFrameSize = Size; } for (const auto &[Key, Destination] : {std::pair{"cvBytesOfCalleeSavedRegisters", &State.CVBytesOfCalleeSavedRegisters}, diff --git a/backend/lib/MIR/MIRFunctionParser.cpp b/backend/lib/MIR/MIRFunctionParser.cpp index 07cb2b0..a59e4bd 100644 --- a/backend/lib/MIR/MIRFunctionParser.cpp +++ b/backend/lib/MIR/MIRFunctionParser.cpp @@ -165,10 +165,9 @@ bool MIRDocumentParser::recomputeProperties(const yaml::Node &Root, MachineFunct const auto ApplyComputed = [this, &Root, &Function](std::optional Explicit, bool Computed, MachineFunctionProperty Property, - std::string_view Name) -> bool { + std::string_view Conflict) -> bool { if (Explicit && *Explicit && !Computed) { - return fail(Root.range(), "explicit property '" + std::string(Name) + - "' conflicts with the parsed machine function"); + return fail(Root.range(), std::string(Function.name()) + std::string(Conflict)); } if (Explicit.value_or(Computed)) Function.properties().set(Property); @@ -176,15 +175,18 @@ bool MIRDocumentParser::recomputeProperties(const yaml::Node &Root, MachineFunct Function.properties().clear(Property); return false; }; - if (ApplyComputed(DocumentInfo.NoPHIs, NoPHIs, MachineFunctionProperty::NoPHIs, "noPhis") || - ApplyComputed(DocumentInfo.IsSSA, IsSSA, MachineFunctionProperty::IsSSA, "isSSA") || - ApplyComputed(DocumentInfo.NoVRegs, NoVRegs, MachineFunctionProperty::NoVRegs, "noVRegs")) { + 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(), - "explicit property 'hasFakeUses: false' conflicts with a fake-use instruction"); + 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); @@ -302,7 +304,7 @@ bool MIRDocumentParser::parseRegisters(const yaml::Node &Root, MachineFunction & const auto Flag = Target.mirText().virtualRegisterFlag(Name); if (!Flag) return fail(FlagNode->range(), - "unknown virtual-register flag '" + std::string(Name) + "'"); + "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); @@ -368,15 +370,23 @@ bool MIRDocumentParser::parseRegisters(const yaml::Node &Root, MachineFunction & return false; } -TextRange MIRDocumentParser::bodyDiagnosticRange(const yaml::Node &Body, - const TextRange &BodyRange) const noexcept { - if (Body.scalarStyle() != yaml::ScalarStyle::Literal) - return Body.range(); +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 = Body.range().Begin.Line + 1U; - const std::size_t LastBodyLine = Body.range().End.Line; + 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 Body.range(); + return Scalar.range(); std::size_t Indent = 0; bool FoundContent = false; @@ -392,9 +402,9 @@ TextRange MIRDocumentParser::bodyDiagnosticRange(const yaml::Node &Body, break; } if (!FoundContent) - return Body.range(); + return Scalar.range(); - const TextPosition Fallback = Body.range().Begin; + const TextPosition Fallback = Scalar.range().Begin; const auto MapPosition = [this, FirstBodyLine, LastBodyLine, Indent, Fallback](TextPosition Position) { const std::size_t SourceLine = FirstBodyLine + Position.Line - 1U; @@ -407,7 +417,7 @@ TextRange MIRDocumentParser::bodyDiagnosticRange(const yaml::Node &Body, const std::size_t Offset = Result.Source.lineOffsets()[SourceLine - 1U] + SourceColumn; return Result.Source.positionAt(Offset); }; - return {.Begin = MapPosition(BodyRange.Begin), .End = MapPosition(BodyRange.End)}; + return {.Begin = MapPosition(ScalarRange.Begin), .End = MapPosition(ScalarRange.End)}; } bool MIRDocumentParser::parseBody(const yaml::Node &Root, MachineFunction &Function, @@ -460,7 +470,7 @@ bool MIRDocumentParser::parseBody(const yaml::Node &Root, MachineFunction &Funct if (!Parser.diagnostics().empty()) { const TextRange Range = Body == nullptr ? BodyRange - : bodyDiagnosticRange(*Body, Parser.diagnostics().front().Range); + : scalarDiagnosticRange(*Body, Parser.diagnostics().front().Range); return fail(Range, Parser.diagnostics().front().Message, "mir-body"); } return importError(BodyRange, Parsed.error(), "mir-body"); diff --git a/backend/lib/MIR/MIRFunctionRecordsParser.cpp b/backend/lib/MIR/MIRFunctionRecordsParser.cpp index dcd486e..aed6947 100644 --- a/backend/lib/MIR/MIRFunctionRecordsParser.cpp +++ b/backend/lib/MIR/MIRFunctionRecordsParser.cpp @@ -234,21 +234,63 @@ bool MIRDocumentParser::parseFunctionRecords(const yaml::Node &Root, MachineFunc std::string_view Text; if (TargetNode == nullptr || scalar(*TargetNode, Text, "prefetch target")) return true; - Text = trim(Text); - if (!Text.starts_with("bb_id")) - return fail(TargetNode->range(), "prefetch target must start with 'bb_id'"); - Text = trim(Text.substr(std::string_view("bb_id").size())); + + std::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{}; - for (std::size_t Index = 0; Index < Values.size(); ++Index) { - const std::size_t Comma = Text.find(','); - const std::string_view Part = Index + 1 == Values.size() ? Text : Text.substr(0, Comma); - if ((Index + 1 != Values.size() && Comma == std::string_view::npos) || - !parseUnsigned32(trim(Part), Values[Index])) { - return fail(TargetNode->range(), "prefetch target has invalid numeric fields"); - } - if (Index + 1 != Values.size()) - Text = trim(Text.substr(Comma + 1)); + 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) diff --git a/backend/lib/MIR/MIRMetadataParser.cpp b/backend/lib/MIR/MIRMetadataParser.cpp index 6ce717b..911edb6 100644 --- a/backend/lib/MIR/MIRMetadataParser.cpp +++ b/backend/lib/MIR/MIRMetadataParser.cpp @@ -20,6 +20,7 @@ #include "rcc/CodeGen/TargetMIRTextInfo.h" #include "rcc/CodeGen/TargetRegisterInfo.h" #include "rcc/MIR/LLVMConstantSyntax.h" +#include "rcc/MIR/MILexer.h" #include #include @@ -89,7 +90,8 @@ bool MIRDocumentParser::parseVariableDebugInfo(const yaml::Node &Entry, MachineF MetadataTextParser Parser(std::string(Text), Resolve); auto Value = Parser.parseValue(); if (!Value) - return importError(ExpressionNode->range(), Value.error(), "mir-debug-metadata"); + return importError(scalarDiagnosticRange(*ExpressionNode, Parser.diagnosticRange()), + Value.error(), "mir-debug-metadata"); if (Value->reference() != nullptr && Value->reference()->scope() != MachineMetadataScope::EmbeddedIR) { return fail(ExpressionNode->range(), @@ -148,132 +150,180 @@ bool MIRDocumentParser::parseMetadata(const yaml::Node &Root, MachineFunction &F 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::string_view Elements; + 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")) { + 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; - } - Text = trim(Text); - if (!Text.starts_with('!')) - return fail(DefinitionNode->range(), "machine metadata definition must start with '!N'"); - const std::size_t Equals = Text.find('='); - if (Equals == std::string_view::npos) - return fail(DefinitionNode->range(), "machine metadata definition requires '='"); std::uint32_t Slot = 0; - if (!parseUnsigned32(trim(Text.substr(1, Equals - 1)), Slot)) - return fail(DefinitionNode->range(), "machine metadata definition has an invalid slot"); - if (!Slots.insert(Slot).second) - return fail(DefinitionNode->range(), "duplicate machine metadata slot"); - std::string_view Right = trim(Text.substr(Equals + 1)); - bool IsDistinct = false; - if (Right.starts_with("distinct")) { - Right = trim(Right.substr(std::string_view("distinct").size())); - IsDistinct = true; + if (!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 (!Right.starts_with("!{") || !Right.ends_with('}')) - return fail(DefinitionNode->range(), "machine metadata node must be a metadata tuple"); + 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 = Right.substr(2, Right.size() - 3)}); + .Elements = std::move(Elements)}); Context.Metadata.push_back( {.TextID = Slot, .Reference = MachineMetadataReference::machineFunction(Slot)}); } - auto ResolveReference = [this, - &Slots](std::uint32_t Slot, - const TextRange &Range) -> Expected { - MachineMetadataReference Reference; - if (ActiveDocument->EmbeddedIR != std::nullopt && - ActiveDocument->EmbeddedIR->metadata(std::to_string(Slot)) != nullptr) { - Reference = MachineMetadataReference::embeddedIR(Slot); - } else if (Slots.contains(Slot)) { - Reference = MachineMetadataReference::machineFunction(Slot); - } else { - return makeUnexpected(ErrorCode::ParseError, "metadata tuple references undefined slot '!" + - std::to_string(Slot) + "' at line " + - std::to_string(Range.Begin.Line)); - } - return MachineMetadataElement::reference(Reference); - }; - for (const Definition &Definition : Definitions) { MachineMetadataNode NewNode{ .Slot = Definition.Slot, .IsDistinct = Definition.IsDistinct, .Elements = {}, }; - std::string_view Remaining = trim(Definition.Elements); - while (!Remaining.empty()) { - std::size_t End = 0; - bool InString = false; - bool Escaped = false; - for (; End < Remaining.size(); ++End) { - const char Character = Remaining[End]; - if (InString && Escaped) { - Escaped = false; - continue; - } - if (InString && Character == '\\') { - Escaped = true; - continue; - } - if (Character == '"') { - InString = !InString; - continue; - } - if (!InString && Character == ',') - break; + 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; } - if (InString) - return fail(Definition.Node->range(), "unterminated machine metadata string"); - std::string_view Element = trim(Remaining.substr(0, End)); - if (Element.empty()) - return fail(Definition.Node->range(), "empty machine metadata tuple element"); - if (Element == "null") { - NewNode.Elements.push_back(MachineMetadataElement::null()); - } else if (Element.starts_with('!') && Element.size() > 1 && Element[1] >= '0' && - Element[1] <= '9') { - std::uint32_t ReferenceSlot = 0; - if (!parseUnsigned32(Element.substr(1), ReferenceSlot)) - return fail(Definition.Node->range(), "invalid metadata tuple reference"); - auto Reference = ResolveReference(ReferenceSlot, Definition.Node->range()); - if (!Reference) - return importError(Definition.Node->range(), Reference.error()); - NewNode.Elements.push_back(std::move(*Reference)); - } else if (Element.starts_with("!\"") && Element.ends_with('"')) { - std::string Value; - std::string_view Encoded = Element.substr(2, Element.size() - 3); - for (std::size_t Index = 0; Index < Encoded.size(); ++Index) { - if (Encoded[Index] == '\\' && Index + 1 < Encoded.size()) { - const char EscapedCharacter = Encoded[++Index]; - if (EscapedCharacter != '\\' && EscapedCharacter != '"') - return fail(Definition.Node->range(), "unsupported machine metadata string escape"); - Value.push_back(EscapedCharacter); - } else { - Value.push_back(Encoded[Index]); - } - } - NewNode.Elements.push_back(MachineMetadataElement::string(std::move(Value))); + 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(Definition.Node->range(), "unsupported machine metadata tuple element"); + return fail(Element.Range, + "use of undefined metadata '!" + std::to_string(Element.Reference) + "'"); } - - if (End == Remaining.size()) - Remaining = {}; - else - Remaining = trim(Remaining.substr(End + 1)); + 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) diff --git a/backend/lib/MIR/MIRMetadataTextParser.cpp b/backend/lib/MIR/MIRMetadataTextParser.cpp index 51b039f..7875b06 100644 --- a/backend/lib/MIR/MIRMetadataTextParser.cpp +++ b/backend/lib/MIR/MIRMetadataTextParser.cpp @@ -74,11 +74,11 @@ Expected MetadataTextParser::parseExpression() { while (!at(MITokenKind::RightParen)) { if (at(MITokenKind::Identifier)) { const auto Opcode = parseDIExpressionOpcodeName(token().stringValue()); - if (!Opcode) { - return makeUnexpected(ErrorCode::ParseError, "unknown DIExpression opcode '" + - std::string(token().stringValue()) + "'"); - } - Elements.push_back(*Opcode); + 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; diff --git a/backend/lib/MIR/MIRMetadataTextParser.h b/backend/lib/MIR/MIRMetadataTextParser.h index b8ebb12..032523d 100644 --- a/backend/lib/MIR/MIRMetadataTextParser.h +++ b/backend/lib/MIR/MIRMetadataTextParser.h @@ -35,6 +35,10 @@ public: [[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]; -- cgit v1.2.3