-
Notifications
You must be signed in to change notification settings - Fork 5
feat: the C AST used by the C codegen backend #6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
a0b8c5f
feat: add rustc_codegen_c_ast crate for C AST handling
Wybxc acff3c9
feat: refactor pretty printing to use trait-based approach
Wybxc c3d19da
feat: refactor type printing for more correctness
Wybxc 836a363
chore: add doc for C AST crate
Wybxc 635aec6
chore: remove unnecessary doc comment
Wybxc 4fbf926
feat: add tests for the ast
Wybxc ebf6f68
fix: code format error
Wybxc b57e10c
chore: update docs
Wybxc e0e973b
chore: remove unused codes
Wybxc 52515f7
chore: add more docs for pprint
Wybxc c47613f
chore: add trailing new line at the end of test output
Wybxc 2860dc4
feat: refactor C types structure
Wybxc File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
[workspace] | ||
resolver = "2" | ||
members = ["rustc_codegen_c"] | ||
members = ["rustc_codegen_c", "rustc_codegen_c_ast"] | ||
|
||
[workspace.package] | ||
version = "0.1.0" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
[package] | ||
name = "rustc_codegen_c_ast" | ||
edition = "2021" | ||
version.workspace = true | ||
|
||
[dependencies] | ||
|
||
# This package uses rustc crates. | ||
[package.metadata.rust-analyzer] | ||
rustc_private = true |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
//! This module defines the memory arena for C AST nodes. | ||
|
||
use crate::decl::CDeclKind; | ||
use crate::expr::CExprKind; | ||
use crate::func::CFuncKind; | ||
use crate::stmt::CStmtKind; | ||
use crate::ty::CTyKind; | ||
|
||
rustc_arena::declare_arena!([ | ||
[] decl: CDeclKind<'tcx>, | ||
[] expr: CExprKind<'tcx>, | ||
[] func: CFuncKind<'tcx>, | ||
[] stmt: CStmtKind<'tcx>, | ||
[] ty: CTyKind<'tcx>, | ||
]); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
//! This module defines AST nodes for C declarations. | ||
|
||
use crate::expr::{CExpr, CValue}; | ||
use crate::pretty::{Print, PrinterCtx, INDENT}; | ||
use crate::ty::{print_declarator, CTy}; | ||
use crate::ModuleCtx; | ||
|
||
/// C declarations. | ||
pub type CDecl<'mx> = &'mx CDeclKind<'mx>; | ||
|
||
/// C declaration kinds. | ||
#[derive(Debug, Clone)] | ||
pub enum CDeclKind<'mx> { | ||
/// Variable declaration consisting of a name, type, and optional initializer. | ||
/// | ||
/// Example: | ||
/// - `int foo;` // `ty val` | ||
/// - `int foo = bar` `ty val = expr` | ||
Var { name: CValue<'mx>, ty: CTy<'mx>, init: Option<CExpr<'mx>> }, | ||
} | ||
|
||
impl<'mx> ModuleCtx<'mx> { | ||
/// Create a new declaration. | ||
pub fn decl(self, decl: CDeclKind<'mx>) -> CDecl<'mx> { | ||
self.arena().alloc(decl) | ||
} | ||
|
||
/// Create a new variable declaration. | ||
pub fn var(self, name: CValue<'mx>, ty: CTy<'mx>, init: Option<CExpr<'mx>>) -> CDecl<'mx> { | ||
self.decl(CDeclKind::Var { name, ty, init }) | ||
} | ||
} | ||
|
||
impl Print for CDecl<'_> { | ||
fn print_to(&self, ctx: &mut PrinterCtx) { | ||
match self { | ||
CDeclKind::Var { name, ty, init } => { | ||
ctx.ibox(INDENT, |ctx| { | ||
print_declarator(*ty, Some(*name), ctx); | ||
if let Some(init) = init { | ||
ctx.word(" ="); | ||
ctx.softbreak(); | ||
init.print_to(ctx); | ||
} | ||
ctx.word(";"); | ||
}); | ||
} | ||
} | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
//! This module defines the AST nodes for C expressions. | ||
|
||
use crate::pretty::{Print, PrinterCtx, INDENT}; | ||
use crate::ty::{print_declarator, CTy}; | ||
use crate::ModuleCtx; | ||
|
||
/// Represents the values of C variables, parameters, and scalars. | ||
/// | ||
/// There are two variants to distinguish between constants and variables, | ||
/// as is done in LLVM IR. We follow the `rustc_codegen_ssa` convention for this representation. | ||
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] | ||
pub enum CValue<'mx> { | ||
/// A constant scalar | ||
Scalar(i128), | ||
/// A local variable indexed by a number, in the form `_0`, `_1`, etc. | ||
Local(usize), | ||
/// A function name | ||
Func(&'mx str), | ||
} | ||
|
||
/// C expressions. | ||
pub type CExpr<'mx> = &'mx CExprKind<'mx>; | ||
|
||
/// C expressions. | ||
#[derive(Debug, Clone)] | ||
pub enum CExprKind<'mx> { | ||
/// A "raw" C expression, simply a string of C code, which is printed as-is. | ||
Raw(&'static str), | ||
/// A value, such as a constant, variable, or function name. | ||
Value(CValue<'mx>), | ||
/// A binary operation expression, e.g. `lhs + rhs`. | ||
Binary { lhs: CExpr<'mx>, rhs: CExpr<'mx>, op: &'static str }, | ||
/// A type cast expression, e.g. `(int) x`. | ||
Cast { ty: CTy<'mx>, expr: CExpr<'mx> }, | ||
/// A function call expression, e.g. `foo(x, y)`. | ||
Call { callee: CExpr<'mx>, args: Vec<CExpr<'mx>> }, | ||
/// A member access expression, e.g. `foo.bar` or `foo->bar`. | ||
Member { | ||
expr: CExpr<'mx>, | ||
/// Whether to use the `->` operator instead of `.`. | ||
arrow: bool, | ||
field: &'mx str, | ||
}, | ||
} | ||
|
||
impl<'mx> ModuleCtx<'mx> { | ||
/// Create a new expression. | ||
pub fn expr(&self, expr: CExprKind<'mx>) -> CExpr<'mx> { | ||
self.arena().alloc(expr) | ||
} | ||
|
||
/// Create a new raw expression. | ||
pub fn raw(&self, raw: &'static str) -> CExpr<'mx> { | ||
self.expr(CExprKind::Raw(raw)) | ||
} | ||
|
||
/// Create a new value expression. | ||
pub fn value(&self, value: CValue<'mx>) -> CExpr<'mx> { | ||
self.expr(CExprKind::Value(value)) | ||
} | ||
|
||
/// Create a new binary expression. | ||
pub fn binary(&self, lhs: CExpr<'mx>, rhs: CExpr<'mx>, op: &'static str) -> CExpr<'mx> { | ||
self.expr(CExprKind::Binary { lhs, rhs, op }) | ||
} | ||
|
||
/// Create a new cast expression. | ||
pub fn cast(&self, ty: CTy<'mx>, expr: CExpr<'mx>) -> CExpr<'mx> { | ||
self.expr(CExprKind::Cast { ty, expr }) | ||
} | ||
|
||
/// Create a new function call expression. | ||
pub fn call(&self, callee: CExpr<'mx>, args: Vec<CExpr<'mx>>) -> CExpr<'mx> { | ||
self.expr(CExprKind::Call { callee, args }) | ||
} | ||
|
||
/// Create a new member access expression. | ||
pub fn member(&self, expr: CExpr<'mx>, field: &'mx str) -> CExpr<'mx> { | ||
self.expr(CExprKind::Member { expr, field, arrow: false }) | ||
} | ||
} | ||
|
||
impl Print for CValue<'_> { | ||
fn print_to(&self, ctx: &mut PrinterCtx) { | ||
match self { | ||
CValue::Scalar(i) => ctx.word(i.to_string()), | ||
CValue::Local(i) => ctx.word(format!("_{}", i)), | ||
CValue::Func(name) => ctx.word(name.to_string()), | ||
} | ||
} | ||
} | ||
|
||
impl Print for CExpr<'_> { | ||
fn print_to(&self, ctx: &mut PrinterCtx) { | ||
match self { | ||
CExprKind::Raw(raw) => ctx.word(*raw), | ||
CExprKind::Value(value) => value.print_to(ctx), | ||
CExprKind::Binary { lhs, rhs, op } => ctx.ibox_delim(INDENT, ("(", ")"), 0, |ctx| { | ||
ctx.ibox(-INDENT, |ctx| lhs.print_to(ctx)); | ||
|
||
ctx.softbreak(); | ||
ctx.word(*op); | ||
ctx.nbsp(); | ||
|
||
rhs.print_to(ctx); | ||
}), | ||
CExprKind::Cast { ty, expr } => ctx.ibox(INDENT, |ctx| { | ||
ctx.word("("); | ||
print_declarator(*ty, None, ctx); | ||
ctx.word(")"); | ||
|
||
ctx.nbsp(); | ||
expr.print_to(ctx); | ||
}), | ||
CExprKind::Call { callee, args } => ctx.ibox(INDENT, |ctx| { | ||
callee.print_to(ctx); | ||
ctx.cbox_delim(INDENT, ("(", ")"), 0, |ctx| { | ||
ctx.seperated(",", args, |ctx, arg| arg.print_to(ctx)); | ||
}); | ||
}), | ||
CExprKind::Member { expr, arrow, field } => ctx.cbox(INDENT, |ctx| { | ||
expr.print_to(ctx); | ||
ctx.zerobreak(); | ||
if *arrow { | ||
ctx.word("->"); | ||
} else { | ||
ctx.word("."); | ||
} | ||
ctx.word(field.to_string()); | ||
}), | ||
} | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
//! This module defines AST nodes for C functions. | ||
|
||
use std::cell::{Cell, RefCell}; | ||
|
||
use rustc_data_structures::intern::Interned; | ||
|
||
use crate::expr::CValue; | ||
use crate::pretty::{Print, PrinterCtx}; | ||
use crate::stmt::{print_compound, CStmt}; | ||
use crate::ty::{print_declarator, CTy}; | ||
use crate::ModuleCtx; | ||
|
||
/// C functions definition. | ||
pub type CFunc<'mx> = Interned<'mx, CFuncKind<'mx>>; | ||
|
||
/// C function definition. | ||
#[derive(Debug, Clone)] | ||
pub struct CFuncKind<'mx> { | ||
/// Function name. | ||
pub name: &'mx str, | ||
/// Return type. | ||
pub ty: CTy<'mx>, | ||
/// Function parameters. | ||
pub params: Vec<(CTy<'mx>, CValue<'mx>)>, | ||
/// Function body. | ||
pub body: RefCell<Vec<CStmt<'mx>>>, | ||
/// A counter for local variables, for generating unique names. | ||
local_var_counter: Cell<usize>, | ||
} | ||
|
||
impl<'mx> CFuncKind<'mx> { | ||
/// Make a new function definition. | ||
pub fn new(name: &'mx str, ty: CTy<'mx>, params: impl IntoIterator<Item = CTy<'mx>>) -> Self { | ||
let params = params | ||
.into_iter() | ||
.enumerate() | ||
.map(|(i, ty)| (ty, CValue::Local(i))) | ||
.collect::<Vec<_>>(); | ||
let local_var_counter = Cell::new(params.len()); | ||
|
||
Self { name, ty, params, body: RefCell::new(Vec::new()), local_var_counter } | ||
} | ||
|
||
/// Push a statement to the end of the function body. | ||
pub fn push_stmt(&self, stmt: CStmt<'mx>) { | ||
self.body.borrow_mut().push(stmt); | ||
} | ||
|
||
/// Get a new unique local variable. | ||
pub fn next_local_var(&self) -> CValue { | ||
let val = CValue::Local(self.local_var_counter.get()); | ||
self.local_var_counter.set(self.local_var_counter.get() + 1); | ||
val | ||
} | ||
} | ||
|
||
impl<'mx> ModuleCtx<'mx> { | ||
/// Create a new function definition. | ||
pub fn func(&self, func: CFuncKind<'mx>) -> &'mx CFuncKind<'mx> { | ||
self.arena().alloc(func) | ||
} | ||
} | ||
|
||
impl Print for CFunc<'_> { | ||
fn print_to(&self, ctx: &mut PrinterCtx) { | ||
ctx.ibox(0, |ctx| { | ||
print_signature(*self, ctx); | ||
ctx.softbreak(); // I don't know how to avoid a newline here | ||
print_compound(&self.0.body.borrow(), ctx); | ||
}) | ||
} | ||
} | ||
|
||
pub(crate) fn print_func_decl(func: CFunc, ctx: &mut PrinterCtx) { | ||
print_signature(func, ctx); | ||
ctx.word(";"); | ||
} | ||
|
||
fn print_signature(func: CFunc, ctx: &mut PrinterCtx) { | ||
ctx.ibox(0, |ctx| { | ||
print_declarator(func.0.ty, Some(CValue::Func(func.0.name)), ctx); | ||
|
||
ctx.valign_delim(("(", ")"), |ctx| { | ||
ctx.seperated(",", &func.0.params, |ctx, (ty, name)| { | ||
ctx.ibox(0, |ctx| { | ||
print_declarator(*ty, Some(*name), ctx); | ||
}) | ||
}) | ||
}); | ||
}); | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.