| author | |
| committer | |
| log | fd75e73ee9818f12fd81d8fdb3cb949c492d664a |
| tree | 84ee56a2ef7a65a0bbb3a7dd12f015970b4ed725 |
| parent | 1f45075a0e1d86fa110011f6cedbef61a9f6f056 |
Add support for half-precision floating point operations.
Introduce `__extendhfsf2` and `__truncsfhf2` in std/special/compiler_rt.
Add `__gnu_h2f_ieee` and `__gnu_f2h_ieee` as aliases that are used in
Windows builds.
The logic in std/special/compiler_rt/extendXfYf2.zig has been reworked
and can now operate on 16 bits floating point types.
`extendXfYf2()` and `truncXfYf2()` are marked `inline` to work around
a not entirely understood stack alignment issue on Windows when calling
the f16 versions of the builtins.
closes #112216 files changed, 505 insertions(+), 35 deletions(-)
CMakeLists.txt+16| ... | ... | @@ -261,12 +261,15 @@ endif() |
| 261 | 261 | set(EMBEDDED_SOFTFLOAT_SOURCES |
| 262 | 262 | "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/f128M_isSignalingNaN.c" |
| 263 | 263 | "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_commonNaNToF128M.c" |
| 264 | "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_commonNaNToF16UI.c" | |
| 264 | 265 | "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_commonNaNToF32UI.c" |
| 265 | 266 | "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_commonNaNToF64UI.c" |
| 266 | 267 | "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_f128MToCommonNaN.c" |
| 268 | "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_f16UIToCommonNaN.c" | |
| 267 | 269 | "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_f32UIToCommonNaN.c" |
| 268 | 270 | "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_f64UIToCommonNaN.c" |
| 269 | 271 | "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_propagateNaNF128M.c" |
| 272 | "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_propagateNaNF16UI.c" | |
| 270 | 273 | "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/softfloat_raiseFlags.c" |
| 271 | 274 | "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f128M_add.c" |
| 272 | 275 | "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f128M_div.c" |
| ... | ... | @@ -293,8 +296,20 @@ set(EMBEDDED_SOFTFLOAT_SOURCES |
| 293 | 296 | "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f128M_to_ui32_r_minMag.c" |
| 294 | 297 | "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f128M_to_ui64.c" |
| 295 | 298 | "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f128M_to_ui64_r_minMag.c" |
| 299 | "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_add.c" | |
| 300 | "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_div.c" | |
| 301 | "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_eq.c" | |
| 302 | "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_lt.c" | |
| 303 | "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_mul.c" | |
| 304 | "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_rem.c" | |
| 305 | "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_roundToInt.c" | |
| 306 | "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_sqrt.c" | |
| 307 | "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_sub.c" | |
| 308 | "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_to_f128M.c" | |
| 309 | "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_to_f64.c" | |
| 296 | 310 | "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f32_to_f128M.c" |
| 297 | 311 | "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f64_to_f128M.c" |
| 312 | "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f64_to_f16.c" | |
| 298 | 313 | "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_add256M.c" |
| 299 | 314 | "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_addCarryM.c" |
| 300 | 315 | "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_addComplCarryM.c" |
| ... | ... | @@ -572,6 +587,7 @@ set(ZIG_STD_FILES |
| 572 | 587 | "special/compiler_rt/floatuntidf.zig" |
| 573 | 588 | "special/compiler_rt/muloti4.zig" |
| 574 | 589 | "special/compiler_rt/index.zig" |
| 590 | "special/compiler_rt/truncXfYf2.zig" | |
| 575 | 591 | "special/compiler_rt/udivmod.zig" |
| 576 | 592 | "special/compiler_rt/udivmoddi4.zig" |
| 577 | 593 | "special/compiler_rt/udivmodti4.zig" |
src/all_types.hpp+2| ... | ... | @@ -258,6 +258,7 @@ struct ConstExprValue { |
| 258 | 258 | // populated if special == ConstValSpecialStatic |
| 259 | 259 | BigInt x_bigint; |
| 260 | 260 | BigFloat x_bigfloat; |
| 261 | float16_t x_f16; | |
| 261 | 262 | float x_f32; |
| 262 | 263 | double x_f64; |
| 263 | 264 | float128_t x_f128; |
| ... | ... | @@ -1598,6 +1599,7 @@ struct CodeGen { |
| 1598 | 1599 | TypeTableEntry *entry_i128; |
| 1599 | 1600 | TypeTableEntry *entry_isize; |
| 1600 | 1601 | TypeTableEntry *entry_usize; |
| 1602 | TypeTableEntry *entry_f16; | |
| 1601 | 1603 | TypeTableEntry *entry_f32; |
| 1602 | 1604 | TypeTableEntry *entry_f64; |
| 1603 | 1605 | TypeTableEntry *entry_f128; |
src/analyze.cpp+15| ... | ... | @@ -4668,6 +4668,13 @@ static uint32_t hash_const_val(ConstExprValue *const_val) { |
| 4668 | 4668 | } |
| 4669 | 4669 | case TypeTableEntryIdFloat: |
| 4670 | 4670 | switch (const_val->type->data.floating.bit_count) { |
| 4671 | case 16: | |
| 4672 | { | |
| 4673 | uint16_t result; | |
| 4674 | static_assert(sizeof(result) == sizeof(const_val->data.x_f16), ""); | |
| 4675 | memcpy(&result, &const_val->data.x_f16, sizeof(result)); | |
| 4676 | return result * 65537u; | |
| 4677 | } | |
| 4671 | 4678 | case 32: |
| 4672 | 4679 | { |
| 4673 | 4680 | uint32_t result; |
| ... | ... | @@ -5128,6 +5135,9 @@ void init_const_float(ConstExprValue *const_val, TypeTableEntry *type, double va |
| 5128 | 5135 | bigfloat_init_64(&const_val->data.x_bigfloat, value); |
| 5129 | 5136 | } else if (type->id == TypeTableEntryIdFloat) { |
| 5130 | 5137 | switch (type->data.floating.bit_count) { |
| 5138 | case 16: | |
| 5139 | const_val->data.x_f16 = zig_double_to_f16(value); | |
| 5140 | break; | |
| 5131 | 5141 | case 32: |
| 5132 | 5142 | const_val->data.x_f32 = value; |
| 5133 | 5143 | break; |
| ... | ... | @@ -5441,6 +5451,8 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) { |
| 5441 | 5451 | case TypeTableEntryIdFloat: |
| 5442 | 5452 | assert(a->type->data.floating.bit_count == b->type->data.floating.bit_count); |
| 5443 | 5453 | switch (a->type->data.floating.bit_count) { |
| 5454 | case 16: | |
| 5455 | return f16_eq(a->data.x_f16, b->data.x_f16); | |
| 5444 | 5456 | case 32: |
| 5445 | 5457 | return a->data.x_f32 == b->data.x_f32; |
| 5446 | 5458 | case 64: |
| ... | ... | @@ -5614,6 +5626,9 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { |
| 5614 | 5626 | return; |
| 5615 | 5627 | case TypeTableEntryIdFloat: |
| 5616 | 5628 | switch (type_entry->data.floating.bit_count) { |
| 5629 | case 16: | |
| 5630 | buf_appendf(buf, "%f", zig_f16_to_double(const_val->data.x_f16)); | |
| 5631 | return; | |
| 5617 | 5632 | case 32: |
| 5618 | 5633 | buf_appendf(buf, "%f", const_val->data.x_f32); |
| 5619 | 5634 | return; |
src/bigfloat.cpp+8| ... | ... | @@ -18,6 +18,10 @@ void bigfloat_init_128(BigFloat *dest, float128_t x) { |
| 18 | 18 | dest->value = x; |
| 19 | 19 | } |
| 20 | 20 | |
| 21 | void bigfloat_init_16(BigFloat *dest, float16_t x) { | |
| 22 | f16_to_f128M(x, &dest->value); | |
| 23 | } | |
| 24 | ||
| 21 | 25 | void bigfloat_init_32(BigFloat *dest, float x) { |
| 22 | 26 | float32_t f32_val; |
| 23 | 27 | memcpy(&f32_val, &x, sizeof(float)); |
| ... | ... | @@ -146,6 +150,10 @@ Cmp bigfloat_cmp(const BigFloat *op1, const BigFloat *op2) { |
| 146 | 150 | } |
| 147 | 151 | } |
| 148 | 152 | |
| 153 | float16_t bigfloat_to_f16(const BigFloat *bigfloat) { | |
| 154 | return f128M_to_f16(&bigfloat->value); | |
| 155 | } | |
| 156 | ||
| 149 | 157 | float bigfloat_to_f32(const BigFloat *bigfloat) { |
| 150 | 158 | float32_t f32_value = f128M_to_f32(&bigfloat->value); |
| 151 | 159 | float result; |
src/bigfloat.hpp+2| ... | ... | @@ -22,6 +22,7 @@ struct BigFloat { |
| 22 | 22 | |
| 23 | 23 | struct Buf; |
| 24 | 24 | |
| 25 | void bigfloat_init_16(BigFloat *dest, float16_t x); | |
| 25 | 26 | void bigfloat_init_32(BigFloat *dest, float x); |
| 26 | 27 | void bigfloat_init_64(BigFloat *dest, double x); |
| 27 | 28 | void bigfloat_init_128(BigFloat *dest, float128_t x); |
| ... | ... | @@ -29,6 +30,7 @@ void bigfloat_init_bigfloat(BigFloat *dest, const BigFloat *x); |
| 29 | 30 | void bigfloat_init_bigint(BigFloat *dest, const BigInt *op); |
| 30 | 31 | int bigfloat_init_buf_base10(BigFloat *dest, const uint8_t *buf_ptr, size_t buf_len); |
| 31 | 32 | |
| 33 | float16_t bigfloat_to_f16(const BigFloat *bigfloat); | |
| 32 | 34 | float bigfloat_to_f32(const BigFloat *bigfloat); |
| 33 | 35 | double bigfloat_to_f64(const BigFloat *bigfloat); |
| 34 | 36 | float128_t bigfloat_to_f128(const BigFloat *bigfloat); |
src/codegen.cpp+4| ... | ... | @@ -17,6 +17,7 @@ |
| 17 | 17 | #include "os.hpp" |
| 18 | 18 | #include "translate_c.hpp" |
| 19 | 19 | #include "target.hpp" |
| 20 | #include "util.hpp" | |
| 20 | 21 | #include "zig_llvm.h" |
| 21 | 22 | |
| 22 | 23 | #include <stdio.h> |
| ... | ... | @@ -5211,6 +5212,8 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c |
| 5211 | 5212 | const_val->data.x_err_set->value, false); |
| 5212 | 5213 | case TypeTableEntryIdFloat: |
| 5213 | 5214 | switch (type_entry->data.floating.bit_count) { |
| 5215 | case 16: | |
| 5216 | return LLVMConstReal(type_entry->type_ref, zig_f16_to_double(const_val->data.x_f16)); | |
| 5214 | 5217 | case 32: |
| 5215 | 5218 | return LLVMConstReal(type_entry->type_ref, const_val->data.x_f32); |
| 5216 | 5219 | case 64: |
| ... | ... | @@ -6195,6 +6198,7 @@ static void define_builtin_types(CodeGen *g) { |
| 6195 | 6198 | *field = entry; |
| 6196 | 6199 | g->primitive_type_table.put(&entry->name, entry); |
| 6197 | 6200 | }; |
| 6201 | add_fp_entry(g, "f16", 16, LLVMHalfType(), &g->builtin_types.entry_f16); | |
| 6198 | 6202 | add_fp_entry(g, "f32", 32, LLVMFloatType(), &g->builtin_types.entry_f32); |
| 6199 | 6203 | add_fp_entry(g, "f64", 64, LLVMDoubleType(), &g->builtin_types.entry_f64); |
| 6200 | 6204 | add_fp_entry(g, "f128", 128, LLVMFP128Type(), &g->builtin_types.entry_f128); |
src/ir.cpp+149-2| ... | ... | @@ -11,9 +11,10 @@ |
| 11 | 11 | #include "ir.hpp" |
| 12 | 12 | #include "ir_print.hpp" |
| 13 | 13 | #include "os.hpp" |
| 14 | #include "translate_c.hpp" | |
| 15 | 14 | #include "range_set.hpp" |
| 16 | 15 | #include "softfloat.hpp" |
| 16 | #include "translate_c.hpp" | |
| 17 | #include "util.hpp" | |
| 17 | 18 | |
| 18 | 19 | struct IrExecContext { |
| 19 | 20 | ConstExprValue *mem_slot_list; |
| ... | ... | @@ -7238,6 +7239,11 @@ static bool float_has_fraction(ConstExprValue *const_val) { |
| 7238 | 7239 | return bigfloat_has_fraction(&const_val->data.x_bigfloat); |
| 7239 | 7240 | } else if (const_val->type->id == TypeTableEntryIdFloat) { |
| 7240 | 7241 | switch (const_val->type->data.floating.bit_count) { |
| 7242 | case 16: | |
| 7243 | { | |
| 7244 | float16_t floored = f16_roundToInt(const_val->data.x_f16, softfloat_round_minMag, false); | |
| 7245 | return !f16_eq(floored, const_val->data.x_f16); | |
| 7246 | } | |
| 7241 | 7247 | case 32: |
| 7242 | 7248 | return floorf(const_val->data.x_f32) != const_val->data.x_f32; |
| 7243 | 7249 | case 64: |
| ... | ... | @@ -7261,6 +7267,9 @@ static void float_append_buf(Buf *buf, ConstExprValue *const_val) { |
| 7261 | 7267 | bigfloat_append_buf(buf, &const_val->data.x_bigfloat); |
| 7262 | 7268 | } else if (const_val->type->id == TypeTableEntryIdFloat) { |
| 7263 | 7269 | switch (const_val->type->data.floating.bit_count) { |
| 7270 | case 16: | |
| 7271 | buf_appendf(buf, "%f", zig_f16_to_double(const_val->data.x_f16)); | |
| 7272 | break; | |
| 7264 | 7273 | case 32: |
| 7265 | 7274 | buf_appendf(buf, "%f", const_val->data.x_f32); |
| 7266 | 7275 | break; |
| ... | ... | @@ -7296,6 +7305,17 @@ static void float_init_bigint(BigInt *bigint, ConstExprValue *const_val) { |
| 7296 | 7305 | bigint_init_bigfloat(bigint, &const_val->data.x_bigfloat); |
| 7297 | 7306 | } else if (const_val->type->id == TypeTableEntryIdFloat) { |
| 7298 | 7307 | switch (const_val->type->data.floating.bit_count) { |
| 7308 | case 16: | |
| 7309 | { | |
| 7310 | double x = zig_f16_to_double(const_val->data.x_f16); | |
| 7311 | if (x >= 0) { | |
| 7312 | bigint_init_unsigned(bigint, (uint64_t)x); | |
| 7313 | } else { | |
| 7314 | bigint_init_unsigned(bigint, (uint64_t)-x); | |
| 7315 | bigint->is_negative = true; | |
| 7316 | } | |
| 7317 | break; | |
| 7318 | } | |
| 7299 | 7319 | case 32: |
| 7300 | 7320 | if (const_val->data.x_f32 >= 0) { |
| 7301 | 7321 | bigint_init_unsigned(bigint, (uint64_t)(const_val->data.x_f32)); |
| ... | ... | @@ -7332,6 +7352,9 @@ static void float_init_bigfloat(ConstExprValue *dest_val, BigFloat *bigfloat) { |
| 7332 | 7352 | bigfloat_init_bigfloat(&dest_val->data.x_bigfloat, bigfloat); |
| 7333 | 7353 | } else if (dest_val->type->id == TypeTableEntryIdFloat) { |
| 7334 | 7354 | switch (dest_val->type->data.floating.bit_count) { |
| 7355 | case 16: | |
| 7356 | dest_val->data.x_f16 = bigfloat_to_f16(bigfloat); | |
| 7357 | break; | |
| 7335 | 7358 | case 32: |
| 7336 | 7359 | dest_val->data.x_f32 = bigfloat_to_f32(bigfloat); |
| 7337 | 7360 | break; |
| ... | ... | @@ -7349,11 +7372,39 @@ static void float_init_bigfloat(ConstExprValue *dest_val, BigFloat *bigfloat) { |
| 7349 | 7372 | } |
| 7350 | 7373 | } |
| 7351 | 7374 | |
| 7375 | static void float_init_f16(ConstExprValue *dest_val, float16_t x) { | |
| 7376 | if (dest_val->type->id == TypeTableEntryIdComptimeFloat) { | |
| 7377 | bigfloat_init_16(&dest_val->data.x_bigfloat, x); | |
| 7378 | } else if (dest_val->type->id == TypeTableEntryIdFloat) { | |
| 7379 | switch (dest_val->type->data.floating.bit_count) { | |
| 7380 | case 16: | |
| 7381 | dest_val->data.x_f16 = x; | |
| 7382 | break; | |
| 7383 | case 32: | |
| 7384 | dest_val->data.x_f32 = zig_f16_to_double(x); | |
| 7385 | break; | |
| 7386 | case 64: | |
| 7387 | dest_val->data.x_f64 = zig_f16_to_double(x); | |
| 7388 | break; | |
| 7389 | case 128: | |
| 7390 | f16_to_f128M(x, &dest_val->data.x_f128); | |
| 7391 | break; | |
| 7392 | default: | |
| 7393 | zig_unreachable(); | |
| 7394 | } | |
| 7395 | } else { | |
| 7396 | zig_unreachable(); | |
| 7397 | } | |
| 7398 | } | |
| 7399 | ||
| 7352 | 7400 | static void float_init_f32(ConstExprValue *dest_val, float x) { |
| 7353 | 7401 | if (dest_val->type->id == TypeTableEntryIdComptimeFloat) { |
| 7354 | 7402 | bigfloat_init_32(&dest_val->data.x_bigfloat, x); |
| 7355 | 7403 | } else if (dest_val->type->id == TypeTableEntryIdFloat) { |
| 7356 | 7404 | switch (dest_val->type->data.floating.bit_count) { |
| 7405 | case 16: | |
| 7406 | dest_val->data.x_f16 = zig_double_to_f16(x); | |
| 7407 | break; | |
| 7357 | 7408 | case 32: |
| 7358 | 7409 | dest_val->data.x_f32 = x; |
| 7359 | 7410 | break; |
| ... | ... | @@ -7380,6 +7431,9 @@ static void float_init_f64(ConstExprValue *dest_val, double x) { |
| 7380 | 7431 | bigfloat_init_64(&dest_val->data.x_bigfloat, x); |
| 7381 | 7432 | } else if (dest_val->type->id == TypeTableEntryIdFloat) { |
| 7382 | 7433 | switch (dest_val->type->data.floating.bit_count) { |
| 7434 | case 16: | |
| 7435 | dest_val->data.x_f16 = zig_double_to_f16(x); | |
| 7436 | break; | |
| 7383 | 7437 | case 32: |
| 7384 | 7438 | dest_val->data.x_f32 = x; |
| 7385 | 7439 | break; |
| ... | ... | @@ -7406,6 +7460,9 @@ static void float_init_f128(ConstExprValue *dest_val, float128_t x) { |
| 7406 | 7460 | bigfloat_init_128(&dest_val->data.x_bigfloat, x); |
| 7407 | 7461 | } else if (dest_val->type->id == TypeTableEntryIdFloat) { |
| 7408 | 7462 | switch (dest_val->type->data.floating.bit_count) { |
| 7463 | case 16: | |
| 7464 | dest_val->data.x_f16 = f128M_to_f16(&x); | |
| 7465 | break; | |
| 7409 | 7466 | case 32: |
| 7410 | 7467 | { |
| 7411 | 7468 | float32_t f32_val = f128M_to_f32(&x); |
| ... | ... | @@ -7436,6 +7493,9 @@ static void float_init_float(ConstExprValue *dest_val, ConstExprValue *src_val) |
| 7436 | 7493 | float_init_bigfloat(dest_val, &src_val->data.x_bigfloat); |
| 7437 | 7494 | } else if (src_val->type->id == TypeTableEntryIdFloat) { |
| 7438 | 7495 | switch (src_val->type->data.floating.bit_count) { |
| 7496 | case 16: | |
| 7497 | float_init_f16(dest_val, src_val->data.x_f16); | |
| 7498 | break; | |
| 7439 | 7499 | case 32: |
| 7440 | 7500 | float_init_f32(dest_val, src_val->data.x_f32); |
| 7441 | 7501 | break; |
| ... | ... | @@ -7459,6 +7519,14 @@ static Cmp float_cmp(ConstExprValue *op1, ConstExprValue *op2) { |
| 7459 | 7519 | return bigfloat_cmp(&op1->data.x_bigfloat, &op2->data.x_bigfloat); |
| 7460 | 7520 | } else if (op1->type->id == TypeTableEntryIdFloat) { |
| 7461 | 7521 | switch (op1->type->data.floating.bit_count) { |
| 7522 | case 16: | |
| 7523 | if (f16_lt(op1->data.x_f16, op2->data.x_f16)) { | |
| 7524 | return CmpLT; | |
| 7525 | } else if (f16_lt(op2->data.x_f16, op1->data.x_f16)) { | |
| 7526 | return CmpGT; | |
| 7527 | } else { | |
| 7528 | return CmpEQ; | |
| 7529 | } | |
| 7462 | 7530 | case 32: |
| 7463 | 7531 | if (op1->data.x_f32 > op2->data.x_f32) { |
| 7464 | 7532 | return CmpGT; |
| ... | ... | @@ -7496,6 +7564,17 @@ static Cmp float_cmp_zero(ConstExprValue *op) { |
| 7496 | 7564 | return bigfloat_cmp_zero(&op->data.x_bigfloat); |
| 7497 | 7565 | } else if (op->type->id == TypeTableEntryIdFloat) { |
| 7498 | 7566 | switch (op->type->data.floating.bit_count) { |
| 7567 | case 16: | |
| 7568 | { | |
| 7569 | const float16_t zero = zig_double_to_f16(0); | |
| 7570 | if (f16_lt(op->data.x_f16, zero)) { | |
| 7571 | return CmpLT; | |
| 7572 | } else if (f16_lt(zero, op->data.x_f16)) { | |
| 7573 | return CmpGT; | |
| 7574 | } else { | |
| 7575 | return CmpEQ; | |
| 7576 | } | |
| 7577 | } | |
| 7499 | 7578 | case 32: |
| 7500 | 7579 | if (op->data.x_f32 < 0.0) { |
| 7501 | 7580 | return CmpLT; |
| ... | ... | @@ -7537,6 +7616,9 @@ static void float_add(ConstExprValue *out_val, ConstExprValue *op1, ConstExprVal |
| 7537 | 7616 | bigfloat_add(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); |
| 7538 | 7617 | } else if (op1->type->id == TypeTableEntryIdFloat) { |
| 7539 | 7618 | switch (op1->type->data.floating.bit_count) { |
| 7619 | case 16: | |
| 7620 | out_val->data.x_f16 = f16_add(op1->data.x_f16, op2->data.x_f16); | |
| 7621 | return; | |
| 7540 | 7622 | case 32: |
| 7541 | 7623 | out_val->data.x_f32 = op1->data.x_f32 + op2->data.x_f32; |
| 7542 | 7624 | return; |
| ... | ... | @@ -7561,6 +7643,9 @@ static void float_sub(ConstExprValue *out_val, ConstExprValue *op1, ConstExprVal |
| 7561 | 7643 | bigfloat_sub(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); |
| 7562 | 7644 | } else if (op1->type->id == TypeTableEntryIdFloat) { |
| 7563 | 7645 | switch (op1->type->data.floating.bit_count) { |
| 7646 | case 16: | |
| 7647 | out_val->data.x_f16 = f16_sub(op1->data.x_f16, op2->data.x_f16); | |
| 7648 | return; | |
| 7564 | 7649 | case 32: |
| 7565 | 7650 | out_val->data.x_f32 = op1->data.x_f32 - op2->data.x_f32; |
| 7566 | 7651 | return; |
| ... | ... | @@ -7585,6 +7670,9 @@ static void float_mul(ConstExprValue *out_val, ConstExprValue *op1, ConstExprVal |
| 7585 | 7670 | bigfloat_mul(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); |
| 7586 | 7671 | } else if (op1->type->id == TypeTableEntryIdFloat) { |
| 7587 | 7672 | switch (op1->type->data.floating.bit_count) { |
| 7673 | case 16: | |
| 7674 | out_val->data.x_f16 = f16_mul(op1->data.x_f16, op2->data.x_f16); | |
| 7675 | return; | |
| 7588 | 7676 | case 32: |
| 7589 | 7677 | out_val->data.x_f32 = op1->data.x_f32 * op2->data.x_f32; |
| 7590 | 7678 | return; |
| ... | ... | @@ -7609,6 +7697,9 @@ static void float_div(ConstExprValue *out_val, ConstExprValue *op1, ConstExprVal |
| 7609 | 7697 | bigfloat_div(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); |
| 7610 | 7698 | } else if (op1->type->id == TypeTableEntryIdFloat) { |
| 7611 | 7699 | switch (op1->type->data.floating.bit_count) { |
| 7700 | case 16: | |
| 7701 | out_val->data.x_f16 = f16_div(op1->data.x_f16, op2->data.x_f16); | |
| 7702 | return; | |
| 7612 | 7703 | case 32: |
| 7613 | 7704 | out_val->data.x_f32 = op1->data.x_f32 / op2->data.x_f32; |
| 7614 | 7705 | return; |
| ... | ... | @@ -7633,6 +7724,19 @@ static void float_div_trunc(ConstExprValue *out_val, ConstExprValue *op1, ConstE |
| 7633 | 7724 | bigfloat_div_trunc(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); |
| 7634 | 7725 | } else if (op1->type->id == TypeTableEntryIdFloat) { |
| 7635 | 7726 | switch (op1->type->data.floating.bit_count) { |
| 7727 | case 16: | |
| 7728 | { | |
| 7729 | double a = zig_f16_to_double(op1->data.x_f16); | |
| 7730 | double b = zig_f16_to_double(op2->data.x_f16); | |
| 7731 | double c = a / b; | |
| 7732 | if (c >= 0.0) { | |
| 7733 | c = floor(c); | |
| 7734 | } else { | |
| 7735 | c = ceil(c); | |
| 7736 | } | |
| 7737 | out_val->data.x_f16 = zig_double_to_f16(c); | |
| 7738 | return; | |
| 7739 | } | |
| 7636 | 7740 | case 32: |
| 7637 | 7741 | out_val->data.x_f32 = op1->data.x_f32 / op2->data.x_f32; |
| 7638 | 7742 | if (out_val->data.x_f32 >= 0.0) { |
| ... | ... | @@ -7668,6 +7772,10 @@ static void float_div_floor(ConstExprValue *out_val, ConstExprValue *op1, ConstE |
| 7668 | 7772 | bigfloat_div_floor(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); |
| 7669 | 7773 | } else if (op1->type->id == TypeTableEntryIdFloat) { |
| 7670 | 7774 | switch (op1->type->data.floating.bit_count) { |
| 7775 | case 16: | |
| 7776 | out_val->data.x_f16 = f16_div(op1->data.x_f16, op2->data.x_f16); | |
| 7777 | out_val->data.x_f16 = f16_roundToInt(out_val->data.x_f16, softfloat_round_min, false); | |
| 7778 | return; | |
| 7671 | 7779 | case 32: |
| 7672 | 7780 | out_val->data.x_f32 = floorf(op1->data.x_f32 / op2->data.x_f32); |
| 7673 | 7781 | return; |
| ... | ... | @@ -7693,6 +7801,9 @@ static void float_rem(ConstExprValue *out_val, ConstExprValue *op1, ConstExprVal |
| 7693 | 7801 | bigfloat_rem(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); |
| 7694 | 7802 | } else if (op1->type->id == TypeTableEntryIdFloat) { |
| 7695 | 7803 | switch (op1->type->data.floating.bit_count) { |
| 7804 | case 16: | |
| 7805 | out_val->data.x_f16 = f16_rem(op1->data.x_f16, op2->data.x_f16); | |
| 7806 | return; | |
| 7696 | 7807 | case 32: |
| 7697 | 7808 | out_val->data.x_f32 = fmodf(op1->data.x_f32, op2->data.x_f32); |
| 7698 | 7809 | return; |
| ... | ... | @@ -7710,6 +7821,16 @@ static void float_rem(ConstExprValue *out_val, ConstExprValue *op1, ConstExprVal |
| 7710 | 7821 | } |
| 7711 | 7822 | } |
| 7712 | 7823 | |
| 7824 | // c = a - b * trunc(a / b) | |
| 7825 | static float16_t zig_f16_mod(float16_t a, float16_t b) { | |
| 7826 | float16_t c; | |
| 7827 | c = f16_div(a, b); | |
| 7828 | c = f16_roundToInt(c, softfloat_round_min, true); | |
| 7829 | c = f16_mul(b, c); | |
| 7830 | c = f16_sub(a, c); | |
| 7831 | return c; | |
| 7832 | } | |
| 7833 | ||
| 7713 | 7834 | // c = a - b * trunc(a / b) |
| 7714 | 7835 | static void zig_f128M_mod(const float128_t* a, const float128_t* b, float128_t* c) { |
| 7715 | 7836 | f128M_div(a, b, c); |
| ... | ... | @@ -7725,6 +7846,9 @@ static void float_mod(ConstExprValue *out_val, ConstExprValue *op1, ConstExprVal |
| 7725 | 7846 | bigfloat_mod(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); |
| 7726 | 7847 | } else if (op1->type->id == TypeTableEntryIdFloat) { |
| 7727 | 7848 | switch (op1->type->data.floating.bit_count) { |
| 7849 | case 16: | |
| 7850 | out_val->data.x_f16 = zig_f16_mod(op1->data.x_f16, op2->data.x_f16); | |
| 7851 | return; | |
| 7728 | 7852 | case 32: |
| 7729 | 7853 | out_val->data.x_f32 = fmodf(fmodf(op1->data.x_f32, op2->data.x_f32) + op2->data.x_f32, op2->data.x_f32); |
| 7730 | 7854 | return; |
| ... | ... | @@ -7748,6 +7872,12 @@ static void float_negate(ConstExprValue *out_val, ConstExprValue *op) { |
| 7748 | 7872 | bigfloat_negate(&out_val->data.x_bigfloat, &op->data.x_bigfloat); |
| 7749 | 7873 | } else if (op->type->id == TypeTableEntryIdFloat) { |
| 7750 | 7874 | switch (op->type->data.floating.bit_count) { |
| 7875 | case 16: | |
| 7876 | { | |
| 7877 | const float16_t zero = zig_double_to_f16(0); | |
| 7878 | out_val->data.x_f16 = f16_sub(zero, op->data.x_f16); | |
| 7879 | return; | |
| 7880 | } | |
| 7751 | 7881 | case 32: |
| 7752 | 7882 | out_val->data.x_f32 = -op->data.x_f32; |
| 7753 | 7883 | return; |
| ... | ... | @@ -7770,6 +7900,9 @@ static void float_negate(ConstExprValue *out_val, ConstExprValue *op) { |
| 7770 | 7900 | void float_write_ieee597(ConstExprValue *op, uint8_t *buf, bool is_big_endian) { |
| 7771 | 7901 | if (op->type->id == TypeTableEntryIdFloat) { |
| 7772 | 7902 | switch (op->type->data.floating.bit_count) { |
| 7903 | case 16: | |
| 7904 | memcpy(buf, &op->data.x_f16, 2); // TODO wrong when compiler is big endian | |
| 7905 | return; | |
| 7773 | 7906 | case 32: |
| 7774 | 7907 | memcpy(buf, &op->data.x_f32, 4); // TODO wrong when compiler is big endian |
| 7775 | 7908 | return; |
| ... | ... | @@ -7790,6 +7923,9 @@ void float_write_ieee597(ConstExprValue *op, uint8_t *buf, bool is_big_endian) { |
| 7790 | 7923 | void float_read_ieee597(ConstExprValue *val, uint8_t *buf, bool is_big_endian) { |
| 7791 | 7924 | if (val->type->id == TypeTableEntryIdFloat) { |
| 7792 | 7925 | switch (val->type->data.floating.bit_count) { |
| 7926 | case 16: | |
| 7927 | memcpy(&val->data.x_f16, buf, 2); // TODO wrong when compiler is big endian | |
| 7928 | return; | |
| 7793 | 7929 | case 32: |
| 7794 | 7930 | memcpy(&val->data.x_f32, buf, 4); // TODO wrong when compiler is big endian |
| 7795 | 7931 | return; |
| ... | ... | @@ -8817,6 +8953,9 @@ static bool eval_const_expr_implicit_cast(IrAnalyze *ira, IrInstruction *source_ |
| 8817 | 8953 | if (other_val->type->id == TypeTableEntryIdComptimeFloat) { |
| 8818 | 8954 | assert(new_type->id == TypeTableEntryIdFloat); |
| 8819 | 8955 | switch (new_type->data.floating.bit_count) { |
| 8956 | case 16: | |
| 8957 | const_val->data.x_f16 = bigfloat_to_f16(&other_val->data.x_bigfloat); | |
| 8958 | break; | |
| 8820 | 8959 | case 32: |
| 8821 | 8960 | const_val->data.x_f32 = bigfloat_to_f32(&other_val->data.x_bigfloat); |
| 8822 | 8961 | break; |
| ... | ... | @@ -8847,6 +8986,9 @@ static bool eval_const_expr_implicit_cast(IrAnalyze *ira, IrInstruction *source_ |
| 8847 | 8986 | BigFloat bigfloat; |
| 8848 | 8987 | bigfloat_init_bigint(&bigfloat, &other_val->data.x_bigint); |
| 8849 | 8988 | switch (new_type->data.floating.bit_count) { |
| 8989 | case 16: | |
| 8990 | const_val->data.x_f16 = bigfloat_to_f16(&bigfloat); | |
| 8991 | break; | |
| 8850 | 8992 | case 32: |
| 8851 | 8993 | const_val->data.x_f32 = bigfloat_to_f32(&bigfloat); |
| 8852 | 8994 | break; |
| ... | ... | @@ -20104,6 +20246,9 @@ static TypeTableEntry *ir_analyze_instruction_sqrt(IrAnalyze *ira, IrInstruction |
| 20104 | 20246 | bigfloat_sqrt(&out_val->data.x_bigfloat, &val->data.x_bigfloat); |
| 20105 | 20247 | } else if (float_type->id == TypeTableEntryIdFloat) { |
| 20106 | 20248 | switch (float_type->data.floating.bit_count) { |
| 20249 | case 16: | |
| 20250 | out_val->data.x_f16 = f16_sqrt(val->data.x_f16); | |
| 20251 | break; | |
| 20107 | 20252 | case 32: |
| 20108 | 20253 | out_val->data.x_f32 = sqrtf(val->data.x_f32); |
| 20109 | 20254 | break; |
| ... | ... | @@ -20124,7 +20269,9 @@ static TypeTableEntry *ir_analyze_instruction_sqrt(IrAnalyze *ira, IrInstruction |
| 20124 | 20269 | } |
| 20125 | 20270 | |
| 20126 | 20271 | assert(float_type->id == TypeTableEntryIdFloat); |
| 20127 | if (float_type->data.floating.bit_count != 32 && float_type->data.floating.bit_count != 64) { | |
| 20272 | if (float_type->data.floating.bit_count != 16 && | |
| 20273 | float_type->data.floating.bit_count != 32 && | |
| 20274 | float_type->data.floating.bit_count != 64) { | |
| 20128 | 20275 | ir_add_error(ira, instruction->type, buf_sprintf("compiler TODO: add implementation of sqrt for '%s'", buf_ptr(&float_type->name))); |
| 20129 | 20276 | return ira->codegen->builtin_types.entry_invalid; |
| 20130 | 20277 | } |
src/util.hpp+19| ... | ... | @@ -31,6 +31,8 @@ |
| 31 | 31 | |
| 32 | 32 | #endif |
| 33 | 33 | |
| 34 | #include "softfloat.hpp" | |
| 35 | ||
| 34 | 36 | #define BREAKPOINT __asm("int $0x03") |
| 35 | 37 | |
| 36 | 38 | ATTRIBUTE_COLD |
| ... | ... | @@ -165,4 +167,21 @@ static inline uint8_t log2_u64(uint64_t x) { |
| 165 | 167 | return (63 - clzll(x)); |
| 166 | 168 | } |
| 167 | 169 | |
| 170 | static inline float16_t zig_double_to_f16(double x) { | |
| 171 | float64_t y; | |
| 172 | static_assert(sizeof(x) == sizeof(y), ""); | |
| 173 | memcpy(&y, &x, sizeof(x)); | |
| 174 | return f64_to_f16(y); | |
| 175 | } | |
| 176 | ||
| 177 | ||
| 178 | // Return value is safe to coerce to float even when |x| is NaN or Infinity. | |
| 179 | static inline double zig_f16_to_double(float16_t x) { | |
| 180 | float64_t y = f16_to_f64(x); | |
| 181 | double z; | |
| 182 | static_assert(sizeof(y) == sizeof(z), ""); | |
| 183 | memcpy(&z, &y, sizeof(y)); | |
| 184 | return z; | |
| 185 | } | |
| 186 | ||
| 168 | 187 | #endif |
std/special/compiler_rt/extendXfYf2.zig+29-27| ... | ... | @@ -10,9 +10,13 @@ pub extern fn __extendsftf2(a: f32) f128 { |
| 10 | 10 | return extendXfYf2(f128, f32, a); |
| 11 | 11 | } |
| 12 | 12 | |
| 13 | pub extern fn __extendhfsf2(a: u16) f32 { | |
| 14 | return extendXfYf2(f32, f16, @bitCast(f16, a)); | |
| 15 | } | |
| 16 | ||
| 13 | 17 | const CHAR_BIT = 8; |
| 14 | 18 | |
| 15 | pub fn extendXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t { | |
| 19 | inline fn extendXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t { | |
| 16 | 20 | const src_rep_t = @IntType(false, @typeInfo(src_t).Float.bits); |
| 17 | 21 | const dst_rep_t = @IntType(false, @typeInfo(dst_t).Float.bits); |
| 18 | 22 | const srcSigBits = std.math.floatMantissaBits(src_t); |
| ... | ... | @@ -22,22 +26,22 @@ pub fn extendXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t { |
| 22 | 26 | |
| 23 | 27 | // Various constants whose values follow from the type parameters. |
| 24 | 28 | // Any reasonable optimizer will fold and propagate all of these. |
| 25 | const srcBits: i32 = @sizeOf(src_t) * CHAR_BIT; | |
| 26 | const srcExpBits: i32 = srcBits - srcSigBits - 1; | |
| 27 | const srcInfExp: i32 = (1 << srcExpBits) - 1; | |
| 28 | const srcExpBias: i32 = srcInfExp >> 1; | |
| 29 | const srcBits = @sizeOf(src_t) * CHAR_BIT; | |
| 30 | const srcExpBits = srcBits - srcSigBits - 1; | |
| 31 | const srcInfExp = (1 << srcExpBits) - 1; | |
| 32 | const srcExpBias = srcInfExp >> 1; | |
| 29 | 33 | |
| 30 | const srcMinNormal: src_rep_t = src_rep_t(1) << srcSigBits; | |
| 31 | const srcInfinity: src_rep_t = src_rep_t(@bitCast(u32, srcInfExp)) << srcSigBits; | |
| 32 | const srcSignMask: src_rep_t = src_rep_t(1) << @intCast(SrcShift, srcSigBits +% srcExpBits); | |
| 33 | const srcAbsMask: src_rep_t = srcSignMask -% 1; | |
| 34 | const srcQNaN: src_rep_t = src_rep_t(1) << @intCast(SrcShift, srcSigBits -% 1); | |
| 35 | const srcNaNCode: src_rep_t = srcQNaN -% 1; | |
| 34 | const srcMinNormal = 1 << srcSigBits; | |
| 35 | const srcInfinity = srcInfExp << srcSigBits; | |
| 36 | const srcSignMask = 1 << (srcSigBits + srcExpBits); | |
| 37 | const srcAbsMask = srcSignMask - 1; | |
| 38 | const srcQNaN = 1 << (srcSigBits - 1); | |
| 39 | const srcNaNCode = srcQNaN - 1; | |
| 36 | 40 | |
| 37 | const dstBits: i32 = @sizeOf(dst_t) * CHAR_BIT; | |
| 38 | const dstExpBits: i32 = dstBits - dstSigBits - 1; | |
| 39 | const dstInfExp: i32 = (1 << dstExpBits) - 1; | |
| 40 | const dstExpBias: i32 = dstInfExp >> 1; | |
| 41 | const dstBits = @sizeOf(dst_t) * CHAR_BIT; | |
| 42 | const dstExpBits = dstBits - dstSigBits - 1; | |
| 43 | const dstInfExp = (1 << dstExpBits) - 1; | |
| 44 | const dstExpBias = dstInfExp >> 1; | |
| 41 | 45 | |
| 42 | 46 | const dstMinNormal: dst_rep_t = dst_rep_t(1) << dstSigBits; |
| 43 | 47 | |
| ... | ... | @@ -47,38 +51,36 @@ pub fn extendXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t { |
| 47 | 51 | const sign: src_rep_t = aRep & srcSignMask; |
| 48 | 52 | var absResult: dst_rep_t = undefined; |
| 49 | 53 | |
| 50 | // If @sizeOf(src_rep_t) < @sizeOf(int), the subtraction result is promoted | |
| 51 | // to (signed) int. To avoid that, explicitly cast to src_rep_t. | |
| 52 | if ((src_rep_t)(aAbs -% srcMinNormal) < srcInfinity -% srcMinNormal) { | |
| 54 | if (aAbs -% srcMinNormal < srcInfinity - srcMinNormal) { | |
| 53 | 55 | // a is a normal number. |
| 54 | 56 | // Extend to the destination type by shifting the significand and |
| 55 | 57 | // exponent into the proper position and rebiasing the exponent. |
| 56 | absResult = dst_rep_t(aAbs) << (dstSigBits -% srcSigBits); | |
| 57 | absResult += dst_rep_t(@bitCast(u32, dstExpBias -% srcExpBias)) << dstSigBits; | |
| 58 | absResult = dst_rep_t(aAbs) << (dstSigBits - srcSigBits); | |
| 59 | absResult += (dstExpBias - srcExpBias) << dstSigBits; | |
| 58 | 60 | } else if (aAbs >= srcInfinity) { |
| 59 | 61 | // a is NaN or infinity. |
| 60 | 62 | // Conjure the result by beginning with infinity, then setting the qNaN |
| 61 | 63 | // bit (if needed) and right-aligning the rest of the trailing NaN |
| 62 | 64 | // payload field. |
| 63 | absResult = dst_rep_t(@bitCast(u32, dstInfExp)) << dstSigBits; | |
| 64 | absResult |= (dst_rep_t)(aAbs & srcQNaN) << (dstSigBits - srcSigBits); | |
| 65 | absResult |= (dst_rep_t)(aAbs & srcNaNCode) << (dstSigBits - srcSigBits); | |
| 65 | absResult = dstInfExp << dstSigBits; | |
| 66 | absResult |= dst_rep_t(aAbs & srcQNaN) << (dstSigBits - srcSigBits); | |
| 67 | absResult |= dst_rep_t(aAbs & srcNaNCode) << (dstSigBits - srcSigBits); | |
| 66 | 68 | } else if (aAbs != 0) { |
| 67 | 69 | // a is denormal. |
| 68 | 70 | // renormalize the significand and clear the leading bit, then insert |
| 69 | 71 | // the correct adjusted exponent in the destination type. |
| 70 | const scale: i32 = @clz(aAbs) - @clz(srcMinNormal); | |
| 72 | const scale: u32 = @clz(aAbs) - @clz(src_rep_t(srcMinNormal)); | |
| 71 | 73 | absResult = dst_rep_t(aAbs) << @intCast(DstShift, dstSigBits - srcSigBits + scale); |
| 72 | 74 | absResult ^= dstMinNormal; |
| 73 | const resultExponent: i32 = dstExpBias - srcExpBias - scale + 1; | |
| 74 | absResult |= dst_rep_t(@bitCast(u32, resultExponent)) << @intCast(DstShift, dstSigBits); | |
| 75 | const resultExponent: u32 = dstExpBias - srcExpBias - scale + 1; | |
| 76 | absResult |= @intCast(dst_rep_t, resultExponent) << dstSigBits; | |
| 75 | 77 | } else { |
| 76 | 78 | // a is zero. |
| 77 | 79 | absResult = 0; |
| 78 | 80 | } |
| 79 | 81 | |
| 80 | 82 | // Apply the signbit to (dst_t)abs(a). |
| 81 | const result: dst_rep_t align(@alignOf(dst_t)) = absResult | dst_rep_t(sign) << @intCast(DstShift, dstBits - srcBits); | |
| 83 | const result: dst_rep_t align(@alignOf(dst_t)) = absResult | dst_rep_t(sign) << (dstBits - srcBits); | |
| 82 | 84 | return @bitCast(dst_t, result); |
| 83 | 85 | } |
| 84 | 86 |
std/special/compiler_rt/extendXfYf2_test.zig+46| ... | ... | @@ -1,4 +1,5 @@ |
| 1 | 1 | const __extenddftf2 = @import("extendXfYf2.zig").__extenddftf2; |
| 2 | const __extendhfsf2 = @import("extendXfYf2.zig").__extendhfsf2; | |
| 2 | 3 | const __extendsftf2 = @import("extendXfYf2.zig").__extendsftf2; |
| 3 | 4 | const assert = @import("std").debug.assert; |
| 4 | 5 | |
| ... | ... | @@ -24,6 +25,22 @@ fn test__extenddftf2(a: f64, expectedHi: u64, expectedLo: u64) void { |
| 24 | 25 | @panic("__extenddftf2 test failure"); |
| 25 | 26 | } |
| 26 | 27 | |
| 28 | fn test__extendhfsf2(a: u16, expected: u32) void { | |
| 29 | const x = __extendhfsf2(a); | |
| 30 | const rep = @bitCast(u32, x); | |
| 31 | ||
| 32 | if (rep == expected) { | |
| 33 | if (rep & 0x7fffffff > 0x7f800000) { | |
| 34 | return; // NaN is always unequal. | |
| 35 | } | |
| 36 | if (x == @bitCast(f32, expected)) { | |
| 37 | return; | |
| 38 | } | |
| 39 | } | |
| 40 | ||
| 41 | @panic("__extendhfsf2 test failure"); | |
| 42 | } | |
| 43 | ||
| 27 | 44 | fn test__extendsftf2(a: f32, expectedHi: u64, expectedLo: u64) void { |
| 28 | 45 | const x = __extendsftf2(a); |
| 29 | 46 | |
| ... | ... | @@ -68,6 +85,35 @@ test "extenddftf2" { |
| 68 | 85 | test__extenddftf2(0x1.edcba987654321fp-45, 0x3fd2edcba9876543, 0x2000000000000000); |
| 69 | 86 | } |
| 70 | 87 | |
| 88 | test "extendhfsf2" { | |
| 89 | test__extendhfsf2(0x7e00, 0x7fc00000); // qNaN | |
| 90 | test__extendhfsf2(0x7f00, 0x7fe00000); // sNaN | |
| 91 | ||
| 92 | test__extendhfsf2(0, 0); // 0 | |
| 93 | test__extendhfsf2(0x8000, 0x80000000); // -0 | |
| 94 | ||
| 95 | test__extendhfsf2(0x7c00, 0x7f800000); // inf | |
| 96 | test__extendhfsf2(0xfc00, 0xff800000); // -inf | |
| 97 | ||
| 98 | test__extendhfsf2(0x0001, 0x33800000); // denormal (min), 2**-24 | |
| 99 | test__extendhfsf2(0x8001, 0xb3800000); // denormal (min), -2**-24 | |
| 100 | ||
| 101 | test__extendhfsf2(0x03ff, 0x387fc000); // denormal (max), 2**-14 - 2**-24 | |
| 102 | test__extendhfsf2(0x83ff, 0xb87fc000); // denormal (max), -2**-14 + 2**-24 | |
| 103 | ||
| 104 | test__extendhfsf2(0x0400, 0x38800000); // normal (min), 2**-14 | |
| 105 | test__extendhfsf2(0x8400, 0xb8800000); // normal (min), -2**-14 | |
| 106 | ||
| 107 | test__extendhfsf2(0x7bff, 0x477fe000); // normal (max), 65504 | |
| 108 | test__extendhfsf2(0xfbff, 0xc77fe000); // normal (max), -65504 | |
| 109 | ||
| 110 | test__extendhfsf2(0x3c01, 0x3f802000); // normal, 1 + 2**-10 | |
| 111 | test__extendhfsf2(0xbc01, 0xbf802000); // normal, -1 - 2**-10 | |
| 112 | ||
| 113 | test__extendhfsf2(0x3555, 0x3eaaa000); // normal, approx. 1/3 | |
| 114 | test__extendhfsf2(0xb555, 0xbeaaa000); // normal, approx. -1/3 | |
| 115 | } | |
| 116 | ||
| 71 | 117 | test "extendsftf2" { |
| 72 | 118 | // qNaN |
| 73 | 119 | test__extendsftf2(makeQNaN32(), 0x7fff800000000000, 0x0); |
std/special/compiler_rt/index.zig+5| ... | ... | @@ -15,6 +15,8 @@ comptime { |
| 15 | 15 | @export("__lttf2", @import("comparetf2.zig").__letf2, linkage); |
| 16 | 16 | @export("__netf2", @import("comparetf2.zig").__letf2, linkage); |
| 17 | 17 | @export("__gttf2", @import("comparetf2.zig").__getf2, linkage); |
| 18 | @export("__gnu_h2f_ieee", @import("extendXfYf2.zig").__extendhfsf2, linkage); | |
| 19 | @export("__gnu_f2h_ieee", @import("truncXfYf2.zig").__truncsfhf2, linkage); | |
| 18 | 20 | } |
| 19 | 21 | |
| 20 | 22 | @export("__unordtf2", @import("comparetf2.zig").__unordtf2, linkage); |
| ... | ... | @@ -22,6 +24,9 @@ comptime { |
| 22 | 24 | @export("__floatuntidf", @import("floatuntidf.zig").__floatuntidf, linkage); |
| 23 | 25 | @export("__extenddftf2", @import("extendXfYf2.zig").__extenddftf2, linkage); |
| 24 | 26 | @export("__extendsftf2", @import("extendXfYf2.zig").__extendsftf2, linkage); |
| 27 | @export("__extendhfsf2", @import("extendXfYf2.zig").__extendhfsf2, linkage); | |
| 28 | ||
| 29 | @export("__truncsfhf2", @import("truncXfYf2.zig").__truncsfhf2, linkage); | |
| 25 | 30 | |
| 26 | 31 | @export("__fixunssfsi", @import("fixunssfsi.zig").__fixunssfsi, linkage); |
| 27 | 32 | @export("__fixunssfdi", @import("fixunssfdi.zig").__fixunssfdi, linkage); |
std/special/compiler_rt/truncXfYf2.zig created+111| ... | ... | @@ -0,0 +1,111 @@ |
| 1 | const std = @import("std"); | |
| 2 | ||
| 3 | pub extern fn __truncsfhf2(a: f32) u16 { | |
| 4 | return @bitCast(u16, truncXfYf2(f16, f32, a)); | |
| 5 | } | |
| 6 | ||
| 7 | const CHAR_BIT = 8; | |
| 8 | ||
| 9 | inline fn truncXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t { | |
| 10 | const src_rep_t = @IntType(false, @typeInfo(src_t).Float.bits); | |
| 11 | const dst_rep_t = @IntType(false, @typeInfo(dst_t).Float.bits); | |
| 12 | const srcSigBits = std.math.floatMantissaBits(src_t); | |
| 13 | const dstSigBits = std.math.floatMantissaBits(dst_t); | |
| 14 | const SrcShift = std.math.Log2Int(src_rep_t); | |
| 15 | const DstShift = std.math.Log2Int(dst_rep_t); | |
| 16 | ||
| 17 | // Various constants whose values follow from the type parameters. | |
| 18 | // Any reasonable optimizer will fold and propagate all of these. | |
| 19 | const srcBits = @sizeOf(src_t) * CHAR_BIT; | |
| 20 | const srcExpBits = srcBits - srcSigBits - 1; | |
| 21 | const srcInfExp = (1 << srcExpBits) - 1; | |
| 22 | const srcExpBias = srcInfExp >> 1; | |
| 23 | ||
| 24 | const srcMinNormal = 1 << srcSigBits; | |
| 25 | const srcSignificandMask = srcMinNormal - 1; | |
| 26 | const srcInfinity = srcInfExp << srcSigBits; | |
| 27 | const srcSignMask = 1 << (srcSigBits + srcExpBits); | |
| 28 | const srcAbsMask = srcSignMask - 1; | |
| 29 | const roundMask = (1 << (srcSigBits - dstSigBits)) - 1; | |
| 30 | const halfway = 1 << (srcSigBits - dstSigBits - 1); | |
| 31 | const srcQNaN = 1 << (srcSigBits - 1); | |
| 32 | const srcNaNCode = srcQNaN - 1; | |
| 33 | ||
| 34 | const dstBits = @sizeOf(dst_t) * CHAR_BIT; | |
| 35 | const dstExpBits = dstBits - dstSigBits - 1; | |
| 36 | const dstInfExp = (1 << dstExpBits) - 1; | |
| 37 | const dstExpBias = dstInfExp >> 1; | |
| 38 | ||
| 39 | const underflowExponent = srcExpBias + 1 - dstExpBias; | |
| 40 | const overflowExponent = srcExpBias + dstInfExp - dstExpBias; | |
| 41 | const underflow = underflowExponent << srcSigBits; | |
| 42 | const overflow = overflowExponent << srcSigBits; | |
| 43 | ||
| 44 | const dstQNaN = 1 << (dstSigBits - 1); | |
| 45 | const dstNaNCode = dstQNaN - 1; | |
| 46 | ||
| 47 | // Break a into a sign and representation of the absolute value | |
| 48 | const aRep: src_rep_t = @bitCast(src_rep_t, a); | |
| 49 | const aAbs: src_rep_t = aRep & srcAbsMask; | |
| 50 | const sign: src_rep_t = aRep & srcSignMask; | |
| 51 | var absResult: dst_rep_t = undefined; | |
| 52 | ||
| 53 | if (aAbs -% underflow < aAbs -% overflow) { | |
| 54 | // The exponent of a is within the range of normal numbers in the | |
| 55 | // destination format. We can convert by simply right-shifting with | |
| 56 | // rounding and adjusting the exponent. | |
| 57 | absResult = @truncate(dst_rep_t, aAbs >> (srcSigBits - dstSigBits)); | |
| 58 | absResult -%= dst_rep_t(srcExpBias - dstExpBias) << dstSigBits; | |
| 59 | ||
| 60 | const roundBits: src_rep_t = aAbs & roundMask; | |
| 61 | if (roundBits > halfway) { | |
| 62 | // Round to nearest | |
| 63 | absResult += 1; | |
| 64 | } else if (roundBits == halfway) { | |
| 65 | // Ties to even | |
| 66 | absResult += absResult & 1; | |
| 67 | } | |
| 68 | } else if (aAbs > srcInfinity) { | |
| 69 | // a is NaN. | |
| 70 | // Conjure the result by beginning with infinity, setting the qNaN | |
| 71 | // bit and inserting the (truncated) trailing NaN field. | |
| 72 | absResult = @intCast(dst_rep_t, dstInfExp) << dstSigBits; | |
| 73 | absResult |= dstQNaN; | |
| 74 | absResult |= @intCast(dst_rep_t, ((aAbs & srcNaNCode) >> (srcSigBits - dstSigBits)) & dstNaNCode); | |
| 75 | } else if (aAbs >= overflow) { | |
| 76 | // a overflows to infinity. | |
| 77 | absResult = @intCast(dst_rep_t, dstInfExp) << dstSigBits; | |
| 78 | } else { | |
| 79 | // a underflows on conversion to the destination type or is an exact | |
| 80 | // zero. The result may be a denormal or zero. Extract the exponent | |
| 81 | // to get the shift amount for the denormalization. | |
| 82 | const aExp: u32 = aAbs >> srcSigBits; | |
| 83 | const shift: u32 = srcExpBias - dstExpBias - aExp + 1; | |
| 84 | ||
| 85 | const significand: src_rep_t = (aRep & srcSignificandMask) | srcMinNormal; | |
| 86 | ||
| 87 | // Right shift by the denormalization amount with sticky. | |
| 88 | if (shift > srcSigBits) { | |
| 89 | absResult = 0; | |
| 90 | } else { | |
| 91 | const sticky: src_rep_t = significand << @intCast(SrcShift, srcBits - shift); | |
| 92 | const denormalizedSignificand: src_rep_t = significand >> @intCast(SrcShift, shift) | sticky; | |
| 93 | absResult = @intCast(dst_rep_t, denormalizedSignificand >> (srcSigBits - dstSigBits)); | |
| 94 | const roundBits: src_rep_t = denormalizedSignificand & roundMask; | |
| 95 | if (roundBits > halfway) { | |
| 96 | // Round to nearest | |
| 97 | absResult += 1; | |
| 98 | } else if (roundBits == halfway) { | |
| 99 | // Ties to even | |
| 100 | absResult += absResult & 1; | |
| 101 | } | |
| 102 | } | |
| 103 | } | |
| 104 | ||
| 105 | const result: dst_rep_t align(@alignOf(dst_t)) = absResult | @truncate(dst_rep_t, sign >> @intCast(SrcShift, srcBits - dstBits)); | |
| 106 | return @bitCast(dst_t, result); | |
| 107 | } | |
| 108 | ||
| 109 | test "import truncXfYf2" { | |
| 110 | _ = @import("truncXfYf2_test.zig"); | |
| 111 | } |
std/special/compiler_rt/truncXfYf2_test.zig created+64| ... | ... | @@ -0,0 +1,64 @@ |
| 1 | const __truncsfhf2 = @import("truncXfYf2.zig").__truncsfhf2; | |
| 2 | ||
| 3 | fn test__truncsfhf2(a: u32, expected: u16) void { | |
| 4 | const actual = __truncsfhf2(@bitCast(f32, a)); | |
| 5 | ||
| 6 | if (actual == expected) { | |
| 7 | return; | |
| 8 | } | |
| 9 | ||
| 10 | @panic("__truncsfhf2 test failure"); | |
| 11 | } | |
| 12 | ||
| 13 | test "truncsfhf2" { | |
| 14 | test__truncsfhf2(0x7fc00000, 0x7e00); // qNaN | |
| 15 | test__truncsfhf2(0x7fe00000, 0x7f00); // sNaN | |
| 16 | ||
| 17 | test__truncsfhf2(0, 0); // 0 | |
| 18 | test__truncsfhf2(0x80000000, 0x8000); // -0 | |
| 19 | ||
| 20 | test__truncsfhf2(0x7f800000, 0x7c00); // inf | |
| 21 | test__truncsfhf2(0xff800000, 0xfc00); // -inf | |
| 22 | ||
| 23 | test__truncsfhf2(0x477ff000, 0x7c00); // 65520 -> inf | |
| 24 | test__truncsfhf2(0xc77ff000, 0xfc00); // -65520 -> -inf | |
| 25 | ||
| 26 | test__truncsfhf2(0x71cc3892, 0x7c00); // 0x1.987124876876324p+100 -> inf | |
| 27 | test__truncsfhf2(0xf1cc3892, 0xfc00); // -0x1.987124876876324p+100 -> -inf | |
| 28 | ||
| 29 | test__truncsfhf2(0x38800000, 0x0400); // normal (min), 2**-14 | |
| 30 | test__truncsfhf2(0xb8800000, 0x8400); // normal (min), -2**-14 | |
| 31 | ||
| 32 | test__truncsfhf2(0x477fe000, 0x7bff); // normal (max), 65504 | |
| 33 | test__truncsfhf2(0xc77fe000, 0xfbff); // normal (max), -65504 | |
| 34 | ||
| 35 | test__truncsfhf2(0x477fe100, 0x7bff); // normal, 65505 -> 65504 | |
| 36 | test__truncsfhf2(0xc77fe100, 0xfbff); // normal, -65505 -> -65504 | |
| 37 | ||
| 38 | test__truncsfhf2(0x477fef00, 0x7bff); // normal, 65519 -> 65504 | |
| 39 | test__truncsfhf2(0xc77fef00, 0xfbff); // normal, -65519 -> -65504 | |
| 40 | ||
| 41 | test__truncsfhf2(0x3f802000, 0x3c01); // normal, 1 + 2**-10 | |
| 42 | test__truncsfhf2(0xbf802000, 0xbc01); // normal, -1 - 2**-10 | |
| 43 | ||
| 44 | test__truncsfhf2(0x3eaaa000, 0x3555); // normal, approx. 1/3 | |
| 45 | test__truncsfhf2(0xbeaaa000, 0xb555); // normal, approx. -1/3 | |
| 46 | ||
| 47 | test__truncsfhf2(0x40490fdb, 0x4248); // normal, 3.1415926535 | |
| 48 | test__truncsfhf2(0xc0490fdb, 0xc248); // normal, -3.1415926535 | |
| 49 | ||
| 50 | test__truncsfhf2(0x45cc3892, 0x6e62); // normal, 0x1.987124876876324p+12 | |
| 51 | ||
| 52 | test__truncsfhf2(0x3f800000, 0x3c00); // normal, 1 | |
| 53 | test__truncsfhf2(0x38800000, 0x0400); // normal, 0x1.0p-14 | |
| 54 | ||
| 55 | test__truncsfhf2(0x33800000, 0x0001); // denormal (min), 2**-24 | |
| 56 | test__truncsfhf2(0xb3800000, 0x8001); // denormal (min), -2**-24 | |
| 57 | ||
| 58 | test__truncsfhf2(0x387fc000, 0x03ff); // denormal (max), 2**-14 - 2**-24 | |
| 59 | test__truncsfhf2(0xb87fc000, 0x83ff); // denormal (max), -2**-14 + 2**-24 | |
| 60 | ||
| 61 | test__truncsfhf2(0x35800000, 0x0010); // denormal, 0x1.0p-20 | |
| 62 | test__truncsfhf2(0x33280000, 0x0001); // denormal, 0x1.5p-25 -> 0x1.0p-24 | |
| 63 | test__truncsfhf2(0x33000000, 0x0000); // 0x1.0p-25 -> zero | |
| 64 | } |
test/cases/cast.zig+23-5| ... | ... | @@ -350,13 +350,16 @@ fn testFloatToInts() void { |
| 350 | 350 | assert(x == 10000); |
| 351 | 351 | const y = @floatToInt(i32, f32(1e4)); |
| 352 | 352 | assert(y == 10000); |
| 353 | expectFloatToInt(u8, 255.1, 255); | |
| 354 | expectFloatToInt(i8, 127.2, 127); | |
| 355 | expectFloatToInt(i8, -128.2, -128); | |
| 353 | expectFloatToInt(f16, 255.1, u8, 255); | |
| 354 | expectFloatToInt(f16, 127.2, i8, 127); | |
| 355 | expectFloatToInt(f16, -128.2, i8, -128); | |
| 356 | expectFloatToInt(f32, 255.1, u8, 255); | |
| 357 | expectFloatToInt(f32, 127.2, i8, 127); | |
| 358 | expectFloatToInt(f32, -128.2, i8, -128); | |
| 356 | 359 | } |
| 357 | 360 | |
| 358 | fn expectFloatToInt(comptime T: type, f: f32, i: T) void { | |
| 359 | assert(@floatToInt(T, f) == i); | |
| 361 | fn expectFloatToInt(comptime F: type, f: F, comptime I: type, i: I) void { | |
| 362 | assert(@floatToInt(I, f) == i); | |
| 360 | 363 | } |
| 361 | 364 | |
| 362 | 365 | test "cast u128 to f128 and back" { |
| ... | ... | @@ -418,6 +421,16 @@ test "@intCast comptime_int" { |
| 418 | 421 | } |
| 419 | 422 | |
| 420 | 423 | test "@floatCast comptime_int and comptime_float" { |
| 424 | { | |
| 425 | const result = @floatCast(f16, 1234); | |
| 426 | assert(@typeOf(result) == f16); | |
| 427 | assert(result == 1234.0); | |
| 428 | } | |
| 429 | { | |
| 430 | const result = @floatCast(f16, 1234.0); | |
| 431 | assert(@typeOf(result) == f16); | |
| 432 | assert(result == 1234.0); | |
| 433 | } | |
| 421 | 434 | { |
| 422 | 435 | const result = @floatCast(f32, 1234); |
| 423 | 436 | assert(@typeOf(result) == f32); |
| ... | ... | @@ -431,6 +444,11 @@ test "@floatCast comptime_int and comptime_float" { |
| 431 | 444 | } |
| 432 | 445 | |
| 433 | 446 | test "comptime_int @intToFloat" { |
| 447 | { | |
| 448 | const result = @intToFloat(f16, 1234); | |
| 449 | assert(@typeOf(result) == f16); | |
| 450 | assert(result == 1234.0); | |
| 451 | } | |
| 434 | 452 | { |
| 435 | 453 | const result = @intToFloat(f32, 1234); |
| 436 | 454 | assert(@typeOf(result) == f32); |
test/cases/math.zig+11-1| ... | ... | @@ -6,15 +6,20 @@ test "division" { |
| 6 | 6 | } |
| 7 | 7 | fn testDivision() void { |
| 8 | 8 | assert(div(u32, 13, 3) == 4); |
| 9 | assert(div(f16, 1.0, 2.0) == 0.5); | |
| 9 | 10 | assert(div(f32, 1.0, 2.0) == 0.5); |
| 10 | 11 | |
| 11 | 12 | assert(divExact(u32, 55, 11) == 5); |
| 12 | 13 | assert(divExact(i32, -55, 11) == -5); |
| 14 | assert(divExact(f16, 55.0, 11.0) == 5.0); | |
| 15 | assert(divExact(f16, -55.0, 11.0) == -5.0); | |
| 13 | 16 | assert(divExact(f32, 55.0, 11.0) == 5.0); |
| 14 | 17 | assert(divExact(f32, -55.0, 11.0) == -5.0); |
| 15 | 18 | |
| 16 | 19 | assert(divFloor(i32, 5, 3) == 1); |
| 17 | 20 | assert(divFloor(i32, -5, 3) == -2); |
| 21 | assert(divFloor(f16, 5.0, 3.0) == 1.0); | |
| 22 | assert(divFloor(f16, -5.0, 3.0) == -2.0); | |
| 18 | 23 | assert(divFloor(f32, 5.0, 3.0) == 1.0); |
| 19 | 24 | assert(divFloor(f32, -5.0, 3.0) == -2.0); |
| 20 | 25 | assert(divFloor(i32, -0x80000000, -2) == 0x40000000); |
| ... | ... | @@ -24,6 +29,8 @@ fn testDivision() void { |
| 24 | 29 | |
| 25 | 30 | assert(divTrunc(i32, 5, 3) == 1); |
| 26 | 31 | assert(divTrunc(i32, -5, 3) == -1); |
| 32 | assert(divTrunc(f16, 5.0, 3.0) == 1.0); | |
| 33 | assert(divTrunc(f16, -5.0, 3.0) == -1.0); | |
| 27 | 34 | assert(divTrunc(f32, 5.0, 3.0) == 1.0); |
| 28 | 35 | assert(divTrunc(f32, -5.0, 3.0) == -1.0); |
| 29 | 36 | |
| ... | ... | @@ -435,10 +442,11 @@ test "comptime float rem int" { |
| 435 | 442 | } |
| 436 | 443 | |
| 437 | 444 | test "remainder division" { |
| 445 | comptime remdiv(f16); | |
| 438 | 446 | comptime remdiv(f32); |
| 439 | 447 | comptime remdiv(f64); |
| 440 | 448 | comptime remdiv(f128); |
| 441 | remdiv(f32); | |
| 449 | remdiv(f16); | |
| 442 | 450 | remdiv(f64); |
| 443 | 451 | remdiv(f128); |
| 444 | 452 | } |
| ... | ... | @@ -453,6 +461,8 @@ test "@sqrt" { |
| 453 | 461 | comptime testSqrt(f64, 12.0); |
| 454 | 462 | testSqrt(f32, 13.0); |
| 455 | 463 | comptime testSqrt(f32, 13.0); |
| 464 | testSqrt(f16, 13.0); | |
| 465 | comptime testSqrt(f16, 13.0); | |
| 456 | 466 | |
| 457 | 467 | const x = 14.0; |
| 458 | 468 | const y = x * x; |
test/cases/misc.zig+1| ... | ... | @@ -53,6 +53,7 @@ test "@IntType builtin" { |
| 53 | 53 | } |
| 54 | 54 | |
| 55 | 55 | test "floating point primitive bit counts" { |
| 56 | assert(f16.bit_count == 16); | |
| 56 | 57 | assert(f32.bit_count == 32); |
| 57 | 58 | assert(f64.bit_count == 64); |
| 58 | 59 | } |