Skip to content

Commit

Permalink
rs2bril cleanup, lil documentation and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Pat-Lafon committed Jul 21, 2022
1 parent a30e3e0 commit 1543945
Show file tree
Hide file tree
Showing 26 changed files with 381 additions and 45 deletions.
3 changes: 2 additions & 1 deletion bril-rs/Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
TESTS := ../test/print/*.json \
../test/parse/*.bril \
../test/linking/*.bril
../test/linking/*.bril \
../test/rs/*.rs

.PHONY: test
test:
Expand Down
14 changes: 14 additions & 0 deletions bril-rs/rs2bril/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,17 @@
This project is a Rust version of the `ts2bril` tool. `rs2bril` compiles a subset of Rust to Bril. See the test cases or `example.rs` for the subset of supported syntax. The goal is to support core bril operations like integer arithmetic/comparisons, function calls, control flow like if/while, and printing. It will additionally support floating point operations like those in Bril and memory operations via Rust arrays/slices.

View the interface with `cargo doc --open` or install with `make install` using the Makefile in `bril/bril_rs`. Then use `rs2bril --help` to get the help page for `rs2bril` with all of the supported flags.

## Limitations

- Currently types are not inferred for variable declarations so all let bindings must be explicitly annotated: `let x:i64 = 5;`
- The `println!` macro is special cased to be converted into a `print` call in Bril. It takes a valid `println!` call, ignores the first argument which it assumes to be a format string, and assume s all of the following comma separated arguments are variables: `println!("this is ignored{}", these, must, be, variables);`
- There currently isn't a way to pass arguments to the main function which are also valid Rust. You can either declared arguments in the main function such that is no longer valid Rust but will generate the right Bril code, or declared them as const values in the first line and edit the Bril IR later: `fn main(a:i64) {}` or `fn main() {let a:i64 = 0;}`
- Automatic static memory management <https://www.cs.cornell.edu/courses/cs6120/2020fa/blog/asmm/> has not been implemented so arrays must be explicitly dropped where `drop` is specialized to translate to a call to free: `drop([0]);`
- For loops, `continue`, `break`, and ranges have not been implemented(but could be).
- Memory is implemented using Rust arrays. These are statically sized values unlike how calls to Bril `alloc` can be dynamically sized. One solution is to just allocate a large enough array and then treat the dynamic size like the length.(A subset of vectors could also be specialized in the future).
- In normal Rust, `if` can also be used as an expression which evaluates a value to be put in a variable. This is not implemented and it is assumed that there will only be if statements.
- The Bril code that is produces is super inefficient and it is left to other tools to optimize it.
- `!=` and automatic promotions of integer literals to floats are not implemented.
- to support indexing into arrays, you can cast to usize in the Rust code. This will be ignored when generating Bril. `arr[i as usize];`
- The parts of Rust which make it valid like lifetimes, references, mutability, and function visibility are compiled away in Bril.
105 changes: 68 additions & 37 deletions bril-rs/rs2bril/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ use bril_rs::{
use syn::punctuated::Punctuated;
use syn::{
BinOp, Block, Expr, ExprArray, ExprAssign, ExprAssignOp, ExprBinary, ExprBlock, ExprCall,
ExprIf, ExprIndex, ExprLit, ExprMacro, ExprParen, ExprPath, ExprReference, ExprRepeat,
ExprReturn, ExprUnary, ExprWhile, File, FnArg, Ident, Item, ItemFn, Lit, Local, Macro, Pat,
PatIdent, PatType, Path, PathSegment, ReturnType, Signature, Stmt, Type as SType, TypeArray,
TypePath, TypeReference, TypeSlice, UnOp,
ExprCast, ExprIf, ExprIndex, ExprLit, ExprMacro, ExprParen, ExprPath, ExprReference,
ExprRepeat, ExprReturn, ExprUnary, ExprWhile, File, FnArg, Ident, Item, ItemFn, Lit, Local,
Macro, Pat, PatIdent, PatType, Path, PathSegment, ReturnType, Signature, Stmt, Type as SType,
TypeArray, TypePath, TypeReference, TypeSlice, UnOp,
};

use proc_macro2::Span;
Expand Down Expand Up @@ -227,7 +227,7 @@ fn array_init_helper(
op: ConstOps::Const,
pos: None,
const_type: Type::Int,
value: Literal::Int(code.len() as i64),
value: Literal::Int(vars.len() as i64),
}));
code.push(Code::Instruction(Instruction::Value {
args: vec![size],
Expand Down Expand Up @@ -436,7 +436,7 @@ fn from_expr_to_bril(expr: Expr, state: &mut State) -> (Option<String>, Vec<Code
(BinOp::Ge(x), Type::Float) => (ValueOps::Fge, Type::Bool, x.spans[0]),
(BinOp::Gt(x), Type::Int) => (ValueOps::Gt, Type::Bool, x.spans[0]),
(BinOp::Gt(x), Type::Float) => (ValueOps::Fgt, Type::Bool, x.spans[0]),
(_, _) => unimplemented!(),
(_, _) => unimplemented!("{op:?}"),
};

let dest = state.fresh_var(op_type.clone());
Expand Down Expand Up @@ -485,38 +485,70 @@ fn from_expr_to_bril(expr: Expr, state: &mut State) -> (Option<String>, Vec<Code
})
.unzip();
let mut code: Vec<Code> = vec_code.into_iter().flatten().collect();
match state.get_ret_type_for_func(&f) {
None => {
code.push(Code::Instruction(Instruction::Effect {
args: vars,
funcs: vec![f],
labels: Vec::new(),
op: EffectOps::Call,
pos: if state.is_pos {
Some(from_span_to_position(paren_token.span))
} else {
None
},
}));
(None, code)
if f == "drop" {
code.push(Code::Instruction(Instruction::Effect {
args: vars,
funcs: Vec::new(),
labels: Vec::new(),
op: EffectOps::Free,
pos: if state.is_pos {
Some(from_span_to_position(paren_token.span))
} else {
None
},
}));
(None, code)
} else {
match state.get_ret_type_for_func(&f) {
None => {
code.push(Code::Instruction(Instruction::Effect {
args: vars,
funcs: vec![f],
labels: Vec::new(),
op: EffectOps::Call,
pos: if state.is_pos {
Some(from_span_to_position(paren_token.span))
} else {
None
},
}));
(None, code)
}
Some(ret) => {
let dest = state.fresh_var(ret.clone());
code.push(Code::Instruction(Instruction::Value {
args: vars,
dest: dest.clone(),
funcs: vec![f],
labels: Vec::new(),
op: ValueOps::Call,
pos: if state.is_pos {
Some(from_span_to_position(paren_token.span))
} else {
None
},
op_type: ret,
}));
(Some(dest), code)
}
}
Some(ret) => {
let dest = state.fresh_var(ret.clone());
code.push(Code::Instruction(Instruction::Value {
args: vars,
dest: dest.clone(),
funcs: vec![f],
labels: Vec::new(),
op: ValueOps::Call,
pos: if state.is_pos {
Some(from_span_to_position(paren_token.span))
} else {
None
},
op_type: ret,
}));
(Some(dest), code)
}
}
Expr::Cast(ExprCast {
attrs,
expr,
as_token: _,
ty,
}) if attrs.is_empty() => {
if let SType::Path(TypePath { qself: None, path }) = *ty {
// ignore casts to usize
if path.get_ident().is_some() && path.get_ident().unwrap() == "usize" {
from_expr_to_bril(*expr, state)
} else {
panic!("can't handle type in cast: {path:?}");
}
} else {
panic!("can't handle type in cast: {ty:?}");
}
}
Expr::ForLoop(_) => todo!(),
Expand Down Expand Up @@ -753,7 +785,6 @@ fn from_expr_to_bril(expr: Expr, state: &mut State) -> (Option<String>, Vec<Code
};

let vars = std::iter::repeat(var.unwrap()).take(array_len).collect();

array_init_helper(vars, code, state)
}
Expr::Return(ExprReturn {
Expand Down
8 changes: 1 addition & 7 deletions bril-rs/rs2bril/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
/* use syn::File; */
use std::fs::File;
use std::io::Read;

use bril_rs::output_program;
Expand All @@ -11,12 +9,8 @@ use clap::Parser;

fn main() {
let args = Cli::parse();
let mut filename = String::new();
std::io::stdin().read_to_string(&mut filename).unwrap();

let mut file = File::open(filename).unwrap();
let mut src = String::new();
file.read_to_string(&mut src).unwrap();
std::io::stdin().read_to_string(&mut src).unwrap();
let syntax = syn::parse_file(&src).unwrap();

output_program(&from_file_to_program(syntax, args.position));
Expand Down
2 changes: 2 additions & 0 deletions docs/tools/rust.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ Tools

This library supports fully compatible Rust implementations of `bril2txt` and `bril2json`. This library also implements the [import][] extension with a static linker called `brild`.

This library is used in a Rust compiler called `rs2bril` which supports generating [core], [float], and [mem] Bril from a subset of valid Rust.

For ease of use, these tools can be installed and added to your path by running the following in `bril-rs/`:

$ make install
Expand Down
1 change: 1 addition & 0 deletions test/rs/ackermann.out
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
509
18 changes: 18 additions & 0 deletions test/rs/ackermann.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//# Compute the Ackermann function recursively.
//# WARNING: Will quickly exceed stack size
//# ARGS: 3 6
fn main(m:i64, n:i64) {
let tmp : i64 = ack(m, n);
println!("{:?}", tmp);
}

fn ack(m:i64, n:i64) -> i64 {
if m == 0 {
return n + 1;
} else if n == 0 {
return ack(m -1, 1);
} else {
let t1 : i64 = ack(m, n -1);
return ack(m -1, t1);
}
}
1 change: 1 addition & 0 deletions test/rs/boolvar.out
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1
10 changes: 10 additions & 0 deletions test/rs/boolvar.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
fn main() {
let x: bool = true;
if x {
let one:i64 = 1;
println!("{:?}", one);
} else {
let two:i64 = 2;
println!("{:?}", two);
}
}
1 change: 1 addition & 0 deletions test/rs/call-explicit.out
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
5
8 changes: 8 additions & 0 deletions test/rs/call-explicit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
fn main() {
let x :i64 = 5;
call_print(x);
}

fn call_print(x:i64) {
println!("{:?}", x)
}
16 changes: 16 additions & 0 deletions test/rs/cholesky.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
65.00000000000000000
0.00000000000000000
0.00000000000000000
0.00000000000000000
137.43076923076924345
55.16143280081702471
0.00000000000000000
0.00000000000000000
148.46153846153845279
22.60304899166557746
44.73559850849774477
0.00000000000000000
432.26153846153846416
250.01098702698598686
374.19363889460623795
548.34440759751453243
125 changes: 125 additions & 0 deletions test/rs/cholesky.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
fn fillarray() -> [f64; 16] {
return [
34.0, 28.0, 38.0, 29.0, 26.0, 78.0, 91.0, 83.0, 67.0, 92.0, 56.0, 92.0, 67.0, 826.0, 38.0,
43.0,
];
}

fn print_array(size: i64, arr: &[f64]) {
let mut i: i64 = 0;
while i < size {
let val: f64 = arr[i as usize];
println!("{:.17}", val);
i += 1
}
}

fn matmul(size: i64, arr1: &[f64], arr2: &[f64], dest: &mut [f64]) {
let mut row: i64 = 0;
while row < size {
let mut col: i64 = 0;
while col < size {
let mut sum: f64 = 0.0;
let mut i: i64 = 0;
while i < size {
sum += arr1[((row * size) + i) as usize] * arr2[((i * size) + col) as usize];
i += 1;
}
dest[((row * size) + col) as usize] = sum;
col += 1;
}
row += 1;
}
}

fn transpose(size: i64, input: &[f64], output: &mut [f64]) {
let mut row: i64 = 0;
while row < size {
let mut col: i64 = 0;
while col < size {
output[((col * size) + row) as usize] = input[((row * size) + col) as usize];
col += 1;
}
row += 1;
}
}

fn sqrt(input: f64) -> f64 {
let n: f64 = input;
let precision: f64 = 0.00001;
let mut x: f64 = input;
let mut notdone: bool = true;
while notdone {
let root: f64 = 0.5 * (x + (n / x));
let diff: f64 = root - x;
if diff < 0.0 {
diff = -diff;
}

if (diff < precision) {
notdone = false;
}

x = root;
}
return x;
}

fn cholesky(size: i64, arr1: &mut [f64], arr2: &mut [f64]) {
let mut i: i64 = 0;
while (i < size) {
let mut j: i64 = 0;
while (j <= i) {
let mut k: i64 = 0;
while (k < j) {
// prepare indices
let ik_index: i64 = (i * size) + k;

let jk_index: i64 = (j * size) + k;

let ij_index: i64 = (i * size) + j;

// load values
let b_ik: f64 = arr2[(ik_index) as usize];

let b_jk: f64 = arr2[(jk_index) as usize];

let a_ij: f64 = arr1[(ij_index) as usize];

let value: f64 = a_ij - (b_ik * b_jk);
arr1[(ij_index) as usize] = value;

k += 1;
}

let ij_index: i64 = (i * size) + j;
let jj_index: i64 = (j * size) + j;

arr2[(ij_index) as usize] = (arr1[(ij_index) as usize] / arr2[(jj_index) as usize]);

j += 1;
}
let index: i64 = (i * size) + i;
arr2[index as usize] = sqrt(arr1[index as usize]);

i += 1;
}
return;
}

fn main() {
let size: i64 = 4;
let arr1: [f64; 16] = fillarray();
let mut arr1_transposed: [f64; 16] = fillarray();
let mut hermitian: [f64; 16] = fillarray();
let mut res: [f64; 16] = [0.0; 16];
transpose(size, &arr1, &mut arr1_transposed);
matmul(size, &arr1, &arr1_transposed, &mut hermitian);
cholesky(size, &mut hermitian, &mut res);
print_array(16, &res);
drop(arr1);
drop(arr1_transposed);
drop(hermitian);
drop(res);
return;
}
1 change: 1 addition & 0 deletions test/rs/cond.out
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
2
Loading

0 comments on commit 1543945

Please sign in to comment.