mirror of
https://github.com/mruby/mruby
synced 2026-06-08 16:11:16 +00:00
mruby-compiler: optimize splat of literal arrays in args/literals
- Skip no-op splats of empty array literals (`*[]` / zarray) in call argument generation and array literal codegen. - Inline non-empty literal splat arrays without inner splats (e.g. `*[a,b]`) as regular positional args/elements, avoiding building a temporary array and ARYCAT. This removes unnecessary `LOADNIL` + `ARRAY 0` + `ARYCAT` sequences (e.g. `mruby -ve 'p *[]'`) and reduces temporary allocations while preserving semantics and evaluation order. Falls back to the generic path when nested splats are present or counts exceed fixed-arity. No behavior change intended; only codegen improvements. Co-authored-by: Codex <codex@openai.com>
This commit is contained in:
@@ -2640,6 +2640,43 @@ gen_values(codegen_scope *s, node *t, int val, int limit)
|
||||
while (t) {
|
||||
int is_splat = is_splat_node(t->car);
|
||||
|
||||
/* Optimization: skip or inline literal splat arrays
|
||||
* - Empty splat (`*[]`/`*zarray`): contributes nothing; skip.
|
||||
* - Non-empty literal array with no inner splat (`*[a,b]`): inline
|
||||
* as normal positional args to avoid building/concatenating arrays.
|
||||
*/
|
||||
if (is_splat) {
|
||||
node *sv = SPLAT_NODE_VALUE(t->car);
|
||||
if (sv) {
|
||||
enum node_type nt = get_node_type(sv);
|
||||
if (nt == NODE_ARRAY) {
|
||||
struct mrb_ast_array_node *an = array_node(sv);
|
||||
if (ARRAY_NODE_ELEMENTS(an) == NULL) {
|
||||
/* empty splat; contributes nothing */
|
||||
t = t->cdr;
|
||||
continue;
|
||||
}
|
||||
else if (nosplat(ARRAY_NODE_ELEMENTS(an))) {
|
||||
/* Inline non-empty literal array elements as regular args */
|
||||
node *e = ARRAY_NODE_ELEMENTS(an);
|
||||
while (e) {
|
||||
/* Honor evaluation order */
|
||||
codegen(s, e->car, val);
|
||||
n++;
|
||||
e = e->cdr;
|
||||
}
|
||||
t = t->cdr;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (nt == NODE_ZARRAY) {
|
||||
/* explicit empty array literal */
|
||||
t = t->cdr;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_splat || cursp() >= slimit) { /* flush stack */
|
||||
pop_n(n);
|
||||
if (first) {
|
||||
@@ -3630,6 +3667,25 @@ codegen_array(codegen_scope *s, node *varnode, int val)
|
||||
struct mrb_ast_node *element = current->car;
|
||||
int is_splat = is_splat_node(element);
|
||||
|
||||
/* Skip splat of an empty literal array: [*[]] => [] without ARYCAT noise */
|
||||
if (is_splat) {
|
||||
node *sv = SPLAT_NODE_VALUE(element);
|
||||
if (sv) {
|
||||
enum node_type nt = get_node_type(sv);
|
||||
if (nt == NODE_ARRAY) {
|
||||
struct mrb_ast_array_node *an = array_node(sv);
|
||||
if (ARRAY_NODE_ELEMENTS(an) == NULL) {
|
||||
current = current->cdr;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (nt == NODE_ZARRAY) {
|
||||
current = current->cdr;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_splat || cursp() >= slimit) { /* flush accumulated elements */
|
||||
if (regular_elements > 0) {
|
||||
pop_n(regular_elements);
|
||||
|
||||
Reference in New Issue
Block a user