VM Internals

Why Lambda C Is Safe for Embedded Systems

This page provides a deep technical analysis of Lambda C's VM architecture, explaining why it is uniquely suited for safety-critical embedded systems.


Non-Recursive Dispatch Loop

The Core Safety Guarantee

Critical Understanding: Lambda C's VM does not use C-level function recursion for script function calls. This is the fundamental difference from naive interpreters.

How Script Calls Work

When a script function calls another function (including recursive calls), the VM:

  1. Pushes frame information onto the VM stack - not the C stack
  2. Updates the program counter to jump to the target function
  3. Continues the dispatch loop - never calls another C function

This means:

Contrast with Traditional Interpreters

Naive Interpreter (Unsafe):

Lambda C (Safe):

Commercial Significance

This architecture enables:


Load-Time Linking: O(1) FFI Dispatch

The Problem with Runtime Symbol Resolution

Traditional scripting languages (Lua, Python) resolve function names every time they are called:

This creates variable, unpredictable FFI call overhead.

Lambda C's Solution

FFI linking happens once at bytecode load time:

  1. Registration phase: Host registers functions with string names
  2. Link phase: Bytecode loader converts all function name references to integer IDs
  3. Execution phase: FFI calls use integer ID → direct table lookup

Result: O(1) constant-time FFI dispatch (~100ns) regardless of:

Why This Matters

In control loops executing at 100Hz-10kHz:


Type Stack: Why Runtime Type Tracking?

The Compact Bytecode Trade-off

Lambda C's 4-byte instruction format achieves extreme compactness by not encoding type information in instructions. An ADD instruction doesn't specify if it's adding int or double.

The Type Stack Solution

The VM maintains a parallel type stack that tracks the runtime type of each value on the VM stack:

Key Insight: This is a space-time trade-off optimized for embedded:

Performance Optimization: Fast Integer Mode

When the VM detects integer-only operations, it can:

This is why fib(25) runs fast despite being an interpreter - the hot loop is all integers.

Static Type Mode (-Os)

Compile with the -Os option to completely omit type information from bytecode, achieving ~2x speedup:

lcvmc -Os -oc output.lcbc source.c

How It Works:

Benchmark (fib(25)):

ModeExecution Time
Normal mode27ms
Static type mode13ms

Limitations:


Arena Allocator: No free() by Design

Why No free()?

Traditional malloc/free causes:

Arena Allocator Characteristics

Lambda C's allocator:

Embedded Use Case Fit

This design is perfect for periodic control loops:

Control loop iteration:
1. Reset heap → Start with fresh memory
2. Allocate temporary buffers → O(1) fast
3. Process sensor data → No fragmentation risk
4. Output control signals → Predictable memory
5. Next iteration → Heap reset, repeat

Trade-off: Must call lcvm_heap_reset() periodically. This is acceptable because:

Heap Watermark API

For scenarios where full heap reset is too coarse, Lambda C provides partial release capability:

size_t mark = lcvm_heap_mark();     // Record current position
char *buf = lcvm_malloc(64*1024);   // Temporary allocation
// ... processing ...
lcvm_heap_release(mark);            // Batch release to mark position

Characteristics:

Use Cases:


4-Byte Instructions: Cache Efficiency

Why Fixed-Length Matters

Variable-length instruction sets (x86, Lua bytecode):

Fixed 4-byte instructions:

Compact Yet Complete

4 bytes provide:

This is sufficient for:

Design principle: Instruction density > raw decode speed for embedded systems where ROM/RAM are scarce but CPU cycles are adequate.


Deterministic Memory Consumption

The Safety-Critical Requirement

In commercial embedded systems, you must prove worst-case memory consumption. Lambda C enables this:

Total Memory = vm_stack_size + vm_garea_size + vm_heap_size

Proof of Bounds

  1. VM Stack: Maximum depth determined by STACKSIZE - stack overflow detection prevents exceeding this
  2. Global Area: Fixed at load time based on bytecode global variable declarations
  3. Heap: Arena allocator bounded by heap_size - allocation fails cleanly when exhausted

No hidden allocations:

Certification Advantage

This determinism is valuable for:


Comparison: Why Lambda C Differs from Lua/Python

Architectural Philosophy

AspectLambda CLuaMicroPython
C stack usageConstantGrows with call depthGrows with call depth
FFI dispatchO(1) table lookupO(1) hash table (after cache)String comparison
Memory allocationArena (no free)GC with mark-sweepGC with mark-sweep
Instruction format4-byte fixedVariable (1-4 bytes)Variable (1-3 bytes)
Type trackingType stackPer-value tagsPyObject headers

Design for Embedded

Lambda C makes trade-offs optimized for determinism and safety:

This is why it's suitable for safety-critical embedded systems where Lua/Python are not.


Summary: Technical Evaluation

S-Grade: Architecture

Non-recursive VM loop: Complete separation of script call stack from C stack. Safe for RTOS tasks with minimal stack allocation.

Load-time linking: O(1) FFI dispatch eliminates runtime symbol resolution overhead. Enables real-time control applications.

S-Grade: Memory Efficiency

4-byte instruction format: Achieves extreme ROM efficiency while maintaining cache-friendly fixed-length design.

Arena allocator: Zero fragmentation, O(1) allocation, deterministic memory consumption. Ideal for periodic control loops.

S-Grade: Commercial Suitability

Deterministic memory bounds: Total memory consumption provable at compile time. Enables safety certification.

AOT compilation: Parse complexity isolated to development environment. Runtime is simple, bounded, and verifiable.


Next Steps