Encode iseq node_id with LEB128

Node ids are small non-negative integers (-1 for synthetic instructions),
so a LEB128 stores them in 1-2 bytes instead of a 4-byte int.

On Shopify's monolith, this reduces the boot heap memsize by 40MB.
This commit is contained in:
Peter Zhu committed 2026-10-25 19:59:05 +09:00
1 parent 78a7f126f3
commit 2c09f747ce
4 files changed
+53 -20
Notes: git 2026-10-25 10:59:39 +00:00

No files matched your search

+23 -9
View File
@@ -2624,6 +2624,19 @@ fix_sp_depth(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
return stack_max;
}
#ifdef USE_ISEQ_NODE_ID
static void
add_node_id(uint8_t *node_ids, unsigned int *node_ids_size, int node_id)
{
uint32_t v = (uint32_t)node_id + 1;
do {
uint8_t byte = v & 0x7f;
v >>= 7;
node_ids[(*node_ids_size)++] = v ? (byte | 0x80) : byte;
} while (v);
}
#endif
static int
add_insn_info(struct iseq_insn_info_entry *insns_info, unsigned int *positions,
int insns_info_index, int code_index, const INSN *iobj)
@@ -2771,7 +2784,7 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
const int positions_size = insn_num;
positions = ALLOC_N(unsigned int, insn_num);
#ifdef USE_ISEQ_NODE_ID
int *node_ids = ALLOC_N(int, insn_num);
uint8_t *node_ids = ALLOC_N(uint8_t, (size_t)insn_num * 5);
unsigned int node_ids_size = 0;
#endif
if (ISEQ_IS_SIZE(body)) {
@@ -2952,7 +2965,7 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
}
if (add_insn_info(insns_info, positions, insns_info_index, code_index, iobj)) insns_info_index++;
#ifdef USE_ISEQ_NODE_ID
node_ids[node_ids_size++] = iobj->insn_info.node_id;
add_node_id(node_ids, &node_ids_size, iobj->insn_info.node_id);
#endif
code_index += len;
break;
@@ -2990,7 +3003,7 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
}
if (add_adjust_info(insns_info, positions, insns_info_index, code_index, adjust)) insns_info_index++;
#ifdef USE_ISEQ_NODE_ID
node_ids[node_ids_size++] = -1;
add_node_id(node_ids, &node_ids_size, -1);
#endif
}
if (diff > 1) {
@@ -3006,7 +3019,7 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
SIZED_FREE_N(insns_info, insns_info_size);
SIZED_FREE_N(positions, positions_size);
#ifdef USE_ISEQ_NODE_ID
SIZED_FREE_N(node_ids, insns_info_size);
SIZED_FREE_N(node_ids, (size_t)insns_info_size * 5);
#endif
if (ISEQ_MBITS_BUFLEN(code_size) > 1) {
SIZED_FREE_N(mark_offset_bits, ISEQ_MBITS_BUFLEN(code_index));
@@ -3058,6 +3071,7 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
body->insns_info.positions_or_succ_index_table.positions = positions;
body->insns_info.size = insns_info_index;
#ifdef USE_ISEQ_NODE_ID
SIZED_REALLOC_N(node_ids, uint8_t, node_ids_size, (size_t)insns_info_size * 5);
body->insns_info.node_ids = node_ids;
body->insns_info.node_ids_size = node_ids_size;
#endif
@@ -12800,7 +12814,7 @@ typedef uint32_t ibf_offset_t;
#define IBF_MAJOR_VERSION ISEQ_MAJOR_VERSION
#ifdef RUBY_DEVEL
#define IBF_DEVEL_VERSION 10
#define IBF_DEVEL_VERSION 11
#define IBF_MINOR_VERSION (ISEQ_MINOR_VERSION * 10000 + IBF_DEVEL_VERSION)
#else
#define IBF_MINOR_VERSION ISEQ_MINOR_VERSION
@@ -13528,7 +13542,7 @@ ibf_dump_insns_info_body(struct ibf_dump *dump, const rb_iseq_t *iseq)
}
#ifdef USE_ISEQ_NODE_ID
if (body->insns_info.node_ids_size > 0) {
ibf_dump_write(dump, body->insns_info.node_ids, sizeof(int) * body->insns_info.node_ids_size);
ibf_dump_write(dump, body->insns_info.node_ids, body->insns_info.node_ids_size);
}
#endif
@@ -13552,11 +13566,11 @@ ibf_load_insns_info_body(const struct ibf_load *load, ibf_offset_t body_offset,
}
#ifdef USE_ISEQ_NODE_ID
if (node_ids_size > 0) {
if ((size_t)node_ids_size > (load->current_buffer->size - reading_pos) / sizeof(int)) {
if (node_ids_size > load->current_buffer->size - reading_pos) {
rb_raise(rb_eRuntimeError, "invalid byte sequence");
}
int *node_ids = ALLOC_N(int, node_ids_size);
memcpy(node_ids, load->current_buffer->buff + reading_pos, sizeof(int) * node_ids_size);
uint8_t *node_ids = ALLOC_N(uint8_t, node_ids_size);
memcpy(node_ids, load->current_buffer->buff + reading_pos, node_ids_size);
load_body->insns_info.node_ids = node_ids;
load_body->insns_info.node_ids_size = node_ids_size;
}
+28 -9
View File
@@ -597,7 +597,7 @@ rb_iseq_memsize(const rb_iseq_t *iseq)
size += body->iseq_size * sizeof(VALUE);
size += body->insns_info.size * (sizeof(struct iseq_insn_info_entry) + sizeof(unsigned int));
#ifdef USE_ISEQ_NODE_ID
size += body->insns_info.node_ids_size * sizeof(int);
size += body->insns_info.node_ids_size;
#endif
size += body->local_table_size * sizeof(ID); // body->local_table
if (!ISEQ_LVAR_STATES_EMBED_P(body->local_table_size) && body->lvar_states.list) {
@@ -2578,6 +2578,21 @@ rb_iseq_line_no(const rb_iseq_t *iseq, size_t pos)
}
#ifdef USE_ISEQ_NODE_ID
static int
node_id_stream_read(const uint8_t **ptr, const uint8_t *end)
{
uint32_t v = 0;
unsigned int shift = 0;
while (*ptr < end) {
uint8_t byte = **ptr;
(*ptr)++;
v |= (uint32_t)(byte & 0x7f) << shift;
if (!(byte & 0x80)) break;
shift += 7;
}
return (int)v - 1;
}
int
rb_iseq_node_id(const rb_iseq_t *iseq, size_t pos)
{
@@ -2587,17 +2602,17 @@ rb_iseq_node_id(const rb_iseq_t *iseq, size_t pos)
}
const VALUE *code = rb_iseq_original_iseq(iseq);
const int *node_ids = body->insns_info.node_ids;
unsigned int i = 0;
const uint8_t *ptr = body->insns_info.node_ids;
const uint8_t *end = ptr + body->insns_info.node_ids_size;
size_t code_pos = 0;
while (i < body->insns_info.node_ids_size && code_pos < body->iseq_size) {
while (ptr < end && code_pos < body->iseq_size) {
size_t len = (size_t)insn_len(code[code_pos]);
int node_id = node_id_stream_read(&ptr, end);
if (pos < code_pos + len) {
return node_ids[i];
return node_id;
}
code_pos += len;
i++;
}
VM_ASSERT(0); /* node_ids out of sync with the instruction sequence */
@@ -3783,6 +3798,10 @@ iseq_data_to_ary(const rb_iseq_t *iseq)
prev_insn_info = NULL;
#ifdef USE_ISEQ_NODE_ID
VALUE node_ids = rb_ary_new();
/* The node id stream holds one varint per instruction, in the same
* order as the instructions in nbody. */
const uint8_t *nid_ptr = iseq_body->insns_info.node_ids;
const uint8_t *nid_end = nid_ptr ? nid_ptr + iseq_body->insns_info.node_ids_size : NULL;
#endif
for (l=0, pos=0; l<RARRAY_LEN(nbody); l++) {
@@ -3796,9 +3815,9 @@ iseq_data_to_ary(const rb_iseq_t *iseq)
info = get_insn_info(iseq, pos);
#ifdef USE_ISEQ_NODE_ID
VM_ASSERT(l < (long)iseq_body->insns_info.node_ids_size);
rb_ary_push(node_ids, INT2FIX(l < (long)iseq_body->insns_info.node_ids_size ?
iseq_body->insns_info.node_ids[l] : -1));
VM_ASSERT(nid_ptr && nid_ptr < nid_end);
rb_ary_push(node_ids, INT2FIX(nid_ptr && nid_ptr < nid_end ?
node_id_stream_read(&nid_ptr, nid_end) : -1));
#endif
if (prev_insn_info != info) {
+1 -1
View File
@@ -533,7 +533,7 @@ struct rb_iseq_constant_body {
} positions_or_succ_index_table;
unsigned int size;
#ifdef USE_ISEQ_NODE_ID
const int *node_ids;
const uint8_t *node_ids;
unsigned int node_ids_size;
#endif
} insns_info;
+1 -1
View File
@@ -1276,7 +1276,7 @@ pub struct rb_iseq_constant_body_iseq_insn_info {
pub body: *const iseq_insn_info_entry,
pub positions_or_succ_index_table: rb_iseq_constant_body_iseq_insn_info__bindgen_ty_1,
pub size: ::std::os::raw::c_uint,
pub node_ids: *const ::std::os::raw::c_int,
pub node_ids: *const u8,
pub node_ids_size: ::std::os::raw::c_uint,
}
#[repr(C)]