Skip to main content

lamina_ir/
types.rs

1//! # Type System
2//!
3//! Complete type system for the Lamina IR, including
4//! primitive types, composite types, and value representations.
5//!
6//! ## Type Categories
7//!
8//! ### Primitive Types
9//! - **Integers**: Signed and unsigned integers of various sizes
10//! - **Floats**: Single and double precision floating point numbers
11//! - **Booleans**: True/false values
12//! - **Characters**: Single character values
13//! - **Pointers**: Generic pointer type for memory addresses
14//!
15//! ### Composite Types
16//! - **Arrays**: Fixed-size sequences of elements of the same type
17//! - **Structs**: Named collections of fields with different types
18//! - **Tuples**: Anonymous collections of values with different types
19//! - **Named Types**: User-defined type aliases
20//!
21//! ### Special Types
22//! - **Void**: Represents the absence of a value (for functions that don't return)
23//!
24//! ## Value Representation
25//!
26//! Values in the IR can be:
27//! - **Variables**: SSA variables like `%result`
28//! - **Constants**: Literal values like `42`, `true`, `"hello"`
29//! - **Globals**: Global variables like `@message`
30//!
31//! ## FEAT:TODO - Missing Features Compared to LLVM IR
32//!
33//! ### Type System Gaps:
34//! - **Vector/SIMD types**: `<4 x i32>` for SIMD operations
35//! - **Function types**: First-class function types like `i32 (i32, i32)*`
36//! - **Address spaces**: Memory spaces like `i32 addrspace(1)*` for GPUs
37//! - **Packed structs**: `<{ i32, i8 }>` for compact memory layout
38//! - **Union types**: Sum types for different representations
39//! - **Opaque types**: Forward declarations like `type opaque`
40//! - **Type aliases**: Named type abbreviations
41//! - **Metadata types**: Types for debug and optimization info
42//!
43//! ### Missing / Planned Instructions:
44//! - **Floating point**: additional ops beyond the existing arithmetic and comparisons
45//! - **Memory intrinsics**: `memcpy`, `memset`, `memmove`
46//! - **Atomic operations**: richer `atomicrmw`/`cmpxchg`/`fence` variants
47//! - **Exception handling**: `invoke`, `landingpad`, `resume`
48//! - **Control flow**: `switch`, `indirectbr`
49//! - **Vector operations**: additional SIMD arithmetic and operations (kept behind `nightly`)
50//!
51//! ### Advanced Features:
52//! - **Debug information**: `!dbg`, `!llvm.dbg.value`
53//! - **Module features**: `comdat`, `section`, `alignment`
54//! - **Optimization hints**: `!invariant.load`, `!dereferenceable`
55//! - **Target intrinsics**: Architecture-specific operations
56//! - **Profile-guided optimization**: Branch weights and profiling data
57//!
58//! ## IMPLEMENTED: Raw I/O Support
59//!
60//! Lamina provides raw I/O operations for direct system-level
61//! input and output without stdlib dependencies:
62//!
63//! ### I/O Instruction Categories:
64//!
65//! #### **Byte-Level I/O**:
66//! - **`writebyte`**: Write single byte (low 8 bits) to stdout; canonical for portable output
67//! - **`readbyte`**: Read single byte from stdin
68//! - **Use case**: Character I/O, text output, keyboard input
69//!
70//! #### **Buffer-Level I/O**:
71//! - **`write`**: Write buffer contents to stdout
72//! - **`read`**: Read data into buffer from stdin
73//! - **Use case**: Bulk data transfer, file operations, network I/O
74//!
75//! #### **Memory-Value I/O** (NEW):
76//! - **`writeptr`**: Write VALUE stored at pointer to stdout
77//!   //! - **Use case**: Memory inspection, binary data output, serialization
78//!
79//! ### WritePtr Semantics (IMPORTANT):
80//!
81//! **What it does:**
82//! ```text
83//! Memory: [42] <- pointer points here
84//! writeptr(pointer) -> outputs: 42 (binary)
85//! ```
86//!
87//! **NOT the pointer address, but the VALUE stored at that address!**
88//!
89//! **Example:**
90//! ```lamina
91//! %buffer = alloc.stack i32      # Allocate memory
92//! store.i32 %buffer, 72          # Store 72 ('H' in ASCII)
93//! %result = writeptr %buffer     # Outputs: 'H' (binary 72)
94//! ```
95//!
96//! **Common misconception:**
97//! - `writeptr(ptr)` does NOT write the pointer address
98//! - `writeptr(ptr)` writes the VALUE stored at that address
99//! - Think of it as "write pointer's value", not "write pointer"
100//!
101//! ### I/O Operation Comparison:
102//!
103//! | Instruction | Input/Output | Data Format | Use Case |
104//! |-------------|--------------|-------------|----------|
105//! | `writebyte` | Single char | ASCII | Simple text |
106//! | `write` | Buffer | Raw bytes | Bulk data |
107//! | `writeptr` | Memory value | Binary | Memory inspection |
108//!
109//! ### Memory Workflow Example:
110//!
111//! ```lamina
112//! # Step 1: Allocate memory
113//! %data = alloc.stack i32
114//!
115//! # Step 2: Store a value
116//! store.i32 %data, 65  # ASCII 'A'
117//!
118//! # Step 3: Load the value (optional, for verification)
119//! %value = load.i32 %data
120//!
121//! # Step 4: Output the stored value
122//! %bytes = writeptr %data  # Outputs: 'A'
123//! ```
124//!
125//! This workflow demonstrates the complete memory manipulation cycle
126//! that forms the foundation of all data processing in Lamina.
127//!
128//! ## Type Safety
129//!
130//! The type system provides:
131//! - **Type checking**: Operations are only performed on compatible types
132//! - **Memory safety**: Proper handling of pointers and memory access
133//! - **Correctness**: Many classes of errors are caught at compile time
134//!
135//! ## Value States (Semantics Contract)
136//!
137//! The IR distinguishes three special value states that every optimization pass
138//! and code generator must handle correctly:
139//!
140//! ### Undef
141//! An *undef* value represents an uninitialized read — any bit pattern is valid.
142//! Optimizers may replace every occurrence of an undef with any concrete value
143//! of the matching type.  Code that reads from uninitialized memory produces
144//! undef.  Undef propagates only when the result of an operation is entirely
145//! determined by an undef operand (e.g. `load` of an un-stored alloc).
146//!
147//! ### Poison
148//! A *poison* value is produced when an instruction's preconditions are
149//! violated but the violation is not observable at the point of production —
150//! for example, integer overflow in a signed `add`, an out-of-range shift
151//! count, or using `undef` as an operand to a pure arithmetic operation.
152//! Poison propagates eagerly through pure operations: any instruction whose
153//! operands include poison produces poison.  A poison value that reaches a
154//! side-effecting instruction (store, branch condition, return value, call
155//! argument) causes **undefined behavior** — the compiler is free to assume
156//! this path is unreachable.  Optimizations may exploit this freedom.
157//!
158//! ### Trap
159//! A *trap* terminates the process at the point of the offending instruction.
160//! The following operations always trap on violation and never produce poison:
161//! - Integer division or remainder by zero (`BinaryOp::Div`, `BinaryOp::Rem`)
162//! - Null-pointer dereference (`Load` / `Store` through a null `Ptr`)
163//! - Out-of-bounds memory access (if bounds information is available)
164//!
165//! All other precondition violations produce poison (see individual instruction
166//! doc comments for details).
167//!
168//! ## Examples
169//!
170//! ```rust
171//! use lamina_ir::{IRBuilder, Type, PrimitiveType, StructField, BinaryOp, CmpOp};
172//! use lamina_ir::builder::{var, i32, bool};
173//!
174//! let mut builder = IRBuilder::new();
175//! builder
176//!     .function("type_demo", Type::Void)
177//!     // Primitive types
178//!     .binary(BinaryOp::Add, "sum", PrimitiveType::I32, i32(10), i32(20))
179//!     // Array type
180//!     .alloc_stack("arr", Type::Array {
181//!         element_type: Box::new(Type::Primitive(PrimitiveType::I32)),
182//!         size: 10
183//!     })
184//!     // Struct type
185//!     .alloc_stack("point", Type::Struct(vec![
186//!         StructField { name: "x", ty: Type::Primitive(PrimitiveType::I32) },
187//!         StructField { name: "y", ty: Type::Primitive(PrimitiveType::I32) }
188//!     ]))
189//!     .ret_void();
190//! ```
191
192use std::fmt;
193
194/// A type alias for identifiers in the IR.
195///
196/// Uses `&'a str` to avoid allocating `String` objects
197/// and memory efficiency. The lifetime parameter keeps identifiers
198/// valid as long as the source text they were parsed from.
199///
200/// Identifiers are used for variable names, function names, and other symbols.
201/// Using string references avoids unnecessary allocations and keeps the IR lightweight.
202pub type Identifier<'a> = &'a str;
203/// A type alias for basic block labels in the IR.
204///
205/// Labels are used to identify basic blocks for control flow operations
206/// like branches and jumps.
207pub type Label<'a> = &'a str;
208
209/// Primitive types in the Lamina IR.
210///
211/// Primitive types are the fundamental building blocks of the type system.
212/// They represent basic data types that can be directly operated on by
213/// the target architecture.
214///
215/// # Integer Types
216///
217/// - **Signed integers**: `I8`, `I16`, `I32`, `I64`
218/// - **Unsigned integers**: `U8`, `U16`, `U32`, `U64`
219///
220/// # Floating Point Types
221///
222/// - **Single precision**: `F32` (32-bit IEEE 754)
223/// - **Double precision**: `F64` (64-bit IEEE 754)
224///
225/// # Other Types
226///
227/// - **Boolean**: `Bool` (true/false)
228/// - **Character**: `Char` (8-bit character)
229/// - **Pointer**: `Ptr` (generic pointer type)
230///
231/// # Examples
232///
233/// ```rust
234/// use lamina_ir::{IRBuilder, Type, PrimitiveType, BinaryOp, CmpOp};
235/// use lamina_ir::builder::{var, i32, bool};
236///
237/// let mut builder = IRBuilder::new();
238/// builder
239///     .function("primitives", Type::Void)
240///     .binary(BinaryOp::Add, "sum", PrimitiveType::I32, i32(10), i32(20))
241///     .cmp(CmpOp::Gt, "is_positive", PrimitiveType::I32, var("sum"), i32(0))
242///     .ret_void();
243/// ```
244#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
245pub enum PrimitiveType {
246    /// 8-bit signed integer (-128 to 127)
247    I8,
248    /// 16-bit signed integer (-32,768 to 32,767)
249    I16,
250    /// 32-bit signed integer (-2,147,483,648 to 2,147,483,647)
251    I32,
252    /// 64-bit signed integer (-9,223,372,036,854,775,808 to 9,223,372,036,854,775,807)
253    I64,
254    /// 8-bit unsigned integer (0 to 255)
255    U8,
256    /// 16-bit unsigned integer (0 to 65,535)
257    U16,
258    /// 32-bit unsigned integer (0 to 4,294,967,295)
259    U32,
260    /// 64-bit unsigned integer (0 to 18,446,744,073,709,551,615)
261    U64,
262    /// 32-bit floating point number (IEEE 754 single precision)
263    F32,
264    /// 64-bit floating point number (IEEE 754 double precision)
265    F64,
266    /// Boolean value (true or false)
267    Bool,
268    /// Single character (8-bit)
269    Char,
270    /// Generic pointer type for memory addresses
271    Ptr,
272}
273
274#[derive(Debug, Clone, PartialEq, Eq, Hash)]
275pub struct StructField<'a> {
276    pub name: Identifier<'a>,
277    pub ty: Type<'a>,
278}
279
280#[derive(Debug, Clone, PartialEq, Eq, Hash)]
281pub enum Type<'a> {
282    Primitive(PrimitiveType),
283    Named(Identifier<'a>), // `@Vec2`
284    Array {
285        element_type: Box<Type<'a>>,
286        size: u64,
287    },
288    #[cfg(feature = "nightly")]
289    /// SIMD vector type: `<N x T>` where N is the number of lanes and T is the element type
290    /// Examples: `<4 x i32>`, `<8 x f32>`, `<2 x i64>`
291    Vector {
292        element_type: PrimitiveType,
293        lanes: u32,
294    },
295    Struct(Vec<StructField<'a>>),
296    Tuple(Vec<Type<'a>>),
297    Void,
298}
299
300#[derive(Debug, Clone, PartialEq)] // Float comparison requires PartialEq only
301pub enum Literal<'a> {
302    I8(i8),
303    I16(i16),
304    I32(i32),
305    I64(i64),
306    U8(u8),
307    U16(u16),
308    U32(u32),
309    U64(u64),
310    F32(f32),
311    F64(f64),
312    Bool(bool),
313    Char(char),
314    String(&'a str),
315}
316
317#[derive(Debug, Clone, PartialEq)] // Removed Eq, Hash because f32 doesn't support them
318pub enum Value<'a> {
319    Variable(Identifier<'a>),
320    Constant(Literal<'a>),
321    Global(Identifier<'a>),
322}
323
324impl PrimitiveType {
325    /// Returns all valid primitive type names as strings.
326    pub fn all_names() -> &'static [&'static str] {
327        const fn name_for(ty: PrimitiveType) -> &'static str {
328            ty.as_str()
329        }
330        const NAMES: &[&str] = &[
331            name_for(PrimitiveType::I8),
332            name_for(PrimitiveType::I16),
333            name_for(PrimitiveType::I32),
334            name_for(PrimitiveType::I64),
335            name_for(PrimitiveType::U8),
336            name_for(PrimitiveType::U16),
337            name_for(PrimitiveType::U32),
338            name_for(PrimitiveType::U64),
339            name_for(PrimitiveType::F32),
340            name_for(PrimitiveType::F64),
341            name_for(PrimitiveType::Bool),
342            name_for(PrimitiveType::Char),
343            name_for(PrimitiveType::Ptr),
344        ];
345        NAMES
346    }
347
348    /// Returns the string representation of this primitive type.
349    pub const fn as_str(&self) -> &'static str {
350        match self {
351            PrimitiveType::I8 => "i8",
352            PrimitiveType::I16 => "i16",
353            PrimitiveType::I32 => "i32",
354            PrimitiveType::I64 => "i64",
355            PrimitiveType::U8 => "u8",
356            PrimitiveType::U16 => "u16",
357            PrimitiveType::U32 => "u32",
358            PrimitiveType::U64 => "u64",
359            PrimitiveType::F32 => "f32",
360            PrimitiveType::F64 => "f64",
361            PrimitiveType::Bool => "bool",
362            PrimitiveType::Char => "char",
363            PrimitiveType::Ptr => "ptr",
364        }
365    }
366}
367
368impl fmt::Display for PrimitiveType {
369    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
370        write!(f, "{}", self.as_str())
371    }
372}
373
374impl fmt::Display for Type<'_> {
375    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
376        match self {
377            Type::Primitive(pt) => write!(f, "{}", pt),
378            Type::Named(id) => write!(f, "@{}", id),
379            Type::Array { element_type, size } => write!(f, "[{} x {}]", size, element_type),
380            #[cfg(feature = "nightly")]
381            Type::Vector {
382                element_type,
383                lanes,
384            } => write!(f, "<{} x {}>", lanes, element_type),
385            Type::Struct(fields) => {
386                write!(f, "struct {{ ")?;
387                for (i, field) in fields.iter().enumerate() {
388                    write!(f, "{}: {}", field.name, field.ty)?;
389                    if i < fields.len() - 1 {
390                        write!(f, ", ")?;
391                    }
392                }
393                write!(f, " }}")
394            }
395            Type::Tuple(types) => {
396                write!(f, "tuple(")?;
397                for (i, ty) in types.iter().enumerate() {
398                    write!(f, "{}", ty)?;
399                    if i < types.len() - 1 {
400                        write!(f, ", ")?;
401                    }
402                }
403                write!(f, ")")
404            }
405            Type::Void => write!(f, "void"),
406        }
407    }
408}
409
410impl fmt::Display for Literal<'_> {
411    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
412        match self {
413            Literal::I8(v) => write!(f, "{}", v),
414            Literal::I16(v) => write!(f, "{}", v),
415            Literal::I32(v) => write!(f, "{}", v),
416            Literal::I64(v) => write!(f, "{}", v),
417            Literal::U8(v) => write!(f, "{}", v),
418            Literal::U16(v) => write!(f, "{}", v),
419            Literal::U32(v) => write!(f, "{}", v),
420            Literal::U64(v) => write!(f, "{}", v),
421            Literal::F32(v) => write!(f, "{}", v),
422            Literal::F64(v) => write!(f, "{}", v),
423            Literal::Bool(v) => write!(f, "{}", v),
424            Literal::Char(c) => write!(f, "'{}'", c),
425            Literal::String(s) => write!(f, "\"{}\"", s), // Note: needs escaping for proper output
426        }
427    }
428}
429
430impl fmt::Display for Value<'_> {
431    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
432        match self {
433            Value::Variable(id) => write!(f, "%{id}"),
434            Value::Constant(lit) => write!(f, "{}", lit),
435            Value::Global(id) => write!(f, "@{id}"),
436        }
437    }
438}
439
440#[cfg(test)]
441mod tests {
442    use super::*;
443
444    #[test]
445    fn test_display_primitive_type() {
446        assert_eq!(format!("{}", PrimitiveType::I8), "i8");
447        assert_eq!(format!("{}", PrimitiveType::I16), "i16");
448        assert_eq!(format!("{}", PrimitiveType::I32), "i32");
449        assert_eq!(format!("{}", PrimitiveType::I64), "i64");
450        assert_eq!(format!("{}", PrimitiveType::U8), "u8");
451        assert_eq!(format!("{}", PrimitiveType::U16), "u16");
452        assert_eq!(format!("{}", PrimitiveType::U32), "u32");
453        assert_eq!(format!("{}", PrimitiveType::U64), "u64");
454        assert_eq!(format!("{}", PrimitiveType::F32), "f32");
455        assert_eq!(format!("{}", PrimitiveType::F64), "f64");
456        assert_eq!(format!("{}", PrimitiveType::Bool), "bool");
457        assert_eq!(format!("{}", PrimitiveType::Char), "char");
458        assert_eq!(format!("{}", PrimitiveType::Ptr), "ptr");
459    }
460
461    #[test]
462    fn test_display_type() {
463        assert_eq!(format!("{}", Type::Primitive(PrimitiveType::I64)), "i64");
464        assert_eq!(format!("{}", Type::Named("MyStruct")), "@MyStruct");
465        let arr_type = Type::Array {
466            element_type: Box::new(Type::Primitive(PrimitiveType::I8)),
467            size: 10,
468        };
469        assert_eq!(format!("{}", arr_type), "[10 x i8]");
470        let struct_type = Type::Struct(vec![
471            StructField {
472                name: "x",
473                ty: Type::Primitive(PrimitiveType::F32),
474            },
475            StructField {
476                name: "y",
477                ty: Type::Primitive(PrimitiveType::F32),
478            },
479        ]);
480        assert_eq!(format!("{}", struct_type), "struct { x: f32, y: f32 }");
481        let tuple_type = Type::Tuple(vec![
482            Type::Primitive(PrimitiveType::I32),
483            Type::Named("MyBool"),
484        ]);
485        assert_eq!(format!("{}", tuple_type), "tuple(i32, @MyBool)");
486        assert_eq!(format!("{}", Type::Void), "void");
487        #[cfg(feature = "nightly")]
488        {
489            let vec_type = Type::Vector {
490                element_type: PrimitiveType::I32,
491                lanes: 4,
492            };
493            assert_eq!(format!("{}", vec_type), "<4 x i32>");
494            let vec_f32 = Type::Vector {
495                element_type: PrimitiveType::F32,
496                lanes: 8,
497            };
498            assert_eq!(format!("{}", vec_f32), "<8 x f32>");
499        }
500    }
501
502    #[test]
503    fn test_display_literal() {
504        assert_eq!(format!("{}", Literal::I8(123)), "123");
505        assert_eq!(format!("{}", Literal::I16(-456)), "-456");
506        assert_eq!(format!("{}", Literal::I32(123)), "123");
507        assert_eq!(format!("{}", Literal::I64(-456)), "-456");
508        assert_eq!(format!("{}", Literal::U8(123)), "123");
509        assert_eq!(format!("{}", Literal::U16(456)), "456");
510        assert_eq!(format!("{}", Literal::U32(123)), "123");
511        assert_eq!(format!("{}", Literal::U64(456)), "456");
512        assert_eq!(format!("{}", Literal::F32(1.25)), "1.25");
513        assert_eq!(format!("{}", Literal::F64(2.5)), "2.5");
514        assert_eq!(format!("{}", Literal::Bool(true)), "true");
515        assert_eq!(format!("{}", Literal::Bool(false)), "false");
516        assert_eq!(format!("{}", Literal::Char('A')), "'A'");
517        assert_eq!(format!("{}", Literal::String("hello")), "\"hello\"");
518    }
519
520    #[test]
521    fn test_display_value() {
522        assert_eq!(format!("{}", Value::Variable("tmp1")), "%tmp1");
523        assert_eq!(format!("{}", Value::Constant(Literal::I32(42))), "42");
524        assert_eq!(
525            format!("{}", Value::Constant(Literal::String("world"))),
526            "\"world\""
527        );
528        assert_eq!(
529            format!("{}", Value::Global("my_global_var")),
530            "@my_global_var"
531        );
532    }
533}