[PRISM] Use a flat lookup table for locals instead of an st_table

This commit is contained in:
Kevin Newton committed 2026-03-20 20:37:26 -04:00
1 parent e6cf66a8e7
commit e10f2521a2
4 files changed
+221 -157
Notes: git 2026-03-21 00:37:53 +00:00

No files matched your search

+53 -35
View File
@@ -1096,41 +1096,15 @@ rb_iseq_new_with_opt(VALUE ast_value, VALUE name, VALUE path, VALUE realpath,
return iseq_translate(iseq);
}
struct pm_iseq_new_with_opt_data {
rb_iseq_t *iseq;
pm_scope_node_t *node;
};
VALUE
pm_iseq_new_with_opt_try(VALUE d)
{
struct pm_iseq_new_with_opt_data *data = (struct pm_iseq_new_with_opt_data *)d;
// This can compile child iseqs, which can raise syntax errors
pm_iseq_compile_node(data->iseq, data->node);
// This raises an exception if there is a syntax error
finish_iseq_build(data->iseq);
return Qundef;
}
/**
* This is a step in the prism compiler that is called once all of the various
* options have been established. It is called from one of the pm_iseq_new_*
* functions or from the RubyVM::InstructionSequence APIs. It is responsible for
* allocating the instruction sequence, calling into the compiler, and returning
* the built instruction sequence.
*
* Importantly, this is also the function where the compiler is re-entered to
* compile child instruction sequences. A child instruction sequence is always
* compiled using a scope node, which is why we cast it explicitly to that here
* in the parameters (as opposed to accepting a generic pm_node_t *).
* Core implementation for building a prism iseq. This does not use rb_protect,
* so any exceptions (e.g. from finish_iseq_build) propagate normally up the
* call stack — matching the parse.y compiler's behavior.
*/
rb_iseq_t *
pm_iseq_new_with_opt(pm_scope_node_t *node, VALUE name, VALUE path, VALUE realpath,
int first_lineno, const rb_iseq_t *parent, int isolated_depth,
enum rb_iseq_type type, const rb_compile_option_t *option, int *error_state)
pm_iseq_build(pm_scope_node_t *node, VALUE name, VALUE path, VALUE realpath,
int first_lineno, const rb_iseq_t *parent, int isolated_depth,
enum rb_iseq_type type, const rb_compile_option_t *option)
{
rb_iseq_t *iseq = iseq_alloc();
ISEQ_BODY(iseq)->prism = true;
@@ -1157,15 +1131,59 @@ pm_iseq_new_with_opt(pm_scope_node_t *node, VALUE name, VALUE path, VALUE realpa
prepare_iseq_build(iseq, name, path, realpath, first_lineno, &code_location, node->ast_node->node_id,
parent, isolated_depth, type, node->script_lines == NULL ? Qnil : *node->script_lines, option);
pm_iseq_compile_node(iseq, node);
finish_iseq_build(iseq);
return iseq_translate(iseq);
}
struct pm_iseq_new_with_opt_data {
rb_iseq_t *iseq;
pm_scope_node_t *node;
VALUE name, path, realpath;
int first_lineno, isolated_depth;
const rb_iseq_t *parent;
enum rb_iseq_type type;
const rb_compile_option_t *option;
};
static VALUE
pm_iseq_new_with_opt_try(VALUE d)
{
struct pm_iseq_new_with_opt_data *data = (struct pm_iseq_new_with_opt_data *)d;
data->iseq = pm_iseq_build(data->node, data->name, data->path, data->realpath,
data->first_lineno, data->parent, data->isolated_depth,
data->type, data->option);
return Qundef;
}
/**
* This is a step in the prism compiler that is called once all of the various
* options have been established. It is called from one of the pm_iseq_new_*
* functions or from the RubyVM::InstructionSequence APIs.
*
* This function uses rb_protect to catch exceptions, storing the error state
* in the provided out parameter. This is only needed at top-level entry points
* where the caller wants to handle errors gracefully. Child iseqs compiled
* during the compilation process do NOT go through this function — they use
* pm_iseq_build directly, letting exceptions propagate naturally (matching
* the parse.y compiler's behavior).
*/
rb_iseq_t *
pm_iseq_new_with_opt(pm_scope_node_t *node, VALUE name, VALUE path, VALUE realpath,
int first_lineno, const rb_iseq_t *parent, int isolated_depth,
enum rb_iseq_type type, const rb_compile_option_t *option, int *error_state)
{
struct pm_iseq_new_with_opt_data data = {
.iseq = iseq,
.node = node
.node = node, .name = name, .path = path, .realpath = realpath,
.first_lineno = first_lineno, .parent = parent,
.isolated_depth = isolated_depth, .type = type, .option = option
};
rb_protect(pm_iseq_new_with_opt_try, (VALUE)&data, error_state);
if (*error_state) return NULL;
return iseq_translate(iseq);
return data.iseq;
}
rb_iseq_t *
+81 -112
View File
@@ -142,6 +142,19 @@ pm_iseq_add_setlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, int line, int node
#define PM_COMPILE_NOT_POPPED(node) \
pm_compile_node(iseq, (node), ret, false, scope_node)
// Direct-indexed lookup table. -1 means "not present".
#define PM_INDEX_LOOKUP_TABLE_INIT { .values = NULL, .capacity = 0, .owned = false }
static inline void
pm_index_lookup_table_init(pm_index_lookup_table_t *table, int constants_size, rb_iseq_t *iseq)
{
int capacity = constants_size + PM_INDEX_LOOKUP_SPECIALS;
table->values = compile_data_alloc2_type(iseq, int, capacity);
memset(table->values, -1, capacity * sizeof(int));
table->capacity = capacity;
table->owned = false;
}
/**
* Cached line lookup that avoids repeated binary searches. Since the compiler
* walks the AST roughly in source order, consecutive lookups tend to be for
@@ -1311,14 +1324,15 @@ static pm_local_index_t
pm_lookup_local_index(rb_iseq_t *iseq, const pm_scope_node_t *scope_node, pm_constant_id_t constant_id, int start_depth)
{
pm_local_index_t lindex = { 0 };
st_data_t local_index;
int local_index;
int level;
for (level = 0; level < start_depth; level++) {
scope_node = scope_node->previous;
}
while (!st_lookup(scope_node->index_lookup_table, constant_id, &local_index)) {
while (!pm_index_lookup_table_lookup(&scope_node->index_lookup_table, constant_id, &local_index))
{
level++;
if (scope_node->previous) {
@@ -1342,12 +1356,10 @@ pm_lookup_local_index(rb_iseq_t *iseq, const pm_scope_node_t *scope_node, pm_con
// We add a constants mapping on the scope_node which is a mapping from
// these constant_id indexes to the CRuby IDs that they represent.
// This helper method allows easy access to those IDs
static ID
static inline ID
pm_constant_id_lookup(const pm_scope_node_t *scope_node, pm_constant_id_t constant_id)
{
if (constant_id < 1 || constant_id > pm_parser_constants_size(scope_node->parser)) {
rb_bug("constant_id out of range: %u", (unsigned int)constant_id);
}
RUBY_ASSERT(constant_id >= 1 && constant_id <= pm_parser_constants_size(scope_node->parser));
return scope_node->constants[constant_id - 1];
}
@@ -1356,18 +1368,11 @@ pm_new_child_iseq(rb_iseq_t *iseq, pm_scope_node_t *node, VALUE name, const rb_i
{
debugs("[new_child_iseq]> ---------------------------------------\n");
int isolated_depth = ISEQ_COMPILE_DATA(iseq)->isolated_depth;
int error_state;
rb_iseq_t *ret_iseq = pm_iseq_new_with_opt(node, name,
rb_iseq_t *ret_iseq = pm_iseq_build(node, name,
rb_iseq_path(iseq), rb_iseq_realpath(iseq),
line_no, parent,
isolated_depth ? isolated_depth + 1 : 0,
type, ISEQ_COMPILE_DATA(iseq)->option, &error_state);
if (error_state) {
pm_scope_node_destroy(node);
RUBY_ASSERT(ret_iseq == NULL);
rb_jump_tag(error_state);
}
type, ISEQ_COMPILE_DATA(iseq)->option);
debugs("[new_child_iseq]< ---------------------------------------\n");
return ret_iseq;
}
@@ -3307,30 +3312,27 @@ pm_compile_pattern(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t
void
pm_scope_node_init(const pm_node_t *node, pm_scope_node_t *scope, pm_scope_node_t *previous)
{
// This is very important, otherwise the scope node could be seen as having
// certain flags set that _should not_ be set.
memset(scope, 0, sizeof(pm_scope_node_t));
if (previous) {
// Copy inherited fields from the parent scope in one shot, then
// zero out the fields that are scope-specific.
*scope = *previous;
scope->locals = (pm_constant_id_list_t) { 0 };
scope->parameters = NULL;
scope->body = NULL;
scope->local_table_for_iseq_size = 0;
scope->index_lookup_table = (pm_index_lookup_table_t) PM_INDEX_LOOKUP_TABLE_INIT;
scope->pre_execution_anchor = NULL;
}
else {
memset(scope, 0, sizeof(pm_scope_node_t));
}
scope->base.type = PM_SCOPE_NODE;
scope->base.location.start = node->location.start;
scope->base.location.length = node->location.length;
scope->previous = previous;
scope->ast_node = (pm_node_t *) node;
if (previous) {
scope->parser = previous->parser;
scope->options = previous->options;
scope->line_offsets = previous->line_offsets;
scope->start_line = previous->start_line;
scope->encoding = previous->encoding;
scope->filepath_encoding = previous->filepath_encoding;
scope->constants = previous->constants;
scope->coverage_enabled = previous->coverage_enabled;
scope->script_lines = previous->script_lines;
scope->last_line = previous->last_line;
}
switch (PM_NODE_TYPE(node)) {
case PM_BLOCK_NODE: {
const pm_block_node_t *cast = (const pm_block_node_t *) node;
@@ -3427,8 +3429,8 @@ pm_scope_node_init(const pm_node_t *node, pm_scope_node_t *scope, pm_scope_node_
void
pm_scope_node_destroy(pm_scope_node_t *scope_node)
{
if (scope_node->index_lookup_table) {
st_free_table(scope_node->index_lookup_table);
if (scope_node->index_lookup_table.owned) {
xfree(scope_node->index_lookup_table.values);
}
}
@@ -3618,8 +3620,7 @@ pm_compile_builtin_mandatory_only_method(rb_iseq_t *iseq, pm_scope_node_t *scope
pm_scope_node_t next_scope_node;
pm_scope_node_init(&def.base, &next_scope_node, scope_node);
int error_state;
const rb_iseq_t *mandatory_only_iseq = pm_iseq_new_with_opt(
const rb_iseq_t *mandatory_only_iseq = pm_iseq_build(
&next_scope_node,
rb_iseq_base_label(iseq),
rb_iseq_path(iseq),
@@ -3628,16 +3629,10 @@ pm_compile_builtin_mandatory_only_method(rb_iseq_t *iseq, pm_scope_node_t *scope
NULL,
0,
ISEQ_TYPE_METHOD,
ISEQ_COMPILE_DATA(iseq)->option,
&error_state
ISEQ_COMPILE_DATA(iseq)->option
);
RB_OBJ_WRITE(iseq, &ISEQ_BODY(iseq)->mandatory_only_iseq, (VALUE)mandatory_only_iseq);
if (error_state) {
RUBY_ASSERT(ISEQ_BODY(iseq)->mandatory_only_iseq == NULL);
rb_jump_tag(error_state);
}
pm_scope_node_destroy(&next_scope_node);
return COMPILE_OK;
}
@@ -4755,33 +4750,7 @@ pm_add_ensure_iseq(LINK_ANCHOR *const ret, rb_iseq_t *iseq, int is_return, pm_sc
PUSH_SEQ(ret, ensure);
}
struct pm_local_table_insert_ctx {
pm_scope_node_t *scope_node;
rb_ast_id_table_t *local_table_for_iseq;
int local_index;
};
static int
pm_local_table_insert_func(st_data_t *key, st_data_t *value, st_data_t arg, int existing)
{
if (!existing) {
pm_constant_id_t constant_id = (pm_constant_id_t) *key;
struct pm_local_table_insert_ctx * ctx = (struct pm_local_table_insert_ctx *) arg;
pm_scope_node_t *scope_node = ctx->scope_node;
rb_ast_id_table_t *local_table_for_iseq = ctx->local_table_for_iseq;
int local_index = ctx->local_index;
ID local = pm_constant_id_lookup(scope_node, constant_id);
local_table_for_iseq->ids[local_index] = local;
*value = (st_data_t)local_index;
ctx->local_index++;
}
return ST_CONTINUE;
}
/**
* Insert a local into the local table for the iseq. This is used to create the
@@ -4789,24 +4758,23 @@ pm_local_table_insert_func(st_data_t *key, st_data_t *value, st_data_t arg, int
* inserted are regular named locals, as opposed to special forwarding locals.
*/
static void
pm_insert_local_index(pm_constant_id_t constant_id, int local_index, st_table *index_lookup_table, rb_ast_id_table_t *local_table_for_iseq, pm_scope_node_t *scope_node)
pm_insert_local_index(pm_constant_id_t constant_id, int local_index, pm_index_lookup_table_t *index_lookup_table, rb_ast_id_table_t *local_table_for_iseq, pm_scope_node_t *scope_node)
{
RUBY_ASSERT((constant_id & PM_SPECIAL_CONSTANT_FLAG) == 0);
ID local = pm_constant_id_lookup(scope_node, constant_id);
local_table_for_iseq->ids[local_index] = local;
st_insert(index_lookup_table, (st_data_t) constant_id, (st_data_t) local_index);
pm_index_lookup_table_insert(index_lookup_table, constant_id, local_index);
}
/**
* Insert a local into the local table for the iseq that is a special forwarding
* local variable.
* Insert a special forwarding local (*, **, &, ...) into the local table.
*/
static void
pm_insert_local_special(ID local_name, int local_index, st_table *index_lookup_table, rb_ast_id_table_t *local_table_for_iseq)
pm_insert_local_special(pm_constant_id_t special_id, ID local_name, int local_index, pm_index_lookup_table_t *index_lookup_table, rb_ast_id_table_t *local_table_for_iseq)
{
local_table_for_iseq->ids[local_index] = local_name;
st_insert(index_lookup_table, (st_data_t) (local_name | PM_SPECIAL_CONSTANT_FLAG), (st_data_t) local_index);
pm_index_lookup_table_insert(index_lookup_table, special_id, local_index);
}
/**
@@ -4816,7 +4784,7 @@ pm_insert_local_special(ID local_name, int local_index, st_table *index_lookup_t
* local and index lookup tables and increments the local index as necessary.
*/
static int
pm_compile_destructured_param_locals(const pm_multi_target_node_t *node, st_table *index_lookup_table, rb_ast_id_table_t *local_table_for_iseq, pm_scope_node_t *scope_node, int local_index)
pm_compile_destructured_param_locals(const pm_multi_target_node_t *node, pm_index_lookup_table_t *index_lookup_table, rb_ast_id_table_t *local_table_for_iseq, pm_scope_node_t *scope_node, int local_index)
{
for (size_t index = 0; index < node->lefts.size; index++) {
const pm_node_t *left = node->lefts.nodes[index];
@@ -6312,8 +6280,9 @@ pm_compile_scope_node(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_nod
// hidden variables and multi target nodes
size_t locals_size = locals->size;
// Index lookup table buffer size is only the number of the locals
st_table *index_lookup_table = st_init_numtable();
// Index lookup table buffer size is only the number of the locals.
// We'll initialize it after computing table_size below.
pm_index_lookup_table_t index_lookup_table = PM_INDEX_LOOKUP_TABLE_INIT;
int table_size = (int) locals_size;
@@ -6437,6 +6406,10 @@ pm_compile_scope_node(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_nod
rb_ast_id_table_t *local_table_for_iseq = ALLOCV(idtmp, sizeof(rb_ast_id_table_t) + table_size * sizeof(ID));
local_table_for_iseq->size = table_size;
// Init the direct-indexed lookup table. The capacity is based on the
// parser's constant pool size (for regular locals) plus special slots.
pm_index_lookup_table_init(&index_lookup_table, (int) pm_parser_constants_size(scope_node->parser), iseq);
//********END OF STEP 1**********
//********STEP 2**********
@@ -6489,7 +6462,7 @@ pm_compile_scope_node(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_nod
local_table_for_iseq->ids[local_index] = local;
}
else {
pm_insert_local_index(param->name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
pm_insert_local_index(param->name, local_index, &index_lookup_table, local_table_for_iseq, scope_node);
}
break;
@@ -6522,7 +6495,7 @@ pm_compile_scope_node(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_nod
local_table_for_iseq->ids[local_index] = local;
}
else {
pm_insert_local_index(name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
pm_insert_local_index(name, local_index, &index_lookup_table, local_table_for_iseq, scope_node);
}
}
}
@@ -6548,14 +6521,14 @@ pm_compile_scope_node(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_nod
local_table_for_iseq->ids[local_index] = local;
}
else {
pm_insert_local_index(name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
pm_insert_local_index(name, local_index, &index_lookup_table, local_table_for_iseq, scope_node);
}
}
else {
// def foo(a, (b, *c, d), e = 1, *, g, (h, *i, j), k:, l: 1, **m, &n)
// ^
body->param.flags.anon_rest = true;
pm_insert_local_special(idMULT, local_index, index_lookup_table, local_table_for_iseq);
pm_insert_local_special(PM_CONSTANT_MULT, idMULT, local_index, &index_lookup_table, local_table_for_iseq);
}
local_index++;
@@ -6595,7 +6568,7 @@ pm_compile_scope_node(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_nod
local_table_for_iseq->ids[local_index] = local;
}
else {
pm_insert_local_index(param->name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
pm_insert_local_index(param->name, local_index, &index_lookup_table, local_table_for_iseq, scope_node);
}
break;
}
@@ -6630,7 +6603,7 @@ pm_compile_scope_node(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_nod
local_table_for_iseq->ids[local_index] = local;
}
else {
pm_insert_local_index(name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
pm_insert_local_index(name, local_index, &index_lookup_table, local_table_for_iseq, scope_node);
}
local_index++;
}
@@ -6660,7 +6633,7 @@ pm_compile_scope_node(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_nod
local_table_for_iseq->ids[local_index] = local;
}
else {
pm_insert_local_index(name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
pm_insert_local_index(name, local_index, &index_lookup_table, local_table_for_iseq, scope_node);
}
local_index++;
}
@@ -6722,12 +6695,12 @@ pm_compile_scope_node(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_nod
local_table_for_iseq->ids[local_index] = local;
}
else {
pm_insert_local_index(constant_id, local_index, index_lookup_table, local_table_for_iseq, scope_node);
pm_insert_local_index(constant_id, local_index, &index_lookup_table, local_table_for_iseq, scope_node);
}
}
else {
body->param.flags.anon_kwrest = true;
pm_insert_local_special(idPow, local_index, index_lookup_table, local_table_for_iseq);
pm_insert_local_special(PM_CONSTANT_POW, idPow, local_index, &index_lookup_table, local_table_for_iseq);
}
local_index++;
@@ -6741,7 +6714,7 @@ pm_compile_scope_node(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_nod
body->param.rest_start = local_index;
body->param.flags.has_rest = true;
body->param.flags.anon_rest = true;
pm_insert_local_special(idMULT, local_index++, index_lookup_table, local_table_for_iseq);
pm_insert_local_special(PM_CONSTANT_MULT, idMULT, local_index++, &index_lookup_table, local_table_for_iseq);
// Add the anonymous **
RUBY_ASSERT(!body->param.flags.has_kw);
@@ -6750,16 +6723,16 @@ pm_compile_scope_node(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_nod
body->param.flags.anon_kwrest = true;
body->param.keyword = keyword = ZALLOC_N(struct rb_iseq_param_keyword, 1);
keyword->rest_start = local_index;
pm_insert_local_special(idPow, local_index++, index_lookup_table, local_table_for_iseq);
pm_insert_local_special(PM_CONSTANT_POW, idPow, local_index++, &index_lookup_table, local_table_for_iseq);
// Add the anonymous &
body->param.block_start = local_index;
body->param.flags.has_block = true;
pm_insert_local_special(idAnd, local_index++, index_lookup_table, local_table_for_iseq);
pm_insert_local_special(PM_CONSTANT_AND, idAnd, local_index++, &index_lookup_table, local_table_for_iseq);
}
// Add the ...
pm_insert_local_special(idDot3, local_index++, index_lookup_table, local_table_for_iseq);
pm_insert_local_special(PM_CONSTANT_DOT3, idDot3, local_index++, &index_lookup_table, local_table_for_iseq);
break;
}
default:
@@ -6785,11 +6758,11 @@ pm_compile_scope_node(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_nod
local_table_for_iseq->ids[local_index] = local;
}
else {
pm_insert_local_index(name, local_index, index_lookup_table, local_table_for_iseq, scope_node);
pm_insert_local_index(name, local_index, &index_lookup_table, local_table_for_iseq, scope_node);
}
}
else {
pm_insert_local_special(idAnd, local_index, index_lookup_table, local_table_for_iseq);
pm_insert_local_special(PM_CONSTANT_AND, idAnd, local_index, &index_lookup_table, local_table_for_iseq);
}
local_index++;
@@ -6825,7 +6798,7 @@ pm_compile_scope_node(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_nod
const pm_node_t *required = requireds_list->nodes[i];
if (PM_NODE_TYPE_P(required, PM_MULTI_TARGET_NODE)) {
local_index = pm_compile_destructured_param_locals((const pm_multi_target_node_t *) required, index_lookup_table, local_table_for_iseq, scope_node, local_index);
local_index = pm_compile_destructured_param_locals((const pm_multi_target_node_t *) required, &index_lookup_table, local_table_for_iseq, scope_node, local_index);
}
}
}
@@ -6839,7 +6812,7 @@ pm_compile_scope_node(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_nod
const pm_node_t *post = posts_list->nodes[i];
if (PM_NODE_TYPE_P(post, PM_MULTI_TARGET_NODE)) {
local_index = pm_compile_destructured_param_locals((const pm_multi_target_node_t *) post, index_lookup_table, local_table_for_iseq, scope_node, local_index);
local_index = pm_compile_destructured_param_locals((const pm_multi_target_node_t *) post, &index_lookup_table, local_table_for_iseq, scope_node, local_index);
}
}
}
@@ -6867,7 +6840,7 @@ pm_compile_scope_node(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_nod
const uint8_t param_name[] = { '_', '1' + i };
pm_constant_id_t constant_id = pm_parser_constant_find(scope_node->parser, param_name, 2);
RUBY_ASSERT(constant_id && "parser should fill in any gaps in numbered parameters");
pm_insert_local_index(constant_id, local_index, index_lookup_table, local_table_for_iseq, scope_node);
pm_insert_local_index(constant_id, local_index, &index_lookup_table, local_table_for_iseq, scope_node);
}
body->param.lead_num = maximum;
body->param.flags.has_lead = true;
@@ -6890,7 +6863,7 @@ pm_compile_scope_node(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_nod
if (block_locals && block_locals->size) {
for (size_t i = 0; i < block_locals->size; i++, local_index++) {
pm_constant_id_t constant_id = ((const pm_block_local_variable_node_t *) block_locals->nodes[i])->name;
pm_insert_local_index(constant_id, local_index, index_lookup_table, local_table_for_iseq, scope_node);
pm_insert_local_index(constant_id, local_index, &index_lookup_table, local_table_for_iseq, scope_node);
}
}
@@ -6899,14 +6872,13 @@ pm_compile_scope_node(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_nod
for (size_t i = 0; i < scope_node->locals.size; i++) {
pm_constant_id_t constant_id = locals->ids[i];
if (constant_id) {
struct pm_local_table_insert_ctx ctx;
ctx.scope_node = scope_node;
ctx.local_table_for_iseq = local_table_for_iseq;
ctx.local_index = local_index;
st_update(index_lookup_table, (st_data_t)constant_id, pm_local_table_insert_func, (st_data_t)&ctx);
local_index = ctx.local_index;
int existing;
if (!pm_index_lookup_table_lookup(&index_lookup_table, constant_id, &existing)) {
ID local = pm_constant_id_lookup(scope_node, constant_id);
local_table_for_iseq->ids[local_index] = local;
pm_index_lookup_table_insert(&index_lookup_table, constant_id, local_index);
local_index++;
}
}
}
}
@@ -6914,10 +6886,7 @@ pm_compile_scope_node(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_nod
//********END OF STEP 4**********
// We set the index_lookup_table on the scope node so we can
// refer to the parameters correctly
if (scope_node->index_lookup_table) {
st_free_table(scope_node->index_lookup_table);
}
// refer to the parameters correctly.
scope_node->index_lookup_table = index_lookup_table;
iseq_calc_param_size(iseq);
@@ -11389,15 +11358,15 @@ pm_parse_process(pm_parse_result_t *result, pm_node_t *node, VALUE *script_lines
scope_node->line_offsets = pm_parser_line_offsets(parser);
scope_node->start_line = pm_parser_start_line(parser);
size_t constants_size = pm_parser_constants_size(parser);
scope_node->constants = constants_size ? xcalloc(constants_size, sizeof(ID)) : NULL;
scope_node->constants = constants_size ? xmalloc(constants_size * sizeof(ID)) : NULL;
pm_intern_constants_ctx_t intern_ctx = { .constants = scope_node->constants, .encoding = scope_node->encoding, .index = 0 };
pm_parser_constants_each(parser, pm_intern_constants_callback, &intern_ctx);
scope_node->index_lookup_table = st_init_numtable();
pm_constant_id_list_t *locals = &scope_node->locals;
pm_index_lookup_table_init_heap(&scope_node->index_lookup_table, (int) constants_size);
for (size_t index = 0; index < locals->size; index++) {
st_insert(scope_node->index_lookup_table, locals->ids[index], index);
pm_index_lookup_table_insert(&scope_node->index_lookup_table, locals->ids[index], (int) index);
}
// If we got here, this is a success and we can return Qnil to indicate that
+85 -7
View File
@@ -16,6 +16,82 @@ typedef struct pm_local_index_struct {
// A declaration for the struct that lives in compile.c.
struct iseq_link_anchor;
/**
* A direct-indexed lookup table mapping constant IDs to local variable indices.
* Regular constant IDs (1..constants_size) index directly. Special forwarding
* parameter IDs (idMULT|FLAG, etc.) are mapped to 4 extra slots at the end.
*
* All lookups are O(1) — a single array dereference.
* The table is arena-allocated for child scopes (no explicit free needed).
*/
typedef struct {
/** Array of local indices, indexed by constant_id. -1 means not present. */
int *values;
/** Total number of slots (constants_size + PM_INDEX_LOOKUP_SPECIALS). */
int capacity;
/** Whether the values array is heap-allocated and needs explicit free. */
bool owned;
} pm_index_lookup_table_t;
/** Number of extra slots for special forwarding parameter IDs. */
#define PM_INDEX_LOOKUP_SPECIALS 4
/** Slot offsets for special forwarding parameters (relative to constants_size). */
#define PM_SPECIAL_CONSTANT_FLAG ((pm_constant_id_t) (1 << 31))
#define PM_INDEX_LOOKUP_SPECIAL_MULT 0
#define PM_INDEX_LOOKUP_SPECIAL_POW 1
#define PM_INDEX_LOOKUP_SPECIAL_AND 2
#define PM_INDEX_LOOKUP_SPECIAL_DOT3 3
/**
* Special constant IDs for forwarding parameters. These use bit 31 to
* distinguish them from regular prism constant pool IDs. The lower bits
* encode which special slot (0-3) they map to in the lookup table.
*/
#define PM_CONSTANT_MULT ((pm_constant_id_t) (PM_SPECIAL_CONSTANT_FLAG | PM_INDEX_LOOKUP_SPECIAL_MULT))
#define PM_CONSTANT_POW ((pm_constant_id_t) (PM_SPECIAL_CONSTANT_FLAG | PM_INDEX_LOOKUP_SPECIAL_POW))
#define PM_CONSTANT_AND ((pm_constant_id_t) (PM_SPECIAL_CONSTANT_FLAG | PM_INDEX_LOOKUP_SPECIAL_AND))
#define PM_CONSTANT_DOT3 ((pm_constant_id_t) (PM_SPECIAL_CONSTANT_FLAG | PM_INDEX_LOOKUP_SPECIAL_DOT3))
static inline int
pm_index_lookup_table_index(const pm_index_lookup_table_t *table, pm_constant_id_t key)
{
if (LIKELY(!(key & PM_SPECIAL_CONSTANT_FLAG))) {
return (int) key - 1;
}
return table->capacity - PM_INDEX_LOOKUP_SPECIALS + (int)(key & ~PM_SPECIAL_CONSTANT_FLAG);
}
static inline void
pm_index_lookup_table_insert(pm_index_lookup_table_t *table, pm_constant_id_t key, int value)
{
int idx = pm_index_lookup_table_index(table, key);
RUBY_ASSERT(idx >= 0 && idx < table->capacity);
table->values[idx] = value;
}
static inline int
pm_index_lookup_table_lookup(const pm_index_lookup_table_t *table, pm_constant_id_t key, int *value)
{
int idx = pm_index_lookup_table_index(table, key);
RUBY_ASSERT(idx >= 0 && idx < table->capacity);
if (table->values[idx] == -1) return 0;
*value = table->values[idx];
return 1;
}
static inline void
pm_index_lookup_table_init_heap(pm_index_lookup_table_t *table, int constants_size)
{
int cap = constants_size + PM_INDEX_LOOKUP_SPECIALS;
table->values = (int *) ruby_xmalloc(cap * sizeof(int));
memset(table->values, -1, cap * sizeof(int));
table->capacity = cap;
table->owned = true;
}
// ScopeNodes are helper nodes, and will never be part of the AST. We manually
// declare them here to avoid generating them.
typedef struct pm_scope_node {
@@ -54,7 +130,14 @@ typedef struct pm_scope_node {
int local_table_for_iseq_size;
ID *constants;
st_table *index_lookup_table;
/**
* A flat lookup table mapping constant IDs (or special IDs) to local
* variable indices. When allocated from the compile data arena (child
* scopes), no explicit free is needed. When heap-allocated (top-level
* scope in pm_parse_process), owned is set to true so destroy can free it.
*/
pm_index_lookup_table_t index_lookup_table;
// The current coverage setting, passed down through the various scopes.
int coverage_enabled;
@@ -96,12 +179,6 @@ typedef struct {
bool parsed;
} pm_parse_result_t;
#define PM_SPECIAL_CONSTANT_FLAG ((pm_constant_id_t)(1 << 31))
#define PM_CONSTANT_AND ((pm_constant_id_t)(idAnd | PM_SPECIAL_CONSTANT_FLAG))
#define PM_CONSTANT_DOT3 ((pm_constant_id_t)(idDot3 | PM_SPECIAL_CONSTANT_FLAG))
#define PM_CONSTANT_MULT ((pm_constant_id_t)(idMULT | PM_SPECIAL_CONSTANT_FLAG))
#define PM_CONSTANT_POW ((pm_constant_id_t)(idPow | PM_SPECIAL_CONSTANT_FLAG))
void pm_parse_result_init(pm_parse_result_t *result);
VALUE pm_load_file(pm_parse_result_t *result, VALUE filepath, bool load_error);
VALUE pm_parse_file(pm_parse_result_t *result, VALUE filepath, VALUE *script_lines);
@@ -116,5 +193,6 @@ rb_iseq_t *pm_iseq_new_top(pm_scope_node_t *node, VALUE name, VALUE path, VALUE
rb_iseq_t *pm_iseq_new_main(pm_scope_node_t *node, VALUE path, VALUE realpath, const rb_iseq_t *parent, int opt, int *error_state);
rb_iseq_t *pm_iseq_new_eval(pm_scope_node_t *node, VALUE name, VALUE path, VALUE realpath, int first_lineno, const rb_iseq_t *parent, int isolated_depth, int *error_state);
rb_iseq_t *pm_iseq_new_with_opt(pm_scope_node_t *node, VALUE name, VALUE path, VALUE realpath, int first_lineno, const rb_iseq_t *parent, int isolated_depth, enum rb_iseq_type, const rb_compile_option_t *option, int *error_state);
rb_iseq_t *pm_iseq_build(pm_scope_node_t *node, VALUE name, VALUE path, VALUE realpath, int first_lineno, const rb_iseq_t *parent, int isolated_depth, enum rb_iseq_type, const rb_compile_option_t *option);
VALUE pm_iseq_compile_node(rb_iseq_t *iseq, pm_scope_node_t *node);
+2 -3
View File
@@ -1840,9 +1840,8 @@ pm_eval_make_iseq(VALUE src, VALUE fname, int line,
const pm_options_scope_t *options_scope = pm_options_scope(result.options, scopes_count - scopes_index - 1);
parent_scope->coverage_enabled = coverage_enabled;
parent_scope->parser = result.parser;
parent_scope->index_lookup_table = st_init_numtable();
int locals_count = ISEQ_BODY(iseq)->local_table_size;
pm_index_lookup_table_init_heap(&parent_scope->index_lookup_table, (int) pm_parser_constants_size(result.parser));
parent_scope->local_table_for_iseq_size = locals_count;
pm_constant_id_list_init(&parent_scope->locals);
@@ -1877,7 +1876,7 @@ pm_eval_make_iseq(VALUE src, VALUE fname, int line,
constant_id = pm_parser_constant_find(result.parser, source, length);
}
st_insert(parent_scope->index_lookup_table, (st_data_t) constant_id, (st_data_t) local_index);
pm_index_lookup_table_insert(&parent_scope->index_lookup_table, constant_id, local_index);
}
pm_constant_id_list_append(result.arena, &parent_scope->locals, constant_id);