From da5a1264d51d78381915c75e9663548c326c3233 Mon Sep 17 00:00:00 2001 From: Jakub Gonet Date: Wed, 17 Dec 2025 16:24:08 +0100 Subject: [PATCH] Fix floats on Wasm On wasm32, terms are aligned by 4 bytes. `memory_heap_allocation` returns a pointer for `n` terms. We used float_term_t to create an union between a term and float. However, floats are aligned to 8 bytes which made casting between union and term pointers unsafe. We replaced union usage to `memcpy` which allows unaligned memory access. The true solution would be to return correctly aligned memory from memory_heap_alloc but fix for that would need a lot more testing. Signed-off-by: Jakub Gonet --- src/libAtomVM/term.h | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/libAtomVM/term.h b/src/libAtomVM/term.h index c10c478e2..eae9f5863 100644 --- a/src/libAtomVM/term.h +++ b/src/libAtomVM/term.h @@ -2627,16 +2627,31 @@ static inline term term_from_float(avm_float_t f, Heap *heap) term *boxed_value = memory_heap_alloc(heap, FLOAT_SIZE); boxed_value[0] = ((FLOAT_SIZE - 1) << 6) | TERM_BOXED_FLOAT; +// `alignof(avm_float_t)` is different than `alignof(term)` on some archs (e.g. wasm32). +// `memory_heap_alloc` returns memory aligned to term size. +// +// Xtensa unaligned memcpy has bad codegen and this arch doesn't suffer from the alignment problem. +// See: https://gcc.gnu.org/pipermail/gcc-bugs/2023-October/839353.html +#if defined(__XTENSA__) float_term_t *boxed_float = (float_term_t *) (boxed_value + 1); boxed_float->f = f; - +#else + memcpy(boxed_value + 1, &f, sizeof(avm_float_t)); +#endif return ((term) boxed_value) | TERM_PRIMARY_BOXED; } static inline avm_float_t term_to_float(term t) { + // see `term_from_float` +#if defined(__XTENSA__) const float_term_t *boxed_float = (float_term_t *) (term_to_const_term_ptr(t) + 1); return boxed_float->f; +#else + avm_float_t result; + memcpy(&result, term_to_const_term_ptr(t) + 1, sizeof(avm_float_t)); + return result; +#endif } static inline bool term_is_number(term t)