mruby-compiler: migrate NODE_FOR to variable-sized nodes exclusively

Remove conditional logic and consolidate NODE_FOR implementation to use
variable-sized nodes exclusively. This eliminates dual code paths and
completes the NODE_FOR migration.

Changes:
- inline new_for_var into new_for, remove p->var_nodes_enabled condition
- remove new_for_var function and forward declaration
- enhance gen_for_var with complete for-loop implementation from for_body
- remove codegen_for and for_body functions
- remove NODE_FOR case from main codegen switch (traditional cons-list path)

The for-loop implementation preserves Ruby's each-based semantics with
proper block scoping, argument handling, and loop control (break/next/redo)
while providing better memory efficiency through variable-sized nodes.

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Yukihiro "Matz" Matsumoto
2025-09-05 10:46:15 +09:00
parent 272c325881
commit d45ec366c5
3 changed files with 1165 additions and 1239 deletions
+36 -90
View File
@@ -2290,79 +2290,6 @@ search_upvar(codegen_scope *s, mrb_sym id, int *idx)
return -1; /* not reached */
}
/*
* Generates the bytecode for a `for` loop.
*
* A `for` loop in mruby, like `for x in collection`, is typically syntactic sugar for
* `collection.each { |x| ... }`. This function implements that transformation.
*
* The process involves:
* 1. Generating code for the `collection` (the receiver of the `each` call).
* 2. Creating a new scope for the block that will be passed to `each`.
* 3. Inside this new block scope:
* a. Emitting `OP_ENTER` to set up the block's argument handling.
* The argument specification `0x40000` likely indicates a block that
* takes one mandatory argument.
* b. Generating code to assign the iterated item (passed as a block argument)
* to the loop variable(s) specified in `tree->car`. This can be a simple
* assignment or a multiple assignment (destructuring).
* c. Setting up a `LOOP_FOR` context for handling `break`/`next`/`redo` within the loop.
* d. Generating code for the actual body of the `for` loop (`tree->cdr->cdr->car`).
* e. Emitting `OP_RETURN` for the block's implicit return.
* 4. Finalizing the block scope and obtaining its `mrb_irep`.
* 5. Back in the original scope, generating `OP_BLOCK` to create a closure from the
* block's `mrb_irep`.
* 6. Generating `OP_SENDB` to call the `each` method (by symbol) on the collection,
* passing the newly created block.
*
* @param s The current code generation scope.
* @param tree The AST node representing the `for` loop.
* `tree->car` contains the loop variable(s).
* `tree->cdr->car` is the collection being iterated over.
* `tree->cdr->cdr->car` is the body of the loop.
*/
static void
for_body(codegen_scope *s, node *tree)
{
codegen_scope *prev = s;
int idx;
struct loopinfo *lp;
node *n2;
/* generate receiver */
codegen(s, tree->cdr->car, VAL);
/* generate loop-block */
s = scope_new(s->mrb, s, NULL);
push(); /* push for a block parameter */
/* generate loop variable */
n2 = tree->car;
genop_W(s, OP_ENTER, 0x40000);
if (n2->car && !n2->car->cdr && !n2->cdr) {
gen_assignment(s, n2->car->car, NULL, 1, NOVAL);
}
else {
gen_massignment(s, n2, 1, VAL);
}
/* construct loop */
lp = loop_push(s, LOOP_FOR);
lp->pc1 = new_label(s);
genop_0(s, OP_NOP); /* for redo */
/* loop body */
codegen(s, tree->cdr->cdr->car, VAL);
pop();
gen_return(s, OP_RETURN, cursp());
loop_pop(s, NOVAL);
scope_finish(s);
s = prev;
genop_2(s, OP_BLOCK, cursp(), s->irep->rlen-1);
push();pop(); /* space for a block */
pop();
idx = new_sym(s, MRB_SYM_2(s->mrb, each));
genop_3(s, OP_SENDB, cursp(), idx, 0);
}
/*
* Generates the bytecode for the body of a lambda or a block.
@@ -3980,14 +3907,6 @@ codegen_case(codegen_scope *s, node *tree, int val)
}
}
static void
codegen_for(codegen_scope *s, node *tree, int val)
{
for_body(s, tree);
if (val) push();
}
static void
codegen_negate(codegen_scope *s, node *tree, int val)
{
@@ -5041,17 +4960,47 @@ static void
gen_for_var(codegen_scope *s, node *varnode, int val)
{
struct mrb_ast_for_node *for_n = for_node(varnode);
node *var = FOR_NODE_VAR(for_n);
node *iterable = FOR_NODE_ITERABLE(for_n);
node *body = FOR_NODE_BODY(for_n);
/* Generate iterable */
codegen_scope *prev = s;
int idx;
struct loopinfo *lp;
/* generate receiver */
codegen(s, iterable, VAL);
pop(); /* Remove iterable value */
/* generate loop-block */
s = scope_new(s->mrb, s, NULL);
/* For now, use a simple iteration approach - this can be optimized later */
if (val) {
genop_1(s, OP_LOADNIL, cursp());
push();
push(); /* push for a block parameter */
/* generate loop variable */
genop_W(s, OP_ENTER, 0x40000);
if (var->car && !var->car->cdr && !var->cdr) {
gen_assignment(s, var->car->car, NULL, 1, NOVAL);
}
else {
gen_massignment(s, var, 1, VAL);
}
/* construct loop */
lp = loop_push(s, LOOP_FOR);
lp->pc1 = new_label(s);
genop_0(s, OP_NOP); /* for redo */
/* loop body */
codegen(s, body, VAL);
pop();
gen_return(s, OP_RETURN, cursp());
loop_pop(s, NOVAL);
scope_finish(s);
s = prev;
genop_2(s, OP_BLOCK, cursp(), s->irep->rlen-1);
push();pop(); /* space for a block */
pop();
idx = new_sym(s, MRB_SYM_2(s->mrb, each));
genop_3(s, OP_SENDB, cursp(), idx, 0);
if (val) push();
}
static void
@@ -6375,9 +6324,6 @@ codegen(codegen_scope *s, node *tree, int val)
codegen_block(s, tree, val);
break;
case NODE_FOR:
codegen_for(s, tree, val);
break;
case NODE_CASE:
codegen_case(s, tree, val);
+12 -22
View File
@@ -599,7 +599,6 @@ new_alias(parser_state *p, mrb_sym a, mrb_sym b)
static node* new_array_var(parser_state *p, node *a);
static node* new_hash_var(parser_state *p, node *a);
static node* new_case_var(parser_state *p, node *value, node *when_list);
static node* new_for_var(parser_state *p, node *var, node *iterable, node *body);
static node* new_def_var(parser_state *p, mrb_sym name, node *args, node *body);
static node* new_class_var(parser_state *p, node *name, node *superclass, node *body);
static node* new_module_var(parser_state *p, node *name, node *body);
@@ -688,11 +687,18 @@ static node*
new_for(parser_state *p, node *v, node *o, node *b)
{
void_expr_error(p, o);
// If variable-sized nodes are enabled, use the specialized creation function
if (p->var_nodes_enabled) {
return new_for_var(p, v, o, b);
}
return list4((node*)NODE_FOR, v, o, b);
size_t total_size = sizeof(struct mrb_ast_for_node);
enum mrb_ast_size_class class = size_to_class(total_size);
struct mrb_ast_for_node *n = (struct mrb_ast_for_node*)parser_alloc_var(p, total_size, class);
init_var_header(&n->header, p, NODE_FOR, class);
n->var = v;
n->iterable = o;
n->body = b;
return cons_head((node*)NODE_VARIABLE, (node*)n);
}
/* (:case a ((when ...) body) ((when...) body)) */
@@ -896,22 +902,6 @@ new_hash_var(parser_state *p, node *a)
}
/* Variable-sized for node creation */
static node*
new_for_var(parser_state *p, node *var, node *iterable, node *body)
{
size_t total_size = sizeof(struct mrb_ast_for_node);
enum mrb_ast_size_class class = size_to_class(total_size);
struct mrb_ast_for_node *n = (struct mrb_ast_for_node*)parser_alloc_var(p, total_size, class);
init_var_header(&n->header, p, NODE_FOR, class);
n->var = var;
n->iterable = iterable;
n->body = body;
return cons_head((node*)NODE_VARIABLE, (node*)n);
}
/* Variable-sized method definition node creation */
static node*
File diff suppressed because it is too large Load Diff