diff --git a/Cargo.lock b/Cargo.lock index db6898a..548b157 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -116,6 +116,31 @@ dependencies = [ "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "hexf" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "hexf-impl 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "hexf-parse 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "hexf-impl" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "hexf-parse 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "hexf-parse" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "itertools" version = "0.9.0" @@ -130,6 +155,7 @@ version = "0.1.0" 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)", + "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)", "ordered-float 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -167,6 +193,19 @@ name = "ppv-lite86" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "proc-macro-hack" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro-hack-impl 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "proc-macro-hack-impl" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "proc-macro2" version = "1.0.10" @@ -175,6 +214,11 @@ dependencies = [ "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "quote" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "quote" version = "1.0.3" @@ -243,6 +287,16 @@ name = "strsim" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "syn" +version = "0.11.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "syn" version = "1.0.17" @@ -253,6 +307,14 @@ dependencies = [ "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "synom" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "synstructure" version = "0.12.3" @@ -290,6 +352,11 @@ name = "unicode-width" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "unicode-xid" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "unicode-xid" version = "0.2.0" @@ -344,13 +411,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "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 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)" = "" "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-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" "checksum proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)" = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3" +"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" "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" @@ -360,11 +433,14 @@ 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 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" "checksum syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)" = "0df0eb663f387145cab623dea85b09c2c5b4b0aef44e945d928e682fce71bb03" +"checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" "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" "checksum unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" +"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" "checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" "checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" "checksum wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" diff --git a/Cargo.toml b/Cargo.toml index 31c0c12..ee1bb4f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,3 +31,4 @@ itertools = "0.9.0" failure = "0.1.7" tempfile = "3.1.0" ordered-float = "1.0" +hexf = "0.1.0" diff --git a/Jenkinsfile b/Jenkinsfile index f2b6599..d414326 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -35,8 +35,12 @@ pipeline { stage('Test') { steps { setupRust() - sh "cargo test" - sh "cargo test --release" + // 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" } } } diff --git a/examples/cmp.c b/examples/cmp.c new file mode 100644 index 0000000..4d68ddf --- /dev/null +++ b/examples/cmp.c @@ -0,0 +1,18 @@ +int int_greater_than(int i, unsigned int j) { + if (i > j) return 1; + else return 0; +} + +int char_greater_than(char i, unsigned char j) { + if (i > j) return 1; + else return 0; +} + +int main() { + // cmp ugt + int r1 = int_greater_than(-1, 1); + // cmp sgt + int r2 = char_greater_than(-1, 1); + + return r1 == 1 && r2 == 0; +} diff --git a/src/c/parse.rs b/src/c/parse.rs index be36ba6..9b46237 100644 --- a/src/c/parse.rs +++ b/src/c/parse.rs @@ -217,7 +217,7 @@ impl AssertSupported for Declarator { fn assert_supported(&self) { self.kind.assert_supported(); self.derived.assert_supported(); - self.extensions.is_empty(); + assert!(self.extensions.is_empty()); } } @@ -282,7 +282,7 @@ impl AssertSupported for ParameterDeclaration { fn assert_supported(&self) { self.specifiers.assert_supported(); self.declarator.assert_supported(); - self.extensions.is_empty(); + assert!(self.extensions.is_empty()); } } diff --git a/src/ir/dtype.rs b/src/ir/dtype.rs index a6694a3..b395709 100644 --- a/src/ir/dtype.rs +++ b/src/ir/dtype.rs @@ -23,7 +23,7 @@ pub trait HasDtype { #[derive(Default)] struct BaseDtype { scalar: Option, - size_modifiers: Option, + size_modifier: Option, signed_option: Option, typedef_name: Option, is_const: bool, @@ -129,12 +129,12 @@ impl BaseDtype { self.scalar = Some(type_specifier.clone()); } ast::TypeSpecifier::Short | ast::TypeSpecifier::Long => { - if self.size_modifiers.is_some() { + if self.size_modifier.is_some() { return Err(DtypeError::Misc { message: "two or more size modifiers in declaration specifiers".to_string(), }); } - self.size_modifiers = Some(type_specifier.clone()); + self.size_modifier = Some(type_specifier.clone()); } ast::TypeSpecifier::TypedefName(identifier) => { if self.typedef_name.is_some() { @@ -277,7 +277,7 @@ impl TryFrom for Dtype { fn try_from(spec: BaseDtype) -> Result { assert!( !(spec.scalar.is_none() - && spec.size_modifiers.is_none() + && spec.size_modifier.is_none() && spec.signed_option.is_none() && spec.typedef_name.is_none() && !spec.is_const), @@ -313,19 +313,19 @@ impl TryFrom for Dtype { }; // Applies size modifier - if let Some(size_modifiers) = spec.size_modifiers { + if let Some(size_modifier) = spec.size_modifier { if dtype != Self::INT { return Err(DtypeError::Misc { message: "size modifier can only be used with `int`".to_string(), }); } - dtype = match size_modifiers { + dtype = match size_modifier { ast::TypeSpecifier::Short => Self::SHORT, ast::TypeSpecifier::Long => Self::LONG, _ => panic!( "Dtype::try_from::: {:?} is not a size modifier", - size_modifiers + size_modifier ), } } @@ -341,6 +341,13 @@ impl TryFrom for Dtype { ), }; + if dtype.get_int_width().is_none() { + return Err(DtypeError::Misc { + message: "`signed` and `unsigned` only be applied to `Dtype::Int`" + .to_string(), + }); + } + dtype = dtype.set_signed(is_signed); } @@ -662,12 +669,18 @@ impl Dtype { self.with_ast_array_size(&array_decl.node.size)? } ast::DerivedDeclarator::Function(func_decl) => { - let params = func_decl + let mut params = func_decl .node .parameters .iter() .map(|p| Self::try_from(&p.node)) .collect::, _>>()?; + + // If function parameter is (void), remove it + if params.len() == 1 && params[0] == Dtype::unit() { + let _ = params.pop(); + } + Self::function(self, params) } ast::DerivedDeclarator::KRFunction(kr_func_decl) => { @@ -758,16 +771,6 @@ impl Dtype { 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), - }) - } - } } impl fmt::Display for Dtype { diff --git a/src/ir/interp.rs b/src/ir/interp.rs index 345609c..06d7311 100644 --- a/src/ir/interp.rs +++ b/src/ir/interp.rs @@ -286,27 +286,31 @@ mod calculator { 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)) + Ok(Value::int(result, 1, false)) } ast::BinaryOperator::NotEquals => { let result = if lhs != rhs { 1 } else { 0 }; - Ok(Value::int(result, 1, lhs_s)) + Ok(Value::int(result, 1, false)) } ast::BinaryOperator::Less => { + // TODO: consider signed option let result = if lhs < rhs { 1 } else { 0 }; - Ok(Value::int(result, 1, lhs_s)) + Ok(Value::int(result, 1, false)) } ast::BinaryOperator::Greater => { + // TODO: consider signed option let result = if lhs > rhs { 1 } else { 0 }; - Ok(Value::int(result, 1, lhs_s)) + Ok(Value::int(result, 1, false)) } ast::BinaryOperator::LessOrEqual => { + // TODO: consider signed option let result = if lhs <= rhs { 1 } else { 0 }; - Ok(Value::int(result, 1, lhs_s)) + Ok(Value::int(result, 1, false)) } ast::BinaryOperator::GreaterOrEqual => { + // TODO: consider signed option let result = if lhs >= rhs { 1 } else { 0 }; - Ok(Value::int(result, 1, lhs_s)) + Ok(Value::int(result, 1, false)) } ast::BinaryOperator::LogicalAnd => { assert!(lhs < 2); diff --git a/src/ir/mod.rs b/src/ir/mod.rs index 997816e..64729d6 100644 --- a/src/ir/mod.rs +++ b/src/ir/mod.rs @@ -5,6 +5,7 @@ mod write_ir; use core::convert::TryFrom; use core::fmt; use core::ops::{Deref, DerefMut}; +use hexf::{parse_hexf32, parse_hexf64}; use lang_c::ast; use ordered_float::OrderedFloat; use std::collections::HashMap; @@ -504,23 +505,44 @@ impl TryFrom<&ast::Constant> for Constant { } .set_signed(is_signed); + let pat = match integer.base { + ast::IntegerBase::Decimal => Self::DECIMAL, + ast::IntegerBase::Octal => Self::OCTAL, + ast::IntegerBase::Hexadecimal => Self::HEXADECIMAL, + }; + let value = if is_signed { - integer.number.parse::().unwrap() as u128 + i128::from_str_radix(integer.number.deref(), pat).unwrap() as u128 } else { - integer.number.parse::().unwrap() + u128::from_str_radix(integer.number.deref(), pat).unwrap() }; Ok(Self::int(value, dtype)) } ast::Constant::Float(float) => { + let pat = match float.base { + ast::FloatBase::Decimal => Self::DECIMAL, + ast::FloatBase::Hexadecimal => Self::HEXADECIMAL, + }; + let (dtype, value) = match float.suffix.format { ast::FloatFormat::Float => { // Casting from an f32 to an f64 is perfect and lossless (f32 -> f64) // https://doc.rust-lang.org/stable/reference/expressions/operator-expr.html#type-cast-expressions - (Dtype::FLOAT, float.number.parse::().unwrap() as f64) + let value = if pat == Self::DECIMAL { + float.number.parse::().unwrap() as f64 + } else { + parse_hexf32(float.number.deref(), false).unwrap() as f64 + }; + (Dtype::FLOAT, value) } ast::FloatFormat::Double => { - (Dtype::DOUBLE, float.number.parse::().unwrap()) + let value = if pat == Self::DECIMAL { + float.number.parse::().unwrap() + } else { + parse_hexf64(float.number.deref(), false).unwrap() + }; + (Dtype::DOUBLE, value) } ast::FloatFormat::LongDouble => { panic!("`FloatFormat::LongDouble` is_unsupported") @@ -567,6 +589,10 @@ impl TryFrom<&ast::Initializer> for Constant { } impl Constant { + const DECIMAL: u32 = 10; + const OCTAL: u32 = 8; + const HEXADECIMAL: u32 = 16; + #[inline] pub fn is_integer_constant(&self) -> bool { if let Self::Int { .. } = self { diff --git a/src/tests.rs b/src/tests.rs index abdaa38..ddb6cd6 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -57,13 +57,21 @@ pub fn test_irgen(unit: &TranslationUnit, path: &Path) { .status() .expect("failed to remove compiled executable"); - let ir = Irgen::default() - .translate(unit) - .expect("failed to generate ir"); + let ir = match Irgen::default().translate(unit) { + Ok(ir) => ir, + Err(irgen_error) => panic!("{}", irgen_error), + }; let args = Vec::new(); - assert_eq!( - ir::interp(&ir, args), - Ok(ir::Value::int(status as u128, 32, true)) - ); + let result = match ir::interp(&ir, args) { + Ok(result) => result, + Err(interp_error) => panic!("{}", interp_error), + }; + let result = if let ir::Value::Int { .. } = &result { + result + } else { + panic!("non-integer value occurs") + }; + + assert_eq!(result, ir::Value::int(status as u128, 32, true)); } diff --git a/tests/fuzz.py b/tests/fuzz.py index f7ebcb2..86a05e3 100644 --- a/tests/fuzz.py +++ b/tests/fuzz.py @@ -36,6 +36,23 @@ REPLACE_DICT = { "\"g_\w*\", ": "", # transparent_crc에서 프린트 목적으로 받은 StringLiteral 삭제 "char\* vname, ": "", # transparent_crc에서 사용하지 않는 파라미터 삭제 r"[^\n]*_IO_2_1_[^;]*;": "", # extern을 지우면서 생긴 size를 알 수 없는 struct 삭제 + r"__asm\s*\([^\)]*\)": "", # asm extension in mac + r"__asm__\s*\([^\)]*\)": "", # asm extension in linux + + # To check fuzzer before make kecc support struct type + "struct[^}]*};": "", + " struct[^{]*[^}]*}[^;]*;": "", + "typedef struct _IO_FILE __FILE;": "", + "struct _IO_FILE;": "", + "typedef struct _IO_FILE FILE;": "typedef int FILE;", + "typedef struct _IO_FILE": "typedef int", + "typedef struct __locale_struct": "typedef int", + "typedef __locale_t locale_t;": "typedef int locale_t;", + "struct _IO_FILE_plus;": "", + "typedef _G_fpos_t": "typedef int", + "typedef struct[^\n]*\n{[^}]*}[^;]*;": "", + "typedef struct[^{]{[^}]*}": "typedef int", + "struct _IO_FILE": "int", } CSMITH_DIR = "csmith-2.3.0"