Project 5.04 Section 5 ⚡ Embedded Relevance: Critical Recursion Stack Overflow constexpr Tail Call Optimization RTOS

5.04 Recursive Stack Frame Growth, Call Depth Hazards & constexpr Compile-Time Evaluation

Executive Summary: Exploring recursive algorithms vs iterative implementations. We demonstrate why unbounded recursion is banned in embedded standards (MISRA / NASA) due to small RTOS task stack budgets (e.g. 512 bytes), analyze Tail-Call Optimization (TCO), and evaluate math at compile-time using constexpr.

💻 1. Annotated Source Code

#include <iostream>
using namespace std;

int factorial(int num);

int main() {

	int result = factorial(6);
	cout << "The factorial(6) is " << result << endl;

	return 0;
}

int factorial(int num) {
	if (num > 1) {
		return num * factorial(num - 1);
	}
	return 1;
}

📐 2. Architecture & UML Class Model

📐 Recursive Factorial Stack Frames & Tail-Call Optimization
+ Public - Private # Protected
<<compilation-unit>> FactorialEngine Recursion Pipeline
-maxDepth : constexpr uint32_t = 32
+factorial(n: uint32_t) : uint64_t
+factorialTail(n: uint32_t, acc: uint64_t = 1) : uint64_t[Tail-Call Optimized to B]

📚 3. Core C++ Concepts Deep-Dive

1. Recursive Call Stack Mechanics

Each recursive function call creates a new stack frame storing local variables, parameters, and the return address. A recursion depth of $N$ consumes $O(N)$ stack memory.

2. Iterative & Tail-Call Alternatives

Iterative loops require $O(1)$ stack space. When the recursive call is the absolute last operation (tail recursion), optimizing compilers can reuse the existing stack frame (Tail-Call Optimization - TCO).

⚡ 4. Embedded Systems & Hardware Reality

1. Why Recursion is Banned in Embedded Systems

In embedded systems and RTOS tasks, stack sizes are statically allocated and very small (e.g. 512 to 2048 bytes). Unbounded or deep recursion quickly exceeds the stack limit, silently clobbering adjacent RAM and causing catastrophic system crashes.

🚫 MISRA C++:2008 Rule 7-5-4 & NASA C Safety Rule #3

Functions shall not call themselves, either directly or indirectly. Execution bounds and stack depth must be deterministically provable.

2. Compile-Time constexpr Evaluation

Modern C++ allows computing mathematical constants at compile time, consuming 0 clock cycles and 0 stack frames at runtime.

💡 5. Production-Ready Embedded Refactoring

Compile-time constexpr factorial calculation:

💡 Production-Ready Refactor
#include <cstdint>

// Evaluated 100% at compile-time; 0 runtime stack usage!
constexpr uint32_t factorial(uint32_t n) noexcept {
    uint32_t result = 1;
    for (uint32_t i = 2; i <= n; ++i) {
        result *= i;
    }
    return result;
}

// Stored as an immediate constant in Flash ROM
constexpr uint32_t FACT_6 = factorial(6); // Emits MOV R0, #720

📝 Knowledge Verification Quiz

Test your understanding of the C++ concepts and embedded microcontroller trade-offs covered in this guide. Click any option for instant feedback.

Q1. Why is recursion strictly prohibited by safety standards like MISRA C++ and NASA Power of 10?
A It creates variable-depth stack growth that risks catastrophic stack overflow in memory-constrained microcontrollers
B Recursion requires an operating system kernel
C Recursive functions cannot access global variables
D Recursion causes hardware clock jitter
Detailed Explanation: Recursion makes maximum stack depth difficult to prove statically, posing severe risks of stack overflow crashes on microcontrollers with small fixed stacks.
Q2. What is 'Tail Call Optimization' (TCO)?
A A compiler optimization where a recursive call at the end of a function reuses the current stack frame instead of allocating a new one
B A method for encrypting function returns
C A tool for debugging stack frames
D An algorithm that reverses array elements
Detailed Explanation: TCO converts a tail-recursive function into a jump loop in assembly, executing in $O(1)$ stack space without growing the call stack.
Q3. What is the runtime execution cost of a constexpr function evaluated with constant arguments at compile time?
A 0 clock cycles and 0 stack frames at runtime; the precomputed value is embedded as an immediate constant
B 1 clock cycle per recursive step
C 50 clock cycles
D The same cost as runtime recursion
Detailed Explanation: constexpr functions with constant arguments are computed by the compiler during compilation, embedding results directly into the binary.
Q4. In a FreeRTOS task with a 512-byte stack, what happens if recursion depth exceeds available memory?
A A stack overflow occurs, corrupting task control blocks (TCBs) and triggering a fatal crash or vApplicationStackOverflowHook()
B The RTOS automatically doubles the stack size
C The recursive calls are redirected to flash
D The CPU ignores further function calls
Detailed Explanation: Exceeding task stack bounds overflows into adjacent memory, corrupting RTOS task data structures and crashing the system.