Crate lamina

Crate lamina 

Source
Expand description

§Lamina Compiler Library

Lamina is a modern compiler that generates efficient machine code from a high-level intermediate representation (IR). It supports multiple target architectures and provides a comprehensive set of tools for building compilers, interpreters, and language runtimes.

§Overview

Lamina consists of several key components:

  • IR (Intermediate Representation): A low-level, architecture-agnostic representation of programs that serves as the bridge between high-level source code and machine code.
  • Parser: Converts text-based IR into structured data that can be processed by the compiler.
  • Code Generator: Translates IR into native assembly code for various target architectures.
  • Error Handling: Comprehensive error reporting and recovery mechanisms.

§Quick Start

use lamina::{compile_lamina_ir_to_assembly};
use lamina::target::Target;
use std::io::Write;

// Detect the host architecture
let target = Target::detect_host();
println!("Host target: {}", target);
println!("Architecture: {}", target.architecture);

// Compile IR to assembly
let ir_code = r#"
fn @main() -> i64 {
  entry:
    %result = add.i64 42, 8
    ret.i64 %result
}
"#;

let mut assembly = Vec::new();
compile_lamina_ir_to_assembly(ir_code, &mut assembly)?;
println!("Generated assembly:\n{}", String::from_utf8(assembly)?);

§Architecture Support

Lamina currently supports the following target architectures:

  • x86_64: Intel/AMD 64-bit processors

    • x86_64_unknown - Generic x86_64 (uses ELF conventions for compatibility)
    • x86_64_linux - Linux x86_64
    • x86_64_windows - Windows x86_64
    • x86_64_macos - macOS x86_64 (Intel Macs)
  • AArch64: ARM 64-bit processors

    • aarch64_unknown - Generic AArch64 (uses ELF conventions for compatibility)
    • aarch64_linux - Linux AArch64
    • aarch64_windows - Windows AArch64
    • aarch64_macos - macOS AArch64 (Apple Silicon)

§Core Modules

§IR (Intermediate Representation)

The IR module provides the fundamental data structures for representing programs:

  • Types: Primitive types, structs, arrays, tuples, and function signatures
  • Instructions: Arithmetic, memory operations, control flow, and function calls
  • Functions: Complete function definitions with basic blocks
  • Modules: Top-level containers for functions, types, and globals
  • Builder: Fluent API for programmatically constructing IR

§Code Generation

The codegen module translates IR into native assembly:

  • Architecture-specific backends: Separate implementations for x86_64 and AArch64
  • Register allocation: Efficient use of target architecture registers
  • Instruction selection: Optimal instruction choice for IR operations
  • ABI compliance: Proper calling conventions and stack management

§Error Handling

Comprehensive error reporting with detailed context:

  • Parse errors: Syntax and semantic errors in IR input
  • Codegen errors: Architecture-specific compilation issues
  • Type errors: Type checking and validation failures
  • Memory errors: Stack overflow, invalid memory access, etc.

§Memory Management

Lamina provides sophisticated memory management capabilities:

  • Stack allocation: Fast, automatic memory management for local variables
  • Heap allocation: Manual memory management for persistent data
  • Pointer arithmetic: Safe array and struct field access
  • Memory safety: Bounds checking and validation (where possible)

§Examples

§Basic Arithmetic

use lamina::ir::{IRBuilder, Type, PrimitiveType, BinaryOp};
use lamina::ir::builder::{var, i32, i64};

let mut builder = IRBuilder::new();
builder
    .function("add_numbers", Type::Primitive(PrimitiveType::I32))
    .binary(BinaryOp::Add, "sum", PrimitiveType::I32, i32(10), i32(32))
    .ret(Type::Primitive(PrimitiveType::I32), var("sum"));

§Memory Operations

use lamina::ir::{IRBuilder, Type, PrimitiveType};
use lamina::ir::builder::{var, i32};

let mut builder = IRBuilder::new();
builder
    .function("memory_demo", Type::Void)
    .alloc_stack("local", Type::Primitive(PrimitiveType::I32))
    .store(Type::Primitive(PrimitiveType::I32), var("local"), i32(42))
    .load("value", Type::Primitive(PrimitiveType::I32), var("local"))
    .print(var("value"))
    .ret_void();

§Complete Builder Example: Memory Workflow

use lamina::ir::{IRBuilder, Type, PrimitiveType, BinaryOp};
use lamina::ir::builder::{var, i32};

// Create a new IR builder
let mut builder = IRBuilder::new();

// Define a function that demonstrates memory operations
builder
    .function_with_params("memory_workflow", vec![
        lamina::ir::FunctionParameter {
            name: "input",
            ty: Type::Primitive(PrimitiveType::I32),
            annotations: vec![]
        }
    ], Type::Primitive(PrimitiveType::I32))

    // Step 1: Allocate memory on stack
    .alloc_stack("buffer", Type::Primitive(PrimitiveType::I32))

    // Step 2: Store the input value in our buffer
    .store(Type::Primitive(PrimitiveType::I32), var("buffer"), var("input"))

    // Step 3: Load the value back from memory
    .load("loaded", Type::Primitive(PrimitiveType::I32), var("buffer"))

    // Step 4: Perform arithmetic on the loaded value
    .binary(BinaryOp::Add, "result", PrimitiveType::I32, var("loaded"), i32(10))

    // Step 5: Store the result back to memory
    .store(Type::Primitive(PrimitiveType::I32), var("buffer"), var("result"))

    // Step 6: Load and return the final value
    .load("final", Type::Primitive(PrimitiveType::I32), var("buffer"))
    .ret(Type::Primitive(PrimitiveType::I32), var("final"));

// Build the module
let module = builder.build();

// The module now contains our memory_workflow function
assert!(module.functions.contains_key("memory_workflow"));

§Advanced Builder Example: Control Flow with Memory

use lamina::ir::{IRBuilder, Type, PrimitiveType, BinaryOp, CmpOp};
use lamina::ir::builder::{var, i32, string};

let mut builder = IRBuilder::new();

builder
    .function_with_params("process_data", vec![
        lamina::ir::FunctionParameter {
            name: "data",
            ty: Type::Primitive(PrimitiveType::I32),
            annotations: vec![]
        }
    ], Type::Void)

    // Allocate memory for processing
    .alloc_stack("temp", Type::Primitive(PrimitiveType::I32))
    .store(Type::Primitive(PrimitiveType::I32), var("temp"), var("data"))

    // Check if data is positive
    .cmp(CmpOp::Gt, "is_positive", PrimitiveType::I32, var("data"), i32(0))
    .branch(var("is_positive"), "positive_path", "negative_path")

    // Positive path: double the value
    .block("positive_path")
    .load("current", Type::Primitive(PrimitiveType::I32), var("temp"))
    .binary(BinaryOp::Mul, "doubled", PrimitiveType::I32, var("current"), i32(2))
    .store(Type::Primitive(PrimitiveType::I32), var("temp"), var("doubled"))
    .print(string("Processed positive value"))
    .jump("cleanup")

    // Negative path: take absolute value
    .block("negative_path")
    .load("current", Type::Primitive(PrimitiveType::I32), var("temp"))
    .binary(BinaryOp::Sub, "abs", PrimitiveType::I32, i32(0), var("current"))
    .store(Type::Primitive(PrimitiveType::I32), var("temp"), var("abs"))
    .print(string("Processed negative value"))
    .jump("cleanup")

    // Cleanup: print final result
    .block("cleanup")
    .load("final_result", Type::Primitive(PrimitiveType::I32), var("temp"))
    .print(var("final_result"))
    .ret_void();

let module = builder.build();

§Control Flow

use lamina::ir::{IRBuilder, Type, PrimitiveType, CmpOp};
use lamina::ir::builder::{var, i32};

let mut builder = IRBuilder::new();
builder
    .function("conditional", Type::Primitive(PrimitiveType::I32))
    .cmp(CmpOp::Lt, "is_negative", PrimitiveType::I32, var("x"), i32(0))
    .branch(var("is_negative"), "negative", "positive")
    .block("negative")
    .ret(Type::Primitive(PrimitiveType::I32), i32(-1))
    .block("positive")
    .ret(Type::Primitive(PrimitiveType::I32), i32(1));

§Performance Considerations

  • Zero-copy parsing: IR uses string references to avoid unnecessary allocations
  • Efficient data structures: HashMaps for O(1) lookups of functions and types
  • SSA form: Single Static Assignment form enables powerful optimizations
  • Architecture-specific optimizations: Tailored code generation for each target

§Thread Safety

Lamina’s IR structures are designed to be thread-safe when used correctly:

  • Immutable by default: IR structures are typically built once and then read-only
  • Copy semantics: Most IR types implement Clone for easy duplication
  • Lifetime management: Uses Rust’s lifetime system to ensure memory safety

§Error Recovery

The compiler provides detailed error messages to help with debugging:

  • Source location: Errors include line and column information
  • Context information: Additional details about what went wrong
  • Suggestions: Helpful hints for fixing common issues
  • Multiple errors: Reports all errors found, not just the first one

§Nightly Features

Lamina includes experimental features that are gated behind the nightly feature flag:

  • Atomic Operations: Thread-safe memory operations with memory ordering constraints
  • Module Annotations: Module-level attributes for optimization and debugging hints
  • Experimental Targets: Additional target architectures (e.g., RISC-V 128-bit)

To enable nightly features, compile with:

[dependencies]
lamina = { version = "0.0.7", features = ["nightly"] }

Note: Nightly features are experimental and may change or be removed in future versions.

§Future Roadmap

  • Additional architectures: RISC-V, WebAssembly, and more
  • Optimization passes: Dead code elimination, constant folding, etc.
  • Debug information: Source-level debugging support
  • Standard library: Common functions and data structures
  • Language bindings: C, Python, JavaScript, and other language interfaces

Re-exports§

pub use codegen::generate_x86_64_assembly;
pub use error::LaminaError;
pub use ir::function::BasicBlock;
pub use ir::function::Function;
pub use ir::function::FunctionAnnotation;
pub use ir::function::FunctionParameter;
pub use ir::function::FunctionSignature;
pub use ir::instruction::AllocType;
pub use ir::instruction::BinaryOp;
pub use ir::instruction::CmpOp;
pub use ir::instruction::Instruction;
pub use ir::module::GlobalDeclaration;
pub use ir::module::Module;
pub use ir::module::TypeDeclaration;
pub use ir::types::Identifier;
pub use ir::types::Label;
pub use ir::types::Literal;
pub use ir::types::PrimitiveType;
pub use ir::types::StructField;
pub use ir::types::Type;
pub use ir::types::Value;
pub use mir_codegen::generate_mir_to_target;

Modules§

codegen
Code generation module for multiple target architectures.
error
Error types for the Lamina compiler.
ir
Lamina Intermediate Representation (IR)
mir
mir_codegen
MIR-based code generation for multiple target architectures.
parser
Lamina IR parser.
target

Functions§

compile_lamina_ir_to_assembly
Parses Lamina IR text and generates assembly code using the host system’s architecture.
compile_lamina_ir_to_target_assembly
Parses Lamina IR text and generates assembly code for a specific target architecture.