mirror of
https://github.com/mruby/mruby
synced 2026-06-08 16:11:16 +00:00
error.h, array.h: support MRB_NAN_BOXING on 32-bit
On 32-bit platforms whose ABI gives 8-byte members 8-byte alignment (xtensa, ARM, MIPS, PowerPC, ...), MRB_NAN_BOXING failed to build with "RVALUE size must be within 5 words" because two structs got padded past the budget: - struct RBreak: had an existing MRB_USE_RBREAK_VALUE_UNION workaround that stores the value as uint32_t[] to avoid forcing 8-byte alignment on the struct, but the gate only enabled it for MRB_NO_BOXING. Extend to NAN_BOXING + 32-bit, with a NAN_BOXING-specific get/set (no separate tt to stash since nan-boxing encodes type in the bits). - struct RArray: MRB_ARY_NO_EMBED was similarly gated to NO_BOXING; embedded mrb_value[] forces 8-byte alignment of the inner union and pads the heap-form layout. Extend the gate to NAN_BOXING + 32-bit. Both gates now name the structural property (32-bit + mrb_value has an 8-byte aligned member) rather than enumerating boxing modes, so adding new boxing modes won't silently miss this class of bug again. i386's System V ABI gives uint64_t only 4-byte alignment, hiding the problem on x86 -m32; -malign-double simulates the strict-alignment ABI that exhibits the failure, and is what was used to verify the fix. Closes #6815, reported by dearblue. Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
+11
-1
@@ -20,7 +20,17 @@ typedef struct mrb_shared_array {
|
||||
mrb_value *ptr;
|
||||
} mrb_shared_array;
|
||||
|
||||
#if defined(MRB_32BIT) && defined(MRB_NO_BOXING) && (!defined(MRB_USE_FLOAT32) || defined(MRB_INT64)) && !defined(MRB_ARY_NO_EMBED)
|
||||
/* On 32-bit platforms whose ABI gives 8-byte members 8-byte alignment
|
||||
(ARM, MIPS, xtensa, ...), an embedded mrb_value array forces 8-byte
|
||||
alignment of the inner union, padding the heap-form layout and
|
||||
inflating struct size past the 5-word RVALUE limit. Disable embedding
|
||||
whenever mrb_value contains an 8-byte aligned member: nan-boxing
|
||||
(uint64_t), or no-boxing with int64_t/double inside the union. */
|
||||
#if defined(MRB_32BIT) && \
|
||||
(defined(MRB_NAN_BOXING) || \
|
||||
(defined(MRB_NO_BOXING) && \
|
||||
(!defined(MRB_USE_FLOAT32) || defined(MRB_INT64)))) && \
|
||||
!defined(MRB_ARY_NO_EMBED)
|
||||
# define MRB_ARY_NO_EMBED
|
||||
#endif
|
||||
|
||||
|
||||
+27
-6
@@ -37,7 +37,14 @@ MRB_API mrb_value mrb_exc_new_str(mrb_state *mrb, struct RClass* c, mrb_value st
|
||||
#define mrb_exc_new_lit(mrb, c, lit) mrb_exc_new_str(mrb, c, mrb_str_new_lit(mrb, lit))
|
||||
MRB_API mrb_noreturn void mrb_no_method_error(mrb_state *mrb, mrb_sym id, mrb_value args, const char *fmt, ...);
|
||||
|
||||
#if defined(MRB_NAN_BOXING) || defined(MRB_WORD_BOXING) || defined(MRB_64BIT)
|
||||
/* On 32-bit platforms whose ABI gives uint64_t/double 8-byte alignment
|
||||
(ARM, MIPS, PowerPC, xtensa, ...), embedding mrb_value directly in
|
||||
RBreak forces 8-byte alignment that pushes the struct past the 5-word
|
||||
RVALUE budget via padding. Store the value bits as a uint32_t array
|
||||
(4-byte aligned) to dodge that padding. Word-boxing's mrb_value is
|
||||
just a uintptr_t with no over-alignment, and 64-bit platforms have no
|
||||
alignment gap to begin with, so neither needs the workaround. */
|
||||
#if defined(MRB_64BIT) || defined(MRB_WORD_BOXING)
|
||||
#undef MRB_USE_RBREAK_VALUE_UNION
|
||||
#else
|
||||
#define MRB_USE_RBREAK_VALUE_UNION 1
|
||||
@@ -45,7 +52,8 @@ MRB_API mrb_noreturn void mrb_no_method_error(mrb_state *mrb, mrb_sym id, mrb_va
|
||||
|
||||
/*
|
||||
* flags:
|
||||
* 0..7: enum mrb_vtype (only when defined MRB_USE_RBREAK_VALUE_UNION)
|
||||
* 0..7: enum mrb_vtype (only when MRB_USE_RBREAK_VALUE_UNION and
|
||||
* !MRB_NAN_BOXING; nan-boxing encodes the type in the bits)
|
||||
* 8..10: RBREAK_TAGs in src/vm.c (otherwise, set to 0)
|
||||
*/
|
||||
struct RBreak {
|
||||
@@ -53,11 +61,11 @@ struct RBreak {
|
||||
uintptr_t ci_break_index; // The top-level ci index to break. One before the return destination.
|
||||
#ifndef MRB_USE_RBREAK_VALUE_UNION
|
||||
mrb_value val;
|
||||
#elif defined(MRB_NAN_BOXING)
|
||||
/* nan-boxing: mrb_value is a single 64-bit word (type encoded in NaN bits) */
|
||||
uint32_t value[sizeof(mrb_value) / sizeof(uint32_t)];
|
||||
#else
|
||||
/* Store value as uint32_t words instead of union mrb_value_union
|
||||
to avoid 8-byte alignment of int64_t/double on 32-bit platforms
|
||||
(e.g., ARM, MIPS, PowerPC) which would inflate struct size beyond
|
||||
the 5-word RVALUE limit due to padding. */
|
||||
/* no-boxing: store only the union bits; tt goes in flags */
|
||||
uint32_t value[sizeof(union mrb_value_union) / sizeof(uint32_t)];
|
||||
#endif
|
||||
};
|
||||
@@ -65,6 +73,19 @@ struct RBreak {
|
||||
#ifndef MRB_USE_RBREAK_VALUE_UNION
|
||||
#define mrb_break_value_get(brk) ((brk)->val)
|
||||
#define mrb_break_value_set(brk, v) ((brk)->val = v)
|
||||
#elif defined(MRB_NAN_BOXING)
|
||||
static inline mrb_value
|
||||
mrb_break_value_get(struct RBreak *brk)
|
||||
{
|
||||
mrb_value val;
|
||||
memcpy(&val, brk->value, sizeof(val));
|
||||
return val;
|
||||
}
|
||||
static inline void
|
||||
mrb_break_value_set(struct RBreak *brk, mrb_value val)
|
||||
{
|
||||
memcpy(brk->value, &val, sizeof(val));
|
||||
}
|
||||
#else
|
||||
#define RBREAK_VALUE_TT_MASK ((1 << 8) - 1)
|
||||
static inline mrb_value
|
||||
|
||||
Reference in New Issue
Block a user