1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
//! # codegen_lang
//!
//! A backend abstraction that turns the [`ir-lang`](ir_lang) intermediate representation
//! into code for a concrete target.
//!
//! A compiler's middle end hands the backend a function in SSA form; the backend lays
//! that control-flow graph out as a linear stream of instructions a machine can run.
//! This crate draws the line between the two. [`Backend`] is the trait a target
//! implements, and [`Bytecode`] is the target shipped here: a small, register-based
//! bytecode that is enough to inspect, serialize, and run generated code without pulling
//! in a native code generator. A backend built on LLVM or Cranelift slots in behind the
//! same trait, producing its own [`Output`](Backend::Output).
//!
//! ## Lowering model
//!
//! Each IR value is given its own virtual [`Reg`]ister, so an [`Op`] names its operands
//! and result by register rather than by a position on a stack. Each basic block becomes
//! a [`Label`] in a flat [op stream](Program::ops). A block's parameters are filled by
//! [`Move`](Op::Move) ops emitted on the edges that target it — the bytecode's stand-in
//! for an SSA phi node, which is how a value crosses a control-flow join. The input is
//! checked with [`Function::validate`](ir_lang::Function::validate) before lowering, so
//! a backend only ever sees well-formed SSA and the op stream it returns is faithful to
//! the function it came from.
//!
//! ## Example
//!
//! Lower `fn double(x: int) -> int { x + x }` and read the result back:
//!
//! ```
//! use codegen_lang::{compile, BinOp, Op};
//! use ir_lang::{Builder, Type};
//!
//! let mut b = Builder::new("double", &[Type::Int], Type::Int);
//! let x = b.block_params(b.entry())[0];
//! let sum = b.bin(BinOp::Add, x, x);
//! b.ret(Some(sum));
//!
//! let program = compile(&b.finish()).expect("double is well-formed");
//! assert_eq!(program.name(), "double");
//!
//! // One add, then a return of its result.
//! assert!(matches!(program.ops()[0], Op::Bin { op: BinOp::Add, .. }));
//! assert!(matches!(program.ops()[1], Op::Return { value: Some(_) }));
//! ```
//!
//! ## Features
//!
//! - `std` (default) — links the standard library. Without it the crate is `#![no_std]`
//! and needs only `alloc`.
//! - `serde` — derives `Serialize` and `Deserialize` for [`Program`] and its parts, so a
//! compiled program can be cached or moved between tools.
//!
//! ## Stability
//!
//! The public surface is frozen and stable as of `1.0.0`: it follows Semantic
//! Versioning, with no breaking changes before `2.0`. [`CodegenError`] is
//! `#[non_exhaustive]`, so a backend reporting a new kind of failure is an additive,
//! non-breaking change. The full surface and the SemVer promise are catalogued in
//! [`docs/API.md`](https://github.com/jamesgober/codegen-lang/blob/main/docs/API.md#semver-promise).
extern crate alloc;
pub use ;
pub use CodegenError;
pub use ;
// Re-exported so the operations carried by [`Op::Bin`] and [`Op::Un`] can be named and
// matched without depending on `ir-lang` directly. They are the IR's own operation
// enums, reused unchanged.
pub use ;