diff --git a/src/c/ast_equiv.rs b/src/c/ast_equiv.rs index dcb8ae5..aee2977 100644 --- a/src/c/ast_equiv.rs +++ b/src/c/ast_equiv.rs @@ -102,6 +102,7 @@ impl IsEquiv for Declarator { impl IsEquiv for DeclaratorKind { fn is_equiv(&self, other: &Self) -> bool { match (self, other) { + (Self::Abstract, Self::Abstract) => true, (Self::Identifier(identifier), Self::Identifier(other_identifier)) => { identifier.node.name == other_identifier.node.name } diff --git a/src/c/parse.rs b/src/c/parse.rs index ee6ceac..c644f4d 100644 --- a/src/c/parse.rs +++ b/src/c/parse.rs @@ -525,7 +525,6 @@ impl AssertSupported for Integer { impl AssertSupported for Float { fn assert_supported(&self) { - assert_eq!(self.base, FloatBase::Decimal); self.suffix.format.assert_supported(); assert_eq!(false, self.suffix.imaginary); } diff --git a/src/ir/interp.rs b/src/ir/interp.rs index 419275a..e992224 100644 --- a/src/ir/interp.rs +++ b/src/ir/interp.rs @@ -11,24 +11,61 @@ use crate::*; // TODO: the variants of Value will be added in the future #[derive(Debug, PartialEq, Clone)] pub enum Value { - Undef, Unit, - Int(i32), - Float(f32), - Bool(bool), - Pointer { bid: Option, offset: usize }, + Int { + value: u128, + width: usize, + is_signed: bool, + }, + Float { + /// `value` may be `f32`, but it is possible to consider it as `f64`. + /// + /// * 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, + width: usize, + }, + Pointer { + bid: Option, + offset: usize, + }, } impl Value { + #[inline] + fn unit() -> Self { + Self::Unit + } + + #[inline] + pub fn int(value: u128, width: usize, is_signed: bool) -> Self { + Self::Int { + value, + width, + is_signed, + } + } + + #[inline] + fn float(value: f64, width: usize) -> Self { + Self::Float { value, width } + } + #[inline] fn pointer(bid: Option, offset: usize) -> Self { Self::Pointer { bid, offset } } #[inline] - fn get_bool(self) -> Option { - if let Value::Bool(value) = self { - Some(value) + fn get_int(self) -> Option<(u128, usize, bool)> { + if let Value::Int { + value, + width, + is_signed, + } = self + { + Some((value, width, is_signed)) } else { None } @@ -54,13 +91,11 @@ impl Value { #[inline] fn default_from_dtype(dtype: &Dtype) -> Self { match dtype { - // TODO: consider `Unit` value in the future - ir::Dtype::Unit { .. } => todo!(), - ir::Dtype::Int { width, .. } => match width { - 32 => Self::Int(i32::default()), - _ => todo!("other cases will be covered"), - }, - ir::Dtype::Float { .. } => Self::Float(f32::default()), + ir::Dtype::Unit { .. } => Self::unit(), + ir::Dtype::Int { + 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::Function { .. } => panic!("function types do not have a default value"), } @@ -183,31 +218,51 @@ mod calculator { use super::Value; use lang_c::ast; + // TODO: change to template function in the future pub fn calculate_binary_operator_expression( op: &ast::BinaryOperator, lhs: Value, rhs: Value, ) -> Result { match (op, lhs, rhs) { - (_, Value::Undef, _) => Err(()), - (_, _, Value::Undef) => Err(()), - (ast::BinaryOperator::Plus, Value::Int(lhs), Value::Int(rhs)) => { - Ok(Value::Int(lhs + rhs)) - } - (ast::BinaryOperator::Minus, Value::Int(lhs), Value::Int(rhs)) => { - Ok(Value::Int(lhs - rhs)) - } - (ast::BinaryOperator::Equals, Value::Int(lhs), Value::Int(rhs)) => { - Ok(Value::Bool(lhs == rhs)) - } - (ast::BinaryOperator::NotEquals, Value::Int(lhs), Value::Int(rhs)) => { - Ok(Value::Bool(lhs != rhs)) - } - (ast::BinaryOperator::Less, Value::Int(lhs), Value::Int(rhs)) => { - Ok(Value::Bool(lhs < rhs)) - } - (ast::BinaryOperator::GreaterOrEqual, Value::Int(lhs), Value::Int(rhs)) => { - Ok(Value::Bool(lhs >= rhs)) + ( + op, + Value::Int { + value: lhs, + width: lhs_w, + is_signed: lhs_s, + }, + Value::Int { + value: rhs, + width: rhs_w, + is_signed: rhs_s, + }, + ) => { + assert_eq!(lhs_w, rhs_w); + assert_eq!(lhs_s, rhs_s); + + match op { + 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::Equals => { + let result = if lhs == rhs { 1 } else { 0 }; + Ok(Value::int(result, 1, lhs_s)) + } + ast::BinaryOperator::NotEquals => { + let result = if lhs != rhs { 1 } else { 0 }; + Ok(Value::int(result, 1, lhs_s)) + } + ast::BinaryOperator::Less => { + 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"), + } } _ => todo!(), } @@ -218,21 +273,57 @@ mod calculator { operand: Value, ) -> Result { match (op, operand) { - (_, Value::Undef) => Err(()), - (ast::UnaryOperator::Plus, Value::Int(value)) => Ok(Value::Int(value)), - (ast::UnaryOperator::Minus, Value::Int(value)) => Ok(Value::Int(-value)), - (ast::UnaryOperator::Negate, Value::Bool(value)) => Ok(Value::Bool(!value)), + ( + ast::UnaryOperator::Plus, + Value::Int { + value, + width, + is_signed, + }, + ) => Ok(Value::int(value, width, is_signed)), + ( + ast::UnaryOperator::Minus, + Value::Int { + value, + width, + is_signed, + }, + ) => { + assert!(is_signed); + let result = -(value as i128); + Ok(Value::int(result as u128, width, is_signed)) + } + ( + ast::UnaryOperator::Negate, + Value::Int { + value, + width, + is_signed, + }, + ) => { + // Check if it is boolean + assert!(width == 1); + let result = if value == 0 { 1 } else { 0 }; + Ok(Value::int(result, width, is_signed)) + } _ => todo!(), } } - pub fn calculate_typecast(value: &Value, dtype: &crate::ir::Dtype) -> Result { + pub fn calculate_typecast(value: Value, dtype: crate::ir::Dtype) -> Result { match (value, dtype) { - (Value::Int(_), crate::ir::Dtype::Int { .. }) => Ok(value.clone()), - (Value::Bool(b), crate::ir::Dtype::Int { .. }) => { - Ok(Value::Int(if *b { 1 } else { 0 })) + // TODO: distinguish zero/signed extension in the future + // TODO: consider truncate in the future + ( + Value::Int { value, .. }, + crate::ir::Dtype::Int { + width, is_signed, .. + }, + ) => Ok(Value::int(value, width, is_signed)), + (Value::Float { value, .. }, crate::ir::Dtype::Float { width, .. }) => { + Ok(Value::float(value, width)) } - _ => todo!("calculate_typecast ({:?}) {:?}", dtype, value), + (value, dtype) => todo!("calculate_typecast ({:?}) {:?}", dtype, value), } } } @@ -246,14 +337,11 @@ struct Memory { impl Memory { fn alloc(&mut self, dtype: &Dtype) -> Result { let memory_block = match dtype { - Dtype::Unit { .. } => vec![], - Dtype::Int { width, .. } => match width { - 32 => vec![Value::Undef], - _ => todo!(), - }, - Dtype::Float { .. } => todo!(), - Dtype::Pointer { .. } => vec![Value::Undef], - Dtype::Function { .. } => vec![], + ir::Dtype::Unit { .. } + | ir::Dtype::Int { .. } + | ir::Dtype::Float { .. } + | ir::Dtype::Pointer { .. } => vec![Value::default_from_dtype(dtype)], + ir::Dtype::Function { .. } => vec![], }; self.inner.push(memory_block); @@ -327,13 +415,14 @@ impl<'i> State<'i> { // Initialize allocated memory space match decl { Declaration::Variable { dtype, initializer } => { - let value = if let Some(constant) = initializer { - self.interp_constant(constant.clone()) - } else { - Value::default_from_dtype(dtype) - }; + if dtype.get_function_inner().is_some() { + panic!("function variable does not exist") + } - self.memory.store(bid, 0, value); + if let Some(constant) = initializer { + let value = self.interp_constant(constant.clone()); + self.memory.store(bid, 0, value); + } } // If functin declaration, skip initialization Declaration::Function { .. } => (), @@ -461,8 +550,11 @@ impl<'i> State<'i> { arg_else, } => { let value = self.interp_operand(condition.clone())?; - let value = value.get_bool().expect("`condition` must be `Value::Bool`"); - self.interp_jump(if value { arg_then } else { arg_else }) + let (value, width, _) = value.get_int().expect("`condition` must be `Value::Int`"); + // Check if it is boolean + assert!(width == 1); + + self.interp_jump(if value == 1 { arg_then } else { arg_else }) } BlockExit::Switch { value, @@ -565,7 +657,7 @@ impl<'i> State<'i> { target_dtype, } => { let value = self.interp_operand(value.clone())?; - calculator::calculate_typecast(&value, target_dtype).map_err(|_| { + calculator::calculate_typecast(value, target_dtype.clone()).map_err(|_| { InterpreterError::Misc { func_name: self.stack_frame.func_name.clone(), pc: self.stack_frame.pc, @@ -594,9 +686,16 @@ impl<'i> State<'i> { fn interp_constant(&self, value: Constant) -> Value { match value { Constant::Unit => Value::Unit, - // TODO: consider `width` and `is_signed` in the future - Constant::Int { value, .. } => Value::Int(value as i32), - Constant::Float { value, .. } => Value::Float(value as f32), + Constant::Int { + value, + width, + is_signed, + } => Value::Int { + value, + width, + is_signed, + }, + Constant::Float { value, width } => Value::Float { value, width }, Constant::GlobalVariable { name, .. } => { let bid = self .global_map diff --git a/src/tests.rs b/src/tests.rs index 3380c34..ed3a287 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -52,5 +52,8 @@ pub fn test_irgen(unit: &TranslationUnit, path: &Path) { .expect("failed to generate ir"); let args = Vec::new(); - assert_eq!(ir::interp(&ir, args), Ok(ir::Value::Int(status))); + assert_eq!( + ir::interp(&ir, args), + Ok(ir::Value::int(status as u128, 32, true)) + ); } diff --git a/tests/.gitignore b/tests/.gitignore index e76cca5..d7388a6 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -1,4 +1,4 @@ /platform.info /csmith-* -/test*.c* +/test*.c /reduce-criteria.sh diff --git a/tests/fuzz.py b/tests/fuzz.py index 1b011a5..9d41562 100644 --- a/tests/fuzz.py +++ b/tests/fuzz.py @@ -22,13 +22,18 @@ REPLACE_DICT = { "__restrict": "", "long __undefined;": "", "return 0;": "return crc32_context % 128;", - r"__attribute__ \(\(.*\)\)": "", + r"__attribute__\s*\(\(.*\)\)": "", "_Float128": "long double", "union": "struct", - r"enum\s*\{[^\}]*\};": "", + r"enum[\w\s]*\{[^\}]*\};": "", "const char \*const sys_errlist\[\];": "", r"[^\n]*printf[^;]*;": "", r"[^\n]*scanf[^;]*;": "", + " restrict": "", + "inline ": "", + "_Nullable": "", + "\"g_\w*\", ": "", + "char\* vname, ": "", } CSMITH_DIR = "csmith-2.3.0" @@ -172,7 +177,8 @@ def creduce(tests_dir, fuzz_arg): raise e try: - args = ["creduce", "./reduce-criteria.sh", "test_reduced.c"] + # --tidy: Do not make a backup copy of each file to reduce as file.orig + args = ["creduce", "--tidy", "./reduce-criteria.sh", "test_reduced.c"] proc = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=tests_dir) (out, err) = proc.communicate() if proc.returncode != 0: diff --git a/tests/reduce-criteria-template.sh b/tests/reduce-criteria-template.sh index f6dddd5..d0320ef 100644 --- a/tests/reduce-criteria-template.sh +++ b/tests/reduce-criteria-template.sh @@ -1,3 +1,4 @@ #!/usr/bin/env bash +cargo run --manifest-path $PROJECT_DIR/Cargo.toml --release -- -p test_reduced.c >/dev/null 2>&1 &&\ ! cargo run --manifest-path $PROJECT_DIR/Cargo.toml --release --bin fuzz -- $FUZZ_ARG test_reduced.c