Skip to main content

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//! The public surface is frozen and stable as of `1.0.0`: it follows Semantic
56//! Versioning, with no breaking changes before `2.0`. [`CodegenError`] is
57//! `#[non_exhaustive]`, so a backend reporting a new kind of failure is an additive,
58//! non-breaking change. The full surface and the SemVer promise are catalogued in
59//! [`docs/API.md`](https://github.com/jamesgober/codegen-lang/blob/main/docs/API.md#semver-promise).
60
61#![cfg_attr(not(feature = "std"), no_std)]
62#![cfg_attr(docsrs, feature(doc_cfg))]
63#![deny(missing_docs)]
64#![forbid(unsafe_code)]
65#![deny(
66    clippy::unwrap_used,
67    clippy::expect_used,
68    clippy::panic,
69    clippy::todo,
70    clippy::unimplemented,
71    clippy::unreachable,
72    clippy::dbg_macro,
73    clippy::print_stdout,
74    clippy::print_stderr
75)]
76
77extern crate alloc;
78
79mod backend;
80mod error;
81mod lower;
82mod program;
83
84pub use backend::{Backend, Bytecode, compile};
85pub use error::CodegenError;
86pub use program::{Const, Label, Op, Program, Reg};
87
88// Re-exported so the operations carried by [`Op::Bin`] and [`Op::Un`] can be named and
89// matched without depending on `ir-lang` directly. They are the IR's own operation
90// enums, reused unchanged.
91pub use ir_lang::{BinOp, UnOp};