Add software implementation of float16
This commit is contained in:
parent
e56242ca62
commit
649574c573
5 changed files with 185 additions and 32 deletions
|
|
@ -441,7 +441,7 @@ namespace pslang::interpreter
|
|||
}
|
||||
else if constexpr (std::is_same_v<H, types::half_float>)
|
||||
{
|
||||
return primitive_value(primitive_value_base<H>{{static_cast<float>(value.value)}});
|
||||
return primitive_value(primitive_value_base<H>{types::half_float::from_float(static_cast<float>(value.value))});
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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<ast::f32_literal>(&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)
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ template <typename T>
|
|||
T value;
|
||||
#ifdef __clang__
|
||||
if constexpr (std::is_same_v<T, ::pslang::types::half_float>)
|
||||
value = {float(std::atof(str.data()))};
|
||||
value = value.from_float(float(std::atof(str.data())));
|
||||
else if constexpr (std::is_floating_point_v<T>)
|
||||
value = std::atof(str.data());
|
||||
else if constexpr (std::is_signed_v<T>)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <pslang/types/half_float.hpp>
|
||||
|
||||
#include <compare>
|
||||
#include <cstdint>
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
|
|
|
|||
167
libs/types/source/half_float.cpp
Normal file
167
libs/types/source/half_float.cpp
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
#include <pslang/types/half_float.hpp>
|
||||
|
||||
#include <bit>
|
||||
#include <cstring>
|
||||
|
||||
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<float>(sign << 31);
|
||||
|
||||
// Subnormal
|
||||
int shift = -14;
|
||||
|
||||
while ((mantissa & 0x400) == 0)
|
||||
{
|
||||
mantissa <<= 1;
|
||||
--shift;
|
||||
}
|
||||
|
||||
mantissa &= 0x3ff;
|
||||
|
||||
return std::bit_cast<float>((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<float>((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))};
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue