Skip to content

Instantly share code, notes, and snippets.

@pmarreck
Created February 6, 2026 18:39
Show Gist options
  • Select an option

  • Save pmarreck/67e6e4f6942816dd07c00eed422c1047 to your computer and use it in GitHub Desktop.

Select an option

Save pmarreck/67e6e4f6942816dd07c00eed422c1047 to your computer and use it in GitHub Desktop.
Erlang/OTP flatmap size-in-header fixes (from Peter's Claude) - 5 bugs fixed, otp_build all passes
diff --git a/erts/emulator/beam/copy.c b/erts/emulator/beam/copy.c
index e09a2ab51c6..6487f830794 100644
--- a/erts/emulator/beam/copy.c
+++ b/erts/emulator/beam/copy.c
@@ -1589,9 +1589,8 @@ Uint copy_shared_perform_x(Eterm obj, Uint size, erts_shcopy_t *info,
switch (MAP_HEADER_TYPE(hdr)) {
case MAP_HEADER_TAG_FLATMAP_HEAD : {
flatmap_t *mp = (flatmap_t *) ptr;
- Uint n = flatmap_get_size(mp);
- ASSERT(n <= MAP_SMALL_MAP_LIMIT);
- *hp++ = *++ptr; /* keys */
+ Uint n = flatmap_get_size(mp) + 1;
+ ASSERT(flatmap_get_size(mp) <= MAP_SMALL_MAP_LIMIT);
while (n--) {
obj = *++ptr;
if (IS_CONST(obj)) {
diff --git a/erts/emulator/beam/erl_gc.h b/erts/emulator/beam/erl_gc.h
index 2c3b9331297..50a05fd2487 100644
--- a/erts/emulator/beam/erl_gc.h
+++ b/erts/emulator/beam/erl_gc.h
@@ -70,7 +70,7 @@ ERTS_GLB_INLINE Eterm* move_boxed(Eterm *ERTS_RESTRICT ptr, Eterm hdr, Eterm **h
switch ((hdr) & _HEADER_SUBTAG_MASK) {
case MAP_SUBTAG:
if (is_flatmap_header(hdr)) {
- nelts += flatmap_get_size(ptr);
+ nelts += 1 + flatmap_get_size(ptr);
} else {
nelts += hashmap_bitcount(MAP_HEADER_VAL(hdr));
}
diff --git a/erts/emulator/beam/erl_map.h b/erts/emulator/beam/erl_map.h
index 0b92b7ba4b1..57edf8ea29c 100644
--- a/erts/emulator/beam/erl_map.h
+++ b/erts/emulator/beam/erl_map.h
@@ -181,7 +181,7 @@ typedef struct hashmap_head_s {
#define make_flatmap_header(Size) \
(ASSERT((Size) <= MAP_SMALL_MAP_LIMIT), \
- MAKE_MAP_HEADER(MAP_HEADER_TAG_FLATMAP_HEAD, 0x1, (Size)))
+ MAKE_MAP_HEADER(MAP_HEADER_TAG_FLATMAP_HEAD, 0x0, (Size)))
#define MAP_HEADER_HAMT_HEAD_ARRAY \
MAKE_MAP_HEADER(MAP_HEADER_TAG_HAMT_HEAD_ARRAY,0x1,0xffff)
diff --git a/erts/emulator/beam/jit/arm/instr_guard_bifs.cpp b/erts/emulator/beam/jit/arm/instr_guard_bifs.cpp
index b70c3a40301..367139d883b 100644
--- a/erts/emulator/beam/jit/arm/instr_guard_bifs.cpp
+++ b/erts/emulator/beam/jit/arm/instr_guard_bifs.cpp
@@ -982,11 +982,33 @@ void BeamModuleAssembler::emit_bif_map_size(const ArgLabel &Fail,
a.bind(good_map);
{
- /* Size is now encoded in the header (upper 16 bits) */
- a.ldur(TMP1, emit_boxed_val(boxed_ptr, offsetof(flatmap_t, thing_word)));
- a.lsr(TMP1, TMP1, imm(16));
+ Label hashmap_size = a.newLabel(), done = a.newLabel();
+
+ /* Load header into TMP4 (not TMP1!) because boxed_ptr may
+ * alias TMP1 when emit_ptr_val returns src.reg unchanged. */
+ a.ldur(TMP4, emit_boxed_val(boxed_ptr, offsetof(flatmap_t, thing_word)));
+
+ /* Flatmaps store size in the header (upper 16 bits), but
+ * hashmaps store size in a separate word (hashmap_head_t.size).
+ * Check the map subtype to pick the right source.
+ * Use TMP5 for the mask (not TMP1) to avoid clobbering boxed_ptr. */
+ a.and_(TMP5, TMP4, imm(_HEADER_MAP_SUBTAG_MASK));
+ a.cmp(TMP5, imm(HAMT_SUBTAG_HEAD_FLATMAP));
+ a.b_ne(hashmap_size);
+
+ /* Flatmap: extract size from header upper 16 bits */
+ a.lsr(TMP1, TMP4, imm(16));
mov_imm(dst.reg, _TAG_IMMED1_SMALL);
a.bfi(dst.reg, TMP1, imm(_TAG_IMMED1_SIZE), imm(SMALL_BITS));
+ a.b(done);
+
+ /* Hashmap: read size from hashmap_head_t.size */
+ a.bind(hashmap_size);
+ a.ldur(TMP1, emit_boxed_val(boxed_ptr, sizeof(Eterm)));
+ mov_imm(dst.reg, _TAG_IMMED1_SMALL);
+ a.bfi(dst.reg, TMP1, imm(_TAG_IMMED1_SIZE), imm(SMALL_BITS));
+
+ a.bind(done);
flush_var(dst);
}
}
diff --git a/erts/emulator/beam/jit/x86/instr_guard_bifs.cpp b/erts/emulator/beam/jit/x86/instr_guard_bifs.cpp
index c5858f60691..fba01693412 100644
--- a/erts/emulator/beam/jit/x86/instr_guard_bifs.cpp
+++ b/erts/emulator/beam/jit/x86/instr_guard_bifs.cpp
@@ -735,15 +735,31 @@ void BeamModuleAssembler::emit_bif_map_size(const ArgLabel &Fail,
a.bind(good_map);
{
- /* Size is now encoded in the header (upper 16 bits) */
- preserve_cache(
- [&]() {
- a.mov(RET, emit_boxed_val(boxed_ptr, offsetof(flatmap_t, thing_word)));
- a.shr(RET, imm(16));
- a.shl(RET, imm(4));
- a.or_(RETb, imm(_TAG_IMMED1_SMALL));
- },
- RET);
+ Label hashmap_size = a.newLabel(), done = a.newLabel();
+
+ a.mov(RET, emit_boxed_val(boxed_ptr, offsetof(flatmap_t, thing_word)));
+
+ /* Flatmaps store size in the header (upper 16 bits), but
+ * hashmaps store size in a separate word (hashmap_head_t.size).
+ * Check the map subtype to pick the right source. */
+ a.mov(x86::ecx, RETd);
+ a.and_(x86::ecx, imm(_HEADER_MAP_SUBTAG_MASK));
+ a.cmp(x86::ecx, imm(HAMT_SUBTAG_HEAD_FLATMAP));
+ a.short_().jne(hashmap_size);
+
+ /* Flatmap: extract size from header upper 16 bits */
+ a.shr(RET, imm(16));
+ a.shl(RET, imm(4));
+ a.or_(RETb, imm(_TAG_IMMED1_SMALL));
+ a.short_().jmp(done);
+
+ /* Hashmap: read size from hashmap_head_t.size */
+ a.bind(hashmap_size);
+ a.mov(RET, emit_boxed_val(boxed_ptr, sizeof(Eterm)));
+ a.shl(RET, imm(4));
+ a.or_(RETb, imm(_TAG_IMMED1_SMALL));
+
+ a.bind(done);
mov_arg(Dst, RET);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment