Tail self-call optimization in generated C code?

PR polos.ruetz at gmail.com
Mon Aug 21 12:55:06 UTC 2017


2017-08-21 14:11 GMT+02:00, Eric Brunel <eric.brunel at pragmadev.com>:
> Hello all,
>
> I'm trying to get ECL to optimize tail self-calls in the C code
> generated from the Lisp files and it looks like I'm missing something,
> because I can't find a way to do that.

Here is an example that works (not written by me, it's from cliki.net):

(defun fib (n)
  "Tail-recursive computation of the nth element."
  (check-type n (integer 0 *))
  (labels ((fib-aux (n f1 f2)
             (if (zerop n)
                 f1
                 (fib-aux (1- n) f2 (+ f1 f2)))))
    (fib-aux n 0 1)))

The generated C code uses goto, as you can see:

static cl_object LC1fib_aux(cl_object v1n, cl_object v2f1, cl_object v3f2)
{
 cl_object env0;
 const cl_env_ptr cl_env_copy = ecl_process_env();
 cl_object value0;
 ecl_cs_check(cl_env_copy,value0);
 {
TTL:
  if (!(ecl_zerop(v1n))) { goto L1; }
  value0 = v2f1;
  cl_env_copy->nvalues = 1;
  return value0;
L1:;
  v1n = ecl_one_minus(v1n);
  {
   cl_object v4;
   v4 = v3f2;
   v3f2 = ecl_plus(v2f1,v3f2);
   v2f1 = v4;
  }
  goto TTL;
 }
}


Paul


>
> Here is the behavior I'm getting for this simple Lisp function:
>
> (defun enumerate (l &optional (index 0) (result nil))
>    (cond
>      ((null l) (reverse result))
>      (t (enumerate (cdr l) (+ index 1) (cons (list index (car l))
> result)))
>    )
> )
>
> As far as I can see, this function is properly tail-recursive, so I
> expected the generated C code to take the into account and generate a
> goto instead of a recursive call. But what I'm getting is this:
>
> static cl_object L1enumerate(cl_narg narg, cl_object v1l, ...)
> {
>   cl_object T0, T1, T2, T3, T4;
>   cl_object env0;
>   const cl_env_ptr cl_env_copy = ecl_process_env();
>   cl_object value0;
>   ecl_cs_check(cl_env_copy,value0);
>   if (ecl_unlikely(narg<1)) FEwrong_num_arguments_anonym();
>   if (ecl_unlikely(narg>3)) FEwrong_num_arguments_anonym();
>   {
>    cl_object v2index;
>    cl_object v3result;
>    va_list args; va_start(args,v1l);
>    {
>     int i = 1;
>     if (i >= narg) {
>      v2index = ecl_make_fixnum(0);
>     } else {
>      i++;
>      v2index = va_arg(args,cl_object);
>     }
>     if (i >= narg) {
>      v3result = ECL_NIL;
>     } else {
>      i++;
>      v3result = va_arg(args,cl_object);
>     }
>    }
>    va_end(args);
>    if (!(v1l==ECL_NIL)) { goto L3; }
>    value0 = cl_reverse(v3result);
>    return value0;
> L3:;
>    T0 = ecl_cdr(v1l);
>    T1 = ecl_plus(v2index,ecl_make_fixnum(1));
>    T2 = ecl_car(v1l);
>    T3 = cl_list(2, v2index, T2);
>    T4 = CONS(T3,v3result);
>    value0 = L1enumerate(3, T0, T1, T4);
>    return value0;
>   }
> }
>
> The recursive call is generated as a recursive call in C too...
>
> I'm almost sure I've seen C code generated from ECL that generated self
> tail calls as goto's, and I know it works when using the bytecode, where
> you just have to compile the function. But my search for a way to
> trigger this behavior for the generated C code has been unsuccessful so
> far.
>
> I'm using ECL 16.1.3. I used "ecl -c ... --compile ..." to get the C
> code, but my main build uses asdf:make-build with an ASD file.
>
>



More information about the ecl-devel mailing list