diff options
| author | Richard-Qin-X <richard.qin.001@gmail.com> | 2026-08-13 06:52:15 +0800 |
|---|---|---|
| committer | Richard-Qin-X <richard.qin.001@gmail.com> | 2026-08-13 06:52:15 +0800 |
| commit | aa7c85ca71859aab15bdf1e6dc1ea3a5aa5d1e28 (patch) | |
| tree | 20d94c4debfe4900e362c6c7f8443bcfbd58ec28 /backend | |
| parent | d62f022c92fcc3ebe2269273628943a0089683fd (diff) | |
| download | rcc-project-aa7c85ca71859aab15bdf1e6dc1ea3a5aa5d1e28.tar.gz rcc-project-aa7c85ca71859aab15bdf1e6dc1ea3a5aa5d1e28.tar.xz | |
[Support] Split APInt and APFloat ownership
Diffstat (limited to 'backend')
34 files changed, 1994 insertions, 2004 deletions
diff --git a/backend/include/rcc/ADT/APFloat.h b/backend/include/rcc/ADT/APFloat.h new file mode 100644 index 0000000..9b56a19 --- /dev/null +++ b/backend/include/rcc/ADT/APFloat.h @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_ADT_APFLOAT_H +#define RCC_ADT_APFLOAT_H + +#include "rcc/Support/Error.h" + +#include <array> +#include <cstdint> +#include <optional> +#include <span> +#include <string> +#include <string_view> + +namespace rcc { + +enum class FloatSemantics : std::uint8_t { + IEEEHalf, + BFloat, + IEEESingle, + IEEEDouble, + IEEEQuad, + PPCDoubleDouble, + Float8E5M2, + Float8E5M2FNUZ, + Float8E4M3, + Float8E4M3FN, + Float8E4M3FNUZ, + Float8E4M3B11FNUZ, + Float8E3M4, + FloatTF32, + Float8E8M0FNU, + Float6E3M2FN, + Float6E2M3FN, + Float4E2M1FN, + X87DoubleExtended, + Count, +}; + +[[nodiscard]] std::optional<std::uint32_t> +getFloatSemanticsBitWidth(FloatSemantics Semantics) noexcept; +[[nodiscard]] std::string_view getFloatSemanticsSpelling(FloatSemantics Semantics) noexcept; + +class APFloat final { +public: + [[nodiscard]] static Expected<APFloat> create(FloatSemantics Semantics, + std::span<const std::uint64_t> Words); + [[nodiscard]] static Expected<APFloat> parse(FloatSemantics Semantics, std::string_view Text); + + [[nodiscard]] FloatSemantics semantics() const noexcept { + return Semantics; + } + + [[nodiscard]] std::uint32_t bitWidth() const noexcept { + return getFloatSemanticsBitWidth(Semantics).value_or(0); + } + + [[nodiscard]] std::span<const std::uint64_t> words() const noexcept { + return std::span(Words).first(WordCount); + } + + [[nodiscard]] std::string toString() const; + + friend bool operator==(const APFloat &Left, const APFloat &Right) = default; + +private: + APFloat(FloatSemantics Semantics, std::array<std::uint64_t, 2> Words, + std::uint8_t WordCount) noexcept + : Semantics(Semantics), Words(Words), WordCount(WordCount) {} + + FloatSemantics Semantics; + std::array<std::uint64_t, 2> Words; + std::uint8_t WordCount; +}; + +} // namespace rcc + +#endif // RCC_ADT_APFLOAT_H diff --git a/backend/include/rcc/ADT/APInt.h b/backend/include/rcc/ADT/APInt.h new file mode 100644 index 0000000..abd10f9 --- /dev/null +++ b/backend/include/rcc/ADT/APInt.h @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_ADT_APINT_H +#define RCC_ADT_APINT_H + +#include "rcc/Support/Error.h" + +#include <cstddef> +#include <cstdint> +#include <span> +#include <string> +#include <string_view> +#include <utility> +#include <vector> + +namespace rcc { + +class APInt final { +public: + static constexpr std::uint32_t MaxBitWidth = std::uint32_t{1} << 23U; + + APInt() = default; + explicit APInt(std::uint64_t Value); + + [[nodiscard]] static Expected<APInt> create(std::uint32_t BitWidth, + std::span<const std::uint64_t> Words); + [[nodiscard]] static Expected<APInt> fromUnsigned(std::uint32_t BitWidth, std::uint64_t Value); + [[nodiscard]] static Expected<APInt> fromSigned(std::uint32_t BitWidth, std::int64_t Value); + [[nodiscard]] static Expected<APInt> parseDecimal(std::uint32_t BitWidth, std::string_view Text); + [[nodiscard]] static Expected<APInt> parseLLVMHex(std::uint32_t BitWidth, std::string_view Text); + + [[nodiscard]] std::uint32_t bitWidth() const noexcept { + return BitWidth; + } + + [[nodiscard]] std::span<const std::uint64_t> words() const noexcept { + return Words; + } + + [[nodiscard]] bool isZero() const noexcept; + [[nodiscard]] bool isNegative() const noexcept; + [[nodiscard]] std::size_t activeBitWidth() const noexcept; + [[nodiscard]] std::size_t countrZero() const noexcept; + [[nodiscard]] std::uint64_t word(std::size_t Index) const noexcept; + [[nodiscard]] int compare(const APInt &Other) const noexcept; + [[nodiscard]] int compareToShifted(const APInt &Other, std::size_t OtherShift) const noexcept; + + void multiplySmall(std::uint32_t Multiplier); + void addSmall(std::uint32_t Value); + void setBit(std::size_t Bit); + void clearBit(std::size_t Bit) noexcept; + void shiftRight(std::size_t Amount) noexcept; + [[nodiscard]] APInt shiftedLeft(std::size_t Amount) const; + void subtract(const APInt &Other) noexcept; + + [[nodiscard]] std::string toUnsignedDecimal() const; + [[nodiscard]] std::string toSignedDecimal() const; + [[nodiscard]] Expected<void> verify() const; + + friend bool operator==(const APInt &Left, const APInt &Right) = default; + +private: + APInt(std::uint32_t BitWidth, std::vector<std::uint64_t> Words) + : BitWidth(BitWidth), Words(std::move(Words)) {} + + void normalizeMagnitude() noexcept; + + std::uint32_t BitWidth = 0; + std::vector<std::uint64_t> Words; +}; + +} // namespace rcc + +#endif // RCC_ADT_APINT_H diff --git a/backend/include/rcc/CodeGen/GenericConstant.h b/backend/include/rcc/CodeGen/GenericConstant.h index 92a8f13..4219c24 100644 --- a/backend/include/rcc/CodeGen/GenericConstant.h +++ b/backend/include/rcc/CodeGen/GenericConstant.h @@ -3,8 +3,9 @@ #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/CodeGen/MachineOperand.h" #include "rcc/Support/Error.h" #include <cstddef> @@ -144,8 +145,8 @@ private: struct GenericConstantData final { GenericConstantType Type; GenericConstantKind Kind = GenericConstantKind::Invalid; - std::optional<IntegerImmediate> IntegerValue; - std::optional<FloatingPointBits> FloatingValue; + std::optional<APInt> IntegerValue; + std::optional<APFloat> FloatingValue; std::vector<std::byte> ByteValue; std::vector<GenericConstantData> Elements; std::string Symbol; @@ -155,11 +156,11 @@ struct GenericConstantData final { GenericConstantExpressionOpcode ExpressionOpcode = GenericConstantExpressionOpcode::Invalid; GenericConstantExpressionFlags ExpressionFlags; std::optional<GenericConstantType> SourceElementType; - std::optional<IntegerImmediate> InRangeStart; - std::optional<IntegerImmediate> InRangeEnd; + std::optional<APInt> InRangeStart; + std::optional<APInt> InRangeEnd; - [[nodiscard]] static Expected<GenericConstantData> integer(IntegerImmediate Value); - [[nodiscard]] static Expected<GenericConstantData> floating(FloatingPointBits Value); + [[nodiscard]] static Expected<GenericConstantData> integer(APInt Value); + [[nodiscard]] static Expected<GenericConstantData> floating(APFloat Value); [[nodiscard]] static Expected<GenericConstantData> byteString(GenericConstantType Type, std::vector<std::byte> Bytes); diff --git a/backend/include/rcc/CodeGen/GlobalObject.h b/backend/include/rcc/CodeGen/GlobalObject.h index 49f1f42..798dcd8 100644 --- a/backend/include/rcc/CodeGen/GlobalObject.h +++ b/backend/include/rcc/CodeGen/GlobalObject.h @@ -82,8 +82,8 @@ public: GlobalInitializer &operator=(GlobalInitializer &&) noexcept = default; ~GlobalInitializer() = default; - [[nodiscard]] static GlobalInitializer integer(IntegerImmediate Value); - [[nodiscard]] static GlobalInitializer floating(FloatingPointBits Value); + [[nodiscard]] static GlobalInitializer integer(APInt Value); + [[nodiscard]] static GlobalInitializer floating(APFloat Value); [[nodiscard]] static Expected<GlobalInitializer> bytes(std::vector<std::byte> Value); [[nodiscard]] static Expected<GlobalInitializer> zeroFill(std::uint64_t Size); [[nodiscard]] static Expected<GlobalInitializer> repeated(std::uint64_t Count, @@ -108,10 +108,10 @@ public: private: using StorageT = - std::variant<std::monostate, IntegerImmediate, FloatingPointBits, std::vector<std::byte>, - GlobalZeroFill, GlobalRepeatedInitializer, GlobalAggregateInitializer, - GlobalSymbolAddress, GlobalSymbolDifference, TargetConstant, - GlobalAlignmentPadding, GlobalUndefinedStorage>; + std::variant<std::monostate, APInt, APFloat, std::vector<std::byte>, GlobalZeroFill, + GlobalRepeatedInitializer, GlobalAggregateInitializer, GlobalSymbolAddress, + GlobalSymbolDifference, TargetConstant, GlobalAlignmentPadding, + GlobalUndefinedStorage>; explicit GlobalInitializer(StorageT Storage) : Storage(std::move(Storage)) {} diff --git a/backend/include/rcc/CodeGen/LowLevelType.h b/backend/include/rcc/CodeGen/LowLevelType.h index 116ffd5..e6ff174 100644 --- a/backend/include/rcc/CodeGen/LowLevelType.h +++ b/backend/include/rcc/CodeGen/LowLevelType.h @@ -3,6 +3,7 @@ #ifndef RCC_CODEGEN_LOWLEVELTYPE_H #define RCC_CODEGEN_LOWLEVELTYPE_H +#include "rcc/ADT/APFloat.h" #include "rcc/Support/Error.h" #include <cstddef> @@ -15,35 +16,6 @@ namespace rcc { -enum class FloatSemantics : std::uint8_t { - IEEEHalf, - BFloat, - IEEESingle, - IEEEDouble, - IEEEQuad, - PPCDoubleDouble, - Float8E5M2, - Float8E5M2FNUZ, - Float8E4M3, - Float8E4M3FN, - Float8E4M3FNUZ, - Float8E4M3B11FNUZ, - Float8E3M4, - FloatTF32, - Float8E8M0FNU, - Float6E3M2FN, - Float6E2M3FN, - Float4E2M1FN, - X87DoubleExtended, - Count, -}; - -[[nodiscard]] bool isKnownFloatSemantics(FloatSemantics Semantics) noexcept; -[[nodiscard]] std::optional<std::uint32_t> -getFloatSemanticsBitWidth(FloatSemantics Semantics) noexcept; -[[nodiscard]] std::string_view getFloatSemanticsName(FloatSemantics Semantics) noexcept; -[[nodiscard]] std::string_view getFloatSemanticsSpelling(FloatSemantics Semantics) noexcept; - class LLT final { public: enum class Kind : std::uint8_t { diff --git a/backend/include/rcc/CodeGen/MachineIRBuilder.h b/backend/include/rcc/CodeGen/MachineIRBuilder.h index 2524c7a..b8ff248 100644 --- a/backend/include/rcc/CodeGen/MachineIRBuilder.h +++ b/backend/include/rcc/CodeGen/MachineIRBuilder.h @@ -52,10 +52,8 @@ public: std::uint32_t TargetFlags = 0); [[nodiscard]] Expected<void> addImmediate(std::int64_t Value, std::uint32_t TargetFlags = 0); - [[nodiscard]] Expected<void> addIntegerImmediate(IntegerImmediate Value, - std::uint32_t TargetFlags = 0); - [[nodiscard]] Expected<void> addFloatingImmediate(FloatingPointBits Bits, - std::uint32_t TargetFlags = 0); + [[nodiscard]] Expected<void> addIntegerImmediate(APInt Value, std::uint32_t TargetFlags = 0); + [[nodiscard]] Expected<void> addFloatingImmediate(APFloat Bits, std::uint32_t TargetFlags = 0); [[nodiscard]] Expected<void> addBasicBlock(MachineBasicBlock &Block, std::uint32_t TargetFlags = 0); [[nodiscard]] Expected<void> addFrameIndex(FrameIndex Index, std::int64_t Offset = 0, diff --git a/backend/include/rcc/CodeGen/MachineOperand.h b/backend/include/rcc/CodeGen/MachineOperand.h index a7feafa..ef14638 100644 --- a/backend/include/rcc/CodeGen/MachineOperand.h +++ b/backend/include/rcc/CodeGen/MachineOperand.h @@ -3,6 +3,8 @@ #ifndef RCC_CODEGEN_MACHINEOPERAND_H #define RCC_CODEGEN_MACHINEOPERAND_H +#include "rcc/ADT/APFloat.h" +#include "rcc/ADT/APInt.h" #include "rcc/CodeGen/LowLevelType.h" #include "rcc/CodeGen/MachineDebugInfo.h" #include "rcc/CodeGen/MachineIRTypes.h" @@ -10,7 +12,6 @@ #include "rcc/CodeGen/RegisterMask.h" #include "rcc/Support/Error.h" -#include <array> #include <cstddef> #include <cstdint> #include <initializer_list> @@ -30,72 +31,6 @@ class MachineInstr; class MachineRegisterInfo; class RegOperandIterator; -class FloatingPointBits final { -public: - [[nodiscard]] static Expected<FloatingPointBits> create(FloatSemantics Semantics, - std::span<const std::uint64_t> Words); - - [[nodiscard]] FloatSemantics semantics() const noexcept { - return Semantics; - } - - [[nodiscard]] std::uint32_t bitWidth() const noexcept { - return getFloatSemanticsBitWidth(Semantics).value_or(0); - } - - [[nodiscard]] std::span<const std::uint64_t> words() const noexcept { - return std::span(Words).first(WordCount); - } - - friend bool operator==(const FloatingPointBits &Left, const FloatingPointBits &Right) = default; - -private: - FloatingPointBits(FloatSemantics Semantics, std::array<std::uint64_t, 2> Words, - std::uint8_t WordCount) noexcept - : Semantics(Semantics), Words(Words), WordCount(WordCount) {} - - FloatSemantics Semantics; - std::array<std::uint64_t, 2> Words; - std::uint8_t WordCount; -}; - -class IntegerImmediate final { -public: - static constexpr std::uint32_t MaxBitWidth = std::uint32_t{1} << 23U; - - [[nodiscard]] static Expected<IntegerImmediate> create(std::uint32_t BitWidth, - std::span<const std::uint64_t> Words); - [[nodiscard]] static Expected<IntegerImmediate> fromUnsigned(std::uint32_t BitWidth, - std::uint64_t Value); - [[nodiscard]] static Expected<IntegerImmediate> fromSigned(std::uint32_t BitWidth, - std::int64_t Value); - [[nodiscard]] static Expected<IntegerImmediate> parseDecimal(std::uint32_t BitWidth, - std::string_view Text); - - [[nodiscard]] std::uint32_t bitWidth() const noexcept { - return BitWidth; - } - - [[nodiscard]] std::span<const std::uint64_t> words() const noexcept { - return Words; - } - - [[nodiscard]] bool isZero() const noexcept; - [[nodiscard]] bool isNegative() const noexcept; - [[nodiscard]] std::string toUnsignedDecimal() const; - [[nodiscard]] std::string toSignedDecimal() const; - [[nodiscard]] Expected<void> verify() const; - - friend bool operator==(const IntegerImmediate &Left, const IntegerImmediate &Right) = default; - -private: - IntegerImmediate(std::uint32_t BitWidth, std::vector<std::uint64_t> Words) - : BitWidth(BitWidth), Words(std::move(Words)) {} - - std::uint32_t BitWidth; - std::vector<std::uint64_t> Words; -}; - enum class RegisterOperandFlag : std::uint8_t { Define, Use, @@ -147,14 +82,14 @@ private: }; struct FloatingImmediateOperand final { - FloatingPointBits Bits; + APFloat Bits; friend bool operator==(const FloatingImmediateOperand &Left, const FloatingImmediateOperand &Right) = default; }; struct IntegerImmediateOperand final { - IntegerImmediate Value; + APInt Value; friend bool operator==(const IntegerImmediateOperand &Left, const IntegerImmediateOperand &Right) = default; @@ -382,9 +317,8 @@ public: SubRegIndex SubReg = {}, std::uint32_t TargetFlags = 0); [[nodiscard]] static MachineOperand immediate(std::int64_t Value, std::uint32_t TargetFlags = 0); - [[nodiscard]] static MachineOperand integerImmediate(IntegerImmediate Value, - std::uint32_t TargetFlags = 0); - [[nodiscard]] static MachineOperand floatingImmediate(FloatingPointBits Bits, + [[nodiscard]] static MachineOperand integerImmediate(APInt Value, std::uint32_t TargetFlags = 0); + [[nodiscard]] static MachineOperand floatingImmediate(APFloat Bits, std::uint32_t TargetFlags = 0); [[nodiscard]] static MachineOperand basicBlock(MachineBasicBlock &Block, std::uint32_t TargetFlags = 0); diff --git a/backend/include/rcc/MIR/LLVMConstantSyntax.h b/backend/include/rcc/MIR/LLVMConstantSyntax.h index a47da54..7e18d60 100644 --- a/backend/include/rcc/MIR/LLVMConstantSyntax.h +++ b/backend/include/rcc/MIR/LLVMConstantSyntax.h @@ -4,6 +4,7 @@ #define RCC_MIR_LLVMCONSTANTSYNTAX_H #include "rcc/CodeGen/GenericConstant.h" +#include "rcc/CodeGen/MachineIRTypes.h" #include "rcc/Support/Error.h" #include <cstddef> diff --git a/backend/lib/CodeGen/GenericConstant.cpp b/backend/lib/CodeGen/GenericConstant.cpp index d9d9233..afcdd44 100644 --- a/backend/lib/CodeGen/GenericConstant.cpp +++ b/backend/lib/CodeGen/GenericConstant.cpp @@ -203,8 +203,7 @@ nonPointerFixedBitWidth(const GenericConstantType &Type) noexcept { return false; } -[[nodiscard]] bool signedIntegerLess(const IntegerImmediate &Left, - const IntegerImmediate &Right) noexcept { +[[nodiscard]] bool signedIntegerLess(const APInt &Left, const APInt &Right) noexcept { if (Left.bitWidth() != Right.bitWidth()) return false; if (Left.isNegative() != Right.isNegative()) @@ -249,7 +248,7 @@ nonPointerFixedBitWidth(const GenericConstantType &Type) noexcept { case GenericConstantTypeKind::Metadata: return RequireNoElements(); case GenericConstantTypeKind::Integer: - if (Type.BitWidth == 0 || Type.BitWidth > IntegerImmediate::MaxBitWidth) { + if (Type.BitWidth == 0 || Type.BitWidth > APInt::MaxBitWidth) { return makeUnexpected(ErrorCode::VerificationError, "generic integer type has an invalid bit width"); } @@ -257,7 +256,7 @@ nonPointerFixedBitWidth(const GenericConstantType &Type) noexcept { case GenericConstantTypeKind::Byte: return RequireNoElements(); case GenericConstantTypeKind::Floating: - if (!isKnownFloatSemantics(Type.Semantics)) { + if (!getFloatSemanticsBitWidth(Type.Semantics)) { return makeUnexpected(ErrorCode::VerificationError, "generic floating type has unknown semantics"); } @@ -702,7 +701,7 @@ nonPointerFixedBitWidth(const GenericConstantType &Type) noexcept { } // namespace Expected<GenericConstantType> GenericConstantType::integer(std::uint32_t Width) { - if (Width == 0 || Width > IntegerImmediate::MaxBitWidth) + if (Width == 0 || Width > APInt::MaxBitWidth) return makeUnexpected(ErrorCode::InvalidArgument, "generic integer type width is invalid"); GenericConstantType Type; Type.Kind = GenericConstantTypeKind::Integer; @@ -724,7 +723,7 @@ GenericConstantType GenericConstantType::byte() noexcept { } Expected<GenericConstantType> GenericConstantType::floating(FloatSemantics ValueSemantics) { - if (!isKnownFloatSemantics(ValueSemantics)) + if (!getFloatSemanticsBitWidth(ValueSemantics)) return makeUnexpected(ErrorCode::InvalidArgument, "generic floating semantics is unknown"); GenericConstantType Type; Type.Kind = GenericConstantTypeKind::Floating; @@ -810,7 +809,7 @@ Expected<void> GenericConstantType::verify() const { return verifyType(*this, 0, Nodes); } -Expected<GenericConstantData> GenericConstantData::integer(IntegerImmediate Value) { +Expected<GenericConstantData> GenericConstantData::integer(APInt Value) { auto Type = GenericConstantType::integer(Value.bitWidth()); if (!Type) return std::unexpected(Type.error()); @@ -821,7 +820,7 @@ Expected<GenericConstantData> GenericConstantData::integer(IntegerImmediate Valu return Result; } -Expected<GenericConstantData> GenericConstantData::floating(FloatingPointBits Value) { +Expected<GenericConstantData> GenericConstantData::floating(APFloat Value) { auto Type = GenericConstantType::floating(Value.semantics()); if (!Type) return std::unexpected(Type.error()); diff --git a/backend/lib/CodeGen/GlobalObject.cpp b/backend/lib/CodeGen/GlobalObject.cpp index cda28bd..aa8fd15 100644 --- a/backend/lib/CodeGen/GlobalObject.cpp +++ b/backend/lib/CodeGen/GlobalObject.cpp @@ -45,7 +45,7 @@ namespace { case GlobalInitializer::Kind::Invalid: return makeUnexpected(ErrorCode::VerificationError, "global initializer is invalid"); case GlobalInitializer::Kind::Integer: { - const auto *Value = Initializer.getIf<IntegerImmediate>(); + const auto *Value = Initializer.getIf<APInt>(); if (Value == nullptr) return makeUnexpected(ErrorCode::InternalError, "integer initializer storage is invalid"); auto Verified = Value->verify(); @@ -54,7 +54,7 @@ namespace { return (static_cast<std::uint64_t>(Value->bitWidth()) + 7U) / 8U; } case GlobalInitializer::Kind::Floating: { - const auto *Value = Initializer.getIf<FloatingPointBits>(); + const auto *Value = Initializer.getIf<APFloat>(); if (Value == nullptr) return makeUnexpected(ErrorCode::InternalError, "floating initializer storage is invalid"); return (static_cast<std::uint64_t>(Value->bitWidth()) + 7U) / 8U; @@ -141,11 +141,11 @@ namespace { } // namespace -GlobalInitializer GlobalInitializer::integer(IntegerImmediate Value) { +GlobalInitializer GlobalInitializer::integer(APInt Value) { return GlobalInitializer(std::move(Value)); } -GlobalInitializer GlobalInitializer::floating(FloatingPointBits Value) { +GlobalInitializer GlobalInitializer::floating(APFloat Value) { return GlobalInitializer(Value); } diff --git a/backend/lib/CodeGen/LowLevelType.cpp b/backend/lib/CodeGen/LowLevelType.cpp index b124765..b50ea6b 100644 --- a/backend/lib/CodeGen/LowLevelType.cpp +++ b/backend/lib/CodeGen/LowLevelType.cpp @@ -8,43 +8,6 @@ namespace rcc { namespace { -struct FloatSemanticsInfo final { - std::uint32_t BitWidth; - std::string_view Name; - std::string_view Spelling; -}; - -constexpr std::array FloatSemanticsTable{ - FloatSemanticsInfo{16, "IEEE half", "f16"}, - FloatSemanticsInfo{16, "bfloat", "bf16"}, - FloatSemanticsInfo{32, "IEEE single", "f32"}, - FloatSemanticsInfo{64, "IEEE double", "f64"}, - FloatSemanticsInfo{128, "IEEE quad", "f128"}, - FloatSemanticsInfo{128, "PPC double-double", "ppcf128"}, - FloatSemanticsInfo{8, "float8 E5M2", "f8e5m2"}, - FloatSemanticsInfo{8, "float8 E5M2 FNUZ", "f8e5m2fnuz"}, - FloatSemanticsInfo{8, "float8 E4M3", "f8e4m3"}, - FloatSemanticsInfo{8, "float8 E4M3 FN", "f8e4m3fn"}, - FloatSemanticsInfo{8, "float8 E4M3 FNUZ", "f8e4m3fnuz"}, - FloatSemanticsInfo{8, "float8 E4M3 B11 FNUZ", "f8e4m3b11fnuz"}, - FloatSemanticsInfo{8, "float8 E3M4", "f8e3m4"}, - FloatSemanticsInfo{19, "TensorFloat-32", "tf32"}, - FloatSemanticsInfo{8, "float8 E8M0 FNU", "f8e8m0fnu"}, - FloatSemanticsInfo{6, "float6 E3M2 FN", "f6e3m2fn"}, - FloatSemanticsInfo{6, "float6 E2M3 FN", "f6e2m3fn"}, - FloatSemanticsInfo{4, "float4 E2M1 FN", "f4e2m1fn"}, - FloatSemanticsInfo{80, "x87 double-extended", "x86fp80"}, -}; - -static_assert(FloatSemanticsTable.size() == static_cast<std::size_t>(FloatSemantics::Count)); - -[[nodiscard]] const FloatSemanticsInfo *getFloatSemanticsInfo(FloatSemantics Semantics) noexcept { - const auto Index = static_cast<std::size_t>(Semantics); - if (Index >= FloatSemanticsTable.size()) - return nullptr; - return &FloatSemanticsTable[Index]; -} - [[nodiscard]] LLT::Kind getVectorKind(LLT ElementType) noexcept { switch (ElementType.kind()) { case LLT::Kind::AnyScalar: @@ -69,31 +32,6 @@ static_assert(FloatSemanticsTable.size() == static_cast<std::size_t>(FloatSemant } // namespace -bool isKnownFloatSemantics(FloatSemantics Semantics) noexcept { - return getFloatSemanticsInfo(Semantics) != nullptr; -} - -std::optional<std::uint32_t> getFloatSemanticsBitWidth(FloatSemantics Semantics) noexcept { - const auto *Info = getFloatSemanticsInfo(Semantics); - if (Info == nullptr) - return std::nullopt; - return Info->BitWidth; -} - -std::string_view getFloatSemanticsName(FloatSemantics Semantics) noexcept { - const auto *Info = getFloatSemanticsInfo(Semantics); - if (Info == nullptr) - return "unknown"; - return Info->Name; -} - -std::string_view getFloatSemanticsSpelling(FloatSemantics Semantics) noexcept { - const auto *Info = getFloatSemanticsInfo(Semantics); - if (Info == nullptr) - return "invalid-float"; - return Info->Spelling; -} - Expected<LLT> LLT::makeScalar(Kind TypeKind, std::uint64_t BitWidth) { if (BitWidth == 0 || BitWidth > MaxBitWidth) { return makeUnexpected(ErrorCode::InvalidArgument, diff --git a/backend/lib/CodeGen/MIRParser/MIParser.cpp b/backend/lib/CodeGen/MIRParser/MIParser.cpp index e38b9f1..183e6b3 100644 --- a/backend/lib/CodeGen/MIRParser/MIParser.cpp +++ b/backend/lib/CodeGen/MIRParser/MIParser.cpp @@ -116,9 +116,9 @@ namespace { Token.isKeyword("llvm_vector_register_mask"); } -[[nodiscard]] Expected<IntegerImmediate> parseIntegerImmediateValue(std::uint32_t BitWidth, - std::string_view Spelling) { - if (BitWidth == 0 || BitWidth > IntegerImmediate::MaxBitWidth) +[[nodiscard]] Expected<APInt> parseIntegerImmediateValue(std::uint32_t BitWidth, + std::string_view Spelling) { + if (BitWidth == 0 || BitWidth > APInt::MaxBitWidth) return makeUnexpected(ErrorCode::ParseError, "typed integer width is out of range"); const bool Negative = Spelling.starts_with('-'); @@ -155,7 +155,7 @@ namespace { if (BitWidth % 64U != 0) Words.back() &= (std::uint64_t{1} << (BitWidth % 64U)) - 1U; } - return IntegerImmediate::create(BitWidth, Words); + return APInt::create(BitWidth, Words); } [[nodiscard]] std::optional<std::uint64_t> minimumTypeSizeInBits(LLT Type) noexcept { @@ -1244,7 +1244,7 @@ bool MIParser::Impl::parseTypedIntegerOperand(ParsedOperand &Result) { return fail(Begin, "typed integer operand must use an 'iN' type"); std::uint64_t WideBitWidth = 0; if (!parseUnsignedDigits(TypeSpelling.substr(1), WideBitWidth) || - WideBitWidth > IntegerImmediate::MaxBitWidth) { + WideBitWidth > APInt::MaxBitWidth) { return fail(Begin, "typed integer operand has an invalid bit width"); } const auto BitWidth = static_cast<std::uint32_t>(WideBitWidth); diff --git a/backend/lib/CodeGen/MIRPrinter.cpp b/backend/lib/CodeGen/MIRPrinter.cpp index d510c83..ea201b9 100644 --- a/backend/lib/CodeGen/MIRPrinter.cpp +++ b/backend/lib/CodeGen/MIRPrinter.cpp @@ -2249,7 +2249,7 @@ Expected<void> BodyPrinter::printOperand(const MachineOperand &Operand, bool IsE Output.append(std::to_string(*Operand.getIf<std::int64_t>())); return {}; case MachineOperand::Kind::IntegerImmediate: { - const IntegerImmediate &Value = Operand.getIf<IntegerImmediateOperand>()->Value; + const APInt &Value = Operand.getIf<IntegerImmediateOperand>()->Value; Output.push_back('i'); Output.append(std::to_string(Value.bitWidth())); Output.push_back(' '); @@ -2260,7 +2260,7 @@ Expected<void> BodyPrinter::printOperand(const MachineOperand &Operand, bool IsE return {}; } case MachineOperand::Kind::FloatingImmediate: { - const FloatingPointBits &Bits = Operand.getIf<FloatingImmediateOperand>()->Bits; + const APFloat &Bits = Operand.getIf<FloatingImmediateOperand>()->Bits; auto Constant = GenericConstantData::floating(Bits); if (!Constant) return std::unexpected(Constant.error()); diff --git a/backend/lib/CodeGen/MachineIRBuilder.cpp b/backend/lib/CodeGen/MachineIRBuilder.cpp index 6887804..e59f613 100644 --- a/backend/lib/CodeGen/MachineIRBuilder.cpp +++ b/backend/lib/CodeGen/MachineIRBuilder.cpp @@ -162,13 +162,11 @@ Expected<void> MachineInstrBuilder::addImmediate(std::int64_t Value, std::uint32 return addOperand(MachineOperand::immediate(Value, TargetFlags)); } -Expected<void> MachineInstrBuilder::addIntegerImmediate(IntegerImmediate Value, - std::uint32_t TargetFlags) { +Expected<void> MachineInstrBuilder::addIntegerImmediate(APInt Value, std::uint32_t TargetFlags) { return addOperand(MachineOperand::integerImmediate(std::move(Value), TargetFlags)); } -Expected<void> MachineInstrBuilder::addFloatingImmediate(FloatingPointBits Bits, - std::uint32_t TargetFlags) { +Expected<void> MachineInstrBuilder::addFloatingImmediate(APFloat Bits, std::uint32_t TargetFlags) { return addOperand(MachineOperand::floatingImmediate(Bits, TargetFlags)); } diff --git a/backend/lib/CodeGen/MachineOperand.cpp b/backend/lib/CodeGen/MachineOperand.cpp index c81a7c3..44ad8b6 100644 --- a/backend/lib/CodeGen/MachineOperand.cpp +++ b/backend/lib/CodeGen/MachineOperand.cpp @@ -8,222 +8,14 @@ #include "rcc/CodeGen/MachineRegisterInfo.h" #include <algorithm> -#include <array> #include <cstdint> #include <limits> #include <memory> -#include <span> #include <string> -#include <string_view> #include <utility> #include <vector> namespace rcc { -namespace { - -[[nodiscard]] constexpr std::size_t integerWordCount(std::uint32_t BitWidth) noexcept { - return (static_cast<std::size_t>(BitWidth) + 63U) / 64U; -} - -[[nodiscard]] constexpr std::uint64_t integerHighWordMask(std::uint32_t BitWidth) noexcept { - const std::uint32_t UsedBits = BitWidth % 64U; - if (UsedBits == 0) - return std::numeric_limits<std::uint64_t>::max(); - return (std::uint64_t{1} << UsedBits) - 1U; -} - -[[nodiscard]] Expected<void> verifyIntegerImmediate(std::uint32_t BitWidth, - std::span<const std::uint64_t> Words, - ErrorCode Code) { - if (BitWidth == 0 || BitWidth > IntegerImmediate::MaxBitWidth) { - return makeUnexpected(Code, "integer immediate bit width is outside the supported range"); - } - if (Words.size() != integerWordCount(BitWidth)) { - return makeUnexpected(Code, "integer immediate bit pattern has the wrong word count"); - } - if ((Words.back() & ~integerHighWordMask(BitWidth)) != 0) { - return makeUnexpected(Code, "integer immediate has nonzero bits above its declared width"); - } - return {}; -} - -} // namespace - -Expected<IntegerImmediate> IntegerImmediate::create(std::uint32_t BitWidth, - std::span<const std::uint64_t> Words) { - auto Verification = verifyIntegerImmediate(BitWidth, Words, ErrorCode::InvalidArgument); - if (!Verification) - return std::unexpected(Verification.error()); - return IntegerImmediate(BitWidth, std::vector<std::uint64_t>(Words.begin(), Words.end())); -} - -Expected<IntegerImmediate> IntegerImmediate::fromUnsigned(std::uint32_t BitWidth, - std::uint64_t Value) { - if (BitWidth == 0 || BitWidth > MaxBitWidth) { - return makeUnexpected(ErrorCode::InvalidArgument, - "integer immediate bit width is outside the supported range"); - } - if (BitWidth < 64U && (Value >> BitWidth) != 0) { - return makeUnexpected(ErrorCode::InvalidArgument, - "unsigned value does not fit the integer immediate bit width"); - } - - std::vector<std::uint64_t> Words(integerWordCount(BitWidth), 0); - Words.front() = Value; - return create(BitWidth, Words); -} - -Expected<IntegerImmediate> IntegerImmediate::fromSigned(std::uint32_t BitWidth, - std::int64_t Value) { - if (BitWidth == 0 || BitWidth > MaxBitWidth) { - return makeUnexpected(ErrorCode::InvalidArgument, - "integer immediate bit width is outside the supported range"); - } - if (BitWidth < 64U) { - const std::uint32_t MagnitudeBits = BitWidth - 1U; - const std::int64_t Minimum = -(std::int64_t{1} << MagnitudeBits); - const std::int64_t Maximum = (std::int64_t{1} << MagnitudeBits) - 1; - if (Value < Minimum || Value > Maximum) { - return makeUnexpected(ErrorCode::InvalidArgument, - "signed value does not fit the integer immediate bit width"); - } - } - - const std::uint64_t Fill = - Value < 0 ? std::numeric_limits<std::uint64_t>::max() : std::uint64_t{0}; - std::vector<std::uint64_t> Words(integerWordCount(BitWidth), Fill); - Words.front() = static_cast<std::uint64_t>(Value); - Words.back() &= integerHighWordMask(BitWidth); - return create(BitWidth, Words); -} - -Expected<IntegerImmediate> IntegerImmediate::parseDecimal(std::uint32_t BitWidth, - std::string_view Text) { - if (BitWidth == 0 || BitWidth > MaxBitWidth) { - return makeUnexpected(ErrorCode::ParseError, - "integer immediate bit width is outside the supported range"); - } - const bool Negative = Text.starts_with('-'); - if (Negative) - Text.remove_prefix(1); - if (Text.empty()) - return makeUnexpected(ErrorCode::ParseError, "integer immediate has no decimal digits"); - - std::vector<std::uint64_t> Words(integerWordCount(BitWidth), 0); - for (const char Character : Text) { - if (Character < '0' || Character > '9') { - return makeUnexpected(ErrorCode::ParseError, - "integer immediate contains a non-decimal digit"); - } - std::uint64_t Carry = static_cast<unsigned>(Character - '0'); - for (std::uint64_t &Word : Words) { - const unsigned __int128 Product = static_cast<unsigned __int128>(Word) * 10U + Carry; - Word = static_cast<std::uint64_t>(Product); - Carry = static_cast<std::uint64_t>(Product >> 64U); - } - } - - // LLVM integer literals are arbitrary precision and are extended or - // truncated to the declared bit width before becoming a constant. - Words.back() &= integerHighWordMask(BitWidth); - if (Negative) { - for (std::uint64_t &Word : Words) - Word = ~Word; - std::uint64_t Carry = 1; - for (std::uint64_t &Word : Words) { - const std::uint64_t Previous = Word; - Word += Carry; - Carry = Carry != 0 && Word < Previous ? 1 : 0; - } - Words.back() &= integerHighWordMask(BitWidth); - } - return create(BitWidth, Words); -} - -bool IntegerImmediate::isZero() const noexcept { - return std::ranges::all_of(Words, [](std::uint64_t Word) { return Word == 0; }); -} - -bool IntegerImmediate::isNegative() const noexcept { - const std::uint32_t SignBit = BitWidth - 1U; - return (Words[SignBit / 64U] & (std::uint64_t{1} << (SignBit % 64U))) != 0; -} - -std::string IntegerImmediate::toUnsignedDecimal() const { - if (isZero()) - return "0"; - std::vector<std::uint64_t> Remaining = Words; - std::string Digits; - while (!std::ranges::all_of(Remaining, [](std::uint64_t Word) { return Word == 0; })) { - unsigned __int128 Remainder = 0; - for (auto Iterator = Remaining.rbegin(); Iterator != Remaining.rend(); ++Iterator) { - const unsigned __int128 Dividend = (Remainder << 64U) | *Iterator; - *Iterator = static_cast<std::uint64_t>(Dividend / 10U); - Remainder = Dividend % 10U; - } - Digits.push_back(static_cast<char>('0' + static_cast<unsigned>(Remainder))); - } - std::ranges::reverse(Digits); - return Digits; -} - -std::string IntegerImmediate::toSignedDecimal() const { - if (!isNegative()) - return toUnsignedDecimal(); - - std::vector<std::uint64_t> Magnitude = Words; - for (std::uint64_t &Word : Magnitude) - Word = ~Word; - std::uint64_t Carry = 1; - for (std::uint64_t &Word : Magnitude) { - const std::uint64_t Previous = Word; - Word += Carry; - Carry = Carry != 0 && Word < Previous ? 1 : 0; - } - Magnitude.back() &= integerHighWordMask(BitWidth); - - std::string Digits; - while (!std::ranges::all_of(Magnitude, [](std::uint64_t Word) { return Word == 0; })) { - unsigned __int128 Remainder = 0; - for (auto Iterator = Magnitude.rbegin(); Iterator != Magnitude.rend(); ++Iterator) { - const unsigned __int128 Dividend = (Remainder << 64U) | *Iterator; - *Iterator = static_cast<std::uint64_t>(Dividend / 10U); - Remainder = Dividend % 10U; - } - Digits.push_back(static_cast<char>('0' + static_cast<unsigned>(Remainder))); - } - std::ranges::reverse(Digits); - Digits.insert(Digits.begin(), '-'); - return Digits; -} - -Expected<void> IntegerImmediate::verify() const { - return verifyIntegerImmediate(BitWidth, Words, ErrorCode::VerificationError); -} - -Expected<FloatingPointBits> FloatingPointBits::create(FloatSemantics Semantics, - std::span<const std::uint64_t> Words) { - const auto BitWidth = getFloatSemanticsBitWidth(Semantics); - if (!BitWidth) { - return makeUnexpected(ErrorCode::InvalidArgument, "floating immediate has unknown semantics"); - } - - const std::size_t RequiredWords = (*BitWidth + 63U) / 64U; - if (RequiredWords > 2 || Words.size() != RequiredWords) { - return makeUnexpected(ErrorCode::InvalidArgument, - "floating immediate bit pattern has the wrong word count"); - } - - const std::uint32_t UsedBitsInLastWord = *BitWidth % 64U; - if (UsedBitsInLastWord != 0 && (Words.back() >> UsedBitsInLastWord) != 0) { - return makeUnexpected(ErrorCode::InvalidArgument, - "floating immediate has nonzero bits above its semantics width"); - } - - std::array<std::uint64_t, 2> StoredWords{}; - std::ranges::copy(Words, StoredWords.begin()); - return FloatingPointBits(Semantics, StoredWords, static_cast<std::uint8_t>(RequiredWords)); -} RegisterOperandFlags::RegisterOperandFlags( std::initializer_list<RegisterOperandFlag> Flags) noexcept { @@ -296,13 +88,12 @@ MachineOperand MachineOperand::immediate(std::int64_t Value, std::uint32_t Targe return MachineOperand(Kind::Immediate, Value, TargetFlags); } -MachineOperand MachineOperand::integerImmediate(IntegerImmediate Value, std::uint32_t TargetFlags) { +MachineOperand MachineOperand::integerImmediate(APInt Value, std::uint32_t TargetFlags) { return MachineOperand(Kind::IntegerImmediate, IntegerImmediateOperand{std::move(Value)}, TargetFlags); } -MachineOperand MachineOperand::floatingImmediate(FloatingPointBits Bits, - std::uint32_t TargetFlags) { +MachineOperand MachineOperand::floatingImmediate(APFloat Bits, std::uint32_t TargetFlags) { return MachineOperand(Kind::FloatingImmediate, FloatingImmediateOperand{Bits}, TargetFlags); } diff --git a/backend/lib/MIR/LLVMConstantSyntax.cpp b/backend/lib/MIR/LLVMConstantSyntax.cpp index a687f80..4ee231d 100644 --- a/backend/lib/MIR/LLVMConstantSyntax.cpp +++ b/backend/lib/MIR/LLVMConstantSyntax.cpp @@ -2,6 +2,9 @@ #include "rcc/MIR/LLVMConstantSyntax.h" +#include "rcc/ADT/APFloat.h" +#include "rcc/ADT/APInt.h" + #include <algorithm> #include <array> #include <bit> @@ -52,1215 +55,6 @@ struct ParsedName final { return static_cast<unsigned>(Character - 'A') + 10U; } -class BigUnsigned final { -public: - BigUnsigned() = default; - - explicit BigUnsigned(std::uint64_t Value) { - if (Value == 0) - return; - Limbs.push_back(static_cast<std::uint32_t>(Value)); - if ((Value >> 32U) != 0) - Limbs.push_back(static_cast<std::uint32_t>(Value >> 32U)); - } - - [[nodiscard]] bool isZero() const noexcept { - return Limbs.empty(); - } - - [[nodiscard]] bool isOdd() const noexcept { - return !Limbs.empty() && (Limbs.front() & 1U) != 0; - } - - [[nodiscard]] std::size_t bitWidth() const noexcept { - if (Limbs.empty()) - return 0; - std::uint32_t MostSignificant = Limbs.back(); - std::size_t Bits = 0; - while (MostSignificant != 0) { - ++Bits; - MostSignificant >>= 1U; - } - return (Limbs.size() - 1U) * 32U + Bits; - } - - [[nodiscard]] std::uint64_t word(std::size_t Index) const noexcept { - const std::size_t First = Index * 2U; - if (First >= Limbs.size()) - return 0; - std::uint64_t Result = Limbs[First]; - if (First + 1U < Limbs.size()) - Result |= static_cast<std::uint64_t>(Limbs[First + 1U]) << 32U; - return Result; - } - - [[nodiscard]] int compare(const BigUnsigned &Other) const noexcept { - if (Limbs.size() != Other.Limbs.size()) - return Limbs.size() < Other.Limbs.size() ? -1 : 1; - for (std::size_t Index = Limbs.size(); Index != 0; --Index) { - const std::uint32_t Left = Limbs[Index - 1U]; - const std::uint32_t Right = Other.Limbs[Index - 1U]; - if (Left != Right) - return Left < Right ? -1 : 1; - } - return 0; - } - - void multiplySmall(std::uint32_t Multiplier) { - if (Multiplier == 0 || isZero()) { - Limbs.clear(); - return; - } - std::uint64_t Carry = 0; - for (std::uint32_t &Limb : Limbs) { - const std::uint64_t Product = static_cast<std::uint64_t>(Limb) * Multiplier + Carry; - Limb = static_cast<std::uint32_t>(Product); - Carry = Product >> 32U; - } - if (Carry != 0) - Limbs.push_back(static_cast<std::uint32_t>(Carry)); - } - - [[nodiscard]] std::uint32_t divideSmall(std::uint32_t Divisor) { - std::uint64_t Remainder = 0; - for (std::size_t Index = Limbs.size(); Index != 0; --Index) { - const std::uint64_t Dividend = (Remainder << 32U) | Limbs[Index - 1U]; - Limbs[Index - 1U] = static_cast<std::uint32_t>(Dividend / Divisor); - Remainder = Dividend % Divisor; - } - normalize(); - return static_cast<std::uint32_t>(Remainder); - } - - void addSmall(std::uint32_t Value) { - std::uint64_t Carry = Value; - std::size_t Index = 0; - while (Carry != 0 && Index < Limbs.size()) { - const std::uint64_t Sum = static_cast<std::uint64_t>(Limbs[Index]) + Carry; - Limbs[Index] = static_cast<std::uint32_t>(Sum); - Carry = Sum >> 32U; - ++Index; - } - if (Carry != 0) - Limbs.push_back(static_cast<std::uint32_t>(Carry)); - } - - void setBit(std::size_t Bit) { - const std::size_t Limb = Bit / 32U; - if (Limb >= Limbs.size()) - Limbs.resize(Limb + 1U, 0); - Limbs[Limb] |= std::uint32_t{1} << (Bit % 32U); - } - - void clearBit(std::size_t Bit) noexcept { - const std::size_t Limb = Bit / 32U; - if (Limb >= Limbs.size()) - return; - Limbs[Limb] &= ~(std::uint32_t{1} << (Bit % 32U)); - normalize(); - } - - void shiftRightOne() noexcept { - std::uint32_t Carry = 0; - for (std::size_t Index = Limbs.size(); Index != 0; --Index) { - const std::uint32_t Current = Limbs[Index - 1U]; - Limbs[Index - 1U] = (Current >> 1U) | (Carry << 31U); - Carry = Current & 1U; - } - normalize(); - } - - [[nodiscard]] std::size_t countrZero() const noexcept { - std::size_t Result = 0; - for (const std::uint32_t Limb : Limbs) { - if (Limb == 0) { - Result += 32U; - continue; - } - return Result + static_cast<std::size_t>(std::countr_zero(Limb)); - } - return Result; - } - - void shiftRight(std::size_t Amount) noexcept { - if (Amount == 0 || isZero()) - return; - const std::size_t LimbOffset = Amount / 32U; - if (LimbOffset >= Limbs.size()) { - Limbs.clear(); - return; - } - if (LimbOffset != 0) - Limbs.erase(Limbs.begin(), Limbs.begin() + static_cast<std::ptrdiff_t>(LimbOffset)); - const unsigned BitOffset = static_cast<unsigned>(Amount % 32U); - if (BitOffset != 0) { - std::uint32_t Carry = 0; - for (std::size_t Index = Limbs.size(); Index != 0; --Index) { - const std::uint32_t Current = Limbs[Index - 1U]; - Limbs[Index - 1U] = (Current >> BitOffset) | Carry; - Carry = Current << (32U - BitOffset); - } - } - normalize(); - } - - [[nodiscard]] std::string toDecimal() const { - if (isZero()) - return "0"; - BigUnsigned Remaining = *this; - std::string Digits; - while (!Remaining.isZero()) - Digits.push_back(static_cast<char>('0' + Remaining.divideSmall(10))); - std::ranges::reverse(Digits); - return Digits; - } - - [[nodiscard]] BigUnsigned shiftedLeft(std::size_t Amount) const { - if (isZero() || Amount == 0) - return *this; - const std::size_t LimbOffset = Amount / 32U; - const unsigned BitOffset = static_cast<unsigned>(Amount % 32U); - BigUnsigned Result; - Result.Limbs.assign(Limbs.size() + LimbOffset + (BitOffset == 0 ? 0U : 1U), 0); - for (std::size_t Index = 0; Index < Limbs.size(); ++Index) { - const std::uint64_t Shifted = static_cast<std::uint64_t>(Limbs[Index]) << BitOffset; - Result.Limbs[Index + LimbOffset] |= static_cast<std::uint32_t>(Shifted); - if (BitOffset != 0) - Result.Limbs[Index + LimbOffset + 1U] |= static_cast<std::uint32_t>(Shifted >> 32U); - } - Result.normalize(); - return Result; - } - - void subtract(const BigUnsigned &Other) noexcept { - std::uint64_t Borrow = 0; - for (std::size_t Index = 0; Index < Limbs.size(); ++Index) { - const std::uint64_t Subtrahend = - (Index < Other.Limbs.size() ? static_cast<std::uint64_t>(Other.Limbs[Index]) : 0) + - Borrow; - const std::uint64_t Current = Limbs[Index]; - Limbs[Index] = static_cast<std::uint32_t>(Current - Subtrahend); - Borrow = Current < Subtrahend ? 1U : 0U; - } - normalize(); - } - -private: - friend int compareToShifted(const BigUnsigned &Left, const BigUnsigned &Right, - std::size_t RightShift) noexcept; - - void normalize() noexcept { - while (!Limbs.empty() && Limbs.back() == 0) - Limbs.pop_back(); - } - - std::vector<std::uint32_t> Limbs; -}; - -[[nodiscard]] int compareToShifted(const BigUnsigned &Left, const BigUnsigned &Right, - std::size_t RightShift) noexcept { - if (Right.isZero()) - return Left.isZero() ? 0 : 1; - if (Left.isZero()) - return -1; - - const std::size_t LeftBits = Left.bitWidth(); - const std::size_t RightBits = Right.bitWidth(); - if (RightShift > std::numeric_limits<std::size_t>::max() - RightBits) - return -1; - const std::size_t ShiftedBits = RightBits + RightShift; - if (LeftBits != ShiftedBits) - return LeftBits < ShiftedBits ? -1 : 1; - - const std::size_t LimbOffset = RightShift / 32U; - const unsigned BitOffset = static_cast<unsigned>(RightShift % 32U); - const std::size_t WordCount = (ShiftedBits + 31U) / 32U; - for (std::size_t Index = WordCount; Index != 0; --Index) { - const std::size_t Destination = Index - 1U; - std::uint32_t ShiftedWord = 0; - if (Destination >= LimbOffset) { - const std::size_t Source = Destination - LimbOffset; - if (Source < Right.Limbs.size()) - ShiftedWord = static_cast<std::uint32_t>(Right.Limbs[Source] << BitOffset); - if (BitOffset != 0 && Source != 0 && Source - 1U < Right.Limbs.size()) - ShiftedWord |= Right.Limbs[Source - 1U] >> (32U - BitOffset); - } - const std::uint32_t LeftWord = Destination < Left.Limbs.size() ? Left.Limbs[Destination] : 0; - if (LeftWord != ShiftedWord) - return LeftWord < ShiftedWord ? -1 : 1; - } - return 0; -} - -[[nodiscard]] bool parseHexWords(std::string_view Digits, std::uint32_t BitWidth, - std::vector<std::uint64_t> &Words) { - if (Digits.empty() || Digits.size() > (static_cast<std::size_t>(BitWidth) + 3U) / 4U) - return false; - Words.assign((BitWidth + 63U) / 64U, 0); - std::size_t Nibble = 0; - for (auto Iterator = Digits.rbegin(); Iterator != Digits.rend(); ++Iterator, ++Nibble) { - if (!isHexDigit(*Iterator)) - return false; - Words[Nibble / 16U] |= static_cast<std::uint64_t>(hexDigitValue(*Iterator)) - << ((Nibble % 16U) * 4U); - } - return (BitWidth % 64U) == 0 || (Words.back() >> (BitWidth % 64U)) == 0; -} - -[[nodiscard]] bool parseWideFloatHexWords(std::string_view Digits, - std::vector<std::uint64_t> &Words) { - if (Digits.empty() || Digits.size() > 32U || !std::ranges::all_of(Digits, isHexDigit)) - return false; - Words.assign(2, 0); - const auto ParseWord = [](std::string_view Text, std::uint64_t &Word) { - const auto Parsed = std::from_chars(Text.data(), Text.data() + Text.size(), Word, 16); - return Parsed.ec == std::errc{} && Parsed.ptr == Text.data() + Text.size(); - }; - const std::size_t FirstWordLength = std::min<std::size_t>(Digits.size(), 16U); - if (!ParseWord(Digits.substr(0, FirstWordLength), Words[0])) - return false; - if (Digits.size() == FirstWordLength) - return true; - return ParseWord(Digits.substr(FirstWordLength), Words[1]); -} - -enum class SpecialFloatLiteralKind : std::uint8_t { - Infinity, - QuietNaN, - SignalingNaN, -}; - -struct SpecialFloatLiteral final { - SpecialFloatLiteralKind Kind = SpecialFloatLiteralKind::Infinity; - bool Negative = false; - std::array<std::uint64_t, 2> Payload{}; -}; - -[[nodiscard]] Expected<std::optional<SpecialFloatLiteral>> -parseSpecialFloatLiteral(std::string_view Token) { - if (!Token.starts_with('+') && !Token.starts_with('-')) - return std::optional<SpecialFloatLiteral>{}; - - SpecialFloatLiteral Result; - Result.Negative = Token.front() == '-'; - Token.remove_prefix(1); - if (Token == "inf") - return Result; - if (Token == "qnan") { - Result.Kind = SpecialFloatLiteralKind::QuietNaN; - return Result; - } - - if (!Token.starts_with("nan") && !Token.starts_with("snan")) - return std::optional<SpecialFloatLiteral>{}; - - const bool IsSignaling = Token.starts_with("snan"); - const std::size_t NameLength = IsSignaling ? 4U : 3U; - const std::string_view Suffix = Token.substr(NameLength); - Result.Kind = - IsSignaling ? SpecialFloatLiteralKind::SignalingNaN : SpecialFloatLiteralKind::QuietNaN; - if (Suffix.size() < 5 || Suffix.front() != '(' || Suffix.back() != ')' || - !Suffix.substr(1).starts_with("0x")) { - return makeUnexpected(ErrorCode::ParseError, "invalid floating-point NaN payload"); - } - - const std::string_view Digits = Suffix.substr(3, Suffix.size() - 4U); - if (Digits.empty() || !std::ranges::all_of(Digits, isHexDigit)) { - return makeUnexpected(ErrorCode::ParseError, "invalid floating-point NaN payload"); - } - std::size_t Nibble = 0; - for (auto Iterator = Digits.rbegin(); Iterator != Digits.rend(); ++Iterator, ++Nibble) { - if (Nibble >= 32U) - break; - Result.Payload[Nibble / 16U] |= static_cast<std::uint64_t>(hexDigitValue(*Iterator)) - << ((Nibble % 16U) * 4U); - } - return Result; -} - -void setFloatBit(std::vector<std::uint64_t> &Words, std::uint32_t Bit) { - Words[Bit / 64U] |= std::uint64_t{1} << (Bit % 64U); -} - -void clearFloatBit(std::vector<std::uint64_t> &Words, std::uint32_t Bit) { - Words[Bit / 64U] &= ~(std::uint64_t{1} << (Bit % 64U)); -} - -[[nodiscard]] bool hasFloatBit(std::span<const std::uint64_t> Words, std::uint32_t Bit) noexcept { - return (Words[Bit / 64U] & (std::uint64_t{1} << (Bit % 64U))) != 0; -} - -void copyNaNPayload(std::vector<std::uint64_t> &Words, const std::array<std::uint64_t, 2> &Payload, - std::uint32_t PayloadBitWidth) { - for (std::uint32_t Bit = 0; Bit < PayloadBitWidth; ++Bit) { - if ((Payload[Bit / 64U] & (std::uint64_t{1} << (Bit % 64U))) != 0) - setFloatBit(Words, Bit); - } -} - -void makeBinarySpecialFloat(std::vector<std::uint64_t> &Words, std::uint32_t BitWidth, - std::uint32_t ExponentBitWidth, const SpecialFloatLiteral &Literal) { - const std::uint32_t FractionBitWidth = BitWidth - ExponentBitWidth - 1U; - if (Literal.Negative) - setFloatBit(Words, BitWidth - 1U); - for (std::uint32_t Bit = FractionBitWidth; Bit < FractionBitWidth + ExponentBitWidth; ++Bit) { - setFloatBit(Words, Bit); - } - if (Literal.Kind == SpecialFloatLiteralKind::Infinity) - return; - - copyNaNPayload(Words, Literal.Payload, FractionBitWidth); - const std::uint32_t QuietNaNBit = FractionBitWidth - 1U; - if (Literal.Kind == SpecialFloatLiteralKind::QuietNaN) { - setFloatBit(Words, QuietNaNBit); - return; - } - clearFloatBit(Words, QuietNaNBit); - bool HasPayload = false; - for (std::uint32_t Bit = 0; Bit < FractionBitWidth; ++Bit) - HasPayload = HasPayload || hasFloatBit(Words, Bit); - if (!HasPayload) - setFloatBit(Words, QuietNaNBit - 1U); -} - -[[nodiscard]] Expected<std::vector<std::uint64_t>> -makeSpecialFloatWords(FloatSemantics Semantics, const SpecialFloatLiteral &Literal) { - const auto BitWidth = getFloatSemanticsBitWidth(Semantics); - if (!BitWidth) - return makeUnexpected(ErrorCode::InvalidArgument, "floating-point semantics is unknown"); - std::vector<std::uint64_t> Words((*BitWidth + 63U) / 64U, 0); - switch (Semantics) { - case FloatSemantics::IEEEHalf: - makeBinarySpecialFloat(Words, *BitWidth, 5, Literal); - return Words; - case FloatSemantics::BFloat: - case FloatSemantics::IEEESingle: - makeBinarySpecialFloat(Words, *BitWidth, 8, Literal); - return Words; - case FloatSemantics::IEEEDouble: - makeBinarySpecialFloat(Words, *BitWidth, 11, Literal); - return Words; - case FloatSemantics::IEEEQuad: - makeBinarySpecialFloat(Words, *BitWidth, 15, Literal); - return Words; - case FloatSemantics::X87DoubleExtended: { - if (Literal.Negative) - setFloatBit(Words, 79); - for (std::uint32_t Bit = 64; Bit < 79; ++Bit) - setFloatBit(Words, Bit); - if (Literal.Kind == SpecialFloatLiteralKind::Infinity) { - setFloatBit(Words, 63); - return Words; - } - copyNaNPayload(Words, Literal.Payload, 63); - setFloatBit(Words, 63); - if (Literal.Kind == SpecialFloatLiteralKind::QuietNaN) { - setFloatBit(Words, 62); - return Words; - } - clearFloatBit(Words, 62); - bool HasPayload = false; - for (std::uint32_t Bit = 0; Bit < 63; ++Bit) - HasPayload = HasPayload || hasFloatBit(Words, Bit); - if (!HasPayload) - setFloatBit(Words, 61); - return Words; - } - case FloatSemantics::PPCDoubleDouble: { - std::vector<std::uint64_t> HighPart(1, 0); - makeBinarySpecialFloat(HighPart, 64, 11, Literal); - Words[0] = HighPart.front(); - return Words; - } - case FloatSemantics::Float8E5M2: - case FloatSemantics::Float8E5M2FNUZ: - case FloatSemantics::Float8E4M3: - case FloatSemantics::Float8E4M3FN: - case FloatSemantics::Float8E4M3FNUZ: - case FloatSemantics::Float8E4M3B11FNUZ: - case FloatSemantics::Float8E3M4: - case FloatSemantics::FloatTF32: - case FloatSemantics::Float8E8M0FNU: - case FloatSemantics::Float6E3M2FN: - case FloatSemantics::Float6E2M3FN: - case FloatSemantics::Float4E2M1FN: - case FloatSemantics::Count: - break; - } - return makeUnexpected(ErrorCode::Unsupported, - "floating-point special literal uses unsupported semantics"); -} - -struct BinaryFloatFormat final { - std::uint32_t Precision = 0; - std::int32_t MinExponent = 0; - std::int32_t MaxExponent = 0; - std::uint32_t ExponentBitWidth = 0; - std::uint32_t StorageBitWidth = 0; - bool HasExplicitIntegerBit = false; -}; - -[[nodiscard]] std::optional<BinaryFloatFormat> -binaryFloatFormat(FloatSemantics Semantics) noexcept { - switch (Semantics) { - case FloatSemantics::IEEEHalf: - return BinaryFloatFormat{.Precision = 11, - .MinExponent = -14, - .MaxExponent = 15, - .ExponentBitWidth = 5, - .StorageBitWidth = 16}; - case FloatSemantics::BFloat: - return BinaryFloatFormat{.Precision = 8, - .MinExponent = -126, - .MaxExponent = 127, - .ExponentBitWidth = 8, - .StorageBitWidth = 16}; - case FloatSemantics::IEEESingle: - return BinaryFloatFormat{.Precision = 24, - .MinExponent = -126, - .MaxExponent = 127, - .ExponentBitWidth = 8, - .StorageBitWidth = 32}; - case FloatSemantics::IEEEDouble: - return BinaryFloatFormat{.Precision = 53, - .MinExponent = -1022, - .MaxExponent = 1023, - .ExponentBitWidth = 11, - .StorageBitWidth = 64}; - case FloatSemantics::IEEEQuad: - return BinaryFloatFormat{.Precision = 113, - .MinExponent = -16382, - .MaxExponent = 16383, - .ExponentBitWidth = 15, - .StorageBitWidth = 128}; - case FloatSemantics::X87DoubleExtended: - return BinaryFloatFormat{.Precision = 64, - .MinExponent = -16382, - .MaxExponent = 16383, - .ExponentBitWidth = 15, - .StorageBitWidth = 80, - .HasExplicitIntegerBit = true}; - case FloatSemantics::PPCDoubleDouble: - // Decimal PPC double-double literals use 106 bits of precision and are - // materialized as a canonical pair of doubles. - return BinaryFloatFormat{.Precision = 106, - .MinExponent = -969, - .MaxExponent = 1023, - .ExponentBitWidth = 0, - .StorageBitWidth = 128}; - case FloatSemantics::Float8E5M2: - case FloatSemantics::Float8E5M2FNUZ: - case FloatSemantics::Float8E4M3: - case FloatSemantics::Float8E4M3FN: - case FloatSemantics::Float8E4M3FNUZ: - case FloatSemantics::Float8E4M3B11FNUZ: - case FloatSemantics::Float8E3M4: - case FloatSemantics::FloatTF32: - case FloatSemantics::Float8E8M0FNU: - case FloatSemantics::Float6E3M2FN: - case FloatSemantics::Float6E2M3FN: - case FloatSemantics::Float4E2M1FN: - case FloatSemantics::Count: - return std::nullopt; - } - return std::nullopt; -} - -struct RoundedBinaryFloat final { - bool IsZero = false; - bool IsSubnormal = false; - std::int64_t Exponent = 0; - std::int64_t Scale = 0; - BigUnsigned Significand; -}; - -[[nodiscard]] RoundedBinaryFloat makeZeroRoundedBinaryFloat() { - RoundedBinaryFloat Result; - Result.IsZero = true; - return Result; -} - -[[nodiscard]] bool checkedAddExponent(std::int64_t Left, std::int64_t Right, - std::int64_t &Result) noexcept { - if (Right > 0 && Left > std::numeric_limits<std::int64_t>::max() - Right) - return false; - if (Right < 0 && Left < std::numeric_limits<std::int64_t>::min() - Right) - return false; - Result = Left + Right; - return true; -} - -[[nodiscard]] bool checkedSubtractExponent(std::int64_t Left, std::int64_t Right, - std::int64_t &Result) noexcept { - if (Right == std::numeric_limits<std::int64_t>::min()) { - if (Left >= 0) - return false; - Result = Left - Right; - return true; - } - return checkedAddExponent(Left, -Right, Result); -} - -[[nodiscard]] Expected<BigUnsigned> divideAndRoundNearest(BigUnsigned Numerator, - const BigUnsigned &Denominator) { - if (Denominator.isZero()) - return makeUnexpected(ErrorCode::VerificationError, - "floating-point conversion has a zero denominator"); - - BigUnsigned Quotient; - const std::size_t NumeratorBits = Numerator.bitWidth(); - const std::size_t DenominatorBits = Denominator.bitWidth(); - if (NumeratorBits >= DenominatorBits) { - for (std::size_t Shift = NumeratorBits - DenominatorBits + 1U; Shift != 0; --Shift) { - const std::size_t ActualShift = Shift - 1U; - if (compareToShifted(Numerator, Denominator, ActualShift) < 0) - continue; - Numerator.subtract(Denominator.shiftedLeft(ActualShift)); - Quotient.setBit(ActualShift); - } - } - - const int HalfComparison = Numerator.shiftedLeft(1).compare(Denominator); - if (HalfComparison > 0 || (HalfComparison == 0 && Quotient.isOdd())) - Quotient.addSmall(1); - return Quotient; -} - -[[nodiscard]] Expected<BigUnsigned> -roundScaledRational(BigUnsigned Numerator, BigUnsigned Denominator, std::int64_t Shift) { - const auto AbsoluteShift = [Shift]() -> std::uint64_t { - if (Shift >= 0) - return static_cast<std::uint64_t>(Shift); - return static_cast<std::uint64_t>(-(Shift + 1)) + 1U; - }(); - if (AbsoluteShift > std::numeric_limits<std::size_t>::max()) { - return makeUnexpected(ErrorCode::ParseError, - "floating-point literal has an unrepresentable binary exponent"); - } - if (Shift >= 0) - Numerator = Numerator.shiftedLeft(static_cast<std::size_t>(AbsoluteShift)); - else - Denominator = Denominator.shiftedLeft(static_cast<std::size_t>(AbsoluteShift)); - return divideAndRoundNearest(std::move(Numerator), Denominator); -} - -[[nodiscard]] Expected<std::int64_t> floorLog2OfRational(const BigUnsigned &Numerator, - const BigUnsigned &Denominator, - std::int64_t BinaryScale) { - if (Numerator.isZero() || Denominator.isZero()) { - return makeUnexpected(ErrorCode::VerificationError, - "floating-point conversion requires a nonzero rational value"); - } - const std::size_t NumeratorBits = Numerator.bitWidth(); - const std::size_t DenominatorBits = Denominator.bitWidth(); - if (NumeratorBits > static_cast<std::size_t>(std::numeric_limits<std::int64_t>::max()) || - DenominatorBits > static_cast<std::size_t>(std::numeric_limits<std::int64_t>::max())) { - return makeUnexpected(ErrorCode::Unsupported, - "floating-point literal significand is too large"); - } - const std::int64_t BitDelta = - static_cast<std::int64_t>(NumeratorBits) - static_cast<std::int64_t>(DenominatorBits); - std::int64_t Candidate = 0; - if (!checkedAddExponent(BinaryScale, BitDelta, Candidate)) { - return makeUnexpected(BinaryScale >= 0 ? ErrorCode::ParseError : ErrorCode::Unsupported, - BinaryScale >= 0 ? "floating-point constant overflowed type" - : "floating-point constant underflowed type"); - } - - const std::int64_t RelativeShift = -BitDelta; - int Comparison = 0; - if (RelativeShift >= 0) { - if (static_cast<std::uint64_t>(RelativeShift) > std::numeric_limits<std::size_t>::max()) { - Comparison = -1; - } else { - Comparison = - -compareToShifted(Denominator, Numerator, static_cast<std::size_t>(RelativeShift)); - } - } else { - const std::uint64_t AbsoluteShift = static_cast<std::uint64_t>(-(RelativeShift + 1)) + 1U; - if (AbsoluteShift > std::numeric_limits<std::size_t>::max()) - Comparison = 1; - else - Comparison = - compareToShifted(Numerator, Denominator, static_cast<std::size_t>(AbsoluteShift)); - } - if (Comparison >= 0) - return Candidate; - if (Candidate == std::numeric_limits<std::int64_t>::min()) { - return makeUnexpected(ErrorCode::ParseError, "floating-point constant underflowed type"); - } - return Candidate - 1; -} - -[[nodiscard]] Expected<RoundedBinaryFloat> roundBinaryRational(const BinaryFloatFormat &Format, - const BigUnsigned &Numerator, - const BigUnsigned &Denominator, - std::int64_t BinaryScale, - bool PermitZeroResult = false) { - if (Numerator.isZero()) - return makeZeroRoundedBinaryFloat(); - - auto FloorExponent = floorLog2OfRational(Numerator, Denominator, BinaryScale); - if (!FloorExponent) - return std::unexpected(FloorExponent.error()); - if (*FloorExponent > Format.MaxExponent) { - return makeUnexpected(ErrorCode::ParseError, "floating-point constant overflowed type"); - } - - const std::int64_t SmallestUnit = - static_cast<std::int64_t>(Format.MinExponent) - (Format.Precision - 1U); - if (*FloorExponent < SmallestUnit - 1) { - if (PermitZeroResult) - return makeZeroRoundedBinaryFloat(); - return makeUnexpected(ErrorCode::ParseError, "floating-point constant underflowed type"); - } - - const bool StartsNormal = *FloorExponent >= Format.MinExponent; - const std::int64_t Unit = StartsNormal ? *FloorExponent - (Format.Precision - 1U) : SmallestUnit; - std::int64_t Shift = 0; - if (!checkedSubtractExponent(BinaryScale, Unit, Shift)) { - return makeUnexpected(BinaryScale >= 0 ? ErrorCode::ParseError : ErrorCode::Unsupported, - BinaryScale >= 0 ? "floating-point constant overflowed type" - : "floating-point constant underflowed type"); - } - auto Significand = roundScaledRational(Numerator, Denominator, Shift); - if (!Significand) - return std::unexpected(Significand.error()); - if (Significand->isZero()) { - if (PermitZeroResult) - return makeZeroRoundedBinaryFloat(); - return makeUnexpected(ErrorCode::ParseError, "floating-point constant underflowed type"); - } - - RoundedBinaryFloat Result; - Result.Significand = std::move(*Significand); - if (StartsNormal) { - Result.Exponent = *FloorExponent; - if (Result.Significand.bitWidth() > Format.Precision) { - Result.Significand.shiftRightOne(); - if (Result.Exponent == Format.MaxExponent) { - return makeUnexpected(ErrorCode::ParseError, "floating-point constant overflowed type"); - } - ++Result.Exponent; - } - Result.Scale = Result.Exponent - (Format.Precision - 1U); - return Result; - } - - if (Result.Significand.bitWidth() >= Format.Precision) { - Result.IsSubnormal = false; - Result.Exponent = Format.MinExponent; - Result.Scale = Result.Exponent - (Format.Precision - 1U); - return Result; - } - Result.IsSubnormal = true; - Result.Exponent = Format.MinExponent; - Result.Scale = SmallestUnit; - return Result; -} - -void appendSignificandWords(std::vector<std::uint64_t> &Words, const BigUnsigned &Significand) { - for (std::size_t Index = 0; Index < Words.size(); ++Index) - Words[Index] = Significand.word(Index); -} - -[[nodiscard]] Expected<std::vector<std::uint64_t>> -packIEEEBinaryFloat(const BinaryFloatFormat &Format, const RoundedBinaryFloat &Value, - bool Negative) { - if (Format.HasExplicitIntegerBit || Format.ExponentBitWidth == 0 || Format.StorageBitWidth == 0) { - return makeUnexpected(ErrorCode::VerificationError, - "floating-point format does not use IEEE binary storage"); - } - std::vector<std::uint64_t> Words((Format.StorageBitWidth + 63U) / 64U, 0); - if (Value.IsZero) { - if (Negative) - setFloatBit(Words, Format.StorageBitWidth - 1U); - return Words; - } - - appendSignificandWords(Words, Value.Significand); - const std::uint32_t FractionBitWidth = Format.Precision - 1U; - if (!Value.IsSubnormal) - clearFloatBit(Words, FractionBitWidth); - const std::int64_t Bias = 1 - static_cast<std::int64_t>(Format.MinExponent); - const std::uint64_t Exponent = - Value.IsSubnormal ? 0 : static_cast<std::uint64_t>(Value.Exponent + Bias); - for (std::uint32_t Bit = 0; Bit < Format.ExponentBitWidth; ++Bit) { - if ((Exponent & (std::uint64_t{1} << Bit)) != 0) - setFloatBit(Words, FractionBitWidth + Bit); - } - if (Negative) - setFloatBit(Words, Format.StorageBitWidth - 1U); - return Words; -} - -[[nodiscard]] Expected<std::vector<std::uint64_t>> -packX87Float(const BinaryFloatFormat &Format, const RoundedBinaryFloat &Value, bool Negative) { - if (!Format.HasExplicitIntegerBit || Format.StorageBitWidth != 80 || Format.Precision != 64) { - return makeUnexpected(ErrorCode::VerificationError, "x87 floating-point format is invalid"); - } - std::vector<std::uint64_t> Words(2, 0); - if (Value.IsZero) { - if (Negative) - setFloatBit(Words, 79); - return Words; - } - Words[0] = Value.Significand.word(0); - if (!Value.IsSubnormal) { - const std::int64_t Bias = 1 - static_cast<std::int64_t>(Format.MinExponent); - Words[1] = static_cast<std::uint64_t>(Value.Exponent + Bias); - } - if (Negative) - setFloatBit(Words, 79); - return Words; -} - -[[nodiscard]] Expected<std::vector<std::uint64_t>> -packPPCDoubleDouble(const RoundedBinaryFloat &Value, bool Negative) { - const auto DoubleFormat = binaryFloatFormat(FloatSemantics::IEEEDouble); - if (!DoubleFormat) - return makeUnexpected(ErrorCode::VerificationError, "IEEE double format is unavailable"); - if (Value.IsZero) { - RoundedBinaryFloat Zero = makeZeroRoundedBinaryFloat(); - auto High = packIEEEBinaryFloat(*DoubleFormat, Zero, Negative); - if (!High) - return std::unexpected(High.error()); - return std::vector<std::uint64_t>{High->front(), 0}; - } - - BigUnsigned One(1); - auto High = roundBinaryRational(*DoubleFormat, Value.Significand, One, Value.Scale, true); - if (!High) - return std::unexpected(High.error()); - auto HighWords = packIEEEBinaryFloat(*DoubleFormat, *High, Negative); - if (!HighWords) - return std::unexpected(HighWords.error()); - - BigUnsigned SourceMagnitude = Value.Significand; - BigUnsigned HighMagnitude = High->Significand; - const std::int64_t CommonScale = std::min(Value.Scale, High->Scale); - if (!SourceMagnitude.isZero()) { - const std::int64_t SourceShift = Value.Scale - CommonScale; - if (SourceShift > 0) - SourceMagnitude = SourceMagnitude.shiftedLeft(static_cast<std::size_t>(SourceShift)); - } - if (!HighMagnitude.isZero()) { - const std::int64_t HighShift = High->Scale - CommonScale; - if (HighShift > 0) - HighMagnitude = HighMagnitude.shiftedLeft(static_cast<std::size_t>(HighShift)); - } - - bool LowNegative = false; - BigUnsigned LowMagnitude; - if (SourceMagnitude.compare(HighMagnitude) >= 0) { - LowMagnitude = std::move(SourceMagnitude); - LowMagnitude.subtract(HighMagnitude); - LowNegative = Negative; - } else { - LowMagnitude = std::move(HighMagnitude); - LowMagnitude.subtract(SourceMagnitude); - LowNegative = !Negative; - } - - auto Low = roundBinaryRational(*DoubleFormat, LowMagnitude, One, CommonScale, true); - if (!Low) - return std::unexpected(Low.error()); - auto LowWords = packIEEEBinaryFloat(*DoubleFormat, *Low, LowNegative); - if (!LowWords) - return std::unexpected(LowWords.error()); - return std::vector<std::uint64_t>{HighWords->front(), LowWords->front()}; -} - -[[nodiscard]] Expected<std::vector<std::uint64_t>> -convertRationalToFloatWords(FloatSemantics Semantics, bool Negative, const BigUnsigned &Numerator, - const BigUnsigned &Denominator, std::int64_t BinaryScale) { - const auto Format = binaryFloatFormat(Semantics); - if (!Format) { - return makeUnexpected(ErrorCode::Unsupported, - "floating-point literal uses unsupported semantics"); - } - auto Rounded = roundBinaryRational(*Format, Numerator, Denominator, BinaryScale); - if (!Rounded) - return std::unexpected(Rounded.error()); - if (Semantics == FloatSemantics::PPCDoubleDouble) { - return packPPCDoubleDouble(*Rounded, Negative); - } - if (Format->HasExplicitIntegerBit) - return packX87Float(*Format, *Rounded, Negative); - return packIEEEBinaryFloat(*Format, *Rounded, Negative); -} - -[[nodiscard]] bool isDecimalDigit(char Character) noexcept { - return Character >= '0' && Character <= '9'; -} - -[[nodiscard]] Expected<std::int32_t> parseFloatingExponent(std::string_view Text, - std::int32_t Limit) { - if (Text.empty()) - return makeUnexpected(ErrorCode::ParseError, "floating-point exponent has no digits"); - bool Negative = false; - if (Text.front() == '+' || Text.front() == '-') { - Negative = Text.front() == '-'; - Text.remove_prefix(1); - } - if (Text.empty()) - return makeUnexpected(ErrorCode::ParseError, "floating-point exponent has no digits"); - - std::int32_t Value = 0; - for (const char Character : Text) { - if (!isDecimalDigit(Character)) { - return makeUnexpected(ErrorCode::ParseError, - "floating-point exponent contains a non-decimal digit"); - } - const std::int32_t Digit = Character - '0'; - if (Value >= Limit || Value > (Limit - Digit) / 10) { - Value = Limit; - continue; - } - Value = Value * 10 + Digit; - } - return Negative ? -Value : Value; -} - -[[nodiscard]] BigUnsigned parseDecimalSignificand(std::string_view Digits) { - BigUnsigned Result; - std::size_t Position = 0; - std::size_t FirstChunkLength = Digits.size() % 9U; - if (FirstChunkLength == 0) - FirstChunkLength = std::min<std::size_t>(Digits.size(), 9U); - while (Position < Digits.size()) { - const std::size_t Length = Position == 0 ? FirstChunkLength : 9U; - std::uint32_t Chunk = 0; - for (std::size_t Index = 0; Index < Length; ++Index) - Chunk = Chunk * 10U + static_cast<std::uint32_t>(Digits[Position + Index] - '0'); - Result.multiplySmall(Position == 0 ? 1U : 1000000000U); - Result.addSmall(Chunk); - Position += Length; - } - return Result; -} - -[[nodiscard]] BigUnsigned parseHexSignificand(std::string_view Digits) { - BigUnsigned Result; - for (const char Character : Digits) { - Result.multiplySmall(16U); - Result.addSmall(hexDigitValue(Character)); - } - return Result; -} - -void multiplyByPowerOfFive(BigUnsigned &Value, std::uint64_t Exponent) { - for (std::uint64_t Index = 0; Index < Exponent; ++Index) - Value.multiplySmall(5U); -} - -[[nodiscard]] BigUnsigned powerOfFive(std::uint64_t Exponent) { - BigUnsigned Result(1); - multiplyByPowerOfFive(Result, Exponent); - return Result; -} - -[[nodiscard]] bool decimalDefinitelyOverflows(const BinaryFloatFormat &Format, - std::int64_t NormalizedExponent) noexcept { - const __int128 Left = (static_cast<__int128>(NormalizedExponent) - 1) * 42039; - const __int128 Right = static_cast<__int128>(12655) * Format.MaxExponent; - return Left >= Right; -} - -[[nodiscard]] bool decimalDefinitelyUnderflows(const BinaryFloatFormat &Format, - std::int64_t NormalizedExponent) noexcept { - const __int128 Left = (static_cast<__int128>(NormalizedExponent) + 1) * 28738; - const __int128 Right = static_cast<__int128>(8651) * - (static_cast<std::int64_t>(Format.MinExponent) - Format.Precision); - return Left <= Right; -} - -struct ParsedFiniteFloatLiteral final { - bool Negative = false; - BigUnsigned Numerator; - BigUnsigned Denominator{1}; - std::int64_t BinaryScale = 0; -}; - -[[nodiscard]] Expected<ParsedFiniteFloatLiteral> -parseDecimalFloatLiteral(std::string_view Token, const BinaryFloatFormat &Format) { - ParsedFiniteFloatLiteral Result; - if (Token.starts_with('+') || Token.starts_with('-')) { - Result.Negative = Token.front() == '-'; - Token.remove_prefix(1); - } - if (Token.empty()) - return makeUnexpected(ErrorCode::ParseError, "floating-point literal has no digits"); - - std::size_t Position = 0; - const std::size_t IntegerBegin = Position; - while (Position < Token.size() && isDecimalDigit(Token[Position])) - ++Position; - if (IntegerBegin == Position || Position == Token.size() || Token[Position] != '.') { - return makeUnexpected(ErrorCode::ParseError, - "decimal floating-point literal requires a decimal point"); - } - - std::string Digits(Token.substr(IntegerBegin, Position - IntegerBegin)); - ++Position; - const std::size_t FractionBegin = Position; - while (Position < Token.size() && isDecimalDigit(Token[Position])) - ++Position; - Digits.append(Token.substr(FractionBegin, Position - FractionBegin)); - const std::size_t FractionDigits = Position - FractionBegin; - - std::int32_t Exponent = 0; - if (Position < Token.size() && (Token[Position] == 'e' || Token[Position] == 'E')) { - auto ParsedExponent = parseFloatingExponent(Token.substr(Position + 1U), 24000); - if (!ParsedExponent) - return std::unexpected(ParsedExponent.error()); - Exponent = *ParsedExponent; - Position = Token.size(); - } - if (Position != Token.size()) { - return makeUnexpected(ErrorCode::ParseError, - "decimal floating-point literal has an invalid suffix"); - } - - const std::size_t FirstSignificant = Digits.find_first_not_of('0'); - if (FirstSignificant == std::string::npos) - return Result; - const std::size_t LastSignificant = Digits.find_last_not_of('0'); - const std::string_view Significant(Digits.data() + FirstSignificant, - LastSignificant - FirstSignificant + 1U); - const std::size_t TrailingZeroCount = Digits.size() - LastSignificant - 1U; - std::int64_t DecimalScale = static_cast<std::int64_t>(Exponent) - - static_cast<std::int64_t>(FractionDigits) + - static_cast<std::int64_t>(TrailingZeroCount); - const std::int64_t NormalizedExponent = - DecimalScale + static_cast<std::int64_t>(Significant.size() - 1U); - if (decimalDefinitelyOverflows(Format, NormalizedExponent)) { - return makeUnexpected(ErrorCode::ParseError, "floating-point constant overflowed type"); - } - if (decimalDefinitelyUnderflows(Format, NormalizedExponent)) { - return makeUnexpected(ErrorCode::ParseError, "floating-point constant underflowed type"); - } - - Result.Numerator = parseDecimalSignificand(Significant); - Result.BinaryScale = DecimalScale; - if (DecimalScale >= 0) { - multiplyByPowerOfFive(Result.Numerator, static_cast<std::uint64_t>(DecimalScale)); - } else { - Result.Denominator = powerOfFive(static_cast<std::uint64_t>(-(DecimalScale + 1)) + 1U); - } - return Result; -} - -[[nodiscard]] Expected<ParsedFiniteFloatLiteral> parseHexFloatLiteral(std::string_view Token) { - ParsedFiniteFloatLiteral Result; - if (Token.starts_with('+') || Token.starts_with('-')) { - Result.Negative = Token.front() == '-'; - Token.remove_prefix(1); - } - if (!Token.starts_with("0x")) { - return makeUnexpected(ErrorCode::ParseError, - "hexadecimal floating-point literal must use the 0x prefix"); - } - std::size_t Position = 2; - const std::size_t IntegerBegin = Position; - while (Position < Token.size() && isHexDigit(Token[Position])) - ++Position; - if (IntegerBegin == Position || Position == Token.size() || Token[Position] != '.') { - return makeUnexpected(ErrorCode::ParseError, - "hexadecimal floating-point literal requires a hexadecimal point"); - } - std::string Digits(Token.substr(IntegerBegin, Position - IntegerBegin)); - ++Position; - const std::size_t FractionBegin = Position; - while (Position < Token.size() && isHexDigit(Token[Position])) - ++Position; - Digits.append(Token.substr(FractionBegin, Position - FractionBegin)); - const std::size_t FractionDigits = Position - FractionBegin; - if (Position == Token.size() || (Token[Position] != 'p' && Token[Position] != 'P')) { - return makeUnexpected(ErrorCode::ParseError, - "hexadecimal floating-point literal requires a binary exponent"); - } - auto Exponent = parseFloatingExponent(Token.substr(Position + 1U), 32767); - if (!Exponent) - return std::unexpected(Exponent.error()); - - const std::size_t FirstSignificant = Digits.find_first_not_of('0'); - if (FirstSignificant == std::string::npos) - return Result; - const std::string_view Significant(Digits.data() + FirstSignificant, - Digits.size() - FirstSignificant); - Result.Numerator = parseHexSignificand(Significant); - Result.BinaryScale = - static_cast<std::int64_t>(*Exponent) - static_cast<std::int64_t>(FractionDigits) * 4; - return Result; -} - -[[nodiscard]] Expected<std::uint64_t> parseRawDoubleBits(std::string_view Digits) { - if (Digits.empty()) - return makeUnexpected(ErrorCode::ParseError, - "hexadecimal floating-point literal has no digits"); - std::uint64_t Result = 0; - for (const char Character : Digits) { - if (!isHexDigit(Character)) { - return makeUnexpected(ErrorCode::ParseError, - "hexadecimal floating-point literal contains a non-hexadecimal digit"); - } - const std::uint64_t Digit = hexDigitValue(Character); - if (Result > (std::numeric_limits<std::uint64_t>::max() - Digit) / 16U) { - return makeUnexpected(ErrorCode::ParseError, - "hexadecimal floating-point literal does not fit IEEE double"); - } - Result = Result * 16U + Digit; - } - return Result; -} - -[[nodiscard]] int compareScaledBinaryIntegers(const BigUnsigned &Left, std::int64_t LeftScale, - const BigUnsigned &Right, std::int64_t RightScale) { - if (Left.isZero()) - return Right.isZero() ? 0 : -1; - if (Right.isZero()) - return 1; - if (LeftScale == RightScale) - return Left.compare(Right); - if (LeftScale > RightScale) - return -compareToShifted(Right, Left, static_cast<std::size_t>(LeftScale - RightScale)); - return compareToShifted(Left, Right, static_cast<std::size_t>(RightScale - LeftScale)); -} - -[[nodiscard]] Expected<std::vector<std::uint64_t>> -convertRawDoubleNaN(FloatSemantics Semantics, bool Negative, std::uint64_t Fraction) { - if (Semantics == FloatSemantics::IEEEDouble) { - return std::vector<std::uint64_t>{(Negative ? std::uint64_t{1} << 63U : 0) | - 0x7ff0000000000000ULL | Fraction}; - } - const auto Format = binaryFloatFormat(Semantics); - if (!Format || Format->HasExplicitIntegerBit || Format->StorageBitWidth > 64 || - (Semantics != FloatSemantics::IEEEHalf && Semantics != FloatSemantics::BFloat && - Semantics != FloatSemantics::IEEESingle)) { - return makeUnexpected(ErrorCode::ParseError, - "unprefixed hexadecimal literal is invalid for type"); - } - const std::uint32_t TargetFractionBits = Format->Precision - 1U; - const std::uint32_t DroppedBits = 53U - Format->Precision; - const std::uint64_t DroppedMask = (std::uint64_t{1} << DroppedBits) - 1U; - if ((Fraction & DroppedMask) != 0) { - return makeUnexpected( - ErrorCode::ParseError, - "hexadecimal floating-point literal is not exactly representable by its type"); - } - - const bool IsSignaling = (Fraction & (std::uint64_t{1} << 51U)) == 0; - std::uint64_t TargetFraction = Fraction >> DroppedBits; - const std::uint64_t QuietBit = std::uint64_t{1} << (TargetFractionBits - 1U); - if (IsSignaling) { - TargetFraction &= ~QuietBit; - if ((TargetFraction & (QuietBit - 1U)) == 0) - TargetFraction |= QuietBit >> 1U; - } else { - TargetFraction |= QuietBit; - } - const std::uint64_t Sign = Negative ? std::uint64_t{1} << (Format->StorageBitWidth - 1U) : 0; - const std::uint64_t Exponent = (std::uint64_t{1} << Format->ExponentBitWidth) - 1U; - return std::vector<std::uint64_t>{Sign | (Exponent << TargetFractionBits) | TargetFraction}; -} - -[[nodiscard]] Expected<std::vector<std::uint64_t>> convertRawDoubleLiteral(FloatSemantics Semantics, - std::uint64_t Bits) { - if (Semantics != FloatSemantics::IEEEHalf && Semantics != FloatSemantics::BFloat && - Semantics != FloatSemantics::IEEESingle && Semantics != FloatSemantics::IEEEDouble) { - return makeUnexpected(ErrorCode::ParseError, - "unprefixed hexadecimal literal is invalid for type"); - } - if (Semantics == FloatSemantics::IEEEDouble) - return std::vector<std::uint64_t>{Bits}; - - const bool Negative = (Bits >> 63U) != 0; - const std::uint64_t Exponent = (Bits >> 52U) & 0x7ffU; - const std::uint64_t Fraction = Bits & 0x000fffffffffffffULL; - if (Exponent == 0x7ffU) { - if (Fraction != 0) - return convertRawDoubleNaN(Semantics, Negative, Fraction); - SpecialFloatLiteral Infinity{.Kind = SpecialFloatLiteralKind::Infinity, .Negative = Negative}; - return makeSpecialFloatWords(Semantics, Infinity); - } - - const auto Format = binaryFloatFormat(Semantics); - if (!Format) - return makeUnexpected(ErrorCode::VerificationError, "IEEE target format is unavailable"); - if (Exponent == 0 && Fraction == 0) { - RoundedBinaryFloat Zero = makeZeroRoundedBinaryFloat(); - return packIEEEBinaryFloat(*Format, Zero, Negative); - } - - const bool IsSubnormal = Exponent == 0; - const std::uint64_t Significand = IsSubnormal ? Fraction : (std::uint64_t{1} << 52U) | Fraction; - const std::int64_t SourceScale = - (IsSubnormal ? -1022 : static_cast<std::int64_t>(Exponent) - 1023) - 52; - const BigUnsigned Source(Significand); - const BigUnsigned One(1); - auto Rounded = roundBinaryRational(*Format, Source, One, SourceScale); - if (!Rounded) - return std::unexpected(Rounded.error()); - if (compareScaledBinaryIntegers(Source, SourceScale, Rounded->Significand, Rounded->Scale) != 0) { - return makeUnexpected( - ErrorCode::ParseError, - "hexadecimal floating-point literal is not exactly representable by its type"); - } - return packIEEEBinaryFloat(*Format, *Rounded, Negative); -} - -[[nodiscard]] Expected<IntegerImmediate> parseExplicitHexInteger(std::uint32_t BitWidth, - std::string_view Spelling) { - if (Spelling.size() < 4 || (Spelling[0] != 'u' && Spelling[0] != 's') || Spelling[1] != '0' || - Spelling[2] != 'x') { - return makeUnexpected(ErrorCode::ParseError, - "integer hexadecimal spelling must use u0x or s0x"); - } - const std::string_view Digits = Spelling.substr(3); - if (Digits.empty() || !std::ranges::all_of(Digits, isHexDigit)) { - return makeUnexpected(ErrorCode::ParseError, - "integer hexadecimal spelling contains a non-hexadecimal digit"); - } - - const auto *const FirstNonZero = - std::ranges::find_if(Digits, [](char Character) { return hexDigitValue(Character) != 0; }); - std::size_t ActiveBits = 0; - if (FirstNonZero != Digits.end()) { - const unsigned MostSignificantNibble = hexDigitValue(*FirstNonZero); - unsigned MostSignificantBits = 0; - for (unsigned Value = MostSignificantNibble; Value != 0; Value >>= 1U) - ++MostSignificantBits; - ActiveBits = - static_cast<std::size_t>(Digits.end() - FirstNonZero - 1U) * 4U + MostSignificantBits; - } - - std::vector<std::uint64_t> Words((BitWidth + 63U) / 64U, 0); - std::size_t Nibble = 0; - for (auto Iterator = Digits.rbegin(); Iterator != Digits.rend(); ++Iterator, ++Nibble) { - const std::size_t BitOffset = Nibble * 4U; - if (BitOffset >= BitWidth) - break; - Words[BitOffset / 64U] |= static_cast<std::uint64_t>(hexDigitValue(*Iterator)) - << (BitOffset % 64U); - } - - // LLVM discards leading zero bits before it assigns signedness to an s0x - // literal. A nonzero signed spelling is therefore sign-extended from its - // active width, rather than from the number of written hexadecimal digits. - if (Spelling[0] == 's' && ActiveBits != 0 && ActiveBits < BitWidth) { - const std::size_t FirstExtendedBit = ActiveBits; - const std::size_t FirstWord = FirstExtendedBit / 64U; - const unsigned FirstBit = static_cast<unsigned>(FirstExtendedBit % 64U); - Words[FirstWord] |= std::numeric_limits<std::uint64_t>::max() << FirstBit; - for (std::size_t Index = FirstWord + 1U; Index < Words.size(); ++Index) - Words[Index] = std::numeric_limits<std::uint64_t>::max(); - } - if ((BitWidth % 64U) != 0) - Words.back() &= (std::uint64_t{1} << (BitWidth % 64U)) - 1U; - return IntegerImmediate::create(BitWidth, Words); -} - [[nodiscard]] std::optional<FloatSemantics> floatingSemantics(std::string_view Name) noexcept { if (Name == "half") return FloatSemantics::IEEEHalf; @@ -1751,98 +545,9 @@ Expected<GenericConstantData> ConstantParser::parseFloatingValue(GenericConstant auto Token = parseValueToken(); if (!Token) return std::unexpected(Token.error()); - const std::uint32_t BitWidth = getFloatSemanticsBitWidth(EffectiveType.Semantics).value_or(0); - const auto Format = binaryFloatFormat(EffectiveType.Semantics); - if (!Format) - return fail<GenericConstantData>("floating-point type has unsupported semantics"); - std::vector<std::uint64_t> Words; - - auto SpecialLiteral = parseSpecialFloatLiteral(*Token); - if (!SpecialLiteral) - return fail<GenericConstantData>(SpecialLiteral.error().message()); - if (*SpecialLiteral) { - auto SpecialWords = makeSpecialFloatWords(EffectiveType.Semantics, **SpecialLiteral); - if (!SpecialWords) - return fail<GenericConstantData>(SpecialWords.error().message()); - Words = std::move(*SpecialWords); - } else if (Token->starts_with("f0x")) { - const std::string_view Digits = Token->substr(3); - if (Digits.size() != (static_cast<std::size_t>(BitWidth) + 3U) / 4U || - !parseHexWords(Digits, BitWidth, Words)) { - return fail<GenericConstantData>("exact floating-point bit pattern does not fit its type"); - } - } else if (Token->starts_with("0x") && Token->find('.') != std::string_view::npos) { - auto Literal = parseHexFloatLiteral(*Token); - if (!Literal) - return fail<GenericConstantData>(Literal.error().message()); - auto Converted = - convertRationalToFloatWords(EffectiveType.Semantics, Literal->Negative, Literal->Numerator, - Literal->Denominator, Literal->BinaryScale); - if (!Converted) - return fail<GenericConstantData>(Converted.error().message()); - Words = std::move(*Converted); - } else if (Token->starts_with("0x")) { - std::string_view Digits = Token->substr(2); - char Prefix = '\0'; - if (!Digits.empty() && !isHexDigit(Digits.front())) { - Prefix = Digits.front(); - Digits.remove_prefix(1); - } - char ExpectedPrefix = '\0'; - switch (EffectiveType.Semantics) { - case FloatSemantics::IEEEHalf: - ExpectedPrefix = 'H'; - break; - case FloatSemantics::BFloat: - ExpectedPrefix = 'R'; - break; - case FloatSemantics::X87DoubleExtended: - ExpectedPrefix = 'K'; - break; - case FloatSemantics::IEEEQuad: - ExpectedPrefix = 'L'; - break; - case FloatSemantics::PPCDoubleDouble: - ExpectedPrefix = 'M'; - break; - default: - break; - } - if (ExpectedPrefix != '\0') { - const bool HasWideHexWords = EffectiveType.Semantics == FloatSemantics::IEEEQuad || - EffectiveType.Semantics == FloatSemantics::PPCDoubleDouble; - const bool Parsed = HasWideHexWords ? parseWideFloatHexWords(Digits, Words) - : parseHexWords(Digits, BitWidth, Words); - if (Prefix != ExpectedPrefix || !Parsed) { - return fail<GenericConstantData>( - "floating-point hexadecimal prefix does not match its type"); - } - } else if (Prefix != '\0') { - return fail<GenericConstantData>("unexpected floating-point hexadecimal prefix"); - } else { - auto DoubleBits = parseRawDoubleBits(Digits); - if (!DoubleBits) - return fail<GenericConstantData>(DoubleBits.error().message()); - auto Converted = convertRawDoubleLiteral(EffectiveType.Semantics, *DoubleBits); - if (!Converted) - return fail<GenericConstantData>(Converted.error().message()); - Words = std::move(*Converted); - } - } else { - auto Literal = parseDecimalFloatLiteral(*Token, *Format); - if (!Literal) - return fail<GenericConstantData>(Literal.error().message()); - auto Converted = - convertRationalToFloatWords(EffectiveType.Semantics, Literal->Negative, Literal->Numerator, - Literal->Denominator, Literal->BinaryScale); - if (!Converted) - return fail<GenericConstantData>(Converted.error().message()); - Words = std::move(*Converted); - } - - auto Bits = FloatingPointBits::create(EffectiveType.Semantics, Words); + auto Bits = APFloat::parse(EffectiveType.Semantics, *Token); if (!Bits) - return std::unexpected(Bits.error()); + return fail<GenericConstantData>(Bits.error().message()); GenericConstantData Value; Value.Type = std::move(Type); Value.Kind = GenericConstantKind::Floating; @@ -1979,7 +684,7 @@ Expected<GenericConstantData> ConstantParser::parseExpression(GenericConstantTyp auto StartToken = parseValueToken(); if (!StartToken) return std::unexpected(StartToken.error()); - auto Start = IntegerImmediate::parseDecimal(64, *StartToken); + auto Start = APInt::parseDecimal(64, *StartToken); if (!Start) return fail<GenericConstantData>("invalid inrange start"); if (auto Comma = expect(',', "',' in inrange interval"); !Comma) @@ -1987,7 +692,7 @@ Expected<GenericConstantData> ConstantParser::parseExpression(GenericConstantTyp auto EndToken = parseValueToken(); if (!EndToken) return std::unexpected(EndToken.error()); - auto End = IntegerImmediate::parseDecimal(64, *EndToken); + auto End = APInt::parseDecimal(64, *EndToken); if (!End) return fail<GenericConstantData>("invalid inrange end"); if (auto Close = expect(')', "')' after inrange interval"); !Close) @@ -2239,8 +944,8 @@ Expected<GenericConstantData> ConstantParser::parseValue(GenericConstantType Typ 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 ? parseExplicitHexInteger(BitWidth, Spelling) - : IntegerImmediate::parseDecimal(BitWidth, Spelling); + auto Integer = IsExplicitHex ? APInt::parseLLVMHex(BitWidth, Spelling) + : APInt::parseDecimal(BitWidth, Spelling); if (!Integer) return fail<GenericConstantData>(Integer.error().message()); GenericConstantData Value; @@ -2280,21 +985,6 @@ Expected<GenericConstantType> ConstantParser::parseTypeOnly() { return Type; } -[[nodiscard]] std::string hexadecimalWords(std::span<const std::uint64_t> Words, - std::uint32_t BitWidth) { - static constexpr std::string_view Digits = "0123456789ABCDEF"; - const std::size_t DigitCount = std::max<std::size_t>((BitWidth + 3U) / 4U, 1); - std::string Result(DigitCount, '0'); - for (std::size_t DigitIndex = 0; DigitIndex < DigitCount; ++DigitIndex) { - const std::size_t SourceNibble = DigitCount - 1U - DigitIndex; - const std::size_t WordIndex = SourceNibble / 16U; - const unsigned Shift = static_cast<unsigned>((SourceNibble % 16U) * 4U); - if (WordIndex < Words.size()) - Result[DigitIndex] = Digits[(Words[WordIndex] >> Shift) & 0xfU]; - } - return Result; -} - [[nodiscard]] bool canUseBareName(std::string_view Name) noexcept { if (Name.empty() || !isIdentifierStart(Name.front())) return false; @@ -2431,224 +1121,6 @@ Expected<std::string> printType(const GenericConstantType &Type) { return makeUnexpected(ErrorCode::VerificationError, "generic constant type is invalid"); } -enum class PrintedFloatCategory : std::uint8_t { - Zero, - Finite, - Infinity, - QuietNaN, - SignalingNaN, -}; - -struct PrintedBinaryFloat final { - PrintedFloatCategory Category = PrintedFloatCategory::Zero; - bool Negative = false; - BigUnsigned Significand; - std::int32_t BinaryExponent = 0; - BigUnsigned NaNPayload; - std::uint32_t QuietNaNBit = 0; -}; - -[[nodiscard]] std::uint64_t extractFloatField(std::span<const std::uint64_t> Words, - std::uint32_t Begin, std::uint32_t Width) noexcept { - std::uint64_t Result = 0; - for (std::uint32_t Bit = 0; Bit < Width; ++Bit) { - if (hasFloatBit(Words, Begin + Bit)) - Result |= std::uint64_t{1} << Bit; - } - return Result; -} - -[[nodiscard]] BigUnsigned extractFloatBits(std::span<const std::uint64_t> Words, - std::uint32_t Begin, std::uint32_t Width) { - BigUnsigned Result; - for (std::uint32_t Bit = 0; Bit < Width; ++Bit) { - if (hasFloatBit(Words, Begin + Bit)) - Result.setBit(Bit); - } - return Result; -} - -[[nodiscard]] std::optional<PrintedBinaryFloat> decodePrintedFloat(const FloatingPointBits &Bits) { - std::optional<BinaryFloatFormat> Format = binaryFloatFormat(Bits.semantics()); - std::span<const std::uint64_t> Words = Bits.words(); - if (!Format) - return std::nullopt; - - if (Bits.semantics() == FloatSemantics::PPCDoubleDouble) { - if (Words.size() != 2 || Words[1] != 0) - return std::nullopt; - Format = binaryFloatFormat(FloatSemantics::IEEEDouble); - Words = Words.first(1); - } - if (!Format || Format->ExponentBitWidth == 0) - return std::nullopt; - - PrintedBinaryFloat Result; - Result.Negative = hasFloatBit(Words, Format->StorageBitWidth - 1U); - const std::uint32_t StoredSignificandWidth = - Format->HasExplicitIntegerBit ? Format->Precision : Format->Precision - 1U; - const std::uint64_t Exponent = - extractFloatField(Words, StoredSignificandWidth, Format->ExponentBitWidth); - const std::uint64_t MaximumExponent = (std::uint64_t{1} << Format->ExponentBitWidth) - 1U; - - if (Exponent == MaximumExponent) { - const std::uint32_t PayloadWidth = Format->Precision - 1U; - Result.NaNPayload = extractFloatBits(Words, 0, PayloadWidth); - if (Result.NaNPayload.isZero()) { - Result.Category = PrintedFloatCategory::Infinity; - return Result; - } - Result.QuietNaNBit = PayloadWidth - 1U; - Result.Category = hasFloatBit(Words, Result.QuietNaNBit) ? PrintedFloatCategory::QuietNaN - : PrintedFloatCategory::SignalingNaN; - return Result; - } - - Result.Significand = extractFloatBits(Words, 0, StoredSignificandWidth); - if (Exponent != 0 && !Format->HasExplicitIntegerBit) - Result.Significand.setBit(Format->Precision - 1U); - if (Result.Significand.isZero()) { - Result.Category = PrintedFloatCategory::Zero; - return Result; - } - - const std::int64_t Bias = 1 - static_cast<std::int64_t>(Format->MinExponent); - const std::int64_t UnbiasedExponent = - Exponent == 0 ? Format->MinExponent : static_cast<std::int64_t>(Exponent) - Bias; - Result.BinaryExponent = static_cast<std::int32_t>(UnbiasedExponent - (Format->Precision - 1U)); - Result.Category = PrintedFloatCategory::Finite; - return Result; -} - -[[nodiscard]] std::string printNaNPayload(BigUnsigned Payload, std::uint32_t QuietNaNBit) { - Payload.clearBit(QuietNaNBit); - const auto Width = static_cast<std::uint32_t>(std::max<std::size_t>(Payload.bitWidth(), 1U)); - std::array<std::uint64_t, 2> Words{Payload.word(0), Payload.word(1)}; - std::string Digits = hexadecimalWords(Words, Width); - const std::size_t FirstNonzero = Digits.find_first_not_of('0'); - if (FirstNonzero == std::string::npos) - return "0x0"; - Digits.erase(0, FirstNonzero); - return "0x" + Digits; -} - -[[nodiscard]] std::string formatFiniteFloat(PrintedBinaryFloat Value) { - std::string Result; - if (Value.Negative) - Result.push_back('-'); - if (Value.Category == PrintedFloatCategory::Zero) { - Result.append("0.000000e+00"); - return Result; - } - - const std::size_t TrailingZeros = Value.Significand.countrZero(); - Value.Significand.shiftRight(TrailingZeros); - Value.BinaryExponent += static_cast<std::int32_t>(TrailingZeros); - - std::int32_t DecimalExponent = 0; - if (Value.BinaryExponent >= 0) { - Value.Significand = - Value.Significand.shiftedLeft(static_cast<std::size_t>(Value.BinaryExponent)); - } else { - DecimalExponent = Value.BinaryExponent; - for (std::int32_t Power = 0; Power < -Value.BinaryExponent; ++Power) - Value.Significand.multiplySmall(5); - } - - std::string Digits = Value.Significand.toDecimal(); - while (Digits.size() > 1U && Digits.back() == '0') { - Digits.pop_back(); - ++DecimalExponent; - } - - constexpr std::size_t FormatPrecision = 6; - if (Digits.size() > FormatPrecision) { - std::size_t Removed = Digits.size() - FormatPrecision; - const bool RoundUp = Digits[FormatPrecision] >= '5'; - Digits.resize(FormatPrecision); - if (RoundUp) { - std::size_t Position = Digits.size(); - while (Position != 0 && Digits[Position - 1U] == '9') { - Digits[Position - 1U] = '0'; - --Position; - } - if (Position == 0) - Digits.insert(Digits.begin(), '1'); - else - ++Digits[Position - 1U]; - } - while (Digits.size() > 1U && Digits.back() == '0') { - Digits.pop_back(); - ++Removed; - } - DecimalExponent += static_cast<std::int32_t>(Removed); - } - - DecimalExponent += static_cast<std::int32_t>(Digits.size() - 1U); - Result.push_back(Digits.front()); - Result.push_back('.'); - Result.append(Digits.substr(1)); - Result.append(7U - Digits.size(), '0'); - Result.push_back('e'); - Result.push_back(DecimalExponent < 0 ? '-' : '+'); - const std::uint32_t Magnitude = static_cast<std::uint32_t>( - DecimalExponent < 0 ? -static_cast<std::int64_t>(DecimalExponent) : DecimalExponent); - std::string Exponent = std::to_string(Magnitude); - if (Exponent.size() < 2U) - Result.push_back('0'); - Result.append(Exponent); - return Result; -} - -[[nodiscard]] std::string exactFloatingText(const FloatingPointBits &Bits) { - return "f0x" + hexadecimalWords(Bits.words(), Bits.bitWidth()); -} - -[[nodiscard]] std::string canonicalFloatingText(const FloatingPointBits &Bits) { - auto Decoded = decodePrintedFloat(Bits); - if (!Decoded) - return exactFloatingText(Bits); - - std::string Candidate; - switch (Decoded->Category) { - case PrintedFloatCategory::Zero: - case PrintedFloatCategory::Finite: - Candidate = formatFiniteFloat(std::move(*Decoded)); - break; - case PrintedFloatCategory::Infinity: - Candidate = Decoded->Negative ? "-inf" : "+inf"; - break; - case PrintedFloatCategory::QuietNaN: { - BigUnsigned Payload = Decoded->NaNPayload; - Payload.clearBit(Decoded->QuietNaNBit); - if (Payload.isZero()) { - Candidate = Decoded->Negative ? "-qnan" : "+qnan"; - break; - } - Candidate = Decoded->Negative ? "-nan(" : "+nan("; - Candidate.append(printNaNPayload(Decoded->NaNPayload, Decoded->QuietNaNBit)); - Candidate.push_back(')'); - break; - } - case PrintedFloatCategory::SignalingNaN: - Candidate = Decoded->Negative ? "-snan(" : "+snan("; - Candidate.append(printNaNPayload(Decoded->NaNPayload, Decoded->QuietNaNBit)); - Candidate.push_back(')'); - break; - } - - auto Type = GenericConstantType::floating(Bits.semantics()); - if (!Type) - return exactFloatingText(Bits); - auto TypeText = printType(*Type); - if (!TypeText) - return exactFloatingText(Bits); - auto Parsed = parseLLVMConstant(*TypeText + " " + Candidate); - if (!Parsed || !Parsed->FloatingValue || *Parsed->FloatingValue != Bits) - return exactFloatingText(Bits); - return Candidate; -} - [[nodiscard]] std::string_view expressionOpcodeName(GenericConstantExpressionOpcode Opcode) noexcept { switch (Opcode) { @@ -2731,7 +1203,7 @@ Expected<std::string> printValue(const GenericConstantData &Value, bool IncludeT if (!Value.FloatingValue) return makeUnexpected(ErrorCode::VerificationError, "floating constant is missing its floating payload"); - Result.append(canonicalFloatingText(*Value.FloatingValue)); + Result.append(Value.FloatingValue->toString()); return Result; case GenericConstantKind::NullPointer: Result.append("null"); diff --git a/backend/lib/Support/APFloat.cpp b/backend/lib/Support/APFloat.cpp new file mode 100644 index 0000000..c7b1503 --- /dev/null +++ b/backend/lib/Support/APFloat.cpp @@ -0,0 +1,1336 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "rcc/ADT/APFloat.h" + +#include "rcc/ADT/APInt.h" + +#include <algorithm> +#include <array> +#include <charconv> +#include <cstddef> +#include <cstdint> +#include <limits> +#include <optional> +#include <ranges> +#include <span> +#include <string> +#include <string_view> +#include <utility> +#include <vector> + +namespace rcc { +namespace { + +struct FloatSemanticsInfo final { + std::uint32_t BitWidth; + std::string_view Spelling; +}; + +constexpr std::array FloatSemanticsTable{ + FloatSemanticsInfo{16, "f16"}, FloatSemanticsInfo{16, "bf16"}, + FloatSemanticsInfo{32, "f32"}, FloatSemanticsInfo{64, "f64"}, + FloatSemanticsInfo{128, "f128"}, FloatSemanticsInfo{128, "ppcf128"}, + FloatSemanticsInfo{8, "f8e5m2"}, FloatSemanticsInfo{8, "f8e5m2fnuz"}, + FloatSemanticsInfo{8, "f8e4m3"}, FloatSemanticsInfo{8, "f8e4m3fn"}, + FloatSemanticsInfo{8, "f8e4m3fnuz"}, FloatSemanticsInfo{8, "f8e4m3b11fnuz"}, + FloatSemanticsInfo{8, "f8e3m4"}, FloatSemanticsInfo{19, "tf32"}, + FloatSemanticsInfo{8, "f8e8m0fnu"}, FloatSemanticsInfo{6, "f6e3m2fn"}, + FloatSemanticsInfo{6, "f6e2m3fn"}, FloatSemanticsInfo{4, "f4e2m1fn"}, + FloatSemanticsInfo{80, "x86fp80"}, +}; + +static_assert(FloatSemanticsTable.size() == static_cast<std::size_t>(FloatSemantics::Count)); + +[[nodiscard]] const FloatSemanticsInfo *getFloatSemanticsInfo(FloatSemantics Semantics) noexcept { + const auto Index = static_cast<std::size_t>(Semantics); + if (Index >= FloatSemanticsTable.size()) + return nullptr; + return &FloatSemanticsTable[Index]; +} + +[[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<unsigned>(Character - '0'); + if (Character >= 'a' && Character <= 'f') + return static_cast<unsigned>(Character - 'a') + 10U; + return static_cast<unsigned>(Character - 'A') + 10U; +} + +[[nodiscard]] bool parseHexWords(std::string_view Digits, std::uint32_t BitWidth, + std::vector<std::uint64_t> &Words) { + if (Digits.empty() || Digits.size() > (static_cast<std::size_t>(BitWidth) + 3U) / 4U) + return false; + Words.assign((BitWidth + 63U) / 64U, 0); + std::size_t Nibble = 0; + for (auto Iterator = Digits.rbegin(); Iterator != Digits.rend(); ++Iterator, ++Nibble) { + if (!isHexDigit(*Iterator)) + return false; + Words[Nibble / 16U] |= static_cast<std::uint64_t>(hexDigitValue(*Iterator)) + << ((Nibble % 16U) * 4U); + } + return (BitWidth % 64U) == 0 || (Words.back() >> (BitWidth % 64U)) == 0; +} + +[[nodiscard]] bool parseWideFloatHexWords(std::string_view Digits, + std::vector<std::uint64_t> &Words) { + if (Digits.empty() || Digits.size() > 32U || !std::ranges::all_of(Digits, isHexDigit)) + return false; + Words.assign(2, 0); + const auto ParseWord = [](std::string_view Text, std::uint64_t &Word) { + const auto Parsed = std::from_chars(Text.data(), Text.data() + Text.size(), Word, 16); + return Parsed.ec == std::errc{} && Parsed.ptr == Text.data() + Text.size(); + }; + const std::size_t FirstWordLength = std::min<std::size_t>(Digits.size(), 16U); + if (!ParseWord(Digits.substr(0, FirstWordLength), Words[0])) + return false; + if (Digits.size() == FirstWordLength) + return true; + return ParseWord(Digits.substr(FirstWordLength), Words[1]); +} + +enum class SpecialFloatLiteralKind : std::uint8_t { + Infinity, + QuietNaN, + SignalingNaN, +}; + +struct SpecialFloatLiteral final { + SpecialFloatLiteralKind Kind = SpecialFloatLiteralKind::Infinity; + bool Negative = false; + std::array<std::uint64_t, 2> Payload{}; +}; + +[[nodiscard]] Expected<std::optional<SpecialFloatLiteral>> +parseSpecialFloatLiteral(std::string_view Token) { + if (!Token.starts_with('+') && !Token.starts_with('-')) + return std::optional<SpecialFloatLiteral>{}; + + SpecialFloatLiteral Result; + Result.Negative = Token.front() == '-'; + Token.remove_prefix(1); + if (Token == "inf") + return Result; + if (Token == "qnan") { + Result.Kind = SpecialFloatLiteralKind::QuietNaN; + return Result; + } + + if (!Token.starts_with("nan") && !Token.starts_with("snan")) + return std::optional<SpecialFloatLiteral>{}; + + const bool IsSignaling = Token.starts_with("snan"); + const std::size_t NameLength = IsSignaling ? 4U : 3U; + const std::string_view Suffix = Token.substr(NameLength); + Result.Kind = + IsSignaling ? SpecialFloatLiteralKind::SignalingNaN : SpecialFloatLiteralKind::QuietNaN; + if (Suffix.size() < 5 || Suffix.front() != '(' || Suffix.back() != ')' || + !Suffix.substr(1).starts_with("0x")) { + return makeUnexpected(ErrorCode::ParseError, "invalid floating-point NaN payload"); + } + + const std::string_view Digits = Suffix.substr(3, Suffix.size() - 4U); + if (Digits.empty() || !std::ranges::all_of(Digits, isHexDigit)) { + return makeUnexpected(ErrorCode::ParseError, "invalid floating-point NaN payload"); + } + std::size_t Nibble = 0; + for (auto Iterator = Digits.rbegin(); Iterator != Digits.rend(); ++Iterator, ++Nibble) { + if (Nibble >= 32U) + break; + Result.Payload[Nibble / 16U] |= static_cast<std::uint64_t>(hexDigitValue(*Iterator)) + << ((Nibble % 16U) * 4U); + } + return Result; +} + +void setFloatBit(std::vector<std::uint64_t> &Words, std::uint32_t Bit) { + Words[Bit / 64U] |= std::uint64_t{1} << (Bit % 64U); +} + +void clearFloatBit(std::vector<std::uint64_t> &Words, std::uint32_t Bit) { + Words[Bit / 64U] &= ~(std::uint64_t{1} << (Bit % 64U)); +} + +[[nodiscard]] bool hasFloatBit(std::span<const std::uint64_t> Words, std::uint32_t Bit) noexcept { + return (Words[Bit / 64U] & (std::uint64_t{1} << (Bit % 64U))) != 0; +} + +void copyNaNPayload(std::vector<std::uint64_t> &Words, const std::array<std::uint64_t, 2> &Payload, + std::uint32_t PayloadBitWidth) { + for (std::uint32_t Bit = 0; Bit < PayloadBitWidth; ++Bit) { + if ((Payload[Bit / 64U] & (std::uint64_t{1} << (Bit % 64U))) != 0) + setFloatBit(Words, Bit); + } +} + +void makeBinarySpecialFloat(std::vector<std::uint64_t> &Words, std::uint32_t BitWidth, + std::uint32_t ExponentBitWidth, const SpecialFloatLiteral &Literal) { + const std::uint32_t FractionBitWidth = BitWidth - ExponentBitWidth - 1U; + if (Literal.Negative) + setFloatBit(Words, BitWidth - 1U); + for (std::uint32_t Bit = FractionBitWidth; Bit < FractionBitWidth + ExponentBitWidth; ++Bit) { + setFloatBit(Words, Bit); + } + if (Literal.Kind == SpecialFloatLiteralKind::Infinity) + return; + + copyNaNPayload(Words, Literal.Payload, FractionBitWidth); + const std::uint32_t QuietNaNBit = FractionBitWidth - 1U; + if (Literal.Kind == SpecialFloatLiteralKind::QuietNaN) { + setFloatBit(Words, QuietNaNBit); + return; + } + clearFloatBit(Words, QuietNaNBit); + bool HasPayload = false; + for (std::uint32_t Bit = 0; Bit < FractionBitWidth; ++Bit) + HasPayload = HasPayload || hasFloatBit(Words, Bit); + if (!HasPayload) + setFloatBit(Words, QuietNaNBit - 1U); +} + +[[nodiscard]] Expected<std::vector<std::uint64_t>> +makeSpecialFloatWords(FloatSemantics Semantics, const SpecialFloatLiteral &Literal) { + const auto BitWidth = getFloatSemanticsBitWidth(Semantics); + if (!BitWidth) + return makeUnexpected(ErrorCode::InvalidArgument, "floating-point semantics is unknown"); + std::vector<std::uint64_t> Words((*BitWidth + 63U) / 64U, 0); + switch (Semantics) { + case FloatSemantics::IEEEHalf: + makeBinarySpecialFloat(Words, *BitWidth, 5, Literal); + return Words; + case FloatSemantics::BFloat: + case FloatSemantics::IEEESingle: + makeBinarySpecialFloat(Words, *BitWidth, 8, Literal); + return Words; + case FloatSemantics::IEEEDouble: + makeBinarySpecialFloat(Words, *BitWidth, 11, Literal); + return Words; + case FloatSemantics::IEEEQuad: + makeBinarySpecialFloat(Words, *BitWidth, 15, Literal); + return Words; + case FloatSemantics::X87DoubleExtended: { + if (Literal.Negative) + setFloatBit(Words, 79); + for (std::uint32_t Bit = 64; Bit < 79; ++Bit) + setFloatBit(Words, Bit); + if (Literal.Kind == SpecialFloatLiteralKind::Infinity) { + setFloatBit(Words, 63); + return Words; + } + copyNaNPayload(Words, Literal.Payload, 63); + setFloatBit(Words, 63); + if (Literal.Kind == SpecialFloatLiteralKind::QuietNaN) { + setFloatBit(Words, 62); + return Words; + } + clearFloatBit(Words, 62); + bool HasPayload = false; + for (std::uint32_t Bit = 0; Bit < 63; ++Bit) + HasPayload = HasPayload || hasFloatBit(Words, Bit); + if (!HasPayload) + setFloatBit(Words, 61); + return Words; + } + case FloatSemantics::PPCDoubleDouble: { + std::vector<std::uint64_t> HighPart(1, 0); + makeBinarySpecialFloat(HighPart, 64, 11, Literal); + Words[0] = HighPart.front(); + return Words; + } + case FloatSemantics::Float8E5M2: + case FloatSemantics::Float8E5M2FNUZ: + case FloatSemantics::Float8E4M3: + case FloatSemantics::Float8E4M3FN: + case FloatSemantics::Float8E4M3FNUZ: + case FloatSemantics::Float8E4M3B11FNUZ: + case FloatSemantics::Float8E3M4: + case FloatSemantics::FloatTF32: + case FloatSemantics::Float8E8M0FNU: + case FloatSemantics::Float6E3M2FN: + case FloatSemantics::Float6E2M3FN: + case FloatSemantics::Float4E2M1FN: + case FloatSemantics::Count: + break; + } + return makeUnexpected(ErrorCode::Unsupported, + "floating-point special literal uses unsupported semantics"); +} + +struct BinaryFloatFormat final { + std::uint32_t Precision = 0; + std::int32_t MinExponent = 0; + std::int32_t MaxExponent = 0; + std::uint32_t ExponentBitWidth = 0; + std::uint32_t StorageBitWidth = 0; + bool HasExplicitIntegerBit = false; +}; + +[[nodiscard]] std::optional<BinaryFloatFormat> +binaryFloatFormat(FloatSemantics Semantics) noexcept { + switch (Semantics) { + case FloatSemantics::IEEEHalf: + return BinaryFloatFormat{.Precision = 11, + .MinExponent = -14, + .MaxExponent = 15, + .ExponentBitWidth = 5, + .StorageBitWidth = 16}; + case FloatSemantics::BFloat: + return BinaryFloatFormat{.Precision = 8, + .MinExponent = -126, + .MaxExponent = 127, + .ExponentBitWidth = 8, + .StorageBitWidth = 16}; + case FloatSemantics::IEEESingle: + return BinaryFloatFormat{.Precision = 24, + .MinExponent = -126, + .MaxExponent = 127, + .ExponentBitWidth = 8, + .StorageBitWidth = 32}; + case FloatSemantics::IEEEDouble: + return BinaryFloatFormat{.Precision = 53, + .MinExponent = -1022, + .MaxExponent = 1023, + .ExponentBitWidth = 11, + .StorageBitWidth = 64}; + case FloatSemantics::IEEEQuad: + return BinaryFloatFormat{.Precision = 113, + .MinExponent = -16382, + .MaxExponent = 16383, + .ExponentBitWidth = 15, + .StorageBitWidth = 128}; + case FloatSemantics::X87DoubleExtended: + return BinaryFloatFormat{.Precision = 64, + .MinExponent = -16382, + .MaxExponent = 16383, + .ExponentBitWidth = 15, + .StorageBitWidth = 80, + .HasExplicitIntegerBit = true}; + case FloatSemantics::PPCDoubleDouble: + // Decimal PPC double-double literals use 106 bits of precision and are + // materialized as a canonical pair of doubles. + return BinaryFloatFormat{.Precision = 106, + .MinExponent = -969, + .MaxExponent = 1023, + .ExponentBitWidth = 0, + .StorageBitWidth = 128}; + case FloatSemantics::Float8E5M2: + case FloatSemantics::Float8E5M2FNUZ: + case FloatSemantics::Float8E4M3: + case FloatSemantics::Float8E4M3FN: + case FloatSemantics::Float8E4M3FNUZ: + case FloatSemantics::Float8E4M3B11FNUZ: + case FloatSemantics::Float8E3M4: + case FloatSemantics::FloatTF32: + case FloatSemantics::Float8E8M0FNU: + case FloatSemantics::Float6E3M2FN: + case FloatSemantics::Float6E2M3FN: + case FloatSemantics::Float4E2M1FN: + case FloatSemantics::Count: + return std::nullopt; + } + return std::nullopt; +} + +struct RoundedBinaryFloat final { + bool IsZero = false; + bool IsSubnormal = false; + std::int64_t Exponent = 0; + std::int64_t Scale = 0; + APInt Significand; +}; + +[[nodiscard]] RoundedBinaryFloat makeZeroRoundedBinaryFloat() { + RoundedBinaryFloat Result; + Result.IsZero = true; + return Result; +} + +[[nodiscard]] bool checkedAddExponent(std::int64_t Left, std::int64_t Right, + std::int64_t &Result) noexcept { + if (Right > 0 && Left > std::numeric_limits<std::int64_t>::max() - Right) + return false; + if (Right < 0 && Left < std::numeric_limits<std::int64_t>::min() - Right) + return false; + Result = Left + Right; + return true; +} + +[[nodiscard]] bool checkedSubtractExponent(std::int64_t Left, std::int64_t Right, + std::int64_t &Result) noexcept { + if (Right == std::numeric_limits<std::int64_t>::min()) { + if (Left >= 0) + return false; + Result = Left - Right; + return true; + } + return checkedAddExponent(Left, -Right, Result); +} + +[[nodiscard]] Expected<APInt> divideAndRoundNearest(APInt Numerator, const APInt &Denominator) { + if (Denominator.isZero()) + return makeUnexpected(ErrorCode::VerificationError, + "floating-point conversion has a zero denominator"); + + APInt Quotient; + const std::size_t NumeratorBits = Numerator.activeBitWidth(); + const std::size_t DenominatorBits = Denominator.activeBitWidth(); + if (NumeratorBits >= DenominatorBits) { + for (std::size_t Shift = NumeratorBits - DenominatorBits + 1U; Shift != 0; --Shift) { + const std::size_t ActualShift = Shift - 1U; + if (Numerator.compareToShifted(Denominator, ActualShift) < 0) + continue; + Numerator.subtract(Denominator.shiftedLeft(ActualShift)); + Quotient.setBit(ActualShift); + } + } + + const int HalfComparison = Numerator.shiftedLeft(1).compare(Denominator); + if (HalfComparison > 0 || (HalfComparison == 0 && (Quotient.word(0) & 1U) != 0)) + Quotient.addSmall(1); + return Quotient; +} + +[[nodiscard]] Expected<APInt> roundScaledRational(APInt Numerator, APInt Denominator, + std::int64_t Shift) { + const auto AbsoluteShift = [Shift]() -> std::uint64_t { + if (Shift >= 0) + return static_cast<std::uint64_t>(Shift); + return static_cast<std::uint64_t>(-(Shift + 1)) + 1U; + }(); + if (AbsoluteShift > std::numeric_limits<std::size_t>::max()) { + return makeUnexpected(ErrorCode::ParseError, + "floating-point literal has an unrepresentable binary exponent"); + } + if (Shift >= 0) + Numerator = Numerator.shiftedLeft(static_cast<std::size_t>(AbsoluteShift)); + else + Denominator = Denominator.shiftedLeft(static_cast<std::size_t>(AbsoluteShift)); + return divideAndRoundNearest(std::move(Numerator), Denominator); +} + +[[nodiscard]] Expected<std::int64_t> +floorLog2OfRational(const APInt &Numerator, const APInt &Denominator, std::int64_t BinaryScale) { + if (Numerator.isZero() || Denominator.isZero()) { + return makeUnexpected(ErrorCode::VerificationError, + "floating-point conversion requires a nonzero rational value"); + } + const std::size_t NumeratorBits = Numerator.activeBitWidth(); + const std::size_t DenominatorBits = Denominator.activeBitWidth(); + if (NumeratorBits > static_cast<std::size_t>(std::numeric_limits<std::int64_t>::max()) || + DenominatorBits > static_cast<std::size_t>(std::numeric_limits<std::int64_t>::max())) { + return makeUnexpected(ErrorCode::Unsupported, + "floating-point literal significand is too large"); + } + const std::int64_t BitDelta = + static_cast<std::int64_t>(NumeratorBits) - static_cast<std::int64_t>(DenominatorBits); + std::int64_t Candidate = 0; + if (!checkedAddExponent(BinaryScale, BitDelta, Candidate)) { + return makeUnexpected(BinaryScale >= 0 ? ErrorCode::ParseError : ErrorCode::Unsupported, + BinaryScale >= 0 ? "floating-point constant overflowed type" + : "floating-point constant underflowed type"); + } + + const std::int64_t RelativeShift = -BitDelta; + int Comparison = 0; + if (RelativeShift >= 0) { + if (static_cast<std::uint64_t>(RelativeShift) > std::numeric_limits<std::size_t>::max()) { + Comparison = -1; + } else { + Comparison = + -Denominator.compareToShifted(Numerator, static_cast<std::size_t>(RelativeShift)); + } + } else { + const std::uint64_t AbsoluteShift = static_cast<std::uint64_t>(-(RelativeShift + 1)) + 1U; + if (AbsoluteShift > std::numeric_limits<std::size_t>::max()) + Comparison = 1; + else + Comparison = Numerator.compareToShifted(Denominator, static_cast<std::size_t>(AbsoluteShift)); + } + if (Comparison >= 0) + return Candidate; + if (Candidate == std::numeric_limits<std::int64_t>::min()) { + return makeUnexpected(ErrorCode::ParseError, "floating-point constant underflowed type"); + } + return Candidate - 1; +} + +[[nodiscard]] Expected<RoundedBinaryFloat> roundBinaryRational(const BinaryFloatFormat &Format, + const APInt &Numerator, + const APInt &Denominator, + std::int64_t BinaryScale, + bool PermitZeroResult = false) { + if (Numerator.isZero()) + return makeZeroRoundedBinaryFloat(); + + auto FloorExponent = floorLog2OfRational(Numerator, Denominator, BinaryScale); + if (!FloorExponent) + return std::unexpected(FloorExponent.error()); + if (*FloorExponent > Format.MaxExponent) { + return makeUnexpected(ErrorCode::ParseError, "floating-point constant overflowed type"); + } + + const std::int64_t SmallestUnit = + static_cast<std::int64_t>(Format.MinExponent) - (Format.Precision - 1U); + if (*FloorExponent < SmallestUnit - 1) { + if (PermitZeroResult) + return makeZeroRoundedBinaryFloat(); + return makeUnexpected(ErrorCode::ParseError, "floating-point constant underflowed type"); + } + + const bool StartsNormal = *FloorExponent >= Format.MinExponent; + const std::int64_t Unit = StartsNormal ? *FloorExponent - (Format.Precision - 1U) : SmallestUnit; + std::int64_t Shift = 0; + if (!checkedSubtractExponent(BinaryScale, Unit, Shift)) { + return makeUnexpected(BinaryScale >= 0 ? ErrorCode::ParseError : ErrorCode::Unsupported, + BinaryScale >= 0 ? "floating-point constant overflowed type" + : "floating-point constant underflowed type"); + } + auto Significand = roundScaledRational(Numerator, Denominator, Shift); + if (!Significand) + return std::unexpected(Significand.error()); + if (Significand->isZero()) { + if (PermitZeroResult) + return makeZeroRoundedBinaryFloat(); + return makeUnexpected(ErrorCode::ParseError, "floating-point constant underflowed type"); + } + + RoundedBinaryFloat Result; + Result.Significand = std::move(*Significand); + if (StartsNormal) { + Result.Exponent = *FloorExponent; + if (Result.Significand.activeBitWidth() > Format.Precision) { + Result.Significand.shiftRight(1); + if (Result.Exponent == Format.MaxExponent) { + return makeUnexpected(ErrorCode::ParseError, "floating-point constant overflowed type"); + } + ++Result.Exponent; + } + Result.Scale = Result.Exponent - (Format.Precision - 1U); + return Result; + } + + if (Result.Significand.activeBitWidth() >= Format.Precision) { + Result.IsSubnormal = false; + Result.Exponent = Format.MinExponent; + Result.Scale = Result.Exponent - (Format.Precision - 1U); + return Result; + } + Result.IsSubnormal = true; + Result.Exponent = Format.MinExponent; + Result.Scale = SmallestUnit; + return Result; +} + +void appendSignificandWords(std::vector<std::uint64_t> &Words, const APInt &Significand) { + for (std::size_t Index = 0; Index < Words.size(); ++Index) + Words[Index] = Significand.word(Index); +} + +[[nodiscard]] Expected<std::vector<std::uint64_t>> +packIEEEBinaryFloat(const BinaryFloatFormat &Format, const RoundedBinaryFloat &Value, + bool Negative) { + if (Format.HasExplicitIntegerBit || Format.ExponentBitWidth == 0 || Format.StorageBitWidth == 0) { + return makeUnexpected(ErrorCode::VerificationError, + "floating-point format does not use IEEE binary storage"); + } + std::vector<std::uint64_t> Words((Format.StorageBitWidth + 63U) / 64U, 0); + if (Value.IsZero) { + if (Negative) + setFloatBit(Words, Format.StorageBitWidth - 1U); + return Words; + } + + appendSignificandWords(Words, Value.Significand); + const std::uint32_t FractionBitWidth = Format.Precision - 1U; + if (!Value.IsSubnormal) + clearFloatBit(Words, FractionBitWidth); + const std::int64_t Bias = 1 - static_cast<std::int64_t>(Format.MinExponent); + const std::uint64_t Exponent = + Value.IsSubnormal ? 0 : static_cast<std::uint64_t>(Value.Exponent + Bias); + for (std::uint32_t Bit = 0; Bit < Format.ExponentBitWidth; ++Bit) { + if ((Exponent & (std::uint64_t{1} << Bit)) != 0) + setFloatBit(Words, FractionBitWidth + Bit); + } + if (Negative) + setFloatBit(Words, Format.StorageBitWidth - 1U); + return Words; +} + +[[nodiscard]] Expected<std::vector<std::uint64_t>> +packX87Float(const BinaryFloatFormat &Format, const RoundedBinaryFloat &Value, bool Negative) { + if (!Format.HasExplicitIntegerBit || Format.StorageBitWidth != 80 || Format.Precision != 64) { + return makeUnexpected(ErrorCode::VerificationError, "x87 floating-point format is invalid"); + } + std::vector<std::uint64_t> Words(2, 0); + if (Value.IsZero) { + if (Negative) + setFloatBit(Words, 79); + return Words; + } + Words[0] = Value.Significand.word(0); + if (!Value.IsSubnormal) { + const std::int64_t Bias = 1 - static_cast<std::int64_t>(Format.MinExponent); + Words[1] = static_cast<std::uint64_t>(Value.Exponent + Bias); + } + if (Negative) + setFloatBit(Words, 79); + return Words; +} + +[[nodiscard]] Expected<std::vector<std::uint64_t>> +packPPCDoubleDouble(const RoundedBinaryFloat &Value, bool Negative) { + const auto DoubleFormat = binaryFloatFormat(FloatSemantics::IEEEDouble); + if (!DoubleFormat) + return makeUnexpected(ErrorCode::VerificationError, "IEEE double format is unavailable"); + if (Value.IsZero) { + RoundedBinaryFloat Zero = makeZeroRoundedBinaryFloat(); + auto High = packIEEEBinaryFloat(*DoubleFormat, Zero, Negative); + if (!High) + return std::unexpected(High.error()); + return std::vector<std::uint64_t>{High->front(), 0}; + } + + APInt One(1); + auto High = roundBinaryRational(*DoubleFormat, Value.Significand, One, Value.Scale, true); + if (!High) + return std::unexpected(High.error()); + auto HighWords = packIEEEBinaryFloat(*DoubleFormat, *High, Negative); + if (!HighWords) + return std::unexpected(HighWords.error()); + + APInt SourceMagnitude = Value.Significand; + APInt HighMagnitude = High->Significand; + const std::int64_t CommonScale = std::min(Value.Scale, High->Scale); + if (!SourceMagnitude.isZero()) { + const std::int64_t SourceShift = Value.Scale - CommonScale; + if (SourceShift > 0) + SourceMagnitude = SourceMagnitude.shiftedLeft(static_cast<std::size_t>(SourceShift)); + } + if (!HighMagnitude.isZero()) { + const std::int64_t HighShift = High->Scale - CommonScale; + if (HighShift > 0) + HighMagnitude = HighMagnitude.shiftedLeft(static_cast<std::size_t>(HighShift)); + } + + bool LowNegative = false; + APInt LowMagnitude; + if (SourceMagnitude.compare(HighMagnitude) >= 0) { + LowMagnitude = std::move(SourceMagnitude); + LowMagnitude.subtract(HighMagnitude); + LowNegative = Negative; + } else { + LowMagnitude = std::move(HighMagnitude); + LowMagnitude.subtract(SourceMagnitude); + LowNegative = !Negative; + } + + auto Low = roundBinaryRational(*DoubleFormat, LowMagnitude, One, CommonScale, true); + if (!Low) + return std::unexpected(Low.error()); + auto LowWords = packIEEEBinaryFloat(*DoubleFormat, *Low, LowNegative); + if (!LowWords) + return std::unexpected(LowWords.error()); + return std::vector<std::uint64_t>{HighWords->front(), LowWords->front()}; +} + +[[nodiscard]] Expected<std::vector<std::uint64_t>> +convertRationalToFloatWords(FloatSemantics Semantics, bool Negative, const APInt &Numerator, + const APInt &Denominator, std::int64_t BinaryScale) { + const auto Format = binaryFloatFormat(Semantics); + if (!Format) { + return makeUnexpected(ErrorCode::Unsupported, + "floating-point literal uses unsupported semantics"); + } + auto Rounded = roundBinaryRational(*Format, Numerator, Denominator, BinaryScale); + if (!Rounded) + return std::unexpected(Rounded.error()); + if (Semantics == FloatSemantics::PPCDoubleDouble) { + return packPPCDoubleDouble(*Rounded, Negative); + } + if (Format->HasExplicitIntegerBit) + return packX87Float(*Format, *Rounded, Negative); + return packIEEEBinaryFloat(*Format, *Rounded, Negative); +} + +[[nodiscard]] bool isDecimalDigit(char Character) noexcept { + return Character >= '0' && Character <= '9'; +} + +[[nodiscard]] Expected<std::int32_t> parseFloatingExponent(std::string_view Text, + std::int32_t Limit) { + if (Text.empty()) + return makeUnexpected(ErrorCode::ParseError, "floating-point exponent has no digits"); + bool Negative = false; + if (Text.front() == '+' || Text.front() == '-') { + Negative = Text.front() == '-'; + Text.remove_prefix(1); + } + if (Text.empty()) + return makeUnexpected(ErrorCode::ParseError, "floating-point exponent has no digits"); + + std::int32_t Value = 0; + for (const char Character : Text) { + if (!isDecimalDigit(Character)) { + return makeUnexpected(ErrorCode::ParseError, + "floating-point exponent contains a non-decimal digit"); + } + const std::int32_t Digit = Character - '0'; + if (Value >= Limit || Value > (Limit - Digit) / 10) { + Value = Limit; + continue; + } + Value = Value * 10 + Digit; + } + return Negative ? -Value : Value; +} + +[[nodiscard]] APInt parseDecimalSignificand(std::string_view Digits) { + APInt Result; + std::size_t Position = 0; + std::size_t FirstChunkLength = Digits.size() % 9U; + if (FirstChunkLength == 0) + FirstChunkLength = std::min<std::size_t>(Digits.size(), 9U); + while (Position < Digits.size()) { + const std::size_t Length = Position == 0 ? FirstChunkLength : 9U; + std::uint32_t Chunk = 0; + for (std::size_t Index = 0; Index < Length; ++Index) + Chunk = Chunk * 10U + static_cast<std::uint32_t>(Digits[Position + Index] - '0'); + Result.multiplySmall(Position == 0 ? 1U : 1000000000U); + Result.addSmall(Chunk); + Position += Length; + } + return Result; +} + +[[nodiscard]] APInt parseHexSignificand(std::string_view Digits) { + APInt Result; + for (const char Character : Digits) { + Result.multiplySmall(16U); + Result.addSmall(hexDigitValue(Character)); + } + return Result; +} + +void multiplyByPowerOfFive(APInt &Value, std::uint64_t Exponent) { + for (std::uint64_t Index = 0; Index < Exponent; ++Index) + Value.multiplySmall(5U); +} + +[[nodiscard]] APInt powerOfFive(std::uint64_t Exponent) { + APInt Result(1); + multiplyByPowerOfFive(Result, Exponent); + return Result; +} + +[[nodiscard]] bool decimalDefinitelyOverflows(const BinaryFloatFormat &Format, + std::int64_t NormalizedExponent) noexcept { + const __int128 Left = (static_cast<__int128>(NormalizedExponent) - 1) * 42039; + const __int128 Right = static_cast<__int128>(12655) * Format.MaxExponent; + return Left >= Right; +} + +[[nodiscard]] bool decimalDefinitelyUnderflows(const BinaryFloatFormat &Format, + std::int64_t NormalizedExponent) noexcept { + const __int128 Left = (static_cast<__int128>(NormalizedExponent) + 1) * 28738; + const __int128 Right = static_cast<__int128>(8651) * + (static_cast<std::int64_t>(Format.MinExponent) - Format.Precision); + return Left <= Right; +} + +struct ParsedFiniteFloatLiteral final { + bool Negative = false; + APInt Numerator; + APInt Denominator{1}; + std::int64_t BinaryScale = 0; +}; + +[[nodiscard]] Expected<ParsedFiniteFloatLiteral> +parseDecimalFloatLiteral(std::string_view Token, const BinaryFloatFormat &Format) { + ParsedFiniteFloatLiteral Result; + if (Token.starts_with('+') || Token.starts_with('-')) { + Result.Negative = Token.front() == '-'; + Token.remove_prefix(1); + } + if (Token.empty()) + return makeUnexpected(ErrorCode::ParseError, "floating-point literal has no digits"); + + std::size_t Position = 0; + const std::size_t IntegerBegin = Position; + while (Position < Token.size() && isDecimalDigit(Token[Position])) + ++Position; + if (IntegerBegin == Position || Position == Token.size() || Token[Position] != '.') { + return makeUnexpected(ErrorCode::ParseError, + "decimal floating-point literal requires a decimal point"); + } + + std::string Digits(Token.substr(IntegerBegin, Position - IntegerBegin)); + ++Position; + const std::size_t FractionBegin = Position; + while (Position < Token.size() && isDecimalDigit(Token[Position])) + ++Position; + Digits.append(Token.substr(FractionBegin, Position - FractionBegin)); + const std::size_t FractionDigits = Position - FractionBegin; + + std::int32_t Exponent = 0; + if (Position < Token.size() && (Token[Position] == 'e' || Token[Position] == 'E')) { + auto ParsedExponent = parseFloatingExponent(Token.substr(Position + 1U), 24000); + if (!ParsedExponent) + return std::unexpected(ParsedExponent.error()); + Exponent = *ParsedExponent; + Position = Token.size(); + } + if (Position != Token.size()) { + return makeUnexpected(ErrorCode::ParseError, + "decimal floating-point literal has an invalid suffix"); + } + + const std::size_t FirstSignificant = Digits.find_first_not_of('0'); + if (FirstSignificant == std::string::npos) + return Result; + const std::size_t LastSignificant = Digits.find_last_not_of('0'); + const std::string_view Significant(Digits.data() + FirstSignificant, + LastSignificant - FirstSignificant + 1U); + const std::size_t TrailingZeroCount = Digits.size() - LastSignificant - 1U; + std::int64_t DecimalScale = static_cast<std::int64_t>(Exponent) - + static_cast<std::int64_t>(FractionDigits) + + static_cast<std::int64_t>(TrailingZeroCount); + const std::int64_t NormalizedExponent = + DecimalScale + static_cast<std::int64_t>(Significant.size() - 1U); + if (decimalDefinitelyOverflows(Format, NormalizedExponent)) { + return makeUnexpected(ErrorCode::ParseError, "floating-point constant overflowed type"); + } + if (decimalDefinitelyUnderflows(Format, NormalizedExponent)) { + return makeUnexpected(ErrorCode::ParseError, "floating-point constant underflowed type"); + } + + Result.Numerator = parseDecimalSignificand(Significant); + Result.BinaryScale = DecimalScale; + if (DecimalScale >= 0) { + multiplyByPowerOfFive(Result.Numerator, static_cast<std::uint64_t>(DecimalScale)); + } else { + Result.Denominator = powerOfFive(static_cast<std::uint64_t>(-(DecimalScale + 1)) + 1U); + } + return Result; +} + +[[nodiscard]] Expected<ParsedFiniteFloatLiteral> parseHexFloatLiteral(std::string_view Token) { + ParsedFiniteFloatLiteral Result; + if (Token.starts_with('+') || Token.starts_with('-')) { + Result.Negative = Token.front() == '-'; + Token.remove_prefix(1); + } + if (!Token.starts_with("0x")) { + return makeUnexpected(ErrorCode::ParseError, + "hexadecimal floating-point literal must use the 0x prefix"); + } + std::size_t Position = 2; + const std::size_t IntegerBegin = Position; + while (Position < Token.size() && isHexDigit(Token[Position])) + ++Position; + if (IntegerBegin == Position || Position == Token.size() || Token[Position] != '.') { + return makeUnexpected(ErrorCode::ParseError, + "hexadecimal floating-point literal requires a hexadecimal point"); + } + std::string Digits(Token.substr(IntegerBegin, Position - IntegerBegin)); + ++Position; + const std::size_t FractionBegin = Position; + while (Position < Token.size() && isHexDigit(Token[Position])) + ++Position; + Digits.append(Token.substr(FractionBegin, Position - FractionBegin)); + const std::size_t FractionDigits = Position - FractionBegin; + if (Position == Token.size() || (Token[Position] != 'p' && Token[Position] != 'P')) { + return makeUnexpected(ErrorCode::ParseError, + "hexadecimal floating-point literal requires a binary exponent"); + } + auto Exponent = parseFloatingExponent(Token.substr(Position + 1U), 32767); + if (!Exponent) + return std::unexpected(Exponent.error()); + + const std::size_t FirstSignificant = Digits.find_first_not_of('0'); + if (FirstSignificant == std::string::npos) + return Result; + const std::string_view Significant(Digits.data() + FirstSignificant, + Digits.size() - FirstSignificant); + Result.Numerator = parseHexSignificand(Significant); + Result.BinaryScale = + static_cast<std::int64_t>(*Exponent) - static_cast<std::int64_t>(FractionDigits) * 4; + return Result; +} + +[[nodiscard]] Expected<std::uint64_t> parseRawDoubleBits(std::string_view Digits) { + if (Digits.empty()) + return makeUnexpected(ErrorCode::ParseError, + "hexadecimal floating-point literal has no digits"); + std::uint64_t Result = 0; + for (const char Character : Digits) { + if (!isHexDigit(Character)) { + return makeUnexpected(ErrorCode::ParseError, + "hexadecimal floating-point literal contains a non-hexadecimal digit"); + } + const std::uint64_t Digit = hexDigitValue(Character); + if (Result > (std::numeric_limits<std::uint64_t>::max() - Digit) / 16U) { + return makeUnexpected(ErrorCode::ParseError, + "hexadecimal floating-point literal does not fit IEEE double"); + } + Result = Result * 16U + Digit; + } + return Result; +} + +[[nodiscard]] int compareScaledBinaryIntegers(const APInt &Left, std::int64_t LeftScale, + const APInt &Right, std::int64_t RightScale) { + if (Left.isZero()) + return Right.isZero() ? 0 : -1; + if (Right.isZero()) + return 1; + if (LeftScale == RightScale) + return Left.compare(Right); + if (LeftScale > RightScale) + return -Right.compareToShifted(Left, static_cast<std::size_t>(LeftScale - RightScale)); + return Left.compareToShifted(Right, static_cast<std::size_t>(RightScale - LeftScale)); +} + +[[nodiscard]] Expected<std::vector<std::uint64_t>> +convertRawDoubleNaN(FloatSemantics Semantics, bool Negative, std::uint64_t Fraction) { + if (Semantics == FloatSemantics::IEEEDouble) { + return std::vector<std::uint64_t>{(Negative ? std::uint64_t{1} << 63U : 0) | + 0x7ff0000000000000ULL | Fraction}; + } + const auto Format = binaryFloatFormat(Semantics); + if (!Format || Format->HasExplicitIntegerBit || Format->StorageBitWidth > 64 || + (Semantics != FloatSemantics::IEEEHalf && Semantics != FloatSemantics::BFloat && + Semantics != FloatSemantics::IEEESingle)) { + return makeUnexpected(ErrorCode::ParseError, + "unprefixed hexadecimal literal is invalid for type"); + } + const std::uint32_t TargetFractionBits = Format->Precision - 1U; + const std::uint32_t DroppedBits = 53U - Format->Precision; + const std::uint64_t DroppedMask = (std::uint64_t{1} << DroppedBits) - 1U; + if ((Fraction & DroppedMask) != 0) { + return makeUnexpected( + ErrorCode::ParseError, + "hexadecimal floating-point literal is not exactly representable by its type"); + } + + const bool IsSignaling = (Fraction & (std::uint64_t{1} << 51U)) == 0; + std::uint64_t TargetFraction = Fraction >> DroppedBits; + const std::uint64_t QuietBit = std::uint64_t{1} << (TargetFractionBits - 1U); + if (IsSignaling) { + TargetFraction &= ~QuietBit; + if ((TargetFraction & (QuietBit - 1U)) == 0) + TargetFraction |= QuietBit >> 1U; + } else { + TargetFraction |= QuietBit; + } + const std::uint64_t Sign = Negative ? std::uint64_t{1} << (Format->StorageBitWidth - 1U) : 0; + const std::uint64_t Exponent = (std::uint64_t{1} << Format->ExponentBitWidth) - 1U; + return std::vector<std::uint64_t>{Sign | (Exponent << TargetFractionBits) | TargetFraction}; +} + +[[nodiscard]] Expected<std::vector<std::uint64_t>> convertRawDoubleLiteral(FloatSemantics Semantics, + std::uint64_t Bits) { + if (Semantics != FloatSemantics::IEEEHalf && Semantics != FloatSemantics::BFloat && + Semantics != FloatSemantics::IEEESingle && Semantics != FloatSemantics::IEEEDouble) { + return makeUnexpected(ErrorCode::ParseError, + "unprefixed hexadecimal literal is invalid for type"); + } + if (Semantics == FloatSemantics::IEEEDouble) + return std::vector<std::uint64_t>{Bits}; + + const bool Negative = (Bits >> 63U) != 0; + const std::uint64_t Exponent = (Bits >> 52U) & 0x7ffU; + const std::uint64_t Fraction = Bits & 0x000fffffffffffffULL; + if (Exponent == 0x7ffU) { + if (Fraction != 0) + return convertRawDoubleNaN(Semantics, Negative, Fraction); + SpecialFloatLiteral Infinity{.Kind = SpecialFloatLiteralKind::Infinity, .Negative = Negative}; + return makeSpecialFloatWords(Semantics, Infinity); + } + + const auto Format = binaryFloatFormat(Semantics); + if (!Format) + return makeUnexpected(ErrorCode::VerificationError, "IEEE target format is unavailable"); + if (Exponent == 0 && Fraction == 0) { + RoundedBinaryFloat Zero = makeZeroRoundedBinaryFloat(); + return packIEEEBinaryFloat(*Format, Zero, Negative); + } + + const bool IsSubnormal = Exponent == 0; + const std::uint64_t Significand = IsSubnormal ? Fraction : (std::uint64_t{1} << 52U) | Fraction; + const std::int64_t SourceScale = + (IsSubnormal ? -1022 : static_cast<std::int64_t>(Exponent) - 1023) - 52; + const APInt Source(Significand); + const APInt One(1); + auto Rounded = roundBinaryRational(*Format, Source, One, SourceScale); + if (!Rounded) + return std::unexpected(Rounded.error()); + if (compareScaledBinaryIntegers(Source, SourceScale, Rounded->Significand, Rounded->Scale) != 0) { + return makeUnexpected( + ErrorCode::ParseError, + "hexadecimal floating-point literal is not exactly representable by its type"); + } + return packIEEEBinaryFloat(*Format, *Rounded, Negative); +} + +enum class PrintedFloatCategory : std::uint8_t { + Zero, + Finite, + Infinity, + QuietNaN, + SignalingNaN, +}; + +struct PrintedBinaryFloat final { + PrintedFloatCategory Category = PrintedFloatCategory::Zero; + bool Negative = false; + APInt Significand; + std::int32_t BinaryExponent = 0; + APInt NaNPayload; + std::uint32_t QuietNaNBit = 0; +}; + +[[nodiscard]] std::string hexadecimalWords(std::span<const std::uint64_t> Words, + std::uint32_t BitWidth) { + constexpr std::string_view Digits = "0123456789ABCDEF"; + const std::size_t DigitCount = (static_cast<std::size_t>(BitWidth) + 3U) / 4U; + std::string Result(DigitCount, '0'); + for (std::size_t Index = 0; Index < DigitCount; ++Index) { + const std::size_t Bit = Index * 4U; + const std::uint64_t Nibble = (Words[Bit / 64U] >> (Bit % 64U)) & 0xFU; + Result[DigitCount - Index - 1U] = Digits[static_cast<std::size_t>(Nibble)]; + } + return Result; +} + +[[nodiscard]] std::uint64_t extractFloatField(std::span<const std::uint64_t> Words, + std::uint32_t Begin, std::uint32_t Width) noexcept { + std::uint64_t Result = 0; + for (std::uint32_t Bit = 0; Bit < Width; ++Bit) { + if (hasFloatBit(Words, Begin + Bit)) + Result |= std::uint64_t{1} << Bit; + } + return Result; +} + +[[nodiscard]] APInt extractFloatBits(std::span<const std::uint64_t> Words, std::uint32_t Begin, + std::uint32_t Width) { + APInt Result; + for (std::uint32_t Bit = 0; Bit < Width; ++Bit) { + if (hasFloatBit(Words, Begin + Bit)) + Result.setBit(Bit); + } + return Result; +} + +[[nodiscard]] std::optional<PrintedBinaryFloat> decodePrintedFloat(const APFloat &Bits) { + std::optional<BinaryFloatFormat> Format = binaryFloatFormat(Bits.semantics()); + std::span<const std::uint64_t> Words = Bits.words(); + if (!Format) + return std::nullopt; + + if (Bits.semantics() == FloatSemantics::PPCDoubleDouble) { + if (Words.size() != 2 || Words[1] != 0) + return std::nullopt; + Format = binaryFloatFormat(FloatSemantics::IEEEDouble); + Words = Words.first(1); + } + if (!Format || Format->ExponentBitWidth == 0) + return std::nullopt; + + PrintedBinaryFloat Result; + Result.Negative = hasFloatBit(Words, Format->StorageBitWidth - 1U); + const std::uint32_t StoredSignificandWidth = + Format->HasExplicitIntegerBit ? Format->Precision : Format->Precision - 1U; + const std::uint64_t Exponent = + extractFloatField(Words, StoredSignificandWidth, Format->ExponentBitWidth); + const std::uint64_t MaximumExponent = (std::uint64_t{1} << Format->ExponentBitWidth) - 1U; + + if (Exponent == MaximumExponent) { + const std::uint32_t PayloadWidth = Format->Precision - 1U; + Result.NaNPayload = extractFloatBits(Words, 0, PayloadWidth); + if (Result.NaNPayload.isZero()) { + Result.Category = PrintedFloatCategory::Infinity; + return Result; + } + Result.QuietNaNBit = PayloadWidth - 1U; + Result.Category = hasFloatBit(Words, Result.QuietNaNBit) ? PrintedFloatCategory::QuietNaN + : PrintedFloatCategory::SignalingNaN; + return Result; + } + + Result.Significand = extractFloatBits(Words, 0, StoredSignificandWidth); + if (Exponent != 0 && !Format->HasExplicitIntegerBit) + Result.Significand.setBit(Format->Precision - 1U); + if (Result.Significand.isZero()) { + Result.Category = PrintedFloatCategory::Zero; + return Result; + } + + const std::int64_t Bias = 1 - static_cast<std::int64_t>(Format->MinExponent); + const std::int64_t UnbiasedExponent = + Exponent == 0 ? Format->MinExponent : static_cast<std::int64_t>(Exponent) - Bias; + Result.BinaryExponent = static_cast<std::int32_t>(UnbiasedExponent - (Format->Precision - 1U)); + Result.Category = PrintedFloatCategory::Finite; + return Result; +} + +[[nodiscard]] std::string printNaNPayload(APInt Payload, std::uint32_t QuietNaNBit) { + Payload.clearBit(QuietNaNBit); + const auto Width = + static_cast<std::uint32_t>(std::max<std::size_t>(Payload.activeBitWidth(), 1U)); + std::array<std::uint64_t, 2> Words{Payload.word(0), Payload.word(1)}; + std::string Digits = hexadecimalWords(Words, Width); + const std::size_t FirstNonzero = Digits.find_first_not_of('0'); + if (FirstNonzero == std::string::npos) + return "0x0"; + Digits.erase(0, FirstNonzero); + return "0x" + Digits; +} + +[[nodiscard]] std::string formatFiniteFloat(PrintedBinaryFloat Value) { + std::string Result; + if (Value.Negative) + Result.push_back('-'); + if (Value.Category == PrintedFloatCategory::Zero) { + Result.append("0.000000e+00"); + return Result; + } + + const std::size_t TrailingZeros = Value.Significand.countrZero(); + Value.Significand.shiftRight(TrailingZeros); + Value.BinaryExponent += static_cast<std::int32_t>(TrailingZeros); + + std::int32_t DecimalExponent = 0; + if (Value.BinaryExponent >= 0) { + Value.Significand = + Value.Significand.shiftedLeft(static_cast<std::size_t>(Value.BinaryExponent)); + } else { + DecimalExponent = Value.BinaryExponent; + for (std::int32_t Power = 0; Power < -Value.BinaryExponent; ++Power) + Value.Significand.multiplySmall(5); + } + + std::string Digits = Value.Significand.toUnsignedDecimal(); + while (Digits.size() > 1U && Digits.back() == '0') { + Digits.pop_back(); + ++DecimalExponent; + } + + constexpr std::size_t FormatPrecision = 6; + if (Digits.size() > FormatPrecision) { + std::size_t Removed = Digits.size() - FormatPrecision; + const bool RoundUp = Digits[FormatPrecision] >= '5'; + Digits.resize(FormatPrecision); + if (RoundUp) { + std::size_t Position = Digits.size(); + while (Position != 0 && Digits[Position - 1U] == '9') { + Digits[Position - 1U] = '0'; + --Position; + } + if (Position == 0) + Digits.insert(Digits.begin(), '1'); + else + ++Digits[Position - 1U]; + } + while (Digits.size() > 1U && Digits.back() == '0') { + Digits.pop_back(); + ++Removed; + } + DecimalExponent += static_cast<std::int32_t>(Removed); + } + + DecimalExponent += static_cast<std::int32_t>(Digits.size() - 1U); + Result.push_back(Digits.front()); + Result.push_back('.'); + Result.append(Digits.substr(1)); + Result.append(7U - Digits.size(), '0'); + Result.push_back('e'); + Result.push_back(DecimalExponent < 0 ? '-' : '+'); + const std::uint32_t Magnitude = static_cast<std::uint32_t>( + DecimalExponent < 0 ? -static_cast<std::int64_t>(DecimalExponent) : DecimalExponent); + std::string Exponent = std::to_string(Magnitude); + if (Exponent.size() < 2U) + Result.push_back('0'); + Result.append(Exponent); + return Result; +} + +[[nodiscard]] std::string exactFloatingText(const APFloat &Bits) { + return "f0x" + hexadecimalWords(Bits.words(), Bits.bitWidth()); +} + +[[nodiscard]] std::string canonicalFloatingText(const APFloat &Bits) { + auto Decoded = decodePrintedFloat(Bits); + if (!Decoded) + return exactFloatingText(Bits); + + std::string Candidate; + switch (Decoded->Category) { + case PrintedFloatCategory::Zero: + case PrintedFloatCategory::Finite: + Candidate = formatFiniteFloat(std::move(*Decoded)); + break; + case PrintedFloatCategory::Infinity: + Candidate = Decoded->Negative ? "-inf" : "+inf"; + break; + case PrintedFloatCategory::QuietNaN: { + APInt Payload = Decoded->NaNPayload; + Payload.clearBit(Decoded->QuietNaNBit); + if (Payload.isZero()) { + Candidate = Decoded->Negative ? "-qnan" : "+qnan"; + break; + } + Candidate = Decoded->Negative ? "-nan(" : "+nan("; + Candidate.append(printNaNPayload(Decoded->NaNPayload, Decoded->QuietNaNBit)); + Candidate.push_back(')'); + break; + } + case PrintedFloatCategory::SignalingNaN: + Candidate = Decoded->Negative ? "-snan(" : "+snan("; + Candidate.append(printNaNPayload(Decoded->NaNPayload, Decoded->QuietNaNBit)); + Candidate.push_back(')'); + break; + } + + auto Parsed = APFloat::parse(Bits.semantics(), Candidate); + if (!Parsed || *Parsed != Bits) + return exactFloatingText(Bits); + return Candidate; +} + +} // namespace + +std::optional<std::uint32_t> getFloatSemanticsBitWidth(FloatSemantics Semantics) noexcept { + const auto *Info = getFloatSemanticsInfo(Semantics); + if (Info == nullptr) + return std::nullopt; + return Info->BitWidth; +} + +std::string_view getFloatSemanticsSpelling(FloatSemantics Semantics) noexcept { + const auto *Info = getFloatSemanticsInfo(Semantics); + return Info == nullptr ? std::string_view{"invalid-float"} : Info->Spelling; +} + +Expected<APFloat> APFloat::create(FloatSemantics Semantics, std::span<const std::uint64_t> Words) { + const auto BitWidth = getFloatSemanticsBitWidth(Semantics); + if (!BitWidth) + return makeUnexpected(ErrorCode::InvalidArgument, "floating value has unknown semantics"); + + const std::size_t RequiredWords = (*BitWidth + 63U) / 64U; + if (RequiredWords > 2 || Words.size() != RequiredWords) { + return makeUnexpected(ErrorCode::InvalidArgument, + "floating bit pattern has the wrong word count"); + } + const std::uint32_t UsedBitsInLastWord = *BitWidth % 64U; + if (UsedBitsInLastWord != 0 && (Words.back() >> UsedBitsInLastWord) != 0) { + return makeUnexpected(ErrorCode::InvalidArgument, + "floating value has nonzero bits above its semantics width"); + } + + std::array<std::uint64_t, 2> StoredWords{}; + std::ranges::copy(Words, StoredWords.begin()); + return APFloat(Semantics, StoredWords, static_cast<std::uint8_t>(RequiredWords)); +} + +Expected<APFloat> APFloat::parse(FloatSemantics Semantics, std::string_view Text) { + const auto Format = binaryFloatFormat(Semantics); + if (!Format) + return makeUnexpected(ErrorCode::Unsupported, "floating-point semantics are unsupported"); + + std::vector<std::uint64_t> Words; + auto SpecialLiteral = parseSpecialFloatLiteral(Text); + if (!SpecialLiteral) + return std::unexpected(SpecialLiteral.error()); + if (*SpecialLiteral) { + auto SpecialWords = makeSpecialFloatWords(Semantics, **SpecialLiteral); + if (!SpecialWords) + return std::unexpected(SpecialWords.error()); + Words = std::move(*SpecialWords); + } else if (Text.starts_with("f0x")) { + const std::uint32_t BitWidth = getFloatSemanticsBitWidth(Semantics).value_or(0); + const std::string_view Digits = Text.substr(3); + if (Digits.size() != (static_cast<std::size_t>(BitWidth) + 3U) / 4U || + !parseHexWords(Digits, BitWidth, Words)) { + return makeUnexpected(ErrorCode::ParseError, + "exact floating-point bit pattern does not fit its type"); + } + } else if (Text.starts_with("0x") && Text.find('.') != std::string_view::npos) { + auto Literal = parseHexFloatLiteral(Text); + if (!Literal) + return std::unexpected(Literal.error()); + auto Converted = convertRationalToFloatWords(Semantics, Literal->Negative, Literal->Numerator, + Literal->Denominator, Literal->BinaryScale); + if (!Converted) + return std::unexpected(Converted.error()); + Words = std::move(*Converted); + } else if (Text.starts_with("0x")) { + const std::uint32_t BitWidth = getFloatSemanticsBitWidth(Semantics).value_or(0); + std::string_view Digits = Text.substr(2); + char Prefix = '\0'; + if (!Digits.empty() && !isHexDigit(Digits.front())) { + Prefix = Digits.front(); + Digits.remove_prefix(1); + } + char ExpectedPrefix = '\0'; + switch (Semantics) { + case FloatSemantics::IEEEHalf: + ExpectedPrefix = 'H'; + break; + case FloatSemantics::BFloat: + ExpectedPrefix = 'R'; + break; + case FloatSemantics::X87DoubleExtended: + ExpectedPrefix = 'K'; + break; + case FloatSemantics::IEEEQuad: + ExpectedPrefix = 'L'; + break; + case FloatSemantics::PPCDoubleDouble: + ExpectedPrefix = 'M'; + break; + default: + break; + } + if (ExpectedPrefix != '\0') { + const bool HasWideWords = + Semantics == FloatSemantics::IEEEQuad || Semantics == FloatSemantics::PPCDoubleDouble; + const bool Parsed = HasWideWords ? parseWideFloatHexWords(Digits, Words) + : parseHexWords(Digits, BitWidth, Words); + if (Prefix != ExpectedPrefix || !Parsed) { + return makeUnexpected(ErrorCode::ParseError, + "floating-point hexadecimal prefix does not match its type"); + } + } else if (Prefix != '\0') { + return makeUnexpected(ErrorCode::ParseError, "unexpected floating-point hexadecimal prefix"); + } else { + auto DoubleBits = parseRawDoubleBits(Digits); + if (!DoubleBits) + return std::unexpected(DoubleBits.error()); + auto Converted = convertRawDoubleLiteral(Semantics, *DoubleBits); + if (!Converted) + return std::unexpected(Converted.error()); + Words = std::move(*Converted); + } + } else { + auto Literal = parseDecimalFloatLiteral(Text, *Format); + if (!Literal) + return std::unexpected(Literal.error()); + auto Converted = convertRationalToFloatWords(Semantics, Literal->Negative, Literal->Numerator, + Literal->Denominator, Literal->BinaryScale); + if (!Converted) + return std::unexpected(Converted.error()); + Words = std::move(*Converted); + } + return create(Semantics, Words); +} + +std::string APFloat::toString() const { + return canonicalFloatingText(*this); +} + +} // namespace rcc diff --git a/backend/lib/Support/APInt.cpp b/backend/lib/Support/APInt.cpp new file mode 100644 index 0000000..898deee --- /dev/null +++ b/backend/lib/Support/APInt.cpp @@ -0,0 +1,395 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "rcc/ADT/APInt.h" + +#include <algorithm> +#include <bit> +#include <cstddef> +#include <cstdint> +#include <limits> +#include <ranges> +#include <span> +#include <string> +#include <string_view> +#include <utility> +#include <vector> + +namespace rcc { +namespace { + +[[nodiscard]] constexpr std::size_t wordCount(std::uint32_t BitWidth) noexcept { + return (static_cast<std::size_t>(BitWidth) + 63U) / 64U; +} + +[[nodiscard]] constexpr std::uint64_t highWordMask(std::uint32_t BitWidth) noexcept { + const std::uint32_t UsedBits = BitWidth % 64U; + if (UsedBits == 0) + return std::numeric_limits<std::uint64_t>::max(); + return (std::uint64_t{1} << UsedBits) - 1U; +} + +[[nodiscard]] std::size_t significantWordCount(std::span<const std::uint64_t> Words) noexcept { + std::size_t Count = Words.size(); + while (Count != 0 && Words[Count - 1U] == 0) + --Count; + return Count; +} + +[[nodiscard]] Expected<void> verifyAPInt(std::uint32_t BitWidth, + std::span<const std::uint64_t> Words, ErrorCode Code) { + if (BitWidth == 0 || BitWidth > APInt::MaxBitWidth) + return makeUnexpected(Code, "APInt bit width is outside the supported range"); + if (Words.size() != wordCount(BitWidth)) + return makeUnexpected(Code, "APInt bit pattern has the wrong word count"); + if ((Words.back() & ~highWordMask(BitWidth)) != 0) + return makeUnexpected(Code, "APInt has nonzero bits above its declared width"); + return {}; +} + +[[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<unsigned>(Character - '0'); + if (Character >= 'a' && Character <= 'f') + return static_cast<unsigned>(Character - 'a') + 10U; + return static_cast<unsigned>(Character - 'A') + 10U; +} + +void negateWords(std::vector<std::uint64_t> &Words, std::uint32_t BitWidth) { + for (std::uint64_t &Word : Words) + Word = ~Word; + std::uint64_t Carry = 1; + for (std::uint64_t &Word : Words) { + const std::uint64_t Previous = Word; + Word += Carry; + Carry = Carry != 0 && Word < Previous ? 1U : 0U; + } + Words.back() &= highWordMask(BitWidth); +} + +[[nodiscard]] std::string unsignedDecimal(std::vector<std::uint64_t> Words) { + if (std::ranges::all_of(Words, [](std::uint64_t Word) { return Word == 0; })) + return "0"; + std::string Digits; + while (!std::ranges::all_of(Words, [](std::uint64_t Word) { return Word == 0; })) { + unsigned __int128 Remainder = 0; + for (auto Iterator = Words.rbegin(); Iterator != Words.rend(); ++Iterator) { + const unsigned __int128 Dividend = (Remainder << 64U) | *Iterator; + *Iterator = static_cast<std::uint64_t>(Dividend / 10U); + Remainder = Dividend % 10U; + } + Digits.push_back(static_cast<char>('0' + static_cast<unsigned>(Remainder))); + } + std::ranges::reverse(Digits); + return Digits; +} + +} // namespace + +APInt::APInt(std::uint64_t Value) { + if (Value == 0) + return; + Words.push_back(Value); + BitWidth = static_cast<std::uint32_t>(std::bit_width(Value)); +} + +Expected<APInt> APInt::create(std::uint32_t BitWidth, std::span<const std::uint64_t> Words) { + auto Verification = verifyAPInt(BitWidth, Words, ErrorCode::InvalidArgument); + if (!Verification) + return std::unexpected(Verification.error()); + return APInt(BitWidth, std::vector<std::uint64_t>(Words.begin(), Words.end())); +} + +Expected<APInt> APInt::fromUnsigned(std::uint32_t BitWidth, std::uint64_t Value) { + if (BitWidth == 0 || BitWidth > MaxBitWidth) + return makeUnexpected(ErrorCode::InvalidArgument, + "APInt bit width is outside the supported range"); + if (BitWidth < 64U && (Value >> BitWidth) != 0) + return makeUnexpected(ErrorCode::InvalidArgument, + "unsigned value does not fit the APInt bit width"); + + std::vector<std::uint64_t> Words(wordCount(BitWidth), 0); + Words.front() = Value; + return create(BitWidth, Words); +} + +Expected<APInt> APInt::fromSigned(std::uint32_t BitWidth, std::int64_t Value) { + if (BitWidth == 0 || BitWidth > MaxBitWidth) + return makeUnexpected(ErrorCode::InvalidArgument, + "APInt bit width is outside the supported range"); + if (BitWidth < 64U) { + const std::uint32_t MagnitudeBits = BitWidth - 1U; + const std::int64_t Minimum = -(std::int64_t{1} << MagnitudeBits); + const std::int64_t Maximum = (std::int64_t{1} << MagnitudeBits) - 1; + if (Value < Minimum || Value > Maximum) + return makeUnexpected(ErrorCode::InvalidArgument, + "signed value does not fit the APInt bit width"); + } + + const std::uint64_t Fill = + Value < 0 ? std::numeric_limits<std::uint64_t>::max() : std::uint64_t{0}; + std::vector<std::uint64_t> Words(wordCount(BitWidth), Fill); + Words.front() = static_cast<std::uint64_t>(Value); + Words.back() &= highWordMask(BitWidth); + return create(BitWidth, Words); +} + +Expected<APInt> APInt::parseDecimal(std::uint32_t BitWidth, std::string_view Text) { + if (BitWidth == 0 || BitWidth > MaxBitWidth) + return makeUnexpected(ErrorCode::ParseError, "APInt bit width is outside the supported range"); + const bool Negative = Text.starts_with('-'); + if (Negative) + Text.remove_prefix(1); + if (Text.empty()) + return makeUnexpected(ErrorCode::ParseError, "APInt has no decimal digits"); + + std::vector<std::uint64_t> Words(wordCount(BitWidth), 0); + for (const char Character : Text) { + if (Character < '0' || Character > '9') + return makeUnexpected(ErrorCode::ParseError, "APInt contains a non-decimal digit"); + std::uint64_t Carry = static_cast<unsigned>(Character - '0'); + for (std::uint64_t &Word : Words) { + const unsigned __int128 Product = static_cast<unsigned __int128>(Word) * 10U + Carry; + Word = static_cast<std::uint64_t>(Product); + Carry = static_cast<std::uint64_t>(Product >> 64U); + } + } + + Words.back() &= highWordMask(BitWidth); + if (Negative) + negateWords(Words, BitWidth); + return create(BitWidth, Words); +} + +Expected<APInt> APInt::parseLLVMHex(std::uint32_t BitWidth, std::string_view Text) { + if (Text.size() < 4 || (Text[0] != 'u' && Text[0] != 's') || Text[1] != '0' || Text[2] != 'x') { + return makeUnexpected(ErrorCode::ParseError, + "integer hexadecimal spelling must use u0x or s0x"); + } + const std::string_view Digits = Text.substr(3); + if (Digits.empty() || !std::ranges::all_of(Digits, isHexDigit)) { + return makeUnexpected(ErrorCode::ParseError, + "integer hexadecimal spelling contains a non-hexadecimal digit"); + } + + const auto *const FirstNonZero = + std::ranges::find_if(Digits, [](char Character) { return hexDigitValue(Character) != 0; }); + std::size_t ActiveBits = 0; + if (FirstNonZero != Digits.end()) { + const unsigned MostSignificantNibble = hexDigitValue(*FirstNonZero); + const auto MostSignificantBits = static_cast<unsigned>(std::bit_width(MostSignificantNibble)); + ActiveBits = + static_cast<std::size_t>(Digits.end() - FirstNonZero - 1U) * 4U + MostSignificantBits; + } + + if (BitWidth == 0 || BitWidth > MaxBitWidth) + return makeUnexpected(ErrorCode::ParseError, "APInt bit width is outside the supported range"); + std::vector<std::uint64_t> Words(wordCount(BitWidth), 0); + std::size_t Nibble = 0; + for (auto Iterator = Digits.rbegin(); Iterator != Digits.rend(); ++Iterator, ++Nibble) { + const std::size_t BitOffset = Nibble * 4U; + if (BitOffset >= BitWidth) + break; + Words[BitOffset / 64U] |= static_cast<std::uint64_t>(hexDigitValue(*Iterator)) + << (BitOffset % 64U); + } + + if (Text[0] == 's' && ActiveBits != 0 && ActiveBits < BitWidth) { + const std::size_t FirstWord = ActiveBits / 64U; + const unsigned FirstBit = static_cast<unsigned>(ActiveBits % 64U); + Words[FirstWord] |= std::numeric_limits<std::uint64_t>::max() << FirstBit; + for (std::size_t Index = FirstWord + 1U; Index < Words.size(); ++Index) + Words[Index] = std::numeric_limits<std::uint64_t>::max(); + } + Words.back() &= highWordMask(BitWidth); + return create(BitWidth, Words); +} + +bool APInt::isZero() const noexcept { + return std::ranges::all_of(Words, [](std::uint64_t Word) { return Word == 0; }); +} + +bool APInt::isNegative() const noexcept { + if (BitWidth == 0 || Words.empty()) + return false; + const std::uint32_t SignBit = BitWidth - 1U; + return (Words[SignBit / 64U] & (std::uint64_t{1} << (SignBit % 64U))) != 0; +} + +std::size_t APInt::activeBitWidth() const noexcept { + const std::size_t Count = significantWordCount(Words); + if (Count == 0) + return 0; + return (Count - 1U) * 64U + static_cast<std::size_t>(std::bit_width(Words[Count - 1U])); +} + +std::size_t APInt::countrZero() const noexcept { + std::size_t Result = 0; + for (const std::uint64_t Word : Words) { + if (Word == 0) { + Result += 64U; + continue; + } + return Result + static_cast<std::size_t>(std::countr_zero(Word)); + } + return Result; +} + +std::uint64_t APInt::word(std::size_t Index) const noexcept { + return Index < Words.size() ? Words[Index] : 0; +} + +int APInt::compare(const APInt &Other) const noexcept { + const std::size_t LeftCount = significantWordCount(Words); + const std::size_t RightCount = significantWordCount(Other.Words); + if (LeftCount != RightCount) + return LeftCount < RightCount ? -1 : 1; + for (std::size_t Index = LeftCount; Index != 0; --Index) { + if (Words[Index - 1U] != Other.Words[Index - 1U]) + return Words[Index - 1U] < Other.Words[Index - 1U] ? -1 : 1; + } + return 0; +} + +int APInt::compareToShifted(const APInt &Other, std::size_t OtherShift) const noexcept { + if (Other.isZero()) + return isZero() ? 0 : 1; + if (isZero()) + return -1; + const std::size_t LeftBits = activeBitWidth(); + const std::size_t RightBits = Other.activeBitWidth(); + if (OtherShift > std::numeric_limits<std::size_t>::max() - RightBits) + return -1; + const std::size_t ShiftedBits = RightBits + OtherShift; + if (LeftBits != ShiftedBits) + return LeftBits < ShiftedBits ? -1 : 1; + return compare(Other.shiftedLeft(OtherShift)); +} + +void APInt::multiplySmall(std::uint32_t Multiplier) { + if (Multiplier == 0 || isZero()) { + Words.clear(); + BitWidth = 0; + return; + } + unsigned __int128 Carry = 0; + for (std::uint64_t &Word : Words) { + const unsigned __int128 Product = static_cast<unsigned __int128>(Word) * Multiplier + Carry; + Word = static_cast<std::uint64_t>(Product); + Carry = Product >> 64U; + } + if (Carry != 0) + Words.push_back(static_cast<std::uint64_t>(Carry)); + normalizeMagnitude(); +} + +void APInt::addSmall(std::uint32_t Value) { + unsigned __int128 Carry = Value; + std::size_t Index = 0; + while (Carry != 0 && Index < Words.size()) { + const unsigned __int128 Sum = static_cast<unsigned __int128>(Words[Index]) + Carry; + Words[Index] = static_cast<std::uint64_t>(Sum); + Carry = Sum >> 64U; + ++Index; + } + if (Carry != 0) + Words.push_back(static_cast<std::uint64_t>(Carry)); + normalizeMagnitude(); +} + +void APInt::setBit(std::size_t Bit) { + const std::size_t Index = Bit / 64U; + if (Index >= Words.size()) + Words.resize(Index + 1U, 0); + Words[Index] |= std::uint64_t{1} << (Bit % 64U); + normalizeMagnitude(); +} + +void APInt::clearBit(std::size_t Bit) noexcept { + const std::size_t Index = Bit / 64U; + if (Index >= Words.size()) + return; + Words[Index] &= ~(std::uint64_t{1} << (Bit % 64U)); + normalizeMagnitude(); +} + +void APInt::shiftRight(std::size_t Amount) noexcept { + if (Amount == 0 || isZero()) + return; + const std::size_t WordOffset = Amount / 64U; + if (WordOffset >= Words.size()) { + Words.clear(); + BitWidth = 0; + return; + } + const unsigned BitOffset = static_cast<unsigned>(Amount % 64U); + const std::size_t NewSize = Words.size() - WordOffset; + for (std::size_t Index = 0; Index < NewSize; ++Index) { + std::uint64_t Value = Words[Index + WordOffset] >> BitOffset; + if (BitOffset != 0 && Index + WordOffset + 1U < Words.size()) + Value |= Words[Index + WordOffset + 1U] << (64U - BitOffset); + Words[Index] = Value; + } + Words.resize(NewSize); + normalizeMagnitude(); +} + +APInt APInt::shiftedLeft(std::size_t Amount) const { + if (isZero() || Amount == 0) + return *this; + const std::size_t WordOffset = Amount / 64U; + const unsigned BitOffset = static_cast<unsigned>(Amount % 64U); + APInt Result; + Result.Words.assign(Words.size() + WordOffset + (BitOffset == 0 ? 0U : 1U), 0); + for (std::size_t Index = 0; Index < Words.size(); ++Index) { + const unsigned __int128 Shifted = static_cast<unsigned __int128>(Words[Index]) << BitOffset; + Result.Words[Index + WordOffset] |= static_cast<std::uint64_t>(Shifted); + if (BitOffset != 0) + Result.Words[Index + WordOffset + 1U] |= static_cast<std::uint64_t>(Shifted >> 64U); + } + Result.normalizeMagnitude(); + return Result; +} + +void APInt::subtract(const APInt &Other) noexcept { + std::uint64_t Borrow = 0; + for (std::size_t Index = 0; Index < Words.size(); ++Index) { + const unsigned __int128 Subtrahend = + (Index < Other.Words.size() ? static_cast<unsigned __int128>(Other.Words[Index]) : 0) + + Borrow; + const unsigned __int128 Current = Words[Index]; + Words[Index] = static_cast<std::uint64_t>(Current - Subtrahend); + Borrow = Current < Subtrahend ? 1U : 0U; + } + normalizeMagnitude(); +} + +std::string APInt::toUnsignedDecimal() const { + return unsignedDecimal(Words); +} + +std::string APInt::toSignedDecimal() const { + if (!isNegative()) + return toUnsignedDecimal(); + + std::vector<std::uint64_t> Magnitude = Words; + negateWords(Magnitude, BitWidth); + return "-" + unsignedDecimal(std::move(Magnitude)); +} + +Expected<void> APInt::verify() const { + return verifyAPInt(BitWidth, Words, ErrorCode::VerificationError); +} + +void APInt::normalizeMagnitude() noexcept { + while (!Words.empty() && Words.back() == 0) + Words.pop_back(); + const std::size_t ActiveBits = activeBitWidth(); + BitWidth = static_cast<std::uint32_t>(ActiveBits); +} + +} // namespace rcc diff --git a/backend/lib/Support/CMakeLists.txt b/backend/lib/Support/CMakeLists.txt index c081106..48bcb09 100644 --- a/backend/lib/Support/CMakeLists.txt +++ b/backend/lib/Support/CMakeLists.txt @@ -1,8 +1,10 @@ # SPDX-License-Identifier: GPL-3.0-or-later -set(RCCBackendSupportSources Error.cpp SHA256.cpp TextDiagnostic.cpp YAML.cpp) +set(RCCBackendSupportSources APFloat.cpp APInt.cpp Error.cpp SHA256.cpp TextDiagnostic.cpp YAML.cpp) set( RCCBackendSupportHeaders + "${RCC_BACKEND_SOURCE_DIR}/include/rcc/ADT/APFloat.h" + "${RCC_BACKEND_SOURCE_DIR}/include/rcc/ADT/APInt.h" "${RCC_BACKEND_SOURCE_DIR}/include/rcc/Support/Error.h" "${RCC_BACKEND_SOURCE_DIR}/include/rcc/Support/SHA256.h" "${RCC_BACKEND_SOURCE_DIR}/include/rcc/Support/TextDiagnostic.h" @@ -37,6 +39,8 @@ install( ) rcc_backend_register_format_files( + "${CMAKE_CURRENT_SOURCE_DIR}/APFloat.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/APInt.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/Error.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/SHA256.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/TextDiagnostic.cpp" @@ -44,6 +48,8 @@ rcc_backend_register_format_files( ${RCCBackendSupportHeaders} ) rcc_backend_register_tidy_files( + "${CMAKE_CURRENT_SOURCE_DIR}/APFloat.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/APInt.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/Error.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/SHA256.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/TextDiagnostic.cpp" diff --git a/backend/lib/Target/RISCV/GISel/RISCVCallLowering.cpp b/backend/lib/Target/RISCV/GISel/RISCVCallLowering.cpp index cfe8bb6..71c7251 100644 --- a/backend/lib/Target/RISCV/GISel/RISCVCallLowering.cpp +++ b/backend/lib/Target/RISCV/GISel/RISCVCallLowering.cpp @@ -781,7 +781,7 @@ RISCVCallLowering::planReturn(const ReturnLoweringRequest &Request) const { [[nodiscard]] Expected<void> addUnsignedImmediate(MachineInstrBuilder &Builder, std::uint64_t Value) { - auto Immediate = IntegerImmediate::fromUnsigned(64, Value); + auto Immediate = APInt::fromUnsigned(64, Value); if (!Immediate) return std::unexpected(Immediate.error()); return Builder.addIntegerImmediate(std::move(*Immediate)); diff --git a/backend/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp b/backend/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp index a2c7194..2090496 100644 --- a/backend/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp +++ b/backend/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp @@ -2512,7 +2512,7 @@ private: const std::uint32_t BitWidth = Type->scalarBitWidth(); const std::uint64_t Bits = Floating->Bits.words().empty() ? 0 : Floating->Bits.words().front(); - auto Integer = IntegerImmediate::fromUnsigned(64, Bits); + auto Integer = APInt::fromUnsigned(64, Bits); if (!Integer) return std::unexpected(Integer.error()); auto IntegerType = LLT::makeInteger(64); diff --git a/backend/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp b/backend/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp index e9d72fd..da8d687 100644 --- a/backend/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp +++ b/backend/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp @@ -99,9 +99,8 @@ namespace { Destination.scalarBitWidth() <= 64; } -[[nodiscard]] Expected<IntegerImmediate> extendIntegerImmediate(const IntegerImmediate &Value, - std::uint32_t DestinationWidth, - bool SignExtend) { +[[nodiscard]] Expected<APInt> +extendIntegerImmediate(const APInt &Value, std::uint32_t DestinationWidth, bool SignExtend) { if (DestinationWidth == 0 || DestinationWidth > 64) return makeUnexpected(ErrorCode::Unsupported, "RISC-V constant extension requires a destination no wider than 64 bits"); @@ -112,7 +111,7 @@ namespace { Raw |= ~std::uint64_t{0} << SourceWidth; if (DestinationWidth < 64) Raw &= (std::uint64_t{1} << DestinationWidth) - 1U; - return IntegerImmediate::create(DestinationWidth, std::span<const std::uint64_t>(&Raw, 1)); + return APInt::create(DestinationWidth, std::span<const std::uint64_t>(&Raw, 1)); } [[nodiscard]] Expected<void> @@ -271,9 +270,8 @@ wideConstantUnmergePlan(const MachineInstr &Constant, const MachineFunction &Fun return Plan; } -[[nodiscard]] Expected<IntegerImmediate> wideConstantWord(const IntegerImmediate &Value, - std::size_t WordIndex, - std::uint32_t Width, bool SignExtend) { +[[nodiscard]] Expected<APInt> wideConstantWord(const APInt &Value, std::size_t WordIndex, + std::uint32_t Width, bool SignExtend) { std::uint64_t Raw = 0; if (WordIndex < Value.words().size()) Raw = Value.words()[WordIndex]; @@ -287,7 +285,7 @@ wideConstantUnmergePlan(const MachineInstr &Constant, const MachineFunction &Fun } if (Width < 64) Raw &= (std::uint64_t{1} << Width) - 1U; - return IntegerImmediate::create(Width, std::span<const std::uint64_t>(&Raw, 1)); + return APInt::create(Width, std::span<const std::uint64_t>(&Raw, 1)); } [[nodiscard]] Expected<void> foldWideConstant(MachineInstr &Instruction, MachineFunction &Function, @@ -303,7 +301,7 @@ wideConstantUnmergePlan(const MachineInstr &Constant, const MachineFunction &Fun return makeUnexpected(ErrorCode::VerificationError, "RISC-V wide constant has no integer immediate"); - std::vector<IntegerImmediate> Words; + std::vector<APInt> Words; Words.reserve(Plan->Outputs.size()); for (std::size_t Index = 0; Index < Plan->Outputs.size(); ++Index) { const auto Info = Function.registerInfo().virtualRegisterInfo(Plan->Outputs[Index]); @@ -1240,7 +1238,7 @@ void eraseDeadArtifacts(std::span<MachineInstr *const> Candidates, auto Result = Builder.function().registerInfo().createVirtualRegister(*Type); if (!Result) return std::unexpected(Result.error()); - auto Immediate = IntegerImmediate::fromUnsigned(Width, Value); + auto Immediate = APInt::fromUnsigned(Width, Value); if (!Immediate) return std::unexpected(Immediate.error()); auto Point = Builder.setInsertPointBefore(Anchor); diff --git a/backend/lib/Target/RISCV/RISCVMCTargetLowering.cpp b/backend/lib/Target/RISCV/RISCVMCTargetLowering.cpp index ee41d47..0db51f0 100644 --- a/backend/lib/Target/RISCV/RISCVMCTargetLowering.cpp +++ b/backend/lib/Target/RISCV/RISCVMCTargetLowering.cpp @@ -252,7 +252,7 @@ struct TypeLayout final { return mc::MCOperand::expression(std::move(*TargetExpression)); } -[[nodiscard]] Expected<std::int64_t> lowerIntegerImmediate(const IntegerImmediate &Value) { +[[nodiscard]] Expected<std::int64_t> lowerIntegerImmediate(const APInt &Value) { if (Value.bitWidth() > 64 || Value.words().size() != 1) { return makeUnexpected(ErrorCode::AssemblyLoweringFailed, "RISC-V instruction has an integer immediate wider than 64 bits"); @@ -714,14 +714,14 @@ struct TypeLayout final { } switch (Initializer.kind()) { case GlobalInitializer::Kind::Integer: { - const auto *Value = Initializer.getIf<IntegerImmediate>(); + const auto *Value = Initializer.getIf<APInt>(); if (Value == nullptr) return makeUnexpected(ErrorCode::InternalError, "integer initializer has no value"); const std::uint64_t Size = (static_cast<std::uint64_t>(Value->bitWidth()) + 7U) / 8U; return Streamer.emitBytes(bytesFromWords(Value->words(), Size)); } case GlobalInitializer::Kind::Floating: { - const auto *Value = Initializer.getIf<FloatingPointBits>(); + const auto *Value = Initializer.getIf<APFloat>(); if (Value == nullptr) return makeUnexpected(ErrorCode::InternalError, "floating initializer has no value"); const std::uint64_t Size = (static_cast<std::uint64_t>(Value->bitWidth()) + 7U) / 8U; @@ -839,12 +839,12 @@ struct TypeLayout final { return false; switch (Initializer.kind()) { case GlobalInitializer::Kind::Integer: { - const auto *Value = Initializer.getIf<IntegerImmediate>(); + const auto *Value = Initializer.getIf<APInt>(); return Value != nullptr && std::ranges::all_of(Value->words(), [](std::uint64_t Word) { return Word == 0; }); } case GlobalInitializer::Kind::Floating: { - const auto *Value = Initializer.getIf<FloatingPointBits>(); + const auto *Value = Initializer.getIf<APFloat>(); return Value != nullptr && std::ranges::all_of(Value->words(), [](std::uint64_t Word) { return Word == 0; }); } diff --git a/backend/unittests/CodeGen/GenericOpcodesTest.cpp b/backend/unittests/CodeGen/GenericOpcodesTest.cpp index 6240c6d..73c1c36 100644 --- a/backend/unittests/CodeGen/GenericOpcodesTest.cpp +++ b/backend/unittests/CodeGen/GenericOpcodesTest.cpp @@ -326,7 +326,7 @@ void testShapesAndTypes(TestContext &Context) { (*Function)->registerInfo()), "G_ADD must reject a source from a different LLT equality group"); - auto ZeroImmediate = rcc::IntegerImmediate::fromUnsigned(64, 0); + auto ZeroImmediate = rcc::APInt::fromUnsigned(64, 0); std::vector<rcc::MachineOperand> PointerConstant; PointerConstant.push_back(registerOperand(*Address, true)); if (ZeroImmediate) @@ -489,7 +489,7 @@ void testShapesAndTypes(TestContext &Context) { "G_FPEXTLOAD must reject an integer result"); const std::array<std::uint64_t, 1> FloatWords{0}; - auto FloatBits = rcc::FloatingPointBits::create(rcc::FloatSemantics::IEEESingle, FloatWords); + auto FloatBits = rcc::APFloat::create(rcc::FloatSemantics::IEEESingle, FloatWords); if (!FloatBits) std::terminate(); std::vector<rcc::MachineOperand> FConstantOperands; @@ -505,7 +505,7 @@ void testShapesAndTypes(TestContext &Context) { "G_FCONSTANT must reject mismatched floating semantics"); const std::array<std::uint64_t, 1> BFloatWords{0}; - auto BFloatBits = rcc::FloatingPointBits::create(rcc::FloatSemantics::BFloat, BFloatWords); + auto BFloatBits = rcc::APFloat::create(rcc::FloatSemantics::BFloat, BFloatWords); if (!BFloatBits) std::terminate(); FConstantOperands[0] = registerOperand(Scalar16, true); diff --git a/backend/unittests/CodeGen/MachineConstantPoolTest.cpp b/backend/unittests/CodeGen/MachineConstantPoolTest.cpp index 08b69e2..39323d5 100644 --- a/backend/unittests/CodeGen/MachineConstantPoolTest.cpp +++ b/backend/unittests/CodeGen/MachineConstantPoolTest.cpp @@ -128,7 +128,7 @@ struct PoolTargetConstant final { [[nodiscard]] rcc::MachineConstantPoolEntryDesc genericDescription(rcc::ConstantPoolMergeKind Merge) { rcc::MachineConstantPoolEntryDesc Description; - auto Integer = rcc::IntegerImmediate::fromUnsigned(32, 0x04030201U); + auto Integer = rcc::APInt::fromUnsigned(32, 0x04030201U); Description.Value = std::move(*rcc::GenericConstantData::integer(std::move(*Integer))); Description.Size = 4; Description.Align = *rcc::Alignment::create(4); diff --git a/backend/unittests/CodeGen/MachineFunctionClonerTest.cpp b/backend/unittests/CodeGen/MachineFunctionClonerTest.cpp index e923108..e5acc04 100644 --- a/backend/unittests/CodeGen/MachineFunctionClonerTest.cpp +++ b/backend/unittests/CodeGen/MachineFunctionClonerTest.cpp @@ -335,7 +335,7 @@ struct SourceFixture final { return false; rcc::MachineConstantPoolEntryDesc HistoricalConstant; - auto HistoricalInteger = rcc::IntegerImmediate::fromUnsigned(32, 0x01010101U); + auto HistoricalInteger = rcc::APInt::fromUnsigned(32, 0x01010101U); HistoricalConstant.Value = std::move(*rcc::GenericConstantData::integer(std::move(*HistoricalInteger))); HistoricalConstant.Size = 4; diff --git a/backend/unittests/CodeGen/MachineIRBuilderTest.cpp b/backend/unittests/CodeGen/MachineIRBuilderTest.cpp index c4d5767..44a0e32 100644 --- a/backend/unittests/CodeGen/MachineIRBuilderTest.cpp +++ b/backend/unittests/CodeGen/MachineIRBuilderTest.cpp @@ -426,7 +426,7 @@ void testImplicitAndMemoryValidation(TestContext &Context) { "builder must reject unknown constant-pool and jump-table indices before allocation"); rcc::MachineConstantPoolEntryDesc ConstantDescription; - auto Integer = rcc::IntegerImmediate::fromUnsigned(64, 0x2a2a2a2a2a2a2a2aULL); + auto Integer = rcc::APInt::fromUnsigned(64, 0x2a2a2a2a2a2a2a2aULL); ConstantDescription.Value = std::move(*rcc::GenericConstantData::integer(std::move(*Integer))); ConstantDescription.Size = 8; ConstantDescription.Align = *rcc::Alignment::create(8); diff --git a/backend/unittests/CodeGen/MachineIRCoreTest.cpp b/backend/unittests/CodeGen/MachineIRCoreTest.cpp index 7982b04..6365e59 100644 --- a/backend/unittests/CodeGen/MachineIRCoreTest.cpp +++ b/backend/unittests/CodeGen/MachineIRCoreTest.cpp @@ -102,7 +102,7 @@ void testOperandKinds(TestContext &Context) { return; const std::array<std::uint64_t, 1> HalfWords{0x3c00}; - auto HalfBits = rcc::FloatingPointBits::create(rcc::FloatSemantics::IEEEHalf, HalfWords); + auto HalfBits = rcc::APFloat::create(rcc::FloatSemantics::IEEEHalf, HalfWords); Context.expect(HalfBits.has_value(), "well-sized floating immediate must be accepted"); if (!HalfBits) return; @@ -110,10 +110,10 @@ void testOperandKinds(TestContext &Context) { "floating immediate must preserve semantics and raw bits"); const std::array<std::uint64_t, 1> OverflowHalfWords{0x10000}; - Context.expect(!rcc::FloatingPointBits::create(rcc::FloatSemantics::IEEEHalf, OverflowHalfWords), + Context.expect(!rcc::APFloat::create(rcc::FloatSemantics::IEEEHalf, OverflowHalfWords), "floating immediate bits above semantics width must be rejected"); const std::array<std::uint64_t, 2> TooManyHalfWords{0, 0}; - Context.expect(!rcc::FloatingPointBits::create(rcc::FloatSemantics::IEEEHalf, TooManyHalfWords), + Context.expect(!rcc::APFloat::create(rcc::FloatSemantics::IEEEHalf, TooManyHalfWords), "floating immediate word-count mismatch must be rejected"); auto Virtual = Function->registerInfo().createVirtualRegister(*rcc::LLT::makeInteger(32)); diff --git a/backend/unittests/CodeGen/MachineOperandTest.cpp b/backend/unittests/CodeGen/MachineOperandTest.cpp index 3cc7060..96f1f19 100644 --- a/backend/unittests/CodeGen/MachineOperandTest.cpp +++ b/backend/unittests/CodeGen/MachineOperandTest.cpp @@ -108,7 +108,7 @@ buildOperandInstruction(rcc::MachineFunction &Function, const rcc::TargetDescrip 0x0123456789abcdefULL, 0xfedcba9876543210ULL, }; - auto Integer = rcc::IntegerImmediate::create(128, Words); + auto Integer = rcc::APInt::create(128, Words); if (!Integer) return std::unexpected(Integer.error()); @@ -149,35 +149,34 @@ void testIntegerImmediate(TestContext &Context) { 0x0123456789abcdefULL, 0x0000000000000001ULL, }; - auto Integer = rcc::IntegerImmediate::create(65, Words); + auto Integer = rcc::APInt::create(65, Words); Context.expect(Integer.has_value() && Integer->bitWidth() == 65 && Integer->words().size() == 2 && Integer->words()[0] == Words[0] && Integer->words()[1] == Words[1] && Integer->isNegative() && !Integer->isZero(), "arbitrary-width integer must preserve exact little-endian words"); - Context.expect(!rcc::IntegerImmediate::create(0, std::span<const std::uint64_t>{}) && - !rcc::IntegerImmediate::create(rcc::IntegerImmediate::MaxBitWidth + 1, - std::span<const std::uint64_t>{}), - "integer immediate must reject unsupported bit widths"); + Context.expect( + !rcc::APInt::create(0, std::span<const std::uint64_t>{}) && + !rcc::APInt::create(rcc::APInt::MaxBitWidth + 1, std::span<const std::uint64_t>{}), + "integer immediate must reject unsupported bit widths"); const std::array<std::uint64_t, 2> TwoWords{1, 0}; const std::array<std::uint64_t, 1> HighBits{0x100}; - Context.expect(!rcc::IntegerImmediate::create(64, TwoWords) && - !rcc::IntegerImmediate::create(8, HighBits), + Context.expect(!rcc::APInt::create(64, TwoWords) && !rcc::APInt::create(8, HighBits), "integer immediate must reject word-count and high-bit mismatches"); - auto UnsignedMaximum = rcc::IntegerImmediate::fromUnsigned(8, 255); - auto UnsignedOverflow = rcc::IntegerImmediate::fromUnsigned(8, 256); - auto SignedMinimum = rcc::IntegerImmediate::fromSigned(8, -128); - auto SignedMaximum = rcc::IntegerImmediate::fromSigned(8, 127); - auto SignedUnderflow = rcc::IntegerImmediate::fromSigned(8, -129); - auto SignedOverflow = rcc::IntegerImmediate::fromSigned(8, 128); + auto UnsignedMaximum = rcc::APInt::fromUnsigned(8, 255); + auto UnsignedOverflow = rcc::APInt::fromUnsigned(8, 256); + auto SignedMinimum = rcc::APInt::fromSigned(8, -128); + auto SignedMaximum = rcc::APInt::fromSigned(8, 127); + auto SignedUnderflow = rcc::APInt::fromSigned(8, -129); + auto SignedOverflow = rcc::APInt::fromSigned(8, 128); Context.expect(UnsignedMaximum && UnsignedMaximum->words()[0] == 255 && !UnsignedOverflow && SignedMinimum && SignedMinimum->words()[0] == 128 && SignedMaximum && SignedMaximum->words()[0] == 127 && !SignedUnderflow && !SignedOverflow, "integer immediate scalar factories must enforce signedness and width"); - auto SignExtended = rcc::IntegerImmediate::fromSigned(128, -1); - auto Zero = rcc::IntegerImmediate::fromUnsigned(128, 0); + auto SignExtended = rcc::APInt::fromSigned(128, -1); + auto Zero = rcc::APInt::fromUnsigned(128, 0); Context.expect(SignExtended && SignExtended->isNegative() && SignExtended->words().size() == 2 && SignExtended->words()[0] == std::numeric_limits<std::uint64_t>::max() && SignExtended->words()[1] == std::numeric_limits<std::uint64_t>::max() && diff --git a/backend/unittests/CodeGen/MachineVerifierTest.cpp b/backend/unittests/CodeGen/MachineVerifierTest.cpp index 528ff3b..c8c512f 100644 --- a/backend/unittests/CodeGen/MachineVerifierTest.cpp +++ b/backend/unittests/CodeGen/MachineVerifierTest.cpp @@ -642,7 +642,7 @@ void testTableReferenceFailures(TestContext &Context) { return; rcc::MachineConstantPoolEntryDesc ConstantDescription; - auto Integer = rcc::IntegerImmediate::fromUnsigned(32, 0x01010101U); + auto Integer = rcc::APInt::fromUnsigned(32, 0x01010101U); ConstantDescription.Value = std::move(*rcc::GenericConstantData::integer(std::move(*Integer))); ConstantDescription.Size = 4; ConstantDescription.Align = *rcc::Alignment::create(4); diff --git a/backend/unittests/Target/RISCVAssemblyPrinterTest.cpp b/backend/unittests/Target/RISCVAssemblyPrinterTest.cpp index e40ce8a..3794227 100644 --- a/backend/unittests/Target/RISCVAssemblyPrinterTest.cpp +++ b/backend/unittests/Target/RISCVAssemblyPrinterTest.cpp @@ -45,7 +45,7 @@ makeObject(std::string Symbol, std::uint64_t Size, std::uint64_t Alignment, } [[nodiscard]] rcc::Expected<void> addObjects(rcc::CompilationUnit &Unit) { - auto AnswerValue = rcc::IntegerImmediate::fromUnsigned(32, 42); + auto AnswerValue = rcc::APInt::fromUnsigned(32, 42); if (!AnswerValue) return std::unexpected(AnswerValue.error()); auto Answer = diff --git a/backend/unittests/Target/RISCVCodeGenTest.cpp b/backend/unittests/Target/RISCVCodeGenTest.cpp index c5fc10a..1bc3361 100644 --- a/backend/unittests/Target/RISCVCodeGenTest.cpp +++ b/backend/unittests/Target/RISCVCodeGenTest.cpp @@ -347,8 +347,8 @@ void testClassification(TestContext &Context) { return; const std::array<std::uint64_t, 1> SingleWords{0x3f800000U}; const std::array<std::uint64_t, 1> DoubleWords{0x3ff0000000000000ULL}; - auto SingleBits = rcc::FloatingPointBits::create(rcc::FloatSemantics::IEEESingle, SingleWords); - auto DoubleBits = rcc::FloatingPointBits::create(rcc::FloatSemantics::IEEEDouble, DoubleWords); + auto SingleBits = rcc::APFloat::create(rcc::FloatSemantics::IEEESingle, SingleWords); + auto DoubleBits = rcc::APFloat::create(rcc::FloatSemantics::IEEEDouble, DoubleWords); Context.expect(SingleBits.has_value() && DoubleBits.has_value(), "floating constant payloads must be constructible"); if (!SingleBits || !DoubleBits) @@ -875,7 +875,7 @@ void testAtomicCompareExchangeLegalization(TestContext &Context) { if (!AddressDefinition || !AddressDefinition->addDef(*Address) || !AddressDefinition->build()) return; const auto BuildConstant = [&](rcc::Register Destination, std::uint64_t Value) -> bool { - auto Immediate = rcc::IntegerImmediate::fromUnsigned(8, Value); + auto Immediate = rcc::APInt::fromUnsigned(8, Value); auto Pending = Builder.buildInstr(rcc::opcode(rcc::GenericOpcode::G_CONSTANT)); return Immediate && Pending && Pending->addDef(Destination) && Pending->addIntegerImmediate(std::move(*Immediate)) && Pending->build(); @@ -1013,7 +1013,7 @@ void testConstantExtensionPreservesLiveSource(TestContext &Context) { auto Source = (*Function)->registerInfo().createVirtualRegister(*I32); auto Extended = (*Function)->registerInfo().createVirtualRegister(*I64); auto PreservedUse = (*Function)->registerInfo().createVirtualRegister(*I32); - auto Immediate = rcc::IntegerImmediate::fromUnsigned(32, 7); + auto Immediate = rcc::APInt::fromUnsigned(32, 7); if (!Source || !Extended || !PreservedUse || !Immediate) return; @@ -1060,7 +1060,7 @@ void testIntegerCompareReusesConstantExtension(TestContext &Context) { rcc::MachineInstr *Compare = Function->firstBlock()->firstInstruction(); const rcc::Register *Right = Compare->operandAt(3)->getIf<rcc::Register>(); auto WideRight = Function->registerInfo().createVirtualRegister(*I64); - auto Immediate = rcc::IntegerImmediate::fromUnsigned(32, 0); + auto Immediate = rcc::APInt::fromUnsigned(32, 0); if (Right == nullptr || !WideRight || !Immediate) return; diff --git a/backend/unittests/Target/RISCVMCTest.cpp b/backend/unittests/Target/RISCVMCTest.cpp index d77899a..f43c574 100644 --- a/backend/unittests/Target/RISCVMCTest.cpp +++ b/backend/unittests/Target/RISCVMCTest.cpp @@ -260,7 +260,7 @@ symbolExpression(rcc::mc::MCContext &Context, std::string_view Name, std::uint32 [[nodiscard]] bool testObjectEmission(const rcc::TargetDescription &Target) { auto Alignment = rcc::Alignment::create(8); - auto Value = rcc::IntegerImmediate::fromUnsigned(64, 0x1122334455667788ULL); + auto Value = rcc::APInt::fromUnsigned(64, 0x1122334455667788ULL); if (!Alignment || !Value) return false; rcc::GlobalObjectDesc Global; diff --git a/backend/unittests/Target/RISCVObjectFileTest.cpp b/backend/unittests/Target/RISCVObjectFileTest.cpp index 1b6d1f7..95ed5c8 100644 --- a/backend/unittests/Target/RISCVObjectFileTest.cpp +++ b/backend/unittests/Target/RISCVObjectFileTest.cpp @@ -293,7 +293,7 @@ private: [[nodiscard]] rcc::Expected<rcc::GlobalInitializer> integer(std::uint32_t Bits, std::uint64_t Value) { - auto Immediate = rcc::IntegerImmediate::fromUnsigned(Bits, Value); + auto Immediate = rcc::APInt::fromUnsigned(Bits, Value); if (!Immediate) return std::unexpected(Immediate.error()); return rcc::GlobalInitializer::integer(std::move(*Immediate)); |
