Automatic tail-call optimization (auto-TCO) in StaticOptimizer#694
Draft
He-Pin wants to merge 1 commit intodatabricks:masterfrom
Draft
Automatic tail-call optimization (auto-TCO) in StaticOptimizer#694He-Pin wants to merge 1 commit intodatabricks:masterfrom
He-Pin wants to merge 1 commit intodatabricks:masterfrom
Conversation
7adb221 to
da38f7c
Compare
Detect self-recursive calls in tail position during static optimization and mark them for the TailCall trampoline, eliminating JVM stack overflow on deep recursion without requiring users to annotate call sites with 'tailstrict'. Key design: introduce TailstrictModeAutoTCO — a third TailstrictMode that enables the trampoline (like TailstrictModeEnabled) but does NOT force eager argument evaluation (unlike explicit tailstrict). This preserves Jsonnet's standard lazy evaluation semantics for auto-TCO'd calls. Implementation: - StaticOptimizer.transformBind: detects self-recursive function bindings - hasNonRecursiveExit: safety check ensuring at least one non-recursive code path exists (prevents infinite trampoline on trivially infinite functions like f(x) = f(x)) - markTailCalls: walks the AST marking self-recursive tail calls with tailstrict=true, autoTCO=true - Expr: adds isAutoTCO/autoTCO field to Apply0-3 and Apply case classes - Val: adds TailstrictModeAutoTCO, TailCall.autoTCO flag, restores @tailrec on TailCall.resolve - Evaluator: visitExprWithTailCallSupport uses visitAsLazy for auto-TCO args; visitApply* defensively handles auto-TCO for future-proofing Test: auto_tco.jsonnet with 6 patterns including lazy semantics regression test (error in auto-TCO'd args is NOT eagerly evaluated). Upstream: databricks#623
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
Jsonnet programs often use recursive functions that are naturally tail-recursive (the recursive call is the last expression in a branch). Without tail-call optimization (TCO), these hit StackOverflowError on deep recursion. Currently, sjsonnet supports TCO via explicit
TailCallnodes, but users must manually structure their code to trigger it.Key Design Decision
Implement automatic TCO detection in the
StaticOptimizer. During the optimization pass, analyze function bodies to identify tail-position calls and automatically rewrite them to useTailCallnodes. This happens transparently — no source code changes needed.Modification
sjsonnet/src/sjsonnet/StaticOptimizer.scala:if/elsebranches,localbindings)Apply*to theTailCallvariantsjsonnet/src/sjsonnet/Expr.scala:Exprnode types for automatic tail callssjsonnet/src/sjsonnet/ExprTransform.scala:sjsonnet/src/sjsonnet/Evaluator.scala:sjsonnet/src/sjsonnet/Val.scala:Tests:
new_test_suite/auto_tco.jsonnet— verifies deep recursion (>10000 depth) works with auto-TCOnew_test_suite/auto_tco.jsonnet.golden— expected outputBenchmark Results
Expected Impact
tail_callbenchmark: direct improvement (deeper recursion without stack overflow)inheritance_function_recursion: may benefit from TCO on recursive inheritance patternsrealistic2: depends on recursion depth in the workloadJMH — Pending Data
Analysis
if/elsebranches,localbindings, and direct self-recursion. Mutual recursion is out of scope.TailCall.resolve) is already battle-tested in the existing manual TCO path.TailCallinfrastructure. The static optimizer detects cases that users would otherwise need to manually optimize.References
TailCallmechanism insjsonnet/src/sjsonnet/Val.scalaResult
Automatic tail-call optimization for self-recursive Jsonnet functions. Eliminates StackOverflowError on deep recursion without source changes. Draft PR pending benchmark data.