aboutsummaryrefslogtreecommitdiff
path: root/backend/lib/CodeGen
diff options
context:
space:
mode:
Diffstat (limited to 'backend/lib/CodeGen')
-rw-r--r--backend/lib/CodeGen/CMakeLists.txt4
-rw-r--r--backend/lib/CodeGen/GenericConstant.cpp875
-rw-r--r--backend/lib/CodeGen/MachineConstantPool.cpp44
-rw-r--r--backend/lib/CodeGen/MachineFrameInfo.cpp143
-rw-r--r--backend/lib/CodeGen/MachineFunctionCloner.cpp55
-rw-r--r--backend/lib/CodeGen/MachineFunctionProperties.cpp2
-rw-r--r--backend/lib/CodeGen/MachineJumpTableInfo.cpp55
-rw-r--r--backend/lib/CodeGen/MachineMetadata.cpp4
-rw-r--r--backend/lib/CodeGen/MachineOperand.cpp83
-rw-r--r--backend/lib/CodeGen/MachineVerifier.cpp30
-rw-r--r--backend/lib/CodeGen/TargetMIRTextInfo.cpp178
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