Skip to main content

Crate lamina

Crate lamina 

Source
Expand description

§Lamina Compiler Library

Lamina is a compiler that generates machine code from a high-level intermediate representation (IR). It supports multiple target architectures.

§Overview

Lamina has four main parts:

  • IR (Intermediate Representation): A low-level, architecture-agnostic representation of programs. It’s the bridge between high-level source code and machine code.
  • Parser: Converts text-based IR into structured data.
  • Code Generator: Translates IR into native assembly code for different architectures.
  • Error Handling: Error reporting and recovery.

§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, 1)?;
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: Uses target architecture registers
  • Instruction selection: Optimal instruction choice for IR operations
  • ABI compliance: Proper calling conventions and stack management

§Error Handling

Error reporting with 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

Memory management:

  • 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};

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(var("doubled"))
    .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(var("abs"))
    .jump("cleanup")

    // Cleanup: print final result (print is debug-only; for portable output use writebyte)
    .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
  • Data structures: HashMaps for O(1) lookups of functions and types
  • SSA form: Single Static Assignment form enables 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 error::LaminaError;
pub use mir_codegen::generate_mir_to_target;
pub use mir_codegen::generate_mir_to_target_with_settings;

Modules§

error
Error types for the Lamina compiler.
ir
mir
MIR (Machine Intermediate Representation) module
mir_codegen
MIR-based code generation for multiple target architectures.
parser
Lamina IR parser.
runtime
Runtime compilation and execution
target
Target detection and platform information

Macros§

impl_backend_boilerplate
Macro to implement common backend methods that delegate to CodegenBase.
impl_codegen_trait_methods
Macro to implement common Codegen trait methods that delegate to CodegenBase.
lamina
Macro for inline Lamina IR code compilation (similar to asm!).

Structs§

BasicBlock
A basic block within a function.
Function
A complete function definition.
FunctionParameter
A function parameter with its name, type, and attributes.
FunctionSignature
The signature of a function (parameters and return type).
GlobalDeclaration
A global variable declaration (e.g., global @message: [5 x i8] = "hello").
Module
StructField
TypeDeclaration
A named type declaration (e.g., type @Vec2 = struct { ... }).

Enums§

AllocType
Memory allocation strategies.
BinaryOp
Binary arithmetic and bitwise operations.
CmpOp
Comparison operations that produce boolean results.
FunctionAnnotation
Function annotations that provide metadata about function behavior.
Instruction
A single instruction in a basic block.
Literal
PrimitiveType
Primitive types in the Lamina IR.
Type
Value

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.

Type Aliases§

Identifier
A type alias for identifiers in the IR.
Label
A type alias for basic block labels in the IR.