mirror of
https://github.com/kmc7468/cs420.git
synced 2025-12-14 22:38:46 +00:00
Update skeleton
This commit is contained in:
68
Cargo.lock
generated
68
Cargo.lock
generated
@@ -13,8 +13,8 @@ name = "atty"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"hermit-abi 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hermit-abi 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@@ -28,19 +28,19 @@ name = "backtrace"
|
||||
version = "0.3.46"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"backtrace-sys 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"backtrace-sys 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "backtrace-sys"
|
||||
version = "0.1.35"
|
||||
version = "0.1.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -104,16 +104,16 @@ version = "0.1.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.10"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -157,20 +157,21 @@ dependencies = [
|
||||
"failure 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hexf 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"itertools 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lang-c 0.8.0 (git+https://github.com/kaist-cp/lang-c)",
|
||||
"lang-c 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ordered-float 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"peg 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wait-timeout 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lang-c"
|
||||
version = "0.8.0"
|
||||
source = "git+https://github.com/kaist-cp/lang-c#44687707f07fb8c5869936dfc549459ecb30d3be"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.68"
|
||||
version = "0.2.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
@@ -189,6 +190,30 @@ dependencies = [
|
||||
"num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "peg"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"peg-macros 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"peg-runtime 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "peg-macros"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"peg-runtime 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "peg-runtime"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.6"
|
||||
@@ -234,7 +259,7 @@ version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -333,7 +358,7 @@ version = "3.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -373,7 +398,7 @@ name = "wait-timeout"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -410,7 +435,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
"checksum atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
"checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
|
||||
"checksum backtrace 0.3.46 (registry+https://github.com/rust-lang/crates.io-index)" = "b1e692897359247cc6bb902933361652380af0f1b7651ae5c5013407f30e109e"
|
||||
"checksum backtrace-sys 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "7de8aba10a69c8e8d7622c5710229485ec32e9d55fdad160ea559c086fdcd118"
|
||||
"checksum backtrace-sys 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)" = "78848718ee1255a2485d1309ad9cdecfc2e7d0362dd11c6829364c6b35ae1bc7"
|
||||
"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||
"checksum cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)" = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd"
|
||||
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
@@ -419,15 +444,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
"checksum failure 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "b8529c2421efa3066a5cbd8063d2244603824daccb6936b079010bb2aa89464b"
|
||||
"checksum failure_derive 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "030a733c8287d6213886dd487564ff5c8f6aae10278b3588ed177f9d18f8d231"
|
||||
"checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb"
|
||||
"checksum hermit-abi 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "725cf19794cf90aa94e65050cb4191ff5d8fa87a498383774c47b332e3af952e"
|
||||
"checksum hermit-abi 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8a0d737e0f947a1864e93d33fdef4af8445a00d1ed8dc0c8ddb73139ea6abf15"
|
||||
"checksum hexf 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e54653cc32d838771a36532647afad59c4bf7155745eeeec406f71fd5d7e7538"
|
||||
"checksum hexf-impl 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "22eadcfadba76a730b2764eaa577d045f35e0ef5174b9c5b46adf1ee42b85e12"
|
||||
"checksum hexf-parse 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "79296f72d53a89096cbc9a88c9547ee8dfe793388674620e2207593d370550ac"
|
||||
"checksum itertools 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b"
|
||||
"checksum lang-c 0.8.0 (git+https://github.com/kaist-cp/lang-c)" = "<none>"
|
||||
"checksum libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)" = "dea0c0405123bba743ee3f91f49b1c7cfb684eef0da0a50110f758ccf24cdff0"
|
||||
"checksum lang-c 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2554b3a7a324e3d8ad63603fe6729c4c144bab2ab36742c45a72788bff50e8ec"
|
||||
"checksum libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)" = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005"
|
||||
"checksum num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096"
|
||||
"checksum ordered-float 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "18869315e81473c951eb56ad5558bbc56978562d3ecfb87abb7a1e944cea4518"
|
||||
"checksum peg 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9075875c14bb21f25f11cad4b6ad2e4dd443b8fb83900b2fbdd6ebd744b82e97"
|
||||
"checksum peg-macros 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c24c165fd39e995246140cc78df55c56c6733ba87e6658cb3e197b8856c62852"
|
||||
"checksum peg-runtime 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0c1a2897e69d986c7986747ebad425cf03746ec5e3e09bb3b2600f91301ba864"
|
||||
"checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b"
|
||||
"checksum proc-macro-hack 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b773f824ff2a495833f85fcdddcf85e096949971decada2e93249fa2c6c3d32f"
|
||||
"checksum proc-macro-hack-impl 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0f674ccc446da486175527473ec8aa064f980b0966bbf767ee743a5dff6244a7"
|
||||
|
||||
@@ -25,11 +25,11 @@ path = "bin/fuzz.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "2.33.0", features = ["yaml"] }
|
||||
# TODO: use crates.io version when https://github.com/vickenty/lang-c/pull/16 is merged
|
||||
lang-c = { git = "https://github.com/kaist-cp/lang-c" }
|
||||
lang-c = "0.8.1"
|
||||
itertools = "0.9.0"
|
||||
failure = "0.1.7"
|
||||
tempfile = "3.1.0"
|
||||
ordered-float = "1.0"
|
||||
hexf = "0.1.0"
|
||||
wait-timeout = "0.2.0"
|
||||
peg = "0.6.2"
|
||||
|
||||
6
Jenkinsfile
vendored
6
Jenkinsfile
vendored
@@ -38,9 +38,9 @@ pipeline {
|
||||
// When `cargo test` runs, the function `it_works()` is called in a new thread.
|
||||
// The stack size of a new thread is `2 MiB` on Linux, and this small stack size
|
||||
// can cause `stack-overflow` error when testing stack-intensive code.
|
||||
// For this reason, we need to increase the default size of stack to `4 MiB`.
|
||||
sh "RUST_MIN_STACK=4194304 cargo test"
|
||||
sh "RUST_MIN_STACK=4194304 cargo test --release"
|
||||
// For this reason, we need to increase the default size of stack to `8 MiB`.
|
||||
sh "RUST_MIN_STACK=8388608 cargo test"
|
||||
sh "RUST_MIN_STACK=8388608 cargo test --release"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
69
bin/kecc.rs
69
bin/kecc.rs
@@ -1,12 +1,17 @@
|
||||
use std::ffi::OsStr;
|
||||
use std::path::Path;
|
||||
#[macro_use]
|
||||
extern crate clap;
|
||||
use clap::{crate_authors, crate_description, crate_version, App};
|
||||
|
||||
use lang_c::ast::TranslationUnit;
|
||||
|
||||
#[macro_use]
|
||||
extern crate kecc;
|
||||
|
||||
use kecc::{
|
||||
write, Asmgen, Deadcode, Gvn, Irgen, Mem2reg, Optimize, Parse, SimplifyCfg, Translate, O1,
|
||||
ir, write, Asmgen, Deadcode, Gvn, IrParse, Irgen, Mem2reg, Optimize, Parse, SimplifyCfg,
|
||||
Translate, O1,
|
||||
};
|
||||
|
||||
fn main() {
|
||||
@@ -19,7 +24,7 @@ fn main() {
|
||||
.get_matches();
|
||||
|
||||
let input = matches.value_of("INPUT").unwrap();
|
||||
let unit = ok_or_exit!(Parse::default().translate(&input), 1);
|
||||
let input = Path::new(input);
|
||||
|
||||
let output = matches.value_of("output").unwrap_or_else(|| "-");
|
||||
let mut output: Box<dyn ::std::io::Write> = if output == "-" {
|
||||
@@ -28,47 +33,87 @@ fn main() {
|
||||
Box::new(ok_or_exit!(::std::fs::File::open(output), 1))
|
||||
};
|
||||
|
||||
let ext = input.extension();
|
||||
if ext == Some(OsStr::new("c")) {
|
||||
let input = ok_or_exit!(Parse::default().translate(&input), 1);
|
||||
compile_c(&input, &mut output, &matches);
|
||||
} else if ext == Some(OsStr::new("ir")) {
|
||||
let mut input = ok_or_exit!(IrParse::default().translate(&input), 1);
|
||||
compile_ir(&mut input, &mut output, &matches);
|
||||
} else {
|
||||
panic!("Unsupported file extension: {:?}", ext);
|
||||
}
|
||||
}
|
||||
|
||||
fn compile_c(
|
||||
input: &TranslationUnit,
|
||||
output: &mut dyn ::std::io::Write,
|
||||
matches: &clap::ArgMatches<'_>,
|
||||
) {
|
||||
if matches.is_present("parse") {
|
||||
return;
|
||||
}
|
||||
|
||||
if matches.is_present("print") {
|
||||
write(&unit, &mut output).unwrap();
|
||||
write(input, output).unwrap();
|
||||
return;
|
||||
}
|
||||
|
||||
let mut ir = match Irgen::default().translate(&unit) {
|
||||
let mut ir = match Irgen::default().translate(input) {
|
||||
Ok(ir) => ir,
|
||||
Err(irgen_error) => {
|
||||
println!("{}", irgen_error);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
if matches.is_present("irgen") {
|
||||
write(&ir, &mut output).unwrap();
|
||||
write(&ir, output).unwrap();
|
||||
return;
|
||||
}
|
||||
|
||||
compile_ir(&mut ir, output, matches)
|
||||
}
|
||||
|
||||
fn compile_ir(
|
||||
input: &mut ir::TranslationUnit,
|
||||
output: &mut dyn ::std::io::Write,
|
||||
matches: &clap::ArgMatches<'_>,
|
||||
) {
|
||||
if matches.is_present("irparse") {
|
||||
return;
|
||||
}
|
||||
|
||||
if matches.is_present("irprint") {
|
||||
write(input, output).unwrap();
|
||||
return;
|
||||
}
|
||||
|
||||
if matches.is_present("optimize") {
|
||||
O1::default().optimize(&mut ir);
|
||||
O1::default().optimize(input);
|
||||
} else {
|
||||
if matches.is_present("simplify-cfg") {
|
||||
SimplifyCfg::default().optimize(&mut ir);
|
||||
SimplifyCfg::default().optimize(input);
|
||||
}
|
||||
|
||||
if matches.is_present("mem2erg") {
|
||||
Mem2reg::default().optimize(&mut ir);
|
||||
Mem2reg::default().optimize(input);
|
||||
}
|
||||
|
||||
if matches.is_present("deadcode") {
|
||||
Deadcode::default().optimize(&mut ir);
|
||||
Deadcode::default().optimize(input);
|
||||
}
|
||||
|
||||
if matches.is_present("gvn") {
|
||||
Gvn::default().optimize(&mut ir);
|
||||
Gvn::default().optimize(input);
|
||||
}
|
||||
}
|
||||
|
||||
let asm = ok_or_exit!(Asmgen::default().translate(&ir), 1);
|
||||
write(&asm, &mut output).unwrap();
|
||||
if matches.is_present("iroutput") {
|
||||
write(input, output).unwrap();
|
||||
return;
|
||||
}
|
||||
|
||||
let asm = ok_or_exit!(Asmgen::default().translate(input), 1);
|
||||
write(&asm, output).unwrap();
|
||||
}
|
||||
|
||||
@@ -6,11 +6,17 @@ args:
|
||||
- print:
|
||||
short: p
|
||||
long: print
|
||||
help: Prints the input file's AST
|
||||
help: Prints the input AST
|
||||
- irgen:
|
||||
short: i
|
||||
long: irgen
|
||||
help: Generates IR
|
||||
- irparse:
|
||||
long: irparse
|
||||
help: Parses the input IR file
|
||||
- irprint:
|
||||
long: irprint
|
||||
help: Prints the input IR AST
|
||||
- optimize:
|
||||
short: O
|
||||
long: optimize
|
||||
@@ -27,6 +33,9 @@ args:
|
||||
- gvn:
|
||||
long: gvn
|
||||
help: Performs gvn
|
||||
- iroutput:
|
||||
long: iroutput
|
||||
help: Prints the output IR
|
||||
- output:
|
||||
short: o
|
||||
long: output
|
||||
|
||||
98
examples/simplify_cfg/const_prop.input.ir
Normal file
98
examples/simplify_cfg/const_prop.input.ir
Normal file
@@ -0,0 +1,98 @@
|
||||
fun i32 @const_prop_same {
|
||||
init:
|
||||
bid: b0
|
||||
allocations:
|
||||
|
||||
block b0:
|
||||
br undef:i1 b1() b1()
|
||||
|
||||
block b1:
|
||||
ret 0:i1
|
||||
}
|
||||
|
||||
fun i32 @const_prop_true {
|
||||
init:
|
||||
bid: b0
|
||||
allocations:
|
||||
|
||||
block b0:
|
||||
br 1:i1 b1() b2()
|
||||
|
||||
block b1:
|
||||
ret 0:i1
|
||||
|
||||
block b2:
|
||||
ret 0:i1
|
||||
}
|
||||
|
||||
fun i32 @const_prop_false {
|
||||
init:
|
||||
bid: b0
|
||||
allocations:
|
||||
|
||||
block b0:
|
||||
br 0:i1 b1() b2()
|
||||
|
||||
block b1:
|
||||
ret 0:i1
|
||||
|
||||
block b2:
|
||||
ret 0:i1
|
||||
}
|
||||
|
||||
fun i32 @const_prop_switch_same {
|
||||
init:
|
||||
bid: b0
|
||||
allocations:
|
||||
|
||||
block b0:
|
||||
switch 42:i32 default b1() [
|
||||
2:i32 b1()
|
||||
3:i32 b1()
|
||||
]
|
||||
|
||||
block b1:
|
||||
ret 0:i1
|
||||
}
|
||||
|
||||
fun i32 @const_prop_switch_case {
|
||||
init:
|
||||
bid: b0
|
||||
allocations:
|
||||
|
||||
block b0:
|
||||
switch 2:i32 default b1() [
|
||||
2:i32 b2()
|
||||
3:i32 b3()
|
||||
]
|
||||
|
||||
block b1:
|
||||
ret 0:i1
|
||||
|
||||
block b2:
|
||||
ret 0:i1
|
||||
|
||||
block b3:
|
||||
ret 0:i1
|
||||
}
|
||||
|
||||
fun i32 @const_prop_switch_default {
|
||||
init:
|
||||
bid: b0
|
||||
allocations:
|
||||
|
||||
block b0:
|
||||
switch 42:i32 default b1() [
|
||||
2:i32 b2()
|
||||
3:i32 b3()
|
||||
]
|
||||
|
||||
block b1:
|
||||
ret 0:i1
|
||||
|
||||
block b2:
|
||||
ret 0:i1
|
||||
|
||||
block b3:
|
||||
ret 0:i1
|
||||
}
|
||||
94
examples/simplify_cfg/const_prop.output.ir
Normal file
94
examples/simplify_cfg/const_prop.output.ir
Normal file
@@ -0,0 +1,94 @@
|
||||
fun i32 @const_prop_same {
|
||||
init:
|
||||
bid: b0
|
||||
allocations:
|
||||
|
||||
|
||||
block b0:
|
||||
j b1()
|
||||
|
||||
block b1:
|
||||
ret 0:i1
|
||||
}
|
||||
|
||||
fun i32 @const_prop_true {
|
||||
init:
|
||||
bid: b0
|
||||
allocations:
|
||||
|
||||
|
||||
block b0:
|
||||
j b1()
|
||||
|
||||
block b1:
|
||||
ret 0:i1
|
||||
|
||||
block b2:
|
||||
ret 0:i1
|
||||
}
|
||||
|
||||
fun i32 @const_prop_false {
|
||||
init:
|
||||
bid: b0
|
||||
allocations:
|
||||
|
||||
|
||||
block b0:
|
||||
j b2()
|
||||
|
||||
block b1:
|
||||
ret 0:i1
|
||||
|
||||
block b2:
|
||||
ret 0:i1
|
||||
}
|
||||
|
||||
fun i32 @const_prop_switch_same {
|
||||
init:
|
||||
bid: b0
|
||||
allocations:
|
||||
|
||||
block b0:
|
||||
j b1()
|
||||
|
||||
block b1:
|
||||
ret 0:i1
|
||||
}
|
||||
|
||||
fun i32 @const_prop_switch_case {
|
||||
init:
|
||||
bid: b0
|
||||
allocations:
|
||||
|
||||
|
||||
block b0:
|
||||
j b2()
|
||||
|
||||
block b1:
|
||||
ret 0:i1
|
||||
|
||||
block b2:
|
||||
ret 0:i1
|
||||
|
||||
block b3:
|
||||
ret 0:i1
|
||||
}
|
||||
|
||||
fun i32 @const_prop_switch_default {
|
||||
init:
|
||||
bid: b0
|
||||
allocations:
|
||||
|
||||
|
||||
block b0:
|
||||
j b1()
|
||||
|
||||
block b1:
|
||||
ret 0:i1
|
||||
|
||||
block b2:
|
||||
ret 0:i1
|
||||
|
||||
block b3:
|
||||
ret 0:i1
|
||||
}
|
||||
35
examples/simplify_cfg/empty.input.ir
Normal file
35
examples/simplify_cfg/empty.input.ir
Normal file
@@ -0,0 +1,35 @@
|
||||
fun i32 @foo {
|
||||
init:
|
||||
bid: b0
|
||||
allocations:
|
||||
|
||||
block b0:
|
||||
j b1(42:i32)
|
||||
|
||||
block b1:
|
||||
%b1:p0:i32
|
||||
j b3()
|
||||
|
||||
block b3:
|
||||
ret 37:i32
|
||||
}
|
||||
|
||||
fun i32 @bar {
|
||||
init:
|
||||
bid: b0
|
||||
allocations:
|
||||
|
||||
block b0:
|
||||
j b1(42:i32)
|
||||
|
||||
block b1:
|
||||
%b1:p0:i32
|
||||
br 1:i1 b2() b2()
|
||||
|
||||
block b2:
|
||||
j b3()
|
||||
|
||||
block b3:
|
||||
%b3:i0:i1 = cmp eq 0:i32 0:i32
|
||||
ret 37:i32
|
||||
}
|
||||
35
examples/simplify_cfg/empty.output.ir
Normal file
35
examples/simplify_cfg/empty.output.ir
Normal file
@@ -0,0 +1,35 @@
|
||||
fun i32 @foo {
|
||||
init:
|
||||
bid: b0
|
||||
allocations:
|
||||
|
||||
block b0:
|
||||
j b1(42:i32)
|
||||
|
||||
block b1:
|
||||
%b1:p0:i32
|
||||
ret 37:i32
|
||||
|
||||
block b3:
|
||||
ret 37:i32
|
||||
}
|
||||
|
||||
fun i32 @bar {
|
||||
init:
|
||||
bid: b0
|
||||
allocations:
|
||||
|
||||
block b0:
|
||||
j b1(42:i32)
|
||||
|
||||
block b1:
|
||||
%b1:p0:i32
|
||||
br 1:i1 b3() b3()
|
||||
|
||||
block b2:
|
||||
j b3()
|
||||
|
||||
block b3:
|
||||
%b3:i0:i1 = cmp eq 0:i32 0:i32
|
||||
ret 37:i32
|
||||
}
|
||||
14
examples/simplify_cfg/merge.input.ir
Normal file
14
examples/simplify_cfg/merge.input.ir
Normal file
@@ -0,0 +1,14 @@
|
||||
fun i32 @foo {
|
||||
init:
|
||||
bid: b0
|
||||
allocations:
|
||||
|
||||
block b0:
|
||||
j b1(42:i32, 37:i32)
|
||||
|
||||
block b1:
|
||||
%b1:p0:i32
|
||||
%b1:p1:i32
|
||||
%b1:i0:i32 = add %b1:p0:i32 %b1:p1:i32
|
||||
ret %b1:i0:i32
|
||||
}
|
||||
9
examples/simplify_cfg/merge.output.ir
Normal file
9
examples/simplify_cfg/merge.output.ir
Normal file
@@ -0,0 +1,9 @@
|
||||
fun i32 @foo {
|
||||
init:
|
||||
bid: b0
|
||||
allocations:
|
||||
|
||||
block b0:
|
||||
%b0:i0:i32 = add 42:i32 37:i32
|
||||
ret %b0:i0:i32
|
||||
}
|
||||
17
examples/simplify_cfg/reach.input.ir
Normal file
17
examples/simplify_cfg/reach.input.ir
Normal file
@@ -0,0 +1,17 @@
|
||||
fun i32 @foo {
|
||||
init:
|
||||
bid: b0
|
||||
allocations:
|
||||
|
||||
block b0:
|
||||
j b1()
|
||||
|
||||
block b1:
|
||||
j b0()
|
||||
|
||||
block b2:
|
||||
j b3()
|
||||
|
||||
block b3:
|
||||
j b2()
|
||||
}
|
||||
11
examples/simplify_cfg/reach.output.ir
Normal file
11
examples/simplify_cfg/reach.output.ir
Normal file
@@ -0,0 +1,11 @@
|
||||
fun i32 @foo {
|
||||
init:
|
||||
bid: b0
|
||||
allocations:
|
||||
|
||||
block b0:
|
||||
j b1()
|
||||
|
||||
block b1:
|
||||
j b0()
|
||||
}
|
||||
@@ -946,7 +946,11 @@ impl fmt::Display for Dtype {
|
||||
write!(f, "{}f{}", if *is_const { "const " } else { "" }, width)
|
||||
}
|
||||
Self::Pointer { inner, is_const } => {
|
||||
write!(f, "{}*{}", inner, if *is_const { "const" } else { "" })
|
||||
if *is_const {
|
||||
write!(f, "*const {}", inner)
|
||||
} else {
|
||||
write!(f, "*{}", inner)
|
||||
}
|
||||
}
|
||||
Self::Array { inner, size, .. } => write!(f, "[{} x {}]", size, inner,),
|
||||
Self::Struct {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
mod dtype;
|
||||
mod interp;
|
||||
mod parse;
|
||||
mod write_ir;
|
||||
|
||||
use core::convert::TryFrom;
|
||||
@@ -13,13 +14,14 @@ use std::hash::{Hash, Hasher};
|
||||
|
||||
pub use dtype::{Dtype, DtypeError, HasDtype};
|
||||
pub use interp::{interp, Value};
|
||||
pub use parse::Parse;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct TranslationUnit {
|
||||
pub decls: HashMap<String, Declaration>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Declaration {
|
||||
Variable {
|
||||
dtype: Dtype,
|
||||
@@ -312,7 +314,16 @@ impl JumpArg {
|
||||
|
||||
impl fmt::Display for JumpArg {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}({:?})", self.bid, self.args)
|
||||
write!(
|
||||
f,
|
||||
"{}({})",
|
||||
self.bid,
|
||||
self.args
|
||||
.iter()
|
||||
.map(|a| a.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -728,7 +739,7 @@ impl fmt::Display for Constant {
|
||||
}
|
||||
),
|
||||
Self::Float { value, .. } => write!(f, "{}", value),
|
||||
Self::GlobalVariable { name, .. } => write!(f, "%{}", name),
|
||||
Self::GlobalVariable { name, .. } => write!(f, "@{}", name),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
327
src/ir/parse.rs
Normal file
327
src/ir/parse.rs
Normal file
@@ -0,0 +1,327 @@
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
|
||||
use lang_c::ast::{BinaryOperator, UnaryOperator};
|
||||
|
||||
use crate::ir::*;
|
||||
use crate::Translate;
|
||||
|
||||
peg::parser! {
|
||||
grammar ir_parse() for str {
|
||||
rule whitespace() = quiet!{[' ' | '\n' | '\t']}
|
||||
|
||||
rule _() = whitespace()*
|
||||
|
||||
rule __() = whitespace()+
|
||||
|
||||
pub rule translation_unit() -> TranslationUnit
|
||||
= _ ds:(named_decl() ** __) _ {
|
||||
let mut decls = HashMap::new();
|
||||
for decl in ds {
|
||||
let result = decls.insert(decl.name.unwrap(), decl.inner);
|
||||
assert!(result.is_none());
|
||||
}
|
||||
TranslationUnit { decls }
|
||||
}
|
||||
|
||||
rule named_decl() -> Named<Declaration> =
|
||||
"var" __ dtype:dtype() __ var:global_variable() _ "=" _ initializer:initializer() {
|
||||
Named::new(Some(var), Declaration::Variable {
|
||||
dtype: dtype,
|
||||
initializer,
|
||||
})
|
||||
}
|
||||
/
|
||||
"fun" __ dtype:dtype() __ var:global_variable() _ "{" _ fun_body:fun_body() _ "}" {
|
||||
Named::new(Some(var), Declaration::Function {
|
||||
signature: FunctionSignature::new(Dtype::function(Dtype::int(32), Vec::new())),
|
||||
definition: Some(fun_body),
|
||||
})
|
||||
}
|
||||
/
|
||||
"fun" __ dtype:dtype() __ var:global_variable() {
|
||||
Named::new(Some(var), Declaration::Function {
|
||||
signature: FunctionSignature::new(Dtype::function(Dtype::int(32), Vec::new())),
|
||||
definition: None,
|
||||
})
|
||||
}
|
||||
|
||||
rule dtype() -> Dtype =
|
||||
"unit" { Dtype::unit() }
|
||||
/
|
||||
"u" n:number() { Dtype::int(n).set_signed(false) }
|
||||
/
|
||||
"i" n:number() { Dtype::int(n) }
|
||||
/
|
||||
"*" _ inner:dtype() { Dtype::pointer(inner) }
|
||||
/ expected!("dtype")
|
||||
|
||||
rule id() -> String
|
||||
= n:$(['_' | 'a'..='z' | 'A'..='Z']['_' | 'a'..='z' | 'A'..='Z' | '0'..='9']*) {
|
||||
String::from(n)
|
||||
}
|
||||
/ expected!("id")
|
||||
|
||||
rule global_variable() -> String
|
||||
= "@" id:id() {
|
||||
id
|
||||
}
|
||||
/ expected!("global-variable")
|
||||
|
||||
rule arg() -> usize // TODO
|
||||
= "<arg>" {
|
||||
todo!()
|
||||
}
|
||||
|
||||
rule fun_body() -> FunctionDefinition
|
||||
= "init:" __ "bid:" _ bid_init:bid() _ "allocations:" _ allocations:(allocation() ** __) _ blocks:(block() ** __) {
|
||||
FunctionDefinition {
|
||||
allocations: allocations.into_iter().map(|a| a.1).collect(),
|
||||
blocks: blocks.into_iter().collect(),
|
||||
bid_init,
|
||||
}
|
||||
}
|
||||
|
||||
rule allocation() -> (usize, Named<Dtype>)
|
||||
= "%l" number:number() ":" dtype:dtype() ":" name:id() {
|
||||
(number, Named::new(Some(name), dtype))
|
||||
}
|
||||
|
||||
rule block() -> (BlockId, Block)
|
||||
= "block" __ bid:bid() _ ":" _ phinodes:(phinode() ** __) _ instructions:(instruction() ** __) _ exit:exit() {
|
||||
if !phinodes.iter().enumerate().all(|(i1, (bid2, i2, _))| bid == *bid2 && i1 == *i2) {
|
||||
panic!("Phinode id mismatches");
|
||||
}
|
||||
|
||||
if !instructions.iter().enumerate().all(|(i1, (bid2, i2, _))| bid == *bid2 && i1 == *i2) {
|
||||
panic!("Instruction id mismatches");
|
||||
}
|
||||
|
||||
(bid,
|
||||
Block {
|
||||
phinodes: phinodes.into_iter().map(|(_, _, phi)| phi).collect(),
|
||||
instructions: instructions.into_iter().map(|(_, _, instr)| instr).collect(),
|
||||
exit,
|
||||
})
|
||||
}
|
||||
|
||||
rule number() -> usize
|
||||
= n:$(['0'..='9']+) {
|
||||
n.parse().unwrap()
|
||||
}
|
||||
/ expected!("number")
|
||||
|
||||
rule bid() -> BlockId
|
||||
= "b" n:number() {
|
||||
BlockId(n)
|
||||
}
|
||||
/ expected!("bid")
|
||||
|
||||
rule phinode() -> (BlockId, usize, Named<Dtype>)
|
||||
= "%" bid:bid() ":p" number:number() ":" dtype:dtype() name:(":" name:id() { name })? {
|
||||
(bid, number, Named::new(name, dtype))
|
||||
}
|
||||
/ expected!("phinode")
|
||||
|
||||
rule instruction() -> (BlockId, usize, Named<Instruction>)
|
||||
= "%" bid:bid() ":i" number:number() ":" dtype:dtype() name:(":" name:id() { name })? _ "=" _ instruction:instruction_inner() {
|
||||
(bid, number, Named::new(name, instruction))
|
||||
}
|
||||
/ expected!("instruction")
|
||||
|
||||
rule instruction_inner() -> Instruction =
|
||||
"call" __ callee:operand() _ "(" _ args:(operand() ** (_ "," _)) _ ")" {
|
||||
Instruction::Call {
|
||||
callee,
|
||||
args,
|
||||
return_type: Dtype::unit(), // TODO
|
||||
}
|
||||
}
|
||||
/
|
||||
"load" __ ptr:operand() {
|
||||
Instruction::Load { ptr }
|
||||
}
|
||||
/
|
||||
"store" __ value:operand() __ ptr:operand() {
|
||||
Instruction::Store { ptr, value }
|
||||
}
|
||||
/
|
||||
"typecast" __ value:operand() __ "to" __ target_dtype:dtype() {
|
||||
Instruction::TypeCast { value, target_dtype }
|
||||
}
|
||||
/
|
||||
"minus" __ operand:operand() {
|
||||
let dtype = operand.dtype();
|
||||
Instruction::UnaryOp {
|
||||
op: UnaryOperator::Minus,
|
||||
operand,
|
||||
dtype,
|
||||
}
|
||||
}
|
||||
/
|
||||
op:arith_op() __ lhs:operand() __ rhs:operand() {
|
||||
let dtype = lhs.dtype();
|
||||
assert_eq!(&dtype, &rhs.dtype());
|
||||
Instruction::BinOp {
|
||||
op,
|
||||
lhs,
|
||||
rhs,
|
||||
dtype,
|
||||
}
|
||||
}
|
||||
/
|
||||
"cmp" __ op:comparison_op() __ lhs:operand() __ rhs:operand() {
|
||||
assert_eq!(lhs.dtype(), rhs.dtype());
|
||||
Instruction::BinOp {
|
||||
op,
|
||||
lhs,
|
||||
rhs,
|
||||
dtype: Dtype::BOOL,
|
||||
}
|
||||
}
|
||||
/
|
||||
"<instruction>" {
|
||||
todo!()
|
||||
}
|
||||
/ expected!("instruction_inner")
|
||||
|
||||
rule arith_op() -> BinaryOperator =
|
||||
"add" { BinaryOperator::Plus }
|
||||
/
|
||||
"sub" { BinaryOperator::Minus }
|
||||
|
||||
rule comparison_op() -> BinaryOperator =
|
||||
"eq" { BinaryOperator::Equals }
|
||||
/
|
||||
"ne" { BinaryOperator::NotEquals }
|
||||
/
|
||||
"lt" { BinaryOperator::Less }
|
||||
/
|
||||
"le" { BinaryOperator::LessOrEqual }
|
||||
/
|
||||
"gt" { BinaryOperator::Greater }
|
||||
/
|
||||
"ge" { BinaryOperator::GreaterOrEqual }
|
||||
|
||||
rule exit() -> BlockExit =
|
||||
"j" __ arg:jump_arg() {
|
||||
BlockExit::Jump { arg }
|
||||
}
|
||||
/
|
||||
"br" __ condition:operand() __ arg_then:jump_arg() __ arg_else:jump_arg() {
|
||||
BlockExit::ConditionalJump { condition, arg_then, arg_else }
|
||||
}
|
||||
/
|
||||
"switch" __ value:operand() __ "default" __ default:jump_arg() _ "[" _ cases:(switch_case() ** __) _ "]" {
|
||||
BlockExit::Switch { value, default, cases }
|
||||
}
|
||||
/
|
||||
"ret" __ value:operand() {
|
||||
BlockExit::Return { value }
|
||||
}
|
||||
/
|
||||
"unreachable" {
|
||||
BlockExit::Unreachable
|
||||
}
|
||||
|
||||
rule constant() -> Constant =
|
||||
n:number() {
|
||||
Constant::int(n as _, Dtype::int(128)) // TODO: the right dtype
|
||||
}
|
||||
/
|
||||
"undef" {
|
||||
Constant::undef(Dtype::unit()) // TODO
|
||||
}
|
||||
/
|
||||
"unit" {
|
||||
Constant::undef(Dtype::unit()) // TODO
|
||||
}
|
||||
/
|
||||
"<constant>" {
|
||||
todo!()
|
||||
}
|
||||
|
||||
|
||||
rule register_id() -> RegisterId =
|
||||
"%l" id:number() {
|
||||
RegisterId::local(id)
|
||||
}
|
||||
/
|
||||
"%" bid:bid() ":p" id:number() {
|
||||
RegisterId::arg(bid, id)
|
||||
}
|
||||
/
|
||||
"%" bid:bid() ":i" id:number() {
|
||||
RegisterId::temp(bid, id)
|
||||
}
|
||||
|
||||
rule operand() -> Operand =
|
||||
name:global_variable() {
|
||||
Operand::Constant(Constant::GlobalVariable {
|
||||
name,
|
||||
dtype: Dtype::unit(), // TODO
|
||||
})
|
||||
}
|
||||
/
|
||||
constant:constant() ":" dtype:dtype() {
|
||||
let constant = match (&constant, &dtype) {
|
||||
(Constant::Int { value, .. }, Dtype::Int { width, is_signed, .. }) => {
|
||||
Constant::Int {
|
||||
value: *value,
|
||||
width: *width,
|
||||
is_signed: *is_signed,
|
||||
}
|
||||
}
|
||||
(Constant::Undef { .. }, _) => {
|
||||
Constant::undef(dtype.clone())
|
||||
}
|
||||
_ => constant.clone(),
|
||||
};
|
||||
Operand::Constant(constant)
|
||||
}
|
||||
/
|
||||
rid:register_id() ":" dtype:dtype() {
|
||||
Operand::Register { rid, dtype }
|
||||
}
|
||||
|
||||
rule jump_arg() -> JumpArg
|
||||
= bid:bid() _ "(" _ args:(operand() ** (_ "," _)) _ ")" {
|
||||
JumpArg { bid, args }
|
||||
}
|
||||
|
||||
rule switch_case() -> (Constant, JumpArg)
|
||||
= operand:operand() __ jump_arg:jump_arg() {
|
||||
let constant = operand.get_constant().unwrap().clone();
|
||||
(constant, jump_arg)
|
||||
}
|
||||
|
||||
rule initializer() -> Option<lang_c::ast::Initializer> =
|
||||
"default" {
|
||||
None
|
||||
}
|
||||
/
|
||||
"<initializer>" {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
IoError(std::io::Error),
|
||||
ParseError(peg::error::ParseError<peg::str::LineCol>),
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Parse {}
|
||||
|
||||
impl<P: AsRef<Path>> Translate<P> for Parse {
|
||||
type Target = TranslationUnit;
|
||||
type Error = Error;
|
||||
|
||||
fn translate(&mut self, source: &P) -> Result<Self::Target, Self::Error> {
|
||||
let ir = fs::read_to_string(source).map_err(Error::IoError)?;
|
||||
let ir = ir_parse::translation_unit(&ir).map_err(Error::ParseError)?;
|
||||
Ok(ir)
|
||||
}
|
||||
}
|
||||
@@ -9,21 +9,14 @@ use lang_c::ast;
|
||||
|
||||
impl WriteLine for TranslationUnit {
|
||||
fn write_line(&self, indent: usize, write: &mut dyn Write) -> Result<()> {
|
||||
write_indent(indent, write)?;
|
||||
writeln!(write, "<variable list>")?;
|
||||
writeln!(write)?;
|
||||
for (name, decl) in &self.decls {
|
||||
let _ = some_or!(decl.get_variable(), continue);
|
||||
(name, decl).write_line(indent, write)?;
|
||||
}
|
||||
|
||||
writeln!(write)?;
|
||||
writeln!(write)?;
|
||||
write_indent(indent, write)?;
|
||||
writeln!(write, "<function list>")?;
|
||||
writeln!(write)?;
|
||||
for (name, decl) in &self.decls {
|
||||
let _ = some_or!(decl.get_function(), continue);
|
||||
writeln!(write)?;
|
||||
(name, decl).write_line(indent, write)?;
|
||||
}
|
||||
|
||||
@@ -40,73 +33,56 @@ impl WriteLine for (&String, &Declaration) {
|
||||
Declaration::Variable { dtype, initializer } => {
|
||||
writeln!(
|
||||
write,
|
||||
"{} = {} {}",
|
||||
"var {} @{} = {}",
|
||||
dtype,
|
||||
name,
|
||||
if let Some(init) = initializer {
|
||||
init.write_string()
|
||||
} else {
|
||||
"default".to_string()
|
||||
},
|
||||
dtype
|
||||
}
|
||||
)?;
|
||||
}
|
||||
Declaration::Function {
|
||||
signature,
|
||||
definition,
|
||||
} => {
|
||||
let declaration = format!(
|
||||
"{} @{}({})",
|
||||
signature.ret,
|
||||
name,
|
||||
signature
|
||||
.params
|
||||
.iter()
|
||||
.map(|d| d.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join(", "),
|
||||
);
|
||||
if let Some(definition) = definition.as_ref() {
|
||||
// print function definition
|
||||
writeln!(write, "fun {} @{} {{", signature.ret, name)?;
|
||||
// print meta data for function
|
||||
writeln!(
|
||||
write,
|
||||
"init:\n bid: {}\n allocations: \n{}",
|
||||
definition.bid_init,
|
||||
definition
|
||||
.allocations
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, a)| format!(
|
||||
" %l{}:{}{}",
|
||||
i,
|
||||
a.deref(),
|
||||
if let Some(name) = a.name() {
|
||||
format!(":{}", name)
|
||||
} else {
|
||||
"".into()
|
||||
}
|
||||
))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n")
|
||||
)?;
|
||||
|
||||
match definition.as_ref() {
|
||||
Some(defintion) => {
|
||||
// print function definition
|
||||
writeln!(write, "define {} {{", declaration)?;
|
||||
// print meta data for function
|
||||
writeln!(
|
||||
write,
|
||||
"init:\n bid: {}\n allocations: \n{}\n",
|
||||
defintion.bid_init,
|
||||
defintion
|
||||
.allocations
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, a)| format!(
|
||||
" %l{}:{}{}",
|
||||
i,
|
||||
a.deref(),
|
||||
if let Some(name) = a.name() {
|
||||
format!(":{}", name)
|
||||
} else {
|
||||
"".into()
|
||||
}
|
||||
))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n")
|
||||
)?;
|
||||
|
||||
for (id, block) in &defintion.blocks {
|
||||
writeln!(write, "block {}", id)?;
|
||||
(id, block).write_line(indent + 1, write)?;
|
||||
writeln!(write)?;
|
||||
}
|
||||
|
||||
writeln!(write, "}}")?;
|
||||
writeln!(write)?;
|
||||
}
|
||||
None => {
|
||||
// print declaration line only
|
||||
writeln!(write, "declare {}", declaration)?;
|
||||
writeln!(write)?;
|
||||
for (id, block) in &definition.blocks {
|
||||
writeln!(write, "\nblock {}:", id)?;
|
||||
(id, block).write_line(indent + 1, write)?;
|
||||
}
|
||||
|
||||
writeln!(write, "}}")?;
|
||||
} else {
|
||||
// print declaration line only
|
||||
writeln!(write, "fun {} @{}", signature.ret, name)?;
|
||||
writeln!(write)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -260,7 +236,7 @@ impl WriteString for BlockExit {
|
||||
default,
|
||||
cases,
|
||||
} => format!(
|
||||
"switch {}, default: {} [\n{}\n ]",
|
||||
"switch {} default: {} [\n{}\n ]",
|
||||
value.write_string(),
|
||||
default,
|
||||
cases
|
||||
|
||||
10
src/lib.rs
10
src/lib.rs
@@ -1,5 +1,9 @@
|
||||
#![deny(warnings)]
|
||||
// Neccessary for skeleton code.
|
||||
#![allow(unreachable_code)]
|
||||
// Necessary to allow `iter.fold(false, |l, r| l || r)`. It's used when iteration should not be
|
||||
// short-circuited.
|
||||
#![allow(clippy::unnecessary_fold)]
|
||||
|
||||
mod tests;
|
||||
mod utils;
|
||||
@@ -18,7 +22,11 @@ pub use utils::*;
|
||||
pub use write_base::write;
|
||||
|
||||
pub use c::Parse;
|
||||
pub use ir::Parse as IrParse;
|
||||
|
||||
pub use asmgen::Asmgen;
|
||||
pub use irgen::Irgen;
|
||||
pub use opt::{Deadcode, Gvn, Mem2reg, Optimize, Repeat, SimplifyCfg, O0, O1};
|
||||
pub use opt::{
|
||||
Deadcode, FunctionPass, Gvn, Mem2reg, Optimize, Repeat, SimplifyCfg, SimplifyCfgConstProp,
|
||||
SimplifyCfgEmpty, SimplifyCfgMerge, SimplifyCfgReach, O0, O1,
|
||||
};
|
||||
|
||||
@@ -9,7 +9,9 @@ mod simplify_cfg;
|
||||
pub use deadcode::Deadcode;
|
||||
pub use gvn::Gvn;
|
||||
pub use mem2reg::Mem2reg;
|
||||
pub use simplify_cfg::SimplifyCfg;
|
||||
pub use simplify_cfg::{
|
||||
SimplifyCfg, SimplifyCfgConstProp, SimplifyCfgEmpty, SimplifyCfgMerge, SimplifyCfgReach,
|
||||
};
|
||||
|
||||
use crate::ir;
|
||||
|
||||
@@ -63,7 +65,10 @@ where
|
||||
T: Optimize<ir::FunctionDefinition>,
|
||||
{
|
||||
fn optimize(&mut self, code: &mut ir::TranslationUnit) -> bool {
|
||||
code.decls.iter_mut().any(|(_, decl)| self.optimize(decl))
|
||||
code.decls
|
||||
.iter_mut()
|
||||
.map(|(_, decl)| self.optimize(decl))
|
||||
.fold(false, |l, r| l || r)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::opt::FunctionPass;
|
||||
use crate::*;
|
||||
|
||||
pub type SimplifyCfg =
|
||||
FunctionPass<Repeat<(SimplifyCfgConstProp, (SimplifyCfgReach, SimplifyCfgMerge))>>;
|
||||
FunctionPass<Repeat<(SimplifyCfgConstProp, (SimplifyCfgReach, (SimplifyCfgMerge, SimplifyCfgEmpty)))>>;
|
||||
|
||||
/// Simplifies block exits by propagating constants.
|
||||
#[derive(Default)]
|
||||
@@ -17,6 +17,10 @@ pub struct SimplifyCfgReach {}
|
||||
#[derive(Default)]
|
||||
pub struct SimplifyCfgMerge {}
|
||||
|
||||
/// Removes empty blocks
|
||||
#[derive(Default)]
|
||||
pub struct SimplifyCfgEmpty {}
|
||||
|
||||
impl Optimize<FunctionDefinition> for SimplifyCfgConstProp {
|
||||
fn optimize(&mut self, _code: &mut FunctionDefinition) -> bool {
|
||||
todo!("homework 3")
|
||||
@@ -34,3 +38,9 @@ impl Optimize<FunctionDefinition> for SimplifyCfgMerge {
|
||||
todo!("homework 3")
|
||||
}
|
||||
}
|
||||
|
||||
impl Optimize<FunctionDefinition> for SimplifyCfgEmpty {
|
||||
fn optimize(&mut self, _code: &mut FunctionDefinition) -> bool {
|
||||
todo!("homework 3")
|
||||
}
|
||||
}
|
||||
|
||||
108
src/tests.rs
108
src/tests.rs
@@ -1,5 +1,6 @@
|
||||
use lang_c::ast::*;
|
||||
use std::fs::{self, File};
|
||||
use std::io::{stderr, Write};
|
||||
use std::path::Path;
|
||||
use std::process::{Command, Stdio};
|
||||
use tempfile::tempdir;
|
||||
@@ -32,7 +33,7 @@ pub fn test_irgen(unit: &TranslationUnit, path: &Path) {
|
||||
.expect("failed to parse the given program");
|
||||
|
||||
let file_path = path.display().to_string();
|
||||
let bin_path = path.with_extension("exe").as_path().display().to_string();
|
||||
let bin_path = path.with_extension("irgen").as_path().display().to_string();
|
||||
|
||||
// Compile c file: If fails, test is vacuously success
|
||||
if !Command::new("gcc")
|
||||
@@ -90,3 +91,108 @@ pub fn test_irgen(unit: &TranslationUnit, path: &Path) {
|
||||
println!("gcc: {}, kecc: {}", status as i8, value as i8);
|
||||
assert_eq!(status as i8, value as i8);
|
||||
}
|
||||
|
||||
pub fn test_irparse(unit: &TranslationUnit, path: &Path) {
|
||||
// Check if the file has .c extension
|
||||
assert_eq!(path.extension(), Some(std::ffi::OsStr::new("c")));
|
||||
|
||||
// Test parse
|
||||
c::Parse::default()
|
||||
.translate(&path)
|
||||
.expect("failed to parse the given program");
|
||||
|
||||
let file_path = path.display().to_string();
|
||||
let bin_path = path
|
||||
.with_extension("irparse")
|
||||
.as_path()
|
||||
.display()
|
||||
.to_string();
|
||||
|
||||
// Compile c file: If fails, test is vacuously success
|
||||
if !Command::new("gcc")
|
||||
.args(&["-O1", &file_path, "-o", &bin_path])
|
||||
.stderr(Stdio::null())
|
||||
.status()
|
||||
.unwrap()
|
||||
.success()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Execute compiled executable
|
||||
let mut child = Command::new(fs::canonicalize(bin_path.clone()).unwrap())
|
||||
.spawn()
|
||||
.expect("failed to execute the compiled executable");
|
||||
|
||||
Command::new("rm")
|
||||
.arg(bin_path)
|
||||
.status()
|
||||
.expect("failed to remove compiled executable");
|
||||
|
||||
let status = some_or!(
|
||||
child
|
||||
.wait_timeout_ms(500)
|
||||
.expect("failed to obtain exit status from child process"),
|
||||
{
|
||||
println!("timeout occurs");
|
||||
child.kill().unwrap();
|
||||
child.wait().unwrap();
|
||||
return;
|
||||
}
|
||||
);
|
||||
let _status = some_or!(status.code(), return);
|
||||
|
||||
let ir = match Irgen::default().translate(unit) {
|
||||
Ok(ir) => ir,
|
||||
Err(irgen_error) => panic!("{}", irgen_error),
|
||||
};
|
||||
|
||||
let temp_dir = tempdir().expect("temp dir creation failed");
|
||||
let temp_file_path = temp_dir.path().join("temp.c");
|
||||
let mut temp_file = File::create(&temp_file_path).unwrap();
|
||||
|
||||
crate::write(&ir, &mut temp_file).unwrap();
|
||||
|
||||
let new_ir = ir::Parse::default()
|
||||
.translate(&temp_file_path.as_path())
|
||||
.expect("parse failed while parsing the output from implemented printer");
|
||||
drop(temp_file);
|
||||
assert_eq!(ir, new_ir);
|
||||
temp_dir.close().expect("temp dir deletion failed");
|
||||
}
|
||||
|
||||
pub fn test_opt<P1: AsRef<Path>, P2: AsRef<Path>, O: Optimize<ir::TranslationUnit>>(
|
||||
from: &P1,
|
||||
to: &P2,
|
||||
opt: &mut O,
|
||||
) {
|
||||
let from = ir::Parse::default()
|
||||
.translate(from)
|
||||
.expect("parse failed while parsing the output from implemented printer");
|
||||
let mut ir = from.clone();
|
||||
let to = ir::Parse::default()
|
||||
.translate(to)
|
||||
.expect("parse failed while parsing the output from implemented printer");
|
||||
opt.optimize(&mut ir);
|
||||
|
||||
if ir != to {
|
||||
stderr()
|
||||
.lock()
|
||||
.write_fmt(format_args!(
|
||||
"[test_opt] actual outcome mismatches with the expected outcome.\n\n[before opt]"
|
||||
))
|
||||
.unwrap();
|
||||
crate::write(&from, &mut stderr()).unwrap();
|
||||
stderr()
|
||||
.lock()
|
||||
.write_fmt(format_args!("\n[after opt]"))
|
||||
.unwrap();
|
||||
crate::write(&ir, &mut stderr()).unwrap();
|
||||
stderr()
|
||||
.lock()
|
||||
.write_fmt(format_args!("\n[after opt (expected)]"))
|
||||
.unwrap();
|
||||
crate::write(&to, &mut stderr()).unwrap();
|
||||
panic!("[test_opt]");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ use lang_c::ast::*;
|
||||
|
||||
use kecc::*;
|
||||
|
||||
fn test_dir<F>(path: &Path, f: F)
|
||||
fn test_dir<F>(path: &Path, ext: &OsStr, f: F)
|
||||
where
|
||||
F: Fn(&TranslationUnit, &Path),
|
||||
{
|
||||
@@ -15,7 +15,7 @@ where
|
||||
let entry = ok_or!(entry, continue);
|
||||
let path = entry.path();
|
||||
|
||||
if !(path.is_file() && path.extension() == Some(&OsStr::new("c"))) {
|
||||
if !(path.is_file() && path.extension() == Some(ext)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -33,11 +33,45 @@ where
|
||||
|
||||
#[test]
|
||||
fn test_examples_write_c() {
|
||||
test_dir(Path::new("examples/"), test_write_c);
|
||||
test_dir(Path::new("examples/hw1"), test_write_c);
|
||||
test_dir(Path::new("examples/"), &OsStr::new("c"), test_write_c);
|
||||
test_dir(Path::new("examples/hw1"), &OsStr::new("c"), test_write_c);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_examples_irgen() {
|
||||
test_dir(Path::new("examples/"), test_irgen);
|
||||
test_dir(Path::new("examples/"), &OsStr::new("c"), test_irgen);
|
||||
}
|
||||
|
||||
// TODO: make it work!
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_examples_irparse() {
|
||||
test_dir(Path::new("examples/"), &OsStr::new("c"), test_irparse);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_examples_simplify_cfg() {
|
||||
test_opt(
|
||||
&Path::new("examples/simplify_cfg/const_prop.input.ir"),
|
||||
&Path::new("examples/simplify_cfg/const_prop.output.ir"),
|
||||
&mut FunctionPass::<SimplifyCfgConstProp>::default(),
|
||||
);
|
||||
|
||||
test_opt(
|
||||
&Path::new("examples/simplify_cfg/reach.input.ir"),
|
||||
&Path::new("examples/simplify_cfg/reach.output.ir"),
|
||||
&mut FunctionPass::<SimplifyCfgReach>::default(),
|
||||
);
|
||||
|
||||
test_opt(
|
||||
&Path::new("examples/simplify_cfg/merge.input.ir"),
|
||||
&Path::new("examples/simplify_cfg/merge.output.ir"),
|
||||
&mut FunctionPass::<SimplifyCfgMerge>::default(),
|
||||
);
|
||||
|
||||
test_opt(
|
||||
&Path::new("examples/simplify_cfg/empty.input.ir"),
|
||||
&Path::new("examples/simplify_cfg/empty.output.ir"),
|
||||
&mut FunctionPass::<SimplifyCfgEmpty>::default(),
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user