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