The Fidelity Framework: A Primer

A Unified Approach to F# Native Performance from Embedded Devices to Cloud Servers
work-single-image

The computing world has fragmented into specialized ecosystems - embedded systems demand byte-level control, mobile platforms enforce strict resource constraints, while server applications require elasticity and parallelism. Traditionally, these environments have forced developers to choose between conflicting approaches: use a high-level language with garbage collection and accept the performance overhead, or drop down to systems programming with manual memory management and lose expressiveness.

Beyond Runtime Boundaries

The Fidelity Framework represents a fundamental rethinking of this dichotomy. Built around the functional-first language F#, it creates a compilation pipeline that generates truly native code across the entire computing spectrum while maintaining strong correctness guarantees. By leveraging a direct path from F# Compiler Services (FCS) to MLIR, Fidelity adapts its implementation strategy to each target platform while preserving a consistent programming model and the rich type information that makes F# so powerful.

Core Architecture: Type-Preserving Compilation Without Compromise

At its heart, Fidelity consists of a direct compilation pathway from F# source code to native executables through the MLIR (Multi-Level Intermediate Representation) and LLVM ecosystem. This approach shares philosophical similarities with Rust’s compilation model, but with a focus on functional programming paradigms and stronger type-based guarantees, all while preserving F#’s rich type system throughout the compilation process.

%%{init: {'theme': 'neutral'} }%% flowchart TB FSharpSource["F# Source Code"] --> FCS["F# Compiler Service"] FCS --> TypeExtract["Type-Preserving Extraction"] TypeExtract --> DepAnalysis["Dependency Analysis"] DepAnalysis --> MemLayout["Memory Layout Analysis"] DepAnalysis --> ReachAnalysis["Reachability Analysis"] MemLayout --> MLIRGen["Direct MLIR Generation"] ReachAnalysis --> MLIRGen TypeExtract -.->|Preserves type info| MLIRGen MLIRGen --> MLIR["Type-Annotated MLIR"] MLIR --> Optimization["MLIR Optimization Passes"] Optimization --> LLVMIR["LLVM IR"] LLVMIR --> NativeCode["Native Executable"]

Direct FCS Integration: Preserving What Matters

Unlike traditional compiler architectures that create intermediate representations, Fidelity works directly with the F# Compiler Services AST. The key innovations are:

  1. Type-Preserving Pipeline: The compilation process maintains complete F# type information from source through to MLIR generation, enabling precise memory layout calculations and type-directed optimizations.

  2. Zero-Allocation Transformation: Direct analysis of FCS expressions allows Fidelity to transform heap allocations into stack allocations, convert closures to explicit parameters, and map higher-order functions to efficient function pointers.

  3. Intelligent Dialect Selection: Type information drives the selection of appropriate MLIR dialects - numeric operations map to the arith dialect, memory operations to memref, and control flow to either scf or cf based on structure.

For Rust developers, this provides the control of manual memory management with the expressiveness of functional programming. For Python developers, imagine if your code could be transformed to run with zero heap allocations while maintaining Python’s clarity.

The Fidelity Type System: Correctness by Construction

The type system is where Fidelity truly distinguishes itself. By preserving F#’s rich type information throughout compilation, Fidelity extends the language’s capabilities with:

Static Dimensions via Type-Level Programming

Similar to how Rust encodes constraints in its type system, Fidelity uses F#’s unit of measure system to encode dimensions and constraints at the type level, with these constraints preserved through to native code:

// A vector with statically known dimension
type Vector<'T, [<Measure>] 'Dim>

// Matrix with statically known dimensions
type Matrix<'T, [<Measure>] 'Rows, [<Measure>] 'Cols>

// Range-constrained integer
type RangeInt<[<Measure>] 'Min, [<Measure>] 'Max>

The memory layout analyzer calculates precise layouts for these types, ensuring efficient memory access patterns in the generated code. For Python developers coming from NumPy, this means shape errors are caught at compile-time with zero runtime overhead.

Advanced Memory Layout Analysis

The restructured Firefly compiler provides unprecedented control over memory layout through direct FCS analysis:

// Compiler automatically determines optimal layout
type AlignedBuffer<'T> with
    // Layout calculated at compile time from type structure
    static member AllocateAligned(size: int)

// BAREWire protocol with compiler-calculated layouts
type BareBuffer<'T> with
    // Zero-copy serialization based on type analysis
    static member Serialize() : byte[]
    static member Deserialize(bytes: byte[]) : BareBuffer<'T>

The enhanced Dabbit.UnionLayouts.FixedLayoutCompiler works directly with FCS types to calculate alignment requirements, padding, and optimal memory structures for all user-defined types.

Memory Management: Static Analysis for Dynamic Adaptation

The restructured architecture enables sophisticated memory management through compile-time analysis:

Stack Frame Analysis

Firefly’s new static analyzer calculates maximum stack usage for every function, enabling:

  • Compile-time verification of stack bounds for embedded targets
  • Automatic transformation of heap allocations to stack allocations
  • Stack usage visualization for debugging and optimization

Direct Reachability Analysis

Working directly with the FCS AST, Firefly performs precise reachability analysis:

  1. Type-Aware Dead Code Elimination: Eliminates unused type definitions, specializations, and unreachable pattern match cases
  2. Cross-Module Optimization: Tracks dependencies across module boundaries for whole-program optimization
  3. Diagnostic Generation: Produces detailed reports about eliminated code and optimization decisions

For developers, this means smaller binaries with only the code actually needed, determined through precise static analysis.

The Compilation Pipeline: From Source to Silicon

The restructured Firefly compiler implements a streamlined pipeline:

Phase 1: FCS Processing and Analysis

The compiler extracts rich type information directly from FCS, preserving:

  • Complete type definitions with constraints
  • Function signatures with generic parameters
  • Namespace hierarchies and module dependencies

Phase 2: Memory Layout Computation

The MemoryLayoutAnalyzer computes precise layouts for all types:

// Automatic layout calculation for discriminated unions
type Shape =
    | Circle of radius: float
    | Rectangle of width: float * height: float
    | Triangle of base': float * height: float

// Compiler determines: 16-byte layout with 8-byte tag
// All variants fit in the same memory footprint

Phase 3: Direct MLIR Generation

Type information drives MLIR generation with:

  • Appropriate dialect selection based on operation types
  • Preserved type annotations for optimization
  • Platform-specific lowering strategies

Phase 4: Optimization and Code Generation

Custom MLIR passes leverage type information for:

  • Memory access pattern optimization
  • Target-specific code generation
  • Link-time optimization across modules

Developer Experience: Understanding Your Code

The restructured architecture provides unprecedented visibility into the compilation process:

Enhanced Diagnostic Formats

Firefly generates multiple intermediate formats for debugging and analysis:

  1. .fcs.pruned: Shows the simplified FCS after dead code elimination
  2. .fcs.ra: Visualizes reachability analysis results
  3. .mlir.annotated: MLIR with type annotations and source mappings
  4. Memory layout visualizations: Shows exact memory structure for types

IDE Integration

The new architecture enables rich IDE support:

  • Hover information showing memory layouts
  • Compile-time stack usage warnings
  • Type-directed code completion
  • Navigation through compilation stages

Real-World Implementation Strategy

The Firefly restructuring follows a phased approach:

First Milestone: Core Pipeline (1-2 months)

  • Direct FCS processing with dependency resolution
  • Basic MLIR generation for simple functions
  • Hello World compilation with zero allocations

Second Milestone: Type System (3-6 months)

  • Comprehensive type mapping to MLIR
  • Memory layout analysis for all F# types
  • Support for discriminated unions and records

Third Milestone: Optimization (6+ months)

  • Full reachability analysis and tree shaking
  • Platform-specific optimization passes
  • Cross-module optimization support

Platform Configuration Through Type-Directed Compilation

The type-preserving pipeline enables sophisticated platform adaptation:

// Platform configuration drives compilation strategy
let embeddedConfig =
    PlatformConfig.compose
        [withPlatform PlatformType.Embedded;
         withMemoryModel MemoryModelType.Constrained;
         withStackLimit (Some 8192);  // 8KB stack limit
         withOptimizationGoal OptimizationGoalType.MinimizeSize]
        PlatformConfig.base'

// Compiler uses configuration to:
// - Verify all functions fit within stack limit
// - Select appropriate MLIR lowering strategies
// - Generate size-optimized code

The Olivier Actor Model: Type-Safe Concurrency

With the enhanced type system, Olivier provides stronger guarantees:

  • Process isolation verified at compile time
  • Message types checked across actor boundaries
  • Zero-copy message passing where type analysis permits
  • Static verification of supervision hierarchies

For Erlang developers, this brings compile-time verification to the actor model. For Rust developers, it provides actor concurrency with type safety guarantees.

Verification: Types as Proofs

The preserved type information enables deeper F* integration:

Incremental Verification

  1. Standard F# code with rich types
  2. Gradual addition of refinement types
  3. Formal proofs about critical sections
  4. Verification preserved through MLIR generation

The type-preserving pipeline ensures verification guarantees aren’t lost during compilation.

Conclusion: A New Era of Native F# Compilation

The Fidelity Framework’s restructured architecture represents a fundamental advance in functional language compilation. By preserving F#’s rich type system throughout the compilation pipeline, Firefly enables:

  • Zero-allocation transformations guided by type analysis
  • Precise memory layout calculation from type definitions
  • Compile-time verification of resource constraints
  • Type-directed optimization strategies

This isn’t just about making F# run natively; it’s about demonstrating that functional languages can match and exceed the performance of systems programming languages while maintaining their expressiveness and safety guarantees.

For developers from any ecosystem, Fidelity offers a glimpse of what’s possible when we leverage type information not just for safety, but as the foundation for a entirely new class of compiler optimizations. The future of systems programming lies not in choosing between safety and performance, but in using one to achieve the other.

Author
Houston Haynes
date
January 3, 2025
category
Systems

We want to hear from you!

Contact Us