Enabling Deep Recursion in C++
Abstract
1. Introduction
2. Background
2.1. Recursion
- fac(n) = 1 · 2 · … · n, for n > 0,
- fac(0) = 1.
| Listing 1. Recursive implementation of the factorial function in C/C++. |
| int factorial( int n ) { if( n == 0 ) return 1; else return factorial( n − 1 ) * n; } |
2.2. Call Stack
2.2.1. Call Stack Implementation
2.2.2. Stack Allocation
2.3. Deep Recursion Limitations
2.3.1. Tail Call Optimization
2.3.2. Dynamic Stack
2.3.3. Split Stack
- The function prologue checks whether the stack contains enough free space for the function. If there is enough space, the function works as a regular one, in the same way as without a split stack. But, if there is not enough space, then a new stack segment is allocated, linked to the previous one, and the SP is updated to point into the new segment.
- The function epilogue checks whether the prologue added a new stack segment, and if so, deallocates this segment and restores the SP to the previous segment.
2.4. Fibers
3. Extendable Stack Library
3.1. The Goals
- Deep recursion should be supported on demand. A major issue with split-stack is its impact on building, as all program parts and libraries must use it. Using stack handling only on demand has no such limitations and limits the additional processing overhead to only the affected code, in accordance with the zero-overhead principle [36].
- The performance penalty should be as low as possible. Dynamic stack manipulation inevitably leads to performance loss, so minimizing the overhead is crucial. Since stack status checks occur far more frequently than manipulations, optimizing their speed greatly affects overall performance.
- ESL should be conceptually cross-platform. A cross-platform solution must rely on OS, CPU ABI, and compiler specifics, but in a way that keeps most of the code universal and platform-specific parts minimal. Support for at least Linux and Windows, with easy portability, is essential.
3.2. The Library Concepts
3.3. ESL API
| Listing 2. The general ESL usage pattern. The stack is handled explicitly, as needed. |
| type functionName( args ) { bool allocated_new_stack = false; if( extendableStack::shouldExtendStack() ) { allocated_new_stack = true; extendableStack::extendStack(); } … function body … if( allocated_new_stack ) extendableStack::condenseStack(); return result; } |
| Listing 3. Delegation usage pattern. The same call is performed recursively with an extended stack. |
| type functionName( args ) { if( extendableStack::shouldExtendStack() ) return extendableStack::runInExtended( functionName, args ); … function body … return result; } |
3.3.1. Usage Methods
- Explicit usage—The basic usage method where ESL API functions are directly used (see sumNM_safe in Listing 4). This method allows the greatest control.
- Guard macros—A pair of macros is used to simplify the ESL usage (see sumNM_macroGuard in Listing 4).
- Block macros—A simple way to handle the case when stack extension is required is to recursively repeat exactly the same call (see sumNM_macroBlock in Listing 4). This adds one more recursive call but simplifies the implementation.
- Duplicate macros—Similar to block macros, this method separates two cases but repeats the same code segment twice instead of using a recursive call (see sumNM_macroDuplicate in Listing 4).
- Delegation—The stack handling and the recursive call are delegated to the runInExtended template function (see sumNM_delegate in Listing 4). The same function with the same arguments can be called in a similar way to the block macro usage method.
| Listing 4. Examples of several methods of using ESL. Recursive functions for calculating the sum of integers between n and m are implemented using various ESL usage methods. Function names are bolded to highlight recursive calls. For more methods, see Listing S1 in Supplementary Materials or [37]. |
| // Without ESL. Deep recursion not supported. long sumNM_native( long n, long m ) { if( n > m ) return 0; return sumNM_native( n + 1, m ) + n; } // Explicit coding. long sumNM_safe( long n, long m ) { if( n > m ) return 0; bool stackHasBeenExtended = false; if( extendableStack::shouldExtendStack() ) { stackHasBeenExtended = true; extendableStack::extendStack(); } long result = n + sumNM_safe( n + 1, m ); if( stackHasBeenExtended ) extendableStack::condenseStack(); return result; } // Using guard macro. long sumNM_macroGuard( long n, long m ) { if( n > m ) return 0; EXTSTACK_GUARD_BEGIN; long result = n + sumNM_macroGuard( n + 1, m ); EXTSTACK_GUARD_END; return result; } // Using block macros. long sumNM_macroBlock( long n, long m ) { if( n > m ) return 0; else EXTSTACK_IF_EXTENDING_RETURN( sumNM_macroBlock( n, m ) ) else return n + sumNM_macroBlock( n + 1, m ); } // Using delegation. long sumNM_delegate( unsigned long long n, unsigned long long m ) { if( n > m ) return 0; else if( extendableStack::shouldExtendStack() ) return extendableStack::runInExtended( sumNM_delegate, n, m ); else return n + sumNM_delegate( n + 1, m ); } |
3.3.2. Initialization
3.4. Some Implementation Details
3.4.1. Stack Block Activation
| Listing 5. Stack extension. The main part of the implementation of the extendStack operation on Linux for AMD64, for GCC and Clang compilers in release builds. |
| size_t copyLen = EstimateStackFramesSize( 2 ); char* stackBottom = LastStackBlock_ ? LastStackBlock_->TopOfUsableBlockSpace_ : (char*) compiler_Linux::getStackBottom(); char* reg_RSP; __asm__ volatile ( "movq %%rsp, %0" : "=r" ( reg_RSP )); // reg_RSP = RSP size_t maxLen = stackBottom - reg_RSP; if( copyLen > maxLen ) copyLen = maxLen; StackBlock* newBlock = AllocateAndLinkNewStackBlock( reg_RSP, 0 ); newBlock->OldTopOfUsableBlockSpace_ = reg_RSP + copyLen; char* new_RSP = newBlock->TopOfUsableBlockSpace_ - copyLen; memcpy( new_RSP-redZoneSize, reg_RSP-redZoneSize, copyLen+redZoneSize ); __asm__ volatile ( "movq %0, %%rsp" : : "r" ( new_RSP )); // RSP = reg_RSP |
3.4.2. Determining the Stack Frame Size
3.4.3. Estimating Stack Limit
3.4.4. Exception Handling
3.4.5. Platform-Specific Elements
3.5. Using Fibers
| Listing 6. Fiber-based implementation. Implementation of the runInFiberWithNewStack function and the runInFiber function template using the Boost.Context library. The runInFiber function assumes that the given function fn returns a result. The version for void functions fn is a little simpler. |
| void runInFiberWithNewStack( const std::function<void()>& fn ) { Fibers::StackAllocator stack_allocator; boost::context::fiber f( std::allocator_arg, stack_allocator, [&stack_allocator,&fn]( boost::context::fiber&& caller ) { Fibers::ContextData fcd; Fibers::InitStackLimits( fcd, stack_allocator ); fn(); Fibers::DeinitStackLimits( fcd ); return std::move( caller ); }); f = std::move( f ).resume(); } template<typename FnType, typename… Args> auto runInFiber( FnType&& fn, Args… args ) { decltype( std::forward<FnType>( fn )( args… ) ) result; runInFiberWithNewStack( [&]() { result = std::forward<FnType>( fn )( args… ); }); return result; } |
4. Usability Assessment
4.1. Validation
4.2. Performance Analysis
4.2.1. Low-Level Benchmarks
4.2.2. Recursive C++ Benchmarks
4.2.3. Wafl Benchmarks
5. Results
5.1. Identified Limitations and Constraints
5.1.1. Lost Updates
5.1.2. Incomplete Copies
5.1.3. Undesirable Optimization
5.1.4. Thrashing
5.1.5. Inaccurate Estimates
5.1.6. Non-Leaf Recursive Functions
5.1.7. Class-Type Arguments and Results
5.1.8. Stack Size Limit
5.2. Performance Benchmarks
5.2.1. Low Level Benchmarks
5.2.2. Recursive Function Benchmarks
5.2.3. Wafl Benchmarks
6. Discussion
6.1. Constraints
- Never define a variable before stack extension, modify it between extension and condensation, and then access it again (e.g., return it) after condensation. Doing so can cause incorrect results even for primitive types.
- If the result must be of a class type, then create it dynamically and return it using a raw pointer. Never return a smart pointer; it is of the class type and is prone to the same problems.
- If a try-catch block is used,
- It must contain both extendStack and condenseStack or neither;
- It is best to end the try-block with a return statement.
6.2. Performance
6.2.1. ESL Performance Cost Estimation
6.2.2. Performance Implications of Stack Size
6.2.3. Performance Implications of Stack Block Size
6.2.4. Comparing Performance of Different Usage Methods
6.3. Future Work
6.4. Alternatives
6.5. Summary
7. Conclusions
Supplementary Materials
Author Contributions
Funding
Data Availability Statement
Conflicts of Interest
Abbreviations
| ABI | Application binary interface |
| API | Application programming interface |
| CPU | Central processing unit |
| ESL | Extendable Stack Library |
| GCC | GNU C++ compiler |
| MSVC | Microsoft Visual C++ compiler |
| SP | Stack pointer |
| TCO | Tail call optimization |
References
- Graham, R.L.; Knuth, D.E.; Patashnik, O. Concrete Mathematics; Addison-Wesley: Boston, MA, USA, 1988; pp. 1–20. ISBN 0-201-55802-5. [Google Scholar]
- Knuth, D.E. The Art of Computer Programming, Volume 1: Fundamental Algorithms, 3rd ed.; Addison-Wesley: Boston, MA, USA, 1997. [Google Scholar]
- Sedgewick, R.; Wayne, K. Algorithms, 4th ed.; Addison-Wesley: Boston, MA, USA, 2011; ISBN 978-0-321-57351-3. [Google Scholar]
- Drozdek, A. Data Structures and Algorithms in C++, 4th ed.; Cengage Learning: Boston, MA, USA, 2012; ISBN 9781285415017. [Google Scholar]
- Weiss, M.A. Data Structures and Algorithm Analysis in C++, 4th ed.; Pearson Education: Upper Saddle River, NJ, USA, 2013; ISBN 978-0132847377. [Google Scholar]
- Patterson, D.A.; Hennessy, J.L. Computer Organization and Design: The Hardware/Software Interface, 4th ed.; Morgan Kaufmann: San Francisco, CA, USA, 2011; ISBN 978-0123747501. [Google Scholar]
- Wafl Programming Language. Available online: https://poincare.matf.bg.ac.rs/~sasa.malkov/wafl/index.html (accessed on 2 June 2025).
- Malkov, S. Customizing a Functional Programming Language for Web Development. Comput. Lang. Syst. Struct. 2010, 36, 345–351. [Google Scholar] [CrossRef]
- Grune, D.; Reeuwijk, K.; Bal, H.E.; Jacobs, C.J.H.; Langendoen, K. Modern Compiler Design, 2nd ed.; Springer: Berlin/Heidelberg, Germany, 2012; ISBN 978-1461446989. [Google Scholar]
- Levine, J.R. Linkers and Loaders; Morgan Kaufmann: San Francisco, CA, USA, 1999; ISBN 978-1558604964. [Google Scholar]
- O’Regan, G. Mathematical Induction and Recursion. In Guide to Discrete Mathematics; Springer: Cham, Switzerland, 2016. [Google Scholar] [CrossRef]
- McCauley, R.; Grissom, S.; Fitzgerald, S.; Murphy, L. Teaching and learning recursive programming: A review of the research literature. Comput. Sci. Educ. 2015, 25, 37–66. [Google Scholar] [CrossRef]
- Rubio-Sanchez, M. Introduction to Recursive Programming; CRC Press: Boca Raton, FL, USA, 2018. [Google Scholar]
- Liu, Y.A.; Stoller, S.D. From recursion to iteration: What are the optimizations? SIGPLAN Not. 1999, 34, 73–82. [Google Scholar] [CrossRef]
- Samelson, K.; Bauer, F.L. Sequential formula translation. Comm. ACM 1960, 3, 76–83. [Google Scholar] [CrossRef]
- Dijkstra, E.W. Recursive Programming. Num. Mathematik 1960, 2, 312–318. [Google Scholar] [CrossRef]
- Daylight, E.G. Dijkstra’s rallying cry for generalization: The advent of the recursive procedure, late 1950s–early 1960s. Comput. J. 2011, 54, 1756–1772. [Google Scholar] [CrossRef]
- Aho, A.V.; Lam, M.S.; Sethi, R.; Ullman, J.D. Compilers: Principles, Techniques, and Tools, 2nd ed.; Pearson Education: Upper Saddle River, NJ, USA, 2006; pp. 427–503. [Google Scholar]
- Bryant, R.E.; O’Hallaron, D.R. Computer Systems: A Programmer’s Perspective, 3rd ed.; Pearson Education: Upper Saddle River, NJ, USA, 2016; ISBN 978-1292101767. [Google Scholar]
- Overview of x64 ABI Conventions. In Microsoft Learn. Available online: https://learn.microsoft.com/en-us/cpp/build/x64-software-conventions (accessed on 6 February 2025).
- System V Application Binary Interface, AMD64 Architecture Processor Supplement. Available online: https://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf (accessed on 6 February 2025).
- C++ ABI for the Arm® 64-bit Architecture (AArch64). In Application Binary Interface for the Arm® Architecture. Available online: https://github.com/ARM-software/abi-aa/tree/main/cppabi64 (accessed on 6 February 2025).
- Love, R. Linux System Programming, 2nd ed.; O’Reilly Media: Sebastopol, CA, USA, 2013; ISBN 978-1449339531. [Google Scholar]
- Yosifovich, P.; Ionescu, A.; Russinovich, M.E.; Solomon, D.A. Windows Internals, Part 1: System Architecture, Processes, Threads, Memory Management, and More, 7th ed.; Microsoft Press: Redmond, WA, USA, 2017; ISBN 978-0735684188. [Google Scholar]
- Denning, P.J. Virtual memory. ACM Comput. Surv. 1970, 2, 153–189. [Google Scholar] [CrossRef]
- Memory Limits for Windows and Windows Server Releases. In Microsoft Learn. Available online: https://learn.microsoft.com/en-us/windows/win32/memory/memory-limits-for-windows-releases (accessed on 6 February 2025).
- MISRA C++:2008: Guidelines for the Use of C++ Language in Critical Systems; MIRA Limited: Nuneaton, UK, 2008; p. 110. ISBN 978-1906400040.
- Options That Control Optimization. In A GNU Manual. Available online: https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html (accessed on 2 February 2025).
- Appel, A.W. Compiling with Continuations; Cambridge University Press: Cambridge, UK, 1992. [Google Scholar]
- Taylor, I.L. Split Stacks in GCC. In GCC Wiki. Available online: https://gcc.gnu.org/wiki/SplitStacks (accessed on 2 February 2025).
- Anderson, B. Abandoning Segmented Stacks in Rust. 2013. Available online: https://web.archive.org/web/20230605154530/https://mail.mozilla.org/pipermail/rust-dev/2013-November/006314.html (accessed on 6 May 2023).
- Ma, Z.; Zhong, L. Bringing Segmented Stacks to Embedded Systems. In Proceedings of the 24th International Workshop on Mobile Computing Systems and Applications, HotMobile 2023, Newport Beach, CA, USA, 22–23 February 2023; ACM: New York, NY, USA, 2023; pp. 117–123. [Google Scholar] [CrossRef]
- GNU Binutils 2.44 Released. Info-Gnu Discussion. Available online: https://lists.gnu.org/archive/html/info-gnu/2025-02/msg00001.html (accessed on 6 February 2025).
- Boost C++ Libraries. Boost. Context Library. Version 1.88. Available online: https://www.boost.org/libs/context/ (accessed on 6 February 2025).
- Kowalke, O.; Goodspeed, N. Fiber_Context—Fibers Without Scheduler, C++ Standard Proposal P0876R20. Available online: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p0876r20.pdf (accessed on 6 February 2025).
- Stroustrup, B. C++ Exceptions and Alternatives. In JTC1/SC22/WG21—Papers. 2019. Available online: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1947r0.pdf (accessed on 23 June 2025).
- Extendable Stack Library. Available online: https://gitlab.com/smalkov_/libextstack (accessed on 12 January 2025).
- Farvardin, K.; Reppy, J. From folklore to fact: Comparing implementations of stacks and continuations. In Proceedings of the PLDI 2020: Proceedings of the 41st ACM SIGPLAN Conference on Programming Language Design and Implementation, London, UK, 15–20 June 2020; ACM: New York, NY, USA, 2020; pp. 75–90. [Google Scholar] [CrossRef]
- Theodoridis, T.; Su, Z. Refined Input, Degraded Output: The Counterintuitive World of Compiler Behavior. In Proceedings of the ACM on Programming Languages, Copenhagen, Denmark, 24–28 June 2024; pp. 671–691. [Google Scholar] [CrossRef]
- Otsuki, Y.; Kawakoya, Y.; Iwamura, M.; Miyoshi, J.; Ohkubo, K. Building stack traces from memory dump of windows x64. Digit. Investig. 2018, 24, S101–S110. [Google Scholar] [CrossRef]





| Benchmark | Arguments | Iterations | Recursion Depth | Total # of Rec. Calls | Total # of Stack Extensions | Max. Depth of Stack Extensions |
|---|---|---|---|---|---|---|
| Tower of Hanoi Count | 30 | 1 | 30 | 1074 M | - | - |
| Tower of Hanoi | 27 | 1 | 27 | 134 M | - | - |
| Fibonacci | 40 | 1 | 40 | 331 M | - | - |
| Catalan | 18 | 1 | 18 | 387 M | - | - |
| Count | 100,000,000 | 1 | 100 M | 100 M | 4586 | 4586 |
| Sum | 100,000,000 | 1 | 100 M | 100 M | 4586 | 4586 |
| Ackermann | A(3, 13) | 1 | 65,535 | 2863 M | 54,786 | 3 |
| Binary Search | int[100,000,000] | 12.5 M | ≤27 | 320 M | - | - |
| Permutations | int[11] | 8 | 11 | 320 M | - | - |
| Sort | int[1,000,000] | 20 | 20 | 40 M | - | - |
| Linux | Windows | ||||||||
|---|---|---|---|---|---|---|---|---|---|
| Bench. | # of Rec. Calls (M) | Native Time (s) | Est. Chk. Cost (s) | Total Cost (s) | Slower Methods | Native Time (s) | Est. Chk. Cost (s) | Total Cost (s) | Slower Methods |
| HAN-C | 1074 | 0.21 | 0.29 | 0.77–0.86 | All (8) | 1.04 | 0.32 | −0.028–0.001 | / |
| HAN-M | 134 | 0.81 | 0.036 | 0.11–0.14 | All (8) | 0.54 | 0.04 | 0.01–0.11 | Safe, M-Grd (2) |
| FIB | 331 | 0.17 | 0.09 | 0.03–0.29 | Safe, M-Grd, M-Blk, M-Dup (4) | 0.51 | 0.1 | 0.03–0.11 | / |
| CAT | 387 | 0.19 | 0.1 | 0.004–0.089 | / | 0.62 | 0.12 | 0–0.25 | Del, Fibers, Del-Exc, Fib-Exc (4) |
| BSRCH | 320 | 1 | 0.087 | −0.026–0.065 | / | 1.02 | 0.096 | −0.02–0.35 | Safe, M-Grd (2) |
| PERM | 320 | 1.07 | 0.087 | 0.024–0.083 | / | 1.04 | 0.096 | −0.01–0.05 | / |
| SORT | 40 | 1.37 | 0.011 | 0.027–0.040 | All (8) | 1.35 | 0.012 | −0.004–0.04 | Safe, M-Grd, M-Blk (3) |
| Linux | Windows | |||||||
|---|---|---|---|---|---|---|---|---|
| Benchmark | 64 K #Ext | 64 K Ext.cost (ns) | 1 M #Ext | 1 M Ext.cost (ns) | 64 K #Ext | 64 K Ext.cost (ns) | 1 M #Ext | 1 M Ext.cost (ns) |
| Safe | 2,161,716 | 239.6 | 129,461 | 733.9 | 1,079,415 | 294.7 | 57,382 | 415.1 |
| M-Grd | 2,161,716 | 233.7 | 105,219 | 313.8 | 1,079,415 | 328.1 | 57,382 | 497.7 |
| M-Blk | 719,865 | 244.9 | 57,385 | 283 | 1,080,179 | 348.8 | 57,380 | 890.9 |
| M-Dup | 1,080,257 | 247.6 | 57,382 | 327.5 | 1,079,363 | 345.9 | 57,377 | 990.3 |
| Del | 358,882 | 250.1 | 34,432 | 162.5 | 1,080,179 | 428.6 | 57,380 | 1051.9 |
| Fibers | 350,290 | 253.2 | 34,383 | 383.9 | 1,054,998 | 277.5 | 57,281 | 723.8 |
| Del-Exc | 358,882 | 274.7 | 34,432 | 357.6 | 1,080,179 | 433.2 | 57,380 | 1116.8 |
| Fib-Exc | 350,287 | 247 | 34,382 | 348.9 | 1,054,998 | 290.6 | 57,281 | 794 |
| AVG | 248.9 | 363.9 | 343.4 | 810.1 |
Disclaimer/Publisher’s Note: The statements, opinions and data contained in all publications are solely those of the individual author(s) and contributor(s) and not of MDPI and/or the editor(s). MDPI and/or the editor(s) disclaim responsibility for any injury to people or property resulting from any ideas, methods, instructions or products referred to in the content. |
© 2026 by the authors. Licensee MDPI, Basel, Switzerland. This article is an open access article distributed under the terms and conditions of the Creative Commons Attribution (CC BY) license.
Share and Cite
Malkov, S.N.; Čukić, I.L.; Đorđević, P.Ž. Enabling Deep Recursion in C++. Computers 2026, 15, 15. https://doi.org/10.3390/computers15010015
Malkov SN, Čukić IL, Đorđević PŽ. Enabling Deep Recursion in C++. Computers. 2026; 15(1):15. https://doi.org/10.3390/computers15010015
Chicago/Turabian StyleMalkov, Saša N., Ivan Lj. Čukić, and Petar Ž. Đorđević. 2026. "Enabling Deep Recursion in C++" Computers 15, no. 1: 15. https://doi.org/10.3390/computers15010015
APA StyleMalkov, S. N., Čukić, I. L., & Đorđević, P. Ž. (2026). Enabling Deep Recursion in C++. Computers, 15(1), 15. https://doi.org/10.3390/computers15010015

