Skip to main content

qbe/
lib.rs

1// Copyright 2022 Garrit Franke
2// Copyright 2021 Alexey Yerin
3//
4// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
7// option. This file may not be copied, modified, or distributed
8// except according to those terms.
9
10//! # QBE Rust
11//!
12//! A Rust library for programmatically generating QBE Intermediate Language code.
13//!
14//! [QBE](https://c9x.me/compile/) is a compiler backend that transforms simple intermediate
15//! representation (IR) into executable machine code. This library provides Rust data structures
16//! and functions to generate valid QBE IL.
17//!
18//! ## Basic Example
19//!
20//! ```rust
21//! use qbe::{Module, Function, Linkage, Type, Value, Instr};
22//!
23//! // Create a new module
24//! let mut module = Module::new();
25//!
26//! // Add a simple function that returns the sum of two integers
27//! let mut func = Function::new(
28//!     Linkage::public(),
29//!     "add",
30//!     vec![
31//!         (Type::Word, Value::Temporary("a".to_string())),
32//!         (Type::Word, Value::Temporary("b".to_string())),
33//!     ],
34//!     Some(Type::Word),
35//! );
36//!
37//! // Add a block to the function
38//! let mut block = func.add_block("start");
39//!
40//! // Add two arguments and store result in "sum"
41//! block.assign_instr(
42//!     Value::Temporary("sum".to_string()),
43//!     Type::Word,
44//!     Instr::Add(
45//!         Value::Temporary("a".to_string()),
46//!         Value::Temporary("b".to_string()),
47//!     ),
48//! );
49//!
50//! // Return the sum
51//! block.add_instr(Instr::Ret(Some(Value::Temporary("sum".to_string()))));
52//!
53//! // Add the function to the module
54//! module.add_function(func);
55//!
56//! // Generate QBE IL code
57//! println!("{}", module);
58//! ```
59//!
60//! This generates the following QBE IL:
61//! ```ssa
62//! export function w $add(w %a, w %b) {
63//! @start
64//!     %sum =w add %a, %b
65//!     ret %sum
66//! }
67//! ```
68
69use std::fmt;
70use std::sync::Arc;
71
72#[cfg(test)]
73mod tests;
74
75/// QBE comparison operations used in conditional instructions.
76///
77/// The result of a comparison is 1 if the condition is true, and 0 if false.
78///
79/// # Examples
80///
81/// ```rust
82/// use qbe::{Cmp, Instr, Type, Value};
83///
84/// // Compare if %a is less than %b (signed comparison)
85/// let slt_instr = Instr::Cmp(
86///     Type::Word,
87///     Cmp::Slt,
88///     Value::Temporary("a".to_string()),
89///     Value::Temporary("b".to_string()),
90/// );
91///
92/// // Check if two values are equal
93/// let eq_instr = Instr::Cmp(
94///     Type::Word,
95///     Cmp::Eq,
96///     Value::Temporary("x".to_string()),
97///     Value::Const(0),
98/// );
99/// ```
100#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Copy)]
101pub enum Cmp {
102    /// Returns 1 if first value is less than second, respecting signedness
103    Slt,
104    /// Returns 1 if first value is less than or equal to second, respecting signedness
105    Sle,
106    /// Returns 1 if first value is greater than second, respecting signedness
107    Sgt,
108    /// Returns 1 if first value is greater than or equal to second, respecting signedness
109    Sge,
110    /// Returns 1 if values are equal
111    Eq,
112    /// Returns 1 if values are not equal
113    Ne,
114    /// Returns 1 if both operands are not NaN (ordered comparison)
115    O,
116    /// Returns 1 if at least one operand is NaN (unordered comparison)
117    Uo,
118    /// Returns 1 if first value is less than second, unsigned comparison
119    Ult,
120    /// Returns 1 if first value is less than or equal to second, unsigned comparison
121    Ule,
122    /// Returns 1 if first value is greater than second, unsigned comparison
123    Ugt,
124    /// Returns 1 if first value is greater than or equal to second, unsigned comparison
125    Uge,
126}
127
128/// QBE instructions representing operations in the intermediate language.
129///
130/// # Examples
131///
132/// ## Arithmetic Operations
133/// ```rust
134/// use qbe::{Instr, Value};
135///
136/// // Addition: %result = %a + %b
137/// let add = Instr::Add(
138///     Value::Temporary("a".to_string()),
139///     Value::Temporary("b".to_string()),
140/// );
141///
142/// // Multiplication: %result = %x * 5
143/// let mul = Instr::Mul(
144///     Value::Temporary("x".to_string()),
145///     Value::Const(5),
146/// );
147/// ```
148///
149/// ## Memory Operations
150/// ```rust
151/// use qbe::{Instr, Type, Value};
152///
153/// // Allocate 8 bytes on the stack with 8-byte alignment
154/// let alloc = Instr::Alloc8(8);
155///
156/// // Store a word to memory: store %value, %ptr
157/// let store = Instr::Store(
158///     Type::Word,
159///     Value::Temporary("ptr".to_string()),
160///     Value::Temporary("value".to_string()),
161/// );
162///
163/// // Load a word from memory: %result = load %ptr
164/// let load = Instr::Load(
165///     Type::Word,
166///     Value::Temporary("ptr".to_string()),
167/// );
168/// ```
169///
170/// ## Control Flow
171/// ```rust
172/// use qbe::{Instr, Value};
173///
174/// // Conditional jump based on %condition
175/// let branch = Instr::Jnz(
176///     Value::Temporary("condition".to_string()),
177///     "true_branch".to_string(),
178///     "false_branch".to_string(),
179/// );
180///
181/// // Return a value from a function
182/// let ret = Instr::Ret(Some(Value::Temporary("result".to_string())));
183/// ```
184#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
185pub enum Instr {
186    /// Adds values of two temporaries together
187    Add(Value, Value),
188    /// Subtracts the second value from the first one
189    Sub(Value, Value),
190    /// Multiplies values of two temporaries
191    Mul(Value, Value),
192    /// Divides the first value by the second one
193    Div(Value, Value),
194    /// Returns a remainder from division
195    Rem(Value, Value),
196    /// Performs a comparion between values
197    Cmp(Type, Cmp, Value, Value),
198    /// Performs a bitwise AND on values
199    And(Value, Value),
200    /// Performs a bitwise OR on values
201    Or(Value, Value),
202    /// Performs a bitwise XOR on values
203    Xor(Value, Value),
204    /// Negates a value
205    Neg(Value),
206    /// Copies either a temporary or a literal value
207    Copy(Value),
208    /// Return from a function, optionally with a value
209    Ret(Option<Value>),
210    /// Jumps to first label if a value is nonzero or to the second one otherwise
211    Jnz(Value, String, String),
212    /// Unconditionally jumps to a label
213    Jmp(String),
214    /// Calls a function
215    Call(String, Vec<(Type, Value)>, Option<u64>),
216    /// Allocates a 4-byte aligned area on the stack
217    Alloc4(u32),
218    /// Allocates a 8-byte aligned area on the stack
219    Alloc8(u64),
220    /// Allocates a 16-byte aligned area on the stack
221    Alloc16(u128),
222    /// Stores a value into memory pointed to by destination.
223    /// `(type, destination, value)`
224    ///
225    /// For sub-word types, signed/unsigned variants (`SignedByte`, `UnsignedByte`,
226    /// `SignedHalfword`, `UnsignedHalfword`) are accepted and map to `storeb`/`storeh`,
227    /// since stores only truncate and don't distinguish signedness.
228    ///
229    /// See the [QBE IL reference](https://c9x.me/compile/doc/il.html#Memory).
230    Store(Type, Value, Value),
231    /// Loads a value from memory pointed to by source.
232    /// `(type, source)`
233    ///
234    /// # Panics
235    ///
236    /// Panics if called with [`Type::Byte`] or [`Type::Halfword`], because QBE requires
237    /// explicit sign/zero extension for sub-word loads. Use [`Type::SignedByte`] /
238    /// [`Type::UnsignedByte`] or [`Type::SignedHalfword`] / [`Type::UnsignedHalfword`]
239    /// instead.
240    ///
241    /// See the [QBE IL reference](https://c9x.me/compile/doc/il.html#Memory).
242    Load(Type, Value),
243    /// `(source, destination, n)`
244    ///
245    /// Copy `n` bytes from the source address to the destination address.
246    ///
247    /// n must be a constant value.
248    ///
249    /// ## Minimum supported QBE version
250    /// `1.1`
251    Blit(Value, Value, u64),
252
253    /// Debug file.
254    DbgFile(String),
255    /// Debug line.
256    ///
257    /// Takes line number and an optional column.
258    DbgLoc(u64, Option<u64>),
259
260    // Unsigned arithmetic
261    /// Performs unsigned division of the first value by the second one
262    Udiv(Value, Value),
263    /// Returns the remainder from unsigned division
264    Urem(Value, Value),
265
266    // Shifts
267    /// Shift arithmetic right (preserves sign)
268    Sar(Value, Value),
269    /// Shift logical right (fills with zeros)
270    Shr(Value, Value),
271    /// Shift left (fills with zeros)
272    Shl(Value, Value),
273
274    // Type conversions
275    /// Cast between integer and floating point of the same width
276    Cast(Value),
277
278    // Extension operations
279    /// Sign-extends a word to a long
280    Extsw(Value),
281    /// Zero-extends a word to a long
282    Extuw(Value),
283    /// Sign-extends a halfword to a word or long
284    Extsh(Value),
285    /// Zero-extends a halfword to a word or long
286    Extuh(Value),
287    /// Sign-extends a byte to a word or long
288    Extsb(Value),
289    /// Zero-extends a byte to a word or long
290    Extub(Value),
291    /// Extends a single-precision float to double-precision
292    Exts(Value),
293    /// Truncates a double-precision float to single-precision
294    Truncd(Value),
295
296    // Float-integer conversions
297    /// Converts a single-precision float to a signed integer
298    Stosi(Value),
299    /// Converts a single-precision float to an unsigned integer
300    Stoui(Value),
301    /// Converts a double-precision float to a signed integer
302    Dtosi(Value),
303    /// Converts a double-precision float to an unsigned integer
304    Dtoui(Value),
305    /// Converts a signed word to a float
306    Swtof(Value),
307    /// Converts an unsigned word to a float
308    Uwtof(Value),
309    /// Converts a signed long to a float
310    Sltof(Value),
311    /// Converts an unsigned long to a float
312    Ultof(Value),
313
314    // Variadic function support
315    /// Initializes a variable argument list
316    Vastart(Value),
317    /// Fetches the next argument from a variable argument list
318    Vaarg(Type, Value),
319
320    // Phi instruction
321    /// Selects value based on the control flow path into a block.
322    Phi(Vec<(String, Value)>),
323
324    // Program termination
325    /// Terminates the program with an error
326    Hlt,
327}
328
329impl fmt::Display for Instr {
330    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
331        match self {
332            Self::Add(lhs, rhs) => write!(f, "add {lhs}, {rhs}"),
333            Self::Sub(lhs, rhs) => write!(f, "sub {lhs}, {rhs}"),
334            Self::Mul(lhs, rhs) => write!(f, "mul {lhs}, {rhs}"),
335            Self::Div(lhs, rhs) => write!(f, "div {lhs}, {rhs}"),
336            Self::Rem(lhs, rhs) => write!(f, "rem {lhs}, {rhs}"),
337            Self::Cmp(ty, cmp, lhs, rhs) => {
338                assert!(
339                    !matches!(ty, Type::Aggregate(_)),
340                    "cannot compare aggregate types"
341                );
342
343                write!(
344                    f,
345                    "c{}{} {}, {}",
346                    match cmp {
347                        Cmp::Slt => "slt",
348                        Cmp::Sle => "sle",
349                        Cmp::Sgt => "sgt",
350                        Cmp::Sge => "sge",
351                        Cmp::Eq => "eq",
352                        Cmp::Ne => "ne",
353                        Cmp::O => "o",
354                        Cmp::Uo => "uo",
355                        Cmp::Ult => "ult",
356                        Cmp::Ule => "ule",
357                        Cmp::Ugt => "ugt",
358                        Cmp::Uge => "uge",
359                    },
360                    ty,
361                    lhs,
362                    rhs,
363                )
364            }
365            Self::And(lhs, rhs) => write!(f, "and {lhs}, {rhs}"),
366            Self::Or(lhs, rhs) => write!(f, "or {lhs}, {rhs}"),
367            Self::Xor(lhs, rhs) => write!(f, "xor {lhs}, {rhs}"),
368            Self::Neg(val) => write!(f, "neg {val}"),
369            Self::Copy(val) => write!(f, "copy {val}"),
370            Self::Ret(val) => match val {
371                Some(val) => write!(f, "ret {val}"),
372                None => write!(f, "ret"),
373            },
374            Self::DbgFile(val) => write!(f, r#"dbgfile "{val}""#),
375            Self::DbgLoc(lineno, column) => match column {
376                Some(val) => write!(f, "dbgloc {lineno}, {val}"),
377                None => write!(f, "dbgloc {lineno}"),
378            },
379            Self::Jnz(val, if_nonzero, if_zero) => {
380                write!(f, "jnz {val}, @{if_nonzero}, @{if_zero}")
381            }
382            Self::Jmp(label) => write!(f, "jmp @{label}"),
383            Self::Call(name, args, opt_variadic_i) => {
384                let mut args_fmt = args
385                    .iter()
386                    .map(|(ty, temp)| format!("{ty} {temp}"))
387                    .collect::<Vec<String>>();
388                if let Some(i) = *opt_variadic_i {
389                    args_fmt.insert(i as usize, "...".to_string());
390                }
391
392                write!(f, "call ${}({})", name, args_fmt.join(", "),)
393            }
394            Self::Alloc4(size) => write!(f, "alloc4 {size}"),
395            Self::Alloc8(size) => write!(f, "alloc8 {size}"),
396            Self::Alloc16(size) => write!(f, "alloc16 {size}"),
397            Self::Store(ty, dest, value) => {
398                let suffix = match ty {
399                    Type::SignedByte | Type::UnsignedByte => "b".to_string(),
400                    Type::SignedHalfword | Type::UnsignedHalfword => "h".to_string(),
401                    Type::Aggregate(_) => panic!("cannot store to an aggregate type"),
402                    _ => ty.to_string(),
403                };
404                write!(f, "store{suffix} {value}, {dest}")
405            }
406            Self::Load(ty, src) => match ty {
407                Type::Byte | Type::Halfword => panic!(
408                    "ambiguous sub-word load: use SignedByte/UnsignedByte or SignedHalfword/UnsignedHalfword"
409                ),
410                Type::Aggregate(_) => panic!("cannot load aggregate type"),
411                _ => write!(f, "load{ty} {src}"),
412            }
413            Self::Blit(src, dst, n) => write!(f, "blit {src}, {dst}, {n}"),
414            Self::Udiv(lhs, rhs) => write!(f, "udiv {lhs}, {rhs}"),
415            Self::Urem(lhs, rhs) => write!(f, "urem {lhs}, {rhs}"),
416            Self::Sar(lhs, rhs) => write!(f, "sar {lhs}, {rhs}"),
417            Self::Shr(lhs, rhs) => write!(f, "shr {lhs}, {rhs}"),
418            Self::Shl(lhs, rhs) => write!(f, "shl {lhs}, {rhs}"),
419            Self::Cast(val) => write!(f, "cast {val}"),
420            Self::Extsw(val) => write!(f, "extsw {val}"),
421            Self::Extuw(val) => write!(f, "extuw {val}"),
422            Self::Extsh(val) => write!(f, "extsh {val}"),
423            Self::Extuh(val) => write!(f, "extuh {val}"),
424            Self::Extsb(val) => write!(f, "extsb {val}"),
425            Self::Extub(val) => write!(f, "extub {val}"),
426            Self::Exts(val) => write!(f, "exts {val}"),
427            Self::Truncd(val) => write!(f, "truncd {val}"),
428            Self::Stosi(val) => write!(f, "stosi {val}"),
429            Self::Stoui(val) => write!(f, "stoui {val}"),
430            Self::Dtosi(val) => write!(f, "dtosi {val}"),
431            Self::Dtoui(val) => write!(f, "dtoui {val}"),
432            Self::Swtof(val) => write!(f, "swtof {val}"),
433            Self::Uwtof(val) => write!(f, "uwtof {val}"),
434            Self::Sltof(val) => write!(f, "sltof {val}"),
435            Self::Ultof(val) => write!(f, "ultof {val}"),
436            Self::Vastart(val) => write!(f, "vastart {val}"),
437            Self::Vaarg(ty, val) => write!(f, "vaarg{ty} {val}"),
438            Self::Phi(args) => {
439                let formatted_args = args
440                    .iter()
441                    .map(|(label, value)| format!("@{label} {value}"))
442                    .collect::<Vec<String>>()
443                    .join(", ");
444                write!(f, "phi {formatted_args}")
445            }
446            Self::Hlt => write!(f, "hlt"),
447        }
448    }
449}
450
451/// QBE types used to specify the size and representation of values.
452///
453/// QBE has a minimal type system with base types and extended types.
454/// Base types are used for temporaries, while extended types can be used
455/// in aggregate types and data definitions.
456///
457/// # Examples
458///
459/// ```rust
460/// use qbe::Type;
461///
462/// // Base types
463/// let word = Type::Word;     // 32-bit integer
464/// let long = Type::Long;     // 64-bit integer
465/// let single = Type::Single; // 32-bit float
466/// let double = Type::Double; // 64-bit float
467///
468/// // Extended types
469/// let byte = Type::Byte;     // 8-bit value
470/// let halfword = Type::Halfword; // 16-bit value
471///
472/// // Get type sizes in bytes
473/// assert_eq!(word.size(), 4);
474/// assert_eq!(byte.size(), 1);
475/// ```
476///
477/// ## Aggregate Types
478///
479/// Aggregate types reference a [`TypeDef`] via [`Arc`](std::sync::Arc):
480///
481/// ```rust
482/// use std::sync::Arc;
483/// use qbe::{TypeDef, Type};
484///
485/// let td = Arc::new(TypeDef::Regular {
486///     ident: "pair".into(),
487///     align: None,
488///     items: vec![(Type::Word, 2)],
489/// });
490///
491/// let ty = Type::aggregate(&td);
492/// assert_eq!(ty.size(), 8);
493/// ```
494///
495/// ## Type Conversions
496///
497/// ```rust
498/// use qbe::Type;
499///
500/// // Convert extended type to corresponding base type
501/// let base = Type::Byte.into_base();
502/// assert_eq!(base, Type::Word);
503///
504/// // Convert to ABI-compatible type for function parameters
505/// let abi = Type::SignedByte.into_abi();
506/// assert_eq!(abi, Type::Word);
507/// ```
508#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
509pub enum Type {
510    // Base types
511    Word,
512    Long,
513    Single,
514    Double,
515
516    // Internal types
517    Zero,
518
519    // Extended types
520    Byte,
521    SignedByte,
522    UnsignedByte,
523    Halfword,
524    SignedHalfword,
525    UnsignedHalfword,
526
527    /// Aggregate type referencing a [`TypeDef`].
528    ///
529    /// Use [`Type::aggregate`] to construct, or wrap a [`TypeDef`] in
530    /// [`Arc::new`](std::sync::Arc::new) and pass it directly.
531    Aggregate(Arc<TypeDef>),
532}
533
534impl From<Arc<TypeDef>> for Type {
535    fn from(td: Arc<TypeDef>) -> Self {
536        Type::Aggregate(td)
537    }
538}
539
540impl From<TypeDef> for Type {
541    fn from(td: TypeDef) -> Self {
542        Type::Aggregate(Arc::new(td))
543    }
544}
545
546impl Type {
547    /// Creates a new [`Type::Aggregate`] from a reference-counted [`TypeDef`].
548    ///
549    /// # Examples
550    ///
551    /// ```rust
552    /// use std::sync::Arc;
553    /// use qbe::{TypeDef, Type};
554    ///
555    /// let td = Arc::new(TypeDef::Regular {
556    ///     ident: "person".into(),
557    ///     align: None,
558    ///     items: vec![(Type::Long, 1)],
559    /// });
560    /// let ty = Type::aggregate(&td);
561    /// assert_eq!(format!("{ty}"), ":person");
562    /// ```
563    pub fn aggregate(td: &Arc<TypeDef>) -> Self {
564        Type::Aggregate(Arc::clone(td))
565    }
566
567    /// Returns a C ABI type. Extended types are converted to closest base
568    /// types
569    pub fn into_abi(self) -> Self {
570        match self {
571            Self::Byte
572            | Self::SignedByte
573            | Self::UnsignedByte
574            | Self::Halfword
575            | Self::SignedHalfword
576            | Self::UnsignedHalfword => Self::Word,
577            other => other,
578        }
579    }
580
581    /// Returns the closest base type
582    pub fn into_base(self) -> Self {
583        match self {
584            Self::Byte
585            | Self::SignedByte
586            | Self::UnsignedByte
587            | Self::Halfword
588            | Self::SignedHalfword
589            | Self::UnsignedHalfword => Self::Word,
590            Self::Aggregate(_) => Self::Long,
591            other => other,
592        }
593    }
594
595    /// Returns byte size for values of the type
596    pub fn size(&self) -> u64 {
597        match self {
598            Self::Byte | Self::SignedByte | Self::UnsignedByte | Self::Zero => 1,
599            Self::Halfword | Self::SignedHalfword | Self::UnsignedHalfword => 2,
600            Self::Word | Self::Single => 4,
601            Self::Long | Self::Double => 8,
602            Self::Aggregate(td) => {
603                fn size_of_items(s: &Type, items: &[(Type, usize)]) -> u64 {
604                    let mut offset = 0;
605
606                    // calculation taken from: https://en.wikipedia.org/wiki/Data_structure_alignment#Computing%20padding
607                    for (item, repeat) in items.iter() {
608                        let align = item.align();
609                        let size = *repeat as u64 * item.size();
610                        let padding = (align - (offset % align)) % align;
611                        offset += padding + size;
612                    }
613
614                    let align = s.align();
615                    let padding = (align - (offset % align)) % align;
616
617                    // size is the final offset with the padding that is left
618                    offset + padding
619                }
620
621                match td.as_ref() {
622                    TypeDef::Regular { items, .. } => size_of_items(self, items),
623                    TypeDef::Union { variations, .. } => variations
624                        .iter()
625                        .map(|items| size_of_items(self, items))
626                        .max()
627                        .unwrap_or(0),
628                    TypeDef::Opaque { size, .. } => *size,
629                }
630            }
631        }
632    }
633
634    /// Returns byte alignment for values of the type
635    pub fn align(&self) -> u64 {
636        match self {
637            Self::Aggregate(td) => {
638                fn align_of_items(items: &[(Type, usize)]) -> u64 {
639                    // the alignment of a type is the maximum alignment of its members
640                    // when there's no members, the alignment is usuallly defined to be 1.
641                    items.iter().map(|item| item.0.align()).max().unwrap_or(1)
642                }
643
644                match td.as_ref() {
645                    TypeDef::Regular { align, items, .. } => {
646                        if let Some(align) = align {
647                            return *align;
648                        }
649
650                        align_of_items(items)
651                    }
652                    TypeDef::Union {
653                        align,
654                        variations: items,
655                        ..
656                    } => {
657                        if let Some(align) = align {
658                            return *align;
659                        }
660
661                        // the alignment of a union is the maximum alignment of its variations
662                        // when there's no variations, the alignment is usuallly defined to be 1.
663                        items.iter().map(|v| align_of_items(v)).max().unwrap_or(1)
664                    }
665                    TypeDef::Opaque { align, .. } => *align,
666                }
667            }
668
669            _ => self.size(),
670        }
671    }
672}
673
674impl fmt::Display for Type {
675    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
676        match self {
677            Self::Byte => write!(f, "b"),
678            Self::SignedByte => write!(f, "sb"),
679            Self::UnsignedByte => write!(f, "ub"),
680            Self::Halfword => write!(f, "h"),
681            Self::SignedHalfword => write!(f, "sh"),
682            Self::UnsignedHalfword => write!(f, "uh"),
683            Self::Word => write!(f, "w"),
684            Self::Long => write!(f, "l"),
685            Self::Single => write!(f, "s"),
686            Self::Double => write!(f, "d"),
687            Self::Zero => write!(f, "z"),
688            Self::Aggregate(td) => write!(f, ":{}", td.ident()),
689        }
690    }
691}
692
693/// QBE value that is accepted by instructions
694#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
695pub enum Value {
696    /// `%`-temporary
697    Temporary(String),
698    /// `$`-global
699    Global(String),
700    /// Constant
701    Const(u64),
702}
703
704impl From<u64> for Value {
705    fn from(val: u64) -> Self {
706        Value::Const(val)
707    }
708}
709
710impl fmt::Display for Value {
711    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
712        match self {
713            Self::Temporary(name) => write!(f, "%{name}"),
714            Self::Global(name) => write!(f, "${name}"),
715            Self::Const(value) => write!(f, "{value}"),
716        }
717    }
718}
719
720/// QBE data definition
721#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Default)]
722pub struct DataDef {
723    pub linkage: Linkage,
724    pub name: String,
725    pub align: Option<u64>,
726    pub items: Vec<(Type, DataItem)>,
727}
728
729impl DataDef {
730    pub fn new(
731        linkage: Linkage,
732        name: impl Into<String>,
733        align: Option<u64>,
734        items: Vec<(Type, DataItem)>,
735    ) -> Self {
736        Self {
737            linkage,
738            name: name.into(),
739            align,
740            items,
741        }
742    }
743}
744
745impl fmt::Display for DataDef {
746    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
747        write!(f, "{}data ${} = ", self.linkage, self.name)?;
748
749        if let Some(align) = self.align {
750            write!(f, "align {align} ")?;
751        }
752        write!(
753            f,
754            "{{ {} }}",
755            self.items
756                .iter()
757                .map(|(ty, item)| format!("{ty} {item}"))
758                .collect::<Vec<String>>()
759                .join(", ")
760        )
761    }
762}
763
764/// Data definition item
765#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
766pub enum DataItem {
767    /// Symbol and offset
768    Symbol(String, Option<u64>),
769    /// String
770    Str(String),
771    /// Constant
772    Const(u64),
773    /// Zero-initialized data of specified size
774    Zero(u64),
775}
776
777impl fmt::Display for DataItem {
778    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
779        match self {
780            Self::Symbol(name, offset) => match offset {
781                Some(off) => write!(f, "${name} +{off}"),
782                None => write!(f, "${name}"),
783            },
784            Self::Str(string) => write!(f, "\"{string}\""),
785            Self::Const(val) => write!(f, "{val}"),
786            Self::Zero(size) => write!(f, "z {size}"),
787        }
788    }
789}
790
791/// QBE aggregate type definition
792#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
793pub enum TypeDef {
794    Regular {
795        ident: String,
796        align: Option<u64>,
797        items: Vec<(Type, usize)>,
798    },
799    Union {
800        ident: String,
801        align: Option<u64>,
802        variations: Vec<Vec<(Type, usize)>>,
803    },
804    Opaque {
805        ident: String,
806        align: u64,
807        size: u64,
808    },
809}
810
811impl TypeDef {
812    pub fn ident(&self) -> &str {
813        match self {
814            TypeDef::Regular { ident, .. } => ident,
815            TypeDef::Union { ident, .. } => ident,
816            TypeDef::Opaque { ident, .. } => ident,
817        }
818    }
819}
820
821impl fmt::Display for TypeDef {
822    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
823        write!(f, "type :{} = ", self.ident())?;
824
825        let align = match self {
826            TypeDef::Regular { align, .. } => *align,
827            TypeDef::Union { align, .. } => *align,
828            TypeDef::Opaque { align, .. } => Some(*align),
829        };
830
831        if let Some(align) = align {
832            write!(f, "align {align} ")?;
833        }
834
835        fn format(items: &[(Type, usize)]) -> String {
836            items
837                .iter()
838                .map(|(ty, count)| {
839                    if *count > 1 {
840                        format!("{ty} {count}")
841                    } else {
842                        format!("{ty}")
843                    }
844                })
845                .collect::<Vec<String>>()
846                .join(", ")
847        }
848
849        match self {
850            TypeDef::Regular { items, .. } => {
851                write!(f, "{{ {} }}", format(items))
852            }
853            TypeDef::Union { variations, .. } => write!(
854                f,
855                "{{ {} }}",
856                variations
857                    .iter()
858                    .map(|items| format!("{{ {} }}", format(items)))
859                    .collect::<Vec<_>>()
860                    .join(" ")
861            ),
862            TypeDef::Opaque { size, .. } => write!(f, "{{ {size} }}"),
863        }
864    }
865}
866
867/// An IR statement
868#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
869pub enum Statement {
870    Assign(Value, Type, Instr),
871    Volatile(Instr),
872}
873
874impl fmt::Display for Statement {
875    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
876        match self {
877            Self::Assign(temp, ty, instr) => {
878                assert!(
879                    matches!(temp, Value::Temporary(_)),
880                    "assignment target must be a temporary, got {temp:?}"
881                );
882                write!(f, "{temp} ={ty} {instr}")
883            }
884            Self::Volatile(instr) => write!(f, "{instr}"),
885        }
886    }
887}
888
889/// A block of QBE instructions with a label.
890///
891/// Blocks are the basic units of control flow in QBE. Each block has a label
892/// that can be the target of jumps, and contains a sequence of instructions.
893/// A block typically ends with a control flow instruction like jump or return.
894///
895/// # Examples
896///
897/// ```rust
898/// use qbe::{Block, BlockItem, Instr, Statement, Type, Value};
899///
900/// // Create a block for a loop body
901/// let mut block = Block {
902///     label: "loop".to_string(),
903///     items: Vec::new(),
904/// };
905///
906/// // Add a helpful comment
907/// block.add_comment("Loop body - increment counter and accumulate sum");
908///
909/// // Increment loop counter: %i = %i + 1
910/// block.assign_instr(
911///     Value::Temporary("i".to_string()),
912///     Type::Word,
913///     Instr::Add(
914///         Value::Temporary("i".to_string()),
915///         Value::Const(1),
916///     ),
917/// );
918///
919/// // Update sum: %sum = %sum + %value
920/// block.assign_instr(
921///     Value::Temporary("sum".to_string()),
922///     Type::Word,
923///     Instr::Add(
924///         Value::Temporary("sum".to_string()),
925///         Value::Temporary("value".to_string()),
926///     ),
927/// );
928///
929/// // Jump to condition check block
930/// block.add_instr(Instr::Jmp("cond".to_string()));
931///
932/// // Check if block ends with a jump (it does)
933/// assert!(block.jumps());
934/// ```
935#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Default)]
936pub struct Block {
937    /// Label before the block
938    pub label: String,
939
940    /// A list of statements in the block
941    pub items: Vec<BlockItem>,
942}
943
944/// See [`Block::items`];
945#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
946pub enum BlockItem {
947    Statement(Statement),
948    Comment(String),
949}
950
951impl fmt::Display for BlockItem {
952    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
953        match self {
954            Self::Statement(stmt) => write!(f, "{stmt}"),
955            Self::Comment(comment) => write!(f, "# {comment}"),
956        }
957    }
958}
959
960impl Block {
961    pub fn add_comment(&mut self, contents: impl Into<String>) {
962        self.items.push(BlockItem::Comment(contents.into()));
963    }
964
965    /// Adds a new instruction to the block
966    pub fn add_instr(&mut self, instr: Instr) {
967        self.items
968            .push(BlockItem::Statement(Statement::Volatile(instr)));
969    }
970
971    /// Adds a new instruction assigned to a temporary
972    pub fn assign_instr(&mut self, temp: Value, ty: Type, instr: Instr) {
973        let final_type = match instr {
974            Instr::Call(_, _, _) => ty,
975            _ => ty.into_base(),
976        };
977
978        self.items.push(BlockItem::Statement(Statement::Assign(
979            temp, final_type, instr,
980        )));
981    }
982
983    /// Returns true if the block's last instruction is a jump
984    pub fn jumps(&self) -> bool {
985        let last = self.items.last();
986
987        if let Some(BlockItem::Statement(Statement::Volatile(instr))) = last {
988            matches!(instr, Instr::Ret(_) | Instr::Jmp(_) | Instr::Jnz(..))
989        } else {
990            false
991        }
992    }
993}
994
995impl fmt::Display for Block {
996    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
997        writeln!(f, "@{}", self.label)?;
998
999        write!(
1000            f,
1001            "{}",
1002            self.items
1003                .iter()
1004                .map(|instr| format!("\t{instr}"))
1005                .collect::<Vec<String>>()
1006                .join("\n")
1007        )
1008    }
1009}
1010
1011/// A QBE function definition.
1012///
1013/// A function consists of a name, linkage information, arguments, return type,
1014/// and a collection of blocks containing the function's implementation.
1015///
1016/// # Examples
1017///
1018/// ```rust
1019/// use qbe::{Function, Linkage, Type, Value, Instr, Cmp};
1020///
1021/// // Create a function that checks if a number is even
1022/// let mut is_even = Function::new(
1023///     Linkage::public(),
1024///     "is_even",
1025///     vec![(Type::Word, Value::Temporary("n".to_string()))],
1026///     Some(Type::Word), // Returns 1 if even, 0 if odd
1027/// );
1028///
1029/// // Add the start block
1030/// let mut start = is_even.add_block("start");
1031///
1032/// // Calculate n % 2 (by using n & 1)
1033/// start.assign_instr(
1034///     Value::Temporary("remainder".to_string()),
1035///     Type::Word,
1036///     Instr::And(
1037///         Value::Temporary("n".to_string()),
1038///         Value::Const(1),
1039///     ),
1040/// );
1041///
1042/// // Check if remainder is 0 (even number)
1043/// start.assign_instr(
1044///     Value::Temporary("is_zero".to_string()),
1045///     Type::Word,
1046///     Instr::Cmp(
1047///         Type::Word,
1048///         Cmp::Eq,
1049///         Value::Temporary("remainder".to_string()),
1050///         Value::Const(0),
1051///     ),
1052/// );
1053///
1054/// // Return the result
1055/// start.add_instr(Instr::Ret(Some(Value::Temporary("is_zero".to_string()))));
1056/// ```
1057#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Default)]
1058pub struct Function {
1059    /// Function's linkage
1060    pub linkage: Linkage,
1061
1062    /// Function name
1063    pub name: String,
1064
1065    /// Function arguments
1066    pub arguments: Vec<(Type, Value)>,
1067
1068    /// Return type
1069    pub return_ty: Option<Type>,
1070
1071    /// Labelled blocks
1072    pub blocks: Vec<Block>,
1073}
1074
1075impl Function {
1076    /// Instantiates an empty function and returns it
1077    pub fn new(
1078        linkage: Linkage,
1079        name: impl Into<String>,
1080        arguments: Vec<(Type, Value)>,
1081        return_ty: Option<Type>,
1082    ) -> Self {
1083        Function {
1084            linkage,
1085            name: name.into(),
1086            arguments,
1087            return_ty,
1088            blocks: Vec::new(),
1089        }
1090    }
1091
1092    /// Adds a new empty block with a specified label and returns a reference to it
1093    pub fn add_block(&mut self, label: impl Into<String>) -> &mut Block {
1094        self.blocks.push(Block {
1095            label: label.into(),
1096            items: Vec::new(),
1097        });
1098        self.blocks.last_mut().unwrap()
1099    }
1100
1101    /// Returns a reference to the last block
1102    #[deprecated(
1103        since = "3.0.0",
1104        note = "Use `self.blocks.last()` or `self.blocks.last_mut()` instead."
1105    )]
1106    pub fn last_block(&mut self) -> &Block {
1107        self.blocks
1108            .last()
1109            .expect("Function must have at least one block")
1110    }
1111
1112    /// Adds a new instruction to the last block.
1113    ///
1114    /// # Panics
1115    ///
1116    /// Panics if the function has no blocks.
1117    pub fn add_instr(&mut self, instr: Instr) {
1118        self.blocks
1119            .last_mut()
1120            .expect("Last block must be present")
1121            .add_instr(instr);
1122    }
1123
1124    /// Adds a new instruction assigned to a temporary.
1125    ///
1126    /// # Panics
1127    ///
1128    /// Panics if the function has no blocks.
1129    pub fn assign_instr(&mut self, temp: Value, ty: Type, instr: Instr) {
1130        self.blocks
1131            .last_mut()
1132            .expect("Last block must be present")
1133            .assign_instr(temp, ty, instr);
1134    }
1135}
1136
1137impl fmt::Display for Function {
1138    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1139        write!(f, "{}function", self.linkage)?;
1140        if let Some(ty) = &self.return_ty {
1141            write!(f, " {ty}")?;
1142        }
1143
1144        writeln!(
1145            f,
1146            " ${name}({args}) {{",
1147            name = self.name,
1148            args = self
1149                .arguments
1150                .iter()
1151                .map(|(ty, temp)| format!("{ty} {temp}"))
1152                .collect::<Vec<String>>()
1153                .join(", "),
1154        )?;
1155
1156        for blk in self.blocks.iter() {
1157            writeln!(f, "{blk}")?;
1158        }
1159
1160        write!(f, "}}")
1161    }
1162}
1163
1164/// Linkage of a function or data defintion (e.g. section and
1165/// private/public status)
1166#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Default)]
1167pub struct Linkage {
1168    /// Specifies whether the target is going to be accessible publicly
1169    pub exported: bool,
1170
1171    /// Specifies target's section
1172    pub section: Option<String>,
1173
1174    /// Specifies target's section flags
1175    pub secflags: Option<String>,
1176
1177    /// Specifies whether the target is stored in thread-local storage
1178    pub thread_local: bool,
1179}
1180
1181impl Linkage {
1182    /// Returns the default configuration for private linkage
1183    pub fn private() -> Linkage {
1184        Linkage {
1185            exported: false,
1186            section: None,
1187            secflags: None,
1188            thread_local: false,
1189        }
1190    }
1191
1192    /// Returns the configuration for private linkage with a provided section
1193    pub fn private_with_section(section: impl Into<String>) -> Linkage {
1194        Linkage {
1195            exported: false,
1196            section: Some(section.into()),
1197            secflags: None,
1198            thread_local: false,
1199        }
1200    }
1201
1202    /// Returns the default configuration for public linkage
1203    pub fn public() -> Linkage {
1204        Linkage {
1205            exported: true,
1206            section: None,
1207            secflags: None,
1208            thread_local: false,
1209        }
1210    }
1211
1212    /// Returns the configuration for public linkage with a provided section
1213    pub fn public_with_section(section: impl Into<String>) -> Linkage {
1214        Linkage {
1215            exported: true,
1216            section: Some(section.into()),
1217            secflags: None,
1218            thread_local: false,
1219        }
1220    }
1221
1222    pub fn thread_local() -> Linkage {
1223        Linkage {
1224            exported: false,
1225            thread_local: true,
1226            section: None,
1227            secflags: None,
1228        }
1229    }
1230
1231    pub fn exported_thread_local() -> Linkage {
1232        Linkage {
1233            exported: true,
1234            thread_local: true,
1235            section: None,
1236            secflags: None,
1237        }
1238    }
1239
1240    pub fn thread_local_with_section(section: impl Into<String>) -> Linkage {
1241        Linkage {
1242            exported: false,
1243            thread_local: true,
1244            section: Some(section.into()),
1245            secflags: None,
1246        }
1247    }
1248}
1249
1250impl fmt::Display for Linkage {
1251    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1252        if self.exported {
1253            write!(f, "export ")?;
1254        }
1255        if self.thread_local {
1256            write!(f, "thread ")?;
1257        }
1258        if let Some(section) = &self.section {
1259            // TODO: escape it, possibly
1260            write!(f, "section \"{section}\"")?;
1261            if let Some(secflags) = &self.secflags {
1262                write!(f, " \"{secflags}\"")?;
1263            }
1264            write!(f, " ")?;
1265        }
1266
1267        Ok(())
1268    }
1269}
1270
1271/// A complete QBE IL module.
1272///
1273/// A module contains all the functions, data definitions, and type definitions
1274/// that make up a QBE IL file. When converted to a string, it produces valid
1275/// QBE IL code that can be compiled by QBE.
1276///
1277/// # Examples
1278///
1279/// ```rust
1280/// use qbe::{Module, Function, DataDef, TypeDef, Linkage, Type, Value, Instr, DataItem};
1281///
1282/// // Create a new module
1283/// let mut module = Module::new();
1284///
1285/// // Add a string constant
1286/// let hello_str = DataDef::new(
1287///     Linkage::private(),
1288///     "hello",
1289///     None,
1290///     vec![
1291///         (Type::Byte, DataItem::Str("Hello, World!\n".to_string())),
1292///         (Type::Byte, DataItem::Const(0)), // Null terminator
1293///     ],
1294/// );
1295/// module.add_data(hello_str);
1296///
1297/// // Add a main function that prints the string
1298/// let mut main = Function::new(
1299///     Linkage::public(),
1300///     "main",
1301///     vec![],
1302///     Some(Type::Word),
1303/// );
1304///
1305/// let mut start = main.add_block("start");
1306///
1307/// // Call printf with the string: %r = call $printf(l $hello)
1308/// start.assign_instr(
1309///     Value::Temporary("r".to_string()),
1310///     Type::Word,
1311///     Instr::Call(
1312///         "printf".to_string(),
1313///         vec![(Type::Long, Value::Global("hello".to_string()))],
1314///         None,
1315///     ),
1316/// );
1317///
1318/// // Return 0
1319/// start.add_instr(Instr::Ret(Some(Value::Const(0))));
1320///
1321/// // Add the function to the module
1322/// module.add_function(main);
1323/// ```
1324#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Default)]
1325pub struct Module {
1326    pub functions: Vec<Function>,
1327    pub types: Vec<Arc<TypeDef>>,
1328    pub data: Vec<DataDef>,
1329}
1330
1331impl Module {
1332    /// Creates a new module
1333    pub fn new() -> Module {
1334        Module {
1335            functions: Vec::new(),
1336            types: Vec::new(),
1337            data: Vec::new(),
1338        }
1339    }
1340
1341    /// Adds a function to the module, returning a reference to it for later
1342    /// modification
1343    pub fn add_function(&mut self, func: Function) -> &mut Function {
1344        self.functions.push(func);
1345        self.functions.last_mut().unwrap()
1346    }
1347
1348    /// Adds a type definition to the module
1349    pub fn add_type(&mut self, def: Arc<TypeDef>) {
1350        self.types.push(def);
1351    }
1352
1353    /// Adds a data definition to the module
1354    pub fn add_data(&mut self, data: DataDef) -> &mut DataDef {
1355        self.data.push(data);
1356        self.data.last_mut().unwrap()
1357    }
1358}
1359
1360impl fmt::Display for Module {
1361    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1362        for ty in self.types.iter() {
1363            writeln!(f, "{ty}")?;
1364        }
1365        for func in self.functions.iter() {
1366            writeln!(f, "{func}")?;
1367        }
1368        for data in self.data.iter() {
1369            writeln!(f, "{data}")?;
1370        }
1371        Ok(())
1372    }
1373}