bytevm: faster closure creation and less consing

OP_FLET previously had to first create functions referencing the env "before"
and then add these functions to the env "after". This is because we were
addressing from the stack top:

  env: bottom[0:var]top
  fn1: ref(top-1)
  fn2: ref(top-1)
  env: bottom[0:var, 1:fn1, 2:f2n]top

Otherwise fn2 referencing top-1 would point to fn1 instead of var.

Now that locals are addressed from the stack bottom we can add functions eagerly
to locals without consing intermediate sequence of functions:

  env: bottom[0:var]top
  fn1: ref(bottom+0)
  env: bottom[0:var, 1:fn1]top
  fn2: ref(bottom+0)
  env: bottom[0:var, 1:fn1, 2:f2n]top

That saves us one loop (nfun iterations).

A similar observation applies to LABELS, although we still need the closure
fixup because earlier function may reference a function defined later, so we
still need a second loop. That said we don't have to cons a separate sequence of
functions, because we may iterate over locals vector instead.

Both changes save us from an operator that adds a collection to the lcl_env, so
we remove tack_lcl macro and its expansion function foot_lcl.
This commit is contained in:
Daniel Kochmański 2025-05-02 08:40:35 +02:00
parent 0bf83ad30c
commit fdf7c77909

View file

@ -122,7 +122,6 @@ VEclose_around_arg_type()
*/
#define bind_lcl(env, entry) push_lcl(env, entry)
#define tack_lcl(env, entries, n) foot_lcl(env, entries, n)
#define bind_var(env, var, val) bind_lcl(env, CONS(var, val))
#define bind_function(env, fun) bind_lcl(env, fun)
@ -138,19 +137,6 @@ push_lcl(cl_object stack, cl_object new)
*(stack->frame.sp++) = new;
}
static void
foot_lcl(cl_object stack, cl_object list, cl_index n)
{
cl_object entry;
cl_index idx = n;
loop_for_on_unsafe(list) {
entry = ECL_CONS_CAR(list);
idx--;
*(stack->frame.sp+idx) = entry;
} end_loop_for_on_unsafe(list);
stack->frame.sp += n;
}
static void
drop_lcl(cl_object stack, cl_fixnum n)
{
@ -724,16 +710,14 @@ ecl_interpret(cl_object frame, cl_object closure, cl_object bytecodes)
*/
CASE(OP_FLET); {
int idx, nfun;
cl_object fun_env=ECL_NIL, fun;
cl_object fun;
GET_OPARG(nfun, vector);
/* Create closures. */
for(idx = 0; idx<nfun; idx++) {
GET_DATA(fun, vector, data);
fun = ecl_close_around(fun, lcl_env, lex_env);
fun_env = CONS(fun, fun_env);
push_lcl(lcl_env, fun);
}
/* Update the environment with new functions. */
tack_lcl(lcl_env, fun_env, nfun);
THREAD_NEXT;
}
/* OP_LABELS nfun{arg}
@ -748,21 +732,19 @@ ecl_interpret(cl_object frame, cl_object closure, cl_object bytecodes)
*/
CASE(OP_LABELS); {
cl_index idx, nfun;
cl_object fun_env=ECL_NIL, fun;
cl_object fun;
cl_object *sp = lcl_env->frame.sp;
GET_OPARG(nfun, vector);
/* Create closures. */
for(idx = 0; idx<nfun; idx++) {
GET_DATA(fun, vector, data);
fun = close_around_self(fun);
fun_env = CONS(fun, fun_env);
push_lcl(lcl_env, fun);
}
/* Update the environment with new functions. */
tack_lcl(lcl_env, fun_env, nfun);
/* Update the closures so that all functions can call each other */
loop_for_on_unsafe(fun_env) {
fun = ECL_CONS_CAR(fun_env);
for(idx = 0; idx<nfun; idx++) {
fun = *sp++;
close_around_self_fixup(fun, lcl_env, lex_env);
} end_loop_for_on_unsafe(fun_env);
}
THREAD_NEXT;
}
/* OP_LFUNCTION index{fixnum} ; local