mirror of
https://github.com/mruby/mruby
synced 2026-06-08 16:11:16 +00:00
mruby-compiler: use variable-sized ast nodes for call, array, and hash
Introduces variable-sized AST nodes for method calls (NODE_CALL), arrays (NODE_ARRAY), and hashes (NODE_HASH). This change improves memory efficiency by storing elements directly within the AST node, avoiding an extra layer of pointer indirection for their data. This is achieved by adding new data structures and functions in both the parser and the code generator to handle these new node types. Variable-sized nodes are now enabled by default. Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -4920,6 +4920,180 @@ codegen_yield(codegen_scope *s, node *tree, int val)
|
||||
|
||||
|
||||
/* Handle variable-sized node types */
|
||||
static void
|
||||
gen_call_var(codegen_scope *s, node *varnode, int val)
|
||||
{
|
||||
struct mrb_ast_call_node *call = call_node(varnode);
|
||||
mrb_sym sym = CALL_NODE_METHOD(call);
|
||||
int skip = 0, n = 0, nk = 0, noop = no_optimize(s), noself = 0, blk = 0, sp_save = cursp();
|
||||
enum mrb_insn opt_op = OP_NOP;
|
||||
int argc = CALL_NODE_ARGC(call);
|
||||
int safe = CALL_NODE_SAFE(call);
|
||||
|
||||
if (!noop) {
|
||||
if (sym == MRB_OPSYM_2(s->mrb, add)) opt_op = OP_ADD;
|
||||
else if (sym == MRB_OPSYM_2(s->mrb, sub)) opt_op = OP_SUB;
|
||||
else if (sym == MRB_OPSYM_2(s->mrb, mul)) opt_op = OP_MUL;
|
||||
else if (sym == MRB_OPSYM_2(s->mrb, div)) opt_op = OP_DIV;
|
||||
else if (sym == MRB_OPSYM_2(s->mrb, lt)) opt_op = OP_LT;
|
||||
else if (sym == MRB_OPSYM_2(s->mrb, le)) opt_op = OP_LE;
|
||||
else if (sym == MRB_OPSYM_2(s->mrb, gt)) opt_op = OP_GT;
|
||||
else if (sym == MRB_OPSYM_2(s->mrb, ge)) opt_op = OP_GE;
|
||||
else if (sym == MRB_OPSYM_2(s->mrb, eq)) opt_op = OP_EQ;
|
||||
else if (sym == MRB_OPSYM_2(s->mrb, aref)) opt_op = OP_GETIDX;
|
||||
else if (sym == MRB_OPSYM_2(s->mrb, aset)) opt_op = OP_SETIDX;
|
||||
}
|
||||
|
||||
if (!CALL_NODE_RECEIVER(call)) {
|
||||
noself = 1;
|
||||
push();
|
||||
}
|
||||
else {
|
||||
codegen(s, CALL_NODE_RECEIVER(call), VAL); /* receiver */
|
||||
}
|
||||
|
||||
if (safe) {
|
||||
int recv = cursp()-1;
|
||||
gen_move(s, cursp(), recv, 1);
|
||||
skip = genjmp2_0(s, OP_JMPNIL, cursp(), val);
|
||||
}
|
||||
|
||||
/* Generate arguments */
|
||||
n = argc;
|
||||
if (argc > 0) {
|
||||
struct mrb_ast_node **args = CALL_NODE_ARGS(call);
|
||||
int i;
|
||||
for (i = 0; i < argc; i++) {
|
||||
codegen(s, args[i], VAL);
|
||||
}
|
||||
if (n > 14) {
|
||||
noop = 1;
|
||||
n = 15;
|
||||
push();
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle keyword arguments if present */
|
||||
if (CALL_NODE_HAS_KWARGS(call)) {
|
||||
noop = 1;
|
||||
nk = 1; /* Simplified - would need proper kwarg handling */
|
||||
}
|
||||
|
||||
/* Handle block if present */
|
||||
if (CALL_NODE_HAS_BLOCK(call)) {
|
||||
noop = 1;
|
||||
blk = 1;
|
||||
}
|
||||
|
||||
push();
|
||||
s->sp = sp_save;
|
||||
|
||||
/* Apply optimizations */
|
||||
if (opt_op == OP_ADD && n == 1) {
|
||||
gen_addsub(s, OP_ADD, cursp());
|
||||
}
|
||||
else if (opt_op == OP_SUB && n == 1) {
|
||||
gen_addsub(s, OP_SUB, cursp());
|
||||
}
|
||||
else if (opt_op == OP_MUL && n == 1) {
|
||||
gen_muldiv(s, OP_MUL, cursp());
|
||||
}
|
||||
else if (opt_op == OP_DIV && n == 1) {
|
||||
gen_muldiv(s, OP_DIV, cursp());
|
||||
}
|
||||
else if (opt_op == OP_LT && n == 1) {
|
||||
genop_1(s, OP_LT, cursp());
|
||||
}
|
||||
else if (opt_op == OP_LE && n == 1) {
|
||||
genop_1(s, OP_LE, cursp());
|
||||
}
|
||||
else if (opt_op == OP_GT && n == 1) {
|
||||
genop_1(s, OP_GT, cursp());
|
||||
}
|
||||
else if (opt_op == OP_GE && n == 1) {
|
||||
genop_1(s, OP_GE, cursp());
|
||||
}
|
||||
else if (opt_op == OP_EQ && n == 1) {
|
||||
genop_1(s, OP_EQ, cursp());
|
||||
}
|
||||
else if (opt_op == OP_SETIDX && n == 2) {
|
||||
genop_1(s, OP_SETIDX, cursp());
|
||||
}
|
||||
else if (!noop && n == 0 && gen_uniop(s, sym, cursp())) {
|
||||
/* constant folding succeeded */
|
||||
}
|
||||
else if (!noop && n == 1 && gen_binop(s, sym, cursp())) {
|
||||
/* constant folding succeeded */
|
||||
}
|
||||
else if (noself) {
|
||||
genop_3(s, blk ? OP_SSENDB : OP_SSEND, cursp(), new_sym(s, sym), n|(nk<<4));
|
||||
}
|
||||
else {
|
||||
genop_3(s, blk ? OP_SENDB : OP_SEND, cursp(), new_sym(s, sym), n|(nk<<4));
|
||||
}
|
||||
|
||||
if (safe) {
|
||||
dispatch(s, skip);
|
||||
}
|
||||
if (!val) return;
|
||||
push();
|
||||
}
|
||||
|
||||
static void
|
||||
gen_array_var(codegen_scope *s, node *varnode, int val)
|
||||
{
|
||||
struct mrb_ast_array_node *array = array_node(varnode);
|
||||
int len = ARRAY_NODE_LEN(array);
|
||||
struct mrb_ast_node **elements = ARRAY_NODE_ELEMENTS(array);
|
||||
int i;
|
||||
|
||||
if (!val) return;
|
||||
|
||||
if (len == 0) {
|
||||
genop_2(s, OP_ARRAY, cursp(), 0);
|
||||
push();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Generate code for each element */
|
||||
for (i = 0; i < len; i++) {
|
||||
codegen(s, elements[i], VAL);
|
||||
}
|
||||
|
||||
/* Create array with elements on stack */
|
||||
pop_n(len);
|
||||
genop_2(s, OP_ARRAY, cursp(), len);
|
||||
push();
|
||||
}
|
||||
|
||||
static void
|
||||
gen_hash_var(codegen_scope *s, node *varnode, int val)
|
||||
{
|
||||
struct mrb_ast_hash_node *hash = hash_node(varnode);
|
||||
int len = HASH_NODE_LEN(hash);
|
||||
struct mrb_ast_node **pairs = HASH_NODE_PAIRS(hash);
|
||||
int i;
|
||||
|
||||
if (!val) return;
|
||||
|
||||
if (len == 0) {
|
||||
genop_2(s, OP_HASH, cursp(), 0);
|
||||
push();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Generate code for each key-value pair */
|
||||
for (i = 0; i < len; i++) {
|
||||
codegen(s, pairs[i * 2], VAL); /* key */
|
||||
codegen(s, pairs[i * 2 + 1], VAL); /* value */
|
||||
}
|
||||
|
||||
/* Create hash with key-value pairs on stack */
|
||||
pop_n(len * 2); /* Pop all keys and values */
|
||||
genop_2(s, OP_HASH, cursp(), len);
|
||||
push();
|
||||
}
|
||||
|
||||
static mrb_bool
|
||||
codegen_variable_node(codegen_scope *s, node *varnode, int val)
|
||||
{
|
||||
@@ -4961,6 +5135,18 @@ codegen_variable_node(codegen_scope *s, node *varnode, int val)
|
||||
codegen_cvar(s, VAR_NODE_SYMBOL(varnode), val);
|
||||
return TRUE;
|
||||
|
||||
case NODE_CALL:
|
||||
gen_call_var(s, varnode, val);
|
||||
return TRUE;
|
||||
|
||||
case NODE_ARRAY:
|
||||
gen_array_var(s, varnode, val);
|
||||
return TRUE;
|
||||
|
||||
case NODE_HASH:
|
||||
gen_hash_var(s, varnode, val);
|
||||
return TRUE;
|
||||
|
||||
default:
|
||||
return FALSE; /* Not handled, fall through to main codegen */
|
||||
}
|
||||
|
||||
@@ -213,6 +213,43 @@ struct mrb_ast_var_node {
|
||||
mrb_sym symbol;
|
||||
};
|
||||
|
||||
/* Phase 2 Variable Node Structures */
|
||||
|
||||
/* Variable-sized call node with inline argument storage */
|
||||
struct mrb_ast_call_node {
|
||||
struct mrb_ast_var_header header; /* 8 bytes */
|
||||
struct mrb_ast_node *receiver; /* Receiver object */
|
||||
mrb_sym method_name; /* Method name symbol */
|
||||
uint8_t argc; /* Number of regular arguments */
|
||||
uint8_t has_kwargs:1; /* Has keyword arguments */
|
||||
uint8_t has_block:1; /* Has block argument */
|
||||
uint8_t safe_call:1; /* Safe navigation (&.) */
|
||||
uint8_t reserved:5; /* Reserved for future flags */
|
||||
/* Followed by variable data:
|
||||
* - argc * sizeof(struct mrb_ast_node*) for regular arguments
|
||||
* - struct mrb_ast_node* for kwargs (if has_kwargs)
|
||||
* - struct mrb_ast_node* for block (if has_block)
|
||||
*/
|
||||
struct mrb_ast_node *args[]; /* Flexible array for arguments */
|
||||
};
|
||||
|
||||
/* Variable-sized array node with inline element storage */
|
||||
struct mrb_ast_array_node {
|
||||
struct mrb_ast_var_header header; /* 8 bytes */
|
||||
uint16_t len; /* Number of elements */
|
||||
uint16_t flags; /* Array-specific flags */
|
||||
struct mrb_ast_node *elements[]; /* Flexible array for elements */
|
||||
};
|
||||
|
||||
/* Variable-sized hash node with inline key-value storage */
|
||||
struct mrb_ast_hash_node {
|
||||
struct mrb_ast_var_header header; /* 8 bytes */
|
||||
uint16_t len; /* Number of key-value pairs */
|
||||
uint16_t flags; /* Hash-specific flags */
|
||||
/* Interleaved key-value pairs: key0, value0, key1, value1, ... */
|
||||
struct mrb_ast_node *pairs[]; /* Flexible array for key-value pairs */
|
||||
};
|
||||
|
||||
/* String storage strategy thresholds */
|
||||
#define STR_INLINE_THRESHOLD 48 /* Inline strings <= 48 bytes */
|
||||
#define STR_SMALL_THRESHOLD 128 /* Small strings <= 128 bytes */
|
||||
@@ -238,6 +275,11 @@ struct mrb_ast_var_node {
|
||||
#define int_node(n) ((struct mrb_ast_int_node*)(n))
|
||||
#define var_node(n) ((struct mrb_ast_var_node*)(n))
|
||||
|
||||
/* Phase 2 node casting macros */
|
||||
#define call_node(n) ((struct mrb_ast_call_node*)(n))
|
||||
#define array_node(n) ((struct mrb_ast_array_node*)(n))
|
||||
#define hash_node(n) ((struct mrb_ast_hash_node*)(n))
|
||||
|
||||
/* Phase 1 value access macros */
|
||||
#define SYM_NODE_VALUE(n) (sym_node(n)->symbol)
|
||||
#define STR_NODE_PTR(n) (str_node(n)->data)
|
||||
@@ -246,4 +288,19 @@ struct mrb_ast_var_node {
|
||||
#define INT_NODE_VALUE(n) (int_node(n)->value)
|
||||
#define VAR_NODE_SYMBOL(n) (var_node(n)->symbol)
|
||||
|
||||
/* Phase 2 value access macros */
|
||||
#define CALL_NODE_RECEIVER(n) (call_node(n)->receiver)
|
||||
#define CALL_NODE_METHOD(n) (call_node(n)->method_name)
|
||||
#define CALL_NODE_ARGC(n) (call_node(n)->argc)
|
||||
#define CALL_NODE_ARGS(n) (call_node(n)->args)
|
||||
#define CALL_NODE_HAS_KWARGS(n) (call_node(n)->has_kwargs)
|
||||
#define CALL_NODE_HAS_BLOCK(n) (call_node(n)->has_block)
|
||||
#define CALL_NODE_SAFE(n) (call_node(n)->safe_call)
|
||||
|
||||
#define ARRAY_NODE_LEN(n) (array_node(n)->len)
|
||||
#define ARRAY_NODE_ELEMENTS(n) (array_node(n)->elements)
|
||||
|
||||
#define HASH_NODE_LEN(n) (hash_node(n)->len)
|
||||
#define HASH_NODE_PAIRS(n) (hash_node(n)->pairs)
|
||||
|
||||
#endif /* MRUBY_COMPILER_NODE_H */
|
||||
|
||||
@@ -610,15 +610,169 @@ new_self(parser_state *p)
|
||||
return list1((node*)NODE_SELF);
|
||||
}
|
||||
|
||||
/* Forward declaration for variable-sized call node */
|
||||
static node* new_call_var(parser_state *p, node *receiver, mrb_sym method, node *args, int pass);
|
||||
static node* new_array_var(parser_state *p, node *a);
|
||||
static node* new_hash_var(parser_state *p, node *a);
|
||||
|
||||
/* (:call a b c) */
|
||||
static node*
|
||||
new_call(parser_state *p, node *a, mrb_sym b, node *c, int pass)
|
||||
{
|
||||
if (p->var_nodes_enabled) {
|
||||
return new_call_var(p, a, b, c, pass);
|
||||
}
|
||||
node *n = list4(int_to_node(pass?NODE_CALL:NODE_SCALL), a, sym_to_node(b), c);
|
||||
void_expr_error(p, a);
|
||||
return n;
|
||||
}
|
||||
|
||||
/* Variable-sized call node creation */
|
||||
static node*
|
||||
new_call_var(parser_state *p, node *receiver, mrb_sym method, node *args, int pass)
|
||||
{
|
||||
/* Analyze the arguments to determine size needed */
|
||||
uint8_t argc = 0;
|
||||
uint8_t has_kwargs = 0;
|
||||
uint8_t has_block = 0;
|
||||
node *regular_args = NULL, *kwargs = NULL, *block = NULL;
|
||||
|
||||
if (args) {
|
||||
regular_args = args->car;
|
||||
if (args->cdr) {
|
||||
kwargs = args->cdr->car;
|
||||
has_kwargs = (kwargs != NULL);
|
||||
if (args->cdr->cdr) {
|
||||
block = args->cdr->cdr;
|
||||
has_block = (block != NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Count regular arguments */
|
||||
node *arg_iter = regular_args;
|
||||
while (arg_iter) {
|
||||
argc++;
|
||||
arg_iter = arg_iter->cdr;
|
||||
}
|
||||
|
||||
/* Calculate total size needed */
|
||||
size_t base_size = sizeof(struct mrb_ast_call_node);
|
||||
size_t args_size = argc * sizeof(struct mrb_ast_node*);
|
||||
size_t kwargs_size = has_kwargs ? sizeof(struct mrb_ast_node*) : 0;
|
||||
size_t block_size = has_block ? sizeof(struct mrb_ast_node*) : 0;
|
||||
size_t total_size = base_size + args_size + kwargs_size + block_size;
|
||||
|
||||
enum mrb_ast_size_class class = size_to_class(total_size);
|
||||
|
||||
struct mrb_ast_call_node *n = (struct mrb_ast_call_node*)
|
||||
parser_alloc_var(p, total_size, class);
|
||||
|
||||
init_var_header(&n->header, p, pass ? NODE_CALL : NODE_SCALL, class);
|
||||
n->receiver = receiver;
|
||||
n->method_name = method;
|
||||
n->argc = argc;
|
||||
n->has_kwargs = has_kwargs;
|
||||
n->has_block = has_block;
|
||||
n->safe_call = (pass == 2); /* Assuming pass == 2 means safe call (&.) */
|
||||
n->reserved = 0;
|
||||
|
||||
/* Copy regular arguments into flexible array */
|
||||
arg_iter = regular_args;
|
||||
for (int i = 0; i < argc; i++) {
|
||||
n->args[i] = arg_iter->car;
|
||||
arg_iter = arg_iter->cdr;
|
||||
}
|
||||
|
||||
/* Add kwargs and block after regular arguments */
|
||||
if (has_kwargs) {
|
||||
n->args[argc] = kwargs;
|
||||
}
|
||||
if (has_block) {
|
||||
n->args[argc + (has_kwargs ? 1 : 0)] = block;
|
||||
}
|
||||
|
||||
void_expr_error(p, receiver);
|
||||
return cons_head((node*)NODE_VARIABLE, (node*)n);
|
||||
}
|
||||
|
||||
/* Variable-sized array node creation */
|
||||
static node*
|
||||
new_array_var(parser_state *p, node *a)
|
||||
{
|
||||
/* Count array elements */
|
||||
uint16_t len = 0;
|
||||
node *elem_iter = a;
|
||||
while (elem_iter) {
|
||||
len++;
|
||||
elem_iter = elem_iter->cdr;
|
||||
}
|
||||
|
||||
/* Calculate total size needed */
|
||||
size_t base_size = sizeof(struct mrb_ast_array_node);
|
||||
size_t elems_size = len * sizeof(struct mrb_ast_node*);
|
||||
size_t total_size = base_size + elems_size;
|
||||
|
||||
enum mrb_ast_size_class class = size_to_class(total_size);
|
||||
|
||||
struct mrb_ast_array_node *n = (struct mrb_ast_array_node*)
|
||||
parser_alloc_var(p, total_size, class);
|
||||
|
||||
init_var_header(&n->header, p, NODE_ARRAY, class);
|
||||
n->len = len;
|
||||
n->flags = 0;
|
||||
|
||||
/* Copy elements into flexible array */
|
||||
elem_iter = a;
|
||||
for (int i = 0; i < len; i++) {
|
||||
n->elements[i] = elem_iter->car;
|
||||
elem_iter = elem_iter->cdr;
|
||||
}
|
||||
|
||||
return cons_head((node*)NODE_VARIABLE, (node*)n);
|
||||
}
|
||||
|
||||
/* Variable-sized hash node creation */
|
||||
static node*
|
||||
new_hash_var(parser_state *p, node *a)
|
||||
{
|
||||
/* Count hash key-value pairs */
|
||||
uint16_t len = 0;
|
||||
node *pair_iter = a;
|
||||
while (pair_iter) {
|
||||
len++;
|
||||
pair_iter = pair_iter->cdr;
|
||||
}
|
||||
|
||||
/* Calculate total size needed */
|
||||
size_t base_size = sizeof(struct mrb_ast_hash_node);
|
||||
size_t pairs_size = len * 2 * sizeof(struct mrb_ast_node*); /* key and value for each pair */
|
||||
size_t total_size = base_size + pairs_size;
|
||||
|
||||
enum mrb_ast_size_class class = size_to_class(total_size);
|
||||
|
||||
struct mrb_ast_hash_node *n = (struct mrb_ast_hash_node*)
|
||||
parser_alloc_var(p, total_size, class);
|
||||
|
||||
init_var_header(&n->header, p, NODE_HASH, class);
|
||||
n->len = len;
|
||||
n->flags = 0;
|
||||
|
||||
/* Copy key-value pairs into flexible array */
|
||||
pair_iter = a;
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (pair_iter && pair_iter->car) {
|
||||
/* Each pair is a cons (key . value) */
|
||||
node *pair = pair_iter->car;
|
||||
n->pairs[i * 2] = pair->car; /* key */
|
||||
n->pairs[i * 2 + 1] = pair->cdr; /* value */
|
||||
}
|
||||
pair_iter = pair_iter->cdr;
|
||||
}
|
||||
|
||||
return cons_head((node*)NODE_VARIABLE, (node*)n);
|
||||
}
|
||||
|
||||
/* (:fcall self mid args) */
|
||||
static node*
|
||||
new_fcall(parser_state *p, mrb_sym b, node *c)
|
||||
@@ -743,6 +897,9 @@ new_or(parser_state *p, node *a, node *b)
|
||||
static node*
|
||||
new_array(parser_state *p, node *a)
|
||||
{
|
||||
if (p->cxt && p->cxt->use_variable_nodes) {
|
||||
return new_array_var(p, a);
|
||||
}
|
||||
return cons_head((node*)NODE_ARRAY, a);
|
||||
}
|
||||
|
||||
@@ -758,6 +915,9 @@ new_splat(parser_state *p, node *a)
|
||||
static node*
|
||||
new_hash(parser_state *p, node *a)
|
||||
{
|
||||
if (p->cxt && p->cxt->use_variable_nodes) {
|
||||
return new_hash_var(p, a);
|
||||
}
|
||||
return cons_head((node*)NODE_HASH, a);
|
||||
}
|
||||
|
||||
@@ -6955,7 +7115,7 @@ mrb_parser_new(mrb_state *mrb)
|
||||
p->var_alloc_counts[i] = 0;
|
||||
}
|
||||
p->var_total_allocated = 0;
|
||||
p->var_nodes_enabled = FALSE; /* Start disabled for compatibility */
|
||||
p->var_nodes_enabled = TRUE; /* Enable variable-sized nodes by default */
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
+2061
-1901
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user