From 9ce1a1d9287c4ed1f638590f8b7940d15c746786 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Mon, 2 Oct 2023 14:36:37 +0200 Subject: [PATCH] Add support for float binary pattern matching Add support to bs_get_float2 in ordet to enable <> = Bin. Signed-off-by: Davide Bettio --- src/libAtomVM/bitstring.c | 121 ++++++++++++++++++++++++++++++++++ src/libAtomVM/bitstring.h | 5 ++ src/libAtomVM/opcodes.h | 1 + src/libAtomVM/opcodesswitch.h | 68 +++++++++++++++++++ 4 files changed, 195 insertions(+) diff --git a/src/libAtomVM/bitstring.c b/src/libAtomVM/bitstring.c index f7e151e0fd..f37854b0cb 100644 --- a/src/libAtomVM/bitstring.c +++ b/src/libAtomVM/bitstring.c @@ -21,6 +21,8 @@ #include "bitstring.h" +#include + static inline uint64_t from_le64(uint64_t value) { return ((((value) &0xFF) << 56) | (((value) &0xFF00) << 40) | (((value) &0xFF0000) << 24) | (((value) &0xFF000000) << 8) | (((value) &0xFF00000000) >> 8) | (((value) &0xFF0000000000) >> 24) | (((value) &0xFF000000000000) >> 40) | (((value) &0xFF00000000000000) >> 56)); @@ -389,3 +391,122 @@ void bitstring_copy_bits_incomplete_bytes(uint8_t *dst, size_t bits_offset, cons } *dst = dest_byte; } + +bool bitstring_extract_f32( + term src_bin, size_t offset, avm_int_t n, enum BitstringFlags bs_flags, avm_float_t *dst) +{ + unsigned long capacity = term_binary_size(src_bin); + if (8 * capacity - offset < (unsigned long) n) { + return false; + } + + if ((offset & 0x7) == 0) { + int byte_offset = offset >> 3; + const uint8_t *src = (const uint8_t *) term_binary_data(src_bin) + byte_offset; + + union + { + uint32_t bits; + float fvalue; + } f32; + + if (bs_flags & LittleEndianIntegerMask) { + f32.bits = READ_32LE_UNALIGNED(src); + } else { + f32.bits = READ_32_UNALIGNED(src); + } + *dst = f32.fvalue; + return true; + } else { + return false; + } +} + +static bool bin64_to_f32(uint64_t bin64, float *out) +{ + int sign = (int) ((bin64 >> 63) & 1); + int exponent = (int) ((bin64 >> 52) & 0x7FF) - 1023; + uint64_t mantissa = (bin64 >> 28) & 0xFFFFFF; + + if (exponent == 1024) { + // not a number + return false; + } else if (exponent > 127) { + // too big + return false; + } else if (exponent < -150) { + // safely convert to 0 + exponent = -127; + mantissa = 0; + } else if (exponent < -126) { + // denormalized + mantissa |= 0x1000000; + mantissa >>= (-126 - exponent); + exponent = -127; + } + + mantissa = (mantissa + 1) >> 1; + if (mantissa & 0x800000) { + exponent += 1; + mantissa &= 0x7FFFFF; + mantissa >>= 1; + } + + union + { + uint32_t u32; + float f32; + } temp; + + temp.u32 = mantissa; + temp.u32 |= (uint32_t) (exponent + 127) << 23; + temp.u32 |= (uint32_t) sign << 31; + + *out = temp.f32; + + return false; +} + +bool bitstring_extract_f64( + term src_bin, size_t offset, avm_int_t n, enum BitstringFlags bs_flags, avm_float_t *dst) +{ + unsigned long capacity = term_binary_size(src_bin); + if (8 * capacity - offset < (unsigned long) n) { + return false; + } + + if ((offset & 0x7) == 0) { + int byte_offset = offset >> 3; + const uint8_t *src = (const uint8_t *) term_binary_data(src_bin) + byte_offset; + + uint64_t bin64; + if (bs_flags & LittleEndianIntegerMask) { + bin64 = READ_64LE_UNALIGNED(src); + } else { + bin64 = READ_64_UNALIGNED(src); + } + + _Static_assert( + (sizeof(avm_float_t) == 4) || (sizeof(avm_float_t) == 8), "Unsupported double size"); + + if (sizeof(avm_float_t) == 8) { + union + { + uint64_t bits; + double fvalue; + } f64; + + f64.bits = bin64; + *dst = f64.fvalue; + } else if (sizeof(avm_float_t) == 4) { + float f32; + bool res = bin64_to_f32(bin64, &f32); + *dst = f32; + return res; + } + return true; + + } else { + return false; + } +} diff --git a/src/libAtomVM/bitstring.h b/src/libAtomVM/bitstring.h index 884f100648..a031efd056 100644 --- a/src/libAtomVM/bitstring.h +++ b/src/libAtomVM/bitstring.h @@ -541,6 +541,11 @@ static inline void bitstring_copy_bits(uint8_t *dst, size_t bits_offset, const u } } +bool bitstring_extract_f32( + term src_bin, size_t offset, avm_int_t n, enum BitstringFlags bs_flags, avm_float_t *dst); +bool bitstring_extract_f64( + term src_bin, size_t offset, avm_int_t n, enum BitstringFlags bs_flags, avm_float_t *dst); + #ifdef __cplusplus } #endif diff --git a/src/libAtomVM/opcodes.h b/src/libAtomVM/opcodes.h index fa6d6b2407..121d1e5a75 100644 --- a/src/libAtomVM/opcodes.h +++ b/src/libAtomVM/opcodes.h @@ -110,6 +110,7 @@ #define OP_IS_FUNCTION2 115 #define OP_BS_START_MATCH2 116 #define OP_BS_GET_INTEGER2 117 +#define OP_BS_GET_FLOAT2 118 #define OP_BS_GET_BINARY2 119 #define OP_BS_SKIP_BITS2 120 #define OP_BS_TEST_TAIL2 121 diff --git a/src/libAtomVM/opcodesswitch.h b/src/libAtomVM/opcodesswitch.h index 68c29204fb..218f5df996 100644 --- a/src/libAtomVM/opcodesswitch.h +++ b/src/libAtomVM/opcodesswitch.h @@ -4761,6 +4761,74 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb) break; } + case OP_BS_GET_FLOAT2: { + uint32_t fail; + DECODE_LABEL(fail, pc) + term src; + DECODE_COMPACT_TERM(src, pc); + term arg2; + DECODE_COMPACT_TERM(arg2, pc); + term size; + DECODE_COMPACT_TERM(size, pc); + uint32_t unit; + DECODE_LITERAL(unit, pc); + uint32_t flags_value; + DECODE_LITERAL(flags_value, pc); + + #ifdef IMPL_CODE_LOADER + TRACE("bs_get_float2/7\n"); + #endif + + #ifdef IMPL_EXECUTE_LOOP + VERIFY_IS_MATCH_STATE(src, "bs_get_float"); + VERIFY_IS_INTEGER(size, "bs_get_float"); + + avm_int_t size_val = term_to_int(size); + + TRACE("bs_get_float2/7, fail=%u src=%p size=%u unit=%u flags=%x\n", (unsigned) fail, (void *) src, (unsigned) size_val, (unsigned) unit, (int) flags_value); + + avm_int_t increment = size_val * unit; + avm_float_t value; + term bs_bin = term_get_match_state_binary(src); + avm_int_t bs_offset = term_get_match_state_offset(src); + bool status; + switch (size_val) { + case 32: + status = bitstring_extract_f32(bs_bin, bs_offset, increment, flags_value, &value); + break; + case 64: + status = bitstring_extract_f64(bs_bin, bs_offset, increment, flags_value, &value); + break; + default: + TRACE("bs_get_float2: error extracting float.\n"); + status = false; + JUMP_TO_ADDRESS(mod->labels[fail]); + break; + } + if (UNLIKELY(!status)) { + TRACE("bs_get_float2: error extracting float.\n"); + JUMP_TO_ADDRESS(mod->labels[fail]); + } else { + term_set_match_state_offset(src, bs_offset + increment); + + if (UNLIKELY(memory_ensure_free_opt(ctx, FLOAT_SIZE, MEMORY_NO_GC) != MEMORY_GC_OK)) { + ctx->x[0] = ERROR_ATOM; + ctx->x[1] = OUT_OF_MEMORY_ATOM; + return term_invalid_term(); + } + term t = term_from_float(value, &ctx->heap); + #endif + + dreg_t dreg; + DECODE_DEST_REGISTER(dreg, pc); + + #ifdef IMPL_EXECUTE_LOOP + WRITE_REGISTER(dreg, t); + } + #endif + break; + } + case OP_BS_GET_BINARY2: { uint32_t fail; DECODE_LABEL(fail, pc)