mruby-compiler: optimize while/until node structures and implementations

Consolidate NODE_WHILE/NODE_UNTIL with MOD variants by sharing structures
and implementations, eliminating redundant code and improving maintainability.

Changes:
- remove separate mrb_ast_while_mod_node and mrb_ast_until_mod_node structures
- share mrb_ast_while_node between NODE_WHILE and NODE_WHILE_MOD variants
- share mrb_ast_until_node between NODE_UNTIL and NODE_UNTIL_MOD variants
- simplify new_while_mod to call new_while and update node_type
- simplify new_until_mod to call new_until and update node_type
- update gen_while_mod_var and gen_until_mod_var to use shared structures

The MOD variants now reuse core allocation logic from regular variants,
differing only in node_type. This eliminates code duplication while
preserving identical functionality for both pre-tested and post-tested loops.

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Yukihiro "Matz" Matsumoto
2025-09-05 07:56:04 +09:00
parent 01b2a7bd22
commit 272c325881
4 changed files with 1285 additions and 1312 deletions
+144 -93
View File
@@ -3987,71 +3987,6 @@ codegen_for(codegen_scope *s, node *tree, int val)
if (val) push();
}
static void
codegen_while_until(codegen_scope *s, node *tree, int val, int nt)
{
/* Handle modifier-specific optimizations first */
if (nt == NODE_WHILE_MOD || nt == NODE_UNTIL_MOD) {
/* Post-tested loops: execute body first, then check condition */
if (false_always(tree->car)) {
if (nt == NODE_WHILE_MOD) {
/* begin...end while false - execute once then exit */
codegen(s, tree->cdr, val);
if (val) push();
return;
}
}
else if (true_always(tree->car)) {
if (nt == NODE_UNTIL_MOD) {
/* begin...end until true - execute once then exit */
codegen(s, tree->cdr, val);
if (val) push();
return;
}
}
}
uint32_t pos0 = JMPLINK_START;
if (nt == NODE_WHILE_MOD || nt == NODE_UNTIL_MOD) {
genjmp_0(s, OP_JMP);
pos0 = s->pc - mrb_insn_size[OP_JMP] + 1;
}
if (true_always(tree->car)) {
if (nt == NODE_UNTIL || nt == NODE_UNTIL_MOD) {
gen_load_nil(s, val);
return;
}
}
else if (false_always(tree->car)) {
if (nt == NODE_WHILE || nt == NODE_WHILE_MOD) {
gen_load_nil(s, val);
return;
}
}
struct loopinfo *lp = loop_push(s, LOOP_NORMAL);
if (!val) lp->reg = -1;
lp->pc0 = new_label(s);
codegen(s, tree->car, VAL);
pop();
uint32_t pos;
if (nt == NODE_WHILE || nt == NODE_WHILE_MOD) {
pos = genjmp2_0(s, OP_JMPNOT, cursp(), NOVAL);
}
else { /* UNTIL */
pos = genjmp2_0(s, OP_JMPIF, cursp(), NOVAL);
}
lp->pc1 = new_label(s);
genop_0(s, OP_NOP); /* for redo */
dispatch(s, pos0);
codegen(s, tree->cdr, NOVAL);
genjmp(s, OP_JMP, lp->pc0);
dispatch(s, pos);
loop_pop(s, val);
}
static void
codegen_negate(codegen_scope *s, node *tree, int val)
@@ -4922,8 +4857,30 @@ static void
gen_while_var(codegen_scope *s, node *varnode, int val)
{
struct mrb_ast_while_node *while_n = while_node(varnode);
node *condition = WHILE_NODE_CONDITION(while_n);
node *body = WHILE_NODE_BODY(while_n);
node *condition = while_n->condition;
node *body = while_n->body;
/* Check for constant conditions first */
if (true_always(condition)) {
/* while true - infinite loop, don't generate condition check */
struct loopinfo *lp = loop_push(s, LOOP_NORMAL);
if (!val) lp->reg = -1;
lp->pc0 = new_label(s);
lp->pc1 = new_label(s);
genop_0(s, OP_NOP); /* for redo */
codegen(s, body, NOVAL);
genjmp(s, OP_JMP, lp->pc0);
loop_pop(s, val);
return;
}
if (false_always(condition)) {
/* while false - never execute, just return nil */
if (val) {
gen_load_nil(s, 1);
}
return;
}
struct loopinfo *lp = loop_push(s, LOOP_NORMAL);
uint32_t pos;
@@ -4944,8 +4901,30 @@ static void
gen_until_var(codegen_scope *s, node *varnode, int val)
{
struct mrb_ast_until_node *until_n = until_node(varnode);
node *condition = UNTIL_NODE_CONDITION(until_n);
node *body = UNTIL_NODE_BODY(until_n);
node *condition = until_n->condition;
node *body = until_n->body;
/* Check for constant conditions first */
if (true_always(condition)) {
/* until true - never execute, just return nil */
if (val) {
gen_load_nil(s, 1);
}
return;
}
if (false_always(condition)) {
/* until false - infinite loop, don't generate condition check */
struct loopinfo *lp = loop_push(s, LOOP_NORMAL);
if (!val) lp->reg = -1;
lp->pc0 = new_label(s);
lp->pc1 = new_label(s);
genop_0(s, OP_NOP); /* for redo */
codegen(s, body, NOVAL);
genjmp(s, OP_JMP, lp->pc0);
loop_pop(s, val);
return;
}
struct loopinfo *lp = loop_push(s, LOOP_NORMAL);
uint32_t pos;
@@ -4962,6 +4941,102 @@ gen_until_var(codegen_scope *s, node *varnode, int val)
loop_pop(s, val);
}
static void
gen_while_mod_var(codegen_scope *s, node *varnode, int val)
{
struct mrb_ast_while_node *while_n = while_node(varnode);
node *condition = while_n->condition;
node *body = while_n->body;
/* Handle special constant cases for post-tested loops */
if (false_always(condition)) {
/* begin...end while false - execute once then exit */
codegen(s, body, val);
if (val) push();
return;
}
if (true_always(condition)) {
/* begin...end while true - infinite loop after first execution */
struct loopinfo *lp = loop_push(s, LOOP_NORMAL);
if (!val) lp->reg = -1;
uint32_t pos0 = genjmp_0(s, OP_JMP);
lp->pc0 = new_label(s);
lp->pc1 = new_label(s);
genop_0(s, OP_NOP); /* for redo */
dispatch(s, pos0);
codegen(s, body, NOVAL);
genjmp(s, OP_JMP, lp->pc0);
loop_pop(s, val);
return;
}
/* Normal post-tested while loop */
struct loopinfo *lp = loop_push(s, LOOP_NORMAL);
if (!val) lp->reg = -1;
uint32_t pos0 = genjmp_0(s, OP_JMP);
lp->pc0 = new_label(s);
codegen(s, condition, VAL);
pop();
uint32_t pos = genjmp2_0(s, OP_JMPNOT, cursp(), NOVAL);
lp->pc1 = new_label(s);
genop_0(s, OP_NOP); /* for redo */
dispatch(s, pos0);
codegen(s, body, NOVAL);
genjmp(s, OP_JMP, lp->pc0);
dispatch(s, pos);
loop_pop(s, val);
}
static void
gen_until_mod_var(codegen_scope *s, node *varnode, int val)
{
struct mrb_ast_until_node *until_n = until_node(varnode);
node *condition = until_n->condition;
node *body = until_n->body;
/* Handle special constant cases for post-tested loops */
if (true_always(condition)) {
/* begin...end until true - execute once then exit */
codegen(s, body, val);
if (val) push();
return;
}
if (false_always(condition)) {
/* begin...end until false - infinite loop after first execution */
struct loopinfo *lp = loop_push(s, LOOP_NORMAL);
if (!val) lp->reg = -1;
uint32_t pos0 = genjmp_0(s, OP_JMP);
lp->pc0 = new_label(s);
lp->pc1 = new_label(s);
genop_0(s, OP_NOP); /* for redo */
dispatch(s, pos0);
codegen(s, body, NOVAL);
genjmp(s, OP_JMP, lp->pc0);
loop_pop(s, val);
return;
}
/* Normal post-tested until loop */
struct loopinfo *lp = loop_push(s, LOOP_NORMAL);
if (!val) lp->reg = -1;
uint32_t pos0 = genjmp_0(s, OP_JMP);
lp->pc0 = new_label(s);
codegen(s, condition, VAL);
pop();
uint32_t pos = genjmp2_0(s, OP_JMPIF, cursp(), NOVAL);
lp->pc1 = new_label(s);
genop_0(s, OP_NOP); /* for redo */
dispatch(s, pos0);
codegen(s, body, NOVAL);
genjmp(s, OP_JMP, lp->pc0);
dispatch(s, pos);
loop_pop(s, val);
}
static void
gen_for_var(codegen_scope *s, node *varnode, int val)
{
@@ -5475,23 +5550,6 @@ gen_retry_var(codegen_scope *s, node *varnode, int val)
codegen_retry(s, NULL, val);
}
static void
gen_while_mod_var(codegen_scope *s, node *varnode, int val)
{
struct mrb_ast_while_mod_node *n = (struct mrb_ast_while_mod_node*)varnode;
// Stack allocation for compatibility with existing codegen
struct mrb_ast_node temp_tree = { .car = n->condition, .cdr = n->body };
codegen_while_until(s, (node*)&temp_tree, val, NODE_WHILE_MOD);
}
static void
gen_until_mod_var(codegen_scope *s, node *varnode, int val)
{
struct mrb_ast_until_mod_node *n = (struct mrb_ast_until_mod_node*)varnode;
// Stack allocation for compatibility with existing codegen
struct mrb_ast_node temp_tree = { .car = n->condition, .cdr = n->body };
codegen_while_until(s, (node*)&temp_tree, val, NODE_UNTIL_MOD);
}
static void
gen_xstr_var(codegen_scope *s, node *varnode, int val)
@@ -6317,13 +6375,6 @@ codegen(codegen_scope *s, node *tree, int val)
codegen_block(s, tree, val);
break;
case NODE_WHILE_MOD:
case NODE_UNTIL_MOD:
case NODE_WHILE:
case NODE_UNTIL:
codegen_while_until(s, tree, val, nt);
break;
case NODE_FOR:
codegen_for(s, tree, val);
break;
-18
View File
@@ -669,31 +669,13 @@ struct mrb_ast_retry_node {
struct mrb_ast_var_header hdr;
};
struct mrb_ast_while_mod_node {
struct mrb_ast_var_header hdr;
struct mrb_ast_node *condition;
struct mrb_ast_node *body;
};
struct mrb_ast_until_mod_node {
struct mrb_ast_var_header hdr;
struct mrb_ast_node *condition;
struct mrb_ast_node *body;
};
#define break_node(n) ((struct mrb_ast_break_node*)(n))
#define next_node(n) ((struct mrb_ast_next_node*)(n))
#define redo_node(n) ((struct mrb_ast_redo_node*)(n))
#define retry_node(n) ((struct mrb_ast_retry_node*)(n))
#define while_mod_node(n) ((struct mrb_ast_while_mod_node*)(n))
#define until_mod_node(n) ((struct mrb_ast_until_mod_node*)(n))
#define BREAK_NODE_VALUE(n) (break_node(n)->value)
#define NEXT_NODE_VALUE(n) (next_node(n)->value)
#define WHILE_MOD_NODE_CONDITION(n) (while_mod_node(n)->condition)
#define WHILE_MOD_NODE_BODY(n) (while_mod_node(n)->body)
#define UNTIL_MOD_NODE_CONDITION(n) (until_mod_node(n)->condition)
#define UNTIL_MOD_NODE_BODY(n) (until_mod_node(n)->body)
// Group 9: String and Regex Variants
struct mrb_ast_xstr_node {
+35 -65
View File
@@ -598,8 +598,6 @@ new_alias(parser_state *p, mrb_sym a, mrb_sym b)
/* Forward declarations for variable-sized AST node creation functions */
static node* new_array_var(parser_state *p, node *a);
static node* new_hash_var(parser_state *p, node *a);
static node* new_while_var(parser_state *p, node *condition, node *body);
static node* new_until_var(parser_state *p, node *condition, node *body);
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);
@@ -631,55 +629,58 @@ new_if(parser_state *p, node *condition, node *then_body, node *else_body)
/* (:while cond body) */
static node*
new_while(parser_state *p, node *a, node *b)
new_while(parser_state *p, node *condition, node *body)
{
void_expr_error(p, a);
// If variable-sized nodes are enabled, use the specialized creation function
if (p->var_nodes_enabled) {
return new_while_var(p, a, b);
}
return cons_head((node*)NODE_WHILE, cons(a, b));
void_expr_error(p, condition);
size_t total_size = sizeof(struct mrb_ast_while_node);
enum mrb_ast_size_class class = size_to_class(total_size);
struct mrb_ast_while_node *n;
n = (struct mrb_ast_while_node*)parser_alloc_var(p, total_size, class);
init_var_header(&n->header, p, NODE_WHILE, class);
n->condition = condition;
n->body = body;
return cons_head((node*)NODE_VARIABLE, (node*)n);
}
/* (:until cond body) */
static node*
new_until(parser_state *p, node *a, node *b)
new_until(parser_state *p, node *condition, node *body)
{
void_expr_error(p, a);
if (p->var_nodes_enabled) {
return new_until_var(p, a, b);
}
return cons_head((node*)NODE_UNTIL, cons(a, b));
void_expr_error(p, condition);
size_t total_size = sizeof(struct mrb_ast_until_node);
enum mrb_ast_size_class class = size_to_class(total_size);
struct mrb_ast_until_node *n;
n = (struct mrb_ast_until_node*)parser_alloc_var(p, total_size, class);
init_var_header(&n->header, p, NODE_UNTIL, class);
n->condition = condition;
n->body = body;
return cons_head((node*)NODE_VARIABLE, (node*)n);
}
/* (:while_mod cond body) */
static node*
new_while_mod(parser_state *p, node *a, node *b)
new_while_mod(parser_state *p, node *condition, node *body)
{
void_expr_error(p, a);
size_t total_size = sizeof(struct mrb_ast_while_mod_node);
enum mrb_ast_size_class class = size_to_class(total_size);
struct mrb_ast_while_mod_node *n = (struct mrb_ast_while_mod_node*)parser_alloc_var(p, total_size, class);
init_var_header(&n->hdr, p, NODE_WHILE_MOD, class);
n->condition = a;
n->body = b;
return cons_head((node*)NODE_VARIABLE, (node*)n);
node *while_node = new_while(p, condition, body);
struct mrb_ast_while_node *n = (struct mrb_ast_while_node*)NODE_VAR_NODE_PTR(while_node);
n->header.node_type = NODE_WHILE_MOD;
return while_node;
}
/* (:until_mod cond body) */
static node*
new_until_mod(parser_state *p, node *a, node *b)
{
void_expr_error(p, a);
size_t total_size = sizeof(struct mrb_ast_until_mod_node);
enum mrb_ast_size_class class = size_to_class(total_size);
struct mrb_ast_until_mod_node *n = (struct mrb_ast_until_mod_node*)parser_alloc_var(p, total_size, class);
init_var_header(&n->hdr, p, NODE_UNTIL_MOD, class);
n->condition = a;
n->body = b;
return cons_head((node*)NODE_VARIABLE, (node*)n);
node *until_node = new_until(p, a, b);
struct mrb_ast_until_node *n = (struct mrb_ast_until_node*)NODE_VAR_NODE_PTR(until_node);
n->header.node_type = NODE_UNTIL_MOD;
return until_node;
}
/* (:for var obj body) */
@@ -894,37 +895,6 @@ new_hash_var(parser_state *p, node *a)
return cons_head((node*)NODE_VARIABLE, (node*)n);
}
/* Variable-sized while node creation */
static node*
new_while_var(parser_state *p, node *condition, node *body)
{
size_t total_size = sizeof(struct mrb_ast_while_node);
enum mrb_ast_size_class class = size_to_class(total_size);
struct mrb_ast_while_node *n = (struct mrb_ast_while_node*)parser_alloc_var(p, total_size, class);
init_var_header(&n->header, p, NODE_WHILE, class);
n->condition = condition;
n->body = body;
return cons_head((node*)NODE_VARIABLE, (node*)n);
}
/* Variable-sized until node creation */
static node*
new_until_var(parser_state *p, node *condition, node *body)
{
size_t total_size = sizeof(struct mrb_ast_until_node);
enum mrb_ast_size_class class = size_to_class(total_size);
struct mrb_ast_until_node *n = (struct mrb_ast_until_node*)parser_alloc_var(p, total_size, class);
init_var_header(&n->header, p, NODE_UNTIL, class);
n->condition = condition;
n->body = body;
return cons_head((node*)NODE_VARIABLE, (node*)n);
}
/* Variable-sized for node creation */
static node*
File diff suppressed because it is too large Load Diff