mirror of
https://github.com/mruby/mruby
synced 2026-06-08 16:11:16 +00:00
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:
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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*
|
||||
|
||||
+1106
-1136
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user