diff --git a/libs/interpreter/source/eval.cpp b/libs/interpreter/source/eval.cpp index d1c30d1..9e31a83 100644 --- a/libs/interpreter/source/eval.cpp +++ b/libs/interpreter/source/eval.cpp @@ -441,7 +441,7 @@ namespace pslang::interpreter } else if constexpr (std::is_same_v) { - return primitive_value(primitive_value_base{{static_cast(value.value)}}); + return primitive_value(primitive_value_base{types::half_float::from_float(static_cast(value.value))}); } else { diff --git a/libs/jit/source/arch/macos_aarch64/compiler.cpp b/libs/jit/source/arch/macos_aarch64/compiler.cpp index 02ef5d8..0e8320c 100644 --- a/libs/jit/source/arch/macos_aarch64/compiler.cpp +++ b/libs/jit/source/arch/macos_aarch64/compiler.cpp @@ -152,6 +152,8 @@ namespace pslang::jit::macos_aarch64 { lcontext.f16_constants[f16_literal->value.repr] = pcontext.storage.size(); push_bytes(f16_literal->value.repr); + // Ensure 4-byte alignment + push_bytes(std::uint16_t{0}); } else if (auto f32_literal = std::get_if(&node.value)) { @@ -235,8 +237,8 @@ namespace pslang::jit::macos_aarch64 { auto offset = lcontext.f16_constants.at(node.value.repr); std::int32_t current = pcontext.storage.size(); - builder.ldr_fp_pc(0, 0, (offset - current) / 4); - builder.fcvt(0, 0b10, 0, 0b01); + builder.adr(0, (offset - current) / 4); + builder.ldr_fp(0, 1, 0, 0); } void operator()(ast::f32_literal const & node) diff --git a/libs/parser/rules/pslang.y b/libs/parser/rules/pslang.y index 41eb24a..c023a3d 100644 --- a/libs/parser/rules/pslang.y +++ b/libs/parser/rules/pslang.y @@ -48,7 +48,7 @@ template T value; #ifdef __clang__ if constexpr (std::is_same_v) - value = {float(std::atof(str.data()))}; + value = value.from_float(float(std::atof(str.data()))); else if constexpr (std::is_floating_point_v) value = std::atof(str.data()); else if constexpr (std::is_signed_v) diff --git a/libs/types/include/pslang/types/half_float.hpp b/libs/types/include/pslang/types/half_float.hpp index 8141a9a..4342791 100644 --- a/libs/types/include/pslang/types/half_float.hpp +++ b/libs/types/include/pslang/types/half_float.hpp @@ -1,8 +1,7 @@ #pragma once -#include - #include +#include namespace pslang::types { @@ -13,35 +12,20 @@ namespace pslang::types struct half_float { - float repr = 0.f; + using repr_type = std::uint16_t; + repr_type repr; - friend bool operator == (half_float const &, half_float const &) = default; - friend auto operator <=> (half_float const &, half_float const &) = default; + static float to_float(half_float value); + static half_float from_float(float value); }; - inline half_float operator - (half_float f) - { - return {-f.repr}; - } + bool operator == (half_float const &, half_float const &); + std::partial_ordering operator <=> (half_float const &, half_float const &); - inline half_float operator + (half_float f1, half_float f2) - { - return {f1.repr + f2.repr}; - } - - inline half_float operator - (half_float f1, half_float f2) - { - return {f1.repr - f2.repr}; - } - - inline half_float operator * (half_float f1, half_float f2) - { - return {f1.repr * f2.repr}; - } - - inline half_float operator / (half_float f1, half_float f2) - { - return {f1.repr / f2.repr}; - } + half_float operator - (half_float f); + half_float operator + (half_float f1, half_float f2); + half_float operator - (half_float f1, half_float f2); + half_float operator * (half_float f1, half_float f2); + half_float operator / (half_float f1, half_float f2); } diff --git a/libs/types/source/half_float.cpp b/libs/types/source/half_float.cpp new file mode 100644 index 0000000..f44f313 --- /dev/null +++ b/libs/types/source/half_float.cpp @@ -0,0 +1,167 @@ +#include + +#include +#include + +namespace pslang::types +{ + + using repr_type = half_float::repr_type; + + namespace + { + + constexpr repr_type SIGN_BIT = 0x8000u; + constexpr repr_type EXPONENT_MASK = 0x7c00u; + constexpr repr_type MANTISSA_MASK = 0x03ff; + + bool is_nan(half_float value) + { + return ((value.repr & EXPONENT_MASK) == EXPONENT_MASK) && ((value.repr & MANTISSA_MASK) != 0); + } + + bool is_zero(half_float value) + { + return (value.repr & (~SIGN_BIT)) == 0; + } + + repr_type ordered(half_float value) + { + return ((value.repr & SIGN_BIT) != 0) ? ~value.repr : (value.repr ^ SIGN_BIT); + } + + } + + bool operator == (half_float const & lhs, half_float const & rhs) + { + if (is_nan(lhs) || is_nan(rhs)) [[unlikely]] + return false; + + if (is_zero(lhs) && is_zero(rhs)) + return true; + + return lhs == rhs; + } + + std::partial_ordering operator <=> (half_float const & lhs, half_float const & rhs) + { + if (is_nan(lhs) || is_nan(rhs)) [[unlikely]] + return std::partial_ordering::unordered; + + if (is_zero(lhs) && is_zero(rhs)) + return std::partial_ordering::equivalent; + + return ordered(lhs) <=> ordered(rhs); + } + + float half_float::to_float(half_float value) + { + std::uint32_t sign = (value.repr & SIGN_BIT) >> 15; + std::uint32_t exponent = (value.repr & EXPONENT_MASK) >> 10; + std::uint32_t mantissa = value.repr & MANTISSA_MASK; + + if (exponent == 0) + { + // Zero + if (mantissa == 0) [[likely]] + return std::bit_cast(sign << 31); + + // Subnormal + int shift = -14; + + while ((mantissa & 0x400) == 0) + { + mantissa <<= 1; + --shift; + } + + mantissa &= 0x3ff; + + return std::bit_cast((sign << 31) | ((shift + 127) << 23) | (mantissa << 13)); + } + + // Extend exponent for infinity & NaNs to all ones + if (exponent == 0x1fu) [[unlikely]] + exponent = 0xffu; + + // Shifting NaN mantissa bits is mostly useless, + // but at least we preserve quiet vs signaling NaNs + return std::bit_cast((sign << 31) | ((exponent - 15 + 127) << 23) | (mantissa << 13)); + } + + half_float half_float::from_float(float value) + { + uint32_t repr; + std::memcpy(&repr, &value, sizeof(repr)); + + std::uint32_t sign = (repr >> 16) & 0x8000; + std::uint32_t mantissa = repr & 0x007FFFFF; + std::int32_t exponent = ((repr >> 23) & 0xFF) - 127 + 15; + + if (exponent <= 0) + { + if (exponent < -10) + return {repr_type(sign)}; + + mantissa |= 0x00800000; + + uint32_t t = mantissa >> (1 - exponent); + + // Rounding + if (t & 0x00001000) + t += 0x00002000; + + return {repr_type(sign | (t >> 13))}; + } + + if (exponent >= 31) + { + // Infinities + if (mantissa == 0) + return {repr_type(sign | 0x7C00)}; + + // NaNs + return {repr_type(sign | 0x7E00)}; + } + + // Rounding + mantissa += 0x00001000; + + if (mantissa & 0x00800000) + { + mantissa = 0; + exponent++; + } + + if (exponent >= 31) + return {repr_type(sign | 0x7C00)}; + + return {repr_type(sign | (exponent << 10) | (mantissa >> 13))}; + } + + inline half_float operator - (half_float f) + { + return {repr_type(f.repr ^ SIGN_BIT)}; + } + + inline half_float operator + (half_float f1, half_float f2) + { + return {half_float::from_float(half_float::to_float(f1) + half_float::to_float(f2))}; + } + + inline half_float operator - (half_float f1, half_float f2) + { + return {half_float::from_float(half_float::to_float(f1) - half_float::to_float(f2))}; + } + + inline half_float operator * (half_float f1, half_float f2) + { + return {half_float::from_float(half_float::to_float(f1) * half_float::to_float(f2))}; + } + + inline half_float operator / (half_float f1, half_float f2) + { + return {half_float::from_float(half_float::to_float(f1) / half_float::to_float(f2))}; + } + +} \ No newline at end of file