diff options
Diffstat (limited to 'backend/lib/IR')
| -rw-r--r-- | backend/lib/IR/AsmWriter.cpp | 393 | ||||
| -rw-r--r-- | backend/lib/IR/CMakeLists.txt | 48 | ||||
| -rw-r--r-- | backend/lib/IR/Constants.cpp | 870 | ||||
| -rw-r--r-- | backend/lib/IR/DataLayout.cpp | 597 |
4 files changed, 1908 insertions, 0 deletions
diff --git a/backend/lib/IR/AsmWriter.cpp b/backend/lib/IR/AsmWriter.cpp new file mode 100644 index 0000000..d5c3f19 --- /dev/null +++ b/backend/lib/IR/AsmWriter.cpp @@ -0,0 +1,393 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "rcc/IR/Constants.h" + +#include <algorithm> +#include <bit> +#include <cstddef> +#include <cstdint> +#include <ranges> +#include <span> +#include <string> +#include <string_view> + +namespace rcc { +namespace { + +[[nodiscard]] bool isIdentifierStart(char Character) noexcept { + return (Character >= 'A' && Character <= 'Z') || (Character >= 'a' && Character <= 'z') || + Character == '-' || Character == '$' || Character == '.' || Character == '_'; +} + +[[nodiscard]] bool isIdentifierBody(char Character) noexcept { + return isIdentifierStart(Character) || (Character >= '0' && Character <= '9'); +} +[[nodiscard]] bool canUseBareName(std::string_view Name) noexcept { + if (Name.empty() || !isIdentifierStart(Name.front())) + return false; + return std::ranges::all_of(Name.substr(1), isIdentifierBody); +} + +[[nodiscard]] std::string printName(char Prefix, std::string_view Name, bool Numeric) { + std::string Result(1, Prefix); + if (Numeric || canUseBareName(Name)) { + Result.append(Name); + return Result; + } + static constexpr std::string_view HexDigits = "0123456789ABCDEF"; + Result.push_back('"'); + for (const char RawCharacter : Name) { + const auto Character = static_cast<unsigned char>(RawCharacter); + if (Character >= 0x20U && Character <= 0x7eU && Character != '"' && Character != '\\') { + Result.push_back(static_cast<char>(Character)); + continue; + } + Result.push_back('\\'); + Result.push_back(HexDigits[Character >> 4U]); + Result.push_back(HexDigits[Character & 0xfU]); + } + Result.push_back('"'); + return Result; +} + +[[nodiscard]] Expected<std::string> printType(const GenericConstantType &Type); + +[[nodiscard]] Expected<std::string> printTypeList(std::span<const GenericConstantType> Types, + std::string_view Open, std::string_view Close) { + std::string Result(Open); + for (std::size_t Index = 0; Index < Types.size(); ++Index) { + if (Index != 0) + Result.append(", "); + auto Element = printType(Types[Index]); + if (!Element) + return std::unexpected(Element.error()); + Result.append(*Element); + } + Result.append(Close); + return Result; +} + +Expected<std::string> printType(const GenericConstantType &Type) { + auto Verification = Type.verify(); + if (!Verification) + return std::unexpected(Verification.error()); + switch (Type.Kind) { + case GenericConstantTypeKind::Invalid: + break; + case GenericConstantTypeKind::Void: + return std::string("void"); + case GenericConstantTypeKind::Label: + return std::string("label"); + case GenericConstantTypeKind::Token: + return std::string("token"); + case GenericConstantTypeKind::Metadata: + return std::string("metadata"); + case GenericConstantTypeKind::Integer: + return "i" + std::to_string(Type.BitWidth); + case GenericConstantTypeKind::Byte: + return std::string("byte"); + case GenericConstantTypeKind::Floating: + switch (Type.Semantics) { + case FloatSemantics::IEEEHalf: + return std::string("half"); + case FloatSemantics::BFloat: + return std::string("bfloat"); + case FloatSemantics::IEEESingle: + return std::string("float"); + case FloatSemantics::IEEEDouble: + return std::string("double"); + case FloatSemantics::IEEEQuad: + return std::string("fp128"); + case FloatSemantics::PPCDoubleDouble: + return std::string("ppc_fp128"); + case FloatSemantics::X87DoubleExtended: + return std::string("x86_fp80"); + default: + return makeUnexpected(ErrorCode::Unsupported, + "floating semantics has no LLVM IR type spelling"); + } + case GenericConstantTypeKind::Pointer: + if (Type.AddressSpace == 0) + return std::string("ptr"); + return "ptr addrspace(" + std::to_string(Type.AddressSpace) + ")"; + case GenericConstantTypeKind::Array: { + auto Element = printType(Type.Elements.front()); + if (!Element) + return std::unexpected(Element.error()); + return "[" + std::to_string(Type.ElementCount) + " x " + *Element + "]"; + } + case GenericConstantTypeKind::Vector: { + auto Element = printType(Type.Elements.front()); + if (!Element) + return std::unexpected(Element.error()); + std::string Result = "<"; + if (Type.Scalable) + Result.append("vscale x "); + Result.append(std::to_string(Type.ElementCount)); + Result.append(" x "); + Result.append(*Element); + Result.push_back('>'); + return Result; + } + case GenericConstantTypeKind::Struct: + return printTypeList(Type.Elements, Type.Packed ? "<{ " : "{ ", Type.Packed ? " }>" : " }"); + case GenericConstantTypeKind::Function: { + auto Return = printType(Type.Elements.front()); + if (!Return) + return std::unexpected(Return.error()); + std::string Result = *Return + " ("; + for (std::size_t Index = 1; Index < Type.Elements.size(); ++Index) { + if (Index != 1) + Result.append(", "); + auto Parameter = printType(Type.Elements[Index]); + if (!Parameter) + return std::unexpected(Parameter.error()); + Result.append(*Parameter); + } + if (Type.VarArg) { + if (Type.Elements.size() != 1) + Result.append(", "); + Result.append("..."); + } + Result.push_back(')'); + return Result; + } + case GenericConstantTypeKind::Named: + return printName('%', Type.Name, Type.NumericName); + } + return makeUnexpected(ErrorCode::VerificationError, "generic constant type is invalid"); +} + +[[nodiscard]] std::string_view +expressionOpcodeName(GenericConstantExpressionOpcode Opcode) noexcept { + switch (Opcode) { + case GenericConstantExpressionOpcode::Trunc: + return "trunc"; + case GenericConstantExpressionOpcode::BitCast: + return "bitcast"; + case GenericConstantExpressionOpcode::AddressSpaceCast: + return "addrspacecast"; + case GenericConstantExpressionOpcode::IntToPtr: + return "inttoptr"; + case GenericConstantExpressionOpcode::PtrToAddr: + return "ptrtoaddr"; + case GenericConstantExpressionOpcode::PtrToInt: + return "ptrtoint"; + case GenericConstantExpressionOpcode::Add: + return "add"; + case GenericConstantExpressionOpcode::Sub: + return "sub"; + case GenericConstantExpressionOpcode::Xor: + return "xor"; + case GenericConstantExpressionOpcode::GetElementPtr: + return "getelementptr"; + case GenericConstantExpressionOpcode::ShuffleVector: + return "shufflevector"; + case GenericConstantExpressionOpcode::InsertElement: + return "insertelement"; + case GenericConstantExpressionOpcode::ExtractElement: + return "extractelement"; + case GenericConstantExpressionOpcode::Splat: + return "splat"; + case GenericConstantExpressionOpcode::Invalid: + return {}; + } + return {}; +} + +[[nodiscard]] Expected<std::string> printValue(const GenericConstantData &Value, bool IncludeType); + +[[nodiscard]] Expected<void> appendTypedOperands(std::string &Output, + std::span<const GenericConstantData> Operands) { + for (std::size_t Index = 0; Index < Operands.size(); ++Index) { + if (Index != 0) + Output.append(", "); + auto Operand = printValue(Operands[Index], true); + if (!Operand) + return std::unexpected(Operand.error()); + Output.append(*Operand); + } + return {}; +} + +Expected<std::string> printValue(const GenericConstantData &Value, bool IncludeType) { + auto Verification = Value.verify(); + if (!Verification) + return std::unexpected(Verification.error()); + std::string Result; + if (IncludeType) { + auto Type = printType(Value.Type); + if (!Type) + return std::unexpected(Type.error()); + Result.append(*Type); + Result.push_back(' '); + } + + switch (Value.Kind) { + case GenericConstantKind::Invalid: + break; + case GenericConstantKind::Integer: + if (!Value.IntegerValue) + return makeUnexpected(ErrorCode::VerificationError, + "integer constant is missing its integer payload"); + if (Value.Type.resolved().Kind == GenericConstantTypeKind::Integer && + Value.Type.resolved().BitWidth == 1) + Result.append(Value.IntegerValue.value().isZero() ? "false" : "true"); + else + Result.append(Value.IntegerValue.value().toUnsignedDecimal()); + return Result; + case GenericConstantKind::Floating: + if (!Value.FloatingValue) + return makeUnexpected(ErrorCode::VerificationError, + "floating constant is missing its floating payload"); + Result.append(Value.FloatingValue->toString()); + return Result; + case GenericConstantKind::NullPointer: + Result.append("null"); + return Result; + case GenericConstantKind::ZeroInitializer: + Result.append("zeroinitializer"); + return Result; + case GenericConstantKind::Undef: + Result.append("undef"); + return Result; + case GenericConstantKind::Poison: + Result.append("poison"); + return Result; + case GenericConstantKind::Aggregate: { + const GenericConstantType &EffectiveType = Value.Type.resolved(); + const bool Packed = + EffectiveType.Kind == GenericConstantTypeKind::Struct && EffectiveType.Packed; + Result.append(Packed ? "<{ " + : EffectiveType.Kind == GenericConstantTypeKind::Array ? "[ " + : EffectiveType.Kind == GenericConstantTypeKind::Struct ? "{ " + : "< "); + auto Appended = appendTypedOperands(Result, Value.Elements); + if (!Appended) + return std::unexpected(Appended.error()); + Result.append(Packed ? " }>" + : EffectiveType.Kind == GenericConstantTypeKind::Array ? " ]" + : EffectiveType.Kind == GenericConstantTypeKind::Struct ? " }" + : " >"); + return Result; + } + case GenericConstantKind::ByteString: { + static constexpr std::string_view Digits = "0123456789ABCDEF"; + Result.append("c\""); + for (const std::byte Byte : Value.ByteValue) { + const unsigned Character = std::to_integer<unsigned>(Byte); + if (Character >= 0x20U && Character <= 0x7eU && Character != '"' && Character != '\\') { + Result.push_back(static_cast<char>(Character)); + } else { + Result.push_back('\\'); + Result.push_back(Digits[Character >> 4U]); + Result.push_back(Digits[Character & 0xfU]); + } + } + Result.push_back('"'); + return Result; + } + case GenericConstantKind::GlobalAddress: + Result.append(printName('@', Value.Symbol, Value.NumericSymbol)); + return Result; + case GenericConstantKind::BlockAddress: + Result.append("blockaddress("); + Result.append(printName('@', Value.Symbol, Value.NumericSymbol)); + Result.append(", "); + Result.append(printName('%', Value.Block, Value.NumericBlock)); + Result.push_back(')'); + return Result; + case GenericConstantKind::DSOLocalEquivalent: + Result.append("dso_local_equivalent "); + Result.append(printName('@', Value.Symbol, Value.NumericSymbol)); + return Result; + case GenericConstantKind::NoCFI: + Result.append("no_cfi "); + Result.append(printName('@', Value.Symbol, Value.NumericSymbol)); + return Result; + case GenericConstantKind::PointerAuth: + Result.append("ptrauth ("); + if (auto Appended = appendTypedOperands(Result, Value.Elements); !Appended) + return std::unexpected(Appended.error()); + Result.push_back(')'); + return Result; + case GenericConstantKind::Expression: { + const std::string_view Opcode = expressionOpcodeName(Value.ExpressionOpcode); + if (Opcode.empty()) + return makeUnexpected(ErrorCode::VerificationError, + "constant expression has an invalid opcode"); + Result.append(Opcode); + if (Value.ExpressionFlags.has(GenericConstantExpressionFlag::InBounds)) + Result.append(" inbounds"); + if (Value.ExpressionFlags.has(GenericConstantExpressionFlag::NoUnsignedSignedWrap)) + Result.append(" nusw"); + if (Value.ExpressionFlags.has(GenericConstantExpressionFlag::GEPNoUnsignedWrap) || + Value.ExpressionFlags.has(GenericConstantExpressionFlag::NoUnsignedWrap)) + Result.append(" nuw"); + if (Value.ExpressionFlags.has(GenericConstantExpressionFlag::NoSignedWrap)) + Result.append(" nsw"); + if (Value.InRangeStart) { + if (!Value.InRangeEnd) { + return makeUnexpected(ErrorCode::VerificationError, + "constant expression is missing its inrange end"); + } + const auto &InRangeStart = Value.InRangeStart.value(); + const auto &InRangeEnd = Value.InRangeEnd.value(); + Result.append(" inrange("); + Result.append(std::to_string(std::bit_cast<std::int64_t>(InRangeStart.words()[0]))); + Result.append(", "); + Result.append(std::to_string(std::bit_cast<std::int64_t>(InRangeEnd.words()[0]))); + Result.push_back(')'); + } + Result.append(" ("); + if (Value.ExpressionOpcode == GenericConstantExpressionOpcode::GetElementPtr) { + if (!Value.SourceElementType) { + return makeUnexpected(ErrorCode::VerificationError, + "getelementptr constant is missing its source element type"); + } + auto SourceType = printType(Value.SourceElementType.value()); + if (!SourceType) + return std::unexpected(SourceType.error()); + Result.append(*SourceType); + Result.append(", "); + } + auto First = printValue(Value.Elements.front(), true); + if (!First) + return std::unexpected(First.error()); + Result.append(*First); + const bool IsCast = + Value.ExpressionOpcode == GenericConstantExpressionOpcode::Trunc || + Value.ExpressionOpcode == GenericConstantExpressionOpcode::BitCast || + Value.ExpressionOpcode == GenericConstantExpressionOpcode::AddressSpaceCast || + Value.ExpressionOpcode == GenericConstantExpressionOpcode::IntToPtr || + Value.ExpressionOpcode == GenericConstantExpressionOpcode::PtrToAddr || + Value.ExpressionOpcode == GenericConstantExpressionOpcode::PtrToInt; + if (IsCast) { + auto Destination = printType(Value.Type); + if (!Destination) + return std::unexpected(Destination.error()); + Result.append(" to "); + Result.append(*Destination); + } else { + for (std::size_t Index = 1; Index < Value.Elements.size(); ++Index) { + Result.append(", "); + auto Operand = printValue(Value.Elements[Index], true); + if (!Operand) + return std::unexpected(Operand.error()); + Result.append(*Operand); + } + } + Result.push_back(')'); + return Result; + } + } + return makeUnexpected(ErrorCode::VerificationError, "generic constant value is invalid"); +} + +} // namespace + +Expected<std::string> GenericConstantData::toLLVMString() const { + return printValue(*this, true); +} + +} // namespace rcc diff --git a/backend/lib/IR/CMakeLists.txt b/backend/lib/IR/CMakeLists.txt new file mode 100644 index 0000000..a53cc96 --- /dev/null +++ b/backend/lib/IR/CMakeLists.txt @@ -0,0 +1,48 @@ +# SPDX-License-Identifier: GPL-3.0-or-later + +set(RCCBackendIRSources AsmWriter.cpp Constants.cpp DataLayout.cpp) +set( + RCCBackendIRHeaders + "${RCC_BACKEND_SOURCE_DIR}/include/rcc/IR/Constants.h" + "${RCC_BACKEND_SOURCE_DIR}/include/rcc/IR/DataLayout.h" +) + +add_library(RCCBackendIR ${RCCBackendIRSources}) +add_library(RCCBackend::IR ALIAS RCCBackendIR) + +rcc_backend_configure_target(RCCBackendIR) + +target_include_directories( + RCCBackendIR + PUBLIC + "$<BUILD_INTERFACE:${RCC_BACKEND_SOURCE_DIR}/include>" + "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>" +) +target_link_libraries(RCCBackendIR PUBLIC RCCBackend::Support) + +set_target_properties( + RCCBackendIR + PROPERTIES + EXPORT_NAME IR + OUTPUT_NAME RCCBackendIR +) + +install( + TARGETS RCCBackendIR + EXPORT RCCBackendTargets + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" +) + +rcc_backend_register_format_files( + "${CMAKE_CURRENT_SOURCE_DIR}/AsmWriter.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/Constants.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/DataLayout.cpp" + ${RCCBackendIRHeaders} +) +rcc_backend_register_tidy_files( + "${CMAKE_CURRENT_SOURCE_DIR}/AsmWriter.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/Constants.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/DataLayout.cpp" +) diff --git a/backend/lib/IR/Constants.cpp b/backend/lib/IR/Constants.cpp new file mode 100644 index 0000000..fd3a375 --- /dev/null +++ b/backend/lib/IR/Constants.cpp @@ -0,0 +1,870 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "rcc/IR/Constants.h" + +#include <algorithm> +#include <bit> +#include <cstddef> +#include <cstdint> +#include <limits> +#include <optional> +#include <utility> + +namespace rcc { + +const GenericConstantType &GenericConstantType::resolved() const noexcept { + constexpr std::size_t MaximumResolvedDepth = 1024; + const GenericConstantType *Result = this; + std::size_t Depth = 0; + while (Result->Kind == GenericConstantTypeKind::Named && Result->Elements.size() == 1 && + Depth++ <= MaximumResolvedDepth) { + Result = &Result->Elements.front(); + } + return *Result; +} + +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]] bool sameResolvedType(const GenericConstantType &Left, + const GenericConstantType &Right) noexcept { + return Left.resolved() == Right.resolved(); +} + +[[nodiscard]] bool isIntegerScalarType(const GenericConstantType &Type) noexcept { + const GenericConstantTypeKind Kind = Type.resolved().Kind; + return Kind == GenericConstantTypeKind::Integer || Kind == GenericConstantTypeKind::Byte; +} + +[[nodiscard]] std::optional<std::uint32_t> +integerScalarBitWidth(const GenericConstantType &Type) noexcept { + const GenericConstantType &Resolved = Type.resolved(); + 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 Type.resolved().Kind == GenericConstantTypeKind::Pointer; +} + +[[nodiscard]] bool isFloatingScalarType(const GenericConstantType &Type) noexcept { + return Type.resolved().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 = Type.resolved(); + 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 = Type.resolved(); + 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 = Type.resolved(); + if (Resolved.Kind == GenericConstantTypeKind::Pointer) + return Resolved.AddressSpace; + const GenericConstantType *Element = vectorElementType(Resolved); + if (Element == nullptr) + return std::nullopt; + const GenericConstantType &ResolvedElement = Element->resolved(); + 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 = Type.resolved(); + 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 = Source.resolved(); + const GenericConstantType &ResolvedDestination = Destination.resolved(); + 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 = Pointer.resolved(); + const GenericConstantType &ResolvedInteger = Integer.resolved(); + 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 = Type.resolved(); + 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 APInt &Left, const APInt &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 > APInt::MaxBitWidth) { + return makeUnexpected(ErrorCode::VerificationError, + "generic integer type has an invalid bit width"); + } + return RequireNoElements(); + case GenericConstantTypeKind::Byte: + return RequireNoElements(); + case GenericConstantTypeKind::Floating: + if (!getFloatSemanticsBitWidth(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 = Value.Type.resolved(); + + 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; + } + const auto KeyBitWidth = integerScalarBitWidth(Value.Elements[1].Type); + if (!isPointerScalarType(Value.Elements[0].Type) || + !sameResolvedType(Value.Type, Value.Elements[0].Type) || !KeyBitWidth || + KeyBitWidth.value() != 32) { + return makeUnexpected(ErrorCode::VerificationError, + "generic pointer-auth constant has invalid pointer or key operands"); + } + if (Value.Elements.size() >= 3) { + const auto DiscriminatorBitWidth = integerScalarBitWidth(Value.Elements[2].Type); + if (!DiscriminatorBitWidth || DiscriminatorBitWidth.value() != 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 > APInt::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 (!getFloatSemanticsBitWidth(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(APInt 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(APFloat 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/IR/DataLayout.cpp b/backend/lib/IR/DataLayout.cpp new file mode 100644 index 0000000..f1be171 --- /dev/null +++ b/backend/lib/IR/DataLayout.cpp @@ -0,0 +1,597 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "rcc/IR/DataLayout.h" + +#include <algorithm> +#include <bit> +#include <charconv> +#include <cstddef> +#include <cstdint> +#include <limits> +#include <ranges> +#include <string> +#include <string_view> +#include <utility> +#include <vector> + +namespace rcc { +namespace { + +constexpr std::uint64_t MaximumDataLayoutSize = (std::uint64_t{1} << 24U) - 1U; +constexpr std::uint64_t MaximumDataLayoutAlignment = (std::uint64_t{1} << 16U) - 1U; +constexpr std::size_t MaximumDataLayoutStringLength = std::size_t{1} << 24U; +constexpr std::size_t MaximumTypeLayoutDepth = 1024; + +[[nodiscard]] Expected<std::uint64_t> checkedMultiply(std::uint64_t Left, std::uint64_t Right, + std::string_view Description) { + if (Left != 0 && Right > std::numeric_limits<std::uint64_t>::max() / Left) + return makeUnexpected(ErrorCode::Unsupported, std::string(Description) + " is too large"); + return Left * Right; +} + +[[nodiscard]] Expected<std::uint64_t> checkedAdd(std::uint64_t Left, std::uint64_t Right, + std::string_view Description) { + if (Right > std::numeric_limits<std::uint64_t>::max() - Left) + return makeUnexpected(ErrorCode::Unsupported, std::string(Description) + " is too large"); + return Left + Right; +} + +[[nodiscard]] Expected<std::uint64_t> alignTo(std::uint64_t Value, std::uint64_t AlignmentValue) { + if (AlignmentValue == 0 || !std::has_single_bit(AlignmentValue)) + return makeUnexpected(ErrorCode::InvalidArgument, "LLVM type alignment is invalid"); + const std::uint64_t Mask = AlignmentValue - 1U; + if (Value > std::numeric_limits<std::uint64_t>::max() - Mask) + return makeUnexpected(ErrorCode::Unsupported, "LLVM type allocation size is too large"); + return (Value + Mask) & ~Mask; +} + +} // namespace + +Expected<std::uint64_t> DataLayout::parseUnsignedField(std::string_view Text, + std::string_view Description) { + std::uint64_t Value = 0; + const auto Parsed = std::from_chars(Text.data(), Text.data() + Text.size(), Value, 10); + if (Text.empty() || Parsed.ec != std::errc{} || Parsed.ptr != Text.data() + Text.size()) { + return makeUnexpected(ErrorCode::ParseError, + "LLVM data layout has an invalid " + std::string(Description)); + } + return Value; +} + +Expected<std::uint64_t> DataLayout::alignmentBytes(std::string_view Text, bool AllowZero, + std::string_view Description) { + auto Bits = parseUnsignedField(Text, Description); + if (!Bits) + return std::unexpected(Bits.error()); + if (*Bits == 0 && AllowZero) + return 1; + if (*Bits > MaximumDataLayoutAlignment || *Bits < 8 || (*Bits % 8U) != 0 || + !std::has_single_bit(*Bits)) { + return makeUnexpected(ErrorCode::ParseError, + "LLVM data layout " + std::string(Description) + + " must be a nonzero power-of-two byte alignment"); + } + return *Bits / 8U; +} + +std::vector<std::string_view> DataLayout::split(std::string_view Text, char Separator) { + std::vector<std::string_view> Parts; + while (true) { + const std::size_t Position = Text.find(Separator); + Parts.push_back(Text.substr(0, Position)); + if (Position == std::string_view::npos) + return Parts; + Text.remove_prefix(Position + 1U); + } +} + +void DataLayout::setPrimitiveSpec(PrimitiveSpec Entry) { + const auto Existing = + std::ranges::find_if(PrimitiveSpecs, [&Entry](const PrimitiveSpec &Candidate) { + return Candidate.Kind == Entry.Kind && Candidate.BitWidth == Entry.BitWidth; + }); + if (Existing == PrimitiveSpecs.end()) + PrimitiveSpecs.push_back(Entry); + else + *Existing = Entry; +} + +void DataLayout::setPointerSpec(PointerSpec Entry) { + const auto Existing = std::ranges::find_if(PointerSpecs, [&Entry](const PointerSpec &Candidate) { + return Candidate.AddressSpace == Entry.AddressSpace; + }); + if (Existing == PointerSpecs.end()) + PointerSpecs.push_back(Entry); + else + *Existing = Entry; +} + +Expected<DataLayout> DataLayout::parse(std::string_view LayoutString) { + DataLayout Result; + if (LayoutString.empty()) + return Result; + if (LayoutString.size() > MaximumDataLayoutStringLength) { + return makeUnexpected(ErrorCode::Unsupported, + "LLVM data layout exceeds the maximum string length"); + } + Result.Explicit = true; + Result.setPointerSpec({.AddressSpace = 0, + .SizeInBits = 64, + .Alignment = {.ABI = 8, .Preferred = 8}, + .AddressSpaceName = {}}); + for (const PrimitiveSpec Entry : { + PrimitiveSpec{'i', 1, {1, 1}}, + PrimitiveSpec{'i', 8, {1, 1}}, + PrimitiveSpec{'i', 16, {2, 2}}, + PrimitiveSpec{'i', 32, {4, 4}}, + PrimitiveSpec{'i', 64, {4, 8}}, + PrimitiveSpec{'f', 16, {2, 2}}, + PrimitiveSpec{'f', 32, {4, 4}}, + PrimitiveSpec{'f', 64, {8, 8}}, + PrimitiveSpec{'f', 128, {16, 16}}, + PrimitiveSpec{'v', 64, {8, 8}}, + PrimitiveSpec{'v', 128, {16, 16}}, + }) { + Result.setPrimitiveSpec(Entry); + } + + for (const std::string_view Component : split(LayoutString, '-')) { + if (Component.empty()) + return makeUnexpected(ErrorCode::ParseError, "LLVM data layout contains an empty component"); + if (Component.front() == 'p') { + const std::size_t Colon = Component.find(':'); + if (Colon == std::string_view::npos) + return makeUnexpected(ErrorCode::ParseError, + "LLVM pointer data layout component has no size"); + std::string_view AddressSpaceText = Component.substr(1, Colon - 1U); + bool HasExternalState = false; + bool HasUnstableRepresentation = false; + bool HasExplicitNullValue = false; + while (!AddressSpaceText.empty()) { + const char Flag = AddressSpaceText.front(); + if (Flag == 'e') { + HasExternalState = true; + } else if (Flag == 'u') { + HasUnstableRepresentation = true; + } else if (Flag == 'z' || Flag == 'o') { + if (HasExplicitNullValue) { + return makeUnexpected(ErrorCode::ParseError, + "LLVM pointer data layout has conflicting null-pointer flags"); + } + HasExplicitNullValue = true; + } else { + break; + } + AddressSpaceText.remove_prefix(1); + } + + std::uint64_t AddressSpace = 0; + std::string AddressSpaceName; + if (!AddressSpaceText.empty()) { + std::size_t NumberLength = 0; + while (NumberLength < AddressSpaceText.size() && AddressSpaceText[NumberLength] >= '0' && + AddressSpaceText[NumberLength] <= '9') { + ++NumberLength; + } + if (NumberLength != 0) { + auto Parsed = + parseUnsignedField(AddressSpaceText.substr(0, NumberLength), "pointer address space"); + if (!Parsed) + return std::unexpected(Parsed.error()); + AddressSpace = *Parsed; + AddressSpaceText.remove_prefix(NumberLength); + } + if (!AddressSpaceText.empty()) { + if (AddressSpaceText.size() <= 2 || AddressSpaceText.front() != '(' || + AddressSpaceText.back() != ')') { + return makeUnexpected(ErrorCode::ParseError, + "LLVM pointer address space or name is invalid"); + } + AddressSpaceName = std::string(AddressSpaceText.substr(1, AddressSpaceText.size() - 2)); + if (AddressSpaceName.size() == 1 && + (AddressSpaceName.front() == 'P' || AddressSpaceName.front() == 'G' || + AddressSpaceName.front() == 'A')) { + return makeUnexpected(ErrorCode::ParseError, + "LLVM pointer address space uses a reserved name"); + } + } + } + if (AddressSpace == 0 && (HasExternalState || HasUnstableRepresentation)) { + return makeUnexpected(ErrorCode::ParseError, + "LLVM address space zero cannot be unstable or external"); + } + if (!AddressSpaceName.empty() && + std::ranges::any_of(Result.PointerSpecs, [&AddressSpaceName](const PointerSpec &Entry) { + return Entry.AddressSpaceName == AddressSpaceName; + })) { + return makeUnexpected(ErrorCode::ParseError, + "LLVM pointer address-space name is duplicated"); + } + const auto Fields = split(Component.substr(Colon + 1U), ':'); + if (Fields.size() < 2 || Fields.size() > 4 || AddressSpace > MaximumDataLayoutSize) { + return makeUnexpected(ErrorCode::ParseError, + "LLVM pointer data layout component has invalid fields"); + } + auto Size = parseUnsignedField(Fields[0], "pointer size"); + auto ABI = alignmentBytes(Fields[1], false, "pointer ABI alignment"); + auto Preferred = Fields.size() >= 3 + ? alignmentBytes(Fields[2], false, "pointer preferred alignment") + : ABI; + if (!Size || !ABI || !Preferred || *Size == 0 || *Size > MaximumDataLayoutSize || + *Preferred < *ABI) { + return makeUnexpected(ErrorCode::ParseError, + "LLVM pointer data layout component is invalid"); + } + if (Fields.size() == 4) { + auto ParsedIndexSize = parseUnsignedField(Fields[3], "pointer index size"); + if (!ParsedIndexSize || *ParsedIndexSize == 0 || *ParsedIndexSize > MaximumDataLayoutSize || + *ParsedIndexSize > *Size) { + return makeUnexpected(ErrorCode::ParseError, "LLVM pointer index size is invalid"); + } + } + Result.setPointerSpec({.AddressSpace = static_cast<std::uint32_t>(AddressSpace), + .SizeInBits = static_cast<std::uint32_t>(*Size), + .Alignment = {.ABI = *ABI, .Preferred = *Preferred}, + .AddressSpaceName = std::move(AddressSpaceName)}); + continue; + } + + if (Component == "ve") + continue; + + if (Component.front() == 'i' || Component.front() == 'f' || Component.front() == 'v') { + const std::size_t Colon = Component.find(':'); + if (Colon == std::string_view::npos) + return makeUnexpected(ErrorCode::ParseError, + "LLVM scalar data layout component has no alignment"); + auto Width = parseUnsignedField(Component.substr(1, Colon - 1U), "type alignment bit width"); + const auto Fields = split(Component.substr(Colon + 1U), ':'); + if (!Width || *Width == 0 || *Width > MaximumDataLayoutSize || Fields.empty() || + Fields.size() > 2) { + return makeUnexpected(ErrorCode::ParseError, + "LLVM scalar data layout component has invalid fields"); + } + auto ABI = alignmentBytes(Fields[0], false, "type ABI alignment"); + auto Preferred = + Fields.size() == 2 ? alignmentBytes(Fields[1], false, "type preferred alignment") : ABI; + if (!ABI || !Preferred || *Preferred < *ABI) + return makeUnexpected(ErrorCode::ParseError, + "LLVM scalar data layout alignment is invalid"); + Result.setPrimitiveSpec( + {Component.front(), static_cast<std::uint32_t>(*Width), {*ABI, *Preferred}}); + continue; + } + + if (Component.starts_with('a')) { + const auto Fields = split(Component.substr(1), ':'); + if (Fields.size() < 2 || Fields.size() > 3 || (!Fields[0].empty() && Fields[0] != "0")) { + return makeUnexpected(ErrorCode::ParseError, + "LLVM aggregate data layout component has invalid fields"); + } + auto ABI = alignmentBytes(Fields[1], true, "aggregate ABI alignment"); + auto Preferred = Fields.size() == 3 + ? alignmentBytes(Fields[2], false, "aggregate preferred alignment") + : ABI; + if (!ABI || !Preferred || *Preferred < *ABI) + return makeUnexpected(ErrorCode::ParseError, + "LLVM aggregate data layout alignment is invalid"); + Result.AggregateAlignment = {.ABI = *ABI, .Preferred = *Preferred}; + continue; + } + + if (Component == "e" || Component == "E" || Component == "s" || Component == "ve") + continue; + + if (Component.starts_with("ni:")) { + const auto AddressSpaces = split(Component.substr(3), ':'); + if (AddressSpaces.empty()) { + return makeUnexpected(ErrorCode::ParseError, + "LLVM non-integral address-space component is empty"); + } + for (const std::string_view AddressSpace : AddressSpaces) { + auto Parsed = parseUnsignedField(AddressSpace, "non-integral address space"); + if (!Parsed || *Parsed == 0 || *Parsed > MaximumDataLayoutSize) { + return makeUnexpected(ErrorCode::ParseError, + "LLVM non-integral address space is invalid"); + } + } + continue; + } + + if (Component.starts_with("m:")) { + const std::string_view Mode = Component.substr(2); + if (Mode.size() != 1 || (Mode.front() != 'e' && Mode.front() != 'l' && Mode.front() != 'o' && + Mode.front() != 'm' && Mode.front() != 'w' && Mode.front() != 'x' && + Mode.front() != 'a')) { + return makeUnexpected(ErrorCode::ParseError, "LLVM mangling mode is invalid"); + } + continue; + } + + if (Component.starts_with('n')) { + const auto Widths = split(Component.substr(1), ':'); + if (Widths.empty()) { + return makeUnexpected(ErrorCode::ParseError, + "LLVM native integer-width component is empty"); + } + for (const std::string_view Width : Widths) { + auto Parsed = parseUnsignedField(Width, "native integer bit width"); + if (!Parsed || *Parsed == 0 || *Parsed > MaximumDataLayoutSize) { + return makeUnexpected(ErrorCode::ParseError, "LLVM native integer bit width is invalid"); + } + } + continue; + } + + if (Component.starts_with('S')) { + auto AlignmentValue = alignmentBytes(Component.substr(1), false, "stack natural alignment"); + if (!AlignmentValue) + return std::unexpected(AlignmentValue.error()); + continue; + } + + if (Component.starts_with('F')) { + const std::string_view Rest = Component.substr(1); + if (Rest.size() < 2 || (Rest.front() != 'i' && Rest.front() != 'n')) { + return makeUnexpected(ErrorCode::ParseError, + "LLVM function-pointer alignment component is invalid"); + } + auto AlignmentValue = alignmentBytes(Rest.substr(1), false, "function-pointer ABI alignment"); + if (!AlignmentValue) + return std::unexpected(AlignmentValue.error()); + continue; + } + + if (Component.starts_with('P') || Component.starts_with('G') || Component.starts_with('A')) { + auto AddressSpace = parseUnsignedField(Component.substr(1), "default address space"); + if (!AddressSpace || *AddressSpace > MaximumDataLayoutSize) { + return makeUnexpected(ErrorCode::ParseError, "LLVM default address space is invalid"); + } + continue; + } + + return makeUnexpected(ErrorCode::ParseError, "unsupported LLVM data layout component '" + + std::string(Component) + "'"); + } + return Result; +} + +DataLayout::AlignmentSpec DataLayout::naturalAlignment(std::uint64_t StoreSize) const noexcept { + if (StoreSize == 0) + return {1, 1}; + constexpr std::uint64_t MaximumPowerOfTwo = std::uint64_t{1} << 63U; + const std::uint64_t LimitedSize = + std::min<std::uint64_t>(StoreSize, Explicit ? MaximumPowerOfTwo : 16U); + const std::uint64_t Alignment = std::bit_ceil(LimitedSize); + return {Alignment, Alignment}; +} + +DataLayout::AlignmentSpec DataLayout::scalarAlignment(char Kind, std::uint32_t BitWidth, + std::uint64_t StoreSize) const noexcept { + if (!Explicit) + return naturalAlignment(StoreSize); + const PrimitiveSpec *Selected = nullptr; + for (const PrimitiveSpec &Entry : PrimitiveSpecs) { + if (Entry.Kind != Kind) + continue; + if (Kind == 'i') { + const bool BetterMatch = + Selected == nullptr || + (Entry.BitWidth >= BitWidth && Entry.BitWidth < Selected->BitWidth) || + (Selected->BitWidth < BitWidth && Entry.BitWidth > Selected->BitWidth); + if (BetterMatch) + Selected = &Entry; + } else if (Entry.BitWidth == BitWidth) { + Selected = &Entry; + break; + } + } + return Selected == nullptr ? naturalAlignment(StoreSize) : Selected->Alignment; +} + +DataLayout::AlignmentSpec DataLayout::integerAlignment(std::uint32_t BitWidth, + std::uint64_t StoreSize) const noexcept { + return scalarAlignment('i', BitWidth, StoreSize); +} + +DataLayout::AlignmentSpec DataLayout::floatingAlignment(std::uint32_t BitWidth, + std::uint64_t StoreSize) const noexcept { + return scalarAlignment('f', BitWidth, StoreSize); +} + +DataLayout::AlignmentSpec DataLayout::vectorAlignment(std::uint32_t BitWidth, + std::uint64_t StoreSize) const noexcept { + return scalarAlignment('v', BitWidth, StoreSize); +} + +Expected<DataLayout::PointerSpec> DataLayout::getPointerSpec(std::uint32_t AddressSpace) const { + if (!Explicit) { + return makeUnexpected(ErrorCode::InvalidArgument, + "pointer size is required to lay out pointer constants"); + } + auto Entry = std::ranges::find_if(PointerSpecs, [AddressSpace](const PointerSpec &Candidate) { + return Candidate.AddressSpace == AddressSpace; + }); + if (Entry == PointerSpecs.end()) { + Entry = std::ranges::find_if( + PointerSpecs, [](const PointerSpec &Candidate) { return Candidate.AddressSpace == 0; }); + } + if (Entry == PointerSpecs.end()) + return makeUnexpected(ErrorCode::InvalidArgument, "LLVM data layout has no pointer layout"); + return *Entry; +} + +Expected<DataLayout::TypeLayout> DataLayout::getTypeLayout(const GenericConstantType &Type, + std::vector<std::string> &NamedTypeStack, + std::size_t Depth) const { + if (Depth > MaximumTypeLayoutDepth) + return makeUnexpected(ErrorCode::Unsupported, "LLVM type exceeds the layout depth limit"); + auto Verification = Type.verify(); + if (!Verification) + return std::unexpected(Verification.error()); + + const auto ScalarLayout = [this](std::uint64_t StoreSize, + AlignmentSpec TypeAlignment) -> Expected<TypeLayout> { + std::uint64_t AllocationSize = StoreSize; + if (Explicit) { + auto Aligned = alignTo(StoreSize, TypeAlignment.ABI); + if (!Aligned) + return std::unexpected(Aligned.error()); + AllocationSize = *Aligned; + } + return TypeLayout{StoreSize, AllocationSize, TypeAlignment.ABI, TypeAlignment.Preferred}; + }; + + switch (Type.Kind) { + case GenericConstantTypeKind::Integer: + case GenericConstantTypeKind::Byte: { + const std::uint32_t Bits = Type.Kind == GenericConstantTypeKind::Byte ? 8U : Type.BitWidth; + const std::uint64_t StoreSize = (static_cast<std::uint64_t>(Bits) + 7U) / 8U; + return ScalarLayout(StoreSize, integerAlignment(Bits, StoreSize)); + } + case GenericConstantTypeKind::Floating: { + const std::uint32_t Bits = getFloatSemanticsBitWidth(Type.Semantics).value_or(0); + const std::uint64_t StoreSize = (static_cast<std::uint64_t>(Bits) + 7U) / 8U; + return ScalarLayout(StoreSize, floatingAlignment(Bits, StoreSize)); + } + case GenericConstantTypeKind::Pointer: { + auto Pointer = getPointerSpec(Type.AddressSpace); + if (!Pointer) + return std::unexpected(Pointer.error()); + const std::uint64_t StoreSize = (static_cast<std::uint64_t>(Pointer->SizeInBits) + 7U) / 8U; + return ScalarLayout(StoreSize, Pointer->Alignment); + } + case GenericConstantTypeKind::Array: { + auto Element = getTypeLayout(Type.Elements.front(), NamedTypeStack, Depth + 1); + if (!Element) + return std::unexpected(Element.error()); + auto Stride = alignTo(Element->AllocationSize, Element->ABIAlignment); + if (!Stride) + return std::unexpected(Stride.error()); + auto Size = checkedMultiply(*Stride, Type.ElementCount, "LLVM array allocation size"); + if (!Size) + return std::unexpected(Size.error()); + return TypeLayout{*Size, *Size, Element->ABIAlignment, Element->PreferredAlignment}; + } + case GenericConstantTypeKind::Vector: { + if (Type.Scalable) + return makeUnexpected(ErrorCode::Unsupported, + "scalable vector has no fixed constant-pool size"); + const GenericConstantType &ElementType = Type.Elements.front().resolved(); + std::uint64_t ElementBits = 0; + if (ElementType.Kind == GenericConstantTypeKind::Integer) + ElementBits = ElementType.BitWidth; + else if (ElementType.Kind == GenericConstantTypeKind::Byte) + ElementBits = 8; + else if (ElementType.Kind == GenericConstantTypeKind::Floating) + ElementBits = getFloatSemanticsBitWidth(ElementType.Semantics).value_or(0); + else if (ElementType.Kind == GenericConstantTypeKind::Pointer) { + auto Pointer = getPointerSpec(ElementType.AddressSpace); + if (!Pointer) + return std::unexpected(Pointer.error()); + ElementBits = Pointer->SizeInBits; + } else { + return makeUnexpected(ErrorCode::Unsupported, + "LLVM vector element type has no fixed bit size"); + } + auto TotalBits = checkedMultiply(ElementBits, Type.ElementCount, "LLVM vector bit size"); + if (!TotalBits || *TotalBits > std::numeric_limits<std::uint64_t>::max() - 7U) + return makeUnexpected(ErrorCode::Unsupported, "LLVM vector bit size is too large"); + const std::uint64_t StoreSize = (*TotalBits + 7U) / 8U; + return ScalarLayout(StoreSize, + vectorAlignment(static_cast<std::uint32_t>(std::min<std::uint64_t>( + *TotalBits, std::numeric_limits<std::uint32_t>::max())), + StoreSize)); + } + case GenericConstantTypeKind::Struct: { + if (Type.Packed) { + std::uint64_t Size = 0; + for (const GenericConstantType &MemberType : Type.Elements) { + auto Member = getTypeLayout(MemberType, NamedTypeStack, Depth + 1); + if (!Member) + return std::unexpected(Member.error()); + auto Next = checkedAdd(Size, Member->AllocationSize, "LLVM packed struct allocation size"); + if (!Next) + return std::unexpected(Next.error()); + Size = *Next; + } + return TypeLayout{Size, Size, 1, std::max<std::uint64_t>(AggregateAlignment.Preferred, 1)}; + } + std::uint64_t Offset = 0; + std::uint64_t ABIAlignment = std::max<std::uint64_t>(AggregateAlignment.ABI, 1); + for (const GenericConstantType &MemberType : Type.Elements) { + auto Member = getTypeLayout(MemberType, NamedTypeStack, Depth + 1); + if (!Member) + return std::unexpected(Member.error()); + auto AlignedOffset = alignTo(Offset, Member->ABIAlignment); + if (!AlignedOffset) + return std::unexpected(AlignedOffset.error()); + auto Next = checkedAdd(*AlignedOffset, Member->AllocationSize, "LLVM struct allocation size"); + if (!Next) + return std::unexpected(Next.error()); + Offset = *Next; + ABIAlignment = std::max(ABIAlignment, Member->ABIAlignment); + } + auto Size = alignTo(Offset, ABIAlignment); + if (!Size) + return std::unexpected(Size.error()); + return TypeLayout{*Size, *Size, ABIAlignment, + std::max(AggregateAlignment.Preferred, ABIAlignment)}; + } + case GenericConstantTypeKind::Named: { + if (std::ranges::find(NamedTypeStack, Type.Name) != NamedTypeStack.end()) { + return makeUnexpected(ErrorCode::Unsupported, + "named LLVM type definition is recursively unsized"); + } + NamedTypeStack.push_back(Type.Name); + Expected<TypeLayout> Result = + makeUnexpected(ErrorCode::Unsupported, "named LLVM type has no embedded type definition"); + if (Type.Elements.size() == 1) + Result = getTypeLayout(Type.Elements.front(), NamedTypeStack, Depth + 1); + NamedTypeStack.pop_back(); + return Result; + } + case GenericConstantTypeKind::Invalid: + case GenericConstantTypeKind::Void: + case GenericConstantTypeKind::Label: + case GenericConstantTypeKind::Token: + case GenericConstantTypeKind::Metadata: + case GenericConstantTypeKind::Function: + return makeUnexpected(ErrorCode::Unsupported, + "LLVM type is not sized for a machine constant pool"); + } + return makeUnexpected(ErrorCode::InternalError, "LLVM constant type layout is unhandled"); +} + +Expected<std::uint64_t> DataLayout::getTypeAllocSize(const GenericConstantType &Type) const { + std::vector<std::string> NamedTypeStack; + auto Layout = getTypeLayout(Type, NamedTypeStack, 0); + if (!Layout) + return std::unexpected(Layout.error()); + return Layout->AllocationSize; +} + +Expected<std::uint64_t> DataLayout::getABITypeAlign(const GenericConstantType &Type) const { + std::vector<std::string> NamedTypeStack; + auto Layout = getTypeLayout(Type, NamedTypeStack, 0); + if (!Layout) + return std::unexpected(Layout.error()); + return Layout->ABIAlignment; +} + +Expected<std::uint64_t> DataLayout::getPrefTypeAlign(const GenericConstantType &Type) const { + std::vector<std::string> NamedTypeStack; + auto Layout = getTypeLayout(Type, NamedTypeStack, 0); + if (!Layout) + return std::unexpected(Layout.error()); + return Layout->PreferredAlignment; +} + +Expected<std::uint32_t> DataLayout::getPointerSizeInBits(std::uint32_t AddressSpace) const { + auto Pointer = getPointerSpec(AddressSpace); + if (!Pointer) + return std::unexpected(Pointer.error()); + return Pointer->SizeInBits; +} + +} // namespace rcc |
