From: Adlai
Subject: Allegro CL question
Date: 
Message-ID: <b3c30076-f0cd-4c84-ab50-027bb0f9d58e@3g2000yqk.googlegroups.com>
What declarations do I need to make in Allegro CL to ensure that the
compiler optimizes the tail-calls in a tail-recursive function?


Some details:

I've defined the following:
(defun tr-fact (n &optional (acc 1))
  (if (zerop n) (break "reached 0")
      (tr-fact (1- n) (* n acc))))

The debugger seems to indicate that the recursive calls are not being
merged -- running (tr-fact 5) gives the following backtrace in the
debugger:

Backtrace:
  0: (SWANK::DEBUG-IN-EMACS #<EXCL:SIMPLE-BREAK @ #x20f9c152>)
  1: (SWANK:SWANK-DEBUGGER-HOOK #<EXCL:SIMPLE-BREAK @ #x20f9c152>
#<Function SWANK-DEBUGGER-HOOK>)
  2: (BREAK "reached 0")
  3: (TR-FACT 1 120)
  4: (TR-FACT 2 60)
  5: (TR-FACT 3 20)
  6: (TR-FACT 4 5)
  7: (TR-FACT 5 1)
  8: (TR-FACT 5)
  9: (EVAL (TR-FACT 5))

From: ·············@gmail.com
Subject: Re: Allegro CL question
Date: 
Message-ID: <3ea0f244-adf6-4c3d-83bd-6327241c8d3c@37g2000yqp.googlegroups.com>
On Apr 6, 4:48 am, Adlai <·········@gmail.com> wrote:
> What declarations do I need to make in Allegro CL to ensure that the
> compiler optimizes the tail-calls in a tail-recursive function?
>
See
http://www.franz.com/support/documentation/8.1/doc/compiling.htm#tail-merge-disc-2

Karsten
From: David Sletten
Subject: Re: Allegro CL question
Date: 
Message-ID: <32bd39a9-c6f3-4898-afa3-e72bf204ac6f@v1g2000prd.googlegroups.com>
On Apr 5, 4:48 pm, Adlai <·········@gmail.com> wrote:
> What declarations do I need to make in Allegro CL to ensure that the
> compiler optimizes the tail-calls in a tail-recursive function?
>
> Some details:
>
> I've defined the following:
> (defun tr-fact (n &optional (acc 1))
>   (if (zerop n) (break "reached 0")
>       (tr-fact (1- n) (* n acc))))
>
> The debugger seems to indicate that the recursive calls are not being
> merged -- running (tr-fact 5) gives the following backtrace in the
> debugger:
>
> Backtrace:
>   0: (SWANK::DEBUG-IN-EMACS #<EXCL:SIMPLE-BREAK @ #x20f9c152>)
>   1: (SWANK:SWANK-DEBUGGER-HOOK #<EXCL:SIMPLE-BREAK @ #x20f9c152>
> #<Function SWANK-DEBUGGER-HOOK>)
>   2: (BREAK "reached 0")
>   3: (TR-FACT 1 120)
>   4: (TR-FACT 2 60)
>   5: (TR-FACT 3 20)
>   6: (TR-FACT 4 5)
>   7: (TR-FACT 5 1)
>   8: (TR-FACT 5)
>   9: (EVAL (TR-FACT 5))

Paul Graham says in _On Lisp_ (pg. 23) that (proclaim '(optimize
speed)) should do the trick. It seems to be enabled by default with
SBCL and Clozure.

Aloha,
David Sletten
From: Raffael Cavallaro
Subject: Re: Allegro CL question
Date: 
Message-ID: <3a2318de-8f1f-4be3-b3cb-a72425d404d5@o11g2000yql.googlegroups.com>
On Apr 6, 4:03 am, David Sletten <·····@bosatsu.net> wrote:
> On Apr 5, 4:48 pm, Adlai <·········@gmail.com> wrote:
>
>
>
>
>
> > What declarations do I need to make in Allegro CL to ensure that the
> > compiler optimizes the tail-calls in a tail-recursive function?
>
> > Some details:
>
> > I've defined the following:
> > (defun tr-fact (n &optional (acc 1))
> >   (if (zerop n) (break "reached 0")
> >       (tr-fact (1- n) (* n acc))))
>
> > The debugger seems to indicate that the recursive calls are not being
> > merged -- running (tr-fact 5) gives the following backtrace in the
> > debugger:
>
> > Backtrace:
> >   0: (SWANK::DEBUG-IN-EMACS #<EXCL:SIMPLE-BREAK @ #x20f9c152>)
> >   1: (SWANK:SWANK-DEBUGGER-HOOK #<EXCL:SIMPLE-BREAK @ #x20f9c152>
> > #<Function SWANK-DEBUGGER-HOOK>)
> >   2: (BREAK "reached 0")
> >   3: (TR-FACT 1 120)
> >   4: (TR-FACT 2 60)
> >   5: (TR-FACT 3 20)
> >   6: (TR-FACT 4 5)
> >   7: (TR-FACT 5 1)
> >   8: (TR-FACT 5)
> >   9: (EVAL (TR-FACT 5))
>
> Paul Graham says in _On Lisp_ (pg. 23) that (proclaim '(optimize
> speed)) should do the trick. It seems to be enabled by default with
> SBCL and Clozure.
>
> Aloha,
> David Sletten

There's already a link to Franz's docs which is of course definitive
for that implementation. In other implementations, be aware that TCO
may not be enabled if debug settings are high because it is the very
nature of TCO that it can eliminate stack frames (i.e., TCO can re-
uses stack frames rather than growing the stack). When one is
debugging, one often wants to be able to see stack backtraces, so when
one does (optimize ... (debug 3)...  one may not get TCO.

Again, each implementation's docs are the last word on how to achieve
this. For example, Lispworks won't do TCO if (optimize (debug 3) is in
effect (among other things):
<http://www.lispworks.com/documentation/lw51/LWUG/html/lwuser-111.htm>
From: Adlai
Subject: Re: Allegro CL question
Date: 
Message-ID: <5c0bb413-6210-4a0a-a071-668c3bc4217d@f19g2000vbf.googlegroups.com>
Thank you all for your help!

-Adlai
From: Pascal J. Bourguignon
Subject: Re: Allegro CL question
Date: 
Message-ID: <87y6uecjoo.fsf@informatimago.com>
Adlai <·········@gmail.com> writes:

> What declarations do I need to make in Allegro CL to ensure that the
> compiler optimizes the tail-calls in a tail-recursive function?
>
>
> Some details:
>
> I've defined the following:
> (defun tr-fact (n &optional (acc 1))
>   (if (zerop n) (break "reached 0")
>       (tr-fact (1- n) (* n acc))))
>
> The debugger seems to indicate that the recursive calls are not being
> merged -- running (tr-fact 5) gives the following backtrace in the
> debugger:
>
> Backtrace:
>   0: (SWANK::DEBUG-IN-EMACS #<EXCL:SIMPLE-BREAK @ #x20f9c152>)
>   1: (SWANK:SWANK-DEBUGGER-HOOK #<EXCL:SIMPLE-BREAK @ #x20f9c152>
> #<Function SWANK-DEBUGGER-HOOK>)
>   2: (BREAK "reached 0")
>   3: (TR-FACT 1 120)
>   4: (TR-FACT 2 60)
>   5: (TR-FACT 3 20)
>   6: (TR-FACT 4 5)
>   7: (TR-FACT 5 1)
>   8: (TR-FACT 5)
>   9: (EVAL (TR-FACT 5))


(defun determine-optimization-level-for-tco ()
  (loop
     :for level :from 0 :to 3
     :do (progn (proclaim `(optimize (debug ,level)))
                (compile 'tr-fact (lambda (n &optional (acc 1))
                                    (block tr-fact
                                      (if (zerop n)
                                          (break "reached 0")
                                          (tr-fact (1- n) (* n acc))))))
                (with-simple-restart
                    (next-level
                     "Current: (debug ~D). Continue with next level" 
                     level)
                  (tr-fact 5)))
     :finally (progn (setf (symbol-function 'tr-fact)
                           (lambda (n &optional (acc 1))
                             (block tr-fact
                               (if (zerop n)
                                   (break "reached 0")
                                   (tr-fact (1- n) (* n acc))))))
                     (with-simple-restart
                         (next-level
                          "Current: not compiled. Done.")
                       (tr-fact 5)))))



But with some implementations, TCO is enabled as soon as you compile:


C/USER[27]> (DETERMINE-OPTIMIZATION-LEVEL-FOR-TCO)

** - Continuable Error
reached 0
If you continue (by typing 'continue'): Return from BREAK loop
The following restarts are also available:
NEXT-LEVEL     :R1      Current: (debug 0). Continue with next level
ABORT          :R2      Abort main loop
C/Break 1 USER[28]> :bt 5
<1/263> #<SYSTEM-FUNCTION EXT:SHOW-STACK> 3
<2/256> #<COMPILED-FUNCTION SYSTEM::PRINT-BACKTRACE>
<3/250> #<COMPILED-FUNCTION SYSTEM::DEBUG-BACKTRACE>
<4/241> #<SYSTEM-FUNCTION SYSTEM::READ-EVAL-PRINT> 2
<5/238> #<COMPILED-FUNCTION SYSTEM::BREAK-LOOP-2-3>
<6/234> #<SYSTEM-FUNCTION SYSTEM::SAME-ENV-AS> 2
<7/220> #<COMPILED-FUNCTION SYSTEM::BREAK-LOOP-2>
<8/218> #<SYSTEM-FUNCTION SYSTEM::DRIVER>
<9/178> #<COMPILED-FUNCTION SYSTEM::BREAK-LOOP>
<10/175> #<SYSTEM-FUNCTION INVOKE-DEBUGGER>
<11/158> #<COMPILED-FUNCTION BREAK>
<12/155> #<COMPILED-FUNCTION TR-FACT>
<13/152> #<COMPILED-FUNCTION TR-FACT>
<14/149> #<COMPILED-FUNCTION TR-FACT>
<15/146> #<COMPILED-FUNCTION TR-FACT>
<16/143> #<COMPILED-FUNCTION TR-FACT>
<17/140> #<COMPILED-FUNCTION TR-FACT>
<18/136> #<SPECIAL-OPERATOR RETURN-FROM>
<19/124> #<SPECIAL-OPERATOR LET*>
<20/112> #<SPECIAL-OPERATOR TAGBODY>
<21/101> #<SPECIAL-OPERATOR LET>
<22/91> #<SPECIAL-OPERATOR BLOCK>
<23/86> #<SPECIAL-OPERATOR PROGN>
<24/73> #<SPECIAL-OPERATOR TAGBODY>
<25/65> #<SPECIAL-OPERATOR LET>
<26/53> #<SPECIAL-OPERATOR LET>
[38] APPLY frame for call (DETERMINE-OPTIMIZATION-LEVEL-FOR-TCO)
Printed 26 frames
C/Break 1 USER[28]> :r1

** - Continuable Error
reached 0
If you continue (by typing 'continue'): Return from BREAK loop
The following restarts are also available:
NEXT-LEVEL     :R1      Current: (debug 1). Continue with next level
ABORT          :R2      Abort main loop
C/Break 1 USER[29]> :bt 5
<1/263> #<SYSTEM-FUNCTION EXT:SHOW-STACK> 3
<2/256> #<COMPILED-FUNCTION SYSTEM::PRINT-BACKTRACE>
<3/250> #<COMPILED-FUNCTION SYSTEM::DEBUG-BACKTRACE>
<4/241> #<SYSTEM-FUNCTION SYSTEM::READ-EVAL-PRINT> 2
<5/238> #<COMPILED-FUNCTION SYSTEM::BREAK-LOOP-2-3>
<6/234> #<SYSTEM-FUNCTION SYSTEM::SAME-ENV-AS> 2
<7/220> #<COMPILED-FUNCTION SYSTEM::BREAK-LOOP-2>
<8/218> #<SYSTEM-FUNCTION SYSTEM::DRIVER>
<9/178> #<COMPILED-FUNCTION SYSTEM::BREAK-LOOP>
<10/175> #<SYSTEM-FUNCTION INVOKE-DEBUGGER>
<11/158> #<COMPILED-FUNCTION BREAK>
<12/155> #<COMPILED-FUNCTION TR-FACT>
<13/152> #<COMPILED-FUNCTION TR-FACT>
<14/149> #<COMPILED-FUNCTION TR-FACT>
<15/146> #<COMPILED-FUNCTION TR-FACT>
<16/143> #<COMPILED-FUNCTION TR-FACT>
<17/140> #<COMPILED-FUNCTION TR-FACT>
<18/136> #<SPECIAL-OPERATOR RETURN-FROM>
<19/124> #<SPECIAL-OPERATOR LET*>
<20/112> #<SPECIAL-OPERATOR TAGBODY>
<21/101> #<SPECIAL-OPERATOR LET>
<22/91> #<SPECIAL-OPERATOR BLOCK>
<23/86> #<SPECIAL-OPERATOR PROGN>
<24/73> #<SPECIAL-OPERATOR TAGBODY>
<25/65> #<SPECIAL-OPERATOR LET>
<26/53> #<SPECIAL-OPERATOR LET>
[38] APPLY frame for call (DETERMINE-OPTIMIZATION-LEVEL-FOR-TCO)
Printed 26 frames
C/Break 1 USER[29]> :r1

** - Continuable Error
reached 0
If you continue (by typing 'continue'): Return from BREAK loop
The following restarts are also available:
NEXT-LEVEL     :R1      Current: (debug 2). Continue with next level
ABORT          :R2      Abort main loop
C/Break 1 USER[30]> :bt 5
<1/263> #<SYSTEM-FUNCTION EXT:SHOW-STACK> 3
<2/256> #<COMPILED-FUNCTION SYSTEM::PRINT-BACKTRACE>
<3/250> #<COMPILED-FUNCTION SYSTEM::DEBUG-BACKTRACE>
<4/241> #<SYSTEM-FUNCTION SYSTEM::READ-EVAL-PRINT> 2
<5/238> #<COMPILED-FUNCTION SYSTEM::BREAK-LOOP-2-3>
<6/234> #<SYSTEM-FUNCTION SYSTEM::SAME-ENV-AS> 2
<7/220> #<COMPILED-FUNCTION SYSTEM::BREAK-LOOP-2>
<8/218> #<SYSTEM-FUNCTION SYSTEM::DRIVER>
<9/178> #<COMPILED-FUNCTION SYSTEM::BREAK-LOOP>
<10/175> #<SYSTEM-FUNCTION INVOKE-DEBUGGER>
<11/158> #<COMPILED-FUNCTION BREAK>
<12/155> #<COMPILED-FUNCTION TR-FACT>
<13/152> #<COMPILED-FUNCTION TR-FACT>
<14/149> #<COMPILED-FUNCTION TR-FACT>
<15/146> #<COMPILED-FUNCTION TR-FACT>
<16/143> #<COMPILED-FUNCTION TR-FACT>
<17/140> #<COMPILED-FUNCTION TR-FACT>
<18/136> #<SPECIAL-OPERATOR RETURN-FROM>
<19/124> #<SPECIAL-OPERATOR LET*>
<20/112> #<SPECIAL-OPERATOR TAGBODY>
<21/101> #<SPECIAL-OPERATOR LET>
<22/91> #<SPECIAL-OPERATOR BLOCK>
<23/86> #<SPECIAL-OPERATOR PROGN>
<24/73> #<SPECIAL-OPERATOR TAGBODY>
<25/65> #<SPECIAL-OPERATOR LET>
<26/53> #<SPECIAL-OPERATOR LET>
[38] APPLY frame for call (DETERMINE-OPTIMIZATION-LEVEL-FOR-TCO)
Printed 26 frames
C/Break 1 USER[30]> :r1

** - Continuable Error
reached 0
If you continue (by typing 'continue'): Return from BREAK loop
The following restarts are also available:
NEXT-LEVEL     :R1      Current: (debug 3). Continue with next level
ABORT          :R2      Abort main loop
C/Break 1 USER[31]> :bt 5
<1/263> #<SYSTEM-FUNCTION EXT:SHOW-STACK> 3
<2/256> #<COMPILED-FUNCTION SYSTEM::PRINT-BACKTRACE>
<3/250> #<COMPILED-FUNCTION SYSTEM::DEBUG-BACKTRACE>
<4/241> #<SYSTEM-FUNCTION SYSTEM::READ-EVAL-PRINT> 2
<5/238> #<COMPILED-FUNCTION SYSTEM::BREAK-LOOP-2-3>
<6/234> #<SYSTEM-FUNCTION SYSTEM::SAME-ENV-AS> 2
<7/220> #<COMPILED-FUNCTION SYSTEM::BREAK-LOOP-2>
<8/218> #<SYSTEM-FUNCTION SYSTEM::DRIVER>
<9/178> #<COMPILED-FUNCTION SYSTEM::BREAK-LOOP>
<10/175> #<SYSTEM-FUNCTION INVOKE-DEBUGGER>
<11/158> #<COMPILED-FUNCTION BREAK>
<12/155> #<COMPILED-FUNCTION TR-FACT>
<13/152> #<COMPILED-FUNCTION TR-FACT>
<14/149> #<COMPILED-FUNCTION TR-FACT>
<15/146> #<COMPILED-FUNCTION TR-FACT>
<16/143> #<COMPILED-FUNCTION TR-FACT>
<17/140> #<COMPILED-FUNCTION TR-FACT>
<18/136> #<SPECIAL-OPERATOR RETURN-FROM>
<19/124> #<SPECIAL-OPERATOR LET*>
<20/112> #<SPECIAL-OPERATOR TAGBODY>
<21/101> #<SPECIAL-OPERATOR LET>
<22/91> #<SPECIAL-OPERATOR BLOCK>
<23/86> #<SPECIAL-OPERATOR PROGN>
<24/73> #<SPECIAL-OPERATOR TAGBODY>
<25/65> #<SPECIAL-OPERATOR LET>
<26/53> #<SPECIAL-OPERATOR LET>
[38] APPLY frame for call (DETERMINE-OPTIMIZATION-LEVEL-FOR-TCO)
Printed 26 frames
C/Break 1 USER[31]> :r1

** - Continuable Error
reached 0
If you continue (by typing 'continue'): Return from BREAK loop
The following restarts are also available:
NEXT-LEVEL     :R1      Current: not compiled. Done.
ABORT          :R2      Abort main loop
C/Break 1 USER[32]> :bt 5
<1/396> #<SYSTEM-FUNCTION EXT:SHOW-STACK> 3
<2/389> #<COMPILED-FUNCTION SYSTEM::PRINT-BACKTRACE>
<3/383> #<COMPILED-FUNCTION SYSTEM::DEBUG-BACKTRACE>
<4/374> #<SYSTEM-FUNCTION SYSTEM::READ-EVAL-PRINT> 2
<5/371> #<COMPILED-FUNCTION SYSTEM::BREAK-LOOP-2-3>
<6/367> #<SYSTEM-FUNCTION SYSTEM::SAME-ENV-AS> 2
<7/353> #<COMPILED-FUNCTION SYSTEM::BREAK-LOOP-2>
<8/351> #<SYSTEM-FUNCTION SYSTEM::DRIVER>
<9/311> #<COMPILED-FUNCTION SYSTEM::BREAK-LOOP>
<10/308> #<SYSTEM-FUNCTION INVOKE-DEBUGGER>
<11/291> #<COMPILED-FUNCTION BREAK>
<12/288> #<SPECIAL-OPERATOR IF>
[266] APPLY frame for call (:LAMBDA '0 '120)
<13/261> 
#<FUNCTION :LAMBDA (N &OPTIONAL (ACC 1))
  (BLOCK TR-FACT (IF (ZEROP N) (BREAK "reached 0") (TR-FACT (1- N) (* N ACC))))> 2
<14/260> #<SPECIAL-OPERATOR IF>
[238] APPLY frame for call (:LAMBDA '1 '120)
<15/233> 
#<FUNCTION :LAMBDA (N &OPTIONAL (ACC 1))
  (BLOCK TR-FACT (IF (ZEROP N) (BREAK "reached 0") (TR-FACT (1- N) (* N ACC))))> 2
<16/232> #<SPECIAL-OPERATOR IF>
[210] APPLY frame for call (:LAMBDA '2 '60)
<17/205> 
#<FUNCTION :LAMBDA (N &OPTIONAL (ACC 1))
  (BLOCK TR-FACT (IF (ZEROP N) (BREAK "reached 0") (TR-FACT (1- N) (* N ACC))))> 2
<18/204> #<SPECIAL-OPERATOR IF>
[182] APPLY frame for call (:LAMBDA '3 '20)
<19/177> 
#<FUNCTION :LAMBDA (N &OPTIONAL (ACC 1))
  (BLOCK TR-FACT (IF (ZEROP N) (BREAK "reached 0") (TR-FACT (1- N) (* N ACC))))> 2
<20/176> #<SPECIAL-OPERATOR IF>
[154] APPLY frame for call (:LAMBDA '4 '5)
<21/149> 
#<FUNCTION :LAMBDA (N &OPTIONAL (ACC 1))
  (BLOCK TR-FACT (IF (ZEROP N) (BREAK "reached 0") (TR-FACT (1- N) (* N ACC))))> 2
<22/148> #<SPECIAL-OPERATOR IF>
[126] APPLY frame for call (:LAMBDA '5)
<23/122> 
#<FUNCTION :LAMBDA (N &OPTIONAL (ACC 1))
  (BLOCK TR-FACT (IF (ZEROP N) (BREAK "reached 0") (TR-FACT (1- N) (* N ACC))))> 1
<24/120> #<SPECIAL-OPERATOR RETURN-FROM>
<25/108> #<SPECIAL-OPERATOR LET>
<26/101> #<SPECIAL-OPERATOR CATCH>
<27/91> #<SPECIAL-OPERATOR BLOCK>
<28/86> #<SPECIAL-OPERATOR PROGN>
<29/73> #<SPECIAL-OPERATOR TAGBODY>
<30/65> #<SPECIAL-OPERATOR LET>
<31/53> #<SPECIAL-OPERATOR LET>
[38] APPLY frame for call (DETERMINE-OPTIMIZATION-LEVEL-FOR-TCO)
Printed 31 frames
C/Break 1 USER[32]> :r1
NIL
C/USER[33]> 

-- 
__Pascal Bourguignon__
http://www.informatimago.com