diff --git a/Cargo.lock b/Cargo.lock index 47701a7..db6898a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,29 +13,34 @@ name = "atty" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "hermit-abi 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "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)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "autocfg" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "backtrace" -version = "0.3.45" +version = "0.3.46" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "backtrace-sys 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace-sys 0.1.35 (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.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (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.34" +version = "0.1.35" 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.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -78,7 +83,7 @@ name = "failure" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "backtrace 0.3.45 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace 0.3.46 (registry+https://github.com/rust-lang/crates.io-index)", "failure_derive 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -87,9 +92,9 @@ name = "failure_derive" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.9 (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)", - "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", "synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -99,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.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (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.8" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -126,20 +131,37 @@ dependencies = [ "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.7 (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 (registry+https://github.com/rust-lang/crates.io-index)", + "lang-c 0.8.0 (git+https://github.com/kaist-cp/lang-c)", + "ordered-float 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "lang-c" version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" +source = "git+https://github.com/kaist-cp/lang-c#44687707f07fb8c5869936dfc549459ecb30d3be" [[package]] name = "libc" -version = "0.2.67" +version = "0.2.68" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "num-traits" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ordered-float" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "ppv-lite86" version = "0.2.6" @@ -147,7 +169,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "proc-macro2" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -158,7 +180,7 @@ name = "quote" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -167,7 +189,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.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (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)", @@ -223,10 +245,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "syn" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.9 (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)", "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -236,9 +258,9 @@ name = "synstructure" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.9 (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)", - "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -248,7 +270,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.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (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)", @@ -310,8 +332,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" "checksum atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -"checksum backtrace 0.3.45 (registry+https://github.com/rust-lang/crates.io-index)" = "ad235dabf00f36301792cfe82499880ba54c6486be094d1047b02bacb67c14e8" -"checksum backtrace-sys 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)" = "ca797db0057bae1a7aa2eef3283a874695455cecf08a43bfb8507ee0ebc1ed69" +"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 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" @@ -320,12 +343,14 @@ 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.8 (registry+https://github.com/rust-lang/crates.io-index)" = "1010591b26bbfe835e9faeabeb11866061cc7dcebffd56ad7d0942d0e61aefd8" +"checksum hermit-abi 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "725cf19794cf90aa94e65050cb4191ff5d8fa87a498383774c47b332e3af952e" "checksum itertools 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" -"checksum lang-c 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "86efc420d5d7407655eb2ff1a77d7c81463307f1b204c886f7608cc2e6506d55" -"checksum libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)" = "eb147597cdf94ed43ab7a9038716637d2d1bf2bc571da995d0028dec06bd3018" +"checksum lang-c 0.8.0 (git+https://github.com/kaist-cp/lang-c)" = "" +"checksum libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)" = "dea0c0405123bba743ee3f91f49b1c7cfb684eef0da0a50110f758ccf24cdff0" +"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 ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" -"checksum proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "6c09721c6781493a2a492a96b5a5bf19b65917fe6728884e7c44dd0c60ca3435" +"checksum proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)" = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3" "checksum quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f" "checksum rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" "checksum rand_chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" @@ -335,7 +360,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" "checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" "checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" -"checksum syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)" = "123bd9499cfb380418d509322d7a6d52e5315f064fe4b3ad18a53d6b92c07859" +"checksum syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)" = "0df0eb663f387145cab623dea85b09c2c5b4b0aef44e945d928e682fce71bb03" "checksum synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" "checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" "checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" diff --git a/Cargo.toml b/Cargo.toml index ac4d17e..31c0c12 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,9 @@ path = "bin/fuzz.rs" [dependencies] clap = { version = "2.33.0", features = ["yaml"] } -lang-c = "0.8.0" +# 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" } itertools = "0.9.0" failure = "0.1.7" tempfile = "3.1.0" +ordered-float = "1.0" diff --git a/examples/array.c b/examples/array.c new file mode 100644 index 0000000..2ace5c6 --- /dev/null +++ b/examples/array.c @@ -0,0 +1,9 @@ +int sum(int len, int p[2][3]) { + return 0; +} + +int main() { + int a[2][3]; + + return 0; +} diff --git a/examples/hw1/complete_cond.c b/examples/complete_cond.c similarity index 100% rename from examples/hw1/complete_cond.c rename to examples/complete_cond.c diff --git a/examples/hw1/cond_and_loop.c b/examples/cond_and_loop.c similarity index 100% rename from examples/hw1/cond_and_loop.c rename to examples/cond_and_loop.c diff --git a/examples/hw1/gcd.c b/examples/gcd.c similarity index 100% rename from examples/hw1/gcd.c rename to examples/gcd.c diff --git a/examples/hw1/temp2.c b/examples/hw1/temp2.c index fd7fbab..290a0a5 100644 --- a/examples/hw1/temp2.c +++ b/examples/hw1/temp2.c @@ -1,4 +1,4 @@ -int f(int i, int const a[const i]) { +int f(int i, int const a[i]) { int temp = 0; const float temp2 = 0.f, temp3 = 0.f; temp = sizeof(unsigned char); diff --git a/examples/test.c b/examples/test.c new file mode 100644 index 0000000..896a578 --- /dev/null +++ b/examples/test.c @@ -0,0 +1,10 @@ +int main() { + long int l = 1; + long l2 = 2; + short int s = 3; + short s2 = 4; + int i = 5; + char c = 6; + + return l + l2 + s + s2 + i + c; +} diff --git a/examples/typedef.c b/examples/typedef.c new file mode 100644 index 0000000..0fa4e94 --- /dev/null +++ b/examples/typedef.c @@ -0,0 +1,10 @@ +typedef int i32_t; +typedef i32_t* p_i32_t; + +int main() { + i32_t a = 0; + p_i32_t const b = &a; + *b = 1; + + return *b; +} diff --git a/src/c/parse.rs b/src/c/parse.rs index c644f4d..be36ba6 100644 --- a/src/c/parse.rs +++ b/src/c/parse.rs @@ -76,7 +76,20 @@ impl AssertSupported for TranslationUnit { impl AssertSupported for ExternalDeclaration { fn assert_supported(&self) { match self { - Self::Declaration(decl) => decl.assert_supported(), + Self::Declaration(decl) => { + for spec in &decl.node.specifiers { + if let DeclarationSpecifier::StorageClass(storage_class) = &spec.node { + // `typedef` is allowed only when it is used in the external declaration. + if StorageClassSpecifier::Typedef != storage_class.node { + panic!("`StorageClassifier` other than `Typedef`") + } + } else { + spec.assert_supported(); + } + } + + decl.node.declarators.assert_supported(); + } Self::StaticAssert(_) => panic!("ExternalDeclaration::StaticAssert"), Self::FunctionDefinition(fdef) => fdef.assert_supported(), } @@ -102,9 +115,7 @@ impl AssertSupported for FunctionDefinition { impl AssertSupported for DeclarationSpecifier { fn assert_supported(&self) { match self { - Self::StorageClass(storage_class_specifier) => { - storage_class_specifier.assert_supported() - } + Self::StorageClass(_) => panic!("DeclarationSpecifier::StorageClass"), Self::TypeSpecifier(type_specifier) => type_specifier.assert_supported(), Self::TypeQualifier(type_qualifier) => type_qualifier.assert_supported(), Self::Function(_) => panic!("DeclarationSpecifier::Function"), @@ -114,15 +125,6 @@ impl AssertSupported for DeclarationSpecifier { } } -impl AssertSupported for StorageClassSpecifier { - fn assert_supported(&self) { - match self { - Self::Typedef => (), - _ => panic!("StorageClassifier other than Typedef"), - } - } -} - impl AssertSupported for TypeSpecifier { fn assert_supported(&self) { match self { @@ -242,7 +244,11 @@ impl AssertSupported for PointerQualifier { impl AssertSupported for ArrayDeclarator { fn assert_supported(&self) { - self.qualifiers.assert_supported(); + // In C99, type qualifier(e.g., const) is allowed when + // array declarator is used as function parameter. + // However, KECC does not allow this feature because + // it complicates IR generating logic. + assert!(self.qualifiers.is_empty()); self.size.assert_supported(); } } diff --git a/src/ir/dtype.rs b/src/ir/dtype.rs index 6c90756..7e3fe2e 100644 --- a/src/ir/dtype.rs +++ b/src/ir/dtype.rs @@ -1,12 +1,13 @@ use core::convert::TryFrom; use core::fmt; use core::ops::Deref; -use itertools::izip; +use failure::Fail; use lang_c::ast; use lang_c::span::Node; +use std::collections::HashMap; use std::hash::Hash; -use failure::Fail; +use crate::ir::*; #[derive(Debug, PartialEq, Fail)] pub enum DtypeError { @@ -22,8 +23,11 @@ pub trait HasDtype { #[derive(Default)] struct BaseDtype { scalar: Option, + size_modifiers: Option, signed_option: Option, + typedef_name: Option, is_const: bool, + is_typedef: bool, } #[derive(Debug, PartialEq, Eq, Hash, Clone)] @@ -52,9 +56,40 @@ pub enum Dtype { ret: Box, params: Vec, }, + Typedef { + name: String, + is_const: bool, + }, } impl BaseDtype { + /// Apply `StorageClassSpecifier` to `BaseDtype` + /// + /// let's say declaration is `typedef int i32_t;`, if `self` represents `int` + /// and `type_qualifier` represents `typedef`, `self` is transformed to + /// representing `typedef int` after function performs. + /// + /// # Arguments + /// + /// * `self` - Part that has been converted to 'BaseDtype' on the declaration + /// * `storage_class` - storage class requiring apply to 'self' immediately + /// + #[inline] + fn apply_storage_class( + &mut self, + storage_class: &ast::StorageClassSpecifier, + ) -> Result<(), DtypeError> { + match storage_class { + ast::StorageClassSpecifier::Typedef => { + // duplicate `typedef` is allowed + self.is_typedef = true; + } + _ => panic!("unsupported storage class"), + } + + Ok(()) + } + /// Apply `TypeSpecifier` to `BaseDtype` /// /// let's say declaration is `const int a;`, if `self` represents `int` @@ -81,9 +116,11 @@ impl BaseDtype { self.signed_option = Some(type_specifier.clone()); } ast::TypeSpecifier::Void + | ast::TypeSpecifier::Bool | ast::TypeSpecifier::Char | ast::TypeSpecifier::Int - | ast::TypeSpecifier::Float => { + | ast::TypeSpecifier::Float + | ast::TypeSpecifier::Double => { if self.scalar.is_some() { return Err(DtypeError::Misc { message: "two or more scalar types in declaration specifiers".to_string(), @@ -91,7 +128,23 @@ impl BaseDtype { } self.scalar = Some(type_specifier.clone()); } - _ => todo!("support more like `double` in the future"), + ast::TypeSpecifier::Short | ast::TypeSpecifier::Long => { + if self.size_modifiers.is_some() { + return Err(DtypeError::Misc { + message: "two or more size modifiers in declaration specifiers".to_string(), + }); + } + self.size_modifiers = Some(type_specifier.clone()); + } + ast::TypeSpecifier::TypedefName(identifier) => { + if self.typedef_name.is_some() { + return Err(DtypeError::Misc { + message: "two or more typedef names in declaration specifiers".to_string(), + }); + } + self.typedef_name = Some(identifier.node.name.clone()); + } + _ => todo!("apply_type_specifier: support {:?}", type_specifier), } Ok(()) @@ -145,9 +198,8 @@ impl BaseDtype { declaration_specifier: &ast::DeclarationSpecifier, ) -> Result<(), DtypeError> { match declaration_specifier { - // TODO: `dtype` must be defined taking into account all specifier information. - ast::DeclarationSpecifier::StorageClass(_storage_class_spec) => { - todo!("analyze storage class specifier keyword to create correct `dtype`") + ast::DeclarationSpecifier::StorageClass(storage_class) => { + self.apply_storage_class(&storage_class.node)? } ast::DeclarationSpecifier::TypeSpecifier(type_specifier) => { self.apply_type_specifier(&type_specifier.node)? @@ -221,48 +273,80 @@ impl TryFrom for Dtype { /// # Example /// /// For declaration is `const unsigned int * p`, `specifiers` is `const unsigned int`, - /// and the result is `Dtype::Int{ width: 32, is_signed: false, is_const: ture }` + /// and the result is `Dtype::Int{ width: 4, is_signed: false, is_const: ture }` fn try_from(spec: BaseDtype) -> Result { assert!( - !(spec.scalar.is_none() && spec.signed_option.is_none() && !spec.is_const), + !(spec.scalar.is_none() + && spec.size_modifiers.is_none() + && spec.signed_option.is_none() + && spec.typedef_name.is_none() + && !spec.is_const), "BaseDtype is empty" ); - // Creates `dtype` from scalar. - let mut dtype = if let Some(t) = spec.scalar { - match t { - ast::TypeSpecifier::Void => Self::unit(), - ast::TypeSpecifier::Unsigned | ast::TypeSpecifier::Signed => { - panic!("Signed option to scalar is not supported") - } - ast::TypeSpecifier::Bool => Self::BOOL, - ast::TypeSpecifier::Char => Self::CHAR, - ast::TypeSpecifier::Short => Self::SHORT, - ast::TypeSpecifier::Int => Self::INT, - ast::TypeSpecifier::Long => Self::LONG, - ast::TypeSpecifier::Float => Self::FLOAT, - ast::TypeSpecifier::Double => Self::DOUBLE, - _ => panic!("Unsupported ast::TypeSpecifier"), + let mut dtype = if let Some(name) = spec.typedef_name { + if spec.scalar.is_some() || spec.signed_option.is_some() { + return Err(DtypeError::Misc { + message: "typedef' cannot be used with scalar type or signed option" + .to_string(), + }); } - } else { - Dtype::default() - }; - // Applies signedness. - if let Some(signed_option) = spec.signed_option { - let is_signed = match signed_option { - ast::TypeSpecifier::Signed => true, - ast::TypeSpecifier::Unsigned => false, - _ => panic!( - "`signed_option` must be `TypeSpecifier::Signed` or `TypeSpecifier::Unsigned`" - ), + Self::typedef(name) + } else { + // Creates `dtype` from scalar. + let mut dtype = if let Some(t) = spec.scalar { + match t { + ast::TypeSpecifier::Void => Self::unit(), + ast::TypeSpecifier::Bool => Self::BOOL, + ast::TypeSpecifier::Char => Self::CHAR, + ast::TypeSpecifier::Int => Self::INT, + ast::TypeSpecifier::Float => Self::FLOAT, + ast::TypeSpecifier::Double => Self::DOUBLE, + ast::TypeSpecifier::Unsigned | ast::TypeSpecifier::Signed => { + panic!("Signed option to scalar is not supported") + } + _ => panic!("Dtype::try_from::: {:?} is not a scalar type", t), + } + } else { + Self::default() }; - dtype = dtype.set_signed(is_signed); - } + // Applies size modifier + if let Some(size_modifiers) = spec.size_modifiers { + if dtype != Self::INT { + return Err(DtypeError::Misc { + message: "size modifier can only be used with `int`".to_string(), + }); + } + + dtype = match size_modifiers { + ast::TypeSpecifier::Short => Self::SHORT, + ast::TypeSpecifier::Long => Self::LONG, + _ => panic!( + "Dtype::try_from::: {:?} is not a size modifier", + size_modifiers + ), + } + } + + // Applies signedness. + if let Some(signed_option) = spec.signed_option { + let is_signed = match signed_option { + ast::TypeSpecifier::Signed => true, + ast::TypeSpecifier::Unsigned => false, + _ => panic!( + "Dtype::try_from::: {:?} is not a signed option", + signed_option + ), + }; + + dtype = dtype.set_signed(is_signed); + } + + dtype + }; - // Applies constness. - assert!(!dtype.is_const()); dtype = dtype.set_const(spec.is_const); Ok(dtype) @@ -296,25 +380,48 @@ impl TryFrom<&ast::ParameterDeclaration> for Dtype { if let Some(declarator) = ¶meter_decl.declarator { dtype = dtype.with_ast_declarator(&declarator.node)?; + + // A function call with an array argument performs array-to-pointer conversion. + // For this reason, when `declarator` is from function parameter declaration + // and `base_dtype` is `Dtype::Array`, `base_dtype` is transformed to pointer type. + // https://www.eskimo.com/~scs/cclass/notes/sx10f.html + if let Some(inner) = dtype.get_array_inner() { + dtype = Self::pointer(inner.clone()); + } } Ok(dtype) } } impl Dtype { - pub const BOOL: Self = Self::int(1); - pub const CHAR: Self = Self::int(8); - pub const SHORT: Self = Self::int(16); - pub const INT: Self = Self::int(32); - pub const LONG: Self = Self::int(64); - pub const LONGLONG: Self = Self::int(64); - - pub const FLOAT: Self = Self::float(32); - pub const DOUBLE: Self = Self::float(64); - - const WIDTH_OF_BYTE: usize = 8; + pub const BITS_OF_BYTE: usize = 8; + pub const SIZE_OF_BYTE: usize = 1; // TODO: consider architecture dependency in the future - const WIDTH_OF_POINTER: usize = 32; + pub const SIZE_OF_POINTER: usize = 4; + + pub const SIZE_OF_CHAR: usize = 1; + pub const SIZE_OF_SHORT: usize = 2; + pub const SIZE_OF_INT: usize = 4; + pub const SIZE_OF_LONG: usize = 8; + pub const SIZE_OF_LONGLONG: usize = 8; + + pub const SIZE_OF_FLOAT: usize = 4; + pub const SIZE_OF_DOUBLE: usize = 8; + + // signed option cannot be applied to boolean value + pub const BOOL: Self = Self::Int { + width: 1, + is_signed: false, + is_const: false, + }; + pub const CHAR: Self = Self::int(Self::SIZE_OF_CHAR * Self::BITS_OF_BYTE); + pub const SHORT: Self = Self::int(Self::SIZE_OF_SHORT * Self::BITS_OF_BYTE); + pub const INT: Self = Self::int(Self::SIZE_OF_INT * Self::BITS_OF_BYTE); + pub const LONG: Self = Self::int(Self::SIZE_OF_LONG * Self::BITS_OF_BYTE); + pub const LONGLONG: Self = Self::int(Self::SIZE_OF_LONGLONG * Self::BITS_OF_BYTE); + + pub const FLOAT: Self = Self::float(Self::SIZE_OF_FLOAT * Self::BITS_OF_BYTE); + pub const DOUBLE: Self = Self::float(Self::SIZE_OF_DOUBLE * Self::BITS_OF_BYTE); #[inline] pub const fn unit() -> Self { @@ -378,6 +485,14 @@ impl Dtype { } } + #[inline] + pub fn typedef(name: String) -> Self { + Self::Typedef { + name, + is_const: false, + } + } + #[inline] pub fn get_int_width(&self) -> Option { if let Self::Int { width, .. } = self { @@ -405,6 +520,15 @@ impl Dtype { } } + #[inline] + pub fn get_array_inner(&self) -> Option<&Dtype> { + if let Self::Array { inner, .. } = self { + Some(inner.deref()) + } else { + None + } + } + #[inline] pub fn get_function_inner(&self) -> Option<(&Dtype, &Vec)> { if let Self::Function { ret, params } = self { @@ -441,6 +565,7 @@ impl Dtype { Self::Pointer { is_const, .. } => *is_const, Self::Array { .. } => true, Self::Function { .. } => true, + Self::Typedef { .. } => panic!("typedef should be replaced by real dtype"), } } @@ -458,6 +583,7 @@ impl Dtype { Self::Pointer { inner, .. } => Self::Pointer { inner, is_const }, Self::Array { .. } => self, Self::Function { .. } => self, + Self::Typedef { name, .. } => Self::Typedef { name, is_const }, } } @@ -465,17 +591,12 @@ impl Dtype { match self { Self::Unit { .. } => Ok((0, 1)), Self::Int { width, .. } | Self::Float { width, .. } => { - let align_of = *width / Self::WIDTH_OF_BYTE; - let size_of = align_of; - - Ok((size_of, align_of)) - } - Self::Pointer { .. } => { - let align_of = Self::WIDTH_OF_POINTER / Self::WIDTH_OF_BYTE; - let size_of = align_of; + let size_of = (*width + Self::BITS_OF_BYTE - 1) / Self::BITS_OF_BYTE; + let align_of = size_of; Ok((size_of, align_of)) } + Self::Pointer { .. } => Ok((Self::SIZE_OF_POINTER, Self::SIZE_OF_POINTER)), Self::Array { inner, size, .. } => { let (size_of_inner, align_of_inner) = inner.size_align_of()?; @@ -484,9 +605,8 @@ impl Dtype { align_of_inner, )) } - Self::Function { .. } => Err(DtypeError::Misc { - message: "`size_align_of` cannot be used with function types".to_string(), - }), + Self::Function { .. } => Ok((0, 1)), + Self::Typedef { .. } => panic!("typedef should be replaced by real dtype"), } } @@ -506,22 +626,26 @@ impl Dtype { /// Derive a data type from declaration specifiers. pub fn try_from_ast_declaration_specifiers( specifiers: &[Node], - ) -> Result { + ) -> Result<(Self, bool), DtypeError> { let mut spec = BaseDtype::default(); BaseDtype::apply_declaration_specifiers(&mut spec, specifiers)?; - Self::try_from(spec) + let is_typedef = spec.is_typedef; + let dtype = Self::try_from(spec)?; + + Ok((dtype, is_typedef)) } - /// Generate `Dtype` based on declarator and `base_dtype` which has scalar type. + /// Generate `Dtype` based on declarator and `self` which has scalar type. /// /// let's say declaration is `const int * const * const a;`. - /// In general `base_dtype` start with `const int` which has scalar type and + /// In general `self` start with `const int` which has scalar type and /// `declarator` represents `* const * const` with `ast::Declarator` /// /// # Arguments /// /// * `declarator` - Parts requiring conversion to 'Dtype' on the declaration - /// * `base_dtype` - Part that has been converted to 'Dtype' on the declaration + /// * `decl_spec` - Containing information that should be referenced + /// when creating `Dtype` from `Declarator`. /// pub fn with_ast_declarator(mut self, declarator: &ast::Declarator) -> Result { for derived_decl in &declarator.derived { @@ -533,7 +657,10 @@ impl Dtype { } Self::pointer(self).set_const(specifier.is_const) } - ast::DerivedDeclarator::Array(_array_decl) => todo!(), + ast::DerivedDeclarator::Array(array_decl) => { + assert!(array_decl.node.qualifiers.is_empty()); + self.with_ast_array_size(&array_decl.node.size)? + } ast::DerivedDeclarator::Function(func_decl) => { let params = func_decl .node @@ -561,37 +688,81 @@ impl Dtype { } } - /// Check whether type conflict exists between the two `Dtype` objects. + /// Generates `Dtype` based on declarator and `self` which has scalar type. /// - /// let's say expression is `const int a = 0; int b = 0; int c = a + b`. - /// Although `const int` of `a` and `int` of `b` looks different, `Plus`(+) operations between - /// these two types are possible without any type-casting. There is no conflict between - /// `const int` and `int`. + /// Let's say the AST declaration is `int a[2][3]`; `self` represents `int [2]`; and + /// `array_size` is `[3]`. Then this function should return `int [2][3]`. /// - /// However, only the outermost const is ignored. - /// If check equivalence between `const int *const` and `int *`, result is false. Because - /// the second `const` (means left most `const`) of the `const int *const` is missed in `int *`. - /// By the way, outermost `const` (means right most `const`) is not a consideration here. - pub fn is_compatible(&self, other: &Self) -> bool { - match (self, other) { - (Self::Unit { .. }, Self::Unit { .. }) - | (Self::Int { .. }, Self::Int { .. }) - | (Self::Float { .. }, Self::Float { .. }) - | (Self::Pointer { .. }, Self::Pointer { .. }) => { - self.clone().set_const(false) == other.clone().set_const(false) + /// # Arguments + /// + /// * `array_size` - the array size to add to the dtype `self` + /// + pub fn with_ast_array_size(self, array_size: &ast::ArraySize) -> Result { + let expr = if let ast::ArraySize::VariableExpression(expr) = array_size { + &expr.node + } else { + panic!("`ArraySize` is unsupported except `ArraySize::VariableExpression`") + }; + + let constant = Constant::try_from(expr) + .expect("expression of `ArraySize::VariableExpression` must be constant value"); + + let (value, _, is_signed) = constant.get_int().ok_or_else(|| DtypeError::Misc { + message: "expression is not an integer constant expression".to_string(), + })?; + + if is_signed && (value as i128) < 0 { + return Err(DtypeError::Misc { + message: "declared as an array with a negative size".to_string(), + }); + } + + Ok(Self::array(self, value as usize)) + } + + pub fn resolve_typedefs(self, typedefs: &HashMap) -> Result { + let dtype = match &self { + Self::Unit { .. } | Self::Int { .. } | Self::Float { .. } => self, + Self::Pointer { inner, is_const } => { + let inner = inner.deref().clone().resolve_typedefs(typedefs)?; + Dtype::pointer(inner).set_const(*is_const) } - ( - Self::Function { ret, params }, - Self::Function { - ret: other_ret, - params: other_params, - }, - ) => { - ret == other_ret - && params.len() == other_params.len() - && izip!(params, other_params).all(|(l, r)| l.is_compatible(r)) + Self::Array { inner, size } => { + let inner = inner.deref().clone().resolve_typedefs(typedefs)?; + Dtype::array(inner, *size) } - _ => false, + Self::Function { ret, params } => { + let ret = ret.deref().clone().resolve_typedefs(typedefs)?; + let params = params + .iter() + .map(|p| p.clone().resolve_typedefs(typedefs)) + .collect::, _>>()?; + + Dtype::function(ret, params) + } + Self::Typedef { name, is_const } => { + let dtype = typedefs + .get(name) + .ok_or_else(|| DtypeError::Misc { + message: format!("unknown type name `{}`", name), + })? + .clone(); + let is_const = dtype.is_const() || *is_const; + + dtype.set_const(is_const) + } + }; + + Ok(dtype) + } + + pub fn merge(self, other: Self) -> Result { + if self == other { + Ok(self) + } else { + Err(DtypeError::Misc { + message: format!("Dtype::merge({:?}, {:?}) failed", self, other), + }) } } } @@ -615,7 +786,7 @@ 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 { "" }) + write!(f, "{}*{}", inner, if *is_const { "const" } else { "" }) } Self::Array { inner, size, .. } => write!(f, "[{} x {}]", size, inner,), Self::Function { ret, params } => write!( @@ -628,6 +799,9 @@ impl fmt::Display for Dtype { .collect::>() .join(", ") ), + Self::Typedef { name, is_const } => { + write!(f, "{}{}", if *is_const { "const " } else { "" }, name) + } } } } diff --git a/src/ir/interp.rs b/src/ir/interp.rs index 2dc76b0..8f4ec27 100644 --- a/src/ir/interp.rs +++ b/src/ir/interp.rs @@ -1,4 +1,5 @@ use core::fmt; +use core::iter; use core::mem; use failure::Fail; use std::collections::HashMap; @@ -31,11 +32,39 @@ pub enum Value { }, Pointer { bid: Option, - offset: usize, + offset: isize, + dtype: Dtype, + }, + Array { + inner_dtype: Dtype, + values: Vec, }, } +impl HasDtype for Value { + fn dtype(&self) -> Dtype { + match self { + Self::Undef { dtype } => dtype.clone(), + Self::Unit => Dtype::unit(), + Self::Int { + width, is_signed, .. + } => Dtype::int(*width).set_signed(*is_signed), + Self::Float { width, .. } => Dtype::float(*width), + Self::Pointer { dtype, .. } => Dtype::pointer(dtype.clone()), + Self::Array { + inner_dtype, + values, + } => Dtype::array(inner_dtype.clone(), values.len()), + } + } +} + impl Value { + #[inline] + fn undef(dtype: Dtype) -> Self { + Self::Undef { dtype } + } + #[inline] fn unit() -> Self { Self::Unit @@ -56,8 +85,8 @@ impl Value { } #[inline] - fn pointer(bid: Option, offset: usize) -> Self { - Self::Pointer { bid, offset } + fn pointer(bid: Option, offset: isize, dtype: Dtype) -> Self { + Self::Pointer { bid, offset, dtype } } #[inline] @@ -75,19 +104,20 @@ impl Value { } #[inline] - fn get_pointer(self) -> Option<(Option, usize)> { - if let Value::Pointer { bid, offset } = self { - Some((bid, offset)) + fn get_pointer(&self) -> Option<(&Option, &isize, &Dtype)> { + if let Value::Pointer { bid, offset, dtype } = self { + Some((bid, offset, dtype)) } else { None } } #[inline] - fn nullptr() -> Self { + fn nullptr(dtype: Dtype) -> Self { Self::Pointer { bid: None, offset: 0, + dtype, } } @@ -99,9 +129,10 @@ impl Value { width, is_signed, .. } => Self::int(u128::default(), *width, *is_signed), ir::Dtype::Float { width, .. } => Self::float(f64::default(), *width), - ir::Dtype::Pointer { .. } => Self::nullptr(), + ir::Dtype::Pointer { inner, .. } => Self::nullptr(inner.deref().clone()), ir::Dtype::Array { .. } => panic!("array type does not have a default value"), ir::Dtype::Function { .. } => panic!("function type does not have a default value"), + ir::Dtype::Typedef { .. } => panic!("typedef should be replaced by real dtype"), } } } @@ -248,9 +279,11 @@ mod calculator { assert_eq!(lhs_s, rhs_s); match op { + // TODO: consider signed value in the future ast::BinaryOperator::Plus => Ok(Value::int(lhs + rhs, lhs_w, lhs_s)), ast::BinaryOperator::Minus => Ok(Value::int(lhs - rhs, lhs_w, lhs_s)), ast::BinaryOperator::Multiply => Ok(Value::int(lhs * rhs, lhs_w, lhs_s)), + ast::BinaryOperator::Modulo => Ok(Value::int(lhs % rhs, lhs_w, lhs_s)), ast::BinaryOperator::Equals => { let result = if lhs == rhs { 1 } else { 0 }; Ok(Value::int(result, 1, lhs_s)) @@ -263,11 +296,28 @@ mod calculator { let result = if lhs < rhs { 1 } else { 0 }; Ok(Value::int(result, 1, lhs_s)) } + ast::BinaryOperator::Greater => { + let result = if lhs > rhs { 1 } else { 0 }; + Ok(Value::int(result, 1, lhs_s)) + } + ast::BinaryOperator::LessOrEqual => { + let result = if lhs <= rhs { 1 } else { 0 }; + Ok(Value::int(result, 1, lhs_s)) + } ast::BinaryOperator::GreaterOrEqual => { let result = if lhs >= rhs { 1 } else { 0 }; Ok(Value::int(result, 1, lhs_s)) } - _ => todo!("will be covered all operator"), + ast::BinaryOperator::LogicalAnd => { + assert!(lhs < 2); + assert!(rhs < 2); + let result = lhs | rhs; + Ok(Value::int(result, 1, lhs_s)) + } + _ => todo!( + "calculate_binary_operator_expression: not supported operator {:?}", + op + ), } } _ => todo!(), @@ -336,45 +386,246 @@ mod calculator { } } +// TODO +#[allow(dead_code)] +#[derive(Debug, PartialEq, Eq, Hash, Clone)] +enum Byte { + Undef, + Concrete(u8), + Pointer { + bid: usize, + offset: isize, + index: usize, + }, +} + #[derive(Default, Debug, PartialEq)] struct Memory { - // TODO: memory type should change to Vec> - inner: Vec>, + inner: Vec>>, +} + +impl Byte { + #[inline] + fn concrete(byte: u8) -> Self { + Self::Concrete(byte) + } + + #[inline] + fn pointer(bid: usize, offset: isize, index: usize) -> Self { + Self::Pointer { bid, offset, index } + } + + fn get_concrete(&self) -> Option { + if let Self::Concrete(byte) = self { + Some(*byte) + } else { + None + } + } + + fn get_pointer(&self) -> Option<(usize, isize, usize)> { + if let Self::Pointer { bid, offset, index } = self { + Some((*bid, *offset, *index)) + } else { + None + } + } + + fn block_from_dtype(dtype: &Dtype) -> Vec { + let size = dtype.size_align_of().unwrap().0; + iter::repeat(Self::Undef).take(size).collect() + } + + fn u128_to_bytes(mut value: u128, size: usize) -> Vec { + let divisor = 1u128 << Dtype::BITS_OF_BYTE; + let mut bytes = Vec::new(); + for _ in 0..size { + bytes.push((value % divisor) as u8); + value /= divisor; + } + + bytes + } + + fn bytes_to_u128(bytes: &[u8], is_signed: bool) -> u128 { + let width = bytes.len(); + assert!(0 < width && width <= 16); + + let is_negative = is_signed && *bytes.last().unwrap() >= 128; + let mut array = [if is_negative { 255 } else { 0 }; 16]; + array[0..width].copy_from_slice(bytes); + u128::from_le_bytes(array) + } + + fn bytes_to_value<'b, I>(bytes: &mut I, dtype: &Dtype) -> Result + where + I: Iterator, + { + match dtype { + ir::Dtype::Unit { .. } => Ok(Value::Unit), + ir::Dtype::Int { + width, is_signed, .. + } => { + let value = some_or!( + bytes + .by_ref() + .take(*width) + .map(|b| b.get_concrete()) + .collect::>>(), + return Ok(Value::undef(dtype.clone())) + ); + let value = Self::bytes_to_u128(&value, *is_signed); + Ok(Value::int(value, *width, *is_signed)) + } + ir::Dtype::Float { width, .. } => { + let value = some_or!( + bytes + .by_ref() + .take(*width) + .map(|b| b.get_concrete()) + .collect::>>(), + return Ok(Value::undef(dtype.clone())) + ); + let value = Self::bytes_to_u128(&value, false); + let value = if *width == Dtype::SIZE_OF_FLOAT { + f32::from_bits(value as u32) as f64 + } else { + f64::from_bits(value as u64) + }; + + Ok(Value::float(value, *width)) + } + ir::Dtype::Pointer { inner, .. } => { + let value = some_or!( + bytes + .by_ref() + .take(Dtype::SIZE_OF_POINTER) + .map(|b| b.get_pointer()) + .collect::>>(), + return Ok(Value::undef(dtype.clone())) + ); + + let (bid, offset, _) = value.first().expect("not empty"); + + Ok( + if !value + .iter() + .enumerate() + .all(|(idx, ptr)| *ptr == (*bid, *offset, idx)) + { + Value::undef(inner.deref().clone()) + } else { + Value::pointer(Some(*bid), *offset, inner.deref().clone()) + }, + ) + } + ir::Dtype::Array { inner, size } => { + let (inner_size, inner_align) = inner.size_align_of().unwrap(); + let padding = std::cmp::max(inner_size, inner_align) - inner_size; + let values = (0..*size) + .map(|_| { + let value = Self::bytes_to_value(bytes, inner)?; + let _ = bytes.by_ref().take(padding); + Ok(value) + }) + .collect::, InterpreterError>>()?; + Ok(Value::Array { + inner_dtype: inner.deref().clone(), + values, + }) + } + ir::Dtype::Function { .. } => panic!("function value cannot be constructed from bytes"), + ir::Dtype::Typedef { .. } => panic!("typedef should be replaced by real dtype"), + } + } + + fn value_to_bytes(value: &Value) -> Vec { + match value { + Value::Undef { dtype } => Self::block_from_dtype(dtype), + Value::Unit => Vec::new(), + Value::Int { value, width, .. } => { + let size = (*width + Dtype::BITS_OF_BYTE - 1) / Dtype::BITS_OF_BYTE; + Self::u128_to_bytes(*value, size) + .iter() + .map(|b| Self::concrete(*b)) + .collect::>() + } + Value::Float { value, width } => { + let size = (*width + Dtype::BITS_OF_BYTE - 1) / Dtype::BITS_OF_BYTE; + let value: u128 = match size { + Dtype::SIZE_OF_FLOAT => (*value as f32).to_bits() as u128, + Dtype::SIZE_OF_DOUBLE => (*value as f64).to_bits() as u128, + _ => panic!("value_to_bytes: {} is not a valid float size", size), + }; + + Self::u128_to_bytes(value, size) + .iter() + .map(|b| Self::concrete(*b)) + .collect::>() + } + Value::Pointer { bid, offset, .. } => (0..Dtype::SIZE_OF_POINTER) + .map(|i| Self::pointer(bid.unwrap(), *offset, i)) + .collect(), + Value::Array { + inner_dtype, + values, + } => { + let (inner_size, inner_align) = inner_dtype.size_align_of().unwrap(); + let padding = std::cmp::max(inner_size, inner_align) - inner_size; + values + .iter() + .map(|v| { + let mut result = Self::value_to_bytes(v); + result.extend(iter::repeat(Byte::Undef).take(padding)); + result + }) + .flatten() + .collect() + } + } + } } impl Memory { fn alloc(&mut self, dtype: &Dtype) -> Result { - let memory_block = Self::block_from_dtype(dtype); - self.inner.push(memory_block); - - Ok(self.inner.len() - 1) + let bid = self.inner.len(); + self.inner.push(Some(Byte::block_from_dtype(dtype))); + Ok(bid) } - fn load(&self, bid: usize, offset: usize) -> &Value { - &self.inner[bid][offset] + fn dealloc( + &mut self, + bid: usize, + offset: isize, + dtype: &Dtype, + ) -> Result<(), InterpreterError> { + let block = &mut self.inner[bid]; + assert_eq!(offset, 0); + assert_eq!( + block.as_mut().unwrap().len(), + dtype.size_align_of().unwrap().0 + ); + *block = None; + Ok(()) } - fn store(&mut self, bid: usize, offset: usize, value: Value) { - self.inner[bid][offset] = value; + fn load(&self, bid: usize, offset: isize, dtype: &Dtype) -> Result { + assert!(0 <= offset); + let offset = offset as usize; + let size = dtype.size_align_of().unwrap().0; + let mut iter = self.inner[bid].as_ref().unwrap()[offset..(offset + size)].iter(); + Byte::bytes_to_value(&mut iter, dtype) } - fn block_from_dtype(dtype: &Dtype) -> Vec { - match dtype { - ir::Dtype::Unit { .. } => vec![], - ir::Dtype::Int { .. } | ir::Dtype::Float { .. } | ir::Dtype::Pointer { .. } => { - vec![Value::Undef { - dtype: dtype.clone(), - }] - } - ir::Dtype::Array { inner, size, .. } => { - let sub_vec = Self::block_from_dtype(inner.deref()); - (0..*size).fold(vec![], |mut result, _| { - result.append(&mut sub_vec.clone()); - result - }) - } - ir::Dtype::Function { .. } => vec![], - } + fn store(&mut self, bid: usize, offset: isize, value: &Value) { + assert!(0 <= offset); + let offset = offset as usize; + let size = value.dtype().size_align_of().unwrap().0; + let bytes = Byte::value_to_bytes(value); + self.inner[bid] + .as_mut() + .unwrap() + .splice(offset..(offset + size), bytes.iter().cloned()); } } @@ -443,10 +694,11 @@ impl<'i> State<'i> { Value::default_from_dtype(&dtype) }; - self.memory.store(bid, 0, value); + self.memory.store(bid, 0, &value); } ir::Dtype::Array { .. } => todo!("Initializer::List is needed"), ir::Dtype::Function { .. } => panic!("function variable does not exist"), + ir::Dtype::Typedef { .. } => panic!("typedef should be replaced by real dtype"), }, // If functin declaration, skip initialization Declaration::Function { .. } => (), @@ -460,8 +712,8 @@ impl<'i> State<'i> { // add alloc register for (id, allocation) in self.stack_frame.func_def.allocations.iter().enumerate() { let bid = self.memory.alloc(&allocation)?; - let ptr = Value::pointer(Some(bid), 0); - let rid = RegisterId::local("".to_string(), id); + let ptr = Value::pointer(Some(bid), 0, allocation.deref().clone()); + let rid = RegisterId::local(id); self.stack_frame.registers.write(rid, ptr) } @@ -498,7 +750,17 @@ impl<'i> State<'i> { // If it's returning from a function, pop the stack frame. - // TODO: free memory allocated in the callee + // Frees memory allocated in the callee + for (i, d) in self.stack_frame.func_def.allocations.iter().enumerate() { + let (bid, offset, dtype) = self + .stack_frame + .registers + .read(RegisterId::local(i)) + .get_pointer() + .unwrap(); + assert_eq!(d.deref(), dtype); + self.memory.dealloc(bid.unwrap(), *offset, dtype)?; + } // restore previous state let prev_stack_frame = some_or!(self.stack.pop(), return Ok(Some(return_value))); @@ -526,7 +788,8 @@ impl<'i> State<'i> { ) -> Result, InterpreterError> { // Check that the dtype of each args matches the expected if !(args.len() == signature.params.len() - && izip!(args, &signature.params).all(|(a, d)| a.dtype().is_compatible(d))) + && izip!(args, &signature.params) + .all(|(a, d)| a.dtype().set_const(false) == d.clone().set_const(false))) { panic!("dtype of args and params must be compatible") } @@ -546,16 +809,14 @@ impl<'i> State<'i> { assert_eq!(arg.args.len(), block.phinodes.len()); for (a, d) in izip!(&arg.args, &block.phinodes) { - assert!(a.dtype().is_compatible(&d)); + assert!(a.dtype().set_const(false) == d.deref().clone().set_const(false)); } for (i, a) in arg.args.iter().enumerate() { let v = self.interp_operand(a.clone()).unwrap(); self.stack_frame .registers - .inner - .insert(RegisterId::arg(arg.bid, i), v) - .unwrap(); + .write(RegisterId::arg(arg.bid, i), v); } self.stack_frame.pc = Pc::new(arg.bid); @@ -629,21 +890,20 @@ impl<'i> State<'i> { Instruction::Store { ptr, value, .. } => { let ptr = self.interp_operand(ptr.clone())?; let value = self.interp_operand(value.clone())?; - let (bid, offset) = self.interp_ptr(ptr)?; - self.memory.store(bid, offset, value); - + let (bid, offset, _) = self.interp_ptr(&ptr)?; + self.memory.store(bid, offset, &value); Value::Unit } Instruction::Load { ptr, .. } => { let ptr = self.interp_operand(ptr.clone())?; - let (bid, offset) = self.interp_ptr(ptr)?; - self.memory.load(bid, offset).clone() + let (bid, offset, dtype) = self.interp_ptr(&ptr)?; + self.memory.load(bid, offset, &dtype)? } Instruction::Call { callee, args, .. } => { let ptr = self.interp_operand(callee.clone())?; // Get function name from pointer - let (bid, _) = ptr.get_pointer().expect("`ptr` must be `Value::Pointer`"); + let (bid, _, _) = ptr.get_pointer().expect("`ptr` must be `Value::Pointer`"); let bid = bid.expect("pointer for global variable must have bid value"); let callee_name = self .global_map @@ -721,8 +981,11 @@ impl<'i> State<'i> { width, is_signed, }, - Constant::Float { value, width } => Value::Float { value, width }, - Constant::GlobalVariable { name, .. } => { + Constant::Float { value, width } => Value::Float { + value: value.into_inner(), + width, + }, + Constant::GlobalVariable { name, dtype } => { let bid = self .global_map .get_bid(&name) @@ -732,13 +995,14 @@ impl<'i> State<'i> { Value::Pointer { bid: Some(bid), offset: 0, + dtype, } } } } - fn interp_ptr(&mut self, pointer: Value) -> Result<(usize, usize), InterpreterError> { - let (bid, offset) = pointer + fn interp_ptr(&mut self, pointer: &Value) -> Result<(usize, isize, Dtype), InterpreterError> { + let (bid, offset, dtype) = pointer .get_pointer() .ok_or_else(|| InterpreterError::Misc { func_name: self.stack_frame.func_name.clone(), @@ -752,7 +1016,7 @@ impl<'i> State<'i> { msg: "Accessing memory with constant pointer".into(), })?; - Ok((bid, offset)) + Ok((bid, *offset, dtype.clone())) } } diff --git a/src/ir/mod.rs b/src/ir/mod.rs index 7fd0a77..e03bc51 100644 --- a/src/ir/mod.rs +++ b/src/ir/mod.rs @@ -4,8 +4,9 @@ mod write_ir; use core::convert::TryFrom; use core::fmt; -use core::ops::Deref; +use core::ops::{Deref, DerefMut}; use lang_c::ast; +use ordered_float::OrderedFloat; use std::collections::HashMap; use std::hash::{Hash, Hasher}; @@ -60,6 +61,7 @@ impl TryFrom for Declaration { signature: FunctionSignature::new(dtype), definition: None, }), + Dtype::Typedef { .. } => panic!("typedef should be replaced by real dtype"), } } } @@ -100,10 +102,6 @@ impl Declaration { } /// Check if type is conflicting for pre-declared one - /// - /// In case of `Variable`, need to check if the two types are exactly the same. - /// On the other hand, in the case of `Function`, outermost `const` of return type and - /// parameters one is not an issue of concern. pub fn is_compatible(&self, other: &Declaration) -> bool { match (self, other) { (Self::Variable { dtype, .. }, Self::Variable { dtype: other, .. }) => dtype == other, @@ -112,7 +110,7 @@ impl Declaration { Self::Function { signature: other, .. }, - ) => signature.dtype().is_compatible(&other.dtype()), + ) => signature.dtype() == other.dtype(), _ => false, } } @@ -127,19 +125,6 @@ impl HasDtype for Declaration { } } -#[derive(Debug, PartialEq, Clone)] -pub struct FunctionDefinition { - /// Memory allocations for local variables. The allocation is performed at the beginning of a - /// function invocation. - pub allocations: Vec, - - /// Basic blocks. - pub blocks: HashMap, - - /// The initial block id. - pub bid_init: BlockId, -} - #[derive(Debug, PartialEq, Clone)] pub struct FunctionSignature { pub ret: Dtype, @@ -164,15 +149,229 @@ impl HasDtype for FunctionSignature { } } +#[derive(Debug, PartialEq, Clone)] +pub struct FunctionDefinition { + /// Memory allocations for local variables. The allocation is performed at the beginning of a + /// function invocation. + pub allocations: Vec>, + + /// Basic blocks. + pub blocks: HashMap, + + /// The initial block id. + pub bid_init: BlockId, +} + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)] +pub struct BlockId(pub usize); + +impl fmt::Display for BlockId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "b{}", self.0) + } +} + +#[derive(Debug, PartialEq, Clone)] +pub struct Block { + pub phinodes: Vec>, + pub instructions: Vec>, + pub exit: BlockExit, +} + +#[derive(Debug, PartialEq, Clone)] +#[allow(clippy::large_enum_variant)] +pub enum Instruction { + // TODO: the variants of Instruction will be added in the future + Nop, + BinOp { + op: ast::BinaryOperator, + lhs: Operand, + rhs: Operand, + dtype: Dtype, + }, + UnaryOp { + op: ast::UnaryOperator, + operand: Operand, + dtype: Dtype, + }, + Store { + ptr: Operand, + value: Operand, + }, + Load { + ptr: Operand, + }, + Call { + callee: Operand, + args: Vec, + return_type: Dtype, + }, + TypeCast { + value: Operand, + target_dtype: Dtype, + }, +} + +impl HasDtype for Instruction { + fn dtype(&self) -> Dtype { + match self { + Self::Nop => Dtype::unit(), + Self::BinOp { dtype, .. } => dtype.clone(), + Self::UnaryOp { dtype, .. } => dtype.clone(), + Self::Store { .. } => Dtype::unit(), + Self::Load { ptr } => ptr + .dtype() + .get_pointer_inner() + .expect("Load instruction must have pointer value as operand") + .deref() + .clone() + .set_const(false), + Self::Call { return_type, .. } => return_type.clone(), + Self::TypeCast { target_dtype, .. } => target_dtype.clone(), + } + } +} + +impl Instruction { + pub fn is_pure(&self) -> bool { + match self { + Self::Store { .. } => false, + Self::Call { .. } => false, + _ => true, + } + } +} + +// TODO +#[derive(Debug, PartialEq, Clone)] +pub enum BlockExit { + Jump { + arg: JumpArg, + }, + ConditionalJump { + condition: Operand, + arg_then: JumpArg, + arg_else: JumpArg, + }, + Switch { + value: Operand, + default: JumpArg, + cases: Vec<(Constant, JumpArg)>, + }, + Return { + value: Operand, + }, + Unreachable, +} + +impl BlockExit { + pub fn walk_jump_args(&mut self, mut f: F) + where + F: FnMut(&mut JumpArg), + { + match self { + Self::Jump { arg } => f(arg), + Self::ConditionalJump { + arg_then, arg_else, .. + } => { + f(arg_then); + f(arg_else); + } + Self::Switch { default, cases, .. } => { + f(default); + for (_, arg) in cases { + f(arg); + } + } + Self::Return { .. } | Self::Unreachable => {} + } + } +} + +#[derive(Debug, PartialEq, Clone)] +pub struct JumpArg { + pub bid: BlockId, + pub args: Vec, +} + +impl JumpArg { + pub fn new(bid: BlockId, args: Vec) -> Self { + Self { bid, args } + } +} + +impl fmt::Display for JumpArg { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}({:?})", self.bid, self.args) + } +} + +#[derive(Debug, PartialEq, Clone)] +pub enum Operand { + Constant(Constant), + Register { rid: RegisterId, dtype: Dtype }, +} + +impl Operand { + pub fn constant(value: Constant) -> Self { + Self::Constant(value) + } + + pub fn register(rid: RegisterId, dtype: Dtype) -> Self { + Self::Register { rid, dtype } + } + + pub fn get_constant(&self) -> Option<&Constant> { + if let Self::Constant(constant) = self { + Some(constant) + } else { + None + } + } + + pub fn get_register(&self) -> Option<(&RegisterId, &Dtype)> { + if let Self::Register { rid, dtype } = self { + Some((rid, dtype)) + } else { + None + } + } + + pub fn get_register_mut(&mut self) -> Option<(&mut RegisterId, &mut Dtype)> { + if let Self::Register { rid, dtype } = self { + Some((rid, dtype)) + } else { + None + } + } +} + +impl fmt::Display for Operand { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Constant(value) => write!(f, "{}", value), + Self::Register { rid, .. } => write!(f, "{}", rid), + } + } +} + +impl HasDtype for Operand { + fn dtype(&self) -> Dtype { + match self { + Self::Constant(value) => value.dtype(), + Self::Register { dtype, .. } => dtype.clone(), + } + } +} + #[derive(Debug, Eq, Clone)] pub enum RegisterId { /// Registers holding pointers to local allocations. /// /// # Fields /// - /// - `name`: only for debugging purposes. - /// - `id`: local allocation id. - Local { name: String, id: usize }, + /// - `aid`: local allocation id. + Local { aid: usize }, /// Registers holding block arguments. /// @@ -193,8 +392,8 @@ pub enum RegisterId { } impl RegisterId { - pub fn local(name: String, id: usize) -> Self { - Self::Local { name, id } + pub fn local(aid: usize) -> Self { + Self::Local { aid } } pub fn arg(bid: BlockId, aid: usize) -> Self { @@ -209,9 +408,9 @@ impl RegisterId { impl fmt::Display for RegisterId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::Local { name, id } => write!(f, "%(local:{}:{})", name, id), - Self::Arg { bid, aid } => write!(f, "%(arg:{}:{})", bid, aid), - Self::Temp { bid, iid } => write!(f, "%({}:{})", bid, iid), + Self::Local { aid } => write!(f, "%l{}", aid), + Self::Arg { bid, aid } => write!(f, "%{}:p{}", bid, aid), + Self::Temp { bid, iid } => write!(f, "%{}:i{}", bid, iid), } } } @@ -219,7 +418,7 @@ impl fmt::Display for RegisterId { impl PartialEq for RegisterId { fn eq(&self, other: &RegisterId) -> bool { match (self, other) { - (Self::Local { id, .. }, Self::Local { id: other_id, .. }) => id == other_id, + (Self::Local { aid }, Self::Local { aid: other_aid }) => aid == other_aid, ( Self::Arg { bid, aid }, Self::Arg { @@ -242,7 +441,7 @@ impl PartialEq for RegisterId { impl Hash for RegisterId { fn hash(&self, state: &mut H) { match self { - Self::Local { id, .. } => id.hash(state), + Self::Local { aid } => aid.hash(state), Self::Arg { bid, aid } => { // TODO: needs to distinguish arg/temp? bid.hash(state); @@ -256,7 +455,7 @@ impl Hash for RegisterId { } } -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Constant { Undef { dtype: Dtype, @@ -273,7 +472,7 @@ pub enum Constant { /// * Casting from an f32 to an f64 is perfect and lossless (f32 -> f64) /// * Casting from an f64 to an f32 will produce the closest possible value (f64 -> f32) /// https://doc.rust-lang.org/stable/reference/expressions/operator-expr.html#type-cast-expressions - value: f64, + value: OrderedFloat, width: usize, }, GlobalVariable { @@ -397,7 +596,10 @@ impl Constant { .get_float_width() .expect("`dtype` must be `Dtype::Float`"); - Self::Float { value, width } + Self::Float { + value: value.into(), + width, + } } #[inline] @@ -405,6 +607,20 @@ impl Constant { Self::GlobalVariable { name, dtype } } + #[inline] + pub fn get_int(&self) -> Option<(u128, usize, bool)> { + if let Self::Int { + value, + width, + is_signed, + } = self + { + Some((*value, *width, *is_signed)) + } else { + None + } + } + pub fn is_undef(&self) -> bool { if let Self::Undef { .. } = self { true @@ -441,179 +657,30 @@ impl HasDtype for Constant { } #[derive(Debug, PartialEq, Clone)] -pub enum Operand { - Constant(Constant), - Register { rid: RegisterId, dtype: Dtype }, +pub struct Named { + name: Option, + inner: T, } -impl Operand { - pub fn constant(value: Constant) -> Self { - Self::Constant(value) - } +impl Deref for Named { + type Target = T; - pub fn register(rid: RegisterId, dtype: Dtype) -> Self { - Self::Register { rid, dtype } + fn deref(&self) -> &Self::Target { + &self.inner } - - pub fn get_constant(&self) -> Option<&Constant> { - if let Self::Constant(constant) = self { - Some(constant) - } else { - None - } - } - - pub fn get_register(&self) -> Option<(&RegisterId, &Dtype)> { - if let Self::Register { rid, dtype } = self { - Some((rid, dtype)) - } else { - None - } - } - - pub fn get_register_mut(&mut self) -> Option<(&mut RegisterId, &mut Dtype)> { - if let Self::Register { rid, dtype } = self { - Some((rid, dtype)) - } else { - None - } +} +impl DerefMut for Named { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner } } -impl fmt::Display for Operand { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Constant(value) => write!(f, "{}", value), - Self::Register { rid, .. } => write!(f, "{}", rid), - } +impl Named { + pub fn new(name: Option, inner: T) -> Self { + Self { name, inner } + } + + pub fn name(&self) -> Option<&String> { + self.name.as_ref() } } - -impl HasDtype for Operand { - fn dtype(&self) -> Dtype { - match self { - Self::Constant(value) => value.dtype(), - Self::Register { dtype, .. } => dtype.clone(), - } - } -} - -#[derive(Debug, PartialEq, Clone)] -#[allow(clippy::large_enum_variant)] -pub enum Instruction { - // TODO: the variants of Instruction will be added in the future - Nop, - BinOp { - op: ast::BinaryOperator, - lhs: Operand, - rhs: Operand, - dtype: Dtype, - }, - UnaryOp { - op: ast::UnaryOperator, - operand: Operand, - dtype: Dtype, - }, - Store { - ptr: Operand, - value: Operand, - }, - Load { - ptr: Operand, - }, - Call { - callee: Operand, - args: Vec, - return_type: Dtype, - }, - TypeCast { - value: Operand, - target_dtype: Dtype, - }, -} - -impl HasDtype for Instruction { - fn dtype(&self) -> Dtype { - match self { - Self::Nop => Dtype::unit(), - Self::BinOp { dtype, .. } => dtype.clone(), - Self::UnaryOp { dtype, .. } => dtype.clone(), - Self::Store { .. } => Dtype::unit(), - Self::Load { ptr } => ptr - .dtype() - .get_pointer_inner() - .expect("Load instruction must have pointer value as operand") - .deref() - .clone() - .set_const(false), - Self::Call { return_type, .. } => return_type.clone(), - Self::TypeCast { target_dtype, .. } => target_dtype.clone(), - } - } -} - -impl Instruction { - pub fn is_pure(&self) -> bool { - match self { - Self::Store { .. } => false, - Self::Call { .. } => false, - _ => true, - } - } -} - -#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] -pub struct BlockId(pub usize); - -impl fmt::Display for BlockId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "b{}", self.0) - } -} - -#[derive(Debug, PartialEq, Clone)] -pub struct JumpArg { - pub bid: BlockId, - pub args: Vec, -} - -impl JumpArg { - pub fn new(bid: BlockId, args: Vec) -> Self { - Self { bid, args } - } -} - -impl fmt::Display for JumpArg { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}({:?})", self.bid, self.args) - } -} - -// TODO -#[derive(Debug, PartialEq, Clone)] -pub enum BlockExit { - Jump { - arg: JumpArg, - }, - ConditionalJump { - condition: Operand, - arg_then: JumpArg, - arg_else: JumpArg, - }, - Switch { - value: Operand, - default: JumpArg, - cases: Vec<(Constant, JumpArg)>, - }, - Return { - value: Operand, - }, - Unreachable, -} - -#[derive(Debug, PartialEq, Clone)] -pub struct Block { - pub phinodes: Vec, - pub instructions: Vec, - pub exit: BlockExit, -} diff --git a/src/ir/write_ir.rs b/src/ir/write_ir.rs index 2bb3d08..f4c1850 100644 --- a/src/ir/write_ir.rs +++ b/src/ir/write_ir.rs @@ -58,25 +58,33 @@ impl WriteLine for (&String, &Declaration) { match definition.as_ref() { Some(defintion) => { + // print function definition + writeln!(write, "define {} {{", declaration)?; // print meta data for function writeln!( write, - "; function meta data:\n; bid_init: {}\n; allocations: {}", + "init:\n bid: {}\n allocations: \n{}\n", defintion.bid_init, defintion .allocations .iter() .enumerate() - .map(|(i, a)| format!("{}:{}", i, a)) + .map(|(i, a)| format!( + " %l{}:{}{}", + i, + a.deref(), + if let Some(name) = a.name() { + format!(":{}", name) + } else { + "".into() + } + )) .collect::>() - .join(", ") + .join("\n") )?; - // print function definition - writeln!(write, "define {} {{", declaration)?; - for (id, block) in &defintion.blocks { - writeln!(write, "; {}", id)?; + writeln!(write, "block {}", id)?; (id, block).write_line(indent + 1, write)?; writeln!(write)?; } @@ -99,13 +107,33 @@ impl WriteLine for (&String, &Declaration) { impl WriteLine for (&BlockId, &Block) { fn write_line(&self, indent: usize, write: &mut dyn Write) -> Result<()> { + for (i, phi) in self.1.phinodes.iter().enumerate() { + write_indent(indent, write)?; + writeln!( + write, + "{}:{}{}", + RegisterId::arg(*self.0, i), + phi.deref(), + if let Some(name) = phi.name() { + format!(":{}", name) + } else { + "".into() + } + )?; + } + for (i, instr) in self.1.instructions.iter().enumerate() { write_indent(indent, write)?; writeln!( write, - "{}:{} = {}", + "{}:{}{} = {}", RegisterId::temp(*self.0, i), instr.dtype(), + if let Some(name) = instr.name() { + format!(":{}", name) + } else { + "".into() + }, instr.write_string() )?; } diff --git a/src/opt/mod.rs b/src/opt/mod.rs index a098819..6ef0c04 100644 --- a/src/opt/mod.rs +++ b/src/opt/mod.rs @@ -9,9 +9,6 @@ mod simplify_cfg; pub use deadcode::Deadcode; pub use gvn::Gvn; pub use mem2reg::Mem2reg; -pub use opt_utils::{ - make_cfg, make_domtree, replace_operand, replace_operands, reverse_cfg, Domtree, Walk, -}; pub use simplify_cfg::SimplifyCfg; use crate::ir; diff --git a/src/opt/opt_utils.rs b/src/opt/opt_utils.rs index 358d1a8..e69de29 100644 --- a/src/opt/opt_utils.rs +++ b/src/opt/opt_utils.rs @@ -1,171 +0,0 @@ -#![allow(dead_code)] - -use std::collections::HashMap; - -use crate::ir::*; - -/// "Replace-all-uses-with". -pub trait Walk { - fn walk(&mut self, f: F) -> bool - where - F: FnMut(&mut Operand) -> bool; -} - -impl Walk for FunctionDefinition { - fn walk(&mut self, mut f: F) -> bool - where - F: FnMut(&mut Operand) -> bool, - { - self.blocks.iter_mut().any(|(_, block)| block.walk(&mut f)) - } -} - -impl Walk for Block { - fn walk(&mut self, mut f: F) -> bool - where - F: FnMut(&mut Operand) -> bool, - { - self.instructions.iter_mut().any(|i| i.walk(&mut f)) || self.exit.walk(&mut f) - } -} - -impl Walk for Instruction { - fn walk(&mut self, mut f: F) -> bool - where - F: FnMut(&mut Operand) -> bool, - { - match self { - Self::Nop => false, - Self::BinOp { lhs, rhs, .. } => lhs.walk(&mut f) || rhs.walk(&mut f), - Self::UnaryOp { operand, .. } => operand.walk(&mut f), - Self::Store { ptr, value } => ptr.walk(&mut f) || value.walk(&mut f), - Self::Load { ptr } => ptr.walk(&mut f), - Self::Call { callee, args, .. } => { - callee.walk(&mut f) || args.iter_mut().any(|a| a.walk(&mut f)) - } - Self::TypeCast { value, .. } => value.walk(&mut f), - } - } -} - -impl Walk for BlockExit { - fn walk(&mut self, mut f: F) -> bool - where - F: FnMut(&mut Operand) -> bool, - { - match self { - Self::Jump { arg } => arg.walk(&mut f), - Self::ConditionalJump { - condition, - arg_then, - arg_else, - } => condition.walk(&mut f) || arg_then.walk(&mut f) || arg_else.walk(&mut f), - Self::Switch { - value, - default, - cases, - } => { - value.walk(&mut f) - || default.walk(&mut f) - || cases.iter_mut().any(|(_, a)| a.walk(&mut f)) - } - Self::Return { value } => value.walk(&mut f), - Self::Unreachable => false, - } - } -} - -impl Walk for JumpArg { - fn walk(&mut self, mut f: F) -> bool - where - F: FnMut(&mut Operand) -> bool, - { - self.args.iter_mut().any(|a| a.walk(&mut f)) - } -} - -impl Walk for Operand { - fn walk(&mut self, mut f: F) -> bool - where - F: FnMut(&mut Operand) -> bool, - { - f(self) - } -} - -pub fn replace_operand(operand: &mut Operand, from: &Operand, to: &Operand) -> bool { - if operand == from { - *operand = to.clone(); - true - } else { - false - } -} - -pub fn replace_operands(operand: &mut Operand, dict: &HashMap) -> bool { - if let Some((rid, dtype)) = operand.get_register_mut() { - if let Some(val) = dict.get(rid) { - assert_eq!(*dtype, val.dtype()); - *operand = val.clone(); - return true; - } - } - false -} - -pub fn make_cfg(fdef: &FunctionDefinition) -> HashMap> { - let mut result = HashMap::new(); - - for (bid, block) in &fdef.blocks { - let mut args = Vec::new(); - match &block.exit { - BlockExit::Jump { arg } => args.push(arg.clone()), - BlockExit::ConditionalJump { - arg_then, arg_else, .. - } => { - args.push(arg_then.clone()); - args.push(arg_else.clone()); - } - BlockExit::Switch { default, cases, .. } => { - args.push(default.clone()); - for (_, arg) in cases { - args.push(arg.clone()); - } - } - _ => {} - } - result.insert(*bid, args); - } - result -} - -pub fn reverse_cfg( - cfg: &HashMap>, -) -> HashMap> { - let mut result = HashMap::new(); - - for (bid, jumps) in cfg { - for jump in jumps { - result - .entry(jump.bid) - .or_insert_with(Vec::new) - .push((*bid, jump.clone())); - } - } - result -} - -pub struct Domtree {} - -impl Domtree { - pub fn walk(&self, _f: F) - where - F: FnMut(BlockId, BlockId), - { - todo!() - } -} - -pub fn make_domtree(_cfg: &HashMap>) -> Domtree { - todo!() -} diff --git a/src/tests.rs b/src/tests.rs index ed3a287..ffd2163 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -1,5 +1,5 @@ use lang_c::ast::*; -use std::fs::File; +use std::fs::{self, File}; use std::path::Path; use std::process::Command; use tempfile::tempdir; @@ -35,7 +35,7 @@ pub fn test_irgen(unit: &TranslationUnit, path: &Path) { .expect("failed to compile the given program"); // Execute compiled executable - let status = Command::new(bin_path.clone()) + let status = Command::new(fs::canonicalize(format!("./{}", bin_path.clone())).unwrap()) .status() .expect("failed to execute the compiled executable") .code() diff --git a/tests/fuzz.py b/tests/fuzz.py index be8c422..f7ebcb2 100644 --- a/tests/fuzz.py +++ b/tests/fuzz.py @@ -27,14 +27,15 @@ REPLACE_DICT = { "union": "struct", r"enum[\w\s]*\{[^\}]*\};": "", r"typedef enum[\w\s]*\{[^;]*;[\s_A-Z]*;": "", - "const char \*const sys_errlist\[\];": "", + "const char \*const sys_errlist\[\];": "", # ArraySize::Unknown 삭제 r"[^\n]*printf[^;]*;": "", r"[^\n]*scanf[^;]*;": "", " restrict": "", "inline ": "", "_Nullable": "", - "\"g_\w*\", ": "", - "char\* vname, ": "", + "\"g_\w*\", ": "", # transparent_crc에서 프린트 목적으로 받은 StringLiteral 삭제 + "char\* vname, ": "", # transparent_crc에서 사용하지 않는 파라미터 삭제 + r"[^\n]*_IO_2_1_[^;]*;": "", # extern을 지우면서 생긴 size를 알 수 없는 struct 삭제 } CSMITH_DIR = "csmith-2.3.0" @@ -94,9 +95,8 @@ def generate(tests_dir, bin_path): global CSMITH_DIR options = [ "--no-argc", "--no-arrays", - "--no-jumps", "--no-longlong", "--no-int8", - "--no-uint8", "--no-safe-math", "--no-pointers", - "--no-structs", "--no-unions", "--no-builtins", + "--no-jumps", "--no-pointers", + "--no-structs", "--no-unions", ] args = [bin_path] + options