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