mruby-compiler: complete NODE_IF migration to variable-sized nodes

Remove conditional logic and consolidate NODE_IF implementation to use
variable-sized nodes exclusively. This eliminates dual code paths and
completes the NODE_IF migration started in previous commits.

Changes:
- inline new_if_var into new_if, remove p->var_nodes_enabled condition
- remove new_unless function, replace calls with new_if (swap then/else)
- remove codegen_if function, merge nil? optimization into gen_if_var
- remove NODE_IF case from main codegen switch (always wrapped in NODE_VARIABLE)
- fix nil? optimization to handle both traditional and variable-sized nodes
- update gen_if_var to use direct struct field access instead of macros

The nil? optimization now works with both node representations:
- Traditional: NODE_TYPE(condition) == NODE_CALL (preserved)
- Variable-sized: NODE_VARIABLE wrapper containing NODE_CALL struct

This ensures obj.nil? patterns generate optimized OP_JMPNIL bytecode
regardless of AST node representation.

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Yukihiro "Matz" Matsumoto
2025-09-05 07:49:04 +09:00
parent a11a3f2fe6
commit 01b2a7bd22
3 changed files with 1142 additions and 1219 deletions
+45 -84
View File
@@ -3787,75 +3787,6 @@ codegen_block(codegen_scope *s, node *tree, int val)
push();
}
static void
codegen_if(codegen_scope *s, node *tree, int val)
{
uint32_t pos1, pos2;
mrb_bool nil_p = FALSE;
node *elsepart = tree->cdr->cdr->car;
if (!tree->car) {
codegen(s, elsepart, val);
return;
}
if (true_always(tree->car)) {
codegen(s, tree->cdr->car, val);
return;
}
if (false_always(tree->car)) {
codegen(s, elsepart, val);
return;
}
if (node_to_int(tree->car->car) == NODE_CALL) {
node *n = tree->car->cdr;
mrb_sym mid = node_to_sym(n->cdr->car);
mrb_sym sym_nil_p = MRB_SYM_Q_2(s->mrb, nil);
if (mid == sym_nil_p && n->cdr->cdr->car == NULL) {
nil_p = TRUE;
codegen(s, n->car, VAL);
}
}
if (!nil_p) {
codegen(s, tree->car, VAL);
}
pop();
if (val || tree->cdr->car) {
if (nil_p) {
pos2 = genjmp2_0(s, OP_JMPNIL, cursp(), val);
pos1 = genjmp_0(s, OP_JMP);
dispatch(s, pos2);
}
else {
pos1 = genjmp2_0(s, OP_JMPNOT, cursp(), val);
}
codegen(s, tree->cdr->car, val);
if (val) pop();
if (elsepart || val) {
pos2 = genjmp_0(s, OP_JMP);
dispatch(s, pos1);
codegen(s, elsepart, val);
dispatch(s, pos2);
}
else {
dispatch(s, pos1);
}
}
else { /* empty then-part */
if (elsepart) {
if (nil_p) {
pos1 = genjmp2_0(s, OP_JMPNIL, cursp(), val);
}
else {
pos1 = genjmp2_0(s, OP_JMPIF, cursp(), val);
}
codegen(s, elsepart, val);
dispatch(s, pos1);
}
else if (val && !nil_p) {
gen_load_nil(s, 1);
}
}
}
static void
@@ -4903,10 +4834,11 @@ static void
gen_if_var(codegen_scope *s, node *varnode, int val)
{
struct mrb_ast_if_node *if_n = if_node(varnode);
node *condition = IF_NODE_CONDITION(if_n);
node *then_body = IF_NODE_THEN(if_n);
node *else_body = IF_NODE_ELSE(if_n);
node *condition = if_n->condition;
node *then_body = if_n->then_body;
node *else_body = if_n->else_body;
uint32_t pos1, pos2;
mrb_bool nil_p = FALSE;
if (!condition) {
codegen(s, else_body, val);
@@ -4921,12 +4853,42 @@ gen_if_var(codegen_scope *s, node *varnode, int val)
return;
}
/* Generate condition code */
codegen(s, condition, VAL);
/* Check for nil? optimization - handle both traditional and variable-sized nodes */
if (NODE_TYPE(condition) == NODE_CALL) {
/* Traditional cons-list NODE_CALL */
node *n = condition->cdr;
mrb_sym mid = node_to_sym(n->cdr->car);
mrb_sym sym_nil_p = MRB_SYM_Q_2(s->mrb, nil);
if (mid == sym_nil_p && n->cdr->cdr->car == NULL) {
nil_p = TRUE;
codegen(s, n->car, VAL);
}
}
else if (NODE_TYPE(condition) == NODE_VARIABLE && VAR_NODE_TYPE(condition->cdr) == NODE_CALL) {
/* Variable-sized NODE_CALL wrapped in NODE_VARIABLE */
struct mrb_ast_call_node *call_n = (struct mrb_ast_call_node*)condition->cdr;
mrb_sym sym_nil_p = MRB_SYM_Q_2(s->mrb, nil);
if (call_n->method_name == sym_nil_p && call_n->argc == 0) {
nil_p = TRUE;
codegen(s, call_n->receiver, VAL);
}
}
if (!nil_p) {
/* Generate condition code */
codegen(s, condition, VAL);
}
pop();
if (val || then_body) {
pos1 = genjmp2_0(s, OP_JMPNOT, cursp(), val);
if (nil_p) {
pos2 = genjmp2_0(s, OP_JMPNIL, cursp(), val);
pos1 = genjmp_0(s, OP_JMP);
dispatch(s, pos2);
}
else {
pos1 = genjmp2_0(s, OP_JMPNOT, cursp(), val);
}
codegen(s, then_body, val);
if (val) pop();
if (else_body || val) {
@@ -4941,13 +4903,17 @@ gen_if_var(codegen_scope *s, node *varnode, int val)
}
else { /* empty then-part */
if (else_body) {
pos1 = genjmp2_0(s, OP_JMPIF, cursp(), val);
if (nil_p) {
pos1 = genjmp2_0(s, OP_JMPNIL, cursp(), val);
}
else {
pos1 = genjmp2_0(s, OP_JMPIF, cursp(), val);
}
codegen(s, else_body, val);
dispatch(s, pos1);
}
else if (val) {
genop_1(s, OP_LOADNIL, cursp());
push();
else if (val && !nil_p) {
gen_load_nil(s, 1);
}
}
}
@@ -6351,11 +6317,6 @@ codegen(codegen_scope *s, node *tree, int val)
codegen_block(s, tree, val);
break;
case NODE_IF:
codegen_if(s, tree, val);
break;
case NODE_WHILE_MOD:
case NODE_UNTIL_MOD:
case NODE_WHILE:
+15 -34
View File
@@ -598,7 +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_if_var(parser_state *p, node *condition, node *then_body, node *else_body);
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);
@@ -613,22 +612,21 @@ static node* new_op_asgn_var(parser_state *p, node *lhs, mrb_sym op, node *rhs);
/* (:if cond then else) */
static node*
new_if(parser_state *p, node *a, node *b, node *c)
new_if(parser_state *p, node *condition, node *then_body, node *else_body)
{
void_expr_error(p, a);
// If variable-sized nodes are enabled, use the specialized creation function
if (p->var_nodes_enabled) {
return new_if_var(p, a, b, c);
}
return list4((node*)NODE_IF, a, b, c);
}
void_expr_error(p, condition);
/* (:unless cond then else) */
static node*
new_unless(parser_state *p, node *a, node *b, node *c)
{
void_expr_error(p, a);
return list4((node*)NODE_IF, a, c, b);
size_t total_size = sizeof(struct mrb_ast_if_node);
enum mrb_ast_size_class class = size_to_class(total_size);
struct mrb_ast_if_node *n;
n = (struct mrb_ast_if_node*)parser_alloc_var(p, total_size, class);
init_var_header(&n->header, p, NODE_IF, class);
n->condition = condition;
n->then_body = then_body;
n->else_body = else_body;
return cons_head((node*)NODE_VARIABLE, (node*)n);
}
/* (:while cond body) */
@@ -896,23 +894,6 @@ new_hash_var(parser_state *p, node *a)
return cons_head((node*)NODE_VARIABLE, (node*)n);
}
/* Variable-sized if node creation */
static node*
new_if_var(parser_state *p, node *condition, node *then_body, node *else_body)
{
size_t total_size = sizeof(struct mrb_ast_if_node);
enum mrb_ast_size_class class = size_to_class(total_size);
struct mrb_ast_if_node *n = (struct mrb_ast_if_node*)parser_alloc_var(p, total_size, class);
init_var_header(&n->header, p, NODE_IF, class);
n->condition = condition;
n->then_body = then_body;
n->else_body = else_body;
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)
@@ -2823,7 +2804,7 @@ stmt : keyword_alias fsym {p->lstate = EXPR_FNAME;} fsym
}
| stmt modifier_unless expr_value
{
$$ = new_unless(p, cond($3), $1, 0);
$$ = new_if(p, cond($3), 0, $1);
}
| stmt modifier_while expr_value
{
@@ -3880,7 +3861,7 @@ primary : literal
opt_else
keyword_end
{
$$ = new_unless(p, cond($2), $4, $5);
$$ = new_if(p, cond($2), $5, $4);
SET_LINENO($$, $1);
}
| keyword_while {COND_PUSH(1);} expr_value do {COND_POP();}
File diff suppressed because it is too large Load Diff