diff --git a/bril-rs/Makefile b/bril-rs/Makefile
index 9eff77701..197be54b7 100644
--- a/bril-rs/Makefile
+++ b/bril-rs/Makefile
@@ -1,6 +1,7 @@
TESTS := ../test/print/*.json \
../test/parse/*.bril \
- ../test/linking/*.bril
+ ../test/linking/*.bril \
+ ../test/rs/*.rs
.PHONY: test
test:
diff --git a/bril-rs/rs2bril/README.md b/bril-rs/rs2bril/README.md
index 43dde81c4..8da8e46c4 100644
--- a/bril-rs/rs2bril/README.md
+++ b/bril-rs/rs2bril/README.md
@@ -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 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.
diff --git a/bril-rs/rs2bril/src/lib.rs b/bril-rs/rs2bril/src/lib.rs
index f97a6f6eb..50ff371a2 100644
--- a/bril-rs/rs2bril/src/lib.rs
+++ b/bril-rs/rs2bril/src/lib.rs
@@ -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;
@@ -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],
@@ -436,7 +436,7 @@ fn from_expr_to_bril(expr: Expr, state: &mut State) -> (Option, Vec (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());
@@ -485,38 +485,70 @@ fn from_expr_to_bril(expr: Expr, state: &mut State) -> (Option, Vec = 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!(),
@@ -753,7 +785,6 @@ fn from_expr_to_bril(expr: Expr, state: &mut State) -> (Option, Vec 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);
+ }
+}
diff --git a/test/rs/boolvar.out b/test/rs/boolvar.out
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/test/rs/boolvar.out
@@ -0,0 +1 @@
+1
diff --git a/test/rs/boolvar.rs b/test/rs/boolvar.rs
new file mode 100644
index 000000000..cfdf7e95d
--- /dev/null
+++ b/test/rs/boolvar.rs
@@ -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);
+ }
+}
\ No newline at end of file
diff --git a/test/rs/call-explicit.out b/test/rs/call-explicit.out
new file mode 100644
index 000000000..7ed6ff82d
--- /dev/null
+++ b/test/rs/call-explicit.out
@@ -0,0 +1 @@
+5
diff --git a/test/rs/call-explicit.rs b/test/rs/call-explicit.rs
new file mode 100644
index 000000000..287ef7620
--- /dev/null
+++ b/test/rs/call-explicit.rs
@@ -0,0 +1,8 @@
+fn main() {
+ let x :i64 = 5;
+ call_print(x);
+}
+
+fn call_print(x:i64) {
+ println!("{:?}", x)
+}
\ No newline at end of file
diff --git a/test/rs/cholesky.out b/test/rs/cholesky.out
new file mode 100644
index 000000000..082867338
--- /dev/null
+++ b/test/rs/cholesky.out
@@ -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
diff --git a/test/rs/cholesky.rs b/test/rs/cholesky.rs
new file mode 100644
index 000000000..bec21b572
--- /dev/null
+++ b/test/rs/cholesky.rs
@@ -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;
+}
diff --git a/test/rs/cond.out b/test/rs/cond.out
new file mode 100644
index 000000000..0cfbf0888
--- /dev/null
+++ b/test/rs/cond.out
@@ -0,0 +1 @@
+2
diff --git a/test/rs/cond.rs b/test/rs/cond.rs
new file mode 100644
index 000000000..07c12a598
--- /dev/null
+++ b/test/rs/cond.rs
@@ -0,0 +1,10 @@
+fn main() {
+ let value:i64 = 5;
+ if value + 3 < 6 {
+ let one:i64 = 1;
+ println!("{:?}", one);
+ } else {
+ let two:i64 = 2;
+ println!("{:?}", two);
+ }
+}
diff --git a/test/rs/factorial.out b/test/rs/factorial.out
new file mode 100644
index 000000000..52bd8e43a
--- /dev/null
+++ b/test/rs/factorial.out
@@ -0,0 +1 @@
+120
diff --git a/test/rs/factorial.rs b/test/rs/factorial.rs
new file mode 100644
index 000000000..fb2de2f27
--- /dev/null
+++ b/test/rs/factorial.rs
@@ -0,0 +1,13 @@
+fn main() {
+ let x:i64 = 5;
+ let f:i64 = fac(x);
+ println!("{:?}", f);
+}
+
+fn fac(x:i64) -> i64 {
+ if x <= 1 {
+ return 1;
+ }
+ let result: i64 = x * fac(x -1);
+ return result;
+}
diff --git a/test/rs/float-add.out b/test/rs/float-add.out
new file mode 100644
index 000000000..6a16ab982
--- /dev/null
+++ b/test/rs/float-add.out
@@ -0,0 +1 @@
+42.00000000000000000
diff --git a/test/rs/float-add.rs b/test/rs/float-add.rs
new file mode 100644
index 000000000..59848f23f
--- /dev/null
+++ b/test/rs/float-add.rs
@@ -0,0 +1,4 @@
+fn main() {
+ let x: f64 = 40.2 + 1.8;
+ println!("{:.17}", x);
+}
diff --git a/test/rs/loopfact.out b/test/rs/loopfact.out
new file mode 100644
index 000000000..47ae14062
--- /dev/null
+++ b/test/rs/loopfact.out
@@ -0,0 +1 @@
+40320
diff --git a/test/rs/loopfact.rs b/test/rs/loopfact.rs
new file mode 100644
index 000000000..4f536f510
--- /dev/null
+++ b/test/rs/loopfact.rs
@@ -0,0 +1,10 @@
+fn main() {
+ let value:i64 = 8;
+ let mut result:i64 = 1;
+ let mut i:i64 = value;
+ while i > 0 {
+ result *= i;
+ i-= 1;
+ }
+ println!("{:?}", result);
+}
diff --git a/test/rs/nested_call.out b/test/rs/nested_call.out
new file mode 100644
index 000000000..00750edc0
--- /dev/null
+++ b/test/rs/nested_call.out
@@ -0,0 +1 @@
+3
diff --git a/test/rs/nested_call.rs b/test/rs/nested_call.rs
new file mode 100644
index 000000000..11c869cb2
--- /dev/null
+++ b/test/rs/nested_call.rs
@@ -0,0 +1,8 @@
+fn main() {
+ let y:i64 = 1 + get_two();
+ println!("{}", y);
+}
+
+fn get_two() -> i64 {
+ return 2;
+}
diff --git a/test/rs/riemann.out b/test/rs/riemann.out
new file mode 100644
index 000000000..0085eb16b
--- /dev/null
+++ b/test/rs/riemann.out
@@ -0,0 +1,3 @@
+284.00000000000000000
+330.00000000000000000
+380.00000000000000000
diff --git a/test/rs/riemann.rs b/test/rs/riemann.rs
new file mode 100644
index 000000000..5ca97b5e2
--- /dev/null
+++ b/test/rs/riemann.rs
@@ -0,0 +1,53 @@
+//# riemann sums from wikipedia article on riemann sums
+
+fn main() {
+ let a: f64 = 2.0;
+ let b: f64 = 10.0;
+ let n: f64 = 8.0;
+ let left: f64 = left_riemann(a, b, n);
+ println!("{:.17}", left);
+ let midpoint: f64 = midpoint_riemann(a, b, n);
+ println!("{:.17}", midpoint);
+ let right: f64 = right_riemann(a, b, n);
+ println!("{:.17}", right);
+}
+
+fn square_function(x: f64) -> f64 {
+ return x * x;
+}
+
+fn left_riemann(a: f64, b: f64, n: f64) -> f64 {
+ let diff: f64 = b - a;
+ let delta: f64 = diff / n;
+ let i: f64 = n - 1.0;
+ let sum: f64 = 0.0;
+ while !(i == -1.0) {
+ sum += square_function(a + (delta * i));
+ i -= 1.0;
+ }
+ return sum * delta;
+}
+
+fn right_riemann(a: f64, b: f64, n: f64) -> f64 {
+ let diff: f64 = b - a;
+ let delta: f64 = diff / n;
+ let i: f64 = n;
+ let sum: f64 = 0.0;
+ while !(i == 0.0) {
+ sum += square_function(a + (delta * i));
+ i -= 1.0;
+ }
+ return sum * delta;
+}
+
+fn midpoint_riemann(a: f64, b: f64, n: f64) -> f64 {
+ let diff: f64 = b - a;
+ let delta: f64 = diff / n;
+ let i: f64 = n-1.0;
+ let sum: f64 = 0.0;
+ while !(i == -1.0) {
+ sum += square_function(a + ((delta * i) + delta /2.0));
+ i -= 1.0;
+ }
+ return sum * delta;
+}
diff --git a/test/rs/turnt.toml b/test/rs/turnt.toml
new file mode 100644
index 000000000..c19c00824
--- /dev/null
+++ b/test/rs/turnt.toml
@@ -0,0 +1,8 @@
+[envs.bril-rs]
+command = "cargo run --manifest-path ../../bril-rs/rs2bril/Cargo.toml < {filename} | cargo run --manifest-path ../../brilirs/Cargo.toml -- {args}"
+output.out = "-"
+
+[envs.rustc]
+default = false
+command = "rustc {filename} --crate-type bin -o ../../bril-rs/target/a.out && ../../bril-rs/target/a.out {args}"
+output.out = "-"
\ No newline at end of file