• Skip to main content
  • Skip to search
  • Skip to footer
Cadence Home
  • This search text may be transcribed, used, stored, or accessed by our third-party service providers per our Cookie Policy and Privacy Policy.

  1. Community Forums
  2. Custom IC SKILL
  3. Unexpected behavior with optimizeTailCall

Stats

  • Replies 5
  • Subscribers 142
  • Views 416
  • Members are here 0

Unexpected behavior with optimizeTailCall

Aurel B
Aurel B 16 days ago
;; Below functions are used to implement left `fold` one.
;; The goal is to make sure tail-call optimization is enabled during the recursion and reverted properly afterwards.
;; 
;; See reference : Wikipedia - Fold_(higher-order_function)"

;; I made several examples but I am surprised about the results...

(inScheme
(let ()

  ;; -------------------------------------------------------
  ;; Recursive helper
  ;; -------------------------------------------------------

  (defun rec ( function init l )
    "Apply FUNCTION to INIT and the head of L.
The result of this call is used as init and applied recursively to the tail of L"
    (if l
        (rec function (funcall function init (car l)) (cdr l))
      init
      ))

  ;; -------------------------------------------------------
  ;; Safe Revert (using `errset` or `unwindProtect`)
  ;; -------------------------------------------------------

  (defglobalfun foldl1_unwind_progn ( function l )
    "Left Fold: Apply combination FUNCTION to all elments of L starting from the left."
    (let ( ( tail_call_opt (status optimizeTailCall) )
           )
      (unwindProtect
        (progn (sstatus optimizeTailCall t) (rec function (car l) (cdr l)))
        (sstatus optimizeTailCall tail_call_opt)
        )
      ))

  (defglobalfun foldl1_unwind ( function l )
    "Left Fold: Apply combination FUNCTION to all elments of L starting from the left."
    (let ( ( tail_call_opt (status optimizeTailCall) )
           )
      (sstatus optimizeTailCall t)
      (unwindProtect
        (rec function (car l) (cdr l))
        (sstatus optimizeTailCall tail_call_opt)
        )
      ))

  (defglobalfun foldl1_unwind_nil ( function l )
    "Left Fold: Apply combination FUNCTION to all elments of L starting from the left."
    (let ( ( tail_call_opt (status optimizeTailCall) )
           )
      (sstatus optimizeTailCall t)
      (unwindProtect
        ;; Setting it to nil here does not seem to matter
        (progn (sstatus optimizeTailCall nil) (rec function (car l) (cdr l)))
        (sstatus optimizeTailCall tail_call_opt)
        )
      ))

  (defglobalfun foldl1_errset ( function l )
    "Left Fold: Apply combination FUNCTION to all elments of L starting from the left."
    (let ( ( tail_call_opt (status optimizeTailCall) )
           res )
      (sstatus optimizeTailCall t)
      (setq res (errset (rec function (car l) (cdr l))))
      (sstatus optimizeTailCall tail_call_opt)
      (if res (car res) (error "foldl1_errset - error occured: %N" errset.errset))
      ))

  ;; -------------------------------------------------------
  ;; Unsafe Revert
  ;; -------------------------------------------------------

  (defglobalfun foldl1_let ( function l )
    "Left Fold: Apply combination FUNCTION to all elments of L starting from the left."
    (let ( ( tail_call_opt (status optimizeTailCall) )
           res )
      (sstatus optimizeTailCall t)
      (setq res (rec function (car l) (cdr l)))
      (sstatus optimizeTailCall tail_call_opt)
      res))

  (defglobalfun foldl1_prog1 ( function l )
    "Left Fold: Apply combination FUNCTION to all elments of L starting from the left."
    (let ( ( tail_call_opt (status optimizeTailCall) )
           )
      (sstatus optimizeTailCall t)
      (prog1 (rec function (car l) (cdr l))) (sstatus optimizeTailCall tail_call_opt)
      ))

  (defglobalfun foldl1_putpropqq ( function l )
    "Left Fold: Apply combination FUNCTION to all elments of L starting from the left."
    (setf foldl1_putpropqq.tail_call_opt (status optimizeTailCall))
    (sstatus optimizeTailCall t)
    (prog1 (rec function (car l) (cdr l)) (sstatus optimizeTailCall foldl1_putpropqq.tail_call_opt))
    )

  ));scheme closure

;; -------------------------------------------------------
;; Testing done in the CIW
;; -------------------------------------------------------

/*
(tracef rec)
(foreach fun '( foldl1_unwind_progn foldl1_unwind foldl1_errset foldl1_let foldl1_prog1 foldl1_putpropqq)
  (println fun)
  (sstatus optimizeTailCall nil)
  (println (funcall fun 'times (list 1 2 3 4)))
  )
;*/

;; The only functions where tail-call optimization is actually working are:
;; foldl1_unwind foldl1_unwind_nil foldl1_errset

;; It feels like wrapping the call inside `errset` or `unwindProtect`
;; enables the switch but when using it in the same environment it is not yet taken in account.

;; Is this normal?
;; Am I missing something?

;; Thanks
;; Aurel

  • Cancel
  • Sign in to reply
Parents
  • Andrew Beckett
    Andrew Beckett 16 days ago

    I'm not sure it works properly if you set it on the fly in the middle of a function. If you call (sstatus optimizeTailCall t) before calling your test code (without turning it off inside the loop), it all works OK.

    It's not well documented exactly when exactly it has an effect. I'll see if I can find out more. This is probably better as a support question than on the forums (it will probably end up with me anyway), but I'll see what I can find out in the meantime.

    Andrew

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • Cancel
  • Aurel B
    Aurel B 16 days ago in reply to Andrew Beckett

    Yes, I was not sure if setting it on the fly is a good practice. (I was surprised that it works specifically in some cases though)

    Although in some of my code it is a requirement, otherwise I would have to rewrite the code and make it iterative instead of recursive...

    Thanks for your answer Slight smile 

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • Cancel
  • Andrew Beckett
    Andrew Beckett 11 days ago in reply to Aurel B

    It seems that optimizeTailCall is enabled during the evaluator, which is why it affects unwindProtect and errset. In normally running code it doesn't have an impact - which is why you get this inconsistent behaviour.

    Ideally it would have been set at compile time so you get predictable behaviour, but sadly that's not how it was implemented.

    My recommendation is to do the tail call optimization yourself and make your function iterative instead. That's the approach we've taken in my team to ensure predictable behaviour (we don't want things changing because somebody alters it on the fly).

    Andrew

    • Cancel
    • Vote Up +1 Vote Down
    • Sign in to reply
    • Cancel
  • Aurel B
    Aurel B 11 days ago in reply to Andrew Beckett

    Hi Andrew Beckett , thanks for the update.

    It's not blocking for now but I will probably update my code in the future to make it iterative.

    Is there any reason why tail-call optimization is not always enabled?

    Can it break some things?

    Or is it only because it is incompatible with debug mode?

    Thanks.

    Aurel

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • Cancel
  • Andrew Beckett
    Andrew Beckett 11 days ago in reply to Aurel B
    Aurel B said:
    Is there any reason why tail-call optimization is not always enabled?

    It was a bit of caution at the time it was added, plus a concern about it not behaving properly in debug mode (although I think it would have been fine; if anything having different behaviour in debug mode than normal mode is worse because the code will behave differently then). These were concerns we raised when this was first implemented, but unfortunately it never got implemented that way.

    • Cancel
    • Vote Up +1 Vote Down
    • Sign in to reply
    • Cancel
Reply
  • Andrew Beckett
    Andrew Beckett 11 days ago in reply to Aurel B
    Aurel B said:
    Is there any reason why tail-call optimization is not always enabled?

    It was a bit of caution at the time it was added, plus a concern about it not behaving properly in debug mode (although I think it would have been fine; if anything having different behaviour in debug mode than normal mode is worse because the code will behave differently then). These were concerns we raised when this was first implemented, but unfortunately it never got implemented that way.

    • Cancel
    • Vote Up +1 Vote Down
    • Sign in to reply
    • Cancel
Children
No Data

Community Guidelines

The Cadence Design Communities support Cadence users and technologists interacting to exchange ideas, news, technical information, and best practices to solve problems and get the most from Cadence technology. The community is open to everyone, and to provide the most value, we require participants to follow our Community Guidelines that facilitate a quality exchange of ideas and information. By accessing, contributing, using or downloading any materials from the site, you agree to be bound by the full Community Guidelines.

© 2025 Cadence Design Systems, Inc. All Rights Reserved.

  • Terms of Use
  • Privacy
  • Cookie Policy
  • US Trademarks
  • Do Not Sell or Share My Personal Information