From fde3e045d4dcc5c404cb4216389e18b0e720b8e0 Mon Sep 17 00:00:00 2001 From: Richard-Qin-X Date: Thu, 13 Aug 2026 12:55:12 +0800 Subject: [IR] Split LL parsing, assembly writing, and data layout --- backend/include/rcc/AsmParser/LLParser.h | 36 + backend/include/rcc/CodeGen/GenericConstant.h | 176 -- backend/include/rcc/CodeGen/GlobalObject.h | 2 +- backend/include/rcc/CodeGen/MIRYamlMapping.h | 4 +- backend/include/rcc/CodeGen/MachineConstantPool.h | 2 +- backend/include/rcc/CodeGen/MachineMemOperand.h | 2 +- backend/include/rcc/IR/Constants.h | 177 ++ backend/include/rcc/IR/DataLayout.h | 82 + backend/include/rcc/MIR/LLVMConstantSyntax.h | 52 - backend/lib/AsmParser/CMakeLists.txt | 41 + backend/lib/AsmParser/LLParser.cpp | 967 ++++++++++ backend/lib/AsmPrinter/AsmPrinter.cpp | 2 +- backend/lib/CMakeLists.txt | 2 + backend/lib/CodeGen/CMakeLists.txt | 6 +- backend/lib/CodeGen/GenericConstant.cpp | 868 --------- backend/lib/CodeGen/MIRParser/MIParser.cpp | 2 +- backend/lib/CodeGen/MIRParser/MIRParser.cpp | 17 +- backend/lib/CodeGen/MIRPrinter.cpp | 19 +- backend/lib/CodeGen/MIRYamlMapping.cpp | 31 +- backend/lib/IR/AsmWriter.cpp | 393 ++++ backend/lib/IR/CMakeLists.txt | 48 + backend/lib/IR/Constants.cpp | 870 +++++++++ backend/lib/IR/DataLayout.cpp | 597 ++++++ backend/lib/MIR/CMakeLists.txt | 12 +- backend/lib/MIR/LLVMConstantSyntax.cpp | 2025 -------------------- backend/lib/Target/RISCV/RISCVMCTargetLowering.cpp | 218 +-- backend/unittests/AsmParser/CMakeLists.txt | 15 + backend/unittests/AsmParser/LLParserTest.cpp | 533 ++++++ backend/unittests/CMakeLists.txt | 1 + backend/unittests/MIR/CMakeLists.txt | 11 - backend/unittests/MIR/LLVMConstantSyntaxTest.cpp | 515 ----- 31 files changed, 3858 insertions(+), 3868 deletions(-) create mode 100644 backend/include/rcc/AsmParser/LLParser.h delete mode 100644 backend/include/rcc/CodeGen/GenericConstant.h create mode 100644 backend/include/rcc/IR/Constants.h create mode 100644 backend/include/rcc/IR/DataLayout.h delete mode 100644 backend/include/rcc/MIR/LLVMConstantSyntax.h create mode 100644 backend/lib/AsmParser/CMakeLists.txt create mode 100644 backend/lib/AsmParser/LLParser.cpp delete mode 100644 backend/lib/CodeGen/GenericConstant.cpp create mode 100644 backend/lib/IR/AsmWriter.cpp create mode 100644 backend/lib/IR/CMakeLists.txt create mode 100644 backend/lib/IR/Constants.cpp create mode 100644 backend/lib/IR/DataLayout.cpp delete mode 100644 backend/lib/MIR/LLVMConstantSyntax.cpp create mode 100644 backend/unittests/AsmParser/CMakeLists.txt create mode 100644 backend/unittests/AsmParser/LLParserTest.cpp delete mode 100644 backend/unittests/MIR/LLVMConstantSyntaxTest.cpp (limited to 'backend') diff --git a/backend/include/rcc/AsmParser/LLParser.h b/backend/include/rcc/AsmParser/LLParser.h new file mode 100644 index 0000000..e15a006 --- /dev/null +++ b/backend/include/rcc/AsmParser/LLParser.h @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_ASMPARSER_LLPARSER_H +#define RCC_ASMPARSER_LLPARSER_H + +#include "rcc/IR/Constants.h" +#include "rcc/Support/Error.h" + +#include +#include +#include + +namespace rcc { + +struct LLNamedTypeDefinition final { + std::string_view Name; + std::string_view Definition; + bool NumericName = false; +}; + +struct LLParserOptions final { + std::span NamedTypes; + std::size_t MaximumDepth = 1024; + std::size_t MaximumNodes = std::size_t{1} << 24U; + std::size_t MaximumTokenLength = std::size_t{1} << 24U; +}; + +[[nodiscard]] Expected parseLLVMConstant(std::string_view Text, + LLParserOptions Options = {}); + +[[nodiscard]] Expected parseLLVMType(std::string_view Text, + LLParserOptions Options = {}); + +} // namespace rcc + +#endif // RCC_ASMPARSER_LLPARSER_H diff --git a/backend/include/rcc/CodeGen/GenericConstant.h b/backend/include/rcc/CodeGen/GenericConstant.h deleted file mode 100644 index 4219c24..0000000 --- a/backend/include/rcc/CodeGen/GenericConstant.h +++ /dev/null @@ -1,176 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#ifndef RCC_CODEGEN_GENERICCONSTANT_H -#define RCC_CODEGEN_GENERICCONSTANT_H - -#include "rcc/ADT/APFloat.h" -#include "rcc/ADT/APInt.h" -#include "rcc/CodeGen/LowLevelType.h" -#include "rcc/Support/Error.h" - -#include -#include -#include -#include -#include - -namespace rcc { - -enum class GenericConstantTypeKind : std::uint8_t { - Invalid, - Void, - Label, - Token, - Metadata, - Integer, - Byte, - Floating, - Pointer, - Array, - Vector, - Struct, - Function, - Named, -}; - -struct GenericConstantType final { - GenericConstantTypeKind Kind = GenericConstantTypeKind::Invalid; - std::uint32_t BitWidth = 0; - FloatSemantics Semantics = FloatSemantics::IEEEHalf; - std::uint32_t AddressSpace = 0; - std::uint64_t ElementCount = 0; - bool Scalable = false; - bool Packed = false; - bool VarArg = false; - bool NumericName = false; - std::string Name; - std::vector Elements; - - [[nodiscard]] static Expected integer(std::uint32_t BitWidth); - [[nodiscard]] static GenericConstantType primitive(GenericConstantTypeKind Kind) noexcept; - [[nodiscard]] static GenericConstantType byte() noexcept; - [[nodiscard]] static Expected floating(FloatSemantics Semantics); - [[nodiscard]] static GenericConstantType pointer(std::uint32_t AddressSpace = 0) noexcept; - [[nodiscard]] static Expected array(std::uint64_t ElementCount, - GenericConstantType Element); - [[nodiscard]] static Expected - vector(std::uint64_t ElementCount, GenericConstantType Element, bool Scalable = false); - [[nodiscard]] static Expected - structure(std::vector Elements, bool Packed = false); - [[nodiscard]] static Expected - function(GenericConstantType Return, std::vector Parameters, - bool VarArg = false); - [[nodiscard]] static Expected named(std::string Name, - bool NumericName = false); - - [[nodiscard]] Expected verify() const; - - friend bool operator==(const GenericConstantType &Left, - const GenericConstantType &Right) = default; -}; - -enum class GenericConstantKind : std::uint8_t { - Invalid, - Integer, - Floating, - NullPointer, - ZeroInitializer, - Undef, - Poison, - Aggregate, - ByteString, - GlobalAddress, - BlockAddress, - DSOLocalEquivalent, - NoCFI, - PointerAuth, - Expression, -}; - -enum class GenericConstantExpressionOpcode : std::uint8_t { - Invalid, - Trunc, - BitCast, - AddressSpaceCast, - IntToPtr, - PtrToAddr, - PtrToInt, - Add, - Sub, - Xor, - GetElementPtr, - ShuffleVector, - InsertElement, - ExtractElement, - Splat, -}; - -enum class GenericConstantExpressionFlag : std::uint8_t { - NoUnsignedWrap, - NoSignedWrap, - InBounds, - NoUnsignedSignedWrap, - GEPNoUnsignedWrap, - Count, -}; - -class GenericConstantExpressionFlags final { -public: - constexpr GenericConstantExpressionFlags() noexcept = default; - - [[nodiscard]] constexpr bool has(GenericConstantExpressionFlag Flag) const noexcept { - const auto Index = static_cast(Flag); - if (Index >= static_cast(GenericConstantExpressionFlag::Count)) - return false; - return (Mask & (std::uint8_t{1} << Index)) != 0; - } - - constexpr void set(GenericConstantExpressionFlag Flag) noexcept { - const auto Index = static_cast(Flag); - if (Index < static_cast(GenericConstantExpressionFlag::Count)) - Mask |= std::uint8_t{1} << Index; - } - - [[nodiscard]] constexpr std::uint8_t rawValue() const noexcept { - return Mask; - } - - friend constexpr bool operator==(GenericConstantExpressionFlags Left, - GenericConstantExpressionFlags Right) noexcept = default; - -private: - std::uint8_t Mask = 0; -}; - -struct GenericConstantData final { - GenericConstantType Type; - GenericConstantKind Kind = GenericConstantKind::Invalid; - std::optional IntegerValue; - std::optional FloatingValue; - std::vector ByteValue; - std::vector Elements; - std::string Symbol; - std::string Block; - bool NumericSymbol = false; - bool NumericBlock = false; - GenericConstantExpressionOpcode ExpressionOpcode = GenericConstantExpressionOpcode::Invalid; - GenericConstantExpressionFlags ExpressionFlags; - std::optional SourceElementType; - std::optional InRangeStart; - std::optional InRangeEnd; - - [[nodiscard]] static Expected integer(APInt Value); - [[nodiscard]] static Expected floating(APFloat Value); - [[nodiscard]] static Expected byteString(GenericConstantType Type, - std::vector Bytes); - - [[nodiscard]] Expected verify() const; - [[nodiscard]] bool referencesSymbol() const noexcept; - - friend bool operator==(const GenericConstantData &Left, - const GenericConstantData &Right) = default; -}; - -} // namespace rcc - -#endif // RCC_CODEGEN_GENERICCONSTANT_H diff --git a/backend/include/rcc/CodeGen/GlobalObject.h b/backend/include/rcc/CodeGen/GlobalObject.h index 798dcd8..1ddc34a 100644 --- a/backend/include/rcc/CodeGen/GlobalObject.h +++ b/backend/include/rcc/CodeGen/GlobalObject.h @@ -3,9 +3,9 @@ #ifndef RCC_CODEGEN_GLOBALOBJECT_H #define RCC_CODEGEN_GLOBALOBJECT_H -#include "rcc/CodeGen/GenericConstant.h" #include "rcc/CodeGen/MachineIRTypes.h" #include "rcc/CodeGen/TargetConstant.h" +#include "rcc/IR/Constants.h" #include "rcc/Support/Error.h" #include diff --git a/backend/include/rcc/CodeGen/MIRYamlMapping.h b/backend/include/rcc/CodeGen/MIRYamlMapping.h index 82880ca..c25559b 100644 --- a/backend/include/rcc/CodeGen/MIRYamlMapping.h +++ b/backend/include/rcc/CodeGen/MIRYamlMapping.h @@ -9,7 +9,7 @@ #include "rcc/CodeGen/MachineJumpTableInfo.h" #include "rcc/CodeGen/MachineMetadata.h" #include "rcc/CodeGen/TargetMIRTextInfo.h" -#include "rcc/MIR/LLVMConstantSyntax.h" +#include "rcc/IR/DataLayout.h" #include "rcc/Support/Error.h" #include @@ -106,7 +106,7 @@ private: [[nodiscard]] bool convertTargetNode(const yaml::Node &Source, MIRTargetNode &Destination); [[nodiscard]] bool validateDocumentReferences(const MachineFunction &Function); [[nodiscard]] std::string_view effectiveDataLayout() const noexcept; - [[nodiscard]] LLVMConstantSyntaxOptions pointerLayoutOptions() const noexcept; + [[nodiscard]] Expected dataLayout() const; [[nodiscard]] MachineBasicBlock *blockAt(MachineFunction &Function, std::uint32_t Number) const noexcept; diff --git a/backend/include/rcc/CodeGen/MachineConstantPool.h b/backend/include/rcc/CodeGen/MachineConstantPool.h index a0c269b..104e559 100644 --- a/backend/include/rcc/CodeGen/MachineConstantPool.h +++ b/backend/include/rcc/CodeGen/MachineConstantPool.h @@ -3,10 +3,10 @@ #ifndef RCC_CODEGEN_MACHINECONSTANTPOOL_H #define RCC_CODEGEN_MACHINECONSTANTPOOL_H -#include "rcc/CodeGen/GenericConstant.h" #include "rcc/CodeGen/MachineIRTypes.h" #include "rcc/CodeGen/MachineMemOperand.h" #include "rcc/CodeGen/TargetConstant.h" +#include "rcc/IR/Constants.h" #include "rcc/Support/Error.h" #include diff --git a/backend/include/rcc/CodeGen/MachineMemOperand.h b/backend/include/rcc/CodeGen/MachineMemOperand.h index c5c1357..d236fd3 100644 --- a/backend/include/rcc/CodeGen/MachineMemOperand.h +++ b/backend/include/rcc/CodeGen/MachineMemOperand.h @@ -3,10 +3,10 @@ #ifndef RCC_CODEGEN_MACHINEMEMOPERAND_H #define RCC_CODEGEN_MACHINEMEMOPERAND_H -#include "rcc/CodeGen/GenericConstant.h" #include "rcc/CodeGen/LowLevelType.h" #include "rcc/CodeGen/MachineIRTypes.h" #include "rcc/CodeGen/MachineMetadata.h" +#include "rcc/IR/Constants.h" #include #include diff --git a/backend/include/rcc/IR/Constants.h b/backend/include/rcc/IR/Constants.h new file mode 100644 index 0000000..9f894f2 --- /dev/null +++ b/backend/include/rcc/IR/Constants.h @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_IR_CONSTANTS_H +#define RCC_IR_CONSTANTS_H + +#include "rcc/ADT/APFloat.h" +#include "rcc/ADT/APInt.h" +#include "rcc/Support/Error.h" + +#include +#include +#include +#include +#include + +namespace rcc { + +enum class GenericConstantTypeKind : std::uint8_t { + Invalid, + Void, + Label, + Token, + Metadata, + Integer, + Byte, + Floating, + Pointer, + Array, + Vector, + Struct, + Function, + Named, +}; + +struct GenericConstantType final { + GenericConstantTypeKind Kind = GenericConstantTypeKind::Invalid; + std::uint32_t BitWidth = 0; + FloatSemantics Semantics = FloatSemantics::IEEEHalf; + std::uint32_t AddressSpace = 0; + std::uint64_t ElementCount = 0; + bool Scalable = false; + bool Packed = false; + bool VarArg = false; + bool NumericName = false; + std::string Name; + std::vector Elements; + + [[nodiscard]] static Expected integer(std::uint32_t BitWidth); + [[nodiscard]] static GenericConstantType primitive(GenericConstantTypeKind Kind) noexcept; + [[nodiscard]] static GenericConstantType byte() noexcept; + [[nodiscard]] static Expected floating(FloatSemantics Semantics); + [[nodiscard]] static GenericConstantType pointer(std::uint32_t AddressSpace = 0) noexcept; + [[nodiscard]] static Expected array(std::uint64_t ElementCount, + GenericConstantType Element); + [[nodiscard]] static Expected + vector(std::uint64_t ElementCount, GenericConstantType Element, bool Scalable = false); + [[nodiscard]] static Expected + structure(std::vector Elements, bool Packed = false); + [[nodiscard]] static Expected + function(GenericConstantType Return, std::vector Parameters, + bool VarArg = false); + [[nodiscard]] static Expected named(std::string Name, + bool NumericName = false); + + [[nodiscard]] const GenericConstantType &resolved() const noexcept; + [[nodiscard]] Expected verify() const; + + friend bool operator==(const GenericConstantType &Left, + const GenericConstantType &Right) = default; +}; + +enum class GenericConstantKind : std::uint8_t { + Invalid, + Integer, + Floating, + NullPointer, + ZeroInitializer, + Undef, + Poison, + Aggregate, + ByteString, + GlobalAddress, + BlockAddress, + DSOLocalEquivalent, + NoCFI, + PointerAuth, + Expression, +}; + +enum class GenericConstantExpressionOpcode : std::uint8_t { + Invalid, + Trunc, + BitCast, + AddressSpaceCast, + IntToPtr, + PtrToAddr, + PtrToInt, + Add, + Sub, + Xor, + GetElementPtr, + ShuffleVector, + InsertElement, + ExtractElement, + Splat, +}; + +enum class GenericConstantExpressionFlag : std::uint8_t { + NoUnsignedWrap, + NoSignedWrap, + InBounds, + NoUnsignedSignedWrap, + GEPNoUnsignedWrap, + Count, +}; + +class GenericConstantExpressionFlags final { +public: + constexpr GenericConstantExpressionFlags() noexcept = default; + + [[nodiscard]] constexpr bool has(GenericConstantExpressionFlag Flag) const noexcept { + const auto Index = static_cast(Flag); + if (Index >= static_cast(GenericConstantExpressionFlag::Count)) + return false; + return (Mask & (std::uint8_t{1} << Index)) != 0; + } + + constexpr void set(GenericConstantExpressionFlag Flag) noexcept { + const auto Index = static_cast(Flag); + if (Index < static_cast(GenericConstantExpressionFlag::Count)) + Mask |= std::uint8_t{1} << Index; + } + + [[nodiscard]] constexpr std::uint8_t rawValue() const noexcept { + return Mask; + } + + friend constexpr bool operator==(GenericConstantExpressionFlags Left, + GenericConstantExpressionFlags Right) noexcept = default; + +private: + std::uint8_t Mask = 0; +}; + +struct GenericConstantData final { + GenericConstantType Type; + GenericConstantKind Kind = GenericConstantKind::Invalid; + std::optional IntegerValue; + std::optional FloatingValue; + std::vector ByteValue; + std::vector Elements; + std::string Symbol; + std::string Block; + bool NumericSymbol = false; + bool NumericBlock = false; + GenericConstantExpressionOpcode ExpressionOpcode = GenericConstantExpressionOpcode::Invalid; + GenericConstantExpressionFlags ExpressionFlags; + std::optional SourceElementType; + std::optional InRangeStart; + std::optional InRangeEnd; + + [[nodiscard]] static Expected integer(APInt Value); + [[nodiscard]] static Expected floating(APFloat Value); + [[nodiscard]] static Expected byteString(GenericConstantType Type, + std::vector Bytes); + + [[nodiscard]] Expected toLLVMString() const; + [[nodiscard]] Expected verify() const; + [[nodiscard]] bool referencesSymbol() const noexcept; + + friend bool operator==(const GenericConstantData &Left, + const GenericConstantData &Right) = default; +}; + +} // namespace rcc + +#endif // RCC_IR_CONSTANTS_H diff --git a/backend/include/rcc/IR/DataLayout.h b/backend/include/rcc/IR/DataLayout.h new file mode 100644 index 0000000..0ca3f07 --- /dev/null +++ b/backend/include/rcc/IR/DataLayout.h @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_IR_DATALAYOUT_H +#define RCC_IR_DATALAYOUT_H + +#include "rcc/IR/Constants.h" +#include "rcc/Support/Error.h" + +#include +#include +#include +#include +#include + +namespace rcc { + +class DataLayout final { +public: + [[nodiscard]] static Expected parse(std::string_view LayoutString); + + [[nodiscard]] Expected getTypeAllocSize(const GenericConstantType &Type) const; + [[nodiscard]] Expected getABITypeAlign(const GenericConstantType &Type) const; + [[nodiscard]] Expected getPrefTypeAlign(const GenericConstantType &Type) const; + [[nodiscard]] Expected getPointerSizeInBits(std::uint32_t AddressSpace = 0) const; + +private: + struct AlignmentSpec final { + std::uint64_t ABI = 1; + std::uint64_t Preferred = 1; + }; + + struct PrimitiveSpec final { + char Kind = '\0'; + std::uint32_t BitWidth = 0; + AlignmentSpec Alignment; + }; + + struct PointerSpec final { + std::uint32_t AddressSpace = 0; + std::uint32_t SizeInBits = 0; + AlignmentSpec Alignment; + std::string AddressSpaceName; + }; + + struct TypeLayout final { + std::uint64_t StoreSize = 0; + std::uint64_t AllocationSize = 0; + std::uint64_t ABIAlignment = 1; + std::uint64_t PreferredAlignment = 1; + }; + + [[nodiscard]] static Expected parseUnsignedField(std::string_view Text, + std::string_view Description); + [[nodiscard]] static Expected alignmentBytes(std::string_view Text, bool AllowZero, + std::string_view Description); + [[nodiscard]] static std::vector split(std::string_view Text, char Separator); + + void setPrimitiveSpec(PrimitiveSpec Entry); + void setPointerSpec(PointerSpec Entry); + [[nodiscard]] AlignmentSpec naturalAlignment(std::uint64_t StoreSize) const noexcept; + [[nodiscard]] AlignmentSpec scalarAlignment(char Kind, std::uint32_t BitWidth, + std::uint64_t StoreSize) const noexcept; + [[nodiscard]] AlignmentSpec integerAlignment(std::uint32_t BitWidth, + std::uint64_t StoreSize) const noexcept; + [[nodiscard]] AlignmentSpec floatingAlignment(std::uint32_t BitWidth, + std::uint64_t StoreSize) const noexcept; + [[nodiscard]] AlignmentSpec vectorAlignment(std::uint32_t BitWidth, + std::uint64_t StoreSize) const noexcept; + [[nodiscard]] Expected getPointerSpec(std::uint32_t AddressSpace) const; + [[nodiscard]] Expected getTypeLayout(const GenericConstantType &Type, + std::vector &NamedTypeStack, + std::size_t Depth) const; + + bool Explicit = false; + std::vector PrimitiveSpecs; + std::vector PointerSpecs; + AlignmentSpec AggregateAlignment{1, 8}; +}; + +} // namespace rcc + +#endif // RCC_IR_DATALAYOUT_H diff --git a/backend/include/rcc/MIR/LLVMConstantSyntax.h b/backend/include/rcc/MIR/LLVMConstantSyntax.h deleted file mode 100644 index 7e18d60..0000000 --- a/backend/include/rcc/MIR/LLVMConstantSyntax.h +++ /dev/null @@ -1,52 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#ifndef RCC_MIR_LLVMCONSTANTSYNTAX_H -#define RCC_MIR_LLVMCONSTANTSYNTAX_H - -#include "rcc/CodeGen/GenericConstant.h" -#include "rcc/CodeGen/MachineIRTypes.h" -#include "rcc/Support/Error.h" - -#include -#include -#include -#include -#include - -namespace rcc::mir { - -struct LLVMNamedTypeDefinition final { - std::string_view Name; - std::string_view Definition; - bool NumericName = false; -}; - -struct LLVMConstantSyntaxOptions final { - std::string_view DataLayout; - std::span NamedTypes; - std::size_t MaximumDepth = 1024; - std::size_t MaximumNodes = std::size_t{1} << 24U; - std::size_t MaximumTokenLength = std::size_t{1} << 24U; -}; - -[[nodiscard]] Expected -parseLLVMConstant(std::string_view Text, LLVMConstantSyntaxOptions Options = {}); - -[[nodiscard]] Expected parseLLVMType(std::string_view Text, - LLVMConstantSyntaxOptions Options = {}); - -[[nodiscard]] Expected printLLVMConstant(const GenericConstantData &Constant); - -[[nodiscard]] Expected -llvmConstantStorageSize(const GenericConstantType &Type, LLVMConstantSyntaxOptions Options = {}); - -[[nodiscard]] Expected -llvmConstantPreferredAlignment(const GenericConstantType &Type, - LLVMConstantSyntaxOptions Options = {}); - -[[nodiscard]] Expected llvmPointerSizeInBits(std::uint32_t AddressSpace, - LLVMConstantSyntaxOptions Options = {}); - -} // namespace rcc::mir - -#endif // RCC_MIR_LLVMCONSTANTSYNTAX_H diff --git a/backend/lib/AsmParser/CMakeLists.txt b/backend/lib/AsmParser/CMakeLists.txt new file mode 100644 index 0000000..d35fa24 --- /dev/null +++ b/backend/lib/AsmParser/CMakeLists.txt @@ -0,0 +1,41 @@ +# SPDX-License-Identifier: GPL-3.0-or-later + +set(RCCBackendAsmParserSources LLParser.cpp) +set( + RCCBackendAsmParserHeaders + "${RCC_BACKEND_SOURCE_DIR}/include/rcc/AsmParser/LLParser.h" +) + +add_library(RCCBackendAsmParser ${RCCBackendAsmParserSources}) +add_library(RCCBackend::AsmParser ALIAS RCCBackendAsmParser) + +rcc_backend_configure_target(RCCBackendAsmParser) + +target_include_directories( + RCCBackendAsmParser + PUBLIC + "$" + "$" +) +target_link_libraries(RCCBackendAsmParser PUBLIC RCCBackend::IR) + +set_target_properties( + RCCBackendAsmParser + PROPERTIES + EXPORT_NAME AsmParser + OUTPUT_NAME RCCBackendAsmParser +) + +install( + TARGETS RCCBackendAsmParser + 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}/LLParser.cpp" + ${RCCBackendAsmParserHeaders} +) +rcc_backend_register_tidy_files("${CMAKE_CURRENT_SOURCE_DIR}/LLParser.cpp") diff --git a/backend/lib/AsmParser/LLParser.cpp b/backend/lib/AsmParser/LLParser.cpp new file mode 100644 index 0000000..7e21e0f --- /dev/null +++ b/backend/lib/AsmParser/LLParser.cpp @@ -0,0 +1,967 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "rcc/AsmParser/LLParser.h" + +#include "rcc/ADT/APFloat.h" +#include "rcc/ADT/APInt.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace rcc { +namespace { + +struct ParsedName final { + std::string Value; + bool Numeric = false; +}; + +[[nodiscard]] bool isSpace(char Character) noexcept { + return Character == ' ' || Character == '\t' || Character == '\r' || Character == '\n'; +} + +[[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 isHexDigit(char Character) noexcept { + return (Character >= '0' && Character <= '9') || (Character >= 'a' && Character <= 'f') || + (Character >= 'A' && Character <= 'F'); +} + +[[nodiscard]] unsigned hexDigitValue(char Character) noexcept { + if (Character >= '0' && Character <= '9') + return static_cast(Character - '0'); + if (Character >= 'a' && Character <= 'f') + return static_cast(Character - 'a') + 10U; + return static_cast(Character - 'A') + 10U; +} + +[[nodiscard]] std::optional floatingSemantics(std::string_view Name) noexcept { + if (Name == "half") + return FloatSemantics::IEEEHalf; + if (Name == "bfloat") + return FloatSemantics::BFloat; + if (Name == "float") + return FloatSemantics::IEEESingle; + if (Name == "double") + return FloatSemantics::IEEEDouble; + if (Name == "fp128") + return FloatSemantics::IEEEQuad; + if (Name == "ppc_fp128") + return FloatSemantics::PPCDoubleDouble; + if (Name == "x86_fp80") + return FloatSemantics::X87DoubleExtended; + return std::nullopt; +} +class ConstantParser final { +public: + ConstantParser(std::string_view SourceText, LLParserOptions ParserOptions, + std::vector ResolvingTypes = {}) + : Text(SourceText), Options(ParserOptions), ResolvingNamedTypes(std::move(ResolvingTypes)) {} + + [[nodiscard]] Expected parse(); + [[nodiscard]] Expected parseTypeOnly(); + +private: + template [[nodiscard]] Expected fail(std::string_view Message) const { + return makeUnexpected(ErrorCode::ParseError, "LLVM constant at byte " + + std::to_string(Position) + ": " + + std::string(Message)); + } + + void skipSpace() noexcept { + while (Position < Text.size() && isSpace(Text[Position])) + ++Position; + } + + [[nodiscard]] bool atEnd() { + skipSpace(); + return Position == Text.size(); + } + + [[nodiscard]] bool consume(char Character) { + skipSpace(); + if (Position >= Text.size() || Text[Position] != Character) + return false; + ++Position; + return true; + } + + [[nodiscard]] bool consumeWord(std::string_view Word) { + skipSpace(); + if (!Text.substr(Position).starts_with(Word)) + return false; + const std::size_t End = Position + Word.size(); + if (End < Text.size() && isIdentifierBody(Text[End])) + return false; + Position = End; + return true; + } + + [[nodiscard]] std::optional peekWord() { + skipSpace(); + if (Position >= Text.size() || !isIdentifierStart(Text[Position])) + return std::nullopt; + std::size_t End = Position + 1; + while (End < Text.size() && isIdentifierBody(Text[End])) + ++End; + return Text.substr(Position, End - Position); + } + + [[nodiscard]] Expected expect(char Character, std::string_view Description) { + if (!consume(Character)) + return fail("expected " + std::string(Description)); + return {}; + } + + [[nodiscard]] Expected expectWord(std::string_view Word) { + if (!consumeWord(Word)) + return fail("expected '" + std::string(Word) + "'"); + return {}; + } + + [[nodiscard]] Expected parseUnsigned(); + [[nodiscard]] Expected parseName(char Prefix); + [[nodiscard]] Expected> parseByteString(); + [[nodiscard]] Expected parseType(std::size_t Depth = 0); + [[nodiscard]] Expected resolveNamedType(GenericConstantType &Type, std::size_t Depth); + [[nodiscard]] Expected parsePrimaryType(std::size_t Depth); + [[nodiscard]] Expected parseTypedValue(std::size_t Depth); + [[nodiscard]] Expected parseValue(GenericConstantType Type, + std::size_t Depth); + [[nodiscard]] Expected + parseAggregate(GenericConstantType Type, char Open, char Close, bool Packed, std::size_t Depth); + [[nodiscard]] Expected parseFloatingValue(GenericConstantType Type, + std::size_t Depth); + [[nodiscard]] Expected + parseExpression(GenericConstantType Type, std::string_view Opcode, std::size_t Depth); + + [[nodiscard]] Expected accountNode(std::size_t Depth) { + if (Depth > Options.MaximumDepth || ++Nodes > Options.MaximumNodes) + return fail("constant exceeds configured structural limits"); + return {}; + } + + [[nodiscard]] Expected parseValueToken(); + + std::string_view Text; + LLParserOptions Options; + std::size_t Position = 0; + std::size_t Nodes = 0; + std::vector ResolvingNamedTypes; +}; + +Expected ConstantParser::parseUnsigned() { + skipSpace(); + const std::size_t Begin = Position; + while (Position < Text.size() && Text[Position] >= '0' && Text[Position] <= '9') + ++Position; + if (Begin == Position) + return fail("expected an unsigned integer"); + std::uint64_t Value = 0; + const auto Parsed = std::from_chars(Text.data() + Begin, Text.data() + Position, Value, 10); + if (Parsed.ec != std::errc{}) + return fail("unsigned integer is out of range"); + return Value; +} + +Expected ConstantParser::parseName(char Prefix) { + skipSpace(); + if (Position >= Text.size() || Text[Position] != Prefix) + return fail(std::string("expected '") + Prefix + "' identifier"); + ++Position; + if (Position >= Text.size()) + return fail("identifier is empty"); + + ParsedName Result; + if (Text[Position] == '"') { + ++Position; + while (Position < Text.size() && Text[Position] != '"') { + if (Text[Position] != '\\') { + if (static_cast(Text[Position]) < 0x20U) + return fail("identifier contains a control character"); + Result.Value.push_back(Text[Position++]); + continue; + } + ++Position; + if (Position + 1 >= Text.size() || !isHexDigit(Text[Position]) || + !isHexDigit(Text[Position + 1])) { + return fail("identifier escape requires two hexadecimal digits"); + } + Result.Value.push_back(static_cast((hexDigitValue(Text[Position]) << 4U) | + hexDigitValue(Text[Position + 1]))); + Position += 2; + } + if (Position >= Text.size()) + return fail("quoted identifier is unterminated"); + ++Position; + return Result; + } + + if (Text[Position] >= '0' && Text[Position] <= '9') { + Result.Numeric = true; + const std::size_t Begin = Position; + while (Position < Text.size() && Text[Position] >= '0' && Text[Position] <= '9') + ++Position; + Result.Value = std::string(Text.substr(Begin, Position - Begin)); + return Result; + } + if (!isIdentifierStart(Text[Position])) + return fail("identifier has an invalid first character"); + const std::size_t Begin = Position++; + while (Position < Text.size() && isIdentifierBody(Text[Position])) + ++Position; + Result.Value = std::string(Text.substr(Begin, Position - Begin)); + return Result; +} + +Expected> ConstantParser::parseByteString() { + skipSpace(); + if (Position >= Text.size() || Text[Position] != '"') + return fail>("expected an LLVM byte string"); + ++Position; + std::vector Bytes; + while (Position < Text.size() && Text[Position] != '"') { + const char Character = Text[Position++]; + if (Character != '\\') { + if (static_cast(Character) < 0x20U) + return fail>("byte string contains a control character"); + Bytes.push_back(static_cast(static_cast(Character))); + continue; + } + if (Position + 1 >= Text.size() || !isHexDigit(Text[Position]) || + !isHexDigit(Text[Position + 1])) { + return fail>("byte-string escape requires two hexadecimal digits"); + } + Bytes.push_back(static_cast((hexDigitValue(Text[Position]) << 4U) | + hexDigitValue(Text[Position + 1]))); + Position += 2; + } + if (Position >= Text.size()) + return fail>("LLVM byte string is unterminated"); + ++Position; + return Bytes; +} + +Expected ConstantParser::parsePrimaryType(std::size_t Depth) { + if (auto Limit = accountNode(Depth); !Limit) + return std::unexpected(Limit.error()); + skipSpace(); + + if (consume('[')) { + auto Count = parseUnsigned(); + if (!Count) + return std::unexpected(Count.error()); + if (auto Separator = expectWord("x"); !Separator) + return std::unexpected(Separator.error()); + auto Element = parseType(Depth + 1); + if (!Element) + return std::unexpected(Element.error()); + if (auto End = expect(']', "']' after array type"); !End) + return std::unexpected(End.error()); + return GenericConstantType::array(*Count, std::move(*Element)); + } + + if (consume('<')) { + if (consume('{')) { + std::vector Elements; + if (!consume('}')) { + while (true) { + auto Element = parseType(Depth + 1); + if (!Element) + return std::unexpected(Element.error()); + Elements.push_back(std::move(*Element)); + if (consume('}')) + break; + if (auto Comma = expect(',', "',' between packed struct members"); !Comma) + return std::unexpected(Comma.error()); + } + } + if (auto End = expect('>', "'>' after packed struct type"); !End) + return std::unexpected(End.error()); + return GenericConstantType::structure(std::move(Elements), true); + } + + const bool Scalable = consumeWord("vscale"); + if (Scalable) { + if (auto Separator = expectWord("x"); !Separator) + return std::unexpected(Separator.error()); + } + auto Count = parseUnsigned(); + if (!Count) + return std::unexpected(Count.error()); + if (auto Separator = expectWord("x"); !Separator) + return std::unexpected(Separator.error()); + auto Element = parseType(Depth + 1); + if (!Element) + return std::unexpected(Element.error()); + if (auto End = expect('>', "'>' after vector type"); !End) + return std::unexpected(End.error()); + return GenericConstantType::vector(*Count, std::move(*Element), Scalable); + } + + if (consume('{')) { + std::vector Elements; + if (!consume('}')) { + while (true) { + auto Element = parseType(Depth + 1); + if (!Element) + return std::unexpected(Element.error()); + Elements.push_back(std::move(*Element)); + if (consume('}')) + break; + if (auto Comma = expect(',', "',' between struct members"); !Comma) + return std::unexpected(Comma.error()); + } + } + return GenericConstantType::structure(std::move(Elements), false); + } + + skipSpace(); + if (Position < Text.size() && Text[Position] == '%') { + auto Name = parseName('%'); + if (!Name) + return std::unexpected(Name.error()); + return GenericConstantType::named(std::move(Name->Value), Name->Numeric); + } + + const auto Word = peekWord(); + if (!Word) + return fail("expected an LLVM IR type"); + Position += Word->size(); + if (*Word == "void") + return GenericConstantType::primitive(GenericConstantTypeKind::Void); + if (*Word == "label") + return GenericConstantType::primitive(GenericConstantTypeKind::Label); + if (*Word == "token") + return GenericConstantType::primitive(GenericConstantTypeKind::Token); + if (*Word == "metadata") + return GenericConstantType::primitive(GenericConstantTypeKind::Metadata); + if (*Word == "byte") + return GenericConstantType::byte(); + if (Word->starts_with('i')) { + std::uint64_t Width = 0; + const auto Parsed = std::from_chars(Word->data() + 1, Word->data() + Word->size(), Width, 10); + if (Word->size() == 1 || Parsed.ec != std::errc{} || + Parsed.ptr != Word->data() + Word->size() || + Width > std::numeric_limits::max()) { + return fail("integer type has an invalid bit width"); + } + return GenericConstantType::integer(static_cast(Width)); + } + if (const auto Semantics = floatingSemantics(*Word)) + return GenericConstantType::floating(*Semantics); + if (*Word == "ptr") { + std::uint32_t AddressSpace = 0; + if (consumeWord("addrspace")) { + if (auto Open = expect('(', "'(' after addrspace"); !Open) + return std::unexpected(Open.error()); + auto ParsedAddressSpace = parseUnsigned(); + if (!ParsedAddressSpace || *ParsedAddressSpace > std::numeric_limits::max()) + return fail("pointer address space is out of range"); + AddressSpace = static_cast(*ParsedAddressSpace); + if (auto Close = expect(')', "')' after address space"); !Close) + return std::unexpected(Close.error()); + } + return GenericConstantType::pointer(AddressSpace); + } + return fail("unsupported LLVM IR type '" + std::string(*Word) + "'"); +} + +Expected ConstantParser::parseType(std::size_t Depth) { + auto Type = parsePrimaryType(Depth); + if (!Type) + return std::unexpected(Type.error()); + + while (true) { + skipSpace(); + if (consume('(')) { + std::vector Parameters; + bool VarArg = false; + if (!consume(')')) { + while (true) { + skipSpace(); + if (Text.substr(Position).starts_with("...")) { + Position += 3; + VarArg = true; + if (auto End = expect(')', "')' after variadic function type"); !End) + return std::unexpected(End.error()); + break; + } + auto Parameter = parseType(Depth + 1); + if (!Parameter) + return std::unexpected(Parameter.error()); + Parameters.push_back(std::move(*Parameter)); + if (consume(')')) + break; + if (auto Comma = expect(',', "',' between function parameters"); !Comma) + return std::unexpected(Comma.error()); + } + } + Type = GenericConstantType::function(std::move(*Type), std::move(Parameters), VarArg); + if (!Type) + return std::unexpected(Type.error()); + continue; + } + + std::uint32_t AddressSpace = 0; + const std::size_t BeforeAddressSpace = Position; + if (consumeWord("addrspace")) { + if (auto Open = expect('(', "'(' after addrspace"); !Open) + return std::unexpected(Open.error()); + auto ParsedAddressSpace = parseUnsigned(); + if (!ParsedAddressSpace || *ParsedAddressSpace > std::numeric_limits::max()) + return fail("pointer address space is out of range"); + AddressSpace = static_cast(*ParsedAddressSpace); + if (auto Close = expect(')', "')' after address space"); !Close) + return std::unexpected(Close.error()); + } + if (consume('*')) { + Type = GenericConstantType::pointer(AddressSpace); + continue; + } + Position = BeforeAddressSpace; + break; + } + if (Type->Kind == GenericConstantTypeKind::Named) { + if (auto Resolved = resolveNamedType(*Type, Depth + 1); !Resolved) + return std::unexpected(Resolved.error()); + } + return Type; +} + +Expected ConstantParser::resolveNamedType(GenericConstantType &Type, std::size_t Depth) { + if (Type.Kind != GenericConstantTypeKind::Named || !Type.Elements.empty()) + return {}; + const auto Definition = + std::ranges::find_if(Options.NamedTypes, [&Type](const LLNamedTypeDefinition &Candidate) { + return Candidate.Name == Type.Name && Candidate.NumericName == Type.NumericName; + }); + if (Definition == Options.NamedTypes.end()) + return {}; + if (Depth > Options.MaximumDepth || + std::ranges::find(ResolvingNamedTypes, Type.Name) != ResolvingNamedTypes.end()) { + return fail("named LLVM type definition is recursively unsized"); + } + std::vector Stack = ResolvingNamedTypes; + Stack.push_back(Type.Name); + ConstantParser DefinitionParser(Definition->Definition, Options, std::move(Stack)); + auto Resolved = DefinitionParser.parseTypeOnly(); + if (!Resolved) + return std::unexpected(Resolved.error()); + Type.Elements.push_back(std::move(*Resolved)); + return {}; +} + +Expected ConstantParser::parseValueToken() { + skipSpace(); + const std::size_t Begin = Position; + unsigned ParenthesisDepth = 0; + while (Position < Text.size()) { + const char Character = Text[Position]; + if (Character == '(') { + ++ParenthesisDepth; + ++Position; + continue; + } + if (Character == ')') { + if (ParenthesisDepth == 0) + break; + --ParenthesisDepth; + ++Position; + continue; + } + if (ParenthesisDepth == 0 && (isSpace(Character) || Character == ',' || Character == ']' || + Character == '}' || Character == '>')) { + break; + } + ++Position; + if (Position - Begin > Options.MaximumTokenLength) + return fail("value token exceeds the configured length limit"); + } + if (Begin == Position) + return fail("expected an LLVM constant value"); + return Text.substr(Begin, Position - Begin); +} + +Expected ConstantParser::parseFloatingValue(GenericConstantType Type, + std::size_t Depth) { + const GenericConstantType &EffectiveType = Type.resolved(); + if (EffectiveType.Kind != GenericConstantTypeKind::Floating) + return fail("floating literal requires a floating-point type"); + auto Token = parseValueToken(); + if (!Token) + return std::unexpected(Token.error()); + auto Bits = APFloat::parse(EffectiveType.Semantics, *Token); + if (!Bits) + return fail(Bits.error().message()); + GenericConstantData Value; + Value.Type = std::move(Type); + Value.Kind = GenericConstantKind::Floating; + Value.FloatingValue = *Bits; + if (auto Limit = accountNode(Depth); !Limit) + return std::unexpected(Limit.error()); + return Value; +} + +Expected ConstantParser::parseAggregate(GenericConstantType Type, char Open, + char Close, bool Packed, + std::size_t Depth) { + if (!consume(Open)) + return fail(std::string("expected '") + Open + "' for aggregate"); + if (Packed && !consume('{')) + return fail("expected '{' in packed struct constant"); + + GenericConstantData Value; + Value.Type = std::move(Type); + Value.Kind = GenericConstantKind::Aggregate; + const char InnerClose = Packed ? '}' : Close; + if (!consume(InnerClose)) { + while (true) { + auto Element = parseTypedValue(Depth + 1); + if (!Element) + return std::unexpected(Element.error()); + Value.Elements.push_back(std::move(*Element)); + if (consume(InnerClose)) + break; + if (auto Comma = expect(',', "',' between aggregate elements"); !Comma) + return std::unexpected(Comma.error()); + } + } + if (Packed) { + if (auto End = expect('>', "'>' after packed struct constant"); !End) + return std::unexpected(End.error()); + } + auto Verification = Value.verify(); + if (!Verification) + return fail(Verification.error().message()); + return Value; +} + +Expected ConstantParser::parseTypedValue(std::size_t Depth) { + auto Type = parseType(Depth + 1); + if (!Type) + return std::unexpected(Type.error()); + return parseValue(std::move(*Type), Depth + 1); +} + +Expected ConstantParser::parseExpression(GenericConstantType Type, + std::string_view Opcode, + std::size_t Depth) { + GenericConstantData Value; + Value.Type = std::move(Type); + Value.Kind = GenericConstantKind::Expression; + + const auto SetOpcode = [&](GenericConstantExpressionOpcode ParsedOpcode) { + Value.ExpressionOpcode = ParsedOpcode; + }; + if (Opcode == "trunc") + SetOpcode(GenericConstantExpressionOpcode::Trunc); + else if (Opcode == "bitcast") + SetOpcode(GenericConstantExpressionOpcode::BitCast); + else if (Opcode == "addrspacecast") + SetOpcode(GenericConstantExpressionOpcode::AddressSpaceCast); + else if (Opcode == "inttoptr") + SetOpcode(GenericConstantExpressionOpcode::IntToPtr); + else if (Opcode == "ptrtoaddr") + SetOpcode(GenericConstantExpressionOpcode::PtrToAddr); + else if (Opcode == "ptrtoint") + SetOpcode(GenericConstantExpressionOpcode::PtrToInt); + else if (Opcode == "add") + SetOpcode(GenericConstantExpressionOpcode::Add); + else if (Opcode == "sub") + SetOpcode(GenericConstantExpressionOpcode::Sub); + else if (Opcode == "xor") + SetOpcode(GenericConstantExpressionOpcode::Xor); + else if (Opcode == "getelementptr") + SetOpcode(GenericConstantExpressionOpcode::GetElementPtr); + else if (Opcode == "shufflevector") + SetOpcode(GenericConstantExpressionOpcode::ShuffleVector); + else if (Opcode == "insertelement") + SetOpcode(GenericConstantExpressionOpcode::InsertElement); + else if (Opcode == "extractelement") + SetOpcode(GenericConstantExpressionOpcode::ExtractElement); + else if (Opcode == "splat") + SetOpcode(GenericConstantExpressionOpcode::Splat); + else + return fail("unsupported constant expression '" + std::string(Opcode) + + "'"); + + 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; + const bool IsBinary = Value.ExpressionOpcode == GenericConstantExpressionOpcode::Add || + Value.ExpressionOpcode == GenericConstantExpressionOpcode::Sub || + Value.ExpressionOpcode == GenericConstantExpressionOpcode::Xor; + + if (IsBinary) { + while (true) { + if (consumeWord("nuw")) { + Value.ExpressionFlags.set(GenericConstantExpressionFlag::NoUnsignedWrap); + continue; + } + if (consumeWord("nsw")) { + Value.ExpressionFlags.set(GenericConstantExpressionFlag::NoSignedWrap); + continue; + } + break; + } + } else if (Value.ExpressionOpcode == GenericConstantExpressionOpcode::GetElementPtr) { + while (true) { + if (consumeWord("inbounds")) { + Value.ExpressionFlags.set(GenericConstantExpressionFlag::InBounds); + continue; + } + if (consumeWord("nusw")) { + Value.ExpressionFlags.set(GenericConstantExpressionFlag::NoUnsignedSignedWrap); + continue; + } + if (consumeWord("nuw")) { + Value.ExpressionFlags.set(GenericConstantExpressionFlag::GEPNoUnsignedWrap); + continue; + } + break; + } + if (consumeWord("inrange")) { + if (auto Open = expect('(', "'(' after inrange"); !Open) + return std::unexpected(Open.error()); + auto StartToken = parseValueToken(); + if (!StartToken) + return std::unexpected(StartToken.error()); + auto Start = APInt::parseDecimal(64, *StartToken); + if (!Start) + return fail("invalid inrange start"); + if (auto Comma = expect(',', "',' in inrange interval"); !Comma) + return std::unexpected(Comma.error()); + auto EndToken = parseValueToken(); + if (!EndToken) + return std::unexpected(EndToken.error()); + auto End = APInt::parseDecimal(64, *EndToken); + if (!End) + return fail("invalid inrange end"); + if (auto Close = expect(')', "')' after inrange interval"); !Close) + return std::unexpected(Close.error()); + Value.InRangeStart = std::move(*Start); + Value.InRangeEnd = std::move(*End); + } + } + + if (auto Open = expect('(', "'(' after constant-expression opcode"); !Open) + return std::unexpected(Open.error()); + if (Value.ExpressionOpcode == GenericConstantExpressionOpcode::GetElementPtr) { + auto SourceType = parseType(Depth + 1); + if (!SourceType) + return std::unexpected(SourceType.error()); + Value.SourceElementType = std::move(*SourceType); + if (auto Comma = expect(',', "',' after getelementptr source type"); !Comma) + return std::unexpected(Comma.error()); + } + + auto First = parseTypedValue(Depth + 1); + if (!First) + return std::unexpected(First.error()); + Value.Elements.push_back(std::move(*First)); + if (IsCast) { + if (auto To = expectWord("to"); !To) + return std::unexpected(To.error()); + auto Destination = parseType(Depth + 1); + if (!Destination) + return std::unexpected(Destination.error()); + if (*Destination != Value.Type) + return fail("constant-expression destination type does not match value"); + } else { + while (consume(',')) { + auto Operand = parseTypedValue(Depth + 1); + if (!Operand) + return std::unexpected(Operand.error()); + Value.Elements.push_back(std::move(*Operand)); + } + } + if (auto Close = expect(')', "')' after constant expression"); !Close) + return std::unexpected(Close.error()); + + const std::size_t RequiredOperands = [&]() -> std::size_t { + switch (Value.ExpressionOpcode) { + case GenericConstantExpressionOpcode::Trunc: + case GenericConstantExpressionOpcode::BitCast: + case GenericConstantExpressionOpcode::AddressSpaceCast: + case GenericConstantExpressionOpcode::IntToPtr: + case GenericConstantExpressionOpcode::PtrToAddr: + case GenericConstantExpressionOpcode::PtrToInt: + case GenericConstantExpressionOpcode::Splat: + return 1; + case GenericConstantExpressionOpcode::Add: + case GenericConstantExpressionOpcode::Sub: + case GenericConstantExpressionOpcode::Xor: + case GenericConstantExpressionOpcode::ExtractElement: + return 2; + case GenericConstantExpressionOpcode::ShuffleVector: + case GenericConstantExpressionOpcode::InsertElement: + return 3; + case GenericConstantExpressionOpcode::GetElementPtr: + return 1; + case GenericConstantExpressionOpcode::Invalid: + return 0; + } + return 0; + }(); + if (Value.ExpressionOpcode == GenericConstantExpressionOpcode::GetElementPtr) { + if (Value.Elements.empty() || + Value.Elements.front().Type.resolved().Kind != GenericConstantTypeKind::Pointer || + Value.Type.resolved().Kind != GenericConstantTypeKind::Pointer) { + return fail("getelementptr constant has no base pointer"); + } + } else if (Value.Elements.size() != RequiredOperands) { + return fail("constant expression has the wrong operand count"); + } + auto Verification = Value.verify(); + if (!Verification) + return fail(Verification.error().message()); + return Value; +} + +Expected ConstantParser::parseValue(GenericConstantType Type, + std::size_t Depth) { + if (auto Limit = accountNode(Depth); !Limit) + return std::unexpected(Limit.error()); + skipSpace(); + + const auto Marker = [&](GenericConstantKind Kind) -> Expected { + GenericConstantData Value; + Value.Type = std::move(Type); + Value.Kind = Kind; + auto Verification = Value.verify(); + if (!Verification) + return fail(Verification.error().message()); + return Value; + }; + if (consumeWord("zeroinitializer")) + return Marker(GenericConstantKind::ZeroInitializer); + if (consumeWord("undef")) + return Marker(GenericConstantKind::Undef); + if (consumeWord("poison")) + return Marker(GenericConstantKind::Poison); + if (consumeWord("null")) + return Marker(GenericConstantKind::NullPointer); + + const GenericConstantType &EffectiveType = Type.resolved(); + + if (consumeWord("c")) { + auto Bytes = parseByteString(); + if (!Bytes) + return std::unexpected(Bytes.error()); + return GenericConstantData::byteString(std::move(Type), std::move(*Bytes)); + } + + if (EffectiveType.Kind == GenericConstantTypeKind::Array) + return parseAggregate(std::move(Type), '[', ']', false, Depth + 1); + if (EffectiveType.Kind == GenericConstantTypeKind::Struct) + return parseAggregate(std::move(Type), EffectiveType.Packed ? '<' : '{', '}', + EffectiveType.Packed, Depth + 1); + if (EffectiveType.Kind == GenericConstantTypeKind::Vector) { + if (consumeWord("splat")) + return parseExpression(std::move(Type), "splat", Depth + 1); + skipSpace(); + if (Position < Text.size() && Text[Position] == '<') + return parseAggregate(std::move(Type), '<', '>', false, Depth + 1); + } + + skipSpace(); + if (Position < Text.size() && Text[Position] == '@') { + auto Name = parseName('@'); + if (!Name) + return std::unexpected(Name.error()); + GenericConstantData Value; + Value.Type = std::move(Type); + Value.Kind = GenericConstantKind::GlobalAddress; + Value.Symbol = std::move(Name->Value); + Value.NumericSymbol = Name->Numeric; + auto Verification = Value.verify(); + if (!Verification) + return fail(Verification.error().message()); + return Value; + } + + if (consumeWord("blockaddress")) { + if (auto Open = expect('(', "'(' after blockaddress"); !Open) + return std::unexpected(Open.error()); + auto Function = parseName('@'); + if (!Function) + return std::unexpected(Function.error()); + if (auto Comma = expect(',', "',' in blockaddress"); !Comma) + return std::unexpected(Comma.error()); + auto Block = parseName('%'); + if (!Block) + return std::unexpected(Block.error()); + if (auto Close = expect(')', "')' after blockaddress"); !Close) + return std::unexpected(Close.error()); + GenericConstantData Value; + Value.Type = std::move(Type); + Value.Kind = GenericConstantKind::BlockAddress; + Value.Symbol = std::move(Function->Value); + Value.NumericSymbol = Function->Numeric; + Value.Block = std::move(Block->Value); + Value.NumericBlock = Block->Numeric; + auto Verification = Value.verify(); + if (!Verification) + return fail(Verification.error().message()); + return Value; + } + + for (const auto &[Keyword, Kind] : + {std::pair{std::string_view{"dso_local_equivalent"}, + GenericConstantKind::DSOLocalEquivalent}, + std::pair{std::string_view{"no_cfi"}, GenericConstantKind::NoCFI}}) { + if (!consumeWord(Keyword)) + continue; + auto Name = parseName('@'); + if (!Name) + return std::unexpected(Name.error()); + GenericConstantData Value; + Value.Type = std::move(Type); + Value.Kind = Kind; + Value.Symbol = std::move(Name->Value); + Value.NumericSymbol = Name->Numeric; + auto Verification = Value.verify(); + if (!Verification) + return fail(Verification.error().message()); + return Value; + } + + if (consumeWord("ptrauth")) { + if (auto Open = expect('(', "'(' after ptrauth"); !Open) + return std::unexpected(Open.error()); + GenericConstantData Value; + Value.Type = std::move(Type); + Value.Kind = GenericConstantKind::PointerAuth; + while (true) { + auto Operand = parseTypedValue(Depth + 1); + if (!Operand) + return std::unexpected(Operand.error()); + Value.Elements.push_back(std::move(*Operand)); + if (consume(')')) + break; + if (auto Comma = expect(',', "',' in ptrauth constant"); !Comma) + return std::unexpected(Comma.error()); + } + auto Verification = Value.verify(); + if (!Verification) + return fail(Verification.error().message()); + return Value; + } + + const auto Word = peekWord(); + if (Word) { + constexpr std::array ExpressionNames{ + std::string_view{"trunc"}, + std::string_view{"bitcast"}, + std::string_view{"addrspacecast"}, + std::string_view{"inttoptr"}, + std::string_view{"ptrtoaddr"}, + std::string_view{"ptrtoint"}, + std::string_view{"add"}, + std::string_view{"sub"}, + std::string_view{"xor"}, + std::string_view{"getelementptr"}, + std::string_view{"shufflevector"}, + std::string_view{"insertelement"}, + std::string_view{"extractelement"}, + }; + if (std::ranges::find(ExpressionNames, *Word) != ExpressionNames.end()) { + Position += Word->size(); + return parseExpression(std::move(Type), *Word, Depth + 1); + } + } + + if (EffectiveType.Kind == GenericConstantTypeKind::Floating) + return parseFloatingValue(std::move(Type), Depth + 1); + if (EffectiveType.Kind == GenericConstantTypeKind::Integer || + EffectiveType.Kind == GenericConstantTypeKind::Byte) { + auto Token = parseValueToken(); + if (!Token) + return std::unexpected(Token.error()); + std::string_view Spelling = *Token; + if (Spelling == "true") + Spelling = "1"; + else if (Spelling == "false") + Spelling = "0"; + const std::uint32_t BitWidth = + EffectiveType.Kind == GenericConstantTypeKind::Byte ? 8U : EffectiveType.BitWidth; + const bool IsExplicitHex = Spelling.starts_with("u0x") || Spelling.starts_with("s0x"); + auto Integer = IsExplicitHex ? APInt::parseLLVMHex(BitWidth, Spelling) + : APInt::parseDecimal(BitWidth, Spelling); + if (!Integer) + return fail(Integer.error().message()); + GenericConstantData Value; + Value.Type = std::move(Type); + Value.Kind = GenericConstantKind::Integer; + Value.IntegerValue = std::move(*Integer); + return Value; + } + return fail("constant value is not valid for its LLVM IR type"); +} + +Expected ConstantParser::parse() { + if (Text.empty()) + return fail("constant text is empty"); + auto Value = parseTypedValue(0); + if (!Value) + return Value; + if (!atEnd()) + return fail("unexpected trailing text"); + auto Verification = Value->verify(); + if (!Verification) + return fail(Verification.error().message()); + return Value; +} + +Expected ConstantParser::parseTypeOnly() { + if (Text.empty()) + return fail("type text is empty"); + auto Type = parseType(0); + if (!Type) + return Type; + if (!atEnd()) + return fail("unexpected trailing text after LLVM type"); + auto Verification = Type->verify(); + if (!Verification) + return fail(Verification.error().message()); + return Type; +} + +} // namespace + +Expected parseLLVMConstant(std::string_view Text, LLParserOptions Options) { + if (Options.MaximumDepth == 0 || Options.MaximumNodes == 0 || Options.MaximumTokenLength == 0) { + return makeUnexpected(ErrorCode::InvalidArgument, + "LLVM constant parser limits must be nonzero"); + } + return ConstantParser(Text, Options).parse(); +} + +Expected parseLLVMType(std::string_view Text, LLParserOptions Options) { + if (Options.MaximumDepth == 0 || Options.MaximumNodes == 0 || Options.MaximumTokenLength == 0) { + return makeUnexpected(ErrorCode::InvalidArgument, "LLVM type parser limits must be nonzero"); + } + return ConstantParser(Text, Options).parseTypeOnly(); +} + +} // namespace rcc diff --git a/backend/lib/AsmPrinter/AsmPrinter.cpp b/backend/lib/AsmPrinter/AsmPrinter.cpp index 1f237af..eb21213 100644 --- a/backend/lib/AsmPrinter/AsmPrinter.cpp +++ b/backend/lib/AsmPrinter/AsmPrinter.cpp @@ -3,7 +3,6 @@ #include "rcc/CodeGen/AsmPrinter.h" #include "rcc/CodeGen/CompilationUnit.h" -#include "rcc/CodeGen/GenericConstant.h" #include "rcc/CodeGen/GenericOpcodes.h" #include "rcc/CodeGen/GlobalObject.h" #include "rcc/CodeGen/InstructionDescriptor.h" @@ -19,6 +18,7 @@ #include "rcc/CodeGen/TargetConstant.h" #include "rcc/CodeGen/TargetDescription.h" #include "rcc/CodeGen/TargetMCInstLowering.h" +#include "rcc/IR/Constants.h" #include "rcc/MC/MCCFI.h" #include "rcc/MC/MCContext.h" #include "rcc/MC/MCExpr.h" diff --git a/backend/lib/CMakeLists.txt b/backend/lib/CMakeLists.txt index a6ba7ef..350cbf2 100644 --- a/backend/lib/CMakeLists.txt +++ b/backend/lib/CMakeLists.txt @@ -1,6 +1,8 @@ # SPDX-License-Identifier: GPL-3.0-or-later add_subdirectory(Support) +add_subdirectory(IR) +add_subdirectory(AsmParser) add_subdirectory(CodeGen) add_subdirectory(MIR) add_subdirectory(MC) diff --git a/backend/lib/CodeGen/CMakeLists.txt b/backend/lib/CodeGen/CMakeLists.txt index 4488903..012b0bb 100644 --- a/backend/lib/CodeGen/CMakeLists.txt +++ b/backend/lib/CodeGen/CMakeLists.txt @@ -6,7 +6,6 @@ set( CommandFlags.cpp GlobalObject.cpp GenericOpcodes.cpp - GenericConstant.cpp FrameIndexElimination.cpp InstructionDescriptor.cpp LowLevelType.cpp @@ -93,7 +92,6 @@ set( "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/CommandFlags.h" "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/GlobalObject.h" "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/GenericOpcodes.h" - "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/GenericConstant.h" "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/FrameIndexElimination.h" "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/InstructionDescriptor.h" "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/InlineAsm.h" @@ -202,7 +200,7 @@ target_include_directories( "$" "$" ) -target_link_libraries(RCCBackendCodeGen PUBLIC RCCBackend::Support) +target_link_libraries(RCCBackendCodeGen PUBLIC RCCBackend::IR) set_target_properties( RCCBackendCodeGen @@ -224,7 +222,6 @@ rcc_backend_register_format_files( "${CMAKE_CURRENT_SOURCE_DIR}/CommandFlags.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/GlobalObject.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/GenericOpcodes.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/GenericConstant.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/InstructionDescriptor.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/LowLevelType.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/MachineBasicBlockAttributes.cpp" @@ -312,7 +309,6 @@ rcc_backend_register_tidy_files( "${CMAKE_CURRENT_SOURCE_DIR}/CommandFlags.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/GlobalObject.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/GenericOpcodes.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/GenericConstant.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/InstructionDescriptor.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/LowLevelType.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/MachineBasicBlockAttributes.cpp" diff --git a/backend/lib/CodeGen/GenericConstant.cpp b/backend/lib/CodeGen/GenericConstant.cpp deleted file mode 100644 index afcdd44..0000000 --- a/backend/lib/CodeGen/GenericConstant.cpp +++ /dev/null @@ -1,868 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "rcc/CodeGen/GenericConstant.h" - -#include -#include -#include -#include -#include -#include -#include - -namespace rcc { -namespace { - -constexpr std::size_t MaximumVerificationDepth = 1024; -constexpr std::size_t MaximumVerificationNodes = std::size_t{1} << 24U; - -[[nodiscard]] bool isAggregateType(GenericConstantTypeKind Kind) noexcept { - return Kind == GenericConstantTypeKind::Array || Kind == GenericConstantTypeKind::Vector || - Kind == GenericConstantTypeKind::Struct; -} - -[[nodiscard]] const GenericConstantType &resolvedType(const GenericConstantType &Type) noexcept { - const GenericConstantType *Resolved = &Type; - std::size_t Depth = 0; - while (Resolved->Kind == GenericConstantTypeKind::Named && Resolved->Elements.size() == 1 && - Depth++ <= MaximumVerificationDepth) { - Resolved = &Resolved->Elements.front(); - } - return *Resolved; -} - -[[nodiscard]] bool sameResolvedType(const GenericConstantType &Left, - const GenericConstantType &Right) noexcept { - return resolvedType(Left) == resolvedType(Right); -} - -[[nodiscard]] bool isIntegerScalarType(const GenericConstantType &Type) noexcept { - const GenericConstantTypeKind Kind = resolvedType(Type).Kind; - return Kind == GenericConstantTypeKind::Integer || Kind == GenericConstantTypeKind::Byte; -} - -[[nodiscard]] std::optional -integerScalarBitWidth(const GenericConstantType &Type) noexcept { - const GenericConstantType &Resolved = resolvedType(Type); - if (Resolved.Kind == GenericConstantTypeKind::Integer) - return Resolved.BitWidth; - if (Resolved.Kind == GenericConstantTypeKind::Byte) - return 8; - return std::nullopt; -} - -[[nodiscard]] bool isPointerScalarType(const GenericConstantType &Type) noexcept { - return resolvedType(Type).Kind == GenericConstantTypeKind::Pointer; -} - -[[nodiscard]] bool isFloatingScalarType(const GenericConstantType &Type) noexcept { - return resolvedType(Type).Kind == GenericConstantTypeKind::Floating; -} - -struct VectorShape final { - std::uint64_t ElementCount = 0; - bool Scalable = false; - - friend constexpr bool operator==(VectorShape Left, VectorShape Right) noexcept = default; -}; - -[[nodiscard]] std::optional vectorShape(const GenericConstantType &Type) noexcept { - const GenericConstantType &Resolved = resolvedType(Type); - if (Resolved.Kind != GenericConstantTypeKind::Vector) - return std::nullopt; - return VectorShape{.ElementCount = Resolved.ElementCount, .Scalable = Resolved.Scalable}; -} - -[[nodiscard]] const GenericConstantType * -vectorElementType(const GenericConstantType &Type) noexcept { - const GenericConstantType &Resolved = resolvedType(Type); - if (Resolved.Kind != GenericConstantTypeKind::Vector || Resolved.Elements.size() != 1) - return nullptr; - return &Resolved.Elements.front(); -} - -[[nodiscard]] bool hasSameVectorShape(const GenericConstantType &Left, - const GenericConstantType &Right) noexcept { - const auto LeftShape = vectorShape(Left); - const auto RightShape = vectorShape(Right); - return LeftShape == RightShape; -} - -[[nodiscard]] bool isIntegerOrIntegerVectorType(const GenericConstantType &Type) noexcept { - if (isIntegerScalarType(Type)) - return true; - const GenericConstantType *Element = vectorElementType(Type); - return Element != nullptr && isIntegerScalarType(*Element); -} - -[[nodiscard]] bool isPointerOrPointerVectorType(const GenericConstantType &Type) noexcept { - if (isPointerScalarType(Type)) - return true; - const GenericConstantType *Element = vectorElementType(Type); - return Element != nullptr && isPointerScalarType(*Element); -} - -[[nodiscard]] std::optional -pointerAddressSpace(const GenericConstantType &Type) noexcept { - const GenericConstantType &Resolved = resolvedType(Type); - if (Resolved.Kind == GenericConstantTypeKind::Pointer) - return Resolved.AddressSpace; - const GenericConstantType *Element = vectorElementType(Resolved); - if (Element == nullptr) - return std::nullopt; - const GenericConstantType &ResolvedElement = resolvedType(*Element); - if (ResolvedElement.Kind != GenericConstantTypeKind::Pointer) - return std::nullopt; - return ResolvedElement.AddressSpace; -} - -[[nodiscard]] bool isNonPointerBitCastType(const GenericConstantType &Type) noexcept { - if (isIntegerScalarType(Type) || isFloatingScalarType(Type)) - return true; - const GenericConstantType *Element = vectorElementType(Type); - return Element != nullptr && (isIntegerScalarType(*Element) || isFloatingScalarType(*Element)); -} - -[[nodiscard]] std::optional -nonPointerFixedBitWidth(const GenericConstantType &Type) noexcept { - const GenericConstantType &Resolved = resolvedType(Type); - if (const auto Width = integerScalarBitWidth(Resolved)) - return *Width; - if (Resolved.Kind == GenericConstantTypeKind::Floating) - return getFloatSemanticsBitWidth(Resolved.Semantics); - const GenericConstantType *Element = vectorElementType(Resolved); - if (Element == nullptr || Resolved.Scalable) - return std::nullopt; - const auto ElementWidth = nonPointerFixedBitWidth(*Element); - if (!ElementWidth || *ElementWidth == 0 || - Resolved.ElementCount > std::numeric_limits::max() / *ElementWidth) { - return std::nullopt; - } - return Resolved.ElementCount * *ElementWidth; -} - -[[nodiscard]] bool hasIntegerCastShape(const GenericConstantType &Source, - const GenericConstantType &Destination, - bool RequireNarrowing) noexcept { - const GenericConstantType &ResolvedSource = resolvedType(Source); - const GenericConstantType &ResolvedDestination = resolvedType(Destination); - if (const auto SourceWidth = integerScalarBitWidth(ResolvedSource)) { - const auto DestinationWidth = integerScalarBitWidth(ResolvedDestination); - return DestinationWidth && (RequireNarrowing ? *SourceWidth > *DestinationWidth - : *SourceWidth == *DestinationWidth); - } - const GenericConstantType *SourceElement = vectorElementType(ResolvedSource); - const GenericConstantType *DestinationElement = vectorElementType(ResolvedDestination); - if (SourceElement == nullptr || DestinationElement == nullptr || - !hasSameVectorShape(ResolvedSource, ResolvedDestination)) { - return false; - } - return hasIntegerCastShape(*SourceElement, *DestinationElement, RequireNarrowing); -} - -[[nodiscard]] bool hasPointerIntegerCastShape(const GenericConstantType &Pointer, - const GenericConstantType &Integer) noexcept { - const GenericConstantType &ResolvedPointer = resolvedType(Pointer); - const GenericConstantType &ResolvedInteger = resolvedType(Integer); - if (isPointerScalarType(ResolvedPointer)) - return isIntegerScalarType(ResolvedInteger); - const GenericConstantType *PointerElement = vectorElementType(ResolvedPointer); - const GenericConstantType *IntegerElement = vectorElementType(ResolvedInteger); - return PointerElement != nullptr && IntegerElement != nullptr && - hasSameVectorShape(ResolvedPointer, ResolvedInteger) && - isPointerScalarType(*PointerElement) && isIntegerScalarType(*IntegerElement); -} - -[[nodiscard]] bool isSizedType(const GenericConstantType &Type, std::size_t Depth = 0) noexcept { - if (Depth > MaximumVerificationDepth) - return false; - const GenericConstantType &Resolved = resolvedType(Type); - switch (Resolved.Kind) { - case GenericConstantTypeKind::Integer: - case GenericConstantTypeKind::Byte: - case GenericConstantTypeKind::Floating: - case GenericConstantTypeKind::Pointer: - return true; - case GenericConstantTypeKind::Array: - case GenericConstantTypeKind::Vector: - return Resolved.Elements.size() == 1 && isSizedType(Resolved.Elements.front(), Depth + 1); - case GenericConstantTypeKind::Struct: - return std::ranges::all_of(Resolved.Elements, [Depth](const GenericConstantType &Element) { - return isSizedType(Element, Depth + 1); - }); - case GenericConstantTypeKind::Named: - return Resolved.Elements.size() == 1 && isSizedType(Resolved.Elements.front(), Depth + 1); - case GenericConstantTypeKind::Invalid: - case GenericConstantTypeKind::Void: - case GenericConstantTypeKind::Label: - case GenericConstantTypeKind::Token: - case GenericConstantTypeKind::Metadata: - case GenericConstantTypeKind::Function: - return false; - } - return false; -} - -[[nodiscard]] bool signedIntegerLess(const 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 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 { - 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 verifyConstant(const GenericConstantData &Value, std::size_t Depth, - std::size_t &Nodes) { - if (Depth > MaximumVerificationDepth || ++Nodes > MaximumVerificationNodes) { - return makeUnexpected(ErrorCode::VerificationError, - "generic constant value exceeds structural limits"); - } - std::size_t TypeNodes = 0; - auto TypeResult = verifyType(Value.Type, 0, TypeNodes); - if (!TypeResult) - return TypeResult; - const GenericConstantType &EffectiveType = resolvedType(Value.Type); - - const auto RequireEmptyScalarPayload = [&]() -> Expected { - 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( - GenericConstantExpressionFlag::NoUnsignedWrap)) | - (std::uint8_t{1} << static_cast(GenericConstantExpressionFlag::NoSignedWrap)); - const std::uint8_t GEPFlags = - (std::uint8_t{1} << static_cast(GenericConstantExpressionFlag::InBounds)) | - (std::uint8_t{1} << static_cast( - GenericConstantExpressionFlag::NoUnsignedSignedWrap)) | - (std::uint8_t{1} << static_cast( - 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 { - 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 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::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::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::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::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::structure(std::vector 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::function(GenericConstantType Return, - std::vector 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::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 GenericConstantType::verify() const { - std::size_t Nodes = 0; - return verifyType(*this, 0, Nodes); -} - -Expected 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::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::byteString(GenericConstantType ValueType, - std::vector 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 GenericConstantData::verify() const { - std::size_t Nodes = 0; - return verifyConstant(*this, 0, Nodes); -} - -bool GenericConstantData::referencesSymbol() const noexcept { - switch (Kind) { - case GenericConstantKind::GlobalAddress: - case GenericConstantKind::BlockAddress: - case GenericConstantKind::DSOLocalEquivalent: - case GenericConstantKind::NoCFI: - return true; - default: - break; - } - for (const GenericConstantData &Element : Elements) { - if (Element.referencesSymbol()) - return true; - } - return false; -} - -} // namespace rcc diff --git a/backend/lib/CodeGen/MIRParser/MIParser.cpp b/backend/lib/CodeGen/MIRParser/MIParser.cpp index 183e6b3..0015b02 100644 --- a/backend/lib/CodeGen/MIRParser/MIParser.cpp +++ b/backend/lib/CodeGen/MIRParser/MIParser.cpp @@ -3,6 +3,7 @@ #include "rcc/CodeGen/MIRParser/MIParser.h" #include "MILexer.h" +#include "rcc/AsmParser/LLParser.h" #include "rcc/CodeGen/GenericOpcodes.h" #include "rcc/CodeGen/InlineAsm.h" #include "rcc/CodeGen/InstructionDescriptor.h" @@ -17,7 +18,6 @@ #include "rcc/CodeGen/TargetDescription.h" #include "rcc/CodeGen/TargetMIRTextInfo.h" #include "rcc/CodeGen/TargetRegisterInfo.h" -#include "rcc/MIR/LLVMConstantSyntax.h" #include #include diff --git a/backend/lib/CodeGen/MIRParser/MIRParser.cpp b/backend/lib/CodeGen/MIRParser/MIRParser.cpp index e22f1bc..3b89491 100644 --- a/backend/lib/CodeGen/MIRParser/MIRParser.cpp +++ b/backend/lib/CodeGen/MIRParser/MIRParser.cpp @@ -18,7 +18,7 @@ #include "rcc/CodeGen/TargetDescription.h" #include "rcc/CodeGen/TargetMIRTextInfo.h" #include "rcc/CodeGen/TargetRegisterInfo.h" -#include "rcc/MIR/LLVMConstantSyntax.h" +#include "rcc/IR/DataLayout.h" #include #include @@ -74,11 +74,8 @@ std::string_view MIRDocumentParser::effectiveDataLayout() const noexcept { return {}; } -LLVMConstantSyntaxOptions MIRDocumentParser::pointerLayoutOptions() const noexcept { - return { - .DataLayout = effectiveDataLayout(), - .NamedTypes = {}, - }; +Expected MIRDocumentParser::dataLayout() const { + return DataLayout::parse(effectiveDataLayout()); } bool MIRDocumentParser::requireKind(const yaml::Node &Node, yaml::NodeKind Kind, @@ -160,7 +157,13 @@ MIRDocumentParseResult MIRDocumentParser::run() { } if (!effectiveDataLayout().empty()) { - auto PointerSize = llvmPointerSizeInBits(0, pointerLayoutOptions()); + auto Layout = dataLayout(); + if (!Layout) { + static_cast( + importError(Stream->documents()[FirstContent].Range, Layout.error(), "mir-data-layout")); + return std::move(Result); + } + auto PointerSize = Layout->getPointerSizeInBits(); if (!PointerSize) { static_cast(importError(Stream->documents()[FirstContent].Range, PointerSize.error(), "mir-data-layout")); diff --git a/backend/lib/CodeGen/MIRPrinter.cpp b/backend/lib/CodeGen/MIRPrinter.cpp index ea201b9..d8a6945 100644 --- a/backend/lib/CodeGen/MIRPrinter.cpp +++ b/backend/lib/CodeGen/MIRPrinter.cpp @@ -24,7 +24,7 @@ #include "rcc/CodeGen/TargetDescription.h" #include "rcc/CodeGen/TargetMIRTextInfo.h" #include "rcc/CodeGen/TargetRegisterInfo.h" -#include "rcc/MIR/LLVMConstantSyntax.h" +#include "rcc/IR/DataLayout.h" #include #include @@ -424,10 +424,9 @@ MIRDocumentPrinter::verifyOmittedEmbeddedIRLayout(const MachineFunction &Functio "DataLayout"); } - const LLVMConstantSyntaxOptions LayoutOptions{ - .DataLayout = Options.DataLayout, - .NamedTypes = {}, - }; + auto Layout = DataLayout::parse(Options.DataLayout); + if (!Layout) + return std::unexpected(Layout.error()); for (const VirtualRegisterInfo &RegisterInfo : Function.registerInfo().virtualRegisters()) { if (RegisterInfo.IsTombstone || !RegisterInfo.Type || !RegisterInfo.Type->isPointerOrPointerVector()) { @@ -438,7 +437,7 @@ MIRDocumentPrinter::verifyOmittedEmbeddedIRLayout(const MachineFunction &Functio return makeUnexpected(ErrorCode::VerificationError, "pointer virtual register has no address space"); } - auto PointerSize = llvmPointerSizeInBits(*AddressSpace, LayoutOptions); + auto PointerSize = Layout->getPointerSizeInBits(*AddressSpace); if (!PointerSize) { return makeUnexpected(ErrorCode::InvalidArgument, "printer DataLayout cannot determine pointer width: " + @@ -457,7 +456,7 @@ MIRDocumentPrinter::verifyOmittedEmbeddedIRLayout(const MachineFunction &Functio const auto *Generic = std::get_if(&Description->Value); if (Generic == nullptr) continue; - auto StorageSize = llvmConstantStorageSize(Generic->Type, LayoutOptions); + auto StorageSize = Layout->getTypeAllocSize(Generic->Type); if (!StorageSize) { return makeUnexpected(ErrorCode::InvalidArgument, "printer DataLayout cannot lay out generic constant: " + @@ -1085,7 +1084,7 @@ Expected MIRDocumentPrinter::printConstants(const MachineFunction &Functio Expected Value = makeUnexpected(ErrorCode::Unsupported, "constant-pool kind is not MIR serializable"); if (const auto *Generic = std::get_if(&Description->Value)) { - Value = printLLVMConstant(*Generic); + Value = Generic->toLLVMString(); } else if (const auto *TargetConstantValue = std::get_if(&Description->Value)) { IsTargetSpecific = true; Value = Target.mirText().printTargetConstant(*TargetConstantValue); @@ -2264,7 +2263,7 @@ Expected BodyPrinter::printOperand(const MachineOperand &Operand, bool IsE auto Constant = GenericConstantData::floating(Bits); if (!Constant) return std::unexpected(Constant.error()); - auto Text = printLLVMConstant(*Constant); + auto Text = Constant->toLLVMString(); if (!Text) return std::unexpected(Text.error()); Output.append(*Text); @@ -2620,7 +2619,7 @@ Expected BodyPrinter::printMemoryBase(const MemoryBase &Base, std::int64_t return makeUnexpected(ErrorCode::VerificationError, "IR-constant memory base has no constant value"); } - auto Printed = printLLVMConstant(*Constant); + auto Printed = Constant->toLLVMString(); if (!Printed) return std::unexpected(Printed.error()); Output.push_back('`'); diff --git a/backend/lib/CodeGen/MIRYamlMapping.cpp b/backend/lib/CodeGen/MIRYamlMapping.cpp index 9ca8cad..b68761d 100644 --- a/backend/lib/CodeGen/MIRYamlMapping.cpp +++ b/backend/lib/CodeGen/MIRYamlMapping.cpp @@ -3,6 +3,7 @@ #include "rcc/CodeGen/MIRYamlMapping.h" #include "MIRParser/MILexer.h" +#include "rcc/AsmParser/LLParser.h" #include "rcc/CodeGen/MachineBasicBlock.h" #include "rcc/CodeGen/MachineConstantPool.h" #include "rcc/CodeGen/MachineFrameInfo.h" @@ -18,7 +19,6 @@ #include "rcc/CodeGen/TargetDescription.h" #include "rcc/CodeGen/TargetMIRTextInfo.h" #include "rcc/CodeGen/TargetRegisterInfo.h" -#include "rcc/MIR/LLVMConstantSyntax.h" #include #include @@ -259,8 +259,8 @@ bool MIRDocumentParser::parseConstants(const yaml::Node &Root, MachineFunction & std::string_view{"alignment"}, std::string_view{"isTargetSpecific"}, }; - std::vector NamedTypes; - const std::string_view DataLayout = effectiveDataLayout(); + std::vector NamedTypes; + const std::string_view DataLayoutText = effectiveDataLayout(); if (ActiveDocument->EmbeddedIR) { const auto Definitions = ActiveDocument->EmbeddedIR->typeDefinitions(); NamedTypes.reserve(Definitions.size()); @@ -270,6 +270,9 @@ bool MIRDocumentParser::parseConstants(const yaml::Node &Root, MachineFunction & .NumericName = Definition.NumericName}); } } + auto Layout = DataLayout::parse(DataLayoutText); + if (!Layout) + return importError(Constants->range(), Layout.error(), "mir-data-layout"); std::unordered_set IDs; for (const yaml::NodeID ConstantID : Constants->sequence()) { const yaml::Node *Constant = node(ConstantID); @@ -328,14 +331,13 @@ bool MIRDocumentParser::parseConstants(const yaml::Node &Root, MachineFunction & std::string_view ValueText; if (scalar(*ValueNode, ValueText, "LLVM constant")) return true; - const LLVMConstantSyntaxOptions ConstantOptions{ - .DataLayout = DataLayout, + const LLParserOptions ConstantOptions{ .NamedTypes = NamedTypes, }; auto Data = parseLLVMConstant(trim(ValueText), ConstantOptions); if (!Data) return importError(ValueNode->range(), Data.error(), "mir-llvm-constant"); - auto Size = llvmConstantStorageSize(Data->Type, ConstantOptions); + auto Size = Layout->getTypeAllocSize(Data->Type); if (!Size) return importError(ValueNode->range(), Size.error(), "mir-llvm-constant"); @@ -346,11 +348,14 @@ bool MIRDocumentParser::parseConstants(const yaml::Node &Root, MachineFunction & if (alignment(*AlignmentNode, Description.Align, true)) return true; } else { - auto Preferred = llvmConstantPreferredAlignment( - std::get(Description.Value).Type, ConstantOptions); + auto Preferred = + Layout->getPrefTypeAlign(std::get(Description.Value).Type); if (!Preferred) return importError(ValueNode->range(), Preferred.error(), "mir-llvm-constant"); - Description.Align = *Preferred; + auto PreferredAlignment = Alignment::create(*Preferred); + if (!PreferredAlignment) + return importError(ValueNode->range(), PreferredAlignment.error(), "mir-llvm-constant"); + Description.Align = *PreferredAlignment; } auto Added = Function.constantPool().addEntry(std::move(Description)); if (!Added) @@ -2219,9 +2224,11 @@ bool MIRDocumentParser::parseBody(const yaml::Node &Root, MachineFunction &Funct } if (!effectiveDataLayout().empty()) { - const LLVMConstantSyntaxOptions PointerOptions = pointerLayoutOptions(); - Context.PointerSizeInBits = [PointerOptions](std::uint32_t AddressSpace) { - return llvmPointerSizeInBits(AddressSpace, PointerOptions); + auto Layout = dataLayout(); + if (!Layout) + return importError(BodyRange, Layout.error(), "mir-data-layout"); + Context.PointerSizeInBits = [Layout = std::move(*Layout)](std::uint32_t AddressSpace) { + return Layout.getPointerSizeInBits(AddressSpace); }; } Context.SyncScopes = ActiveDocument->SyncScopes; 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 +#include +#include +#include +#include +#include +#include +#include + +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(RawCharacter); + if (Character >= 0x20U && Character <= 0x7eU && Character != '"' && Character != '\\') { + Result.push_back(static_cast(Character)); + continue; + } + Result.push_back('\\'); + Result.push_back(HexDigits[Character >> 4U]); + Result.push_back(HexDigits[Character & 0xfU]); + } + Result.push_back('"'); + return Result; +} + +[[nodiscard]] Expected printType(const GenericConstantType &Type); + +[[nodiscard]] Expected printTypeList(std::span 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 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 printValue(const GenericConstantData &Value, bool IncludeType); + +[[nodiscard]] Expected appendTypedOperands(std::string &Output, + std::span 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 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(Byte); + if (Character >= 0x20U && Character <= 0x7eU && Character != '"' && Character != '\\') { + Result.push_back(static_cast(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(InRangeStart.words()[0]))); + Result.append(", "); + Result.append(std::to_string(std::bit_cast(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 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 + "$" + "$" +) +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 +#include +#include +#include +#include +#include +#include + +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 +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(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 +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 +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::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 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 { + 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 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 { + 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( + GenericConstantExpressionFlag::NoUnsignedWrap)) | + (std::uint8_t{1} << static_cast(GenericConstantExpressionFlag::NoSignedWrap)); + const std::uint8_t GEPFlags = + (std::uint8_t{1} << static_cast(GenericConstantExpressionFlag::InBounds)) | + (std::uint8_t{1} << static_cast( + GenericConstantExpressionFlag::NoUnsignedSignedWrap)) | + (std::uint8_t{1} << static_cast( + 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 { + 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 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::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::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::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::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::structure(std::vector 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::function(GenericConstantType Return, + std::vector 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::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 GenericConstantType::verify() const { + std::size_t Nodes = 0; + return verifyType(*this, 0, Nodes); +} + +Expected 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::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::byteString(GenericConstantType ValueType, + std::vector 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 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 checkedMultiply(std::uint64_t Left, std::uint64_t Right, + std::string_view Description) { + if (Left != 0 && Right > std::numeric_limits::max() / Left) + return makeUnexpected(ErrorCode::Unsupported, std::string(Description) + " is too large"); + return Left * Right; +} + +[[nodiscard]] Expected checkedAdd(std::uint64_t Left, std::uint64_t Right, + std::string_view Description) { + if (Right > std::numeric_limits::max() - Left) + return makeUnexpected(ErrorCode::Unsupported, std::string(Description) + " is too large"); + return Left + Right; +} + +[[nodiscard]] Expected 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::max() - Mask) + return makeUnexpected(ErrorCode::Unsupported, "LLVM type allocation size is too large"); + return (Value + Mask) & ~Mask; +} + +} // namespace + +Expected 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 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 DataLayout::split(std::string_view Text, char Separator) { + std::vector 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::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(AddressSpace), + .SizeInBits = static_cast(*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(*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(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::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::getTypeLayout(const GenericConstantType &Type, + std::vector &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 { + 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(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(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(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::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::min( + *TotalBits, std::numeric_limits::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(AggregateAlignment.Preferred, 1)}; + } + std::uint64_t Offset = 0; + std::uint64_t ABIAlignment = std::max(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 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 DataLayout::getTypeAllocSize(const GenericConstantType &Type) const { + std::vector NamedTypeStack; + auto Layout = getTypeLayout(Type, NamedTypeStack, 0); + if (!Layout) + return std::unexpected(Layout.error()); + return Layout->AllocationSize; +} + +Expected DataLayout::getABITypeAlign(const GenericConstantType &Type) const { + std::vector NamedTypeStack; + auto Layout = getTypeLayout(Type, NamedTypeStack, 0); + if (!Layout) + return std::unexpected(Layout.error()); + return Layout->ABIAlignment; +} + +Expected DataLayout::getPrefTypeAlign(const GenericConstantType &Type) const { + std::vector NamedTypeStack; + auto Layout = getTypeLayout(Type, NamedTypeStack, 0); + if (!Layout) + return std::unexpected(Layout.error()); + return Layout->PreferredAlignment; +} + +Expected DataLayout::getPointerSizeInBits(std::uint32_t AddressSpace) const { + auto Pointer = getPointerSpec(AddressSpace); + if (!Pointer) + return std::unexpected(Pointer.error()); + return Pointer->SizeInBits; +} + +} // namespace rcc diff --git a/backend/lib/MIR/CMakeLists.txt b/backend/lib/MIR/CMakeLists.txt index cf5dbcd..cbdf201 100644 --- a/backend/lib/MIR/CMakeLists.txt +++ b/backend/lib/MIR/CMakeLists.txt @@ -3,7 +3,6 @@ set( RCCBackendMIRSources EmbeddedIR.cpp - LLVMConstantSyntax.cpp "${RCC_BACKEND_SOURCE_DIR}/lib/CodeGen/MIRParser/MILexer.cpp" "${RCC_BACKEND_SOURCE_DIR}/lib/CodeGen/MIRParser/MIParser.cpp" "${RCC_BACKEND_SOURCE_DIR}/lib/CodeGen/MIRParser/MIRParser.cpp" @@ -17,7 +16,6 @@ set( "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/MIRPrinter.h" "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/MIRYamlMapping.h" "${RCC_BACKEND_SOURCE_DIR}/include/rcc/MIR/EmbeddedIR.h" - "${RCC_BACKEND_SOURCE_DIR}/include/rcc/MIR/LLVMConstantSyntax.h" ) add_library(RCCBackendMIR ${RCCBackendMIRSources}) @@ -31,7 +29,13 @@ target_include_directories( "$" "$" ) -target_link_libraries(RCCBackendMIR PUBLIC RCCBackend::CodeGen) +target_link_libraries( + RCCBackendMIR + PUBLIC + RCCBackend::AsmParser + RCCBackend::CodeGen + RCCBackend::IR +) set_target_properties( RCCBackendMIR @@ -50,7 +54,6 @@ install( rcc_backend_register_format_files( "${CMAKE_CURRENT_SOURCE_DIR}/EmbeddedIR.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/LLVMConstantSyntax.cpp" "${RCC_BACKEND_SOURCE_DIR}/lib/CodeGen/MIRParser/MILexer.cpp" "${RCC_BACKEND_SOURCE_DIR}/lib/CodeGen/MIRParser/MILexer.h" "${RCC_BACKEND_SOURCE_DIR}/lib/CodeGen/MIRParser/MIParser.cpp" @@ -61,7 +64,6 @@ rcc_backend_register_format_files( ) rcc_backend_register_tidy_files( "${CMAKE_CURRENT_SOURCE_DIR}/EmbeddedIR.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/LLVMConstantSyntax.cpp" "${RCC_BACKEND_SOURCE_DIR}/lib/CodeGen/MIRParser/MILexer.cpp" "${RCC_BACKEND_SOURCE_DIR}/lib/CodeGen/MIRParser/MIParser.cpp" "${RCC_BACKEND_SOURCE_DIR}/lib/CodeGen/MIRParser/MIRParser.cpp" diff --git a/backend/lib/MIR/LLVMConstantSyntax.cpp b/backend/lib/MIR/LLVMConstantSyntax.cpp deleted file mode 100644 index 4ee231d..0000000 --- a/backend/lib/MIR/LLVMConstantSyntax.cpp +++ /dev/null @@ -1,2025 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "rcc/MIR/LLVMConstantSyntax.h" - -#include "rcc/ADT/APFloat.h" -#include "rcc/ADT/APInt.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace rcc::mir { -namespace { - -struct ParsedName final { - std::string Value; - bool Numeric = false; -}; - -[[nodiscard]] bool isSpace(char Character) noexcept { - return Character == ' ' || Character == '\t' || Character == '\r' || Character == '\n'; -} - -[[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 isHexDigit(char Character) noexcept { - return (Character >= '0' && Character <= '9') || (Character >= 'a' && Character <= 'f') || - (Character >= 'A' && Character <= 'F'); -} - -[[nodiscard]] unsigned hexDigitValue(char Character) noexcept { - if (Character >= '0' && Character <= '9') - return static_cast(Character - '0'); - if (Character >= 'a' && Character <= 'f') - return static_cast(Character - 'a') + 10U; - return static_cast(Character - 'A') + 10U; -} - -[[nodiscard]] std::optional floatingSemantics(std::string_view Name) noexcept { - if (Name == "half") - return FloatSemantics::IEEEHalf; - if (Name == "bfloat") - return FloatSemantics::BFloat; - if (Name == "float") - return FloatSemantics::IEEESingle; - if (Name == "double") - return FloatSemantics::IEEEDouble; - if (Name == "fp128") - return FloatSemantics::IEEEQuad; - if (Name == "ppc_fp128") - return FloatSemantics::PPCDoubleDouble; - if (Name == "x86_fp80") - return FloatSemantics::X87DoubleExtended; - return std::nullopt; -} - -[[nodiscard]] Expected checkedMultiply(std::uint64_t Left, std::uint64_t Right, - std::string_view Description) { - if (Left != 0 && Right > std::numeric_limits::max() / Left) - return makeUnexpected(ErrorCode::Unsupported, std::string(Description) + " is too large"); - return Left * Right; -} - -[[nodiscard]] Expected checkedAdd(std::uint64_t Left, std::uint64_t Right, - std::string_view Description) { - if (Right > std::numeric_limits::max() - Left) - return makeUnexpected(ErrorCode::Unsupported, std::string(Description) + " is too large"); - return Left + Right; -} - -[[nodiscard]] Expected 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::max() - Mask) - return makeUnexpected(ErrorCode::Unsupported, "LLVM type allocation size is too large"); - return (Value + Mask) & ~Mask; -} - -[[nodiscard]] const GenericConstantType &resolvedType(const GenericConstantType &Type) noexcept { - const GenericConstantType *Resolved = &Type; - std::size_t Depth = 0; - while (Resolved->Kind == GenericConstantTypeKind::Named && Resolved->Elements.size() == 1 && - Depth++ < 1024) { - Resolved = &Resolved->Elements.front(); - } - return *Resolved; -} - -class ConstantParser final { -public: - ConstantParser(std::string_view SourceText, LLVMConstantSyntaxOptions ParserOptions, - std::vector ResolvingTypes = {}) - : Text(SourceText), Options(ParserOptions), ResolvingNamedTypes(std::move(ResolvingTypes)) {} - - [[nodiscard]] Expected parse(); - [[nodiscard]] Expected parseTypeOnly(); - -private: - template [[nodiscard]] Expected fail(std::string_view Message) const { - return makeUnexpected(ErrorCode::ParseError, "LLVM constant at byte " + - std::to_string(Position) + ": " + - std::string(Message)); - } - - void skipSpace() noexcept { - while (Position < Text.size() && isSpace(Text[Position])) - ++Position; - } - - [[nodiscard]] bool atEnd() { - skipSpace(); - return Position == Text.size(); - } - - [[nodiscard]] bool consume(char Character) { - skipSpace(); - if (Position >= Text.size() || Text[Position] != Character) - return false; - ++Position; - return true; - } - - [[nodiscard]] bool consumeWord(std::string_view Word) { - skipSpace(); - if (!Text.substr(Position).starts_with(Word)) - return false; - const std::size_t End = Position + Word.size(); - if (End < Text.size() && isIdentifierBody(Text[End])) - return false; - Position = End; - return true; - } - - [[nodiscard]] std::optional peekWord() { - skipSpace(); - if (Position >= Text.size() || !isIdentifierStart(Text[Position])) - return std::nullopt; - std::size_t End = Position + 1; - while (End < Text.size() && isIdentifierBody(Text[End])) - ++End; - return Text.substr(Position, End - Position); - } - - [[nodiscard]] Expected expect(char Character, std::string_view Description) { - if (!consume(Character)) - return fail("expected " + std::string(Description)); - return {}; - } - - [[nodiscard]] Expected expectWord(std::string_view Word) { - if (!consumeWord(Word)) - return fail("expected '" + std::string(Word) + "'"); - return {}; - } - - [[nodiscard]] Expected parseUnsigned(); - [[nodiscard]] Expected parseName(char Prefix); - [[nodiscard]] Expected> parseByteString(); - [[nodiscard]] Expected parseType(std::size_t Depth = 0); - [[nodiscard]] Expected resolveNamedType(GenericConstantType &Type, std::size_t Depth); - [[nodiscard]] Expected parsePrimaryType(std::size_t Depth); - [[nodiscard]] Expected parseTypedValue(std::size_t Depth); - [[nodiscard]] Expected parseValue(GenericConstantType Type, - std::size_t Depth); - [[nodiscard]] Expected - parseAggregate(GenericConstantType Type, char Open, char Close, bool Packed, std::size_t Depth); - [[nodiscard]] Expected parseFloatingValue(GenericConstantType Type, - std::size_t Depth); - [[nodiscard]] Expected - parseExpression(GenericConstantType Type, std::string_view Opcode, std::size_t Depth); - - [[nodiscard]] Expected accountNode(std::size_t Depth) { - if (Depth > Options.MaximumDepth || ++Nodes > Options.MaximumNodes) - return fail("constant exceeds configured structural limits"); - return {}; - } - - [[nodiscard]] Expected parseValueToken(); - - std::string_view Text; - LLVMConstantSyntaxOptions Options; - std::size_t Position = 0; - std::size_t Nodes = 0; - std::vector ResolvingNamedTypes; -}; - -Expected ConstantParser::parseUnsigned() { - skipSpace(); - const std::size_t Begin = Position; - while (Position < Text.size() && Text[Position] >= '0' && Text[Position] <= '9') - ++Position; - if (Begin == Position) - return fail("expected an unsigned integer"); - std::uint64_t Value = 0; - const auto Parsed = std::from_chars(Text.data() + Begin, Text.data() + Position, Value, 10); - if (Parsed.ec != std::errc{}) - return fail("unsigned integer is out of range"); - return Value; -} - -Expected ConstantParser::parseName(char Prefix) { - skipSpace(); - if (Position >= Text.size() || Text[Position] != Prefix) - return fail(std::string("expected '") + Prefix + "' identifier"); - ++Position; - if (Position >= Text.size()) - return fail("identifier is empty"); - - ParsedName Result; - if (Text[Position] == '"') { - ++Position; - while (Position < Text.size() && Text[Position] != '"') { - if (Text[Position] != '\\') { - if (static_cast(Text[Position]) < 0x20U) - return fail("identifier contains a control character"); - Result.Value.push_back(Text[Position++]); - continue; - } - ++Position; - if (Position + 1 >= Text.size() || !isHexDigit(Text[Position]) || - !isHexDigit(Text[Position + 1])) { - return fail("identifier escape requires two hexadecimal digits"); - } - Result.Value.push_back(static_cast((hexDigitValue(Text[Position]) << 4U) | - hexDigitValue(Text[Position + 1]))); - Position += 2; - } - if (Position >= Text.size()) - return fail("quoted identifier is unterminated"); - ++Position; - return Result; - } - - if (Text[Position] >= '0' && Text[Position] <= '9') { - Result.Numeric = true; - const std::size_t Begin = Position; - while (Position < Text.size() && Text[Position] >= '0' && Text[Position] <= '9') - ++Position; - Result.Value = std::string(Text.substr(Begin, Position - Begin)); - return Result; - } - if (!isIdentifierStart(Text[Position])) - return fail("identifier has an invalid first character"); - const std::size_t Begin = Position++; - while (Position < Text.size() && isIdentifierBody(Text[Position])) - ++Position; - Result.Value = std::string(Text.substr(Begin, Position - Begin)); - return Result; -} - -Expected> ConstantParser::parseByteString() { - skipSpace(); - if (Position >= Text.size() || Text[Position] != '"') - return fail>("expected an LLVM byte string"); - ++Position; - std::vector Bytes; - while (Position < Text.size() && Text[Position] != '"') { - const char Character = Text[Position++]; - if (Character != '\\') { - if (static_cast(Character) < 0x20U) - return fail>("byte string contains a control character"); - Bytes.push_back(static_cast(static_cast(Character))); - continue; - } - if (Position + 1 >= Text.size() || !isHexDigit(Text[Position]) || - !isHexDigit(Text[Position + 1])) { - return fail>("byte-string escape requires two hexadecimal digits"); - } - Bytes.push_back(static_cast((hexDigitValue(Text[Position]) << 4U) | - hexDigitValue(Text[Position + 1]))); - Position += 2; - } - if (Position >= Text.size()) - return fail>("LLVM byte string is unterminated"); - ++Position; - return Bytes; -} - -Expected ConstantParser::parsePrimaryType(std::size_t Depth) { - if (auto Limit = accountNode(Depth); !Limit) - return std::unexpected(Limit.error()); - skipSpace(); - - if (consume('[')) { - auto Count = parseUnsigned(); - if (!Count) - return std::unexpected(Count.error()); - if (auto Separator = expectWord("x"); !Separator) - return std::unexpected(Separator.error()); - auto Element = parseType(Depth + 1); - if (!Element) - return std::unexpected(Element.error()); - if (auto End = expect(']', "']' after array type"); !End) - return std::unexpected(End.error()); - return GenericConstantType::array(*Count, std::move(*Element)); - } - - if (consume('<')) { - if (consume('{')) { - std::vector Elements; - if (!consume('}')) { - while (true) { - auto Element = parseType(Depth + 1); - if (!Element) - return std::unexpected(Element.error()); - Elements.push_back(std::move(*Element)); - if (consume('}')) - break; - if (auto Comma = expect(',', "',' between packed struct members"); !Comma) - return std::unexpected(Comma.error()); - } - } - if (auto End = expect('>', "'>' after packed struct type"); !End) - return std::unexpected(End.error()); - return GenericConstantType::structure(std::move(Elements), true); - } - - const bool Scalable = consumeWord("vscale"); - if (Scalable) { - if (auto Separator = expectWord("x"); !Separator) - return std::unexpected(Separator.error()); - } - auto Count = parseUnsigned(); - if (!Count) - return std::unexpected(Count.error()); - if (auto Separator = expectWord("x"); !Separator) - return std::unexpected(Separator.error()); - auto Element = parseType(Depth + 1); - if (!Element) - return std::unexpected(Element.error()); - if (auto End = expect('>', "'>' after vector type"); !End) - return std::unexpected(End.error()); - return GenericConstantType::vector(*Count, std::move(*Element), Scalable); - } - - if (consume('{')) { - std::vector Elements; - if (!consume('}')) { - while (true) { - auto Element = parseType(Depth + 1); - if (!Element) - return std::unexpected(Element.error()); - Elements.push_back(std::move(*Element)); - if (consume('}')) - break; - if (auto Comma = expect(',', "',' between struct members"); !Comma) - return std::unexpected(Comma.error()); - } - } - return GenericConstantType::structure(std::move(Elements), false); - } - - skipSpace(); - if (Position < Text.size() && Text[Position] == '%') { - auto Name = parseName('%'); - if (!Name) - return std::unexpected(Name.error()); - return GenericConstantType::named(std::move(Name->Value), Name->Numeric); - } - - const auto Word = peekWord(); - if (!Word) - return fail("expected an LLVM IR type"); - Position += Word->size(); - if (*Word == "void") - return GenericConstantType::primitive(GenericConstantTypeKind::Void); - if (*Word == "label") - return GenericConstantType::primitive(GenericConstantTypeKind::Label); - if (*Word == "token") - return GenericConstantType::primitive(GenericConstantTypeKind::Token); - if (*Word == "metadata") - return GenericConstantType::primitive(GenericConstantTypeKind::Metadata); - if (*Word == "byte") - return GenericConstantType::byte(); - if (Word->starts_with('i')) { - std::uint64_t Width = 0; - const auto Parsed = std::from_chars(Word->data() + 1, Word->data() + Word->size(), Width, 10); - if (Word->size() == 1 || Parsed.ec != std::errc{} || - Parsed.ptr != Word->data() + Word->size() || - Width > std::numeric_limits::max()) { - return fail("integer type has an invalid bit width"); - } - return GenericConstantType::integer(static_cast(Width)); - } - if (const auto Semantics = floatingSemantics(*Word)) - return GenericConstantType::floating(*Semantics); - if (*Word == "ptr") { - std::uint32_t AddressSpace = 0; - if (consumeWord("addrspace")) { - if (auto Open = expect('(', "'(' after addrspace"); !Open) - return std::unexpected(Open.error()); - auto ParsedAddressSpace = parseUnsigned(); - if (!ParsedAddressSpace || *ParsedAddressSpace > std::numeric_limits::max()) - return fail("pointer address space is out of range"); - AddressSpace = static_cast(*ParsedAddressSpace); - if (auto Close = expect(')', "')' after address space"); !Close) - return std::unexpected(Close.error()); - } - return GenericConstantType::pointer(AddressSpace); - } - return fail("unsupported LLVM IR type '" + std::string(*Word) + "'"); -} - -Expected ConstantParser::parseType(std::size_t Depth) { - auto Type = parsePrimaryType(Depth); - if (!Type) - return std::unexpected(Type.error()); - - while (true) { - skipSpace(); - if (consume('(')) { - std::vector Parameters; - bool VarArg = false; - if (!consume(')')) { - while (true) { - skipSpace(); - if (Text.substr(Position).starts_with("...")) { - Position += 3; - VarArg = true; - if (auto End = expect(')', "')' after variadic function type"); !End) - return std::unexpected(End.error()); - break; - } - auto Parameter = parseType(Depth + 1); - if (!Parameter) - return std::unexpected(Parameter.error()); - Parameters.push_back(std::move(*Parameter)); - if (consume(')')) - break; - if (auto Comma = expect(',', "',' between function parameters"); !Comma) - return std::unexpected(Comma.error()); - } - } - Type = GenericConstantType::function(std::move(*Type), std::move(Parameters), VarArg); - if (!Type) - return std::unexpected(Type.error()); - continue; - } - - std::uint32_t AddressSpace = 0; - const std::size_t BeforeAddressSpace = Position; - if (consumeWord("addrspace")) { - if (auto Open = expect('(', "'(' after addrspace"); !Open) - return std::unexpected(Open.error()); - auto ParsedAddressSpace = parseUnsigned(); - if (!ParsedAddressSpace || *ParsedAddressSpace > std::numeric_limits::max()) - return fail("pointer address space is out of range"); - AddressSpace = static_cast(*ParsedAddressSpace); - if (auto Close = expect(')', "')' after address space"); !Close) - return std::unexpected(Close.error()); - } - if (consume('*')) { - Type = GenericConstantType::pointer(AddressSpace); - continue; - } - Position = BeforeAddressSpace; - break; - } - if (Type->Kind == GenericConstantTypeKind::Named) { - if (auto Resolved = resolveNamedType(*Type, Depth + 1); !Resolved) - return std::unexpected(Resolved.error()); - } - return Type; -} - -Expected ConstantParser::resolveNamedType(GenericConstantType &Type, std::size_t Depth) { - if (Type.Kind != GenericConstantTypeKind::Named || !Type.Elements.empty()) - return {}; - const auto Definition = - std::ranges::find_if(Options.NamedTypes, [&Type](const LLVMNamedTypeDefinition &Candidate) { - return Candidate.Name == Type.Name && Candidate.NumericName == Type.NumericName; - }); - if (Definition == Options.NamedTypes.end()) - return {}; - if (Depth > Options.MaximumDepth || - std::ranges::find(ResolvingNamedTypes, Type.Name) != ResolvingNamedTypes.end()) { - return fail("named LLVM type definition is recursively unsized"); - } - std::vector Stack = ResolvingNamedTypes; - Stack.push_back(Type.Name); - ConstantParser DefinitionParser(Definition->Definition, Options, std::move(Stack)); - auto Resolved = DefinitionParser.parseTypeOnly(); - if (!Resolved) - return std::unexpected(Resolved.error()); - Type.Elements.push_back(std::move(*Resolved)); - return {}; -} - -Expected ConstantParser::parseValueToken() { - skipSpace(); - const std::size_t Begin = Position; - unsigned ParenthesisDepth = 0; - while (Position < Text.size()) { - const char Character = Text[Position]; - if (Character == '(') { - ++ParenthesisDepth; - ++Position; - continue; - } - if (Character == ')') { - if (ParenthesisDepth == 0) - break; - --ParenthesisDepth; - ++Position; - continue; - } - if (ParenthesisDepth == 0 && (isSpace(Character) || Character == ',' || Character == ']' || - Character == '}' || Character == '>')) { - break; - } - ++Position; - if (Position - Begin > Options.MaximumTokenLength) - return fail("value token exceeds the configured length limit"); - } - if (Begin == Position) - return fail("expected an LLVM constant value"); - return Text.substr(Begin, Position - Begin); -} - -Expected ConstantParser::parseFloatingValue(GenericConstantType Type, - std::size_t Depth) { - const GenericConstantType &EffectiveType = resolvedType(Type); - if (EffectiveType.Kind != GenericConstantTypeKind::Floating) - return fail("floating literal requires a floating-point type"); - auto Token = parseValueToken(); - if (!Token) - return std::unexpected(Token.error()); - auto Bits = APFloat::parse(EffectiveType.Semantics, *Token); - if (!Bits) - return fail(Bits.error().message()); - GenericConstantData Value; - Value.Type = std::move(Type); - Value.Kind = GenericConstantKind::Floating; - Value.FloatingValue = *Bits; - if (auto Limit = accountNode(Depth); !Limit) - return std::unexpected(Limit.error()); - return Value; -} - -Expected ConstantParser::parseAggregate(GenericConstantType Type, char Open, - char Close, bool Packed, - std::size_t Depth) { - if (!consume(Open)) - return fail(std::string("expected '") + Open + "' for aggregate"); - if (Packed && !consume('{')) - return fail("expected '{' in packed struct constant"); - - GenericConstantData Value; - Value.Type = std::move(Type); - Value.Kind = GenericConstantKind::Aggregate; - const char InnerClose = Packed ? '}' : Close; - if (!consume(InnerClose)) { - while (true) { - auto Element = parseTypedValue(Depth + 1); - if (!Element) - return std::unexpected(Element.error()); - Value.Elements.push_back(std::move(*Element)); - if (consume(InnerClose)) - break; - if (auto Comma = expect(',', "',' between aggregate elements"); !Comma) - return std::unexpected(Comma.error()); - } - } - if (Packed) { - if (auto End = expect('>', "'>' after packed struct constant"); !End) - return std::unexpected(End.error()); - } - auto Verification = Value.verify(); - if (!Verification) - return fail(Verification.error().message()); - return Value; -} - -Expected ConstantParser::parseTypedValue(std::size_t Depth) { - auto Type = parseType(Depth + 1); - if (!Type) - return std::unexpected(Type.error()); - return parseValue(std::move(*Type), Depth + 1); -} - -Expected ConstantParser::parseExpression(GenericConstantType Type, - std::string_view Opcode, - std::size_t Depth) { - GenericConstantData Value; - Value.Type = std::move(Type); - Value.Kind = GenericConstantKind::Expression; - - const auto SetOpcode = [&](GenericConstantExpressionOpcode ParsedOpcode) { - Value.ExpressionOpcode = ParsedOpcode; - }; - if (Opcode == "trunc") - SetOpcode(GenericConstantExpressionOpcode::Trunc); - else if (Opcode == "bitcast") - SetOpcode(GenericConstantExpressionOpcode::BitCast); - else if (Opcode == "addrspacecast") - SetOpcode(GenericConstantExpressionOpcode::AddressSpaceCast); - else if (Opcode == "inttoptr") - SetOpcode(GenericConstantExpressionOpcode::IntToPtr); - else if (Opcode == "ptrtoaddr") - SetOpcode(GenericConstantExpressionOpcode::PtrToAddr); - else if (Opcode == "ptrtoint") - SetOpcode(GenericConstantExpressionOpcode::PtrToInt); - else if (Opcode == "add") - SetOpcode(GenericConstantExpressionOpcode::Add); - else if (Opcode == "sub") - SetOpcode(GenericConstantExpressionOpcode::Sub); - else if (Opcode == "xor") - SetOpcode(GenericConstantExpressionOpcode::Xor); - else if (Opcode == "getelementptr") - SetOpcode(GenericConstantExpressionOpcode::GetElementPtr); - else if (Opcode == "shufflevector") - SetOpcode(GenericConstantExpressionOpcode::ShuffleVector); - else if (Opcode == "insertelement") - SetOpcode(GenericConstantExpressionOpcode::InsertElement); - else if (Opcode == "extractelement") - SetOpcode(GenericConstantExpressionOpcode::ExtractElement); - else if (Opcode == "splat") - SetOpcode(GenericConstantExpressionOpcode::Splat); - else - return fail("unsupported constant expression '" + std::string(Opcode) + - "'"); - - 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; - const bool IsBinary = Value.ExpressionOpcode == GenericConstantExpressionOpcode::Add || - Value.ExpressionOpcode == GenericConstantExpressionOpcode::Sub || - Value.ExpressionOpcode == GenericConstantExpressionOpcode::Xor; - - if (IsBinary) { - while (true) { - if (consumeWord("nuw")) { - Value.ExpressionFlags.set(GenericConstantExpressionFlag::NoUnsignedWrap); - continue; - } - if (consumeWord("nsw")) { - Value.ExpressionFlags.set(GenericConstantExpressionFlag::NoSignedWrap); - continue; - } - break; - } - } else if (Value.ExpressionOpcode == GenericConstantExpressionOpcode::GetElementPtr) { - while (true) { - if (consumeWord("inbounds")) { - Value.ExpressionFlags.set(GenericConstantExpressionFlag::InBounds); - continue; - } - if (consumeWord("nusw")) { - Value.ExpressionFlags.set(GenericConstantExpressionFlag::NoUnsignedSignedWrap); - continue; - } - if (consumeWord("nuw")) { - Value.ExpressionFlags.set(GenericConstantExpressionFlag::GEPNoUnsignedWrap); - continue; - } - break; - } - if (consumeWord("inrange")) { - if (auto Open = expect('(', "'(' after inrange"); !Open) - return std::unexpected(Open.error()); - auto StartToken = parseValueToken(); - if (!StartToken) - return std::unexpected(StartToken.error()); - auto Start = APInt::parseDecimal(64, *StartToken); - if (!Start) - return fail("invalid inrange start"); - if (auto Comma = expect(',', "',' in inrange interval"); !Comma) - return std::unexpected(Comma.error()); - auto EndToken = parseValueToken(); - if (!EndToken) - return std::unexpected(EndToken.error()); - auto End = APInt::parseDecimal(64, *EndToken); - if (!End) - return fail("invalid inrange end"); - if (auto Close = expect(')', "')' after inrange interval"); !Close) - return std::unexpected(Close.error()); - Value.InRangeStart = std::move(*Start); - Value.InRangeEnd = std::move(*End); - } - } - - if (auto Open = expect('(', "'(' after constant-expression opcode"); !Open) - return std::unexpected(Open.error()); - if (Value.ExpressionOpcode == GenericConstantExpressionOpcode::GetElementPtr) { - auto SourceType = parseType(Depth + 1); - if (!SourceType) - return std::unexpected(SourceType.error()); - Value.SourceElementType = std::move(*SourceType); - if (auto Comma = expect(',', "',' after getelementptr source type"); !Comma) - return std::unexpected(Comma.error()); - } - - auto First = parseTypedValue(Depth + 1); - if (!First) - return std::unexpected(First.error()); - Value.Elements.push_back(std::move(*First)); - if (IsCast) { - if (auto To = expectWord("to"); !To) - return std::unexpected(To.error()); - auto Destination = parseType(Depth + 1); - if (!Destination) - return std::unexpected(Destination.error()); - if (*Destination != Value.Type) - return fail("constant-expression destination type does not match value"); - } else { - while (consume(',')) { - auto Operand = parseTypedValue(Depth + 1); - if (!Operand) - return std::unexpected(Operand.error()); - Value.Elements.push_back(std::move(*Operand)); - } - } - if (auto Close = expect(')', "')' after constant expression"); !Close) - return std::unexpected(Close.error()); - - const std::size_t RequiredOperands = [&]() -> std::size_t { - switch (Value.ExpressionOpcode) { - case GenericConstantExpressionOpcode::Trunc: - case GenericConstantExpressionOpcode::BitCast: - case GenericConstantExpressionOpcode::AddressSpaceCast: - case GenericConstantExpressionOpcode::IntToPtr: - case GenericConstantExpressionOpcode::PtrToAddr: - case GenericConstantExpressionOpcode::PtrToInt: - case GenericConstantExpressionOpcode::Splat: - return 1; - case GenericConstantExpressionOpcode::Add: - case GenericConstantExpressionOpcode::Sub: - case GenericConstantExpressionOpcode::Xor: - case GenericConstantExpressionOpcode::ExtractElement: - return 2; - case GenericConstantExpressionOpcode::ShuffleVector: - case GenericConstantExpressionOpcode::InsertElement: - return 3; - case GenericConstantExpressionOpcode::GetElementPtr: - return 1; - case GenericConstantExpressionOpcode::Invalid: - return 0; - } - return 0; - }(); - if (Value.ExpressionOpcode == GenericConstantExpressionOpcode::GetElementPtr) { - if (Value.Elements.empty() || - resolvedType(Value.Elements.front().Type).Kind != GenericConstantTypeKind::Pointer || - resolvedType(Value.Type).Kind != GenericConstantTypeKind::Pointer) { - return fail("getelementptr constant has no base pointer"); - } - } else if (Value.Elements.size() != RequiredOperands) { - return fail("constant expression has the wrong operand count"); - } - auto Verification = Value.verify(); - if (!Verification) - return fail(Verification.error().message()); - return Value; -} - -Expected ConstantParser::parseValue(GenericConstantType Type, - std::size_t Depth) { - if (auto Limit = accountNode(Depth); !Limit) - return std::unexpected(Limit.error()); - skipSpace(); - - const auto Marker = [&](GenericConstantKind Kind) -> Expected { - GenericConstantData Value; - Value.Type = std::move(Type); - Value.Kind = Kind; - auto Verification = Value.verify(); - if (!Verification) - return fail(Verification.error().message()); - return Value; - }; - if (consumeWord("zeroinitializer")) - return Marker(GenericConstantKind::ZeroInitializer); - if (consumeWord("undef")) - return Marker(GenericConstantKind::Undef); - if (consumeWord("poison")) - return Marker(GenericConstantKind::Poison); - if (consumeWord("null")) - return Marker(GenericConstantKind::NullPointer); - - const GenericConstantType &EffectiveType = resolvedType(Type); - - if (consumeWord("c")) { - auto Bytes = parseByteString(); - if (!Bytes) - return std::unexpected(Bytes.error()); - return GenericConstantData::byteString(std::move(Type), std::move(*Bytes)); - } - - if (EffectiveType.Kind == GenericConstantTypeKind::Array) - return parseAggregate(std::move(Type), '[', ']', false, Depth + 1); - if (EffectiveType.Kind == GenericConstantTypeKind::Struct) - return parseAggregate(std::move(Type), EffectiveType.Packed ? '<' : '{', '}', - EffectiveType.Packed, Depth + 1); - if (EffectiveType.Kind == GenericConstantTypeKind::Vector) { - if (consumeWord("splat")) - return parseExpression(std::move(Type), "splat", Depth + 1); - skipSpace(); - if (Position < Text.size() && Text[Position] == '<') - return parseAggregate(std::move(Type), '<', '>', false, Depth + 1); - } - - skipSpace(); - if (Position < Text.size() && Text[Position] == '@') { - auto Name = parseName('@'); - if (!Name) - return std::unexpected(Name.error()); - GenericConstantData Value; - Value.Type = std::move(Type); - Value.Kind = GenericConstantKind::GlobalAddress; - Value.Symbol = std::move(Name->Value); - Value.NumericSymbol = Name->Numeric; - auto Verification = Value.verify(); - if (!Verification) - return fail(Verification.error().message()); - return Value; - } - - if (consumeWord("blockaddress")) { - if (auto Open = expect('(', "'(' after blockaddress"); !Open) - return std::unexpected(Open.error()); - auto Function = parseName('@'); - if (!Function) - return std::unexpected(Function.error()); - if (auto Comma = expect(',', "',' in blockaddress"); !Comma) - return std::unexpected(Comma.error()); - auto Block = parseName('%'); - if (!Block) - return std::unexpected(Block.error()); - if (auto Close = expect(')', "')' after blockaddress"); !Close) - return std::unexpected(Close.error()); - GenericConstantData Value; - Value.Type = std::move(Type); - Value.Kind = GenericConstantKind::BlockAddress; - Value.Symbol = std::move(Function->Value); - Value.NumericSymbol = Function->Numeric; - Value.Block = std::move(Block->Value); - Value.NumericBlock = Block->Numeric; - auto Verification = Value.verify(); - if (!Verification) - return fail(Verification.error().message()); - return Value; - } - - for (const auto &[Keyword, Kind] : - {std::pair{std::string_view{"dso_local_equivalent"}, - GenericConstantKind::DSOLocalEquivalent}, - std::pair{std::string_view{"no_cfi"}, GenericConstantKind::NoCFI}}) { - if (!consumeWord(Keyword)) - continue; - auto Name = parseName('@'); - if (!Name) - return std::unexpected(Name.error()); - GenericConstantData Value; - Value.Type = std::move(Type); - Value.Kind = Kind; - Value.Symbol = std::move(Name->Value); - Value.NumericSymbol = Name->Numeric; - auto Verification = Value.verify(); - if (!Verification) - return fail(Verification.error().message()); - return Value; - } - - if (consumeWord("ptrauth")) { - if (auto Open = expect('(', "'(' after ptrauth"); !Open) - return std::unexpected(Open.error()); - GenericConstantData Value; - Value.Type = std::move(Type); - Value.Kind = GenericConstantKind::PointerAuth; - while (true) { - auto Operand = parseTypedValue(Depth + 1); - if (!Operand) - return std::unexpected(Operand.error()); - Value.Elements.push_back(std::move(*Operand)); - if (consume(')')) - break; - if (auto Comma = expect(',', "',' in ptrauth constant"); !Comma) - return std::unexpected(Comma.error()); - } - auto Verification = Value.verify(); - if (!Verification) - return fail(Verification.error().message()); - return Value; - } - - const auto Word = peekWord(); - if (Word) { - constexpr std::array ExpressionNames{ - std::string_view{"trunc"}, - std::string_view{"bitcast"}, - std::string_view{"addrspacecast"}, - std::string_view{"inttoptr"}, - std::string_view{"ptrtoaddr"}, - std::string_view{"ptrtoint"}, - std::string_view{"add"}, - std::string_view{"sub"}, - std::string_view{"xor"}, - std::string_view{"getelementptr"}, - std::string_view{"shufflevector"}, - std::string_view{"insertelement"}, - std::string_view{"extractelement"}, - }; - if (std::ranges::find(ExpressionNames, *Word) != ExpressionNames.end()) { - Position += Word->size(); - return parseExpression(std::move(Type), *Word, Depth + 1); - } - } - - if (EffectiveType.Kind == GenericConstantTypeKind::Floating) - return parseFloatingValue(std::move(Type), Depth + 1); - if (EffectiveType.Kind == GenericConstantTypeKind::Integer || - EffectiveType.Kind == GenericConstantTypeKind::Byte) { - auto Token = parseValueToken(); - if (!Token) - return std::unexpected(Token.error()); - std::string_view Spelling = *Token; - if (Spelling == "true") - Spelling = "1"; - else if (Spelling == "false") - Spelling = "0"; - const std::uint32_t BitWidth = - EffectiveType.Kind == GenericConstantTypeKind::Byte ? 8U : EffectiveType.BitWidth; - const bool IsExplicitHex = Spelling.starts_with("u0x") || Spelling.starts_with("s0x"); - auto Integer = IsExplicitHex ? APInt::parseLLVMHex(BitWidth, Spelling) - : APInt::parseDecimal(BitWidth, Spelling); - if (!Integer) - return fail(Integer.error().message()); - GenericConstantData Value; - Value.Type = std::move(Type); - Value.Kind = GenericConstantKind::Integer; - Value.IntegerValue = std::move(*Integer); - return Value; - } - return fail("constant value is not valid for its LLVM IR type"); -} - -Expected ConstantParser::parse() { - if (Text.empty()) - return fail("constant text is empty"); - auto Value = parseTypedValue(0); - if (!Value) - return Value; - if (!atEnd()) - return fail("unexpected trailing text"); - auto Verification = Value->verify(); - if (!Verification) - return fail(Verification.error().message()); - return Value; -} - -Expected ConstantParser::parseTypeOnly() { - if (Text.empty()) - return fail("type text is empty"); - auto Type = parseType(0); - if (!Type) - return Type; - if (!atEnd()) - return fail("unexpected trailing text after LLVM type"); - auto Verification = Type->verify(); - if (!Verification) - return fail(Verification.error().message()); - return Type; -} - -[[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(RawCharacter); - if (Character >= 0x20U && Character <= 0x7eU && Character != '"' && Character != '\\') { - Result.push_back(static_cast(Character)); - continue; - } - Result.push_back('\\'); - Result.push_back(HexDigits[Character >> 4U]); - Result.push_back(HexDigits[Character & 0xfU]); - } - Result.push_back('"'); - return Result; -} - -[[nodiscard]] Expected printType(const GenericConstantType &Type); - -[[nodiscard]] Expected printTypeList(std::span 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 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 printValue(const GenericConstantData &Value, bool IncludeType); - -[[nodiscard]] Expected appendTypedOperands(std::string &Output, - std::span 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 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 (resolvedType(Value.Type).Kind == GenericConstantTypeKind::Integer && - resolvedType(Value.Type).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 = resolvedType(Value.Type); - 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(Byte); - if (Character >= 0x20U && Character <= 0x7eU && Character != '"' && Character != '\\') { - Result.push_back(static_cast(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(InRangeStart.words()[0]))); - Result.append(", "); - Result.append(std::to_string(std::bit_cast(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"); -} - -struct LayoutAlignment final { - std::uint64_t ABI = 1; - std::uint64_t Preferred = 1; -}; - -struct LayoutAlignmentEntry final { - char Kind = '\0'; - std::uint32_t BitWidth = 0; - LayoutAlignment Alignment; -}; - -struct LayoutPointerEntry final { - std::uint32_t AddressSpace = 0; - std::uint32_t SizeInBits = 0; - std::uint32_t IndexSizeInBits = 0; - LayoutAlignment Alignment; - bool HasExternalState = false; - bool HasUnstableRepresentation = false; - bool NullIsAllOnes = false; - std::string AddressSpaceName; -}; - -constexpr std::uint64_t MaximumDataLayoutSize = (std::uint64_t{1} << 24U) - 1U; -constexpr std::uint64_t MaximumDataLayoutAlignment = (std::uint64_t{1} << 16U) - 1U; - -class DataLayoutInfo final { -public: - [[nodiscard]] static Expected parse(LLVMConstantSyntaxOptions Options); - - [[nodiscard]] bool isExplicit() const noexcept { - return Explicit; - } - - [[nodiscard]] Expected pointer(std::uint32_t AddressSpace) const; - [[nodiscard]] LayoutAlignment integer(std::uint32_t BitWidth, - std::uint64_t StoreSize) const noexcept; - [[nodiscard]] LayoutAlignment floating(std::uint32_t BitWidth, - std::uint64_t StoreSize) const noexcept; - [[nodiscard]] LayoutAlignment vector(std::uint32_t BitWidth, - std::uint64_t StoreSize) const noexcept; - - [[nodiscard]] LayoutAlignment aggregate() const noexcept { - return Aggregate; - } - -private: - [[nodiscard]] static Expected parseUnsignedField(std::string_view Text, - std::string_view Description); - [[nodiscard]] static Expected alignmentBytes(std::string_view Text, bool AllowZero, - std::string_view Description); - [[nodiscard]] static std::vector split(std::string_view Text, char Separator); - - void setAlignment(LayoutAlignmentEntry Entry); - void setPointer(LayoutPointerEntry Entry); - [[nodiscard]] LayoutAlignment natural(std::uint64_t StoreSize) const noexcept; - [[nodiscard]] LayoutAlignment scalarAlignment(char Kind, std::uint32_t BitWidth, - std::uint64_t StoreSize) const noexcept; - - bool Explicit = false; - std::vector Alignments; - std::vector Pointers; - LayoutAlignment Aggregate{1, 8}; -}; - -Expected DataLayoutInfo::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 DataLayoutInfo::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 DataLayoutInfo::split(std::string_view Text, char Separator) { - std::vector 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 DataLayoutInfo::setAlignment(LayoutAlignmentEntry Entry) { - const auto Existing = - std::ranges::find_if(Alignments, [&Entry](const LayoutAlignmentEntry &Candidate) { - return Candidate.Kind == Entry.Kind && Candidate.BitWidth == Entry.BitWidth; - }); - if (Existing == Alignments.end()) - Alignments.push_back(Entry); - else - *Existing = Entry; -} - -void DataLayoutInfo::setPointer(LayoutPointerEntry Entry) { - const auto Existing = - std::ranges::find_if(Pointers, [&Entry](const LayoutPointerEntry &Candidate) { - return Candidate.AddressSpace == Entry.AddressSpace; - }); - if (Existing == Pointers.end()) - Pointers.push_back(Entry); - else - *Existing = Entry; -} - -Expected DataLayoutInfo::parse(LLVMConstantSyntaxOptions Options) { - DataLayoutInfo Result; - if (Options.DataLayout.empty()) - return Result; - if (Options.DataLayout.size() > Options.MaximumTokenLength) { - return makeUnexpected(ErrorCode::Unsupported, - "LLVM data layout exceeds the configured token length limit"); - } - Result.Explicit = true; - Result.setPointer({.AddressSpace = 0, - .SizeInBits = 64, - .IndexSizeInBits = 64, - .Alignment = {.ABI = 8, .Preferred = 8}, - .HasExternalState = false, - .HasUnstableRepresentation = false, - .NullIsAllOnes = false, - .AddressSpaceName = {}}); - for (const LayoutAlignmentEntry Entry : { - LayoutAlignmentEntry{'i', 1, {1, 1}}, - LayoutAlignmentEntry{'i', 8, {1, 1}}, - LayoutAlignmentEntry{'i', 16, {2, 2}}, - LayoutAlignmentEntry{'i', 32, {4, 4}}, - LayoutAlignmentEntry{'i', 64, {4, 8}}, - LayoutAlignmentEntry{'f', 16, {2, 2}}, - LayoutAlignmentEntry{'f', 32, {4, 4}}, - LayoutAlignmentEntry{'f', 64, {8, 8}}, - LayoutAlignmentEntry{'f', 128, {16, 16}}, - LayoutAlignmentEntry{'v', 64, {8, 8}}, - LayoutAlignmentEntry{'v', 128, {16, 16}}, - }) { - Result.setAlignment(Entry); - } - - for (const std::string_view Component : split(Options.DataLayout, '-')) { - 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 NullIsAllOnes = 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; - NullIsAllOnes = Flag == 'o'; - } 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.Pointers, - [&AddressSpaceName](const LayoutPointerEntry &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"); - } - std::uint64_t IndexSize = *Size; - 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"); - } - IndexSize = *ParsedIndexSize; - } - Result.setPointer({.AddressSpace = static_cast(AddressSpace), - .SizeInBits = static_cast(*Size), - .IndexSizeInBits = static_cast(IndexSize), - .Alignment = {.ABI = *ABI, .Preferred = *Preferred}, - .HasExternalState = HasExternalState, - .HasUnstableRepresentation = HasUnstableRepresentation, - .NullIsAllOnes = NullIsAllOnes, - .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.setAlignment( - {Component.front(), static_cast(*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.Aggregate = {.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; -} - -LayoutAlignment DataLayoutInfo::natural(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(StoreSize, Explicit ? MaximumPowerOfTwo : 16U); - const std::uint64_t Alignment = std::bit_ceil(LimitedSize); - return {Alignment, Alignment}; -} - -LayoutAlignment DataLayoutInfo::scalarAlignment(char Kind, std::uint32_t BitWidth, - std::uint64_t StoreSize) const noexcept { - if (!Explicit) - return natural(StoreSize); - const LayoutAlignmentEntry *Selected = nullptr; - for (const LayoutAlignmentEntry &Entry : Alignments) { - 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 ? natural(StoreSize) : Selected->Alignment; -} - -LayoutAlignment DataLayoutInfo::integer(std::uint32_t BitWidth, - std::uint64_t StoreSize) const noexcept { - return scalarAlignment('i', BitWidth, StoreSize); -} - -LayoutAlignment DataLayoutInfo::floating(std::uint32_t BitWidth, - std::uint64_t StoreSize) const noexcept { - return scalarAlignment('f', BitWidth, StoreSize); -} - -LayoutAlignment DataLayoutInfo::vector(std::uint32_t BitWidth, - std::uint64_t StoreSize) const noexcept { - return scalarAlignment('v', BitWidth, StoreSize); -} - -Expected DataLayoutInfo::pointer(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(Pointers, [AddressSpace](const LayoutPointerEntry &Candidate) { - return Candidate.AddressSpace == AddressSpace; - }); - if (Entry == Pointers.end()) { - Entry = std::ranges::find_if( - Pointers, [](const LayoutPointerEntry &Candidate) { return Candidate.AddressSpace == 0; }); - } - if (Entry == Pointers.end()) - return makeUnexpected(ErrorCode::InvalidArgument, "LLVM data layout has no pointer layout"); - return *Entry; -} - -struct TypeLayout final { - std::uint64_t StoreSize = 0; - std::uint64_t AllocationSize = 0; - std::uint64_t ABIAlignment = 1; - std::uint64_t PreferredAlignment = 1; -}; - -[[nodiscard]] Expected layoutType(const GenericConstantType &Type, - LLVMConstantSyntaxOptions Options, - const DataLayoutInfo &Layout, - std::vector &NamedTypeStack, - std::size_t Depth) { - if (Depth > Options.MaximumDepth) - 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 = [&Layout](std::uint64_t StoreSize, - LayoutAlignment TypeAlignment) -> Expected { - std::uint64_t AllocationSize = StoreSize; - if (Layout.isExplicit()) { - 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(Bits) + 7U) / 8U; - return ScalarLayout(StoreSize, Layout.integer(Bits, StoreSize)); - } - case GenericConstantTypeKind::Floating: { - const std::uint32_t Bits = getFloatSemanticsBitWidth(Type.Semantics).value_or(0); - const std::uint64_t StoreSize = (static_cast(Bits) + 7U) / 8U; - return ScalarLayout(StoreSize, Layout.floating(Bits, StoreSize)); - } - case GenericConstantTypeKind::Pointer: { - auto Pointer = Layout.pointer(Type.AddressSpace); - if (!Pointer) - return std::unexpected(Pointer.error()); - const std::uint64_t StoreSize = (static_cast(Pointer->SizeInBits) + 7U) / 8U; - return ScalarLayout(StoreSize, Pointer->Alignment); - } - case GenericConstantTypeKind::Array: { - auto Element = layoutType(Type.Elements.front(), Options, Layout, 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 = resolvedType(Type.Elements.front()); - 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 = Layout.pointer(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::max() - 7U) - return makeUnexpected(ErrorCode::Unsupported, "LLVM vector bit size is too large"); - const std::uint64_t StoreSize = (*TotalBits + 7U) / 8U; - return ScalarLayout(StoreSize, - Layout.vector(static_cast(std::min( - *TotalBits, std::numeric_limits::max())), - StoreSize)); - } - case GenericConstantTypeKind::Struct: { - if (Type.Packed) { - std::uint64_t Size = 0; - for (const GenericConstantType &MemberType : Type.Elements) { - auto Member = layoutType(MemberType, Options, Layout, 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(Layout.aggregate().Preferred, 1)}; - } - std::uint64_t Offset = 0; - std::uint64_t ABIAlignment = std::max(Layout.aggregate().ABI, 1); - for (const GenericConstantType &MemberType : Type.Elements) { - auto Member = layoutType(MemberType, Options, Layout, 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(Layout.aggregate().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 Result = - makeUnexpected(ErrorCode::Unsupported, "named LLVM type has no embedded type definition"); - if (Type.Elements.size() == 1) { - Result = layoutType(Type.Elements.front(), Options, Layout, NamedTypeStack, Depth + 1); - } else { - const auto Definition = std::ranges::find_if( - Options.NamedTypes, [&Type](const LLVMNamedTypeDefinition &Candidate) { - return Candidate.Name == Type.Name && Candidate.NumericName == Type.NumericName; - }); - if (Definition != Options.NamedTypes.end()) { - auto Resolved = parseLLVMType(Definition->Definition, Options); - if (!Resolved) - Result = std::unexpected(Resolved.error()); - else - Result = layoutType(*Resolved, Options, Layout, 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"); -} - -[[nodiscard]] Expected layoutType(const GenericConstantType &Type, - LLVMConstantSyntaxOptions Options) { - auto Layout = DataLayoutInfo::parse(Options); - if (!Layout) - return std::unexpected(Layout.error()); - std::vector NamedTypeStack; - return layoutType(Type, Options, *Layout, NamedTypeStack, 0); -} - -} // namespace - -Expected parseLLVMConstant(std::string_view Text, - LLVMConstantSyntaxOptions Options) { - if (Options.MaximumDepth == 0 || Options.MaximumNodes == 0 || Options.MaximumTokenLength == 0) { - return makeUnexpected(ErrorCode::InvalidArgument, - "LLVM constant parser limits must be nonzero"); - } - return ConstantParser(Text, Options).parse(); -} - -Expected parseLLVMType(std::string_view Text, - LLVMConstantSyntaxOptions Options) { - if (Options.MaximumDepth == 0 || Options.MaximumNodes == 0 || Options.MaximumTokenLength == 0) { - return makeUnexpected(ErrorCode::InvalidArgument, "LLVM type parser limits must be nonzero"); - } - return ConstantParser(Text, Options).parseTypeOnly(); -} - -Expected printLLVMConstant(const GenericConstantData &Constant) { - return printValue(Constant, true); -} - -Expected llvmConstantStorageSize(const GenericConstantType &Type, - LLVMConstantSyntaxOptions Options) { - auto Layout = layoutType(Type, Options); - if (!Layout) - return std::unexpected(Layout.error()); - return Layout->AllocationSize; -} - -Expected llvmConstantPreferredAlignment(const GenericConstantType &Type, - LLVMConstantSyntaxOptions Options) { - auto Layout = layoutType(Type, Options); - if (!Layout) - return std::unexpected(Layout.error()); - return Alignment::create(Layout->PreferredAlignment); -} - -Expected llvmPointerSizeInBits(std::uint32_t AddressSpace, - LLVMConstantSyntaxOptions Options) { - auto Layout = DataLayoutInfo::parse(Options); - if (!Layout) - return std::unexpected(Layout.error()); - auto Pointer = Layout->pointer(AddressSpace); - if (!Pointer) - return std::unexpected(Pointer.error()); - return Pointer->SizeInBits; -} - -} // namespace rcc::mir diff --git a/backend/lib/Target/RISCV/RISCVMCTargetLowering.cpp b/backend/lib/Target/RISCV/RISCVMCTargetLowering.cpp index 0db51f0..78c56a3 100644 --- a/backend/lib/Target/RISCV/RISCVMCTargetLowering.cpp +++ b/backend/lib/Target/RISCV/RISCVMCTargetLowering.cpp @@ -2,7 +2,6 @@ #include "rcc/Target/RISCV/RISCVMCTargetLowering.h" -#include "rcc/CodeGen/GenericConstant.h" #include "rcc/CodeGen/GenericOpcodes.h" #include "rcc/CodeGen/GlobalObject.h" #include "rcc/CodeGen/InstructionDescriptor.h" @@ -15,6 +14,8 @@ #include "rcc/CodeGen/MachineOperand.h" #include "rcc/CodeGen/Register.h" #include "rcc/CodeGen/TargetConstant.h" +#include "rcc/IR/Constants.h" +#include "rcc/IR/DataLayout.h" #include "rcc/MC/MCContext.h" #include "rcc/MC/MCExpr.h" #include "rcc/MC/MCInst.h" @@ -51,11 +52,6 @@ constexpr std::uint32_t KnownTargetFlags = CallTargetFlag | PCRelativeHighTargetFlag | PCRelativeLowTargetFlag | CopyF32TargetFlag | CopyF64TargetFlag | AbsoluteHighTargetFlag | AbsoluteLowTargetFlag; -struct TypeLayout final { - std::uint64_t Size = 0; - std::uint64_t Alignment = 1; -}; - [[nodiscard]] Expected targetRelocationSize(TargetRelocationKindID Kind) { if (Kind == targetRelocationKind(RISCVTargetRelocationKind::Absolute32)) return 4; @@ -65,132 +61,6 @@ struct TypeLayout final { "RV64GC target constant has an unknown relocation kind"); } -[[nodiscard]] Expected checkedAdd(std::uint64_t Left, std::uint64_t Right) { - std::uint64_t Result = 0; - if (__builtin_add_overflow(Left, Right, &Result)) - return makeUnexpected(ErrorCode::AssemblyLoweringFailed, "constant layout size overflows"); - return Result; -} - -[[nodiscard]] Expected checkedMultiply(std::uint64_t Left, std::uint64_t Right) { - std::uint64_t Result = 0; - if (__builtin_mul_overflow(Left, Right, &Result)) - return makeUnexpected(ErrorCode::AssemblyLoweringFailed, "constant layout size overflows"); - return Result; -} - -[[nodiscard]] Expected alignTo(std::uint64_t Value, std::uint64_t Alignment) { - if (Alignment == 0 || (Alignment & (Alignment - 1U)) != 0) { - return makeUnexpected(ErrorCode::AssemblyLoweringFailed, - "constant layout requires power-of-two alignment"); - } - auto Adjusted = checkedAdd(Value, Alignment - 1U); - if (!Adjusted) - return std::unexpected(Adjusted.error()); - return *Adjusted & ~(Alignment - 1U); -} - -[[nodiscard]] std::uint64_t naturalAlignment(std::uint64_t Size) noexcept { - if (Size <= 1) - return 1; - std::uint64_t Result = 1; - while (Result < Size && Result < 16) - Result <<= 1U; - return Result; -} - -[[nodiscard]] const GenericConstantType &resolvedType(const GenericConstantType &Type) noexcept { - const GenericConstantType *Result = &Type; - std::size_t Depth = 0; - while (Result->Kind == GenericConstantTypeKind::Named && Result->Elements.size() == 1 && - Depth++ < 1024) { - Result = &Result->Elements.front(); - } - return *Result; -} - -[[nodiscard]] Expected layoutType(const GenericConstantType &InputType, - std::size_t Depth = 0) { - if (Depth > 1024) { - return makeUnexpected(ErrorCode::AssemblyLoweringFailed, - "constant layout exceeds its nesting limit"); - } - const GenericConstantType &Type = resolvedType(InputType); - switch (Type.Kind) { - case GenericConstantTypeKind::Integer: { - const std::uint64_t Size = (static_cast(Type.BitWidth) + 7U) / 8U; - return TypeLayout{.Size = Size, .Alignment = naturalAlignment(Size)}; - } - case GenericConstantTypeKind::Byte: - return TypeLayout{.Size = 1, .Alignment = 1}; - case GenericConstantTypeKind::Floating: { - const auto BitWidth = getFloatSemanticsBitWidth(Type.Semantics); - if (!BitWidth) { - return makeUnexpected(ErrorCode::AssemblyLoweringFailed, - "floating constant has unknown semantics"); - } - const std::uint64_t Size = (static_cast(*BitWidth) + 7U) / 8U; - return TypeLayout{.Size = Size, .Alignment = naturalAlignment(Size)}; - } - case GenericConstantTypeKind::Pointer: - return TypeLayout{.Size = 8, .Alignment = 8}; - case GenericConstantTypeKind::Array: - case GenericConstantTypeKind::Vector: { - if (Type.Elements.size() != 1 || - (Type.Kind == GenericConstantTypeKind::Vector && Type.Scalable)) { - return makeUnexpected(ErrorCode::Unsupported, - "RV64GC assembly cannot lay out this sequential constant type"); - } - auto Element = layoutType(Type.Elements.front(), Depth + 1); - if (!Element) - return std::unexpected(Element.error()); - auto Stride = alignTo(Element->Size, Element->Alignment); - if (!Stride) - return std::unexpected(Stride.error()); - auto Size = checkedMultiply(*Stride, Type.ElementCount); - if (!Size) - return std::unexpected(Size.error()); - std::uint64_t Alignment = Element->Alignment; - if (Type.Kind == GenericConstantTypeKind::Vector) - Alignment = naturalAlignment(*Size); - return TypeLayout{.Size = *Size, .Alignment = Alignment}; - } - case GenericConstantTypeKind::Struct: { - std::uint64_t Offset = 0; - std::uint64_t StructAlignment = 1; - for (const GenericConstantType &ElementType : Type.Elements) { - auto Element = layoutType(ElementType, Depth + 1); - if (!Element) - return std::unexpected(Element.error()); - const std::uint64_t ElementAlignment = Type.Packed ? 1 : Element->Alignment; - auto Aligned = alignTo(Offset, ElementAlignment); - if (!Aligned) - return std::unexpected(Aligned.error()); - auto End = checkedAdd(*Aligned, Element->Size); - if (!End) - return std::unexpected(End.error()); - Offset = *End; - StructAlignment = std::max(StructAlignment, ElementAlignment); - } - auto Size = alignTo(Offset, StructAlignment); - if (!Size) - return std::unexpected(Size.error()); - return TypeLayout{.Size = *Size, .Alignment = StructAlignment}; - } - case GenericConstantTypeKind::Named: - return makeUnexpected(ErrorCode::Unsupported, - "RV64GC assembly cannot lay out an unresolved named constant type"); - case GenericConstantTypeKind::Invalid: - case GenericConstantTypeKind::Void: - case GenericConstantTypeKind::Label: - case GenericConstantTypeKind::Token: - case GenericConstantTypeKind::Metadata: - case GenericConstantTypeKind::Function: - return makeUnexpected(ErrorCode::AssemblyLoweringFailed, "constant has a non-storage type"); - } - return makeUnexpected(ErrorCode::InternalError, "generic constant type kind is invalid"); -} - [[nodiscard]] Expected addOffset(mc::MCExprRef Expression, std::int64_t Offset) { if (Offset == 0) return Expression; @@ -509,11 +379,10 @@ struct TypeLayout final { return Size == 0 ? Expected{} : Streamer.emitZeroFill(Size); } -[[nodiscard]] Expected emitGenericConstantImpl(const GenericConstantData &Constant, - const TypeLayout &Layout, - MachineMCContext &Context, - mc::MCStreamer &Streamer, - std::size_t Depth = 0) { +[[nodiscard]] Expected +emitGenericConstantImpl(const GenericConstantData &Constant, const DataLayout &Layout, + std::uint64_t AllocationSize, MachineMCContext &Context, + mc::MCStreamer &Streamer, std::size_t Depth = 0) { if (Depth > 1024) { return makeUnexpected(ErrorCode::AssemblyLoweringFailed, "generic constant exceeds its emission nesting limit"); @@ -522,17 +391,17 @@ struct TypeLayout final { case GenericConstantKind::Integer: { if (!Constant.IntegerValue) return makeUnexpected(ErrorCode::InternalError, "integer constant has no value"); - const auto Bytes = bytesFromWords(Constant.IntegerValue->words(), Layout.Size); + const auto Bytes = bytesFromWords(Constant.IntegerValue->words(), AllocationSize); return Streamer.emitBytes(Bytes); } case GenericConstantKind::Floating: { if (!Constant.FloatingValue) return makeUnexpected(ErrorCode::InternalError, "floating constant has no value"); - const auto Bytes = bytesFromWords(Constant.FloatingValue->words(), Layout.Size); + const auto Bytes = bytesFromWords(Constant.FloatingValue->words(), AllocationSize); return Streamer.emitBytes(Bytes); } case GenericConstantKind::ByteString: { - if (Constant.ByteValue.size() != Layout.Size) { + if (Constant.ByteValue.size() != AllocationSize) { return makeUnexpected(ErrorCode::AssemblyLoweringFailed, "byte-string constant size does not match its RV64GC layout"); } @@ -546,61 +415,57 @@ struct TypeLayout final { case GenericConstantKind::ZeroInitializer: case GenericConstantKind::Undef: case GenericConstantKind::Poison: - return Streamer.emitZeroFill(Layout.Size); + return Streamer.emitZeroFill(AllocationSize); case GenericConstantKind::Aggregate: { - const GenericConstantType &Type = resolvedType(Constant.Type); + const GenericConstantType &Type = Constant.Type.resolved(); std::uint64_t Offset = 0; for (std::size_t Index = 0; Index < Constant.Elements.size(); ++Index) { - auto ElementLayout = layoutType(Constant.Elements[Index].Type, Depth + 1); - if (!ElementLayout) - return std::unexpected(ElementLayout.error()); - const std::uint64_t Alignment = Type.Kind == GenericConstantTypeKind::Struct && Type.Packed - ? 1 - : ElementLayout->Alignment; - auto ElementOffset = alignTo(Offset, Alignment); - if (!ElementOffset) - return std::unexpected(ElementOffset.error()); - auto Padding = emitPadding(*ElementOffset - Offset, Streamer); + auto ElementSize = Layout.getTypeAllocSize(Constant.Elements[Index].Type); + auto ElementAlignment = Layout.getABITypeAlign(Constant.Elements[Index].Type); + if (!ElementSize) + return std::unexpected(ElementSize.error()); + if (!ElementAlignment) + return std::unexpected(ElementAlignment.error()); + const std::uint64_t Alignment = + Type.Kind == GenericConstantTypeKind::Struct && Type.Packed ? 1 : *ElementAlignment; + const std::uint64_t AlignmentMask = Alignment - 1U; + if (Offset > std::numeric_limits::max() - AlignmentMask) { + return makeUnexpected(ErrorCode::AssemblyLoweringFailed, + "aggregate constant exceeds its RV64GC layout"); + } + const std::uint64_t ElementOffset = (Offset + AlignmentMask) & ~AlignmentMask; + if (ElementOffset > AllocationSize || *ElementSize > AllocationSize - ElementOffset) { + return makeUnexpected(ErrorCode::AssemblyLoweringFailed, + "aggregate constant exceeds its RV64GC layout"); + } + auto Padding = emitPadding(ElementOffset - Offset, Streamer); if (!Padding) return std::unexpected(Padding.error()); - auto Element = emitGenericConstantImpl(Constant.Elements[Index], *ElementLayout, Context, - Streamer, Depth + 1); + auto Element = emitGenericConstantImpl(Constant.Elements[Index], Layout, *ElementSize, + Context, Streamer, Depth + 1); if (!Element) return std::unexpected(Element.error()); - auto End = checkedAdd(*ElementOffset, ElementLayout->Size); - if (!End) - return std::unexpected(End.error()); - Offset = *End; - if (Type.Kind == GenericConstantTypeKind::Array || - Type.Kind == GenericConstantTypeKind::Vector) { - auto StrideEnd = alignTo(Offset, ElementLayout->Alignment); - if (!StrideEnd) - return std::unexpected(StrideEnd.error()); - auto ElementPadding = emitPadding(*StrideEnd - Offset, Streamer); - if (!ElementPadding) - return std::unexpected(ElementPadding.error()); - Offset = *StrideEnd; - } + Offset = ElementOffset + *ElementSize; } - if (Offset > Layout.Size) { + if (Offset > AllocationSize) { return makeUnexpected(ErrorCode::AssemblyLoweringFailed, "aggregate constant exceeds its RV64GC layout"); } - return emitPadding(Layout.Size - Offset, Streamer); + return emitPadding(AllocationSize - Offset, Streamer); } case GenericConstantKind::GlobalAddress: case GenericConstantKind::BlockAddress: case GenericConstantKind::DSOLocalEquivalent: case GenericConstantKind::NoCFI: case GenericConstantKind::Expression: { - if (Layout.Size != 1 && Layout.Size != 2 && Layout.Size != 4 && Layout.Size != 8) { + if (AllocationSize != 1 && AllocationSize != 2 && AllocationSize != 4 && AllocationSize != 8) { return makeUnexpected(ErrorCode::Unsupported, "symbolic RV64GC constant has an unsupported storage size"); } auto Expression = lowerConstantExpression(Constant, Context); if (!Expression) return std::unexpected(Expression.error()); - return Streamer.emitValue(std::move(*Expression), static_cast(Layout.Size)); + return Streamer.emitValue(std::move(*Expression), static_cast(AllocationSize)); } case GenericConstantKind::PointerAuth: return makeUnexpected(ErrorCode::Unsupported, @@ -1151,14 +1016,17 @@ Expected RISCVMCTargetLowering::emitGenericConstant(const GenericConstantD auto Verification = Constant.verify(); if (!Verification) return std::unexpected(Verification.error()); - auto Layout = layoutType(Constant.Type); + auto Layout = DataLayout::parse(TargetDataLayout); if (!Layout) return std::unexpected(Layout.error()); - if (Layout->Size != Size) { + auto AllocationSize = Layout->getTypeAllocSize(Constant.Type); + if (!AllocationSize) + return std::unexpected(AllocationSize.error()); + if (*AllocationSize != Size) { return makeUnexpected(ErrorCode::AssemblyLoweringFailed, "generic constant size does not match the RV64GC data layout"); } - return emitGenericConstantImpl(Constant, *Layout, Context, Streamer); + return emitGenericConstantImpl(Constant, *Layout, *AllocationSize, Context, Streamer); } Expected RISCVMCTargetLowering::emitGlobalInitializer(const GlobalInitializer &Initializer, diff --git a/backend/unittests/AsmParser/CMakeLists.txt b/backend/unittests/AsmParser/CMakeLists.txt new file mode 100644 index 0000000..00e7429 --- /dev/null +++ b/backend/unittests/AsmParser/CMakeLists.txt @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-3.0-or-later + +add_executable(RCCBackendLLParserTests LLParserTest.cpp) +rcc_backend_configure_target(RCCBackendLLParserTests) +target_link_libraries( + RCCBackendLLParserTests + PRIVATE + RCCBackend::AsmParser + RCCBackend::IR +) + +add_test(NAME RCCBackend.AsmParser.LLParser COMMAND RCCBackendLLParserTests) + +rcc_backend_register_format_files("${CMAKE_CURRENT_SOURCE_DIR}/LLParserTest.cpp") +rcc_backend_register_tidy_files("${CMAKE_CURRENT_SOURCE_DIR}/LLParserTest.cpp") diff --git a/backend/unittests/AsmParser/LLParserTest.cpp b/backend/unittests/AsmParser/LLParserTest.cpp new file mode 100644 index 0000000..75312e4 --- /dev/null +++ b/backend/unittests/AsmParser/LLParserTest.cpp @@ -0,0 +1,533 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "rcc/AsmParser/LLParser.h" + +#include "rcc/IR/DataLayout.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + +class TestContext final { +public: + void expect(bool Condition, std::string_view Message) { + if (Condition) + return; + std::cerr << Message << '\n'; + ++FailureCount; + } + + [[nodiscard]] int result() const noexcept { + return FailureCount == 0 ? 0 : 1; + } + +private: + int FailureCount = 0; +}; + +struct TestOptions final { + std::string_view DataLayout; + std::span NamedTypes; +}; + +[[nodiscard]] rcc::Expected parseConstant(std::string_view Text, + TestOptions Options) { + return rcc::parseLLVMConstant(Text, {.NamedTypes = Options.NamedTypes}); +} + +[[nodiscard]] rcc::Expected parseType(std::string_view Text, + TestOptions Options) { + return rcc::parseLLVMType(Text, {.NamedTypes = Options.NamedTypes}); +} + +[[nodiscard]] rcc::Expected allocationSize(const rcc::GenericConstantType &Type, + TestOptions Options) { + auto Layout = rcc::DataLayout::parse(Options.DataLayout); + if (!Layout) + return std::unexpected(Layout.error()); + return Layout->getTypeAllocSize(Type); +} + +[[nodiscard]] rcc::Expected preferredAlignment(const rcc::GenericConstantType &Type, + TestOptions Options) { + auto Layout = rcc::DataLayout::parse(Options.DataLayout); + if (!Layout) + return std::unexpected(Layout.error()); + return Layout->getPrefTypeAlign(Type); +} + +[[nodiscard]] rcc::Expected pointerSizeInBits(std::uint32_t AddressSpace, + TestOptions Options = {}) { + auto Layout = rcc::DataLayout::parse(Options.DataLayout); + if (!Layout) + return std::unexpected(Layout.error()); + return Layout->getPointerSizeInBits(AddressSpace); +} + +constexpr TestOptions Options{ + .DataLayout = "e-p:64:64", + .NamedTypes = {}, +}; + +void expectRoundTrip(TestContext &Context, std::string_view Text, std::uint64_t ExpectedSize) { + auto Parsed = parseConstant(Text, Options); + Context.expect(Parsed.has_value(), "valid LLVM constant must parse"); + if (!Parsed) { + std::cerr << Text << ": " << Parsed.error().message() << '\n'; + return; + } + auto Size = allocationSize(Parsed->Type, Options); + Context.expect(Size.has_value() && *Size == ExpectedSize, + "LLVM constant storage size must match its structured type"); + auto Printed = Parsed->toLLVMString(); + Context.expect(Printed.has_value(), "structured LLVM constant must print"); + if (!Printed) + return; + auto Reparsing = parseConstant(*Printed, Options); + Context.expect(Reparsing.has_value() && *Reparsing == *Parsed, + "LLVM constant parse-print-parse must preserve the value tree"); + if (!Reparsing) + std::cerr << *Printed << ": " << Reparsing.error().message() << '\n'; +} + +void expectLayoutRoundTrip(TestContext &Context, std::string_view Text, TestOptions LayoutOptions, + std::uint64_t ExpectedSize, std::uint64_t ExpectedPreferredAlignment) { + auto Parsed = parseConstant(Text, LayoutOptions); + Context.expect(Parsed.has_value(), "DataLayout-aware LLVM constant must parse"); + if (!Parsed) { + std::cerr << Text << ": " << Parsed.error().message() << '\n'; + return; + } + auto Size = allocationSize(Parsed->Type, LayoutOptions); + auto Alignment = preferredAlignment(Parsed->Type, LayoutOptions); + Context.expect(Size.has_value() && *Size == ExpectedSize, + "DataLayout must determine the exact allocation size"); + Context.expect(Alignment.has_value() && *Alignment == ExpectedPreferredAlignment, + "DataLayout must determine the exact preferred alignment"); + auto Printed = Parsed->toLLVMString(); + Context.expect(Printed.has_value(), "DataLayout-aware LLVM constant must print"); + if (!Printed) + return; + auto Reparsing = parseConstant(*Printed, LayoutOptions); + Context.expect(Reparsing.has_value() && *Reparsing == *Parsed, + "DataLayout-aware named constants must round-trip structurally"); +} + +void testScalarConstants(TestContext &Context) { + expectRoundTrip(Context, "i1 true", 1); + expectRoundTrip(Context, "i65 -1", 12); + expectRoundTrip(Context, "half 0xH3C00", 2); + expectRoundTrip(Context, "bfloat 0xR3F80", 2); + expectRoundTrip(Context, "float 1.250000e+00", 4); + expectRoundTrip(Context, "double 0x3FF0000000000000", 8); + expectRoundTrip(Context, "x86_fp80 f0x4000C000000000000000", 16); + expectRoundTrip(Context, "fp128 f0x3FFF0000000000000000000000000000", 16); + expectRoundTrip(Context, "ptr null", 8); + expectRoundTrip(Context, "i32 undef", 4); + expectRoundTrip(Context, "i32 poison", 4); + expectRoundTrip(Context, "i32 zeroinitializer", 4); + + auto BFloat = parseConstant("bfloat 0xR3E80", Options); + auto PrintedBFloat = + BFloat ? BFloat->toLLVMString() : rcc::Expected(std::unexpected(BFloat.error())); + Context.expect(PrintedBFloat && *PrintedBFloat == "bfloat 2.500000e-01", + "LLVM-compatible floating printing must prefer an exact decimal spelling"); + + auto Pi = parseConstant("double f0x400921FB54442D18", Options); + auto PrintedPi = + Pi ? Pi->toLLVMString() : rcc::Expected(std::unexpected(Pi.error())); + Context.expect( + PrintedPi && *PrintedPi == "double f0x400921FB54442D18", + "floating printing must retain exact bits when six decimal digits do not round-trip"); +} + +void expectIntegerWords(TestContext &Context, std::string_view Text, + std::span ExpectedWords) { + auto Parsed = parseConstant(Text, Options); + Context.expect(Parsed.has_value() && Parsed->IntegerValue.has_value() && + std::ranges::equal(Parsed->IntegerValue->words(), ExpectedWords), + "LLVM integer literal must preserve its exact bit pattern"); + if (!Parsed) + std::cerr << Text << ": " << Parsed.error().message() << '\n'; +} + +void testDecimalIntegerTruncation(TestContext &Context) { + constexpr std::array NegativeOne{std::numeric_limits::max()}; + constexpr std::array NarrowNegative{767}; + constexpr std::array NarrowMinimum{8}; + constexpr std::array WidePositive{1}; + + expectIntegerWords(Context, "i64 -1", NegativeOne); + expectIntegerWords(Context, "i16 -64769", NarrowNegative); + expectIntegerWords(Context, "i4 -8", NarrowMinimum); + expectIntegerWords(Context, "i64 18446744073709551617", WidePositive); +} + +void expectFloatingWords(TestContext &Context, std::string_view Text, + std::span ExpectedWords) { + auto Parsed = parseConstant(Text, Options); + Context.expect(Parsed.has_value() && Parsed->FloatingValue.has_value() && + std::ranges::equal(Parsed->FloatingValue->words(), ExpectedWords), + "LLVM floating special literal must preserve its exact bit pattern"); + if (!Parsed) { + std::cerr << Text << ": " << Parsed.error().message() << '\n'; + return; + } + if (!Parsed->FloatingValue || + !std::ranges::equal(Parsed->FloatingValue->words(), ExpectedWords)) { + std::cerr << Text << ": expected words"; + for (const std::uint64_t Word : ExpectedWords) + std::cerr << " 0x" << std::hex << Word; + std::cerr << ", got"; + if (Parsed->FloatingValue) { + for (const std::uint64_t Word : Parsed->FloatingValue->words()) + std::cerr << " 0x" << std::hex << Word; + } + std::cerr << std::dec << '\n'; + } + auto Printed = Parsed->toLLVMString(); + Context.expect(Printed.has_value(), "LLVM floating special literal must print"); + if (!Printed) + return; + auto Reparsing = parseConstant(*Printed, Options); + Context.expect(Reparsing.has_value() && *Reparsing == *Parsed, + "printed LLVM floating special literal must round-trip"); +} + +void testExplicitHexIntegerConstants(TestContext &Context) { + constexpr std::array AllOnes65{std::numeric_limits::max(), 1}; + constexpr std::array SignBit65{0, 1}; + constexpr std::array UnsignedFF{0xff}; + constexpr std::array SignedFF{0xffff}; + constexpr std::array SignedFF8{0xff}; + + expectIntegerWords(Context, "i65 u0x1FFFFFFFFFFFFFFFF", AllOnes65); + expectIntegerWords(Context, "i65 s0x1FFFFFFFFFFFFFFFF", AllOnes65); + expectIntegerWords(Context, "i65 s0x10000000000000000", SignBit65); + expectIntegerWords(Context, "i65 s0x01", AllOnes65); + expectIntegerWords(Context, "i16 u0xFF", UnsignedFF); + expectIntegerWords(Context, "i16 s0xFF", SignedFF); + expectIntegerWords(Context, "i8 s0x1FF", SignedFF8); + + expectRoundTrip(Context, "i65 s0x01", 12); + Context.expect(!parseConstant("i32 u0x", Options) && !parseConstant("i32 u0X1", Options), + "explicit LLVM hexadecimal integers must use the fixed spelling"); +} + +void testSpecialFloatingLiterals(TestContext &Context) { + constexpr std::array HalfInfinity{0x7c00}; + constexpr std::array NegativeHalfInfinity{0xfc00}; + constexpr std::array HalfQuietNaN{0x7e00}; + constexpr std::array NegativeHalfQuietNaN{0xfe00}; + constexpr std::array HalfSignalingNaN{0x7d00}; + constexpr std::array HalfPayloadNaN{0x7fef}; + constexpr std::array FloatSignalingNaN{0x7f800001}; + constexpr std::array X87Infinity{0x8000000000000000, 0x7fff}; + constexpr std::array X87QuietNaN{0xc000000000000000, 0x7fff}; + constexpr std::array QuadOne{0, 0x3fff000000000000}; + constexpr std::array QuadInfinity{0, 0x7fff000000000000}; + constexpr std::array QuadQuietNaN{0, 0x7fff800000000000}; + constexpr std::array PPCOne{0x3ff0000000000000, 0}; + constexpr std::array PPCInfinity{0x7ff0000000000000, 0}; + constexpr std::array PPCQuietNaN{0x7ff8000000000000, 0}; + + expectFloatingWords(Context, "half +inf", HalfInfinity); + expectFloatingWords(Context, "half -inf", NegativeHalfInfinity); + expectFloatingWords(Context, "half +qnan", HalfQuietNaN); + expectFloatingWords(Context, "half -qnan", NegativeHalfQuietNaN); + expectFloatingWords(Context, "half +snan(0x100)", HalfSignalingNaN); + expectFloatingWords(Context, "half +nan(0x123456789ABCDEF)", HalfPayloadNaN); + expectFloatingWords(Context, "float +snan(0x400001)", FloatSignalingNaN); + expectFloatingWords(Context, "x86_fp80 +inf", X87Infinity); + expectFloatingWords(Context, "x86_fp80 +qnan", X87QuietNaN); + expectFloatingWords(Context, "fp128 +inf", QuadInfinity); + expectFloatingWords(Context, "fp128 +qnan", QuadQuietNaN); + expectFloatingWords(Context, "fp128 0xL00000000000000003FFF000000000000", QuadOne); + expectFloatingWords(Context, "ppc_fp128 +inf", PPCInfinity); + expectFloatingWords(Context, "ppc_fp128 +qnan", PPCQuietNaN); + expectFloatingWords(Context, "ppc_fp128 0xM3FF00000000000000000000000000000", PPCOne); + + for (const std::string_view Text : { + std::string_view{"float nan(0x)"}, + std::string_view{"float snan(12)"}, + std::string_view{"float qnan(0x1)"}, + std::string_view{"float inf(0x1)"}, + std::string_view{"float inf"}, + std::string_view{"float +nan"}, + std::string_view{"float +snan"}, + std::string_view{"float +qnan(0x1)"}, + }) { + Context.expect(!parseConstant(Text, Options), + "malformed LLVM floating special literal must be rejected"); + } +} + +void testExactFloatingConversion(TestContext &Context) { + constexpr std::array PositiveZero{0}; + constexpr std::array NegativeFloatZero{0x80000000}; + constexpr std::array HalfOne{0x3c00}; + constexpr std::array HalfTwoUlps{0x3c02}; + constexpr std::array HalfSmallestSubnormal{0x0001}; + constexpr std::array BFloatOne{0x3f80}; + constexpr std::array BFloatTwoUlps{0x3f82}; + constexpr std::array FloatOne{0x3f800000}; + constexpr std::array FloatTwoUlps{0x3f800002}; + constexpr std::array FloatHexPower{0x2f800000}; + constexpr std::array FloatSignalingNaN{0x7f800001}; + constexpr std::array FloatQuietNaN{0x7fc00000}; + constexpr std::array LargestDouble{0x7fefffffffffffff}; + constexpr std::array X87One{0x8000000000000000, 0x3fff}; + constexpr std::array QuadOne{0, 0x3fff000000000000}; + constexpr std::array PPCOne{0x3ff0000000000000, 0}; + + expectFloatingWords(Context, "float +0.0", PositiveZero); + expectFloatingWords(Context, "float -0.0", NegativeFloatZero); + expectFloatingWords(Context, "half 1.00048828125", HalfOne); + expectFloatingWords(Context, "half 1.00146484375", HalfTwoUlps); + expectFloatingWords(Context, "half 0.0000000298023223876953126", HalfSmallestSubnormal); + expectFloatingWords(Context, "bfloat 1.00390625", BFloatOne); + expectFloatingWords(Context, "bfloat 1.01171875", BFloatTwoUlps); + expectFloatingWords(Context, "float 1.000000059604644775390625", FloatOne); + expectFloatingWords(Context, "float 1.000000178813934326171875", FloatTwoUlps); + expectFloatingWords(Context, "float 0x1.0p-32", FloatHexPower); + expectFloatingWords(Context, "double 1.79769313486231570815e+308", LargestDouble); + expectFloatingWords(Context, "x86_fp80 1.0", X87One); + expectFloatingWords(Context, "x86_fp80 0x1.0p0", X87One); + expectFloatingWords(Context, "fp128 1.0", QuadOne); + expectFloatingWords(Context, "fp128 0x1.0p0", QuadOne); + expectFloatingWords(Context, "ppc_fp128 1.0", PPCOne); + expectFloatingWords(Context, "ppc_fp128 0x1.0p0", PPCOne); + expectFloatingWords(Context, "float 0x3FF0000000000000", FloatOne); + expectFloatingWords(Context, "float 0x7FF0000020000000", FloatSignalingNaN); + expectFloatingWords(Context, "float 0x7FF8000000000000", FloatQuietNaN); + expectFloatingWords(Context, "float f0x3F800000", FloatOne); + + for (const std::string_view Text : { + std::string_view{"half 0.0000000298023223876953125"}, + std::string_view{"float 0x3FF0000000000001"}, + std::string_view{"float 0x7FF8000000000001"}, + std::string_view{"float f0x3F8000"}, + std::string_view{"float f0X3F800000"}, + std::string_view{"float 0X1.0p0"}, + std::string_view{"float 0x1p0"}, + }) { + Context.expect(!parseConstant(Text, Options), + "noncanonical LLVM floating syntax must be rejected"); + } +} + +void testAggregateConstants(TestContext &Context) { + expectRoundTrip(Context, "[4 x i8] c\"ab\\00\\FF\"", 4); + expectRoundTrip(Context, "[3 x i16] [i16 1, i16 2, i16 3]", 6); + expectRoundTrip(Context, "<4 x i32> ", 16); + expectRoundTrip(Context, "<4 x float> ", 16); + expectRoundTrip(Context, "{ i32, ptr } { i32 7, ptr @global }", 16); + expectRoundTrip(Context, "<{ i8, i32 }> <{ i8 1, i32 2 }>", 5); + expectRoundTrip(Context, "<8 x i16> splat (i16 9)", 16); +} + +void testAddressConstants(TestContext &Context) { + expectRoundTrip(Context, "ptr @global", 8); + expectRoundTrip(Context, "ptr @\"quoted name\"", 8); + expectRoundTrip(Context, "ptr blockaddress(@function, %entry)", 8); + expectRoundTrip(Context, "ptr dso_local_equivalent @function", 8); + expectRoundTrip(Context, "ptr no_cfi @function", 8); + expectRoundTrip(Context, "i64 ptrtoint (ptr @global to i64)", 8); + expectRoundTrip(Context, "ptr bitcast (ptr @global to ptr)", 8); + expectRoundTrip(Context, "i64 add nuw nsw (i64 1, i64 2)", 8); + expectRoundTrip(Context, "ptr getelementptr inbounds nusw nuw (i8, ptr @global, i64 4)", 8); + expectRoundTrip(Context, + "<2 x i32> shufflevector (<2 x i32> , " + "<2 x i32> , <2 x i32> )", + 8); +} + +void testConstantExpressionVerification(TestContext &Context) { + expectRoundTrip(Context, "i8 trunc (i16 257 to i8)", 1); + expectRoundTrip(Context, "i32 bitcast (float 1.0 to i32)", 4); + expectRoundTrip(Context, "ptr addrspacecast (ptr addrspace(1) @global to ptr)", 8); + expectRoundTrip(Context, "ptr inttoptr (i64 0 to ptr)", 8); + expectRoundTrip(Context, "i64 ptrtoaddr (ptr @global to i64)", 8); + expectRoundTrip(Context, "i64 ptrtoint (ptr @global to i64)", 8); + expectRoundTrip(Context, + "<2 x i32> insertelement (<2 x i32> , " + "i32 3, i64 0)", + 8); + expectRoundTrip(Context, "i32 extractelement (<2 x i32> , i64 0)", 4); + expectRoundTrip(Context, "ptr ptrauth (ptr @function, i32 1, i64 0, ptr null, ptr null)", 8); + + for (const std::string_view Text : { + std::string_view{"i16 trunc (i8 1 to i16)"}, + std::string_view{"i64 bitcast (float 1.0 to i64)"}, + std::string_view{"ptr bitcast (ptr addrspace(1) @global to ptr)"}, + std::string_view{"ptr addrspacecast (ptr @global to ptr)"}, + std::string_view{"ptr inttoptr (float 1.0 to ptr)"}, + std::string_view{"i64 ptrtoint (i64 0 to i64)"}, + std::string_view{"i32 add nuw (i32 1, i64 2)"}, + std::string_view{"ptr getelementptr (i8, ptr @global, float 1.0)"}, + std::string_view{"<2 x i32> shufflevector (<2 x i32> , " + "<2 x i32> , <2 x i64> )"}, + std::string_view{"ptr ptrauth (ptr @function, i64 1)"}, + }) { + Context.expect(!parseConstant(Text, Options), + "constant expression with incompatible typed operands must be rejected"); + } +} + +void testDataLayoutAndNamedTypes(TestContext &Context) { + static constexpr std::array Definitions{ + rcc::LLNamedTypeDefinition{.Name = "pair", .Definition = "{ i8, i64 }", .NumericName = false}, + rcc::LLNamedTypeDefinition{.Name = "word", .Definition = "i32", .NumericName = false}, + rcc::LLNamedTypeDefinition{ + .Name = "node", .Definition = "{ i32, %node* }", .NumericName = false}, + rcc::LLNamedTypeDefinition{.Name = "loop", .Definition = "{ %loop }", .NumericName = false}, + }; + static constexpr TestOptions LayoutOptions{ + .DataLayout = "e-p:64:64-p1:32:16:64-i64:64:128-i128:128-v96:128:256-a:0:64", + .NamedTypes = Definitions, + }; + + expectLayoutRoundTrip(Context, "i65 -1", LayoutOptions, 16, 16); + expectLayoutRoundTrip(Context, "[3 x i24] [i24 1, i24 2, i24 3]", LayoutOptions, 12, 4); + expectLayoutRoundTrip(Context, "<3 x i32> ", LayoutOptions, 16, 32); + expectLayoutRoundTrip(Context, "ptr addrspace(1) null", LayoutOptions, 4, 8); + expectLayoutRoundTrip(Context, "%pair { i8 1, i64 2 }", LayoutOptions, 16, 8); + expectLayoutRoundTrip(Context, "%word 7", LayoutOptions, 4, 4); + expectLayoutRoundTrip(Context, "%node zeroinitializer", LayoutOptions, 16, 8); + + Context.expect(!parseType("%loop", LayoutOptions), + "directly recursive named aggregate types must be rejected as unsized"); + auto NodeType = parseType("%node", LayoutOptions); + Context.expect(NodeType.has_value(), + "named recursion through a pointer must remain sized and legal"); +} + +void testAddressSpacePointerWidths(TestContext &Context) { + constexpr TestOptions HeterogeneousLayoutOptions{ + .DataLayout = "e-p:64:64-p1:32:16:64", + .NamedTypes = {}, + }; + auto DefaultPointer = pointerSizeInBits(0, HeterogeneousLayoutOptions); + auto AddressSpaceOnePointer = pointerSizeInBits(1, HeterogeneousLayoutOptions); + Context.expect(DefaultPointer.has_value() && *DefaultPointer == 64 && + AddressSpaceOnePointer.has_value() && *AddressSpaceOnePointer == 32, + "LLVM data layout must provide pointer width by address space"); + + auto MissingPointer = pointerSizeInBits(7); + Context.expect(!MissingPointer, + "pointer width lookup must reject a missing explicit data layout"); +} + +void testDataLayoutBoundaryRules(TestContext &Context) { + static constexpr TestOptions IntegerAlignmentOptions{ + .DataLayout = "e-p:64:64-i24:128:128", + .NamedTypes = {}, + }; + static constexpr TestOptions HistoricalAggregateOptions{ + .DataLayout = "e-p:64:64-a0:64:128", + .NamedTypes = {}, + }; + static constexpr TestOptions ZeroAggregateOptions{ + .DataLayout = "e-p:64:64-a:0:64", + .NamedTypes = {}, + }; + static constexpr TestOptions NonBytePointerOptions{ + .DataLayout = "e-p:33:8:8:32", + .NamedTypes = {}, + }; + static constexpr TestOptions PointerFlagsOptions{ + .DataLayout = "e-pue1(storage):33:8:8:32-po2(allones):64:64", + .NamedTypes = {}, + }; + + expectLayoutRoundTrip(Context, "i20 1", IntegerAlignmentOptions, 16, 16); + expectLayoutRoundTrip(Context, "{ i8, i32 } { i8 1, i32 2 }", HistoricalAggregateOptions, 8, 16); + expectLayoutRoundTrip(Context, "{ i8, i32 } { i8 1, i32 2 }", ZeroAggregateOptions, 8, 8); + expectLayoutRoundTrip(Context, "<{ i8, i32 }> <{ i8 1, i32 2 }>", ZeroAggregateOptions, 5, 8); + expectLayoutRoundTrip(Context, "ptr null", NonBytePointerOptions, 5, 1); + expectLayoutRoundTrip(Context, "ptr addrspace(1) null", PointerFlagsOptions, 5, 1); + + static constexpr TestOptions InvalidIndexSizeOptions{ + .DataLayout = "e-p:33:8:8:34", + .NamedTypes = {}, + }; + auto Pointer = parseType("ptr", InvalidIndexSizeOptions); + Context.expect(Pointer.has_value() && !allocationSize(*Pointer, InvalidIndexSizeOptions), + "LLVM pointer index size larger than its representation must be rejected"); + + static constexpr TestOptions NonLayoutOptions{ + .DataLayout = "e-m:e-n8:16:32-S128-Fi64-P1-G2-A3-ni:1:2-ve-s-p:64:64", + .NamedTypes = {}, + }; + expectLayoutRoundTrip(Context, "ptr null", NonLayoutOptions, 8, 8); + + const auto ExpectInvalidDataLayout = [&Context](std::string_view DataLayout) { + const TestOptions InvalidOptions{ + .DataLayout = DataLayout, + .NamedTypes = {}, + }; + auto Type = parseType("ptr", InvalidOptions); + Context.expect(Type.has_value() && !allocationSize(*Type, InvalidOptions), + "malformed non-layout LLVM data-layout component must be rejected"); + }; + for (const std::string_view DataLayout : { + std::string_view{"ee-p:64:64"}, + std::string_view{"m:q-p:64:64"}, + std::string_view{"n64:0-p:64:64"}, + std::string_view{"S-p:64:64"}, + std::string_view{"Fx64-p:64:64"}, + std::string_view{"P16777216-p:64:64"}, + std::string_view{"ni:-p:64:64"}, + std::string_view{"ni:0-p:64:64"}, + std::string_view{"pe:64:64"}, + std::string_view{"pu:64:64"}, + std::string_view{"pzo1:64:64"}, + std::string_view{"p1(P):64:64"}, + std::string_view{"p1(foo):64:64-p2(foo):64:64"}, + std::string_view{"p16777216:64:64"}, + }) { + ExpectInvalidDataLayout(DataLayout); + } +} + +void testInvalidConstants(TestContext &Context) { + for (const std::string_view Text : { + std::string_view{""}, + std::string_view{"opaque 0"}, + std::string_view{"i32 null"}, + std::string_view{"ptr 42"}, + std::string_view{"[2 x i8] [i8 1]"}, + std::string_view{"<2 x i32> "}, + std::string_view{"i64 ptrtoint (ptr @global to i32)"}, + std::string_view{"ptr getelementptr (i8, i64 0)"}, + std::string_view{"i32 1 trailing"}, + }) { + Context.expect(!parseConstant(Text, Options), "invalid LLVM constant must be rejected"); + } +} + +} // namespace + +int main() { + TestContext Context; + testScalarConstants(Context); + testExplicitHexIntegerConstants(Context); + testDecimalIntegerTruncation(Context); + testSpecialFloatingLiterals(Context); + testExactFloatingConversion(Context); + testAggregateConstants(Context); + testAddressConstants(Context); + testConstantExpressionVerification(Context); + testDataLayoutAndNamedTypes(Context); + testAddressSpacePointerWidths(Context); + testDataLayoutBoundaryRules(Context); + testInvalidConstants(Context); + return Context.result(); +} diff --git a/backend/unittests/CMakeLists.txt b/backend/unittests/CMakeLists.txt index 87e42b3..59e3c24 100644 --- a/backend/unittests/CMakeLists.txt +++ b/backend/unittests/CMakeLists.txt @@ -2,6 +2,7 @@ add_subdirectory(Support) add_subdirectory(MC) +add_subdirectory(AsmParser) add_subdirectory(Target) add_subdirectory(CodeGen) add_subdirectory(MIR) diff --git a/backend/unittests/MIR/CMakeLists.txt b/backend/unittests/MIR/CMakeLists.txt index ae72452..8a8a8b4 100644 --- a/backend/unittests/MIR/CMakeLists.txt +++ b/backend/unittests/MIR/CMakeLists.txt @@ -6,15 +6,6 @@ target_link_libraries(RCCBackendEmbeddedIRTests PRIVATE RCCBackend::MIR) add_test(NAME RCCBackend.MIR.EmbeddedIR COMMAND RCCBackendEmbeddedIRTests) -add_executable(RCCBackendLLVMConstantSyntaxTests LLVMConstantSyntaxTest.cpp) -rcc_backend_configure_target(RCCBackendLLVMConstantSyntaxTests) -target_link_libraries(RCCBackendLLVMConstantSyntaxTests PRIVATE RCCBackend::MIR) - -add_test( - NAME RCCBackend.MIR.LLVMConstantSyntax - COMMAND RCCBackendLLVMConstantSyntaxTests -) - add_executable(RCCBackendMILexerTests MILexerTest.cpp) rcc_backend_configure_target(RCCBackendMILexerTests) target_include_directories( @@ -49,14 +40,12 @@ add_test(NAME RCCBackend.MIR.MIRDocument COMMAND RCCBackendMIRDocumentTests) rcc_backend_register_format_files( "${CMAKE_CURRENT_SOURCE_DIR}/EmbeddedIRTest.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/LLVMConstantSyntaxTest.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/MILexerTest.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/MIRDocumentTest.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/MIParserTest.cpp" ) rcc_backend_register_tidy_files( "${CMAKE_CURRENT_SOURCE_DIR}/EmbeddedIRTest.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/LLVMConstantSyntaxTest.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/MILexerTest.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/MIRDocumentTest.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/MIParserTest.cpp" diff --git a/backend/unittests/MIR/LLVMConstantSyntaxTest.cpp b/backend/unittests/MIR/LLVMConstantSyntaxTest.cpp deleted file mode 100644 index c10fc68..0000000 --- a/backend/unittests/MIR/LLVMConstantSyntaxTest.cpp +++ /dev/null @@ -1,515 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "rcc/MIR/LLVMConstantSyntax.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace { - -class TestContext final { -public: - void expect(bool Condition, std::string_view Message) { - if (Condition) - return; - std::cerr << Message << '\n'; - ++FailureCount; - } - - [[nodiscard]] int result() const noexcept { - return FailureCount == 0 ? 0 : 1; - } - -private: - int FailureCount = 0; -}; - -constexpr rcc::mir::LLVMConstantSyntaxOptions Options{ - .DataLayout = "e-p:64:64", - .NamedTypes = {}, -}; - -void expectRoundTrip(TestContext &Context, std::string_view Text, std::uint64_t ExpectedSize) { - auto Parsed = rcc::mir::parseLLVMConstant(Text, Options); - Context.expect(Parsed.has_value(), "valid LLVM constant must parse"); - if (!Parsed) { - std::cerr << Text << ": " << Parsed.error().message() << '\n'; - return; - } - auto Size = rcc::mir::llvmConstantStorageSize(Parsed->Type, Options); - if (!Size || *Size != ExpectedSize) { - std::cerr << Text << ": expected storage size " << ExpectedSize << ", got "; - if (Size) - std::cerr << *Size; - else - std::cerr << Size.error().message(); - std::cerr << '\n'; - } - Context.expect(Size.has_value() && *Size == ExpectedSize, - "LLVM constant storage size must match its structured type"); - auto Printed = rcc::mir::printLLVMConstant(*Parsed); - Context.expect(Printed.has_value(), "structured LLVM constant must print"); - if (!Printed) - return; - auto Reparsing = rcc::mir::parseLLVMConstant(*Printed, Options); - Context.expect(Reparsing.has_value() && *Reparsing == *Parsed, - "LLVM constant parse-print-parse must preserve the value tree"); - if (!Reparsing) - std::cerr << *Printed << ": " << Reparsing.error().message() << '\n'; -} - -void expectLayoutRoundTrip(TestContext &Context, std::string_view Text, - rcc::mir::LLVMConstantSyntaxOptions LayoutOptions, - std::uint64_t ExpectedSize, std::uint64_t ExpectedPreferredAlignment) { - auto Parsed = rcc::mir::parseLLVMConstant(Text, LayoutOptions); - Context.expect(Parsed.has_value(), "DataLayout-aware LLVM constant must parse"); - if (!Parsed) { - std::cerr << Text << ": " << Parsed.error().message() << '\n'; - return; - } - auto Size = rcc::mir::llvmConstantStorageSize(Parsed->Type, LayoutOptions); - auto Alignment = rcc::mir::llvmConstantPreferredAlignment(Parsed->Type, LayoutOptions); - Context.expect(Size.has_value() && *Size == ExpectedSize, - "DataLayout must determine the exact allocation size"); - if (!Size || *Size != ExpectedSize) { - std::cerr << Text << ": expected size " << ExpectedSize << ", got " - << (Size ? std::to_string(*Size) : Size.error().message()) << '\n'; - } - Context.expect(Alignment.has_value() && Alignment->bytes() == ExpectedPreferredAlignment, - "DataLayout must determine the exact preferred alignment"); - if (!Alignment || Alignment->bytes() != ExpectedPreferredAlignment) { - std::cerr << Text << ": expected preferred alignment " << ExpectedPreferredAlignment << ", got " - << (Alignment ? std::to_string(Alignment->bytes()) : Alignment.error().message()) - << '\n'; - } - auto Printed = rcc::mir::printLLVMConstant(*Parsed); - Context.expect(Printed.has_value(), "DataLayout-aware LLVM constant must print"); - if (!Printed) - return; - auto Reparsing = rcc::mir::parseLLVMConstant(*Printed, LayoutOptions); - Context.expect(Reparsing.has_value() && *Reparsing == *Parsed, - "DataLayout-aware named constants must round-trip structurally"); -} - -void testScalarConstants(TestContext &Context) { - expectRoundTrip(Context, "i1 true", 1); - expectRoundTrip(Context, "i65 -1", 12); - expectRoundTrip(Context, "half 0xH3C00", 2); - expectRoundTrip(Context, "bfloat 0xR3F80", 2); - expectRoundTrip(Context, "float 1.250000e+00", 4); - expectRoundTrip(Context, "double 0x3FF0000000000000", 8); - expectRoundTrip(Context, "x86_fp80 f0x4000C000000000000000", 16); - expectRoundTrip(Context, "fp128 f0x3FFF0000000000000000000000000000", 16); - expectRoundTrip(Context, "ptr null", 8); - expectRoundTrip(Context, "i32 undef", 4); - expectRoundTrip(Context, "i32 poison", 4); - expectRoundTrip(Context, "i32 zeroinitializer", 4); - - auto BFloat = rcc::mir::parseLLVMConstant("bfloat 0xR3E80", Options); - auto PrintedBFloat = BFloat ? rcc::mir::printLLVMConstant(*BFloat) - : rcc::Expected(std::unexpected(BFloat.error())); - Context.expect(PrintedBFloat && *PrintedBFloat == "bfloat 2.500000e-01", - "LLVM-compatible floating printing must prefer an exact decimal spelling"); - - auto Pi = rcc::mir::parseLLVMConstant("double f0x400921FB54442D18", Options); - auto PrintedPi = Pi ? rcc::mir::printLLVMConstant(*Pi) - : rcc::Expected(std::unexpected(Pi.error())); - Context.expect( - PrintedPi && *PrintedPi == "double f0x400921FB54442D18", - "floating printing must retain exact bits when six decimal digits do not round-trip"); -} - -void expectIntegerWords(TestContext &Context, std::string_view Text, - std::span ExpectedWords) { - auto Parsed = rcc::mir::parseLLVMConstant(Text, Options); - Context.expect(Parsed.has_value() && Parsed->IntegerValue.has_value() && - std::ranges::equal(Parsed->IntegerValue->words(), ExpectedWords), - "LLVM integer literal must preserve its exact bit pattern"); - if (!Parsed) - std::cerr << Text << ": " << Parsed.error().message() << '\n'; -} - -void testDecimalIntegerTruncation(TestContext &Context) { - constexpr std::array NegativeOne{std::numeric_limits::max()}; - constexpr std::array NarrowNegative{767}; - constexpr std::array NarrowMinimum{8}; - constexpr std::array WidePositive{1}; - - expectIntegerWords(Context, "i64 -1", NegativeOne); - expectIntegerWords(Context, "i16 -64769", NarrowNegative); - expectIntegerWords(Context, "i4 -8", NarrowMinimum); - expectIntegerWords(Context, "i64 18446744073709551617", WidePositive); -} - -void expectFloatingWords(TestContext &Context, std::string_view Text, - std::span ExpectedWords) { - auto Parsed = rcc::mir::parseLLVMConstant(Text, Options); - Context.expect(Parsed.has_value() && Parsed->FloatingValue.has_value() && - std::ranges::equal(Parsed->FloatingValue->words(), ExpectedWords), - "LLVM floating special literal must preserve its exact bit pattern"); - if (!Parsed) { - std::cerr << Text << ": " << Parsed.error().message() << '\n'; - return; - } - if (!Parsed->FloatingValue || - !std::ranges::equal(Parsed->FloatingValue->words(), ExpectedWords)) { - std::cerr << Text << ": expected words"; - for (const std::uint64_t Word : ExpectedWords) - std::cerr << " 0x" << std::hex << Word; - std::cerr << ", got"; - if (Parsed->FloatingValue) { - for (const std::uint64_t Word : Parsed->FloatingValue->words()) - std::cerr << " 0x" << std::hex << Word; - } - std::cerr << std::dec << '\n'; - } - auto Printed = rcc::mir::printLLVMConstant(*Parsed); - Context.expect(Printed.has_value(), "LLVM floating special literal must print"); - if (!Printed) - return; - auto Reparsing = rcc::mir::parseLLVMConstant(*Printed, Options); - Context.expect(Reparsing.has_value() && *Reparsing == *Parsed, - "printed LLVM floating special literal must round-trip"); -} - -void testExplicitHexIntegerConstants(TestContext &Context) { - constexpr std::array AllOnes65{std::numeric_limits::max(), 1}; - constexpr std::array SignBit65{0, 1}; - constexpr std::array UnsignedFF{0xff}; - constexpr std::array SignedFF{0xffff}; - constexpr std::array SignedFF8{0xff}; - - expectIntegerWords(Context, "i65 u0x1FFFFFFFFFFFFFFFF", AllOnes65); - expectIntegerWords(Context, "i65 s0x1FFFFFFFFFFFFFFFF", AllOnes65); - expectIntegerWords(Context, "i65 s0x10000000000000000", SignBit65); - expectIntegerWords(Context, "i65 s0x01", AllOnes65); - expectIntegerWords(Context, "i16 u0xFF", UnsignedFF); - expectIntegerWords(Context, "i16 s0xFF", SignedFF); - expectIntegerWords(Context, "i8 s0x1FF", SignedFF8); - - expectRoundTrip(Context, "i65 s0x01", 12); - Context.expect(!rcc::mir::parseLLVMConstant("i32 u0x", Options) && - !rcc::mir::parseLLVMConstant("i32 u0X1", Options), - "explicit LLVM hexadecimal integers must use the fixed spelling"); -} - -void testSpecialFloatingLiterals(TestContext &Context) { - constexpr std::array HalfInfinity{0x7c00}; - constexpr std::array NegativeHalfInfinity{0xfc00}; - constexpr std::array HalfQuietNaN{0x7e00}; - constexpr std::array NegativeHalfQuietNaN{0xfe00}; - constexpr std::array HalfSignalingNaN{0x7d00}; - constexpr std::array HalfPayloadNaN{0x7fef}; - constexpr std::array FloatSignalingNaN{0x7f800001}; - constexpr std::array X87Infinity{0x8000000000000000, 0x7fff}; - constexpr std::array X87QuietNaN{0xc000000000000000, 0x7fff}; - constexpr std::array QuadOne{0, 0x3fff000000000000}; - constexpr std::array QuadInfinity{0, 0x7fff000000000000}; - constexpr std::array QuadQuietNaN{0, 0x7fff800000000000}; - constexpr std::array PPCOne{0x3ff0000000000000, 0}; - constexpr std::array PPCInfinity{0x7ff0000000000000, 0}; - constexpr std::array PPCQuietNaN{0x7ff8000000000000, 0}; - - expectFloatingWords(Context, "half +inf", HalfInfinity); - expectFloatingWords(Context, "half -inf", NegativeHalfInfinity); - expectFloatingWords(Context, "half +qnan", HalfQuietNaN); - expectFloatingWords(Context, "half -qnan", NegativeHalfQuietNaN); - expectFloatingWords(Context, "half +snan(0x100)", HalfSignalingNaN); - expectFloatingWords(Context, "half +nan(0x123456789ABCDEF)", HalfPayloadNaN); - expectFloatingWords(Context, "float +snan(0x400001)", FloatSignalingNaN); - expectFloatingWords(Context, "x86_fp80 +inf", X87Infinity); - expectFloatingWords(Context, "x86_fp80 +qnan", X87QuietNaN); - expectFloatingWords(Context, "fp128 +inf", QuadInfinity); - expectFloatingWords(Context, "fp128 +qnan", QuadQuietNaN); - expectFloatingWords(Context, "fp128 0xL00000000000000003FFF000000000000", QuadOne); - expectFloatingWords(Context, "ppc_fp128 +inf", PPCInfinity); - expectFloatingWords(Context, "ppc_fp128 +qnan", PPCQuietNaN); - expectFloatingWords(Context, "ppc_fp128 0xM3FF00000000000000000000000000000", PPCOne); - - for (const std::string_view Text : { - std::string_view{"float nan(0x)"}, - std::string_view{"float snan(12)"}, - std::string_view{"float qnan(0x1)"}, - std::string_view{"float inf(0x1)"}, - std::string_view{"float inf"}, - std::string_view{"float +nan"}, - std::string_view{"float +snan"}, - std::string_view{"float +qnan(0x1)"}, - }) { - Context.expect(!rcc::mir::parseLLVMConstant(Text, Options), - "malformed LLVM floating special literal must be rejected"); - } -} - -void testExactFloatingConversion(TestContext &Context) { - constexpr std::array PositiveZero{0}; - constexpr std::array NegativeFloatZero{0x80000000}; - constexpr std::array HalfOne{0x3c00}; - constexpr std::array HalfTwoUlps{0x3c02}; - constexpr std::array HalfSmallestSubnormal{0x0001}; - constexpr std::array BFloatOne{0x3f80}; - constexpr std::array BFloatTwoUlps{0x3f82}; - constexpr std::array FloatOne{0x3f800000}; - constexpr std::array FloatTwoUlps{0x3f800002}; - constexpr std::array FloatHexPower{0x2f800000}; - constexpr std::array FloatSignalingNaN{0x7f800001}; - constexpr std::array FloatQuietNaN{0x7fc00000}; - constexpr std::array LargestDouble{0x7fefffffffffffff}; - constexpr std::array X87One{0x8000000000000000, 0x3fff}; - constexpr std::array QuadOne{0, 0x3fff000000000000}; - constexpr std::array PPCOne{0x3ff0000000000000, 0}; - - expectFloatingWords(Context, "float +0.0", PositiveZero); - expectFloatingWords(Context, "float -0.0", NegativeFloatZero); - expectFloatingWords(Context, "half 1.00048828125", HalfOne); - expectFloatingWords(Context, "half 1.00146484375", HalfTwoUlps); - expectFloatingWords(Context, "half 0.0000000298023223876953126", HalfSmallestSubnormal); - expectFloatingWords(Context, "bfloat 1.00390625", BFloatOne); - expectFloatingWords(Context, "bfloat 1.01171875", BFloatTwoUlps); - expectFloatingWords(Context, "float 1.000000059604644775390625", FloatOne); - expectFloatingWords(Context, "float 1.000000178813934326171875", FloatTwoUlps); - expectFloatingWords(Context, "float 0x1.0p-32", FloatHexPower); - expectFloatingWords(Context, "double 1.79769313486231570815e+308", LargestDouble); - expectFloatingWords(Context, "x86_fp80 1.0", X87One); - expectFloatingWords(Context, "x86_fp80 0x1.0p0", X87One); - expectFloatingWords(Context, "fp128 1.0", QuadOne); - expectFloatingWords(Context, "fp128 0x1.0p0", QuadOne); - expectFloatingWords(Context, "ppc_fp128 1.0", PPCOne); - expectFloatingWords(Context, "ppc_fp128 0x1.0p0", PPCOne); - expectFloatingWords(Context, "float 0x3FF0000000000000", FloatOne); - expectFloatingWords(Context, "float 0x7FF0000020000000", FloatSignalingNaN); - expectFloatingWords(Context, "float 0x7FF8000000000000", FloatQuietNaN); - expectFloatingWords(Context, "float f0x3F800000", FloatOne); - - for (const std::string_view Text : { - std::string_view{"half 0.0000000298023223876953125"}, - std::string_view{"float 0x3FF0000000000001"}, - std::string_view{"float 0x7FF8000000000001"}, - std::string_view{"float f0x3F8000"}, - std::string_view{"float f0X3F800000"}, - std::string_view{"float 0X1.0p0"}, - std::string_view{"float 0x1p0"}, - }) { - Context.expect(!rcc::mir::parseLLVMConstant(Text, Options), - "noncanonical LLVM floating syntax must be rejected"); - } -} - -void testAggregateConstants(TestContext &Context) { - expectRoundTrip(Context, "[4 x i8] c\"ab\\00\\FF\"", 4); - expectRoundTrip(Context, "[3 x i16] [i16 1, i16 2, i16 3]", 6); - expectRoundTrip(Context, "<4 x i32> ", 16); - expectRoundTrip(Context, "<4 x float> ", 16); - expectRoundTrip(Context, "{ i32, ptr } { i32 7, ptr @global }", 16); - expectRoundTrip(Context, "<{ i8, i32 }> <{ i8 1, i32 2 }>", 5); - expectRoundTrip(Context, "<8 x i16> splat (i16 9)", 16); -} - -void testAddressConstants(TestContext &Context) { - expectRoundTrip(Context, "ptr @global", 8); - expectRoundTrip(Context, "ptr @\"quoted name\"", 8); - expectRoundTrip(Context, "ptr blockaddress(@function, %entry)", 8); - expectRoundTrip(Context, "ptr dso_local_equivalent @function", 8); - expectRoundTrip(Context, "ptr no_cfi @function", 8); - expectRoundTrip(Context, "i64 ptrtoint (ptr @global to i64)", 8); - expectRoundTrip(Context, "ptr bitcast (ptr @global to ptr)", 8); - expectRoundTrip(Context, "i64 add nuw nsw (i64 1, i64 2)", 8); - expectRoundTrip(Context, "ptr getelementptr inbounds nusw nuw (i8, ptr @global, i64 4)", 8); - expectRoundTrip(Context, - "<2 x i32> shufflevector (<2 x i32> , " - "<2 x i32> , <2 x i32> )", - 8); -} - -void testConstantExpressionVerification(TestContext &Context) { - expectRoundTrip(Context, "i8 trunc (i16 257 to i8)", 1); - expectRoundTrip(Context, "i32 bitcast (float 1.0 to i32)", 4); - expectRoundTrip(Context, "ptr addrspacecast (ptr addrspace(1) @global to ptr)", 8); - expectRoundTrip(Context, "ptr inttoptr (i64 0 to ptr)", 8); - expectRoundTrip(Context, "i64 ptrtoaddr (ptr @global to i64)", 8); - expectRoundTrip(Context, "i64 ptrtoint (ptr @global to i64)", 8); - expectRoundTrip(Context, - "<2 x i32> insertelement (<2 x i32> , " - "i32 3, i64 0)", - 8); - expectRoundTrip(Context, "i32 extractelement (<2 x i32> , i64 0)", 4); - expectRoundTrip(Context, "ptr ptrauth (ptr @function, i32 1, i64 0, ptr null, ptr null)", 8); - - for (const std::string_view Text : { - std::string_view{"i16 trunc (i8 1 to i16)"}, - std::string_view{"i64 bitcast (float 1.0 to i64)"}, - std::string_view{"ptr bitcast (ptr addrspace(1) @global to ptr)"}, - std::string_view{"ptr addrspacecast (ptr @global to ptr)"}, - std::string_view{"ptr inttoptr (float 1.0 to ptr)"}, - std::string_view{"i64 ptrtoint (i64 0 to i64)"}, - std::string_view{"i32 add nuw (i32 1, i64 2)"}, - std::string_view{"ptr getelementptr (i8, ptr @global, float 1.0)"}, - std::string_view{"<2 x i32> shufflevector (<2 x i32> , " - "<2 x i32> , <2 x i64> )"}, - std::string_view{"ptr ptrauth (ptr @function, i64 1)"}, - }) { - Context.expect(!rcc::mir::parseLLVMConstant(Text, Options), - "constant expression with incompatible typed operands must be rejected"); - } -} - -void testDataLayoutAndNamedTypes(TestContext &Context) { - static constexpr std::array Definitions{ - rcc::mir::LLVMNamedTypeDefinition{ - .Name = "pair", .Definition = "{ i8, i64 }", .NumericName = false}, - rcc::mir::LLVMNamedTypeDefinition{.Name = "word", .Definition = "i32", .NumericName = false}, - rcc::mir::LLVMNamedTypeDefinition{ - .Name = "node", .Definition = "{ i32, %node* }", .NumericName = false}, - rcc::mir::LLVMNamedTypeDefinition{ - .Name = "loop", .Definition = "{ %loop }", .NumericName = false}, - }; - static constexpr rcc::mir::LLVMConstantSyntaxOptions LayoutOptions{ - .DataLayout = "e-p:64:64-p1:32:16:64-i64:64:128-i128:128-v96:128:256-a:0:64", - .NamedTypes = Definitions, - }; - - expectLayoutRoundTrip(Context, "i65 -1", LayoutOptions, 16, 16); - expectLayoutRoundTrip(Context, "[3 x i24] [i24 1, i24 2, i24 3]", LayoutOptions, 12, 4); - expectLayoutRoundTrip(Context, "<3 x i32> ", LayoutOptions, 16, 32); - expectLayoutRoundTrip(Context, "ptr addrspace(1) null", LayoutOptions, 4, 8); - expectLayoutRoundTrip(Context, "%pair { i8 1, i64 2 }", LayoutOptions, 16, 8); - expectLayoutRoundTrip(Context, "%word 7", LayoutOptions, 4, 4); - expectLayoutRoundTrip(Context, "%node zeroinitializer", LayoutOptions, 16, 8); - - Context.expect(!rcc::mir::parseLLVMType("%loop", LayoutOptions), - "directly recursive named aggregate types must be rejected as unsized"); - auto NodeType = rcc::mir::parseLLVMType("%node", LayoutOptions); - Context.expect(NodeType.has_value(), - "named recursion through a pointer must remain sized and legal"); -} - -void testAddressSpacePointerWidths(TestContext &Context) { - constexpr rcc::mir::LLVMConstantSyntaxOptions HeterogeneousLayoutOptions{ - .DataLayout = "e-p:64:64-p1:32:16:64", - .NamedTypes = {}, - }; - auto DefaultPointer = rcc::mir::llvmPointerSizeInBits(0, HeterogeneousLayoutOptions); - auto AddressSpaceOnePointer = rcc::mir::llvmPointerSizeInBits(1, HeterogeneousLayoutOptions); - Context.expect(DefaultPointer.has_value() && *DefaultPointer == 64 && - AddressSpaceOnePointer.has_value() && *AddressSpaceOnePointer == 32, - "LLVM data layout must provide pointer width by address space"); - - auto MissingPointer = rcc::mir::llvmPointerSizeInBits(7); - Context.expect(!MissingPointer, - "pointer width lookup must reject a missing explicit data layout"); -} - -void testDataLayoutBoundaryRules(TestContext &Context) { - static constexpr rcc::mir::LLVMConstantSyntaxOptions IntegerAlignmentOptions{ - .DataLayout = "e-p:64:64-i24:128:128", - .NamedTypes = {}, - }; - static constexpr rcc::mir::LLVMConstantSyntaxOptions HistoricalAggregateOptions{ - .DataLayout = "e-p:64:64-a0:64:128", - .NamedTypes = {}, - }; - static constexpr rcc::mir::LLVMConstantSyntaxOptions ZeroAggregateOptions{ - .DataLayout = "e-p:64:64-a:0:64", - .NamedTypes = {}, - }; - static constexpr rcc::mir::LLVMConstantSyntaxOptions NonBytePointerOptions{ - .DataLayout = "e-p:33:8:8:32", - .NamedTypes = {}, - }; - static constexpr rcc::mir::LLVMConstantSyntaxOptions PointerFlagsOptions{ - .DataLayout = "e-pue1(storage):33:8:8:32-po2(allones):64:64", - .NamedTypes = {}, - }; - - expectLayoutRoundTrip(Context, "i20 1", IntegerAlignmentOptions, 16, 16); - expectLayoutRoundTrip(Context, "{ i8, i32 } { i8 1, i32 2 }", HistoricalAggregateOptions, 8, 16); - expectLayoutRoundTrip(Context, "{ i8, i32 } { i8 1, i32 2 }", ZeroAggregateOptions, 8, 8); - expectLayoutRoundTrip(Context, "<{ i8, i32 }> <{ i8 1, i32 2 }>", ZeroAggregateOptions, 5, 8); - expectLayoutRoundTrip(Context, "ptr null", NonBytePointerOptions, 5, 1); - expectLayoutRoundTrip(Context, "ptr addrspace(1) null", PointerFlagsOptions, 5, 1); - - static constexpr rcc::mir::LLVMConstantSyntaxOptions InvalidIndexSizeOptions{ - .DataLayout = "e-p:33:8:8:34", - .NamedTypes = {}, - }; - auto Pointer = rcc::mir::parseLLVMType("ptr", InvalidIndexSizeOptions); - Context.expect(Pointer.has_value() && - !rcc::mir::llvmConstantStorageSize(*Pointer, InvalidIndexSizeOptions), - "LLVM pointer index size larger than its representation must be rejected"); - - static constexpr rcc::mir::LLVMConstantSyntaxOptions NonLayoutOptions{ - .DataLayout = "e-m:e-n8:16:32-S128-Fi64-P1-G2-A3-ni:1:2-ve-s-p:64:64", - .NamedTypes = {}, - }; - expectLayoutRoundTrip(Context, "ptr null", NonLayoutOptions, 8, 8); - - const auto ExpectInvalidDataLayout = [&Context](std::string_view DataLayout) { - const rcc::mir::LLVMConstantSyntaxOptions InvalidOptions{ - .DataLayout = DataLayout, - .NamedTypes = {}, - }; - auto Type = rcc::mir::parseLLVMType("ptr", InvalidOptions); - Context.expect(Type.has_value() && !rcc::mir::llvmConstantStorageSize(*Type, InvalidOptions), - "malformed non-layout LLVM data-layout component must be rejected"); - }; - for (const std::string_view DataLayout : { - std::string_view{"ee-p:64:64"}, - std::string_view{"m:q-p:64:64"}, - std::string_view{"n64:0-p:64:64"}, - std::string_view{"S-p:64:64"}, - std::string_view{"Fx64-p:64:64"}, - std::string_view{"P16777216-p:64:64"}, - std::string_view{"ni:-p:64:64"}, - std::string_view{"ni:0-p:64:64"}, - std::string_view{"pe:64:64"}, - std::string_view{"pu:64:64"}, - std::string_view{"pzo1:64:64"}, - std::string_view{"p1(P):64:64"}, - std::string_view{"p1(foo):64:64-p2(foo):64:64"}, - std::string_view{"p16777216:64:64"}, - }) { - ExpectInvalidDataLayout(DataLayout); - } -} - -void testInvalidConstants(TestContext &Context) { - for (const std::string_view Text : { - std::string_view{""}, - std::string_view{"opaque 0"}, - std::string_view{"i32 null"}, - std::string_view{"ptr 42"}, - std::string_view{"[2 x i8] [i8 1]"}, - std::string_view{"<2 x i32> "}, - std::string_view{"i64 ptrtoint (ptr @global to i32)"}, - std::string_view{"ptr getelementptr (i8, i64 0)"}, - std::string_view{"i32 1 trailing"}, - }) { - Context.expect(!rcc::mir::parseLLVMConstant(Text, Options), - "invalid LLVM constant must be rejected"); - } -} - -} // namespace - -int main() { - TestContext Context; - testScalarConstants(Context); - testExplicitHexIntegerConstants(Context); - testDecimalIntegerTruncation(Context); - testSpecialFloatingLiterals(Context); - testExactFloatingConversion(Context); - testAggregateConstants(Context); - testAddressConstants(Context); - testConstantExpressionVerification(Context); - testDataLayoutAndNamedTypes(Context); - testAddressSpacePointerWidths(Context); - testDataLayoutBoundaryRules(Context); - testInvalidConstants(Context); - return Context.result(); -} -- cgit v1.2.3