Skip to content

Commit aeb8fe5

Browse files
authored
feat: the C AST used by the C codegen backend (#6)
1 parent e932f3e commit aeb8fe5

35 files changed

+1235
-1
lines changed

bootstrap/src/test.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@ impl Run for TestCommand {
2222
cprintln!("<r,s>Test failed</r,s>: {}", info);
2323
}));
2424

25+
cprintln!("<b>[TEST]</b> running cargo test");
26+
let mut command = std::process::Command::new("cargo");
27+
command.args(["test", "--manifest-path", "crates/Cargo.toml"]);
28+
log::debug!("running {:?}", command);
29+
assert!(command.status().unwrap().success(), "failed to run {:?}", command);
30+
2531
let testcases = self.collect_testcases(manifest);
2632
cprintln!("<b>[TEST]</b> found {} testcases", testcases.len());
2733

crates/Cargo.lock

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[workspace]
22
resolver = "2"
3-
members = ["rustc_codegen_c"]
3+
members = ["rustc_codegen_c", "rustc_codegen_c_ast"]
44

55
[workspace.package]
66
version = "0.1.0"

crates/rustc_codegen_c_ast/Cargo.toml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[package]
2+
name = "rustc_codegen_c_ast"
3+
edition = "2021"
4+
version.workspace = true
5+
6+
[dependencies]
7+
8+
# This package uses rustc crates.
9+
[package.metadata.rust-analyzer]
10+
rustc_private = true
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//! This module defines the memory arena for C AST nodes.
2+
3+
use crate::decl::CDeclKind;
4+
use crate::expr::CExprKind;
5+
use crate::func::CFuncKind;
6+
use crate::stmt::CStmtKind;
7+
use crate::ty::CTyKind;
8+
9+
rustc_arena::declare_arena!([
10+
[] decl: CDeclKind<'tcx>,
11+
[] expr: CExprKind<'tcx>,
12+
[] func: CFuncKind<'tcx>,
13+
[] stmt: CStmtKind<'tcx>,
14+
[] ty: CTyKind<'tcx>,
15+
]);
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
//! This module defines AST nodes for C declarations.
2+
3+
use crate::expr::{CExpr, CValue};
4+
use crate::pretty::{Print, PrinterCtx, INDENT};
5+
use crate::ty::{print_declarator, CTy};
6+
use crate::ModuleCtx;
7+
8+
/// C declarations.
9+
pub type CDecl<'mx> = &'mx CDeclKind<'mx>;
10+
11+
/// C declaration kinds.
12+
#[derive(Debug, Clone)]
13+
pub enum CDeclKind<'mx> {
14+
/// Variable declaration consisting of a name, type, and optional initializer.
15+
///
16+
/// Example:
17+
/// - `int foo;` // `ty val`
18+
/// - `int foo = bar` `ty val = expr`
19+
Var { name: CValue<'mx>, ty: CTy<'mx>, init: Option<CExpr<'mx>> },
20+
}
21+
22+
impl<'mx> ModuleCtx<'mx> {
23+
/// Create a new declaration.
24+
pub fn decl(self, decl: CDeclKind<'mx>) -> CDecl<'mx> {
25+
self.arena().alloc(decl)
26+
}
27+
28+
/// Create a new variable declaration.
29+
pub fn var(self, name: CValue<'mx>, ty: CTy<'mx>, init: Option<CExpr<'mx>>) -> CDecl<'mx> {
30+
self.decl(CDeclKind::Var { name, ty, init })
31+
}
32+
}
33+
34+
impl Print for CDecl<'_> {
35+
fn print_to(&self, ctx: &mut PrinterCtx) {
36+
match self {
37+
CDeclKind::Var { name, ty, init } => {
38+
ctx.ibox(INDENT, |ctx| {
39+
print_declarator(*ty, Some(*name), ctx);
40+
if let Some(init) = init {
41+
ctx.word(" =");
42+
ctx.softbreak();
43+
init.print_to(ctx);
44+
}
45+
ctx.word(";");
46+
});
47+
}
48+
}
49+
}
50+
}
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
//! This module defines the AST nodes for C expressions.
2+
3+
use crate::pretty::{Print, PrinterCtx, INDENT};
4+
use crate::ty::{print_declarator, CTy};
5+
use crate::ModuleCtx;
6+
7+
/// Represents the values of C variables, parameters, and scalars.
8+
///
9+
/// There are two variants to distinguish between constants and variables,
10+
/// as is done in LLVM IR. We follow the `rustc_codegen_ssa` convention for this representation.
11+
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
12+
pub enum CValue<'mx> {
13+
/// A constant scalar
14+
Scalar(i128),
15+
/// A local variable indexed by a number, in the form `_0`, `_1`, etc.
16+
Local(usize),
17+
/// A function name
18+
Func(&'mx str),
19+
}
20+
21+
/// C expressions.
22+
pub type CExpr<'mx> = &'mx CExprKind<'mx>;
23+
24+
/// C expressions.
25+
#[derive(Debug, Clone)]
26+
pub enum CExprKind<'mx> {
27+
/// A "raw" C expression, simply a string of C code, which is printed as-is.
28+
Raw(&'static str),
29+
/// A value, such as a constant, variable, or function name.
30+
Value(CValue<'mx>),
31+
/// A binary operation expression, e.g. `lhs + rhs`.
32+
Binary { lhs: CExpr<'mx>, rhs: CExpr<'mx>, op: &'static str },
33+
/// A type cast expression, e.g. `(int) x`.
34+
Cast { ty: CTy<'mx>, expr: CExpr<'mx> },
35+
/// A function call expression, e.g. `foo(x, y)`.
36+
Call { callee: CExpr<'mx>, args: Vec<CExpr<'mx>> },
37+
/// A member access expression, e.g. `foo.bar` or `foo->bar`.
38+
Member {
39+
expr: CExpr<'mx>,
40+
/// Whether to use the `->` operator instead of `.`.
41+
arrow: bool,
42+
field: &'mx str,
43+
},
44+
}
45+
46+
impl<'mx> ModuleCtx<'mx> {
47+
/// Create a new expression.
48+
pub fn expr(&self, expr: CExprKind<'mx>) -> CExpr<'mx> {
49+
self.arena().alloc(expr)
50+
}
51+
52+
/// Create a new raw expression.
53+
pub fn raw(&self, raw: &'static str) -> CExpr<'mx> {
54+
self.expr(CExprKind::Raw(raw))
55+
}
56+
57+
/// Create a new value expression.
58+
pub fn value(&self, value: CValue<'mx>) -> CExpr<'mx> {
59+
self.expr(CExprKind::Value(value))
60+
}
61+
62+
/// Create a new binary expression.
63+
pub fn binary(&self, lhs: CExpr<'mx>, rhs: CExpr<'mx>, op: &'static str) -> CExpr<'mx> {
64+
self.expr(CExprKind::Binary { lhs, rhs, op })
65+
}
66+
67+
/// Create a new cast expression.
68+
pub fn cast(&self, ty: CTy<'mx>, expr: CExpr<'mx>) -> CExpr<'mx> {
69+
self.expr(CExprKind::Cast { ty, expr })
70+
}
71+
72+
/// Create a new function call expression.
73+
pub fn call(&self, callee: CExpr<'mx>, args: Vec<CExpr<'mx>>) -> CExpr<'mx> {
74+
self.expr(CExprKind::Call { callee, args })
75+
}
76+
77+
/// Create a new member access expression.
78+
pub fn member(&self, expr: CExpr<'mx>, field: &'mx str) -> CExpr<'mx> {
79+
self.expr(CExprKind::Member { expr, field, arrow: false })
80+
}
81+
}
82+
83+
impl Print for CValue<'_> {
84+
fn print_to(&self, ctx: &mut PrinterCtx) {
85+
match self {
86+
CValue::Scalar(i) => ctx.word(i.to_string()),
87+
CValue::Local(i) => ctx.word(format!("_{}", i)),
88+
CValue::Func(name) => ctx.word(name.to_string()),
89+
}
90+
}
91+
}
92+
93+
impl Print for CExpr<'_> {
94+
fn print_to(&self, ctx: &mut PrinterCtx) {
95+
match self {
96+
CExprKind::Raw(raw) => ctx.word(*raw),
97+
CExprKind::Value(value) => value.print_to(ctx),
98+
CExprKind::Binary { lhs, rhs, op } => ctx.ibox_delim(INDENT, ("(", ")"), 0, |ctx| {
99+
ctx.ibox(-INDENT, |ctx| lhs.print_to(ctx));
100+
101+
ctx.softbreak();
102+
ctx.word(*op);
103+
ctx.nbsp();
104+
105+
rhs.print_to(ctx);
106+
}),
107+
CExprKind::Cast { ty, expr } => ctx.ibox(INDENT, |ctx| {
108+
ctx.word("(");
109+
print_declarator(*ty, None, ctx);
110+
ctx.word(")");
111+
112+
ctx.nbsp();
113+
expr.print_to(ctx);
114+
}),
115+
CExprKind::Call { callee, args } => ctx.ibox(INDENT, |ctx| {
116+
callee.print_to(ctx);
117+
ctx.cbox_delim(INDENT, ("(", ")"), 0, |ctx| {
118+
ctx.seperated(",", args, |ctx, arg| arg.print_to(ctx));
119+
});
120+
}),
121+
CExprKind::Member { expr, arrow, field } => ctx.cbox(INDENT, |ctx| {
122+
expr.print_to(ctx);
123+
ctx.zerobreak();
124+
if *arrow {
125+
ctx.word("->");
126+
} else {
127+
ctx.word(".");
128+
}
129+
ctx.word(field.to_string());
130+
}),
131+
}
132+
}
133+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
//! This module defines AST nodes for C functions.
2+
3+
use std::cell::{Cell, RefCell};
4+
5+
use rustc_data_structures::intern::Interned;
6+
7+
use crate::expr::CValue;
8+
use crate::pretty::{Print, PrinterCtx};
9+
use crate::stmt::{print_compound, CStmt};
10+
use crate::ty::{print_declarator, CTy};
11+
use crate::ModuleCtx;
12+
13+
/// C functions definition.
14+
pub type CFunc<'mx> = Interned<'mx, CFuncKind<'mx>>;
15+
16+
/// C function definition.
17+
#[derive(Debug, Clone)]
18+
pub struct CFuncKind<'mx> {
19+
/// Function name.
20+
pub name: &'mx str,
21+
/// Return type.
22+
pub ty: CTy<'mx>,
23+
/// Function parameters.
24+
pub params: Vec<(CTy<'mx>, CValue<'mx>)>,
25+
/// Function body.
26+
pub body: RefCell<Vec<CStmt<'mx>>>,
27+
/// A counter for local variables, for generating unique names.
28+
local_var_counter: Cell<usize>,
29+
}
30+
31+
impl<'mx> CFuncKind<'mx> {
32+
/// Make a new function definition.
33+
pub fn new(name: &'mx str, ty: CTy<'mx>, params: impl IntoIterator<Item = CTy<'mx>>) -> Self {
34+
let params = params
35+
.into_iter()
36+
.enumerate()
37+
.map(|(i, ty)| (ty, CValue::Local(i)))
38+
.collect::<Vec<_>>();
39+
let local_var_counter = Cell::new(params.len());
40+
41+
Self { name, ty, params, body: RefCell::new(Vec::new()), local_var_counter }
42+
}
43+
44+
/// Push a statement to the end of the function body.
45+
pub fn push_stmt(&self, stmt: CStmt<'mx>) {
46+
self.body.borrow_mut().push(stmt);
47+
}
48+
49+
/// Get a new unique local variable.
50+
pub fn next_local_var(&self) -> CValue {
51+
let val = CValue::Local(self.local_var_counter.get());
52+
self.local_var_counter.set(self.local_var_counter.get() + 1);
53+
val
54+
}
55+
}
56+
57+
impl<'mx> ModuleCtx<'mx> {
58+
/// Create a new function definition.
59+
pub fn func(&self, func: CFuncKind<'mx>) -> &'mx CFuncKind<'mx> {
60+
self.arena().alloc(func)
61+
}
62+
}
63+
64+
impl Print for CFunc<'_> {
65+
fn print_to(&self, ctx: &mut PrinterCtx) {
66+
ctx.ibox(0, |ctx| {
67+
print_signature(*self, ctx);
68+
ctx.softbreak(); // I don't know how to avoid a newline here
69+
print_compound(&self.0.body.borrow(), ctx);
70+
})
71+
}
72+
}
73+
74+
pub(crate) fn print_func_decl(func: CFunc, ctx: &mut PrinterCtx) {
75+
print_signature(func, ctx);
76+
ctx.word(";");
77+
}
78+
79+
fn print_signature(func: CFunc, ctx: &mut PrinterCtx) {
80+
ctx.ibox(0, |ctx| {
81+
print_declarator(func.0.ty, Some(CValue::Func(func.0.name)), ctx);
82+
83+
ctx.valign_delim(("(", ")"), |ctx| {
84+
ctx.seperated(",", &func.0.params, |ctx, (ty, name)| {
85+
ctx.ibox(0, |ctx| {
86+
print_declarator(*ty, Some(*name), ctx);
87+
})
88+
})
89+
});
90+
});
91+
}

0 commit comments

Comments
 (0)