diff options
Diffstat (limited to 'backend/lib/CodeGen')
| -rw-r--r-- | backend/lib/CodeGen/CMakeLists.txt | 4 | ||||
| -rw-r--r-- | backend/lib/CodeGen/GenericConstant.cpp | 875 | ||||
| -rw-r--r-- | backend/lib/CodeGen/MachineConstantPool.cpp | 44 | ||||
| -rw-r--r-- | backend/lib/CodeGen/MachineFrameInfo.cpp | 143 | ||||
| -rw-r--r-- | backend/lib/CodeGen/MachineFunctionCloner.cpp | 55 | ||||
| -rw-r--r-- | backend/lib/CodeGen/MachineFunctionProperties.cpp | 2 | ||||
| -rw-r--r-- | backend/lib/CodeGen/MachineJumpTableInfo.cpp | 55 | ||||
| -rw-r--r-- | backend/lib/CodeGen/MachineMetadata.cpp | 4 | ||||
| -rw-r--r-- | backend/lib/CodeGen/MachineOperand.cpp | 83 | ||||
| -rw-r--r-- | backend/lib/CodeGen/MachineVerifier.cpp | 30 | ||||
| -rw-r--r-- | backend/lib/CodeGen/TargetMIRTextInfo.cpp | 178 |
11 files changed, 1449 insertions, 24 deletions
diff --git a/backend/lib/CodeGen/CMakeLists.txt b/backend/lib/CodeGen/CMakeLists.txt index ce53cd2..34e9ef6 100644 --- a/backend/lib/CodeGen/CMakeLists.txt +++ b/backend/lib/CodeGen/CMakeLists.txt @@ -3,6 +3,7 @@ set( RCCBackendCodeGenSources CompilationUnit.cpp + GenericConstant.cpp InstructionDescriptor.cpp LowLevelType.cpp MachineBasicBlockAttributes.cpp @@ -38,6 +39,7 @@ set( set( RCCBackendCodeGenHeaders "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/CompilationUnit.h" + "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/GenericConstant.h" "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/InstructionDescriptor.h" "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/LowLevelType.h" "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/MachineBasicBlockAttributes.h" @@ -102,6 +104,7 @@ install( rcc_backend_register_format_files( "${CMAKE_CURRENT_SOURCE_DIR}/CompilationUnit.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/GenericConstant.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/InstructionDescriptor.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/LowLevelType.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/MachineBasicBlockAttributes.cpp" @@ -137,6 +140,7 @@ rcc_backend_register_format_files( ) rcc_backend_register_tidy_files( "${CMAKE_CURRENT_SOURCE_DIR}/CompilationUnit.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/GenericConstant.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/InstructionDescriptor.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/LowLevelType.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/MachineBasicBlockAttributes.cpp" diff --git a/backend/lib/CodeGen/GenericConstant.cpp b/backend/lib/CodeGen/GenericConstant.cpp new file mode 100644 index 0000000..2ef24bf --- /dev/null +++ b/backend/lib/CodeGen/GenericConstant.cpp @@ -0,0 +1,875 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "rcc/CodeGen/GenericConstant.h" + +#include <algorithm> +#include <bit> +#include <cstddef> +#include <cstdint> +#include <limits> +#include <optional> +#include <utility> + +namespace rcc { +namespace { + +constexpr std::size_t MaximumVerificationDepth = 1024; +constexpr std::size_t MaximumVerificationNodes = std::size_t{1} << 24U; + +[[nodiscard]] bool isAggregateType(GenericConstantTypeKind Kind) noexcept { + return Kind == GenericConstantTypeKind::Array || Kind == GenericConstantTypeKind::Vector || + Kind == GenericConstantTypeKind::Struct; +} + +[[nodiscard]] const GenericConstantType &resolvedType(const GenericConstantType &Type) noexcept { + const GenericConstantType *Resolved = &Type; + std::size_t Depth = 0; + while (Resolved->Kind == GenericConstantTypeKind::Named && Resolved->Elements.size() == 1 && + Depth++ <= MaximumVerificationDepth) { + Resolved = &Resolved->Elements.front(); + } + return *Resolved; +} + +[[nodiscard]] bool sameResolvedType(const GenericConstantType &Left, + const GenericConstantType &Right) noexcept { + return resolvedType(Left) == resolvedType(Right); +} + +[[nodiscard]] bool isIntegerScalarType(const GenericConstantType &Type) noexcept { + const GenericConstantTypeKind Kind = resolvedType(Type).Kind; + return Kind == GenericConstantTypeKind::Integer || Kind == GenericConstantTypeKind::Byte; +} + +[[nodiscard]] std::optional<std::uint32_t> +integerScalarBitWidth(const GenericConstantType &Type) noexcept { + const GenericConstantType &Resolved = resolvedType(Type); + if (Resolved.Kind == GenericConstantTypeKind::Integer) + return Resolved.BitWidth; + if (Resolved.Kind == GenericConstantTypeKind::Byte) + return 8; + return std::nullopt; +} + +[[nodiscard]] bool isPointerScalarType(const GenericConstantType &Type) noexcept { + return resolvedType(Type).Kind == GenericConstantTypeKind::Pointer; +} + +[[nodiscard]] bool isFloatingScalarType(const GenericConstantType &Type) noexcept { + return resolvedType(Type).Kind == GenericConstantTypeKind::Floating; +} + +struct VectorShape final { + std::uint64_t ElementCount = 0; + bool Scalable = false; + + friend constexpr bool operator==(VectorShape Left, VectorShape Right) noexcept = default; +}; + +[[nodiscard]] std::optional<VectorShape> +vectorShape(const GenericConstantType &Type) noexcept { + const GenericConstantType &Resolved = resolvedType(Type); + if (Resolved.Kind != GenericConstantTypeKind::Vector) + return std::nullopt; + return VectorShape{.ElementCount = Resolved.ElementCount, .Scalable = Resolved.Scalable}; +} + +[[nodiscard]] const GenericConstantType * +vectorElementType(const GenericConstantType &Type) noexcept { + const GenericConstantType &Resolved = resolvedType(Type); + if (Resolved.Kind != GenericConstantTypeKind::Vector || Resolved.Elements.size() != 1) + return nullptr; + return &Resolved.Elements.front(); +} + +[[nodiscard]] bool hasSameVectorShape(const GenericConstantType &Left, + const GenericConstantType &Right) noexcept { + const auto LeftShape = vectorShape(Left); + const auto RightShape = vectorShape(Right); + return LeftShape == RightShape; +} + +[[nodiscard]] bool isIntegerOrIntegerVectorType(const GenericConstantType &Type) noexcept { + if (isIntegerScalarType(Type)) + return true; + const GenericConstantType *Element = vectorElementType(Type); + return Element != nullptr && isIntegerScalarType(*Element); +} + +[[nodiscard]] bool isPointerOrPointerVectorType(const GenericConstantType &Type) noexcept { + if (isPointerScalarType(Type)) + return true; + const GenericConstantType *Element = vectorElementType(Type); + return Element != nullptr && isPointerScalarType(*Element); +} + +[[nodiscard]] std::optional<std::uint32_t> +pointerAddressSpace(const GenericConstantType &Type) noexcept { + const GenericConstantType &Resolved = resolvedType(Type); + if (Resolved.Kind == GenericConstantTypeKind::Pointer) + return Resolved.AddressSpace; + const GenericConstantType *Element = vectorElementType(Resolved); + if (Element == nullptr) + return std::nullopt; + const GenericConstantType &ResolvedElement = resolvedType(*Element); + if (ResolvedElement.Kind != GenericConstantTypeKind::Pointer) + return std::nullopt; + return ResolvedElement.AddressSpace; +} + +[[nodiscard]] bool isNonPointerBitCastType(const GenericConstantType &Type) noexcept { + if (isIntegerScalarType(Type) || isFloatingScalarType(Type)) + return true; + const GenericConstantType *Element = vectorElementType(Type); + return Element != nullptr && + (isIntegerScalarType(*Element) || isFloatingScalarType(*Element)); +} + +[[nodiscard]] std::optional<std::uint64_t> +nonPointerFixedBitWidth(const GenericConstantType &Type) noexcept { + const GenericConstantType &Resolved = resolvedType(Type); + if (const auto Width = integerScalarBitWidth(Resolved)) + return *Width; + if (Resolved.Kind == GenericConstantTypeKind::Floating) + return getFloatSemanticsBitWidth(Resolved.Semantics); + const GenericConstantType *Element = vectorElementType(Resolved); + if (Element == nullptr || Resolved.Scalable) + return std::nullopt; + const auto ElementWidth = nonPointerFixedBitWidth(*Element); + if (!ElementWidth || *ElementWidth == 0 || + Resolved.ElementCount > std::numeric_limits<std::uint64_t>::max() / *ElementWidth) { + return std::nullopt; + } + return Resolved.ElementCount * *ElementWidth; +} + +[[nodiscard]] bool hasIntegerCastShape(const GenericConstantType &Source, + const GenericConstantType &Destination, + bool RequireNarrowing) noexcept { + const GenericConstantType &ResolvedSource = resolvedType(Source); + const GenericConstantType &ResolvedDestination = resolvedType(Destination); + if (const auto SourceWidth = integerScalarBitWidth(ResolvedSource)) { + const auto DestinationWidth = integerScalarBitWidth(ResolvedDestination); + return DestinationWidth && (RequireNarrowing ? *SourceWidth > *DestinationWidth + : *SourceWidth == *DestinationWidth); + } + const GenericConstantType *SourceElement = vectorElementType(ResolvedSource); + const GenericConstantType *DestinationElement = vectorElementType(ResolvedDestination); + if (SourceElement == nullptr || DestinationElement == nullptr || + !hasSameVectorShape(ResolvedSource, ResolvedDestination)) { + return false; + } + return hasIntegerCastShape(*SourceElement, *DestinationElement, RequireNarrowing); +} + +[[nodiscard]] bool hasPointerIntegerCastShape(const GenericConstantType &Pointer, + const GenericConstantType &Integer) noexcept { + const GenericConstantType &ResolvedPointer = resolvedType(Pointer); + const GenericConstantType &ResolvedInteger = resolvedType(Integer); + if (isPointerScalarType(ResolvedPointer)) + return isIntegerScalarType(ResolvedInteger); + const GenericConstantType *PointerElement = vectorElementType(ResolvedPointer); + const GenericConstantType *IntegerElement = vectorElementType(ResolvedInteger); + return PointerElement != nullptr && IntegerElement != nullptr && + hasSameVectorShape(ResolvedPointer, ResolvedInteger) && + isPointerScalarType(*PointerElement) && isIntegerScalarType(*IntegerElement); +} + +[[nodiscard]] bool isSizedType(const GenericConstantType &Type, + std::size_t Depth = 0) noexcept { + if (Depth > MaximumVerificationDepth) + return false; + const GenericConstantType &Resolved = resolvedType(Type); + switch (Resolved.Kind) { + case GenericConstantTypeKind::Integer: + case GenericConstantTypeKind::Byte: + case GenericConstantTypeKind::Floating: + case GenericConstantTypeKind::Pointer: + return true; + case GenericConstantTypeKind::Array: + case GenericConstantTypeKind::Vector: + return Resolved.Elements.size() == 1 && isSizedType(Resolved.Elements.front(), Depth + 1); + case GenericConstantTypeKind::Struct: + return std::ranges::all_of(Resolved.Elements, [Depth](const GenericConstantType &Element) { + return isSizedType(Element, Depth + 1); + }); + case GenericConstantTypeKind::Named: + return Resolved.Elements.size() == 1 && isSizedType(Resolved.Elements.front(), Depth + 1); + case GenericConstantTypeKind::Invalid: + case GenericConstantTypeKind::Void: + case GenericConstantTypeKind::Label: + case GenericConstantTypeKind::Token: + case GenericConstantTypeKind::Metadata: + case GenericConstantTypeKind::Function: + return false; + } + return false; +} + +[[nodiscard]] bool signedIntegerLess(const IntegerImmediate &Left, + const IntegerImmediate &Right) noexcept { + if (Left.bitWidth() != Right.bitWidth()) + return false; + if (Left.isNegative() != Right.isNegative()) + return Left.isNegative(); + const auto LeftWords = Left.words(); + const auto RightWords = Right.words(); + for (std::size_t Index = LeftWords.size(); Index != 0; --Index) { + if (LeftWords[Index - 1U] != RightWords[Index - 1U]) + return LeftWords[Index - 1U] < RightWords[Index - 1U]; + } + return false; +} + +[[nodiscard]] bool hasOnlyValuePayload(const GenericConstantData &Value) noexcept { + return Value.Symbol.empty() && Value.Block.empty() && + Value.ExpressionOpcode == GenericConstantExpressionOpcode::Invalid && + Value.ExpressionFlags.rawValue() == 0 && !Value.SourceElementType && + !Value.InRangeStart && !Value.InRangeEnd; +} + +[[nodiscard]] Expected<void> verifyType(const GenericConstantType &Type, std::size_t Depth, + std::size_t &Nodes) { + if (Depth > MaximumVerificationDepth || ++Nodes > MaximumVerificationNodes) { + return makeUnexpected(ErrorCode::VerificationError, + "generic constant type exceeds structural limits"); + } + + const auto RequireNoElements = [&]() -> Expected<void> { + if (!Type.Elements.empty()) { + return makeUnexpected(ErrorCode::VerificationError, + "scalar generic constant type cannot contain child types"); + } + return {}; + }; + + switch (Type.Kind) { + case GenericConstantTypeKind::Invalid: + return makeUnexpected(ErrorCode::VerificationError, "generic constant type is invalid"); + case GenericConstantTypeKind::Void: + case GenericConstantTypeKind::Label: + case GenericConstantTypeKind::Token: + case GenericConstantTypeKind::Metadata: + return RequireNoElements(); + case GenericConstantTypeKind::Integer: + if (Type.BitWidth == 0 || Type.BitWidth > IntegerImmediate::MaxBitWidth) { + return makeUnexpected(ErrorCode::VerificationError, + "generic integer type has an invalid bit width"); + } + return RequireNoElements(); + case GenericConstantTypeKind::Byte: + return RequireNoElements(); + case GenericConstantTypeKind::Floating: + if (!isKnownFloatSemantics(Type.Semantics)) { + return makeUnexpected(ErrorCode::VerificationError, + "generic floating type has unknown semantics"); + } + return RequireNoElements(); + case GenericConstantTypeKind::Pointer: + return RequireNoElements(); + case GenericConstantTypeKind::Array: + case GenericConstantTypeKind::Vector: + if (Type.Elements.size() != 1 || + (Type.Kind == GenericConstantTypeKind::Vector && Type.ElementCount == 0)) { + return makeUnexpected(ErrorCode::VerificationError, + "generic sequential type has an invalid element description"); + } + return verifyType(Type.Elements.front(), Depth + 1, Nodes); + case GenericConstantTypeKind::Struct: + for (const GenericConstantType &Element : Type.Elements) { + auto Result = verifyType(Element, Depth + 1, Nodes); + if (!Result) + return Result; + } + return {}; + case GenericConstantTypeKind::Function: + if (Type.Elements.empty()) { + return makeUnexpected(ErrorCode::VerificationError, + "generic function type has no return type"); + } + for (const GenericConstantType &Element : Type.Elements) { + auto Result = verifyType(Element, Depth + 1, Nodes); + if (!Result) + return Result; + } + return {}; + case GenericConstantTypeKind::Named: + if (Type.Name.empty() || Type.Elements.size() > 1) { + return makeUnexpected(ErrorCode::VerificationError, + "generic named type has an invalid identity"); + } + if (!Type.Elements.empty()) + return verifyType(Type.Elements.front(), Depth + 1, Nodes); + return {}; + } + return makeUnexpected(ErrorCode::InternalError, "generic constant type kind is unhandled"); +} + +[[nodiscard]] Expected<void> verifyConstant(const GenericConstantData &Value, std::size_t Depth, + std::size_t &Nodes) { + if (Depth > MaximumVerificationDepth || ++Nodes > MaximumVerificationNodes) { + return makeUnexpected(ErrorCode::VerificationError, + "generic constant value exceeds structural limits"); + } + std::size_t TypeNodes = 0; + auto TypeResult = verifyType(Value.Type, 0, TypeNodes); + if (!TypeResult) + return TypeResult; + const GenericConstantType &EffectiveType = resolvedType(Value.Type); + + const auto RequireEmptyScalarPayload = [&]() -> Expected<void> { + if (Value.IntegerValue || Value.FloatingValue || !Value.ByteValue.empty() || + !Value.Elements.empty() || !hasOnlyValuePayload(Value)) { + return makeUnexpected(ErrorCode::VerificationError, + "generic constant kind contains an unrelated payload"); + } + return {}; + }; + + switch (Value.Kind) { + case GenericConstantKind::Invalid: + return makeUnexpected(ErrorCode::VerificationError, "generic constant value is invalid"); + case GenericConstantKind::Integer: + if (!Value.IntegerValue || Value.FloatingValue || !Value.ByteValue.empty() || + !Value.Elements.empty() || !hasOnlyValuePayload(Value) || + (EffectiveType.Kind != GenericConstantTypeKind::Integer && + EffectiveType.Kind != GenericConstantTypeKind::Byte) || + Value.IntegerValue->bitWidth() != + (EffectiveType.Kind == GenericConstantTypeKind::Byte ? 8U + : EffectiveType.BitWidth)) { + return makeUnexpected(ErrorCode::VerificationError, + "generic integer constant has an inconsistent payload"); + } + return Value.IntegerValue->verify(); + case GenericConstantKind::Floating: + if (!Value.FloatingValue || Value.IntegerValue || !Value.ByteValue.empty() || + !Value.Elements.empty() || !hasOnlyValuePayload(Value) || + EffectiveType.Kind != GenericConstantTypeKind::Floating || + Value.FloatingValue->semantics() != EffectiveType.Semantics) { + return makeUnexpected(ErrorCode::VerificationError, + "generic floating constant has an inconsistent payload"); + } + return {}; + case GenericConstantKind::NullPointer: + if (EffectiveType.Kind != GenericConstantTypeKind::Pointer) { + return makeUnexpected(ErrorCode::VerificationError, + "generic null constant must have pointer type"); + } + return RequireEmptyScalarPayload(); + case GenericConstantKind::ZeroInitializer: + case GenericConstantKind::Undef: + case GenericConstantKind::Poison: + if (EffectiveType.Kind == GenericConstantTypeKind::Invalid || + EffectiveType.Kind == GenericConstantTypeKind::Function) { + return makeUnexpected(ErrorCode::VerificationError, + "generic marker constant has an invalid type"); + } + return RequireEmptyScalarPayload(); + case GenericConstantKind::Aggregate: { + if (!isAggregateType(EffectiveType.Kind) || Value.IntegerValue || Value.FloatingValue || + !Value.ByteValue.empty() || !hasOnlyValuePayload(Value)) { + return makeUnexpected(ErrorCode::VerificationError, + "generic aggregate constant has an inconsistent payload"); + } + if ((EffectiveType.Kind == GenericConstantTypeKind::Array || + EffectiveType.Kind == GenericConstantTypeKind::Vector) && + Value.Elements.size() != EffectiveType.ElementCount) { + return makeUnexpected(ErrorCode::VerificationError, + "generic aggregate constant has the wrong element count"); + } + if (EffectiveType.Kind == GenericConstantTypeKind::Struct && + Value.Elements.size() != EffectiveType.Elements.size()) { + return makeUnexpected(ErrorCode::VerificationError, + "generic struct constant has the wrong element count"); + } + for (std::size_t Index = 0; Index < Value.Elements.size(); ++Index) { + const GenericConstantType &ExpectedType = + EffectiveType.Kind == GenericConstantTypeKind::Struct + ? EffectiveType.Elements[Index] + : EffectiveType.Elements.front(); + if (Value.Elements[Index].Type != ExpectedType) { + return makeUnexpected(ErrorCode::VerificationError, + "generic aggregate element type does not match its container"); + } + auto Result = verifyConstant(Value.Elements[Index], Depth + 1, Nodes); + if (!Result) + return Result; + } + return {}; + } + case GenericConstantKind::ByteString: + if (EffectiveType.Kind != GenericConstantTypeKind::Array || + EffectiveType.Elements.size() != 1 || + (EffectiveType.Elements.front().Kind != GenericConstantTypeKind::Integer && + EffectiveType.Elements.front().Kind != GenericConstantTypeKind::Byte) || + (EffectiveType.Elements.front().Kind == GenericConstantTypeKind::Integer && + EffectiveType.Elements.front().BitWidth != 8) || + EffectiveType.ElementCount != Value.ByteValue.size() || Value.IntegerValue || + Value.FloatingValue || !Value.Elements.empty() || !hasOnlyValuePayload(Value)) { + return makeUnexpected(ErrorCode::VerificationError, + "generic byte-string constant has an inconsistent type or payload"); + } + return {}; + case GenericConstantKind::GlobalAddress: + case GenericConstantKind::DSOLocalEquivalent: + case GenericConstantKind::NoCFI: + if (EffectiveType.Kind != GenericConstantTypeKind::Pointer || Value.Symbol.empty() || + Value.IntegerValue || Value.FloatingValue || !Value.ByteValue.empty() || + !Value.Elements.empty() || !Value.Block.empty() || + Value.ExpressionOpcode != GenericConstantExpressionOpcode::Invalid || + Value.ExpressionFlags.rawValue() != 0 || Value.SourceElementType || + Value.InRangeStart || Value.InRangeEnd) { + return makeUnexpected(ErrorCode::VerificationError, + "generic global-address constant has an inconsistent payload"); + } + return {}; + case GenericConstantKind::BlockAddress: + if (EffectiveType.Kind != GenericConstantTypeKind::Pointer || Value.Symbol.empty() || + Value.Block.empty() || Value.IntegerValue || Value.FloatingValue || + !Value.ByteValue.empty() || !Value.Elements.empty() || + Value.ExpressionOpcode != GenericConstantExpressionOpcode::Invalid || + Value.ExpressionFlags.rawValue() != 0 || Value.SourceElementType || + Value.InRangeStart || Value.InRangeEnd) { + return makeUnexpected(ErrorCode::VerificationError, + "generic block-address constant has an inconsistent payload"); + } + return {}; + case GenericConstantKind::PointerAuth: + if (EffectiveType.Kind != GenericConstantTypeKind::Pointer || Value.Elements.size() < 2 || + Value.Elements.size() > 5 || Value.IntegerValue || Value.FloatingValue || + !Value.ByteValue.empty() || !hasOnlyValuePayload(Value)) { + return makeUnexpected(ErrorCode::VerificationError, + "generic pointer-auth constant has an inconsistent payload"); + } + for (const GenericConstantData &Element : Value.Elements) { + auto Result = verifyConstant(Element, Depth + 1, Nodes); + if (!Result) + return Result; + } + if (!isPointerScalarType(Value.Elements[0].Type) || + !sameResolvedType(Value.Type, Value.Elements[0].Type) || + !integerScalarBitWidth(Value.Elements[1].Type) || + *integerScalarBitWidth(Value.Elements[1].Type) != 32) { + return makeUnexpected(ErrorCode::VerificationError, + "generic pointer-auth constant has invalid pointer or key operands"); + } + if (Value.Elements.size() >= 3 && + (!integerScalarBitWidth(Value.Elements[2].Type) || + *integerScalarBitWidth(Value.Elements[2].Type) != 64)) { + return makeUnexpected( + ErrorCode::VerificationError, + "generic pointer-auth constant integer discriminator must be a scalar i64"); + } + for (std::size_t Index = 3; Index < Value.Elements.size(); ++Index) { + if (!isPointerScalarType(Value.Elements[Index].Type)) { + return makeUnexpected( + ErrorCode::VerificationError, + "generic pointer-auth constant address operands must be scalar pointers"); + } + } + return {}; + case GenericConstantKind::Expression: + if (Value.ExpressionOpcode == GenericConstantExpressionOpcode::Invalid || + Value.Elements.empty() || Value.IntegerValue || Value.FloatingValue || + !Value.ByteValue.empty() || !Value.Symbol.empty() || !Value.Block.empty()) { + return makeUnexpected(ErrorCode::VerificationError, + "generic constant expression has an inconsistent payload"); + } + if ((Value.InRangeStart.has_value() != Value.InRangeEnd.has_value()) || + ((Value.InRangeStart || Value.InRangeEnd || Value.SourceElementType) && + Value.ExpressionOpcode != GenericConstantExpressionOpcode::GetElementPtr)) { + return makeUnexpected(ErrorCode::VerificationError, + "generic constant expression has misplaced GEP state"); + } + if (Value.ExpressionOpcode == GenericConstantExpressionOpcode::GetElementPtr && + !Value.SourceElementType) { + return makeUnexpected(ErrorCode::VerificationError, + "generic getelementptr constant has no source element type"); + } + for (const GenericConstantData &Element : Value.Elements) { + auto Result = verifyConstant(Element, Depth + 1, Nodes); + if (!Result) + return Result; + } + + const bool IsBinary = Value.ExpressionOpcode == GenericConstantExpressionOpcode::Add || + Value.ExpressionOpcode == GenericConstantExpressionOpcode::Sub || + Value.ExpressionOpcode == GenericConstantExpressionOpcode::Xor; + const bool IsGEP = Value.ExpressionOpcode == + GenericConstantExpressionOpcode::GetElementPtr; + const std::uint8_t Flags = Value.ExpressionFlags.rawValue(); + const std::uint8_t BinaryFlags = + (std::uint8_t{1} << static_cast<std::uint8_t>(GenericConstantExpressionFlag::NoUnsignedWrap)) | + (std::uint8_t{1} << static_cast<std::uint8_t>(GenericConstantExpressionFlag::NoSignedWrap)); + const std::uint8_t GEPFlags = + (std::uint8_t{1} << static_cast<std::uint8_t>(GenericConstantExpressionFlag::InBounds)) | + (std::uint8_t{1} << + static_cast<std::uint8_t>(GenericConstantExpressionFlag::NoUnsignedSignedWrap)) | + (std::uint8_t{1} << + static_cast<std::uint8_t>(GenericConstantExpressionFlag::GEPNoUnsignedWrap)); + if ((!IsBinary && !IsGEP && Flags != 0) || (IsBinary && (Flags & ~BinaryFlags) != 0) || + (IsGEP && (Flags & ~GEPFlags) != 0)) { + return makeUnexpected(ErrorCode::VerificationError, + "generic constant expression has invalid opcode flags"); + } + + const auto RequireExactOperandCount = [&Value](std::size_t Count) -> Expected<void> { + if (Value.Elements.size() != Count) { + return makeUnexpected(ErrorCode::VerificationError, + "generic constant expression has the wrong operand count"); + } + return {}; + }; + const GenericConstantType &FirstType = Value.Elements.front().Type; + switch (Value.ExpressionOpcode) { + case GenericConstantExpressionOpcode::Trunc: + if (auto Count = RequireExactOperandCount(1); !Count) + return Count; + if (!hasIntegerCastShape(FirstType, Value.Type, true)) { + return makeUnexpected(ErrorCode::VerificationError, + "generic trunc constant has incompatible integer types"); + } + return {}; + case GenericConstantExpressionOpcode::BitCast: { + if (auto Count = RequireExactOperandCount(1); !Count) + return Count; + const bool SourceIsPointer = isPointerOrPointerVectorType(FirstType); + const bool DestinationIsPointer = isPointerOrPointerVectorType(Value.Type); + if (SourceIsPointer || DestinationIsPointer) { + const auto SourceAddressSpace = pointerAddressSpace(FirstType); + const auto DestinationAddressSpace = pointerAddressSpace(Value.Type); + if (!SourceIsPointer || !DestinationIsPointer || !SourceAddressSpace || + !DestinationAddressSpace || *SourceAddressSpace != *DestinationAddressSpace || + !hasSameVectorShape(FirstType, Value.Type)) { + return makeUnexpected(ErrorCode::VerificationError, + "generic bitcast constant has incompatible pointer types"); + } + return {}; + } + const auto SourceWidth = nonPointerFixedBitWidth(FirstType); + const auto DestinationWidth = nonPointerFixedBitWidth(Value.Type); + if (!isNonPointerBitCastType(FirstType) || !isNonPointerBitCastType(Value.Type) || + !SourceWidth || !DestinationWidth || *SourceWidth != *DestinationWidth || + !hasSameVectorShape(FirstType, Value.Type)) { + return makeUnexpected(ErrorCode::VerificationError, + "generic bitcast constant has incompatible fixed-width types"); + } + return {}; + } + case GenericConstantExpressionOpcode::AddressSpaceCast: { + if (auto Count = RequireExactOperandCount(1); !Count) + return Count; + const auto SourceAddressSpace = pointerAddressSpace(FirstType); + const auto DestinationAddressSpace = pointerAddressSpace(Value.Type); + if (!SourceAddressSpace || !DestinationAddressSpace || + *SourceAddressSpace == *DestinationAddressSpace || + !hasSameVectorShape(FirstType, Value.Type)) { + return makeUnexpected(ErrorCode::VerificationError, + "generic addrspacecast constant has incompatible pointer types"); + } + return {}; + } + case GenericConstantExpressionOpcode::IntToPtr: + if (auto Count = RequireExactOperandCount(1); !Count) + return Count; + if (!hasPointerIntegerCastShape(Value.Type, FirstType)) { + return makeUnexpected(ErrorCode::VerificationError, + "generic inttoptr constant has incompatible operand types"); + } + return {}; + case GenericConstantExpressionOpcode::PtrToAddr: + case GenericConstantExpressionOpcode::PtrToInt: + if (auto Count = RequireExactOperandCount(1); !Count) + return Count; + if (!hasPointerIntegerCastShape(FirstType, Value.Type)) { + return makeUnexpected(ErrorCode::VerificationError, + "generic pointer-to-integer constant has incompatible operand types"); + } + return {}; + case GenericConstantExpressionOpcode::Add: + case GenericConstantExpressionOpcode::Sub: + case GenericConstantExpressionOpcode::Xor: + if (auto Count = RequireExactOperandCount(2); !Count) + return Count; + if (!isIntegerOrIntegerVectorType(Value.Type) || + !sameResolvedType(Value.Elements[0].Type, Value.Type) || + !sameResolvedType(Value.Elements[1].Type, Value.Type)) { + return makeUnexpected(ErrorCode::VerificationError, + "generic binary constant requires equal integer operand and result types"); + } + return {}; + case GenericConstantExpressionOpcode::GetElementPtr: { + if (!Value.SourceElementType || !isSizedType(*Value.SourceElementType) || + !isPointerOrPointerVectorType(FirstType) || + !isPointerOrPointerVectorType(Value.Type)) { + return makeUnexpected(ErrorCode::VerificationError, + "generic getelementptr constant has invalid source or pointer types"); + } + const auto BaseAddressSpace = pointerAddressSpace(FirstType); + const auto ResultAddressSpace = pointerAddressSpace(Value.Type); + if (!BaseAddressSpace || !ResultAddressSpace || *BaseAddressSpace != *ResultAddressSpace) { + return makeUnexpected(ErrorCode::VerificationError, + "generic getelementptr constant changes pointer address space"); + } + std::optional<VectorShape> RequiredResultShape = vectorShape(FirstType); + for (std::size_t Index = 1; Index < Value.Elements.size(); ++Index) { + const GenericConstantType &IndexType = Value.Elements[Index].Type; + if (!isIntegerOrIntegerVectorType(IndexType)) { + return makeUnexpected(ErrorCode::VerificationError, + "generic getelementptr index must be an integer or integer vector"); + } + if (const auto IndexShape = vectorShape(IndexType)) { + if (RequiredResultShape && *RequiredResultShape != *IndexShape) { + return makeUnexpected( + ErrorCode::VerificationError, + "generic getelementptr vector index has an incompatible element count"); + } + RequiredResultShape = *IndexShape; + } + } + if (RequiredResultShape != vectorShape(Value.Type)) { + return makeUnexpected(ErrorCode::VerificationError, + "generic getelementptr result has an incompatible vector shape"); + } + if (Value.InRangeStart && + (Value.InRangeStart->bitWidth() != Value.InRangeEnd->bitWidth() || + !signedIntegerLess(*Value.InRangeStart, *Value.InRangeEnd))) { + return makeUnexpected(ErrorCode::VerificationError, + "generic getelementptr inrange interval is invalid"); + } + return {}; + } + case GenericConstantExpressionOpcode::ShuffleVector: { + if (auto Count = RequireExactOperandCount(3); !Count) + return Count; + const GenericConstantType *InputElement = vectorElementType(FirstType); + const GenericConstantType *MaskElement = vectorElementType(Value.Elements[2].Type); + const GenericConstantType *ResultElement = vectorElementType(Value.Type); + const auto InputShape = vectorShape(FirstType); + const auto MaskShape = vectorShape(Value.Elements[2].Type); + const auto ResultShape = vectorShape(Value.Type); + if (InputElement == nullptr || MaskElement == nullptr || ResultElement == nullptr || + !InputShape || !MaskShape || !ResultShape || + !sameResolvedType(FirstType, Value.Elements[1].Type) || + !integerScalarBitWidth(*MaskElement) || *integerScalarBitWidth(*MaskElement) != 32 || + InputShape->Scalable != MaskShape->Scalable || + !sameResolvedType(*InputElement, *ResultElement) || + ResultShape->ElementCount != MaskShape->ElementCount || + ResultShape->Scalable != MaskShape->Scalable) { + return makeUnexpected(ErrorCode::VerificationError, + "generic shufflevector constant has incompatible operand types"); + } + return {}; + } + case GenericConstantExpressionOpcode::InsertElement: { + if (auto Count = RequireExactOperandCount(3); !Count) + return Count; + const GenericConstantType *Element = vectorElementType(FirstType); + if (Element == nullptr || !sameResolvedType(Value.Type, FirstType) || + !sameResolvedType(Value.Elements[1].Type, *Element) || + !isIntegerScalarType(Value.Elements[2].Type)) { + return makeUnexpected(ErrorCode::VerificationError, + "generic insertelement constant has incompatible operand types"); + } + return {}; + } + case GenericConstantExpressionOpcode::ExtractElement: { + if (auto Count = RequireExactOperandCount(2); !Count) + return Count; + const GenericConstantType *Element = vectorElementType(FirstType); + if (Element == nullptr || !sameResolvedType(Value.Type, *Element) || + !isIntegerScalarType(Value.Elements[1].Type)) { + return makeUnexpected(ErrorCode::VerificationError, + "generic extractelement constant has incompatible operand types"); + } + return {}; + } + case GenericConstantExpressionOpcode::Splat: { + if (auto Count = RequireExactOperandCount(1); !Count) + return Count; + const GenericConstantType *Element = vectorElementType(Value.Type); + if (Element == nullptr || !sameResolvedType(FirstType, *Element)) { + return makeUnexpected(ErrorCode::VerificationError, + "generic splat constant has incompatible operand types"); + } + return {}; + } + case GenericConstantExpressionOpcode::Invalid: + break; + } + return makeUnexpected(ErrorCode::InternalError, + "generic constant expression opcode is unhandled"); + } + return makeUnexpected(ErrorCode::InternalError, "generic constant value kind is unhandled"); +} + +} // namespace + +Expected<GenericConstantType> GenericConstantType::integer(std::uint32_t Width) { + if (Width == 0 || Width > IntegerImmediate::MaxBitWidth) + return makeUnexpected(ErrorCode::InvalidArgument, "generic integer type width is invalid"); + GenericConstantType Type; + Type.Kind = GenericConstantTypeKind::Integer; + Type.BitWidth = Width; + return Type; +} + +GenericConstantType GenericConstantType::primitive(GenericConstantTypeKind PrimitiveKind) noexcept { + GenericConstantType Type; + Type.Kind = PrimitiveKind; + return Type; +} + +GenericConstantType GenericConstantType::byte() noexcept { + GenericConstantType Type; + Type.Kind = GenericConstantTypeKind::Byte; + Type.BitWidth = 8; + return Type; +} + +Expected<GenericConstantType> GenericConstantType::floating(FloatSemantics ValueSemantics) { + if (!isKnownFloatSemantics(ValueSemantics)) + return makeUnexpected(ErrorCode::InvalidArgument, "generic floating semantics is unknown"); + GenericConstantType Type; + Type.Kind = GenericConstantTypeKind::Floating; + Type.Semantics = ValueSemantics; + Type.BitWidth = getFloatSemanticsBitWidth(ValueSemantics).value_or(0); + return Type; +} + +GenericConstantType GenericConstantType::pointer(std::uint32_t PointerAddressSpace) noexcept { + GenericConstantType Type; + Type.Kind = GenericConstantTypeKind::Pointer; + Type.AddressSpace = PointerAddressSpace; + return Type; +} + +Expected<GenericConstantType> GenericConstantType::array(std::uint64_t Count, + GenericConstantType Element) { + auto Verification = Element.verify(); + if (!Verification) + return std::unexpected(Verification.error()); + GenericConstantType Type; + Type.Kind = GenericConstantTypeKind::Array; + Type.ElementCount = Count; + Type.Elements.push_back(std::move(Element)); + return Type; +} + +Expected<GenericConstantType> GenericConstantType::vector(std::uint64_t Count, + GenericConstantType Element, + bool IsScalable) { + if (Count == 0) + return makeUnexpected(ErrorCode::InvalidArgument, "generic vector type cannot be empty"); + auto Verification = Element.verify(); + if (!Verification) + return std::unexpected(Verification.error()); + GenericConstantType Type; + Type.Kind = GenericConstantTypeKind::Vector; + Type.ElementCount = Count; + Type.Scalable = IsScalable; + Type.Elements.push_back(std::move(Element)); + return Type; +} + +Expected<GenericConstantType> +GenericConstantType::structure(std::vector<GenericConstantType> MemberTypes, bool IsPacked) { + GenericConstantType Type; + Type.Kind = GenericConstantTypeKind::Struct; + Type.Packed = IsPacked; + Type.Elements = std::move(MemberTypes); + auto Verification = Type.verify(); + if (!Verification) + return std::unexpected(Verification.error()); + return Type; +} + +Expected<GenericConstantType> +GenericConstantType::function(GenericConstantType Return, + std::vector<GenericConstantType> Parameters, bool IsVarArg) { + GenericConstantType Type; + Type.Kind = GenericConstantTypeKind::Function; + Type.VarArg = IsVarArg; + Type.Elements.reserve(Parameters.size() + 1); + Type.Elements.push_back(std::move(Return)); + for (GenericConstantType &Parameter : Parameters) + Type.Elements.push_back(std::move(Parameter)); + auto Verification = Type.verify(); + if (!Verification) + return std::unexpected(Verification.error()); + return Type; +} + +Expected<GenericConstantType> GenericConstantType::named(std::string TypeName, + bool IsNumericName) { + if (TypeName.empty()) + return makeUnexpected(ErrorCode::InvalidArgument, "generic named type cannot be empty"); + GenericConstantType Type; + Type.Kind = GenericConstantTypeKind::Named; + Type.Name = std::move(TypeName); + Type.NumericName = IsNumericName; + return Type; +} + +Expected<void> GenericConstantType::verify() const { + std::size_t Nodes = 0; + return verifyType(*this, 0, Nodes); +} + +Expected<GenericConstantData> GenericConstantData::integer(IntegerImmediate Value) { + auto Type = GenericConstantType::integer(Value.bitWidth()); + if (!Type) + return std::unexpected(Type.error()); + GenericConstantData Result; + Result.Type = std::move(*Type); + Result.Kind = GenericConstantKind::Integer; + Result.IntegerValue = std::move(Value); + return Result; +} + +Expected<GenericConstantData> GenericConstantData::floating(FloatingPointBits Value) { + auto Type = GenericConstantType::floating(Value.semantics()); + if (!Type) + return std::unexpected(Type.error()); + GenericConstantData Result; + Result.Type = std::move(*Type); + Result.Kind = GenericConstantKind::Floating; + Result.FloatingValue = Value; + return Result; +} + +Expected<GenericConstantData> GenericConstantData::byteString(GenericConstantType ValueType, + std::vector<std::byte> Bytes) { + GenericConstantData Result; + Result.Type = std::move(ValueType); + Result.Kind = GenericConstantKind::ByteString; + Result.ByteValue = std::move(Bytes); + auto Verification = Result.verify(); + if (!Verification) + return std::unexpected(Verification.error()); + return Result; +} + +Expected<void> GenericConstantData::verify() const { + std::size_t Nodes = 0; + return verifyConstant(*this, 0, Nodes); +} + +bool GenericConstantData::referencesSymbol() const noexcept { + switch (Kind) { + case GenericConstantKind::GlobalAddress: + case GenericConstantKind::BlockAddress: + case GenericConstantKind::DSOLocalEquivalent: + case GenericConstantKind::NoCFI: + return true; + default: + break; + } + for (const GenericConstantData &Element : Elements) { + if (Element.referencesSymbol()) + return true; + } + return false; +} + +} // namespace rcc diff --git a/backend/lib/CodeGen/MachineConstantPool.cpp b/backend/lib/CodeGen/MachineConstantPool.cpp index 43f2620..bd46f2d 100644 --- a/backend/lib/CodeGen/MachineConstantPool.cpp +++ b/backend/lib/CodeGen/MachineConstantPool.cpp @@ -53,6 +53,34 @@ getTargetConstant(const MachineConstantPoolValue &Value) noexcept { return {}; } +[[nodiscard]] std::optional<std::uint64_t> +fixedGenericStoreSize(const GenericConstantType &Type) noexcept { + switch (Type.Kind) { + case GenericConstantTypeKind::Integer: + return (static_cast<std::uint64_t>(Type.BitWidth) + 7U) / 8U; + case GenericConstantTypeKind::Byte: + return 1; + case GenericConstantTypeKind::Floating: + return (static_cast<std::uint64_t>(getFloatSemanticsBitWidth(Type.Semantics).value_or(0)) + + 7U) / + 8U; + case GenericConstantTypeKind::Array: + case GenericConstantTypeKind::Vector: { + if (Type.Scalable || Type.Elements.size() != 1) + return std::nullopt; + const auto ElementSize = fixedGenericStoreSize(Type.Elements.front()); + if (!ElementSize || + (*ElementSize != 0 && + Type.ElementCount > std::numeric_limits<std::uint64_t>::max() / *ElementSize)) { + return std::nullopt; + } + return *ElementSize * Type.ElementCount; + } + default: + return std::nullopt; + } +} + } // namespace ConstantPoolEntryKind MachineConstantPoolEntryDesc::kind() const noexcept { @@ -95,13 +123,21 @@ Expected<void> MachineConstantPoolEntryDesc::verify() const { switch (kind()) { case ConstantPoolEntryKind::Generic: { const auto *Generic = std::get_if<GenericConstantData>(&Value); - if (Generic == nullptr || Generic->TypeIdentity.empty() || Generic->Bytes.size() != Size) { + if (Generic == nullptr) { + return makeUnexpected(ErrorCode::VerificationError, + "generic constant pool entry has an invalid payload"); + } + auto Verification = Generic->verify(); + if (!Verification) + return Verification; + if (const auto FixedSize = fixedGenericStoreSize(Generic->Type); + FixedSize && *FixedSize != Size) { return makeUnexpected(ErrorCode::VerificationError, - "generic constant pool entry has invalid type or byte size"); + "generic constant store size does not match its pool entry size"); } - if (Relocation != ConstantPoolRelocationMode::None) { + if (!Generic->referencesSymbol() && Relocation != ConstantPoolRelocationMode::None) { return makeUnexpected(ErrorCode::VerificationError, - "raw generic constant pool bytes cannot carry relocations"); + "symbol-free generic constant cannot require relocation"); } return {}; } diff --git a/backend/lib/CodeGen/MachineFrameInfo.cpp b/backend/lib/CodeGen/MachineFrameInfo.cpp index 23f9852..21a4b78 100644 --- a/backend/lib/CodeGen/MachineFrameInfo.cpp +++ b/backend/lib/CodeGen/MachineFrameInfo.cpp @@ -5,6 +5,7 @@ #include <cstddef> #include <cstdint> #include <limits> +#include <string> #include <string_view> #include <unordered_set> #include <utility> @@ -50,17 +51,53 @@ std::string_view getStackIDName(StackID ID) noexcept { return "unknown"; } +std::optional<MachineFramePointerPolicy> +parseMachineFramePointerPolicy(std::string_view Name) noexcept { + if (Name == "none") + return MachineFramePointerPolicy::None; + if (Name == "non-leaf") + return MachineFramePointerPolicy::NonLeaf; + if (Name == "all") + return MachineFramePointerPolicy::All; + if (Name == "reserved") + return MachineFramePointerPolicy::Reserved; + if (Name == "non-leaf-no-reserve") + return MachineFramePointerPolicy::NonLeafNoReserve; + return std::nullopt; +} + +std::string_view +getMachineFramePointerPolicyName(MachineFramePointerPolicy Policy) noexcept { + switch (Policy) { + case MachineFramePointerPolicy::None: + return "none"; + case MachineFramePointerPolicy::NonLeaf: + return "non-leaf"; + case MachineFramePointerPolicy::All: + return "all"; + case MachineFramePointerPolicy::Reserved: + return "reserved"; + case MachineFramePointerPolicy::NonLeafNoReserve: + return "non-leaf-no-reserve"; + case MachineFramePointerPolicy::Count: + return "unknown"; + } + return "unknown"; +} + bool StackObject::isSpill() const noexcept { const StackObjectDesc *Desc = description(); if (Desc == nullptr) return false; return Desc->Kind == StackObjectKind::SpillSlot || Desc->Kind == StackObjectKind::CalleeSaved || - Desc->Kind == StackObjectKind::EmergencySpill; + Desc->Kind == StackObjectKind::EmergencySpill || + Desc->Kind == StackObjectKind::FixedSpillSlot; } bool StackObject::isFixed() const noexcept { const StackObjectDesc *Desc = description(); - return Desc != nullptr && Desc->Kind == StackObjectKind::FixedABI; + return Desc != nullptr && (Desc->Kind == StackObjectKind::FixedABI || + Desc->Kind == StackObjectKind::FixedSpillSlot); } bool StackObject::isVariableSized() const noexcept { @@ -82,15 +119,35 @@ Expected<void> MachineFrameInfo::verifyDescription(const StackObjectDesc &Descri return makeUnexpected(ErrorCode::InvalidArgument, "variable-sized stack objects cannot have a fixed size or offset"); } - } else if (!Description.Size || *Description.Size == 0) { + } else if (!Description.Size) { return makeUnexpected(ErrorCode::InvalidArgument, - "fixed-size stack objects require a nonzero size"); + "fixed-size stack objects require an explicit size"); } - if (Description.Kind == StackObjectKind::FixedABI && !Description.Offset) { + if ((Description.Kind == StackObjectKind::FixedABI || + Description.Kind == StackObjectKind::FixedSpillSlot) && + !Description.Offset) { return makeUnexpected(ErrorCode::InvalidArgument, "fixed ABI stack objects require a fixed offset"); } + if (Description.AllocaName && Description.AllocaName->empty()) { + return makeUnexpected(ErrorCode::InvalidArgument, + "named stack objects require a nonempty alloca name"); + } + if (Description.IsImmutable && Description.Kind != StackObjectKind::FixedABI) { + return makeUnexpected(ErrorCode::InvalidArgument, + "only fixed ABI stack objects may be immutable"); + } + if (Description.CalleeSavedRegister && Description.CalleeSavedRegister->number() == 0) { + return makeUnexpected(ErrorCode::InvalidArgument, + "callee-saved stack objects require a nonzero physical register"); + } + if (Description.LocalOffset && + (Description.Kind == StackObjectKind::FixedABI || + Description.Kind == StackObjectKind::FixedSpillSlot)) { + return makeUnexpected(ErrorCode::InvalidArgument, + "fixed ABI stack objects cannot have a local frame offset"); + } if (Description.Kind == StackObjectKind::Target && !Description.Stack.isSpecialStack() && Description.TargetFlags == 0) { return makeUnexpected( @@ -118,6 +175,41 @@ Expected<void> MachineFrameInfo::verifyDescription(const StackObjectDesc &Descri return {}; } +Expected<void> MachineFrameInfo::verifyStateStructure(const MachineFrameState &State) { + if (State.FramePointerPolicy >= MachineFramePointerPolicy::Count) { + return makeUnexpected(ErrorCode::InvalidArgument, "frame-pointer policy is invalid"); + } + + auto VerifyPoints = [](std::span<const MachineFrameSaveRestorePoint> Points, + std::string_view Kind) -> Expected<void> { + std::unordered_set<MachineBlockStableID> Blocks; + for (const MachineFrameSaveRestorePoint &Point : Points) { + if (!Point.Block.isValid()) { + return makeUnexpected(ErrorCode::InvalidArgument, + std::string(Kind) + " point requires a valid basic-block identity"); + } + if (!Blocks.insert(Point.Block).second) { + return makeUnexpected(ErrorCode::InvalidArgument, + std::string(Kind) + " points must use unique basic blocks"); + } + std::unordered_set<PhysicalRegister> Registers; + for (const PhysicalRegister Reg : Point.Registers) { + if (Reg.number() == 0 || !Registers.insert(Reg).second) { + return makeUnexpected(ErrorCode::InvalidArgument, + std::string(Kind) + + " point registers must be nonzero and unique"); + } + } + } + return {}; + }; + + auto SaveResult = VerifyPoints(State.SavePoints, "save"); + if (!SaveResult) + return SaveResult; + return VerifyPoints(State.RestorePoints, "restore"); +} + Expected<FrameIndex> MachineFrameInfo::createObject(StackObjectDesc Description) { if (Frozen) return makeUnexpected(ErrorCode::InvalidArgument, "frozen frame cannot create stack objects"); @@ -214,7 +306,26 @@ Expected<void> MachineFrameInfo::setMaxCallFrameSize(std::uint64_t Size) { if (Frozen) return makeUnexpected(ErrorCode::InvalidArgument, "frozen frame cannot change its maximum call-frame size"); - MaxCallFrameSize = Size; + State.MaxCallFrameSize = Size; + return {}; +} + +Expected<void> MachineFrameInfo::clearMaxCallFrameSize() { + if (Frozen) { + return makeUnexpected(ErrorCode::InvalidArgument, + "frozen frame cannot change its maximum call-frame size"); + } + State.MaxCallFrameSize.reset(); + return {}; +} + +Expected<void> MachineFrameInfo::setState(MachineFrameState NewState) { + if (Frozen) + return makeUnexpected(ErrorCode::InvalidArgument, "frozen frame cannot replace its state"); + auto Verification = verifyStateStructure(NewState); + if (!Verification) + return std::unexpected(Verification.error()); + State = std::move(NewState); return {}; } @@ -234,13 +345,17 @@ Expected<void> MachineFrameInfo::freeze(std::uint64_t Size, Alignment Align) { "static stack size must be aligned to the stack alignment"); } - StaticStackSize = Size; - StackAlign = Align; + State.StackSize = Size; + State.MaxAlignment = Align; Frozen = true; return {}; } Expected<void> MachineFrameInfo::verify() const { + auto StateResult = verifyStateStructure(State); + if (!StateResult) + return StateResult; + for (std::size_t Position = 0; Position < Objects.size(); ++Position) { const StackObject &Object = Objects[Position]; if (Object.index() != FrameIndex::fromValue(static_cast<std::uint32_t>(Position))) { @@ -258,11 +373,17 @@ Expected<void> MachineFrameInfo::verify() const { } } - if (Frozen != StaticStackSize.has_value()) { + for (const std::optional<FrameIndex> Index : {State.StackProtector, State.FunctionContext}) { + if (Index && object(*Index) == nullptr) { + return makeUnexpected(ErrorCode::VerificationError, + "frame state references an unknown stack object"); + } + } + if (Frozen && !State.MaxAlignment) { return makeUnexpected(ErrorCode::VerificationError, - "frame frozen state and static stack size are inconsistent"); + "frozen frame is missing its maximum alignment"); } - if (Frozen && *StaticStackSize % StackAlign.bytes() != 0) { + if (Frozen && State.StackSize % State.MaxAlignment->bytes() != 0) { return makeUnexpected(ErrorCode::VerificationError, "frozen static stack size violates stack alignment"); } diff --git a/backend/lib/CodeGen/MachineFunctionCloner.cpp b/backend/lib/CodeGen/MachineFunctionCloner.cpp index 9342c5a..ced3b1a 100644 --- a/backend/lib/CodeGen/MachineFunctionCloner.cpp +++ b/backend/lib/CodeGen/MachineFunctionCloner.cpp @@ -263,10 +263,29 @@ cloneMemOperand(const MachineMemOperand &SourceOperand, CloneState &State) { return std::unexpected(DestinationIndex.error()); State.Result.FrameIndices.emplace(SourceObject.index(), *DestinationIndex); } - auto CallFrameResult = State.Destination.frameInfo().setMaxCallFrameSize( - State.Source.frameInfo().maxCallFrameSize()); - if (!CallFrameResult) - return std::unexpected(CallFrameResult.error()); + MachineFrameState FrameState = State.Source.frameInfo().state(); + FrameState.SavePoints.clear(); + FrameState.RestorePoints.clear(); + auto MapFrameReference = [&State](std::optional<FrameIndex> &Reference) -> Expected<void> { + if (!Reference) + return {}; + const auto Position = State.Result.FrameIndices.find(*Reference); + if (Position == State.Result.FrameIndices.end()) { + return makeUnexpected(ErrorCode::VerificationError, + "source frame state references an unmapped stack object"); + } + Reference = Position->second; + return {}; + }; + auto StackProtectorResult = MapFrameReference(FrameState.StackProtector); + if (!StackProtectorResult) + return StackProtectorResult; + auto FunctionContextResult = MapFrameReference(FrameState.FunctionContext); + if (!FunctionContextResult) + return FunctionContextResult; + auto StateResult = State.Destination.frameInfo().setState(std::move(FrameState)); + if (!StateResult) + return std::unexpected(StateResult.error()); return {}; } @@ -491,6 +510,34 @@ cloneMemOperand(const MachineMemOperand &SourceOperand, CloneState &State) { return std::unexpected(UpdateResult.error()); } + MachineFrameState FrameState = State.Destination.frameInfo().state(); + auto ClonePoints = + [&State](std::span<const MachineFrameSaveRestorePoint> SourcePoints, + std::vector<MachineFrameSaveRestorePoint> &DestinationPoints) -> Expected<void> { + DestinationPoints.clear(); + DestinationPoints.reserve(SourcePoints.size()); + for (const MachineFrameSaveRestorePoint &Point : SourcePoints) { + const auto Position = State.Result.BlockStableIDs.find(Point.Block); + if (Position == State.Result.BlockStableIDs.end()) { + return makeUnexpected(ErrorCode::VerificationError, + "source frame save/restore point references an unmapped block"); + } + DestinationPoints.push_back({Position->second, Point.Registers}); + } + return {}; + }; + auto SaveResult = + ClonePoints(State.Source.frameInfo().state().SavePoints, FrameState.SavePoints); + if (!SaveResult) + return SaveResult; + auto RestoreResult = + ClonePoints(State.Source.frameInfo().state().RestorePoints, FrameState.RestorePoints); + if (!RestoreResult) + return RestoreResult; + auto StateResult = State.Destination.frameInfo().setState(std::move(FrameState)); + if (!StateResult) + return std::unexpected(StateResult.error()); + if (State.Source.frameInfo().isFrozen()) { const auto StackSize = State.Source.frameInfo().staticStackSize(); if (!StackSize) { diff --git a/backend/lib/CodeGen/MachineFunctionProperties.cpp b/backend/lib/CodeGen/MachineFunctionProperties.cpp index 55a4ca3..d3dfdac 100644 --- a/backend/lib/CodeGen/MachineFunctionProperties.cpp +++ b/backend/lib/CodeGen/MachineFunctionProperties.cpp @@ -20,6 +20,8 @@ constexpr std::array PropertyNames{ std::string_view{"FrameFinalized"}, std::string_view{"NoFrameIndices"}, std::string_view{"NoExecutablePseudos"}, + std::string_view{"FailedISel"}, + std::string_view{"FailsVerification"}, }; static_assert(PropertyNames.size() == static_cast<std::size_t>(MachineFunctionProperty::Count)); diff --git a/backend/lib/CodeGen/MachineJumpTableInfo.cpp b/backend/lib/CodeGen/MachineJumpTableInfo.cpp index 4ae0c15..5ba0853 100644 --- a/backend/lib/CodeGen/MachineJumpTableInfo.cpp +++ b/backend/lib/CodeGen/MachineJumpTableInfo.cpp @@ -33,10 +33,18 @@ MachineJumpTableInfo::verifyDescription(const MachineJumpTableDesc &Description) return makeUnexpected(ErrorCode::VerificationError, "target jump table encoding and target encoding ID are inconsistent"); } - if (Description.Encoding == JumpTableEntryEncoding::CompressedOffset && - Description.EntrySize > 4) { + if ((Description.Encoding == JumpTableEntryEncoding::GPRelative32BlockAddress || + Description.Encoding == JumpTableEntryEncoding::LabelDifference32 || + Description.Encoding == JumpTableEntryEncoding::Custom32) && + Description.EntrySize != 4) { return makeUnexpected(ErrorCode::VerificationError, - "compressed jump table entries cannot exceed four bytes"); + "32-bit jump table encodings require four-byte entries"); + } + if ((Description.Encoding == JumpTableEntryEncoding::GPRelative64BlockAddress || + Description.Encoding == JumpTableEntryEncoding::LabelDifference64) && + Description.EntrySize != 8) { + return makeUnexpected(ErrorCode::VerificationError, + "64-bit jump table encodings require eight-byte entries"); } for (const MachineBasicBlock *Target : Description.Targets) { @@ -63,6 +71,37 @@ Expected<JumpTableIndex> MachineJumpTableInfo::createTable(MachineJumpTableDesc return Index; } +Expected<JumpTableIndex> MachineJumpTableInfo::reserveTable() { + if (Frozen) { + return makeUnexpected(ErrorCode::InvalidArgument, + "frozen jump table info cannot reserve tables"); + } + if (Tables.size() > std::numeric_limits<std::uint32_t>::max()) + return makeUnexpected(ErrorCode::Unsupported, "jump table index space is exhausted"); + + const JumpTableIndex Index = JumpTableIndex::fromValue(static_cast<std::uint32_t>(Tables.size())); + Tables.push_back(MachineJumpTableEntry(Index)); + return Index; +} + +Expected<void> +MachineJumpTableInfo::defineReservedTable(JumpTableIndex Index, + MachineJumpTableDesc Description) { + if (Frozen) { + return makeUnexpected(ErrorCode::InvalidArgument, + "frozen jump table info cannot define reserved tables"); + } + if (Index.value() >= Tables.size() || !Tables[Index.value()].isPending()) { + return makeUnexpected(ErrorCode::InvalidArgument, + "jump table index is not an unresolved reservation"); + } + auto Verification = verifyDescription(Description); + if (!Verification) + return std::unexpected(Verification.error()); + Tables[Index.value()].Description.emplace<MachineJumpTableDesc>(std::move(Description)); + return {}; +} + Expected<MachineJumpTableEntry *> MachineJumpTableInfo::mutableTable(JumpTableIndex Index) { if (Frozen) return makeUnexpected(ErrorCode::InvalidArgument, @@ -70,8 +109,10 @@ Expected<MachineJumpTableEntry *> MachineJumpTableInfo::mutableTable(JumpTableIn if (Index.value() >= Tables.size()) return makeUnexpected(ErrorCode::InvalidArgument, "jump table index is out of range"); MachineJumpTableEntry &Entry = Tables[Index.value()]; - if (Entry.isTombstone()) - return makeUnexpected(ErrorCode::InvalidArgument, "jump table index is a tombstone"); + if (Entry.isTombstone() || Entry.isPending()) { + return makeUnexpected(ErrorCode::InvalidArgument, + "jump table index has no defined description"); + } return &Entry; } @@ -130,6 +171,10 @@ Expected<void> MachineJumpTableInfo::verify() const { "jump table index does not match its stable table position"); } const MachineJumpTableDesc *Description = Entry.description(); + if (Entry.isPending()) { + return makeUnexpected(ErrorCode::VerificationError, + "jump table contains an unresolved reservation"); + } if (Description == nullptr) continue; auto Verification = verifyDescription(*Description); diff --git a/backend/lib/CodeGen/MachineMetadata.cpp b/backend/lib/CodeGen/MachineMetadata.cpp index f789a74..756124c 100644 --- a/backend/lib/CodeGen/MachineMetadata.cpp +++ b/backend/lib/CodeGen/MachineMetadata.cpp @@ -27,6 +27,10 @@ namespace { } // namespace +MachineMetadataElement MachineMetadataElement::null() { + return MachineMetadataElement(NullValue{}); +} + Expected<MachineMetadataElement> MachineMetadataElement::reference(MachineMetadataReference Reference) { if (!Reference.isValid()) { diff --git a/backend/lib/CodeGen/MachineOperand.cpp b/backend/lib/CodeGen/MachineOperand.cpp index 6cce188..ae46505 100644 --- a/backend/lib/CodeGen/MachineOperand.cpp +++ b/backend/lib/CodeGen/MachineOperand.cpp @@ -12,6 +12,8 @@ #include <limits> #include <memory> #include <span> +#include <string> +#include <string_view> #include <utility> #include <vector> @@ -94,6 +96,68 @@ Expected<IntegerImmediate> IntegerImmediate::fromSigned(std::uint32_t BitWidth, return create(BitWidth, Words); } +Expected<IntegerImmediate> IntegerImmediate::parseDecimal(std::uint32_t BitWidth, + std::string_view Text) { + if (BitWidth == 0 || BitWidth > MaxBitWidth) { + return makeUnexpected(ErrorCode::ParseError, + "integer immediate bit width is outside the supported range"); + } + const bool Negative = Text.starts_with('-'); + if (Negative) + Text.remove_prefix(1); + if (Text.empty()) + return makeUnexpected(ErrorCode::ParseError, "integer immediate has no decimal digits"); + + std::vector<std::uint64_t> Words(integerWordCount(BitWidth), 0); + for (const char Character : Text) { + if (Character < '0' || Character > '9') { + return makeUnexpected(ErrorCode::ParseError, + "integer immediate contains a non-decimal digit"); + } + std::uint64_t Carry = static_cast<unsigned>(Character - '0'); + for (std::uint64_t &Word : Words) { + const unsigned __int128 Product = static_cast<unsigned __int128>(Word) * 10U + Carry; + Word = static_cast<std::uint64_t>(Product); + Carry = static_cast<std::uint64_t>(Product >> 64U); + } + if (Carry != 0 || (Words.back() & ~integerHighWordMask(BitWidth)) != 0) { + return makeUnexpected(ErrorCode::ParseError, + "integer immediate does not fit its declared bit width"); + } + } + + if (Negative) { + if (std::ranges::all_of(Words, [](std::uint64_t Word) { return Word == 0; })) { + return makeUnexpected(ErrorCode::ParseError, + "negative zero is not a canonical integer immediate"); + } + const std::uint32_t SignBit = BitWidth - 1U; + const std::size_t SignWord = SignBit / 64U; + const std::uint64_t SignMask = std::uint64_t{1} << (SignBit % 64U); + bool Fits = Words[SignWord] <= SignMask; + for (std::size_t Index = SignWord + 1; Index < Words.size(); ++Index) + Fits = Fits && Words[Index] == 0; + if (Words[SignWord] == SignMask) { + for (std::size_t Index = 0; Index < SignWord; ++Index) + Fits = Fits && Words[Index] == 0; + } + if (!Fits) { + return makeUnexpected(ErrorCode::ParseError, + "negative integer immediate does not fit its declared bit width"); + } + for (std::uint64_t &Word : Words) + Word = ~Word; + std::uint64_t Carry = 1; + for (std::uint64_t &Word : Words) { + const std::uint64_t Previous = Word; + Word += Carry; + Carry = Carry != 0 && Word < Previous ? 1 : 0; + } + Words.back() &= integerHighWordMask(BitWidth); + } + return create(BitWidth, Words); +} + bool IntegerImmediate::isZero() const noexcept { return std::ranges::all_of(Words, [](std::uint64_t Word) { return Word == 0; }); } @@ -103,6 +167,25 @@ bool IntegerImmediate::isNegative() const noexcept { return (Words[SignBit / 64U] & (std::uint64_t{1} << (SignBit % 64U))) != 0; } +std::string IntegerImmediate::toUnsignedDecimal() const { + if (isZero()) + return "0"; + std::vector<std::uint64_t> Remaining = Words; + std::string Digits; + while (!std::ranges::all_of(Remaining, + [](std::uint64_t Word) { return Word == 0; })) { + unsigned __int128 Remainder = 0; + for (auto Iterator = Remaining.rbegin(); Iterator != Remaining.rend(); ++Iterator) { + const unsigned __int128 Dividend = (Remainder << 64U) | *Iterator; + *Iterator = static_cast<std::uint64_t>(Dividend / 10U); + Remainder = Dividend % 10U; + } + Digits.push_back(static_cast<char>('0' + static_cast<unsigned>(Remainder))); + } + std::ranges::reverse(Digits); + return Digits; +} + Expected<void> IntegerImmediate::verify() const { return verifyIntegerImmediate(BitWidth, Words, ErrorCode::VerificationError); } diff --git a/backend/lib/CodeGen/MachineVerifier.cpp b/backend/lib/CodeGen/MachineVerifier.cpp index 3ebe683..f6f5640 100644 --- a/backend/lib/CodeGen/MachineVerifier.cpp +++ b/backend/lib/CodeGen/MachineVerifier.cpp @@ -211,6 +211,7 @@ private: return fail("block layout contains a cycle or duplicate block"); if (!Block->isLinked() || Block->previous() != Previous) return fail("block layout ownership or previous link is inconsistent"); + LinkedBlockIDs.insert(Block->stableID()); if (Block->denseNumber() != LayoutIndex || Block->layoutIndex() != LayoutIndex) { return fail("block dense number or layout index is stale"); } @@ -427,6 +428,30 @@ private: } [[nodiscard]] Expected<void> verifyFrameLifetimes() const { + auto VerifyPoints = + [this](std::span<const MachineFrameSaveRestorePoint> Points, + std::string_view Kind) -> Expected<void> { + for (const MachineFrameSaveRestorePoint &Point : Points) { + if (!LinkedBlockIDs.contains(Point.Block)) { + return fail(std::string("frame ") + std::string(Kind) + + " point references a detached or unknown block"); + } + for (const PhysicalRegister Reg : Point.Registers) { + if (!Target.registers().isKnownPhysicalRegister(Reg)) { + return fail(std::string("frame ") + std::string(Kind) + + " point references an unknown physical register"); + } + } + } + return {}; + }; + auto SaveResult = VerifyPoints(Function.frameInfo().state().SavePoints, "save"); + if (!SaveResult) + return SaveResult; + auto RestoreResult = VerifyPoints(Function.frameInfo().state().RestorePoints, "restore"); + if (!RestoreResult) + return RestoreResult; + for (const StackObject &Object : Function.frameInfo().objects()) { const StackObjectDesc *Description = Object.description(); if (Description == nullptr) @@ -442,6 +467,10 @@ private: if (!DebugInfoResult) return failWith("stack object debug information", DebugInfoResult.error()); } + if (Description->CalleeSavedRegister && + !Target.registers().isKnownPhysicalRegister(*Description->CalleeSavedRegister)) { + return fail("callee-saved stack object references an unknown physical register"); + } } return {}; } @@ -794,6 +823,7 @@ private: MachineVerifierOptions Options; std::unordered_set<const MachineBasicBlock *> RegisteredBlocks; std::unordered_set<const MachineBasicBlock *> LinkedBlocks; + std::unordered_set<MachineBlockStableID> LinkedBlockIDs; std::unordered_set<const MachineInstr *> RegisteredInstructions; std::unordered_set<const MachineInstr *> LinkedInstructions; std::unordered_set<MachineInstrStableID> LinkedInstructionIDs; diff --git a/backend/lib/CodeGen/TargetMIRTextInfo.cpp b/backend/lib/CodeGen/TargetMIRTextInfo.cpp index a349432..65e3c65 100644 --- a/backend/lib/CodeGen/TargetMIRTextInfo.cpp +++ b/backend/lib/CodeGen/TargetMIRTextInfo.cpp @@ -8,6 +8,7 @@ #include <cstddef> #include <cstdint> #include <string> +#include <string_view> #include <unordered_set> #include <utility> #include <vector> @@ -51,6 +52,89 @@ namespace { } // namespace +MIRTargetNode::MIRTargetNode() = default; +MIRTargetNode::MIRTargetNode(const MIRTargetNode &) = default; +MIRTargetNode &MIRTargetNode::operator=(const MIRTargetNode &) = default; +MIRTargetNode::MIRTargetNode(MIRTargetNode &&) noexcept = default; +MIRTargetNode &MIRTargetNode::operator=(MIRTargetNode &&) noexcept = default; +MIRTargetNode::~MIRTargetNode() = default; + +MIRTargetNode MIRTargetNode::null() { + return {}; +} + +MIRTargetNode MIRTargetNode::scalar(std::string Value) { + MIRTargetNode Result; + Result.Kind = MIRTargetNodeKind::Scalar; + Result.Scalar = std::move(Value); + return Result; +} + +MIRTargetNode MIRTargetNode::sequence(std::vector<MIRTargetNode> Values) { + MIRTargetNode Result; + Result.Kind = MIRTargetNodeKind::Sequence; + Result.Sequence = std::move(Values); + return Result; +} + +MIRTargetNode MIRTargetNode::mapping(std::vector<MIRTargetMappingEntry> Entries) { + MIRTargetNode Result; + Result.Kind = MIRTargetNodeKind::Mapping; + Result.Mapping = std::move(Entries); + return Result; +} + +bool operator==(const MIRTargetNode &Left, const MIRTargetNode &Right) { + return Left.Kind == Right.Kind && Left.Scalar == Right.Scalar && + Left.Sequence == Right.Sequence && Left.Mapping == Right.Mapping; +} + +const MIRTargetNode *MIRTargetNode::mappingValue(std::string_view Key) const noexcept { + if (Kind != MIRTargetNodeKind::Mapping) + return nullptr; + for (const MIRTargetMappingEntry &Entry : Mapping) { + if (Entry.Key == Key) + return &Entry.Value; + } + return nullptr; +} + +Expected<void> MIRTargetNode::verify() const { + if (Kind >= MIRTargetNodeKind::Count) + return makeUnexpected(ErrorCode::InvalidArgument, "target MIR node kind is invalid"); + + const bool HasScalar = !Scalar.empty(); + const bool HasSequence = !Sequence.empty(); + const bool HasMapping = !Mapping.empty(); + if ((Kind != MIRTargetNodeKind::Scalar && HasScalar) || + (Kind != MIRTargetNodeKind::Sequence && HasSequence) || + (Kind != MIRTargetNodeKind::Mapping && HasMapping)) { + return makeUnexpected(ErrorCode::InvalidArgument, + "target MIR node contains storage for a different node kind"); + } + + if (Kind == MIRTargetNodeKind::Sequence) { + for (const MIRTargetNode &Element : Sequence) { + auto ElementResult = Element.verify(); + if (!ElementResult) + return ElementResult; + } + } + if (Kind == MIRTargetNodeKind::Mapping) { + std::unordered_set<std::string_view> Keys; + for (const MIRTargetMappingEntry &Entry : Mapping) { + if (Entry.Key.empty() || !Keys.insert(Entry.Key).second) { + return makeUnexpected(ErrorCode::InvalidArgument, + "target MIR mapping keys must be nonempty and unique"); + } + auto ValueResult = Entry.Value.verify(); + if (!ValueResult) + return ValueResult; + } + } + return {}; +} + Expected<TargetMIRTextInfo> TargetMIRTextInfo::create(TargetMIRTextInfoDesc Description, const TargetRegisterInfo &Registers) { TargetMIRTextInfo Result(std::move(Description)); @@ -192,6 +276,18 @@ Expected<TargetMIRTextInfo> TargetMIRTextInfo::create(TargetMIRTextInfoDesc Desc if (Result.Description.FunctionStateType && Result.Description.FunctionStateType->value() == 0) { return invalidDescription("target function state TypeID must be nonzero"); } + const bool HasStateType = Result.Description.FunctionStateType.has_value(); + const bool HasParser = Result.Description.ParseFunctionState != nullptr; + const bool HasPrinter = Result.Description.PrintFunctionState != nullptr; + if (HasStateType != HasParser || HasStateType != HasPrinter) { + return invalidDescription( + "target function state TypeID, parser, and printer must be provided together"); + } + const bool HasConstantParser = Result.Description.ParseTargetConstant != nullptr; + const bool HasConstantPrinter = Result.Description.PrintTargetConstant != nullptr; + if (HasConstantParser != HasConstantPrinter) { + return invalidDescription("target constant parser and printer must be provided together"); + } return Result; } @@ -365,4 +461,86 @@ TargetMIRTextInfo::physicalRegister(std::uint32_t DwarfRegister) const noexcept return Position->second; } +Expected<TargetFunctionState> +TargetMIRTextInfo::parseFunctionState(const MIRTargetNode &Node) const { + if (Description.ParseFunctionState == nullptr || !Description.FunctionStateType) { + return makeUnexpected(ErrorCode::Unsupported, + "target does not define a machineFunctionInfo MIR schema"); + } + auto NodeResult = Node.verify(); + if (!NodeResult) + return std::unexpected(NodeResult.error()); + auto State = Description.ParseFunctionState(Node); + if (!State) + return std::unexpected(State.error()); + auto StateResult = State->verify(); + if (!StateResult) + return std::unexpected(StateResult.error()); + if (State->typeID() != Description.FunctionStateType) { + return makeUnexpected(ErrorCode::ParseError, + "parsed target function state has the wrong schema TypeID"); + } + return State; +} + +Expected<MIRTargetNode> +TargetMIRTextInfo::printFunctionState(const TargetFunctionState &State) const { + if (Description.PrintFunctionState == nullptr || !Description.FunctionStateType) { + return makeUnexpected(ErrorCode::Unsupported, + "target does not define a machineFunctionInfo MIR schema"); + } + auto StateResult = State.verify(); + if (!StateResult) + return std::unexpected(StateResult.error()); + if (State.typeID() != Description.FunctionStateType) { + return makeUnexpected(ErrorCode::InvalidArgument, + "target function state has the wrong MIR schema TypeID"); + } + auto Node = Description.PrintFunctionState(State); + if (!Node) + return std::unexpected(Node.error()); + auto NodeResult = Node->verify(); + if (!NodeResult) + return std::unexpected(NodeResult.error()); + if (Node->Kind != MIRTargetNodeKind::Mapping) { + return makeUnexpected(ErrorCode::InvalidArgument, + "machineFunctionInfo printer must produce a mapping"); + } + return Node; +} + +Expected<TargetConstant> +TargetMIRTextInfo::parseTargetConstant(std::string_view Text) const { + if (Description.ParseTargetConstant == nullptr) { + return makeUnexpected(ErrorCode::Unsupported, + "target does not define a target constant MIR schema"); + } + auto Constant = Description.ParseTargetConstant(Text); + if (!Constant) + return std::unexpected(Constant.error()); + auto Verification = Constant->verify(); + if (!Verification) + return std::unexpected(Verification.error()); + return Constant; +} + +Expected<std::string> +TargetMIRTextInfo::printTargetConstant(const TargetConstant &Constant) const { + if (Description.PrintTargetConstant == nullptr) { + return makeUnexpected(ErrorCode::Unsupported, + "target does not define a target constant MIR schema"); + } + auto Verification = Constant.verify(); + if (!Verification) + return std::unexpected(Verification.error()); + auto Text = Description.PrintTargetConstant(Constant); + if (!Text) + return std::unexpected(Text.error()); + if (Text->empty()) { + return makeUnexpected(ErrorCode::InvalidArgument, + "target constant printer produced an empty MIR value"); + } + return Text; +} + } // namespace rcc |
