aboutsummaryrefslogtreecommitdiff
path: root/backend/lib/IR
diff options
context:
space:
mode:
Diffstat (limited to 'backend/lib/IR')
-rw-r--r--backend/lib/IR/AsmWriter.cpp393
-rw-r--r--backend/lib/IR/CMakeLists.txt48
-rw-r--r--backend/lib/IR/Constants.cpp870
-rw-r--r--backend/lib/IR/DataLayout.cpp597
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