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}